diff options
Diffstat (limited to 'platform/android/example')
5 files changed, 1408 insertions, 1408 deletions
diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocPageView.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocPageView.java index 5ab52646..95eb4cdb 100644 --- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocPageView.java +++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocPageView.java @@ -23,440 +23,440 @@ import com.artifex.mupdf.fitz.Page; public class DocPageView extends View implements Callback { - private final Document mDoc; - private int mPageNum = -1; - private Page mPage; - private boolean mFinished = false; - - private float mScale = 1.0f; - private float mZoom = 1.0f; - - // drawing bitmap - private Bitmap mDrawBitmap = null; - private Rect mDrawSrcRect = new Rect(); - private final Rect mDrawDstRect = new Rect(); - private final Rect sourceRect = new Rect(); - private final Rect renderRect = new Rect(); - private float renderScale; - - // current size of this view - private Point mSize; - - // for drawing - private final Paint mPainter; - private final Rect mSrcRect = new Rect(); - private final Rect mDstRect = new Rect(); - private float drawScale; - - private static final boolean DEBUG_PAGE_RENDERING = false; - - private static final float mResolution = 160f; - - DisplayList pageContents = null; - DisplayList annotContents = null; - - public DocPageView(Context context, Document theDoc) - { - super(context); - setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - - mDoc = theDoc; - mPainter = new Paint(); - - setFocusable(true); - setFocusableInTouchMode(true); - } - - public void setupPage(final int thePageNum, int w, int h) - { - // if the page number has not yet been set, or has changed, - // make a new page object. - if (thePageNum != mPageNum) - { - mPageNum = thePageNum; - - // de-cache contents and annotations - if (pageContents != null) { - pageContents.destroy(); - pageContents = null; - } - if (annotContents != null) { - annotContents.destroy(); - annotContents = null; - } - - // destroy the page before making a new one. - if (mPage!=null) - mPage.destroy(); - mPage = mDoc.loadPage(mPageNum); - } - - // calculate zoom that makes page fit - - com.artifex.mupdf.fitz.Rect pageBounds = mPage.getBounds(); - - float pagew = (pageBounds.x1 - pageBounds.x0)*mResolution/72f; - float pageH = (pageBounds.y1 - pageBounds.y0)*mResolution/72f; - - mZoom = w/pagew; - mSize = new Point((int)(pagew*mZoom), (int)(pageH*mZoom)); - } - - public void setNewScale(float scale) { - mScale = scale; - } - - public int getCalculatedWidth() - { - return (int)(mSize.x * mScale); - } - - public int getCalculatedHeight() - { - return (int)(mSize.y * mScale); - } - - // a test for real visibility - private static final Rect visRect = new Rect(); - public boolean isReallyVisible() { - return getLocalVisibleRect(visRect); - } - - // This function renders colored rectangles and text in place of the page. - // Use it to test layouts. - private void renderNoPage(Bitmap bitmap, final RenderListener listener, Rect localVisRect, Rect globalVisRect) - { - // specify where to draw to and from - mDrawBitmap = bitmap; - mDrawSrcRect.set(globalVisRect); - mDrawDstRect.set(localVisRect); - - // make a rect representing the entire page in screen coordinates - int[] locations = new int[2]; - getLocationOnScreen(locations); - Rect pageRect = new Rect(locations[0], locations[1], locations[0]+getWidth(), locations[1]+getHeight()); - - // draw a yellow page with a red border containing the page number - - Paint p = new Paint(); - Canvas c = new Canvas(bitmap); - p.setColor(Color.RED); - p.setStyle(Paint.Style.FILL); - c.drawRect(pageRect,p); - - Rect smaller = new Rect(pageRect); - int inset = (int)(40*mScale); - smaller.inset(inset, inset); - p.setColor(Color.YELLOW); - p.setStyle(Paint.Style.FILL); - c.drawRect(smaller,p); - - String s = "" + (mPageNum+1); - p.setColor(Color.BLACK); - p.setTextSize(200.0f*mScale); - c.drawText(s, pageRect.left+(90*mScale), pageRect.top+(290*mScale), p); - - invalidate(); - listener.progress(0); - } - - public void render(Bitmap bitmap, final RenderListener listener) - { - if (mFinished) - return; - - // get local visible rect - Rect localVisRect = new Rect(); - if (!getLocalVisibleRect(localVisRect)) { - listener.progress(0); - return; // not visible - } - - // get global visible rect - Rect globalVisRect = new Rect(); - if (!getGlobalVisibleRect(globalVisRect)) { - listener.progress(0); - return; // not visible - } - - // do the render. - if (DEBUG_PAGE_RENDERING) - renderNoPage(bitmap, listener, localVisRect, globalVisRect); - else - renderPage(bitmap, listener, localVisRect, globalVisRect); - } - - // This function renders the document's page. - private void renderPage(final Bitmap bitmap, final RenderListener listener, final Rect localVisRect, final Rect globalVisRect) - { - // make a rect representing the entire page - // This might be outside the bounds of the bitmap - int[] locations = new int[2]; - getLocationOnScreen(locations); - Rect pageRect = new Rect(locations[0], locations[1], locations[0]+getCalculatedWidth(), locations[1]+getCalculatedHeight()); - - // make a rect representing the patch - // clip this to the bitmap - Rect patchRect = new Rect(pageRect); - patchRect.left = Math.max(patchRect.left,0); - patchRect.top = Math.max(patchRect.top,0); - patchRect.right = Math.min(patchRect.right,bitmap.getWidth()); - patchRect.bottom = Math.min(patchRect.bottom,bitmap.getHeight()); - - // set up the page and patch coordinates for the device - int pageX0 = pageRect.left; - int pageY0 = pageRect.top; - int pageX1 = pageRect.right; - int pageY1 = pageRect.bottom; - - int patchX0 = patchRect.left; - int patchY0 = patchRect.top; - int patchX1 = patchRect.right; - int patchY1 = patchRect.bottom; - - // set up a matrix for scaling - Matrix ctm = Matrix.Identity(); - ctm.scale(mScale*mZoom*mResolution/72f); - - // cache this page's display and annotation lists - cachePage(); - - sourceRect.set(globalVisRect); - renderRect.set(localVisRect); - renderScale = mScale; - - // Render the page in the background - DrawTaskParams params = new DrawTaskParams(new RenderListener() { - @Override - public void progress(int error) - { - // specify where to draw to and from - mDrawBitmap = bitmap; - mDrawSrcRect.set(sourceRect); - mDrawDstRect.set(renderRect); - drawScale = renderScale; - - invalidate(); - listener.progress(0); - } - }, ctm, bitmap, pageX0, pageY0, pageX1, pageY1, patchX0, patchY0, patchX1, patchY1); - - new DrawTask().execute(params, null, null); - } - - private void cachePage() - { - Cookie cookie = new Cookie(); - - if (pageContents==null) - { - // run the display list - pageContents = new DisplayList(); - DisplayListDevice dispDev = new DisplayListDevice(pageContents); - try { - mPage.run(dispDev, new Matrix(1, 0, 0, 1, 0, 0), cookie); - } - catch (RuntimeException e) { - pageContents.destroy(); - dispDev.destroy(); - throw(e); - } - finally { - dispDev.destroy(); - } - } - - if (annotContents==null) - { - // run the annotation list - annotContents = new DisplayList(); - DisplayListDevice annotDev = new DisplayListDevice(annotContents); - try { - mPage.run(annotDev, new Matrix(1, 0, 0, 1, 0, 0), cookie); - } - catch (RuntimeException e) { - annotContents.destroy(); - annotDev.destroy(); - throw(e); - } - finally { - annotDev.destroy(); - } - } - } - - @Override - public void onDraw(Canvas canvas) { - - if (mFinished) - return; - - // get bitmap to draw - Bitmap bitmap = mDrawBitmap; - if (bitmap==null) - return; // not yet rendered - - // set rectangles for drawing - mSrcRect.set(mDrawSrcRect); - mDstRect.set(mDrawDstRect); - - // if the scale has changed, adjust the destination - if (drawScale != mScale) - { - mDstRect.left *= (mScale/drawScale); - mDstRect.top *= (mScale/drawScale); - mDstRect.right *= (mScale/drawScale); - mDstRect.bottom *= (mScale/drawScale); - } - - // draw - canvas.drawBitmap(bitmap, mSrcRect, mDstRect, mPainter); - } - - public boolean onSingleTap(int x, int y) { - // NOTE: when double-tapping, a single-tap will also happen first. - // so that must be safe to do. - return false; - } - - public void onDoubleTap(int x, int y) { - } - - private Point screenToPage(Point p) - { - return screenToPage(p.x, p.y); - } - - private Point screenToPage(int screenX, int screenY) - { - // convert to view-relative - int viewX = screenX; - int viewY = screenY; - int loc[] = new int[2]; - getLocationOnScreen(loc); - viewX -= loc[0]; - viewY -= loc[1]; - - // convert to page-relative - double factor = mZoom * mScale; - int pageX = (int)(((double)viewX)/factor); - int pageY = (int)(((double)viewY)/factor); - - return new Point(pageX,pageY); - } - - public Point pageToView(int pageX, int pageY) - { - double factor = mZoom * mScale; - - int viewX = (int)(((double)pageX)*factor); - int viewY = (int)(((double)pageY)*factor); - - return new Point(viewX, viewY); - } - - public Point viewToPage(int viewX, int viewY) - { - double factor = mZoom * mScale; - - int pageX = (int)(((double)viewX)/factor); - int pageY = (int)(((double)viewY)/factor); - - return new Point(pageX, pageY); - } - - public void finish() - { - mFinished = true; - - // destroy the page - if (mPage!=null) { - mPage.destroy(); - mPage = null; - } - } - - // during layout, a DocView-relative rect is calculated and stashed here. - private final Rect mChildRect = new Rect(); - public void setChildRect(Rect r) {mChildRect.set(r);} - public Rect getChildRect() {return mChildRect;} - - private class DrawTaskParams { - DrawTaskParams (RenderListener listener, Matrix ctm, Bitmap bitmap, - int pageX0, int pageY0, int pageX1, int pageY1, - int patchX0, int patchY0, int patchX1, int patchY1) - { - this.listener = listener; - this.ctm = ctm; - this.bitmap = bitmap; - this.pageX0 = pageX0; - this.pageY0 = pageY0; - this.pageX1 = pageX1; - this.pageY1 = pageY1; - this.patchX0 = patchX0; - this.patchY0 = patchY0; - this.patchX1 = patchX1; - this.patchY1 = patchY1; - } - - public RenderListener listener; - public Matrix ctm; - public Bitmap bitmap; - public int pageX0; - public int pageY0; - public int pageX1; - public int pageY1; - public int patchX0; - public int patchY0; - public int patchX1; - public int patchY1; - } - - // The definition of our task class - private class DrawTask extends AsyncTask<DrawTaskParams, Void, Void> - { - private DrawTaskParams params = null; - - @Override - protected void onPreExecute() { - super.onPreExecute(); - } - - @Override - protected Void doInBackground(DrawTaskParams... paramList) { - params = paramList[0]; - - Cookie cookie = new Cookie(); - AndroidDrawDevice dev = new AndroidDrawDevice(params.bitmap, params.pageX0, params.pageY0, params.pageX1, params.pageY1, params.patchX0, params.patchY0, params.patchX1, params.patchY1); - try { - if (pageContents != null) { - pageContents.run(dev, params.ctm, cookie); - } - if (annotContents != null) { - annotContents.run(dev, params.ctm, cookie); - } - } - catch (Exception e) - { - Log.e("mupdf", e.getMessage()); - } - finally { - dev.destroy(); - } - - return null; - } - - @Override - protected void onProgressUpdate(Void... values) { - super.onProgressUpdate(values); - } - - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - params.listener.progress(0); - } - } + private final Document mDoc; + private int mPageNum = -1; + private Page mPage; + private boolean mFinished = false; + + private float mScale = 1.0f; + private float mZoom = 1.0f; + + // drawing bitmap + private Bitmap mDrawBitmap = null; + private Rect mDrawSrcRect = new Rect(); + private final Rect mDrawDstRect = new Rect(); + private final Rect sourceRect = new Rect(); + private final Rect renderRect = new Rect(); + private float renderScale; + + // current size of this view + private Point mSize; + + // for drawing + private final Paint mPainter; + private final Rect mSrcRect = new Rect(); + private final Rect mDstRect = new Rect(); + private float drawScale; + + private static final boolean DEBUG_PAGE_RENDERING = false; + + private static final float mResolution = 160f; + + DisplayList pageContents = null; + DisplayList annotContents = null; + + public DocPageView(Context context, Document theDoc) + { + super(context); + setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + mDoc = theDoc; + mPainter = new Paint(); + + setFocusable(true); + setFocusableInTouchMode(true); + } + + public void setupPage(final int thePageNum, int w, int h) + { + // if the page number has not yet been set, or has changed, + // make a new page object. + if (thePageNum != mPageNum) + { + mPageNum = thePageNum; + + // de-cache contents and annotations + if (pageContents != null) { + pageContents.destroy(); + pageContents = null; + } + if (annotContents != null) { + annotContents.destroy(); + annotContents = null; + } + + // destroy the page before making a new one. + if (mPage!=null) + mPage.destroy(); + mPage = mDoc.loadPage(mPageNum); + } + + // calculate zoom that makes page fit + + com.artifex.mupdf.fitz.Rect pageBounds = mPage.getBounds(); + + float pagew = (pageBounds.x1 - pageBounds.x0)*mResolution/72f; + float pageH = (pageBounds.y1 - pageBounds.y0)*mResolution/72f; + + mZoom = w/pagew; + mSize = new Point((int)(pagew*mZoom), (int)(pageH*mZoom)); + } + + public void setNewScale(float scale) { + mScale = scale; + } + + public int getCalculatedWidth() + { + return (int)(mSize.x * mScale); + } + + public int getCalculatedHeight() + { + return (int)(mSize.y * mScale); + } + + // a test for real visibility + private static final Rect visRect = new Rect(); + public boolean isReallyVisible() { + return getLocalVisibleRect(visRect); + } + + // This function renders colored rectangles and text in place of the page. + // Use it to test layouts. + private void renderNoPage(Bitmap bitmap, final RenderListener listener, Rect localVisRect, Rect globalVisRect) + { + // specify where to draw to and from + mDrawBitmap = bitmap; + mDrawSrcRect.set(globalVisRect); + mDrawDstRect.set(localVisRect); + + // make a rect representing the entire page in screen coordinates + int[] locations = new int[2]; + getLocationOnScreen(locations); + Rect pageRect = new Rect(locations[0], locations[1], locations[0]+getWidth(), locations[1]+getHeight()); + + // draw a yellow page with a red border containing the page number + + Paint p = new Paint(); + Canvas c = new Canvas(bitmap); + p.setColor(Color.RED); + p.setStyle(Paint.Style.FILL); + c.drawRect(pageRect,p); + + Rect smaller = new Rect(pageRect); + int inset = (int)(40*mScale); + smaller.inset(inset, inset); + p.setColor(Color.YELLOW); + p.setStyle(Paint.Style.FILL); + c.drawRect(smaller,p); + + String s = "" + (mPageNum+1); + p.setColor(Color.BLACK); + p.setTextSize(200.0f*mScale); + c.drawText(s, pageRect.left+(90*mScale), pageRect.top+(290*mScale), p); + + invalidate(); + listener.progress(0); + } + + public void render(Bitmap bitmap, final RenderListener listener) + { + if (mFinished) + return; + + // get local visible rect + Rect localVisRect = new Rect(); + if (!getLocalVisibleRect(localVisRect)) { + listener.progress(0); + return; // not visible + } + + // get global visible rect + Rect globalVisRect = new Rect(); + if (!getGlobalVisibleRect(globalVisRect)) { + listener.progress(0); + return; // not visible + } + + // do the render. + if (DEBUG_PAGE_RENDERING) + renderNoPage(bitmap, listener, localVisRect, globalVisRect); + else + renderPage(bitmap, listener, localVisRect, globalVisRect); + } + + // This function renders the document's page. + private void renderPage(final Bitmap bitmap, final RenderListener listener, final Rect localVisRect, final Rect globalVisRect) + { + // make a rect representing the entire page + // This might be outside the bounds of the bitmap + int[] locations = new int[2]; + getLocationOnScreen(locations); + Rect pageRect = new Rect(locations[0], locations[1], locations[0]+getCalculatedWidth(), locations[1]+getCalculatedHeight()); + + // make a rect representing the patch + // clip this to the bitmap + Rect patchRect = new Rect(pageRect); + patchRect.left = Math.max(patchRect.left,0); + patchRect.top = Math.max(patchRect.top,0); + patchRect.right = Math.min(patchRect.right,bitmap.getWidth()); + patchRect.bottom = Math.min(patchRect.bottom,bitmap.getHeight()); + + // set up the page and patch coordinates for the device + int pageX0 = pageRect.left; + int pageY0 = pageRect.top; + int pageX1 = pageRect.right; + int pageY1 = pageRect.bottom; + + int patchX0 = patchRect.left; + int patchY0 = patchRect.top; + int patchX1 = patchRect.right; + int patchY1 = patchRect.bottom; + + // set up a matrix for scaling + Matrix ctm = Matrix.Identity(); + ctm.scale(mScale*mZoom*mResolution/72f); + + // cache this page's display and annotation lists + cachePage(); + + sourceRect.set(globalVisRect); + renderRect.set(localVisRect); + renderScale = mScale; + + // Render the page in the background + DrawTaskParams params = new DrawTaskParams(new RenderListener() { + @Override + public void progress(int error) + { + // specify where to draw to and from + mDrawBitmap = bitmap; + mDrawSrcRect.set(sourceRect); + mDrawDstRect.set(renderRect); + drawScale = renderScale; + + invalidate(); + listener.progress(0); + } + }, ctm, bitmap, pageX0, pageY0, pageX1, pageY1, patchX0, patchY0, patchX1, patchY1); + + new DrawTask().execute(params, null, null); + } + + private void cachePage() + { + Cookie cookie = new Cookie(); + + if (pageContents==null) + { + // run the display list + pageContents = new DisplayList(); + DisplayListDevice dispDev = new DisplayListDevice(pageContents); + try { + mPage.run(dispDev, new Matrix(1, 0, 0, 1, 0, 0), cookie); + } + catch (RuntimeException e) { + pageContents.destroy(); + dispDev.destroy(); + throw(e); + } + finally { + dispDev.destroy(); + } + } + + if (annotContents==null) + { + // run the annotation list + annotContents = new DisplayList(); + DisplayListDevice annotDev = new DisplayListDevice(annotContents); + try { + mPage.run(annotDev, new Matrix(1, 0, 0, 1, 0, 0), cookie); + } + catch (RuntimeException e) { + annotContents.destroy(); + annotDev.destroy(); + throw(e); + } + finally { + annotDev.destroy(); + } + } + } + + @Override + public void onDraw(Canvas canvas) { + + if (mFinished) + return; + + // get bitmap to draw + Bitmap bitmap = mDrawBitmap; + if (bitmap==null) + return; // not yet rendered + + // set rectangles for drawing + mSrcRect.set(mDrawSrcRect); + mDstRect.set(mDrawDstRect); + + // if the scale has changed, adjust the destination + if (drawScale != mScale) + { + mDstRect.left *= (mScale/drawScale); + mDstRect.top *= (mScale/drawScale); + mDstRect.right *= (mScale/drawScale); + mDstRect.bottom *= (mScale/drawScale); + } + + // draw + canvas.drawBitmap(bitmap, mSrcRect, mDstRect, mPainter); + } + + public boolean onSingleTap(int x, int y) { + // NOTE: when double-tapping, a single-tap will also happen first. + // so that must be safe to do. + return false; + } + + public void onDoubleTap(int x, int y) { + } + + private Point screenToPage(Point p) + { + return screenToPage(p.x, p.y); + } + + private Point screenToPage(int screenX, int screenY) + { + // convert to view-relative + int viewX = screenX; + int viewY = screenY; + int loc[] = new int[2]; + getLocationOnScreen(loc); + viewX -= loc[0]; + viewY -= loc[1]; + + // convert to page-relative + double factor = mZoom * mScale; + int pageX = (int)(((double)viewX)/factor); + int pageY = (int)(((double)viewY)/factor); + + return new Point(pageX,pageY); + } + + public Point pageToView(int pageX, int pageY) + { + double factor = mZoom * mScale; + + int viewX = (int)(((double)pageX)*factor); + int viewY = (int)(((double)pageY)*factor); + + return new Point(viewX, viewY); + } + + public Point viewToPage(int viewX, int viewY) + { + double factor = mZoom * mScale; + + int pageX = (int)(((double)viewX)/factor); + int pageY = (int)(((double)viewY)/factor); + + return new Point(pageX, pageY); + } + + public void finish() + { + mFinished = true; + + // destroy the page + if (mPage!=null) { + mPage.destroy(); + mPage = null; + } + } + + // during layout, a DocView-relative rect is calculated and stashed here. + private final Rect mChildRect = new Rect(); + public void setChildRect(Rect r) {mChildRect.set(r);} + public Rect getChildRect() {return mChildRect;} + + private class DrawTaskParams { + DrawTaskParams (RenderListener listener, Matrix ctm, Bitmap bitmap, + int pageX0, int pageY0, int pageX1, int pageY1, + int patchX0, int patchY0, int patchX1, int patchY1) + { + this.listener = listener; + this.ctm = ctm; + this.bitmap = bitmap; + this.pageX0 = pageX0; + this.pageY0 = pageY0; + this.pageX1 = pageX1; + this.pageY1 = pageY1; + this.patchX0 = patchX0; + this.patchY0 = patchY0; + this.patchX1 = patchX1; + this.patchY1 = patchY1; + } + + public RenderListener listener; + public Matrix ctm; + public Bitmap bitmap; + public int pageX0; + public int pageY0; + public int pageX1; + public int pageY1; + public int patchX0; + public int patchY0; + public int patchX1; + public int patchY1; + } + + // The definition of our task class + private class DrawTask extends AsyncTask<DrawTaskParams, Void, Void> + { + private DrawTaskParams params = null; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + } + + @Override + protected Void doInBackground(DrawTaskParams... paramList) { + params = paramList[0]; + + Cookie cookie = new Cookie(); + AndroidDrawDevice dev = new AndroidDrawDevice(params.bitmap, params.pageX0, params.pageY0, params.pageX1, params.pageY1, params.patchX0, params.patchY0, params.patchX1, params.patchY1); + try { + if (pageContents != null) { + pageContents.run(dev, params.ctm, cookie); + } + if (annotContents != null) { + annotContents.run(dev, params.ctm, cookie); + } + } + catch (Exception e) + { + Log.e("mupdf", e.getMessage()); + } + finally { + dev.destroy(); + } + + return null; + } + + @Override + protected void onProgressUpdate(Void... values) { + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + params.listener.progress(0); + } + } } diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocView.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocView.java index 34613dc3..da6d7677 100644 --- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocView.java +++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocView.java @@ -22,897 +22,897 @@ import android.widget.Scroller; import com.artifex.mupdf.fitz.Document; public class DocView - extends AdapterView<Adapter> - implements GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener, Runnable + extends AdapterView<Adapter> + implements GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener, Runnable { - private static final int GAP = 20; - - private static final float MIN_SCALE = .15f; - private static final float MAX_SCALE = 5.0f; - - private PageAdapter mAdapter; - private boolean mFinished = false; - - private final SparseArray<View> mChildViews = new SparseArray<View>(3); - - private boolean mScaling; // Whether the user is currently pinch zooming - private float mScale = 1.0f; - private int mXScroll; // Scroll amounts recorded from events. - private int mYScroll; // and then accounted for in onLayout - - private GestureDetector mGestureDetector; - private ScaleGestureDetector mScaleGestureDetector; - - // bitmaps for rendering - // these are created by the activity and set using setBitmaps() - private final static double OVERSIZE_FACTOR = 1.1; - private final Bitmap[] bitmaps = {null,null}; - - private int bitmapIndex = 0; - private boolean renderRequested = false; - private int renderCount = 0; - - // used during layout - private final Rect mChildRect = new Rect(); - private final Rect mViewport = new Rect(); - private final Point mViewportOrigin = new Point(); - private final Rect mBlockRect = new Rect(); - private final Rect mLastBlockRect = new Rect(); - private int mLastLayoutColumns = 1; - protected int mPageCollectionHeight; - private int mPageCollectionWidth; - - // for flinging - private static final int MOVING_DIAGONALLY = 0; - private static final int MOVING_LEFT = 1; - private static final int MOVING_RIGHT = 2; - private static final int MOVING_UP = 3; - private static final int MOVING_DOWN = 4; - - private static final float MIN_FLING_VELOCITY = 1500.0f; - private static final long FLING_THROTTLE_TIME = 20; - - private Scroller mScroller; - private Stepper mStepper; - private int mScrollerLastX; - private int mScrollerLastY; - private long mFlingStartTime; - - // for single- and double-tapping - private long mLastTapTime = 0; - private float lastTapX; - private float lastTapY; - private int mTapStatus = 0; - - private static final int DOUBLE_TAP_TIME = 300; - private static final int SHOW_KEYBOARD_TIME = 500; - - // the document. - private Document mDoc; - - private boolean mStarted = false; - - public DocView(Context context) { - super(context); - initialize(context); - } - - public DocView(Context context, AttributeSet attrs) { - super(context, attrs); - initialize(context); - } - - public DocView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initialize(context); - } - - protected Context mContext = null; - - protected void initialize(Context context) - { - mContext = context; - mGestureDetector = new GestureDetector(context, this); - mScaleGestureDetector = new ScaleGestureDetector(context, this); - mScroller = new Scroller(context); - mStepper = new Stepper(this, this); - - // create bitmaps - makeBitmaps(); - } - - private void makeBitmaps() - { - // get current screen size - WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - DisplayMetrics metrics = new DisplayMetrics(); - wm.getDefaultDisplay().getMetrics(metrics); - int screenW = metrics.widthPixels; - int screenH = metrics.heightPixels; - - // make two bitmaps. - // make them large enough for both screen orientations, so we don't have to - // change them when the orientation changes. - - int w = (int)(screenW*OVERSIZE_FACTOR); - int h = (int)(screenH*OVERSIZE_FACTOR); - int size = Math.max(w,h); - for (int i=0;i<bitmaps.length;i++) - bitmaps[i] = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - } - - public void start(final String path) - { - // wait for the layout to finish - ViewTreeObserver observer = getViewTreeObserver(); - observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() - { - @Override - public void onGlobalLayout() - { - getViewTreeObserver().removeOnGlobalLayoutListener(this); - - mAdapter = new PageAdapter(mContext); - mAdapter.setWidth(getWidth()); - mDoc = new Document(path); - mAdapter.setDocument(mDoc); - mScale = 1.0f; - mStarted = true; - triggerRender(); - } - }); - } - - private void onScaleChild(View v, Float scale) - { - ((DocPageView)v).setNewScale(scale); - } - - public void onOrientationChange() - { - triggerRender(); - } - - private void onSizeChange(float factor) - { - mScale *= factor; - scaleChildren(); - requestLayout(); - } - - public boolean onDown(MotionEvent arg0) { - mScroller.forceFinished(true); - return true; - } - - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - - // not while we're scaling - if (mScaling) - return true; - - // not while a previous fling is underway - if (!mScroller.isFinished()) - return true; - - // must be really flinging - float vel = Math.max(Math.abs(velocityX),Math.abs(velocityY)); - if (vel<MIN_FLING_VELOCITY) - return false; - - // what direction? - int direction = directionOfTravel(velocityX,velocityY); - - mFlingStartTime = System.currentTimeMillis(); - - switch (direction) - { - case MOVING_DOWN: - smoothScrollBy(0, getHeight()/2); - break; - - case MOVING_UP: - smoothScrollBy(0, -getHeight()/2); - break; - - default: - break; - } - - return true; - } - - private static int directionOfTravel(float vx, float vy) { - if (Math.abs(vx) > 2 * Math.abs(vy)) - return (vx > 0) ? MOVING_RIGHT : MOVING_LEFT; - else if (Math.abs(vy) > 2 * Math.abs(vx)) - return (vy > 0) ? MOVING_DOWN : MOVING_UP; - else - return MOVING_DIAGONALLY; - } - - public void onLongPress(MotionEvent e) { - } - - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - - // not if we're scaling - if (mScaling) - return true; - - // not if a previous fling is underway - if (!mScroller.isFinished()) - return true; - - // accumulate scrolling amount. - mXScroll -= distanceX; - mYScroll -= distanceY; - - requestLayout(); - - return true; - } - - public void onShowPress(MotionEvent e) { - } - - protected DocPageView findPageViewContainingPoint(int x, int y, boolean includeMargin) - { - for (int i = 0; i < getChildCount(); i++) - { - // get the rect for the page - View child = getChildAt(i); - Rect childRect = new Rect(); - child.getGlobalVisibleRect(childRect); - - // add in the margin - if (includeMargin) - { - childRect.left -= GAP/2; - childRect.right += GAP/2; - childRect.top -= GAP/2; - childRect.bottom += GAP/2; - } - - // see if the rect contains the point - if (childRect.contains(x,y)) - return (DocPageView)child; - } - - return null; - } - - protected Point eventToScreen(float fx, float fy) - { - int x = Math.round(fx); - int y = Math.round(fy); - Rect docRect = new Rect(); - getGlobalVisibleRect(docRect); - x += docRect.left; - y += docRect.top; - - return new Point(x,y); - } - - protected void doSingleTap(float fx, float fy) - { - // find the page view that was tapped. - Point p = eventToScreen(fx,fy); - final DocPageView dpv = findPageViewContainingPoint(p.x, p.y, false); - if (dpv==null) - return; - - // see if the age wants to handle the single tap - boolean handled = dpv.onSingleTap(p.x,p.y); - - // if not, ... - if (!handled) - { - // schedule a task in the near future to check if we're still a single-tap. - final Handler handler = new Handler(); - final Point tappedPoint = p; - handler.postDelayed(new Runnable() - { - @Override - public void run() - { - if (mTapStatus==1) - { - // still single - } - else - { - // double - } - mTapStatus = 0; - } - }, SHOW_KEYBOARD_TIME); - } - } - - protected void doDoubleTap(float fx, float fy) - { - Point p = eventToScreen(fx,fy); - DocPageView v = findPageViewContainingPoint(p.x, p.y, false); - if (v != null) { - v.onDoubleTap(p.x,p.y); - } - } - - public boolean onSingleTapUp(final MotionEvent e) - { - long now = System.currentTimeMillis(); - if (mLastTapTime!=0 && ((now-mLastTapTime)<DOUBLE_TAP_TIME)) - { - mTapStatus = 2; - doDoubleTap(lastTapX,lastTapY); - mLastTapTime = 0; - } - else - { - mLastTapTime = now; - lastTapX = e.getX(); - lastTapY = e.getY(); - doSingleTap(lastTapX, lastTapY); - mTapStatus = 1; - } - - return false; - } - - private void scaleChildren() - { - // scale children - for (int i=0; i<getPageCount(); i++) - { - DocPageView cv = (DocPageView)getOrCreateChild(i); - cv.setNewScale(mScale); - } - } - - public boolean onScale(ScaleGestureDetector detector) - { - // new scale factor - float previousScale = mScale; - mScale = Math.min(Math.max(mScale * detector.getScaleFactor(), MIN_SCALE), MAX_SCALE); - - // did we really scale? - if (mScale == previousScale) - return true; - - // scale children - scaleChildren(); - - // maintain focus while scaling - float currentFocusX = detector.getFocusX(); - float currentFocusY = detector.getFocusY(); - int viewFocusX = (int)currentFocusX + getScrollX(); - int viewFocusY = (int)currentFocusY + getScrollY(); - mXScroll += viewFocusX - viewFocusX * detector.getScaleFactor(); - mYScroll += viewFocusY - viewFocusY * detector.getScaleFactor(); - - requestLayout(); - - return true; - } - - public boolean onScaleBegin(ScaleGestureDetector detector) { - - mScaling = true; - - // Ignore any scroll amounts yet to be accounted for: the - // screen is not showing the effect of them, so they can - // only confuse the user - mXScroll = mYScroll = 0; - - return true; - } - - public void onScaleEnd(ScaleGestureDetector detector) - { - // When a pinch-scale is done, we want to get n-across - // to fit properly. - - // get current viewport - Rect viewport = new Rect(); - getGlobalVisibleRect(viewport); - - // if we're at one column and wider than the viewport, - // leave it alone. - if (mLastLayoutColumns==0 && mPageCollectionWidth>=viewport.width()) - { - mScaling = false; - return; - } - - // ratio of the viewport width to layout width - float ratio = ((float)(viewport.width()))/((float)(mPageCollectionWidth)); - - // set a new scale factor - mScale *= ratio; - scaleChildren(); - - // scroll so the left edged is flush to the viewport. - mXScroll +=getScrollX(); - - requestLayout(); - - mScaling = false; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - - if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { - // do something when user interaction begins - } - - if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) { - // do something when user interaction ends - triggerRender(); - } - - mScaleGestureDetector.onTouchEvent(event); - mGestureDetector.onTouchEvent(event); - - return true; - } - - protected int getPageCount() - { - return getAdapter().getCount(); - } - - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - - super.onLayout(changed, left, top, right, bottom); - - if (!mStarted) - return; - - // not if there are no pages - if (getPageCount()==0) - return; - - int numDocPages = getPageCount(); - - // not if we've been finished - if (finished()) - return; - - // perform any pending scrolling - scrollBy(-mXScroll, -mYScroll); - mXScroll = mYScroll = 0; - - // get current viewport - mViewportOrigin.set(getScrollX(), getScrollY()); - getGlobalVisibleRect(mViewport); - mViewport.offsetTo(mViewportOrigin.x, mViewportOrigin.y); - - // find the widest child - int maxw = 0; - for (int i=0; i<getPageCount(); i++) - { - DocPageView cv = (DocPageView)getOrCreateChild(i); - - int childWidth = cv.getCalculatedWidth(); - if (childWidth>maxw) - maxw = childWidth; - } - - // how many columns? - double dcol = (double)(mViewport.width()+GAP)/(double)(maxw+GAP); - int columns = (int) dcol; - - // lay them out - int mostVisibleChildHeight = -1; - int childTop = 0; - mPageCollectionHeight = 0; - mPageCollectionWidth = 0; - int column = 0; - mBlockRect.setEmpty(); - - for (int i=0; i<getPageCount(); i++) - { - DocPageView cv = (DocPageView)getOrCreateChild(i); - int childWidth = cv.getCalculatedWidth(); - int childHeight = cv.getCalculatedHeight(); - int childLeft = column * (maxw + GAP); - int childRight = childLeft + childWidth; - int childBottom = childTop + childHeight; - mChildRect.set(childLeft, childTop, childRight, childBottom); - - // stash the rect in the page view for later use. - cv.setChildRect(mChildRect); - - // at each layout, we remember the entire width and height of the laid-out - // pages. This is used in applying constraints to scrolling amounts. - if (childBottom> mPageCollectionHeight) - mPageCollectionHeight = childBottom; - if (childRight>mPageCollectionWidth) - mPageCollectionWidth = childRight; - - if (mBlockRect.isEmpty()) - mBlockRect.set(mChildRect); - else - mBlockRect.union(mChildRect); - - if (mChildRect.intersect(mViewport) && i<numDocPages) - { - // visible, so include in layout - if (cv.getParent()==null) - addChildToLayout(cv); - cv.layout(childLeft, childTop, childRight, childBottom); - cv.invalidate(); - } - else - { - // not visible, so remove from layout - removeViewInLayout(cv); - } - - column++; - if (column >= columns) { - column = 0; - childTop += childHeight; - childTop += GAP; - } - } - - // if the number of columns has changed, do some scrolling to adjust - if (mScaling && columns>=1 && mLastLayoutColumns>=1 && mLastLayoutColumns!=columns) - { - // x - center in the viewport - int dx = mBlockRect.centerX() - mViewport.centerX(); - scrollBy(dx,0); - - // y - attempt to keep what's in the center of the viewport in view. - int oldy = mViewport.centerY() - mLastBlockRect.top; - int newy = (int)((float)oldy*mBlockRect.height()/mLastBlockRect.height()); - scrollBy(0,newy-oldy); - } - mLastLayoutColumns = columns; - mLastBlockRect.set(mBlockRect); - - // see if we're handling a start page - handleStartPage(); - - triggerRender(); - } - - // start page, get and set. - private int mStartPage = 0; - public void setStartPage(int page) { - mStartPage = page; - } - protected int getStartPage() {return mStartPage;} - - // handle start page - public void handleStartPage() - { - // if we've been given a start page, go there. - final int start = getStartPage(); - if (start>0) - { - setStartPage(0); // but just once - - // post all of this so that we get an additional layout request - final Handler handler = new Handler(); - handler.post(new Runnable() { - @Override - public void run() { - DocPageView cv = (DocPageView)getOrCreateChild(start-1); - Rect r = cv.getChildRect(); - scrollBy(0,r.top); - requestLayout(); - } - }); - } - } - - // override the view's scrollBy() function so we can - // take the opportunity to apply some constraints - - @Override - public void scrollBy(int dx, int dy) - { - Point p = constrainScrollBy(dx, dy); - super.scrollBy(p.x, p.y); - } - - // apply contraints to every scroll request. - - protected Point constrainScrollBy(int dx, int dy) - { - int vph; - int vpw; - { - Rect viewport = new Rect(); - getGlobalVisibleRect(viewport); - vph = viewport.height(); - vpw = viewport.width(); - } - int sx = getScrollX(); - int sy = getScrollY(); - - if (mPageCollectionWidth <= vpw) - { - // not too far to the right - if (mPageCollectionWidth-sx-dx > vpw) - dx = 0; - - // not too far to the left - if (sx+dx>0) - dx = -sx; - } - else - { - // not too far to the right - if (mPageCollectionWidth < sx+vpw+dx) - dx = 0; - - // not too far to the left - if (sx+dx < 0) - dx = -sx; - } - - if (mPageCollectionHeight <= vph) - { - // not too far down - if (mPageCollectionHeight-sy-dy > vph) - dy = 0; - - // not too far up - if (sy+dy>0) - dy = -sy; - } - else - { - // not too far down - if (sy+dy < 0) - dy = -sy; - - // not too far up. - if (mPageCollectionHeight+2*vph/3 < sy+vph+dy) - dy = 0; - } - - return new Point(dx, dy); - } - - @Override - public Adapter getAdapter() { - return mAdapter; - } - - @Override - public View getSelectedView() { - return null; - } - - @Override - public void setAdapter(Adapter adapter) { - mAdapter = (PageAdapter)adapter; - requestLayout(); - } - - @Override - public void setSelection(int arg0) { - throw new UnsupportedOperationException("setSelection is not supported"); - } - - private View getCached() { - return null; - } - - protected View getOrCreateChild(int i) { - View v = mChildViews.get(i); - if (v == null) { - v = getViewFromAdapter(i); - mChildViews.append(i, v); // Record the view against it's adapter index - onScaleChild(v, mScale); - } - - return v; - } - - protected View getViewFromAdapter(int index) - { - return getAdapter().getView(index, getCached(), this); - } - - private void addChildToLayout(View v) { - LayoutParams params = v.getLayoutParams(); - if (params == null) { - params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - } - addViewInLayout(v, 0, params, true); - } - - private void triggerRender() - { - // Note that a render is needed - renderRequested = true; - - // If the previous render has completed, start a new one. Otherwise - // a new one will start as soon as the previous one completes. - if (renderCount == 0) - renderPages(); - } - - private void renderPages() - { - renderRequested = false; - - if (mFinished) - return; - - if (bitmaps == null) - return; - - // Rotate to the next bitmap - bitmapIndex++; - if (bitmapIndex>=bitmaps.length) - bitmapIndex = 0; - - // iterate through the children - for (int i = 0; i < getPageCount(); i++) { - - if (mFinished) - return; - - final DocPageView cv = (DocPageView) getOrCreateChild(i); - if (cv.getParent()!=null && cv.isReallyVisible()) { - // Count up as we kick off rendering of each visible page - renderCount++; - cv.render(bitmaps[bitmapIndex], new RenderListener() { - @Override - public void progress(int error) { - - if (error==0) - cv.invalidate(); - - // Count down as they complete - renderCount--; - - if (renderCount==0) { - if (renderRequested) { - // If this phase of rendering has completed and another has - // been requested, start it now - renderPages(); - } - } - } - }); - } - } - } - - @Override - public void run() - { - if (!mScroller.isFinished()) - { - mScroller.computeScrollOffset(); - int x = mScroller.getCurrX(); - int y = mScroller.getCurrY(); - mXScroll += x - mScrollerLastX; - mYScroll += y - mScrollerLastY; - mScrollerLastX = x; - mScrollerLastY = y; - - // limit the amount of repeated layouts. - long tNow = System.currentTimeMillis(); - long diff = tNow - mFlingStartTime; - if (diff>FLING_THROTTLE_TIME) - { - requestLayout(); - mFlingStartTime = tNow; - } - - mStepper.prod(); - } - else - { - // one more - long tNow = System.currentTimeMillis(); - if (tNow != mFlingStartTime) - requestLayout(); - } - } - - public void finish() - { - // we're done with this view. - mFinished = true; - - // first, hide and remove all the children - for (int i=0; i<getPageCount(); i++) - { - DocPageView cv = (DocPageView)getOrCreateChild(i); - cv.setVisibility(GONE); - removeViewInLayout(cv); - cv.finish(); - } - } - - public boolean finished() {return mFinished;} - - protected void smoothScrollBy(int dx, int dy) - { - mScrollerLastX = mScrollerLastY = 0; - mScroller.startScroll(0, 0, dx, dy, 400); - mStepper.prod(); - } - - public void scrollToPage(int pageNumber) - { - // scroll to bring the page into view - - // get current viewport - Rect viewport = new Rect(); - getGlobalVisibleRect(viewport); - - // offset it based on current scroll position - Point viewportOrigin = new Point(); - viewportOrigin.set(getScrollX(), getScrollY()); - viewport.offsetTo(viewportOrigin.x, viewportOrigin.y); - - // get page rect from last layout - DocPageView cv = (DocPageView)getOrCreateChild(pageNumber); - Rect childRect = cv.getChildRect(); - - // scroll - if ((childRect.height()) > viewport.height()) - { - // put the top of the page at the top and the left at 0 - smoothScrollBy(getScrollX(),getScrollY()-childRect.top); - } - else - { - // if the whole page is not visible, move the center of the page at the center - if (childRect.top < viewport.top || childRect.bottom > viewport.bottom) - { - if (childRect.top==0) - smoothScrollBy(0, getScrollY()); - else - smoothScrollBy(0, getScrollY() + viewport.height() / 2 - (childRect.bottom + childRect.top) / 2); - } - } - } - - private Point viewToScreen(Point p) - { - Point newPoint = new Point(p); - - Rect r = new Rect(); - this.getGlobalVisibleRect(r); - - newPoint.offset(r.left, r.top); - - return newPoint; - } - - public void scrollBoxIntoView (int pageNum, RectF box) - { - // get our viewport - Rect viewport = new Rect(); - getGlobalVisibleRect(viewport); - viewport.offset(0,-viewport.top); - - // get the location of the box's lower left corner, - // relative to the viewport - DocPageView cv = (DocPageView)getOrCreateChild(pageNum); - Point point = cv.pageToView((int)box.left,(int)box.bottom); - Rect childRect = cv.getChildRect(); - point.y += childRect.top; - point.y -= getScrollY(); - - // if the point is outside the viewport, scroll so it is. - if (point.y<viewport.top || point.y>viewport.bottom) - { - int diff = (viewport.top + viewport.bottom)/2 - point.y; - smoothScrollBy(0,diff); - } - } + private static final int GAP = 20; + + private static final float MIN_SCALE = .15f; + private static final float MAX_SCALE = 5.0f; + + private PageAdapter mAdapter; + private boolean mFinished = false; + + private final SparseArray<View> mChildViews = new SparseArray<View>(3); + + private boolean mScaling; // Whether the user is currently pinch zooming + private float mScale = 1.0f; + private int mXScroll; // Scroll amounts recorded from events. + private int mYScroll; // and then accounted for in onLayout + + private GestureDetector mGestureDetector; + private ScaleGestureDetector mScaleGestureDetector; + + // bitmaps for rendering + // these are created by the activity and set using setBitmaps() + private final static double OVERSIZE_FACTOR = 1.1; + private final Bitmap[] bitmaps = {null,null}; + + private int bitmapIndex = 0; + private boolean renderRequested = false; + private int renderCount = 0; + + // used during layout + private final Rect mChildRect = new Rect(); + private final Rect mViewport = new Rect(); + private final Point mViewportOrigin = new Point(); + private final Rect mBlockRect = new Rect(); + private final Rect mLastBlockRect = new Rect(); + private int mLastLayoutColumns = 1; + protected int mPageCollectionHeight; + private int mPageCollectionWidth; + + // for flinging + private static final int MOVING_DIAGONALLY = 0; + private static final int MOVING_LEFT = 1; + private static final int MOVING_RIGHT = 2; + private static final int MOVING_UP = 3; + private static final int MOVING_DOWN = 4; + + private static final float MIN_FLING_VELOCITY = 1500.0f; + private static final long FLING_THROTTLE_TIME = 20; + + private Scroller mScroller; + private Stepper mStepper; + private int mScrollerLastX; + private int mScrollerLastY; + private long mFlingStartTime; + + // for single- and double-tapping + private long mLastTapTime = 0; + private float lastTapX; + private float lastTapY; + private int mTapStatus = 0; + + private static final int DOUBLE_TAP_TIME = 300; + private static final int SHOW_KEYBOARD_TIME = 500; + + // the document. + private Document mDoc; + + private boolean mStarted = false; + + public DocView(Context context) { + super(context); + initialize(context); + } + + public DocView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context); + } + + public DocView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(context); + } + + protected Context mContext = null; + + protected void initialize(Context context) + { + mContext = context; + mGestureDetector = new GestureDetector(context, this); + mScaleGestureDetector = new ScaleGestureDetector(context, this); + mScroller = new Scroller(context); + mStepper = new Stepper(this, this); + + // create bitmaps + makeBitmaps(); + } + + private void makeBitmaps() + { + // get current screen size + WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics metrics = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(metrics); + int screenW = metrics.widthPixels; + int screenH = metrics.heightPixels; + + // make two bitmaps. + // make them large enough for both screen orientations, so we don't have to + // change them when the orientation changes. + + int w = (int)(screenW*OVERSIZE_FACTOR); + int h = (int)(screenH*OVERSIZE_FACTOR); + int size = Math.max(w,h); + for (int i=0;i<bitmaps.length;i++) + bitmaps[i] = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + } + + public void start(final String path) + { + // wait for the layout to finish + ViewTreeObserver observer = getViewTreeObserver(); + observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() + { + @Override + public void onGlobalLayout() + { + getViewTreeObserver().removeOnGlobalLayoutListener(this); + + mAdapter = new PageAdapter(mContext); + mAdapter.setWidth(getWidth()); + mDoc = new Document(path); + mAdapter.setDocument(mDoc); + mScale = 1.0f; + mStarted = true; + triggerRender(); + } + }); + } + + private void onScaleChild(View v, Float scale) + { + ((DocPageView)v).setNewScale(scale); + } + + public void onOrientationChange() + { + triggerRender(); + } + + private void onSizeChange(float factor) + { + mScale *= factor; + scaleChildren(); + requestLayout(); + } + + public boolean onDown(MotionEvent arg0) { + mScroller.forceFinished(true); + return true; + } + + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + + // not while we're scaling + if (mScaling) + return true; + + // not while a previous fling is underway + if (!mScroller.isFinished()) + return true; + + // must be really flinging + float vel = Math.max(Math.abs(velocityX),Math.abs(velocityY)); + if (vel<MIN_FLING_VELOCITY) + return false; + + // what direction? + int direction = directionOfTravel(velocityX,velocityY); + + mFlingStartTime = System.currentTimeMillis(); + + switch (direction) + { + case MOVING_DOWN: + smoothScrollBy(0, getHeight()/2); + break; + + case MOVING_UP: + smoothScrollBy(0, -getHeight()/2); + break; + + default: + break; + } + + return true; + } + + private static int directionOfTravel(float vx, float vy) { + if (Math.abs(vx) > 2 * Math.abs(vy)) + return (vx > 0) ? MOVING_RIGHT : MOVING_LEFT; + else if (Math.abs(vy) > 2 * Math.abs(vx)) + return (vy > 0) ? MOVING_DOWN : MOVING_UP; + else + return MOVING_DIAGONALLY; + } + + public void onLongPress(MotionEvent e) { + } + + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + + // not if we're scaling + if (mScaling) + return true; + + // not if a previous fling is underway + if (!mScroller.isFinished()) + return true; + + // accumulate scrolling amount. + mXScroll -= distanceX; + mYScroll -= distanceY; + + requestLayout(); + + return true; + } + + public void onShowPress(MotionEvent e) { + } + + protected DocPageView findPageViewContainingPoint(int x, int y, boolean includeMargin) + { + for (int i = 0; i < getChildCount(); i++) + { + // get the rect for the page + View child = getChildAt(i); + Rect childRect = new Rect(); + child.getGlobalVisibleRect(childRect); + + // add in the margin + if (includeMargin) + { + childRect.left -= GAP/2; + childRect.right += GAP/2; + childRect.top -= GAP/2; + childRect.bottom += GAP/2; + } + + // see if the rect contains the point + if (childRect.contains(x,y)) + return (DocPageView)child; + } + + return null; + } + + protected Point eventToScreen(float fx, float fy) + { + int x = Math.round(fx); + int y = Math.round(fy); + Rect docRect = new Rect(); + getGlobalVisibleRect(docRect); + x += docRect.left; + y += docRect.top; + + return new Point(x,y); + } + + protected void doSingleTap(float fx, float fy) + { + // find the page view that was tapped. + Point p = eventToScreen(fx,fy); + final DocPageView dpv = findPageViewContainingPoint(p.x, p.y, false); + if (dpv==null) + return; + + // see if the age wants to handle the single tap + boolean handled = dpv.onSingleTap(p.x,p.y); + + // if not, ... + if (!handled) + { + // schedule a task in the near future to check if we're still a single-tap. + final Handler handler = new Handler(); + final Point tappedPoint = p; + handler.postDelayed(new Runnable() + { + @Override + public void run() + { + if (mTapStatus==1) + { + // still single + } + else + { + // double + } + mTapStatus = 0; + } + }, SHOW_KEYBOARD_TIME); + } + } + + protected void doDoubleTap(float fx, float fy) + { + Point p = eventToScreen(fx,fy); + DocPageView v = findPageViewContainingPoint(p.x, p.y, false); + if (v != null) { + v.onDoubleTap(p.x,p.y); + } + } + + public boolean onSingleTapUp(final MotionEvent e) + { + long now = System.currentTimeMillis(); + if (mLastTapTime!=0 && ((now-mLastTapTime)<DOUBLE_TAP_TIME)) + { + mTapStatus = 2; + doDoubleTap(lastTapX,lastTapY); + mLastTapTime = 0; + } + else + { + mLastTapTime = now; + lastTapX = e.getX(); + lastTapY = e.getY(); + doSingleTap(lastTapX, lastTapY); + mTapStatus = 1; + } + + return false; + } + + private void scaleChildren() + { + // scale children + for (int i=0; i<getPageCount(); i++) + { + DocPageView cv = (DocPageView)getOrCreateChild(i); + cv.setNewScale(mScale); + } + } + + public boolean onScale(ScaleGestureDetector detector) + { + // new scale factor + float previousScale = mScale; + mScale = Math.min(Math.max(mScale * detector.getScaleFactor(), MIN_SCALE), MAX_SCALE); + + // did we really scale? + if (mScale == previousScale) + return true; + + // scale children + scaleChildren(); + + // maintain focus while scaling + float currentFocusX = detector.getFocusX(); + float currentFocusY = detector.getFocusY(); + int viewFocusX = (int)currentFocusX + getScrollX(); + int viewFocusY = (int)currentFocusY + getScrollY(); + mXScroll += viewFocusX - viewFocusX * detector.getScaleFactor(); + mYScroll += viewFocusY - viewFocusY * detector.getScaleFactor(); + + requestLayout(); + + return true; + } + + public boolean onScaleBegin(ScaleGestureDetector detector) { + + mScaling = true; + + // Ignore any scroll amounts yet to be accounted for: the + // screen is not showing the effect of them, so they can + // only confuse the user + mXScroll = mYScroll = 0; + + return true; + } + + public void onScaleEnd(ScaleGestureDetector detector) + { + // When a pinch-scale is done, we want to get n-across + // to fit properly. + + // get current viewport + Rect viewport = new Rect(); + getGlobalVisibleRect(viewport); + + // if we're at one column and wider than the viewport, + // leave it alone. + if (mLastLayoutColumns==0 && mPageCollectionWidth>=viewport.width()) + { + mScaling = false; + return; + } + + // ratio of the viewport width to layout width + float ratio = ((float)(viewport.width()))/((float)(mPageCollectionWidth)); + + // set a new scale factor + mScale *= ratio; + scaleChildren(); + + // scroll so the left edged is flush to the viewport. + mXScroll +=getScrollX(); + + requestLayout(); + + mScaling = false; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + + if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { + // do something when user interaction begins + } + + if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) { + // do something when user interaction ends + triggerRender(); + } + + mScaleGestureDetector.onTouchEvent(event); + mGestureDetector.onTouchEvent(event); + + return true; + } + + protected int getPageCount() + { + return getAdapter().getCount(); + } + + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + + super.onLayout(changed, left, top, right, bottom); + + if (!mStarted) + return; + + // not if there are no pages + if (getPageCount()==0) + return; + + int numDocPages = getPageCount(); + + // not if we've been finished + if (finished()) + return; + + // perform any pending scrolling + scrollBy(-mXScroll, -mYScroll); + mXScroll = mYScroll = 0; + + // get current viewport + mViewportOrigin.set(getScrollX(), getScrollY()); + getGlobalVisibleRect(mViewport); + mViewport.offsetTo(mViewportOrigin.x, mViewportOrigin.y); + + // find the widest child + int maxw = 0; + for (int i=0; i<getPageCount(); i++) + { + DocPageView cv = (DocPageView)getOrCreateChild(i); + + int childWidth = cv.getCalculatedWidth(); + if (childWidth>maxw) + maxw = childWidth; + } + + // how many columns? + double dcol = (double)(mViewport.width()+GAP)/(double)(maxw+GAP); + int columns = (int) dcol; + + // lay them out + int mostVisibleChildHeight = -1; + int childTop = 0; + mPageCollectionHeight = 0; + mPageCollectionWidth = 0; + int column = 0; + mBlockRect.setEmpty(); + + for (int i=0; i<getPageCount(); i++) + { + DocPageView cv = (DocPageView)getOrCreateChild(i); + int childWidth = cv.getCalculatedWidth(); + int childHeight = cv.getCalculatedHeight(); + int childLeft = column * (maxw + GAP); + int childRight = childLeft + childWidth; + int childBottom = childTop + childHeight; + mChildRect.set(childLeft, childTop, childRight, childBottom); + + // stash the rect in the page view for later use. + cv.setChildRect(mChildRect); + + // at each layout, we remember the entire width and height of the laid-out + // pages. This is used in applying constraints to scrolling amounts. + if (childBottom> mPageCollectionHeight) + mPageCollectionHeight = childBottom; + if (childRight>mPageCollectionWidth) + mPageCollectionWidth = childRight; + + if (mBlockRect.isEmpty()) + mBlockRect.set(mChildRect); + else + mBlockRect.union(mChildRect); + + if (mChildRect.intersect(mViewport) && i<numDocPages) + { + // visible, so include in layout + if (cv.getParent()==null) + addChildToLayout(cv); + cv.layout(childLeft, childTop, childRight, childBottom); + cv.invalidate(); + } + else + { + // not visible, so remove from layout + removeViewInLayout(cv); + } + + column++; + if (column >= columns) { + column = 0; + childTop += childHeight; + childTop += GAP; + } + } + + // if the number of columns has changed, do some scrolling to adjust + if (mScaling && columns>=1 && mLastLayoutColumns>=1 && mLastLayoutColumns!=columns) + { + // x - center in the viewport + int dx = mBlockRect.centerX() - mViewport.centerX(); + scrollBy(dx,0); + + // y - attempt to keep what's in the center of the viewport in view. + int oldy = mViewport.centerY() - mLastBlockRect.top; + int newy = (int)((float)oldy*mBlockRect.height()/mLastBlockRect.height()); + scrollBy(0,newy-oldy); + } + mLastLayoutColumns = columns; + mLastBlockRect.set(mBlockRect); + + // see if we're handling a start page + handleStartPage(); + + triggerRender(); + } + + // start page, get and set. + private int mStartPage = 0; + public void setStartPage(int page) { + mStartPage = page; + } + protected int getStartPage() {return mStartPage;} + + // handle start page + public void handleStartPage() + { + // if we've been given a start page, go there. + final int start = getStartPage(); + if (start>0) + { + setStartPage(0); // but just once + + // post all of this so that we get an additional layout request + final Handler handler = new Handler(); + handler.post(new Runnable() { + @Override + public void run() { + DocPageView cv = (DocPageView)getOrCreateChild(start-1); + Rect r = cv.getChildRect(); + scrollBy(0,r.top); + requestLayout(); + } + }); + } + } + + // override the view's scrollBy() function so we can + // take the opportunity to apply some constraints + + @Override + public void scrollBy(int dx, int dy) + { + Point p = constrainScrollBy(dx, dy); + super.scrollBy(p.x, p.y); + } + + // apply contraints to every scroll request. + + protected Point constrainScrollBy(int dx, int dy) + { + int vph; + int vpw; + { + Rect viewport = new Rect(); + getGlobalVisibleRect(viewport); + vph = viewport.height(); + vpw = viewport.width(); + } + int sx = getScrollX(); + int sy = getScrollY(); + + if (mPageCollectionWidth <= vpw) + { + // not too far to the right + if (mPageCollectionWidth-sx-dx > vpw) + dx = 0; + + // not too far to the left + if (sx+dx>0) + dx = -sx; + } + else + { + // not too far to the right + if (mPageCollectionWidth < sx+vpw+dx) + dx = 0; + + // not too far to the left + if (sx+dx < 0) + dx = -sx; + } + + if (mPageCollectionHeight <= vph) + { + // not too far down + if (mPageCollectionHeight-sy-dy > vph) + dy = 0; + + // not too far up + if (sy+dy>0) + dy = -sy; + } + else + { + // not too far down + if (sy+dy < 0) + dy = -sy; + + // not too far up. + if (mPageCollectionHeight+2*vph/3 < sy+vph+dy) + dy = 0; + } + + return new Point(dx, dy); + } + + @Override + public Adapter getAdapter() { + return mAdapter; + } + + @Override + public View getSelectedView() { + return null; + } + + @Override + public void setAdapter(Adapter adapter) { + mAdapter = (PageAdapter)adapter; + requestLayout(); + } + + @Override + public void setSelection(int arg0) { + throw new UnsupportedOperationException("setSelection is not supported"); + } + + private View getCached() { + return null; + } + + protected View getOrCreateChild(int i) { + View v = mChildViews.get(i); + if (v == null) { + v = getViewFromAdapter(i); + mChildViews.append(i, v); // Record the view against it's adapter index + onScaleChild(v, mScale); + } + + return v; + } + + protected View getViewFromAdapter(int index) + { + return getAdapter().getView(index, getCached(), this); + } + + private void addChildToLayout(View v) { + LayoutParams params = v.getLayoutParams(); + if (params == null) { + params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + addViewInLayout(v, 0, params, true); + } + + private void triggerRender() + { + // Note that a render is needed + renderRequested = true; + + // If the previous render has completed, start a new one. Otherwise + // a new one will start as soon as the previous one completes. + if (renderCount == 0) + renderPages(); + } + + private void renderPages() + { + renderRequested = false; + + if (mFinished) + return; + + if (bitmaps == null) + return; + + // Rotate to the next bitmap + bitmapIndex++; + if (bitmapIndex>=bitmaps.length) + bitmapIndex = 0; + + // iterate through the children + for (int i = 0; i < getPageCount(); i++) { + + if (mFinished) + return; + + final DocPageView cv = (DocPageView) getOrCreateChild(i); + if (cv.getParent()!=null && cv.isReallyVisible()) { + // Count up as we kick off rendering of each visible page + renderCount++; + cv.render(bitmaps[bitmapIndex], new RenderListener() { + @Override + public void progress(int error) { + + if (error==0) + cv.invalidate(); + + // Count down as they complete + renderCount--; + + if (renderCount==0) { + if (renderRequested) { + // If this phase of rendering has completed and another has + // been requested, start it now + renderPages(); + } + } + } + }); + } + } + } + + @Override + public void run() + { + if (!mScroller.isFinished()) + { + mScroller.computeScrollOffset(); + int x = mScroller.getCurrX(); + int y = mScroller.getCurrY(); + mXScroll += x - mScrollerLastX; + mYScroll += y - mScrollerLastY; + mScrollerLastX = x; + mScrollerLastY = y; + + // limit the amount of repeated layouts. + long tNow = System.currentTimeMillis(); + long diff = tNow - mFlingStartTime; + if (diff>FLING_THROTTLE_TIME) + { + requestLayout(); + mFlingStartTime = tNow; + } + + mStepper.prod(); + } + else + { + // one more + long tNow = System.currentTimeMillis(); + if (tNow != mFlingStartTime) + requestLayout(); + } + } + + public void finish() + { + // we're done with this view. + mFinished = true; + + // first, hide and remove all the children + for (int i=0; i<getPageCount(); i++) + { + DocPageView cv = (DocPageView)getOrCreateChild(i); + cv.setVisibility(GONE); + removeViewInLayout(cv); + cv.finish(); + } + } + + public boolean finished() {return mFinished;} + + protected void smoothScrollBy(int dx, int dy) + { + mScrollerLastX = mScrollerLastY = 0; + mScroller.startScroll(0, 0, dx, dy, 400); + mStepper.prod(); + } + + public void scrollToPage(int pageNumber) + { + // scroll to bring the page into view + + // get current viewport + Rect viewport = new Rect(); + getGlobalVisibleRect(viewport); + + // offset it based on current scroll position + Point viewportOrigin = new Point(); + viewportOrigin.set(getScrollX(), getScrollY()); + viewport.offsetTo(viewportOrigin.x, viewportOrigin.y); + + // get page rect from last layout + DocPageView cv = (DocPageView)getOrCreateChild(pageNumber); + Rect childRect = cv.getChildRect(); + + // scroll + if ((childRect.height()) > viewport.height()) + { + // put the top of the page at the top and the left at 0 + smoothScrollBy(getScrollX(),getScrollY()-childRect.top); + } + else + { + // if the whole page is not visible, move the center of the page at the center + if (childRect.top < viewport.top || childRect.bottom > viewport.bottom) + { + if (childRect.top==0) + smoothScrollBy(0, getScrollY()); + else + smoothScrollBy(0, getScrollY() + viewport.height() / 2 - (childRect.bottom + childRect.top) / 2); + } + } + } + + private Point viewToScreen(Point p) + { + Point newPoint = new Point(p); + + Rect r = new Rect(); + this.getGlobalVisibleRect(r); + + newPoint.offset(r.left, r.top); + + return newPoint; + } + + public void scrollBoxIntoView (int pageNum, RectF box) + { + // get our viewport + Rect viewport = new Rect(); + getGlobalVisibleRect(viewport); + viewport.offset(0,-viewport.top); + + // get the location of the box's lower left corner, + // relative to the viewport + DocPageView cv = (DocPageView)getOrCreateChild(pageNum); + Point point = cv.pageToView((int)box.left,(int)box.bottom); + Rect childRect = cv.getChildRect(); + point.y += childRect.top; + point.y -= getScrollY(); + + // if the point is outside the viewport, scroll so it is. + if (point.y<viewport.top || point.y>viewport.bottom) + { + int diff = (viewport.top + viewport.bottom)/2 - point.y; + smoothScrollBy(0,diff); + } + } } diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/PageAdapter.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/PageAdapter.java index 46a93068..0a1d8c05 100644 --- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/PageAdapter.java +++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/PageAdapter.java @@ -10,52 +10,52 @@ import com.artifex.mupdf.fitz.Document; public class PageAdapter extends BaseAdapter { - private final Context mContext; - private Document mDoc; - - public PageAdapter(Context c) { - mContext = c; - } - - public void setDocument(Document doc) { - mDoc = doc; - } - private int mWidth; - public void setWidth(int w) {mWidth=w;} - - @Override - public int getCount() { - return mDoc.countPages(); - } - - public Object getItem(int position) { - return null; // not used - } - - public long getItemId(int position) { - return 0; // not used - } - - public View getView(final int position, View convertView, ViewGroup parent) - { - // make or reuse a view - DocPageView pageView; - - final Activity activity = (Activity)mContext; - if (convertView == null) - { - // make a new one - pageView = new DocPageView(activity, mDoc); - } - else - { - // reuse an existing one - pageView = (DocPageView) convertView; - } - - // set up the page - pageView.setupPage(position, mWidth, 1); - - return pageView; - } + private final Context mContext; + private Document mDoc; + + public PageAdapter(Context c) { + mContext = c; + } + + public void setDocument(Document doc) { + mDoc = doc; + } + private int mWidth; + public void setWidth(int w) {mWidth=w;} + + @Override + public int getCount() { + return mDoc.countPages(); + } + + public Object getItem(int position) { + return null; // not used + } + + public long getItemId(int position) { + return 0; // not used + } + + public View getView(final int position, View convertView, ViewGroup parent) + { + // make or reuse a view + DocPageView pageView; + + final Activity activity = (Activity)mContext; + if (convertView == null) + { + // make a new one + pageView = new DocPageView(activity, mDoc); + } + else + { + // reuse an existing one + pageView = (DocPageView) convertView; + } + + // set up the page + pageView.setupPage(position, mWidth, 1); + + return pageView; + } } diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/RenderListener.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/RenderListener.java index 7030ef8d..37a3432b 100644 --- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/RenderListener.java +++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/RenderListener.java @@ -2,5 +2,5 @@ package com.artifex.mupdf.android; public interface RenderListener { - void progress(int error); -}
\ No newline at end of file + void progress(int error); +} diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Stepper.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Stepper.java index 8a6b29a5..9275cf99 100644 --- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Stepper.java +++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Stepper.java @@ -5,38 +5,38 @@ import android.os.Build; import android.view.View; public class Stepper { - private final View mPoster; - private final Runnable mTask; - private boolean mPending; + private final View mPoster; + private final Runnable mTask; + private boolean mPending; - public Stepper(View v, Runnable r) { - mPoster = v; - mTask = r; - mPending = false; - } + public Stepper(View v, Runnable r) { + mPoster = v; + mTask = r; + mPending = false; + } - @SuppressLint("NewApi") - public void prod() { - if (!mPending) { - mPending = true; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - mPoster.postOnAnimation(new Runnable() { - @Override - public void run() { - mPending = false; - mTask.run(); - } - }); - } else { - mPoster.post(new Runnable() { - @Override - public void run() { - mPending = false; - mTask.run(); - } - }); + @SuppressLint("NewApi") + public void prod() { + if (!mPending) { + mPending = true; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mPoster.postOnAnimation(new Runnable() { + @Override + public void run() { + mPending = false; + mTask.run(); + } + }); + } else { + mPoster.post(new Runnable() { + @Override + public void run() { + mPending = false; + mTask.run(); + } + }); - } - } - } + } + } + } } |