summaryrefslogtreecommitdiff
path: root/android/src/com/artifex/mupdfdemo
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2013-01-23 16:33:41 +0000
committerRobin Watts <robin.watts@artifex.com>2013-01-24 14:33:43 +0000
commit6cd85db89ff9a95c1d9833a0ec20ddc65adb98c4 (patch)
tree004bc9c4f20c2104e4ed5771ed20ad355f28e977 /android/src/com/artifex/mupdfdemo
parent0e9c3462598dedc22ba7520865c83d29ffb160b9 (diff)
downloadmupdf-6cd85db89ff9a95c1d9833a0ec20ddc65adb98c4.tar.xz
Android: Implement 'Smart Motion'.
Currently, when the edges of the screen are tapped, we move just enough to bring the next/previous page onto the screen. When we are zoomed out, this is exactly what we want. When we are zoomed in, however, it rarely is, as 1) it doesn't allow for their being more content on the same page that we might want to view, and 2) it doesn't take us the the same region of the next page, but rather to the 'nearest edge' of the next page at the same zoom. This is particularly annoying as if we accidentally hit 'right', we can't then hit 'left' to go back to where we were. Smart Motion is an attempt to more neatly advance/retreat through the document in a logical fashion. When we are asked to 'advance', we try to advance down the page by 90% of the screen height; this will bring the next pageful of information into view, allowing for lines that might have been split over the edge of the screen before. If that would take us past the bottom of the page, we just move to the bottom of the page. If we are already at the bottom of the page, however, we consider advancing to the next 'column' of content on this page (i.e. we look to see if we have another screenful of content to the right of this one). If we do, we move to the top of the same page, at the top. If we don't, we move to the next page. When we move to the next page, we always move to the top, but our X position is chosen to match the first column of text (as calculated from the current screen position) on the next page. We assign the right hand side and bottom edges of the screen to 'advance', and the left and top edges to 'retreat'. Common use cases: * When the screen is zoomed out, left/right act precisely as they always have. * When the screen is zoomed in to avoid the margins of the page, left/right jump to the next/previous pages, with the same zoom, an improvement on the current mechanism. * When the screen is zoomed in to view a portrait file in landscape mode so that a line of text just fills the screen, left/right move nicely down the page a screenful at a time, and advances to do the same on the next page. We had no way of doing this before. * When the page is in 2 (or more) columns, and the user zooms to fit a single column in, advance nicely follows the flow across multiple pages. We had no way to do this before.
Diffstat (limited to 'android/src/com/artifex/mupdfdemo')
-rw-r--r--android/src/com/artifex/mupdfdemo/MuPDFActivity.java8
-rw-r--r--android/src/com/artifex/mupdfdemo/ReaderView.java151
2 files changed, 157 insertions, 2 deletions
diff --git a/android/src/com/artifex/mupdfdemo/MuPDFActivity.java b/android/src/com/artifex/mupdfdemo/MuPDFActivity.java
index bfe0e135..f41b6178 100644
--- a/android/src/com/artifex/mupdfdemo/MuPDFActivity.java
+++ b/android/src/com/artifex/mupdfdemo/MuPDFActivity.java
@@ -393,9 +393,13 @@ public class MuPDFActivity extends Activity
}
});
} else if (e.getX() < tapPageMargin) {
- super.moveToPrevious();
+ super.smartMoveBackwards();
} else if (e.getX() > super.getWidth()-tapPageMargin) {
- super.moveToNext();
+ super.smartMoveForwards();
+ } else if (e.getY() < tapPageMargin) {
+ super.smartMoveBackwards();
+ } else if (e.getY() > super.getHeight()-tapPageMargin) {
+ super.smartMoveForwards();
} else if (!mButtonsVisible) {
showButtons();
} else {
diff --git a/android/src/com/artifex/mupdfdemo/ReaderView.java b/android/src/com/artifex/mupdfdemo/ReaderView.java
index 16ac1129..6a01e288 100644
--- a/android/src/com/artifex/mupdfdemo/ReaderView.java
+++ b/android/src/com/artifex/mupdfdemo/ReaderView.java
@@ -105,6 +105,157 @@ public class ReaderView extends AdapterView<Adapter>
slideViewOntoScreen(v);
}
+ public void smartMoveForwards() {
+ View v = mChildViews.get(mCurrent);
+ if (v == null)
+ return;
+
+ // The following code works in terms of where the screen is on the views;
+ // so for example, if the currentView is at (-100,-100), the visible
+ // region would be at (100,100). If the previous page was (2000, 3000) in
+ // size, the visible region of the previous page might be (2100 + GAP, 100)
+ // (i.e. off the previous page). This is different to the way the rest of
+ // the code in this file is written, but it's easier for me to think about.
+ // At some point we may refactor this to fit better with the rest of the
+ // code.
+
+ // screenWidth/Height are the actual width/height of the screen. e.g. 480/800
+ int screenWidth = getWidth();
+ int screenHeight = getHeight();
+ // We might be mid scroll; we want to calculate where we scroll to based on
+ // where this scroll would end, not where we are now (to allow for people
+ // bashing 'forwards' very fast.
+ int remainingX = mScroller.getFinalX() - mScroller.getCurrX();
+ int remainingY = mScroller.getFinalY() - mScroller.getCurrY();
+ // right/bottom is in terms of pixels within the scaled document; e.g. 1000
+ int top = -(v.getTop() + mYScroll + remainingY);
+ int right = screenWidth -(v.getLeft() + mXScroll + remainingX);
+ int bottom = screenHeight+top;
+ // docWidth/Height are the width/height of the scaled document e.g. 2000x3000
+ int docWidth = v.getMeasuredWidth();
+ int docHeight = v.getMeasuredHeight();
+
+ int xOffset, yOffset;
+ if (bottom >= docHeight) {
+ // We are flush with the bottom. Advance to next column.
+ if (right + screenWidth > docWidth) {
+ // No room for another column - go to next page
+ View nv = mChildViews.get(mCurrent+1);
+ if (nv == null) // No page to advance to
+ return;
+ int nextTop = -(nv.getTop() + mYScroll + remainingY);
+ int nextLeft = -(nv.getLeft() + mXScroll + remainingX);
+ int nextDocWidth = nv.getMeasuredWidth();
+ int nextDocHeight = nv.getMeasuredHeight();
+
+ // Allow for the next page maybe being shorter than the screen is high
+ yOffset = (nextDocHeight < screenHeight ? ((nextDocHeight - screenHeight)>>1) : 0);
+
+ if (nextDocWidth < screenWidth) {
+ // Next page is too narrow to fill the screen. Scroll to the top, centred.
+ xOffset = (nextDocWidth - screenWidth)>>1;
+ } else {
+ // Reset X back to the left hand column
+ xOffset = right % screenWidth;
+ // Adjust in case the previous page is less wide
+ if (xOffset + screenWidth > nextDocWidth)
+ xOffset = nextDocWidth - screenWidth;
+ }
+ xOffset -= nextLeft;
+ yOffset -= nextTop;
+ } else {
+ // Move to top of next column
+ xOffset = screenWidth;
+ yOffset = screenHeight - bottom;
+ }
+ } else {
+ // Advance by 90% of the screen height downwards (in case lines are partially cut off)
+ xOffset = 0;
+ yOffset = (int)(screenHeight * 0.9 + 0.5);
+ if (yOffset + bottom > docHeight)
+ yOffset = docHeight - bottom;
+ }
+ mScrollerLastX = mScrollerLastY = 0;
+ mScroller.startScroll(0, 0, remainingX - xOffset, remainingY - yOffset, 400);
+ post(this);
+ }
+
+ public void smartMoveBackwards() {
+ View v = mChildViews.get(mCurrent);
+ if (v == null)
+ return;
+
+ // The following code works in terms of where the screen is on the views;
+ // so for example, if the currentView is at (-100,-100), the visible
+ // region would be at (100,100). If the previous page was (2000, 3000) in
+ // size, the visible region of the previous page might be (2100 + GAP, 100)
+ // (i.e. off the previous page). This is different to the way the rest of
+ // the code in this file is written, but it's easier for me to think about.
+ // At some point we may refactor this to fit better with the rest of the
+ // code.
+
+ // screenWidth/Height are the actual width/height of the screen. e.g. 480/800
+ int screenWidth = getWidth();
+ int screenHeight = getHeight();
+ // We might be mid scroll; we want to calculate where we scroll to based on
+ // where this scroll would end, not where we are now (to allow for people
+ // bashing 'forwards' very fast.
+ int remainingX = mScroller.getFinalX() - mScroller.getCurrX();
+ int remainingY = mScroller.getFinalY() - mScroller.getCurrY();
+ // left/top is in terms of pixels within the scaled document; e.g. 1000
+ int left = -(v.getLeft() + mXScroll + remainingX);
+ int top = -(v.getTop() + mYScroll + remainingY);
+ // docWidth/Height are the width/height of the scaled document e.g. 2000x3000
+ int docWidth = v.getMeasuredWidth();
+ int docHeight = v.getMeasuredHeight();
+
+ int xOffset, yOffset;
+ if (top <= 0) {
+ // We are flush with the top. Step back to previous column.
+ if (left < screenWidth) {
+ /* No room for previous column - go to previous page */
+ View pv = mChildViews.get(mCurrent-1);
+ if (pv == null) /* No page to advance to */
+ return;
+ int prevDocWidth = pv.getMeasuredWidth();
+ int prevDocHeight = pv.getMeasuredHeight();
+
+ // Allow for the next page maybe being shorter than the screen is high
+ yOffset = (prevDocHeight < screenHeight ? ((prevDocHeight - screenHeight)>>1) : 0);
+
+ int prevLeft = -(pv.getLeft() + mXScroll);
+ int prevTop = -(pv.getTop() + mYScroll);
+ if (prevDocWidth < screenWidth) {
+ // Previous page is too narrow to fill the screen. Scroll to the bottom, centred.
+ xOffset = (prevDocWidth - screenWidth)>>1;
+ } else {
+ // Reset X back to the right hand column
+ xOffset = (left > 0 ? left % screenWidth : 0);
+ if (xOffset + screenWidth > prevDocWidth)
+ xOffset = prevDocWidth - screenWidth;
+ while (xOffset + screenWidth*2 < prevDocWidth)
+ xOffset += screenWidth;
+ yOffset -= screenHeight-prevDocHeight;
+ }
+ xOffset -= prevLeft;
+ yOffset -= prevTop;
+ } else {
+ // Move to bottom of previous column
+ xOffset = -screenWidth;
+ yOffset = docHeight - screenHeight + top;
+ }
+ } else {
+ // Retreat by 90% of the screen height downwards (in case lines are partially cut off)
+ xOffset = 0;
+ yOffset = - (int)(screenHeight * 0.9 + 0.5);
+ if (yOffset < -top)
+ yOffset = -top;
+ }
+ mScrollerLastX = mScrollerLastY = 0;
+ mScroller.startScroll(0, 0, remainingX - xOffset, remainingY - yOffset, 400);
+ post(this);
+ }
+
public void resetupChildren() {
for (int i = 0; i < mChildViews.size(); i++)
onChildSetup(mChildViews.keyAt(i), mChildViews.valueAt(i));