summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.com>2014-09-18 22:22:08 +0200
committerTomaž Vajngerl <tomaz.vajngerl@collabora.com>2014-09-18 23:05:04 +0200
commitf02cef962b4f930eb5637dea1bb9d8c382839286 (patch)
tree791605f435d03b90466c2798e720c0bd581c2e65
parent046e19ef2cb02d11560b5812364aa5211b20e4ac (diff)
android: convert to ImmutableViewportMetrics
Change-Id: Idd5e604541577f6b812a971e585cee9b089d2b4b
-rw-r--r--android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java7
-rw-r--r--android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java4
-rw-r--r--android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java97
-rw-r--r--android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java2
-rw-r--r--android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java173
-rw-r--r--android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java653
6 files changed, 434 insertions, 502 deletions
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index c5918e69b9ce..c12170fa7fb4 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -7,6 +7,7 @@ import android.util.Log;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.GeckoLayerClient;
+import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.gfx.SubTile;
import org.mozilla.gecko.gfx.ViewportMetrics;
@@ -29,9 +30,9 @@ public class LOKitThread extends Thread {
mInputFile = inputFile;
}
- RectF normlizeRect(ViewportMetrics metrics) {
+ RectF normlizeRect(ImmutableViewportMetrics metrics) {
RectF rect = metrics.getViewport();
- float zoomFactor = metrics.getZoomFactor();
+ float zoomFactor = metrics.zoomFactor;
return new RectF(rect.left / zoomFactor, rect.top / zoomFactor, rect.right / zoomFactor, rect.bottom / zoomFactor);
}
@@ -68,7 +69,7 @@ public class LOKitThread extends Thread {
GeckoLayerClient layerClient = mApplication.getLayerClient();
layerClient.beginDrawing(mViewportMetrics);
- ViewportMetrics metrics = mApplication.getLayerController().getViewportMetrics();
+ ImmutableViewportMetrics metrics = mApplication.getLayerController().getViewportMetrics();
RectF viewport = normlizeRect(metrics);
Rect rect = inflate(roundToTileSize(viewport, TILE_SIZE), TILE_SIZE);
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
index 1ebb9a1b528f..e7a9059a953b 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -149,7 +149,7 @@ public class GeckoLayerClient {
mGeckoViewport = mNewGeckoViewport;
mGeckoViewport.setSize(viewportSize);
- PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
+ PointF displayportOrigin = mGeckoViewport.getOrigin();
RectF position = mGeckoViewport.getViewport();
mTileLayer.setPosition(RectUtils.round(position));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
@@ -288,7 +288,7 @@ public class GeckoLayerClient {
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
- mDisplayPort = calculateDisplayPort(new ImmutableViewportMetrics(mLayerController.getViewportMetrics()));
+ mDisplayPort = calculateDisplayPort(mLayerController.getViewportMetrics());
LOKitShell.sendEvent(LOEvent.viewport(viewportMetrics));
if (mViewportSizeChanged) {
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
index 9c497f72fa0e..e9d0cb6188c0 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerController.java
@@ -70,7 +70,20 @@ public class LayerController {
private Layer mRootLayer; /* The root layer. */
private LayerView mView; /* The main rendering view. */
private Context mContext; /* The current context. */
- private ViewportMetrics mViewportMetrics; /* The current viewport metrics. */
+
+ /* This is volatile so that we can read and write to it from different threads.
+ * We avoid synchronization to make getting the viewport metrics from
+ * the compositor as cheap as possible. The viewport is immutable so
+ * we don't need to worry about anyone mutating it while we're reading from it.
+ * Specifically:
+ * 1) reading mViewportMetrics from any thread is fine without synchronization
+ * 2) writing to mViewportMetrics requires synchronizing on the layer controller object
+ * 3) whenver reading multiple fields from mViewportMetrics without synchronization (i.e. in
+ * case 1 above) you should always frist grab a local copy of the reference, and then use
+ * that because mViewportMetrics might get reassigned in between reading the different
+ * fields. */
+ private volatile ImmutableViewportMetrics mViewportMetrics; /* The current viewport metrics. */
+
private boolean mWaitForTouchListeners;
private PanZoomController mPanZoomController;
@@ -84,7 +97,7 @@ public class LayerController {
/* The new color for the checkerboard. */
private int mCheckerboardColor;
- private boolean mCheckerboardShouldShowChecks;
+ private boolean mCheckerboardShouldShowChecks = true;
private boolean mForceRedraw;
@@ -113,10 +126,9 @@ public class LayerController {
mContext = context;
mForceRedraw = true;
- mViewportMetrics = new ViewportMetrics();
+ mViewportMetrics = new ImmutableViewportMetrics(new ViewportMetrics());
mPanZoomController = new PanZoomController(this);
mView = new LayerView(context, this);
- mCheckerboardShouldShowChecks = true;
}
public void onDestroy() {
@@ -136,7 +148,7 @@ public class LayerController {
public Layer getRoot() { return mRootLayer; }
public LayerView getView() { return mView; }
public Context getContext() { return mContext; }
- public ViewportMetrics getViewportMetrics() { return mViewportMetrics; }
+ public ImmutableViewportMetrics getViewportMetrics() { return mViewportMetrics; }
public RectF getViewport() {
return mViewportMetrics.getViewport();
@@ -155,7 +167,7 @@ public class LayerController {
}
public float getZoomFactor() {
- return mViewportMetrics.getZoomFactor();
+ return mViewportMetrics.zoomFactor;
}
public Bitmap getBackgroundPattern() { return getDrawable("background"); }
@@ -185,13 +197,49 @@ public class LayerController {
* result in an infinite loop.
*/
public void setViewportSize(FloatSize size) {
+ // Resize the viewport, and modify its zoom factor so that the page retains proportionally
+ // zoomed relative to the screen.
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
+ float oldHeight = viewportMetrics.getSize().height;
+ float oldWidth = viewportMetrics.getSize().width;
+ float oldZoomFactor = viewportMetrics.getZoomFactor();
viewportMetrics.setSize(size);
- mViewportMetrics = new ViewportMetrics(viewportMetrics);
+
+ // if the viewport got larger (presumably because the vkb went away), and the page
+ // is smaller than the new viewport size, increase the page size so that the panzoomcontroller
+ // doesn't zoom in to make it fit (bug 718270). this page size change is in anticipation of
+ // gecko increasing the page size to match the new viewport size, which will happen the next
+ // time we get a draw update.
+ if (size.width >= oldWidth && size.height >= oldHeight) {
+ FloatSize pageSize = viewportMetrics.getPageSize();
+ if (pageSize.width < size.width || pageSize.height < size.height) {
+ viewportMetrics.setPageSize(new FloatSize(Math.max(pageSize.width, size.width),
+ Math.max(pageSize.height, size.height)));
+ }
+ }
+
+ // For rotations, we want the focus point to be at the top left.
+ boolean rotation = (size.width > oldWidth && size.height < oldHeight) ||
+ (size.width < oldWidth && size.height > oldHeight);
+ PointF newFocus;
+ if (rotation) {
+ newFocus = new PointF(0, 0);
+ } else {
+ newFocus = new PointF(size.width / 2.0f, size.height / 2.0f);
+ }
+ float newZoomFactor = size.width * oldZoomFactor / oldWidth;
+ viewportMetrics.scaleTo(newZoomFactor, newFocus);
+ mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
+
+ setForceRedraw();
if (mLayerClient != null) {
mLayerClient.viewportSizeChanged();
+ notifyLayerClientOfGeometryChange();
}
+
+ mPanZoomController.abortAnimation();
+ mView.requestRender();
}
/** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
@@ -200,7 +248,7 @@ public class LayerController {
PointF origin = viewportMetrics.getOrigin();
origin.offset(point.x, point.y);
viewportMetrics.setOrigin(origin);
- mViewportMetrics = new ViewportMetrics(viewportMetrics);
+ mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
notifyLayerClientOfGeometryChange();
mView.requestRender();
@@ -213,7 +261,7 @@ public class LayerController {
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
viewportMetrics.setPageSize(size);
- mViewportMetrics = new ViewportMetrics(viewportMetrics);
+ mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
// Page size is owned by the layer client, so no need to notify it of
// this change.
@@ -233,7 +281,7 @@ public class LayerController {
* while calling this.
*/
public void setViewportMetrics(ViewportMetrics viewport) {
- mViewportMetrics = new ViewportMetrics(viewport);
+ mViewportMetrics = new ImmutableViewportMetrics(viewport);
Log.d(LOGTAG, "setViewportMetrics: " + mViewportMetrics);
mView.requestRender();
}
@@ -245,7 +293,7 @@ public class LayerController {
public void scaleWithFocus(float zoomFactor, PointF focus) {
ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
viewportMetrics.scaleTo(zoomFactor, focus);
- mViewportMetrics = new ViewportMetrics(viewportMetrics);
+ mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
Log.d(LOGTAG, "scaleWithFocus: " + mViewportMetrics + "; zf=" + zoomFactor);
// We assume the zoom level will only be modified by the
@@ -317,31 +365,26 @@ public class LayerController {
* Converts a point from layer view coordinates to layer coordinates. In other words, given a
* point measured in pixels from the top left corner of the layer view, returns the point in
* pixels measured from the top left corner of the root layer, in the coordinate system of the
- * layer itself (CSS pixels). This method is used as part of the process of translating touch
- * events to Gecko's coordinate system.
+ * layer itself. This method is used by the viewport controller as part of the process of
+ * translating touch events to Gecko's coordinate system.
*/
public PointF convertViewPointToLayerPoint(PointF viewPoint) {
if (mRootLayer == null)
return null;
- ViewportMetrics viewportMetrics = mViewportMetrics;
+ ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
+ // Undo the transforms.
PointF origin = viewportMetrics.getOrigin();
PointF newPoint = new PointF(origin.x, origin.y);
- float zoom = viewportMetrics.getZoomFactor();
+ float zoom = viewportMetrics.zoomFactor;
+ viewPoint.x /= zoom;
+ viewPoint.y /= zoom;
+ newPoint.offset(viewPoint.x, viewPoint.y);
Rect rootPosition = mRootLayer.getPosition();
- float rootScale = mRootLayer.getResolution();
-
- // viewPoint + origin gives the coordinate in device pixels from the top-left corner of the page.
- // Divided by zoom, this gives us the coordinate in CSS pixels from the top-left corner of the page.
- // rootPosition / rootScale is where Gecko thinks it is (scrollTo position) in CSS pixels from
- // the top-left corner of the page. Subtracting the two gives us the offset of the viewPoint from
- // the current Gecko coordinate in CSS pixels.
- PointF layerPoint = new PointF(
- ((viewPoint.x + origin.x) / zoom) - (rootPosition.left / rootScale),
- ((viewPoint.y + origin.y) / zoom) - (rootPosition.top / rootScale));
-
- return layerPoint;
+ newPoint.offset(-rootPosition.left, -rootPosition.top);
+
+ return newPoint;
}
/*
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
index 44973bf57b29..6ff9a8a857bd 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
@@ -286,7 +286,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
* Called whenever a new frame is about to be drawn.
*/
public void onDrawFrame(GL10 gl) {
- Frame frame = createFrame(new ImmutableViewportMetrics(mView.getController().getViewportMetrics()));
+ Frame frame = createFrame(mView.getController().getViewportMetrics());
synchronized (mView.getController()) {
frame.beginDrawing();
frame.drawBackground();
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java
index d98b47421520..8c3004eebf83 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ViewportMetrics.java
@@ -53,14 +53,10 @@ import org.mozilla.gecko.util.FloatUtils;
*/
public class ViewportMetrics {
private static final String LOGTAG = "GeckoViewportMetrics";
- private static final float MAX_BIAS = 0.8f;
+
private FloatSize mPageSize;
private RectF mViewportRect;
- private PointF mViewportOffset;
private float mZoomFactor;
- // A scale from -1,-1 to 1,1 that represents what edge of the displayport
- // we want the viewport to be biased towards.
- private PointF mViewportBias;
public ViewportMetrics() {
DisplayMetrics metrics = new DisplayMetrics();
@@ -68,119 +64,52 @@ public class ViewportMetrics {
mPageSize = new FloatSize(metrics.widthPixels, metrics.heightPixels);
mViewportRect = new RectF(0, 0, metrics.widthPixels, metrics.heightPixels);
- mViewportOffset = new PointF(0, 0);
mZoomFactor = 1.0f;
- mViewportBias = new PointF(0.0f, 0.0f);
}
public ViewportMetrics(ViewportMetrics viewport) {
mPageSize = new FloatSize(viewport.getPageSize());
mViewportRect = new RectF(viewport.getViewport());
- PointF offset = viewport.getViewportOffset();
- mViewportOffset = new PointF(offset.x, offset.y);
mZoomFactor = viewport.getZoomFactor();
- mViewportBias = viewport.mViewportBias;
}
+ public ViewportMetrics(ImmutableViewportMetrics viewport) {
+ mPageSize = new FloatSize(viewport.pageSizeWidth, viewport.pageSizeHeight);
+ mViewportRect = new RectF(viewport.viewportRectLeft,
+ viewport.viewportRectTop,
+ viewport.viewportRectRight,
+ viewport.viewportRectBottom);
+ mZoomFactor = viewport.zoomFactor;
+ }
+
+
public ViewportMetrics(JSONObject json) throws JSONException {
- float x = (float) json.getDouble("x");
- float y = (float) json.getDouble("y");
- float width = (float) json.getDouble("width");
- float height = (float) json.getDouble("height");
- float pageWidth = (float) json.getDouble("pageWidth");
- float pageHeight = (float) json.getDouble("pageHeight");
- float offsetX = (float) json.getDouble("offsetX");
- float offsetY = (float) json.getDouble("offsetY");
- float zoom = (float) json.getDouble("zoom");
+ float x = (float)json.getDouble("x");
+ float y = (float)json.getDouble("y");
+ float width = (float)json.getDouble("width");
+ float height = (float)json.getDouble("height");
+ float pageWidth = (float)json.getDouble("pageWidth");
+ float pageHeight = (float)json.getDouble("pageHeight");
+ float zoom = (float)json.getDouble("zoom");
mPageSize = new FloatSize(pageWidth, pageHeight);
mViewportRect = new RectF(x, y, x + width, y + height);
- mViewportOffset = new PointF(offsetX, offsetY);
mZoomFactor = zoom;
- mViewportBias = new PointF(0.0f, 0.0f);
- }
-
- public PointF getOptimumViewportOffset(IntSize displayportSize) {
- /* XXX Until bug #524925 is fixed, changing the viewport origin will
- * cause unnecessary relayouts. This may cause rendering time to
- * increase and should be considered.
- */
- RectF viewport = getClampedViewport();
-
- FloatSize bufferSpace = new FloatSize(displayportSize.width - viewport.width(),
- displayportSize.height - viewport.height());
- PointF optimumOffset =
- new PointF(bufferSpace.width * ((mViewportBias.x + 1.0f) / 2.0f),
- bufferSpace.height * ((mViewportBias.y + 1.0f) / 2.0f));
-
- // Make sure this offset won't cause wasted pixels in the displayport
- // (i.e. make sure the resultant displayport intersects with the page
- // as much as possible)
- if (viewport.left - optimumOffset.x < 0)
- optimumOffset.x = viewport.left;
- else if ((bufferSpace.width - optimumOffset.x) + viewport.right > mPageSize.width)
- optimumOffset.x = bufferSpace.width - (mPageSize.width - viewport.right);
-
- if (viewport.top - optimumOffset.y < 0)
- optimumOffset.y = viewport.top;
- else if ((bufferSpace.height - optimumOffset.y) + viewport.bottom > mPageSize.height)
- optimumOffset.y = bufferSpace.height - (mPageSize.height - viewport.bottom);
-
- return new PointF(Math.round(optimumOffset.x), Math.round(optimumOffset.y));
}
public PointF getOrigin() {
return new PointF(mViewportRect.left, mViewportRect.top);
}
- public void setOrigin(PointF origin) {
- // When the origin is set, we compare it with the last value set and
- // change the viewport bias accordingly, so that any viewport based
- // on these metrics will have a larger buffer in the direction of
- // movement.
-
- // XXX Note the comment about bug #524925 in getOptimumViewportOffset.
- // Ideally, the viewport bias would be a sliding scale, but we
- // don't want to change it too often at the moment.
- if (FloatUtils.fuzzyEquals(origin.x, mViewportRect.left))
- mViewportBias.x = 0;
- else
- mViewportBias.x = ((mViewportRect.left - origin.x) > 0) ? MAX_BIAS : -MAX_BIAS;
- if (FloatUtils.fuzzyEquals(origin.y, mViewportRect.top))
- mViewportBias.y = 0;
- else
- mViewportBias.y = ((mViewportRect.top - origin.y) > 0) ? MAX_BIAS : -MAX_BIAS;
-
- mViewportRect.set(origin.x, origin.y,
- origin.x + mViewportRect.width(),
- origin.y + mViewportRect.height());
- }
-
- public PointF getDisplayportOrigin() {
- return new PointF(mViewportRect.left - mViewportOffset.x,
- mViewportRect.top - mViewportOffset.y);
- }
-
public FloatSize getSize() {
return new FloatSize(mViewportRect.width(), mViewportRect.height());
}
- public void setSize(FloatSize size) {
- mViewportRect.right = mViewportRect.left + size.width;
- mViewportRect.bottom = mViewportRect.top + size.height;
- }
-
public RectF getViewport() {
return mViewportRect;
}
- public void setViewport(RectF viewport) {
- mViewportRect = viewport;
- }
-
- /**
- * Returns the viewport rectangle, clamped within the page-size.
- */
+ /** Returns the viewport rectangle, clamped within the page-size. */
public RectF getClampedViewport() {
RectF clampedViewport = new RectF(mViewportRect);
@@ -200,24 +129,31 @@ public class ViewportMetrics {
return clampedViewport;
}
- public PointF getViewportOffset() {
- return mViewportOffset;
- }
-
- public void setViewportOffset(PointF offset) {
- mViewportOffset = offset;
- }
-
public FloatSize getPageSize() {
return mPageSize;
}
+ public float getZoomFactor() {
+ return mZoomFactor;
+ }
+
public void setPageSize(FloatSize pageSize) {
mPageSize = pageSize;
}
- public float getZoomFactor() {
- return mZoomFactor;
+ public void setViewport(RectF viewport) {
+ mViewportRect = viewport;
+ }
+
+ public void setOrigin(PointF origin) {
+ mViewportRect.set(origin.x, origin.y,
+ origin.x + mViewportRect.width(),
+ origin.y + mViewportRect.height());
+ }
+
+ public void setSize(FloatSize size) {
+ mViewportRect.right = mViewportRect.left + size.width;
+ mViewportRect.bottom = mViewportRect.top + size.height;
}
public void setZoomFactor(float zoomFactor) {
@@ -240,15 +176,6 @@ public class ViewportMetrics {
setOrigin(origin);
mZoomFactor = newZoomFactor;
-
- // Similar to setOrigin, set the viewport bias based on the focal point
- // of the zoom so that a viewport based on these metrics will have a
- // larger buffer based on the direction of movement when scaling.
- //
- // This is biased towards scaling outwards, as zooming in doesn't
- // really require a viewport bias.
- mViewportBias.set(((focus.x / mViewportRect.width()) * (2.0f * MAX_BIAS)) - MAX_BIAS,
- ((focus.y / mViewportRect.height()) * (2.0f * MAX_BIAS)) - MAX_BIAS);
}
/*
@@ -261,15 +188,13 @@ public class ViewportMetrics {
result.mPageSize = mPageSize.interpolate(to.mPageSize, t);
result.mZoomFactor = FloatUtils.interpolate(mZoomFactor, to.mZoomFactor, t);
result.mViewportRect = RectUtils.interpolate(mViewportRect, to.mViewportRect, t);
- result.mViewportOffset = PointUtils.interpolate(mViewportOffset, to.mViewportOffset, t);
return result;
}
public boolean fuzzyEquals(ViewportMetrics other) {
return mPageSize.fuzzyEquals(other.mPageSize)
- && RectUtils.fuzzyEquals(mViewportRect, other.mViewportRect)
- && FloatUtils.fuzzyEquals(mViewportOffset, other.mViewportOffset)
- && FloatUtils.fuzzyEquals(mZoomFactor, other.mZoomFactor);
+ && RectUtils.fuzzyEquals(mViewportRect, other.mViewportRect)
+ && FloatUtils.fuzzyEquals(mZoomFactor, other.mZoomFactor);
}
public String toJSON() {
@@ -280,15 +205,13 @@ public class ViewportMetrics {
StringBuffer sb = new StringBuffer(256);
sb.append("{ \"x\" : ").append(mViewportRect.left)
- .append(", \"y\" : ").append(mViewportRect.top)
- .append(", \"width\" : ").append(width)
- .append(", \"height\" : ").append(height)
- .append(", \"pageWidth\" : ").append(mPageSize.width)
- .append(", \"pageHeight\" : ").append(mPageSize.height)
- .append(", \"offsetX\" : ").append(mViewportOffset.x)
- .append(", \"offsetY\" : ").append(mViewportOffset.y)
- .append(", \"zoom\" : ").append(mZoomFactor)
- .append(" }");
+ .append(", \"y\" : ").append(mViewportRect.top)
+ .append(", \"width\" : ").append(width)
+ .append(", \"height\" : ").append(height)
+ .append(", \"pageWidth\" : ").append(mPageSize.width)
+ .append(", \"pageHeight\" : ").append(mPageSize.height)
+ .append(", \"zoom\" : ").append(mZoomFactor)
+ .append(" }");
return sb.toString();
}
@@ -296,10 +219,8 @@ public class ViewportMetrics {
public String toString() {
StringBuffer buff = new StringBuffer(128);
buff.append("v=").append(mViewportRect.toString())
- .append(" p=").append(mPageSize.toString())
- .append(" z=").append(mZoomFactor)
- .append(" o=").append(mViewportOffset.x)
- .append(',').append(mViewportOffset.y);
+ .append(" p=").append(mPageSize.toString())
+ .append(" z=").append(mZoomFactor);
return buff.toString();
}
}
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java
index 7d15bdb09728..1eb033145fb0 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ui/PanZoomController.java
@@ -61,45 +61,69 @@ import java.util.TimerTask;
* Many ideas are from Joe Hewitt's Scrollability:
* https://github.com/joehewitt/scrollability/
*/
-public class PanZoomController extends GestureDetector.SimpleOnGestureListener implements SimpleScaleGestureDetector.SimpleScaleGestureListener {
- // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans
- // between the touch-down and touch-up of a click). In units of density-independent pixels.
- public static final float PAN_THRESHOLD = 1 / 16f * LOKitShell.getDpi();
+public class PanZoomController
+ extends GestureDetector.SimpleOnGestureListener
+ implements SimpleScaleGestureDetector.SimpleScaleGestureListener
+{
private static final String LOGTAG = "GeckoPanZoomController";
+
+
// Animation stops if the velocity is below this value when overscrolled or panning.
private static final float STOPPED_THRESHOLD = 4.0f;
+
// Animation stops is the velocity is below this threshold when flinging.
private static final float FLING_STOPPED_THRESHOLD = 0.1f;
+
+ // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans
+ // between the touch-down and touch-up of a click). In units of density-independent pixels.
+ public static final float PAN_THRESHOLD = 1/16f * LOKitShell.getDpi();
+
// Angle from axis within which we stay axis-locked
private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees
+
// The maximum amount we allow you to zoom into a page
private static final float MAX_ZOOM = 4.0f;
+
/* 16 precomputed frames of the _ease-out_ animation from the CSS Transitions specification. */
private static final float[] EASE_OUT_ANIMATION_FRAMES = {
- 0.00000f, /* 0 */
- 0.10211f, /* 1 */
- 0.19864f, /* 2 */
- 0.29043f, /* 3 */
- 0.37816f, /* 4 */
- 0.46155f, /* 5 */
- 0.54054f, /* 6 */
- 0.61496f, /* 7 */
- 0.68467f, /* 8 */
- 0.74910f, /* 9 */
- 0.80794f, /* 10 */
- 0.86069f, /* 11 */
- 0.90651f, /* 12 */
- 0.94471f, /* 13 */
- 0.97401f, /* 14 */
- 0.99309f, /* 15 */
+ 0.00000f, /* 0 */
+ 0.10211f, /* 1 */
+ 0.19864f, /* 2 */
+ 0.29043f, /* 3 */
+ 0.37816f, /* 4 */
+ 0.46155f, /* 5 */
+ 0.54054f, /* 6 */
+ 0.61496f, /* 7 */
+ 0.68467f, /* 8 */
+ 0.74910f, /* 9 */
+ 0.80794f, /* 10 */
+ 0.86069f, /* 11 */
+ 0.90651f, /* 12 */
+ 0.94471f, /* 13 */
+ 0.97401f, /* 14 */
+ 0.99309f, /* 15 */
};
- private static String MESSAGE_ZOOM_RECT = "Browser:ZoomToRect";
- private static String MESSAGE_ZOOM_PAGE = "Browser:ZoomToPageWidth";
+
+ private enum PanZoomState {
+ NOTHING, /* no touch-start events received */
+ FLING, /* all touches removed, but we're still scrolling page */
+ TOUCHING, /* one touch-start event received */
+ PANNING_LOCKED, /* touch-start followed by move (i.e. panning with axis lock) */
+ PANNING, /* panning without axis lock */
+ PANNING_HOLD, /* in panning, but not moving.
+ * similar to TOUCHING but after starting a pan */
+ PANNING_HOLD_LOCKED, /* like PANNING_HOLD, but axis lock still in effect */
+ PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
+ ANIMATED_ZOOM /* animated zoom to a new rect */
+ }
+
private final LayerController mController;
private final SubdocumentScrollHelper mSubscroller;
private final Axis mX;
private final Axis mY;
+
private Thread mMainThread;
+
/* The timer that handles flings or bounces. */
private Timer mAnimationTimer;
/* The runnable being scheduled by the animation timer. */
@@ -118,59 +142,48 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
mY = new AxisY(mSubscroller);
mMainThread = LibreOfficeMainActivity.mAppContext.getMainLooper().getThread();
+ checkMainThread();
mState = PanZoomState.NOTHING;
}
+ // for debugging bug 713011; it can be taken out once that is resolved.
+ private void checkMainThread() {
+ if (mMainThread != Thread.currentThread()) {
+ // log with full stack trace
+ Log.e(LOGTAG, "Uh-oh, we're running on the wrong thread!", new Exception());
+ }
+ }
+
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN:
- return onTouchStart(event);
- case MotionEvent.ACTION_MOVE:
- return onTouchMove(event);
- case MotionEvent.ACTION_UP:
- return onTouchEnd(event);
- case MotionEvent.ACTION_CANCEL:
- return onTouchCancel(event);
- default:
- return false;
+ case MotionEvent.ACTION_DOWN: return onTouchStart(event);
+ case MotionEvent.ACTION_MOVE: return onTouchMove(event);
+ case MotionEvent.ACTION_UP: return onTouchEnd(event);
+ case MotionEvent.ACTION_CANCEL: return onTouchCancel(event);
+ default: return false;
}
}
- /**
- * This function must be called from the UI thread.
- */
+ /** This function must be called from the UI thread. */
public void abortAnimation() {
+ checkMainThread();
// this happens when gecko changes the viewport on us or if the device is rotated.
// if that's the case, abort any animation in progress and re-zoom so that the page
// snaps to edges. for other cases (where the user's finger(s) are down) don't do
// anything special.
- switch (mState) {
- case FLING:
- mX.stopFling();
- mY.stopFling();
- mState = PanZoomState.NOTHING;
- // fall through
- case ANIMATED_ZOOM:
- // the zoom that's in progress likely makes no sense any more (such as if
- // the screen orientation changed) so abort it
- // fall through
- case NOTHING:
- // Don't do animations here; they're distracting and can cause flashes on page
- // transitions.
- mController.setViewportMetrics(getValidViewportMetrics());
- mController.notifyLayerClientOfGeometryChange();
- break;
+ if (mState == PanZoomState.FLING) {
+ mX.stopFling();
+ mY.stopFling();
+ mState = PanZoomState.NOTHING;
}
}
- /**
- * This must be called on the UI thread.
- */
+ /** This must be called on the UI thread. */
public void pageSizeUpdated() {
if (mState == PanZoomState.NOTHING) {
ViewportMetrics validated = getValidViewportMetrics();
- if (!mController.getViewportMetrics().fuzzyEquals(validated)) {
+ if (! (new ViewportMetrics(mController.getViewportMetrics())).fuzzyEquals(validated)) {
// page size changed such that we are now in overscroll. snap to the
// the nearest valid viewport
mController.setViewportMetrics(validated);
@@ -179,116 +192,110 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
}
}
+ /*
+ * Panning/scrolling
+ */
+
private boolean onTouchStart(MotionEvent event) {
- Log.d(LOGTAG, "onTouchStart in state " + mState);
// user is taking control of movement, so stop
// any auto-movement we have going
stopAnimationTimer();
mSubscroller.cancel();
switch (mState) {
- case ANIMATED_ZOOM:
- return false;
- case FLING:
- case NOTHING:
- startTouch(event.getX(0), event.getY(0), event.getEventTime());
- return false;
- case TOUCHING:
- case PANNING:
- case PANNING_LOCKED:
- case PANNING_HOLD:
- case PANNING_HOLD_LOCKED:
- case PINCHING:
- Log.e(LOGTAG, "Received impossible touch down while in " + mState);
- return false;
+ case ANIMATED_ZOOM:
+ return false;
+ case FLING:
+ case NOTHING:
+ startTouch(event.getX(0), event.getY(0), event.getEventTime());
+ return false;
+ case TOUCHING:
+ case PANNING:
+ case PANNING_LOCKED:
+ case PANNING_HOLD:
+ case PANNING_HOLD_LOCKED:
+ case PINCHING:
+ Log.e(LOGTAG, "Received impossible touch down while in " + mState);
+ return false;
}
Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchStart");
return false;
}
- /*
- * Panning/scrolling
- */
private boolean onTouchMove(MotionEvent event) {
- Log.d(LOGTAG, "onTouchMove in state " + mState);
switch (mState) {
- case NOTHING:
- case FLING:
- // should never happen
- Log.e(LOGTAG, "Received impossible touch move while in " + mState);
- return false;
+ case NOTHING:
+ case FLING:
+ // should never happen
+ Log.e(LOGTAG, "Received impossible touch move while in " + mState);
+ return false;
- case TOUCHING:
- if (panDistance(event) < PAN_THRESHOLD) {
- return false;
- }
- cancelTouch();
- startPanning(event.getX(0), event.getY(0), event.getEventTime());
- track(event);
- return true;
-
- case PANNING_HOLD_LOCKED:
- //GeckoApp.mAutoCompletePopup.hide();
- mState = PanZoomState.PANNING_LOCKED;
- // fall through
- case PANNING_LOCKED:
- track(event);
- return true;
-
- case PANNING_HOLD:
- //GeckoApp.mAutoCompletePopup.hide();
- mState = PanZoomState.PANNING;
- // fall through
- case PANNING:
- track(event);
- return true;
-
- case ANIMATED_ZOOM:
- case PINCHING:
- // scale gesture listener will handle this
+ case TOUCHING:
+ if (panDistance(event) < PAN_THRESHOLD) {
return false;
+ }
+ cancelTouch();
+ startPanning(event.getX(0), event.getY(0), event.getEventTime());
+ track(event);
+ return true;
+
+ case PANNING_HOLD_LOCKED:
+ mState = PanZoomState.PANNING_LOCKED;
+ // fall through
+ case PANNING_LOCKED:
+ track(event);
+ return true;
+
+ case PANNING_HOLD:
+ mState = PanZoomState.PANNING;
+ // fall through
+ case PANNING:
+ track(event);
+ return true;
+
+ case ANIMATED_ZOOM:
+ case PINCHING:
+ // scale gesture listener will handle this
+ return false;
}
Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchMove");
return false;
}
private boolean onTouchEnd(MotionEvent event) {
- Log.d(LOGTAG, "onTouchEnd in " + mState);
switch (mState) {
- case NOTHING:
- case FLING:
- // should never happen
- Log.e(LOGTAG, "Received impossible touch end while in " + mState);
- return false;
- case TOUCHING:
- mState = PanZoomState.NOTHING;
- // the switch into TOUCHING might have happened while the page was
- // snapping back after overscroll. we need to finish the snap if that
- // was the case
- bounce();
- return false;
- case PANNING:
- case PANNING_LOCKED:
- case PANNING_HOLD:
- case PANNING_HOLD_LOCKED:
- mState = PanZoomState.FLING;
- fling();
- return true;
- case PINCHING:
- mState = PanZoomState.NOTHING;
- return true;
- case ANIMATED_ZOOM:
- return false;
+ case NOTHING:
+ case FLING:
+ // should never happen
+ Log.e(LOGTAG, "Received impossible touch end while in " + mState);
+ return false;
+ case TOUCHING:
+ mState = PanZoomState.NOTHING;
+ // the switch into TOUCHING might have happened while the page was
+ // snapping back after overscroll. we need to finish the snap if that
+ // was the case
+ bounce();
+ return false;
+ case PANNING:
+ case PANNING_LOCKED:
+ case PANNING_HOLD:
+ case PANNING_HOLD_LOCKED:
+ mState = PanZoomState.FLING;
+ fling();
+ return true;
+ case PINCHING:
+ mState = PanZoomState.NOTHING;
+ return true;
+ case ANIMATED_ZOOM:
+ return false;
}
Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchEnd");
return false;
}
private boolean onTouchCancel(MotionEvent event) {
- Log.d(LOGTAG, "onTouchCancel in " + mState);
-
mState = PanZoomState.NOTHING;
// ensure we snap back if we're overscrolled
bounce();
@@ -332,7 +339,7 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
}
private void track(float x, float y, long time) {
- float timeDelta = (float) (time - mLastEventTime);
+ float timeDelta = (float)(time - mLastEventTime);
if (FloatUtils.fuzzyEquals(timeDelta, 0)) {
// probably a duplicate event, ignore it. using a zero timeDelta will mess
// up our velocity
@@ -350,8 +357,8 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
for (int i = 0; i < event.getHistorySize(); i++) {
track(event.getHistoricalX(0, i),
- event.getHistoricalY(0, i),
- event.getHistoricalEventTime(i));
+ event.getHistoricalY(0, i),
+ event.getHistoricalEventTime(i));
}
track(event.getX(0), event.getY(0), event.getEventTime());
@@ -395,7 +402,6 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
}
mState = PanZoomState.FLING;
- Log.d(LOGTAG, "end bounce at " + metrics);
startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics));
}
@@ -412,16 +418,12 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
stopAnimationTimer();
}
- //GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */);
-
mAnimationTimer = new Timer("Animation Timer");
mAnimationRunnable = runnable;
mAnimationTimer.scheduleAtFixedRate(new TimerTask() {
@Override
- public void run() {
- mController.post(runnable);
- }
- }, 0, 1000L / 60L);
+ public void run() { mController.post(runnable); }
+ }, 0, 1000L/60L);
}
/* Stops the fling or bounce animation. */
@@ -437,9 +439,9 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
}
private float getVelocity() {
- float xVelocity = mX.getRealVelocity();
- float yVelocity = mY.getRealVelocity();
- return FloatMath.sqrt(xVelocity * xVelocity + yVelocity * yVelocity);
+ float xvel = mX.getRealVelocity();
+ float yvel = mY.getRealVelocity();
+ return FloatMath.sqrt(xvel * xvel + yvel * yvel);
}
private boolean stopped() {
@@ -454,14 +456,150 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
mX.displace();
mY.displace();
PointF displacement = getDisplacement();
- if (!mSubscroller.scrollBy(displacement)) {
+ if (! mSubscroller.scrollBy(displacement)) {
synchronized (mController) {
mController.scrollBy(displacement);
}
}
}
+ private abstract class AnimationRunnable implements Runnable {
+ private boolean mAnimationTerminated;
+
+ /* This should always run on the UI thread */
+ public final void run() {
+ /*
+ * Since the animation timer queues this runnable on the UI thread, it
+ * is possible that even when the animation timer is cancelled, there
+ * are multiple instances of this queued, so we need to have another
+ * mechanism to abort. This is done by using the mAnimationTerminated flag.
+ */
+ if (mAnimationTerminated) {
+ return;
+ }
+ animateFrame();
+ }
+
+ protected abstract void animateFrame();
+
+ /* This should always run on the UI thread */
+ protected final void terminate() {
+ mAnimationTerminated = true;
+ }
+ }
+
+ /* The callback that performs the bounce animation. */
+ private class BounceRunnable extends AnimationRunnable {
+ /* The current frame of the bounce-back animation */
+ private int mBounceFrame;
+ /*
+ * The viewport metrics that represent the start and end of the bounce-back animation,
+ * respectively.
+ */
+ private ViewportMetrics mBounceStartMetrics;
+ private ViewportMetrics mBounceEndMetrics;
+
+ BounceRunnable(ViewportMetrics startMetrics, ViewportMetrics endMetrics) {
+ mBounceStartMetrics = startMetrics;
+ mBounceEndMetrics = endMetrics;
+ }
+
+ protected void animateFrame() {
+ /*
+ * The pan/zoom controller might have signaled to us that it wants to abort the
+ * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
+ * out.
+ */
+ if (mState != PanZoomState.FLING) {
+ finishAnimation();
+ return;
+ }
+
+ /* Perform the next frame of the bounce-back animation. */
+ if (mBounceFrame < EASE_OUT_ANIMATION_FRAMES.length) {
+ advanceBounce();
+ return;
+ }
+
+ /* Finally, if there's nothing else to do, complete the animation and go to sleep. */
+ finishBounce();
+ finishAnimation();
+ mState = PanZoomState.NOTHING;
+ }
+
+ /* Performs one frame of a bounce animation. */
+ private void advanceBounce() {
+ synchronized (mController) {
+ float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
+ ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
+ mController.setViewportMetrics(newMetrics);
+ mController.notifyLayerClientOfGeometryChange();
+ mBounceFrame++;
+ }
+ }
+
+ /* Concludes a bounce animation and snaps the viewport into place. */
+ private void finishBounce() {
+ synchronized (mController) {
+ mController.setViewportMetrics(mBounceEndMetrics);
+ mController.notifyLayerClientOfGeometryChange();
+ mBounceFrame = -1;
+ }
+ }
+ }
+
+ // The callback that performs the fling animation.
+ private class FlingRunnable extends AnimationRunnable {
+ protected void animateFrame() {
+ /*
+ * The pan/zoom controller might have signaled to us that it wants to abort the
+ * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
+ * out.
+ */
+ if (mState != PanZoomState.FLING) {
+ finishAnimation();
+ return;
+ }
+
+ /* Advance flings, if necessary. */
+ boolean flingingX = mX.advanceFling();
+ boolean flingingY = mY.advanceFling();
+
+ boolean overscrolled = (mX.overscrolled() || mY.overscrolled());
+
+ /* If we're still flinging in any direction, update the origin. */
+ if (flingingX || flingingY) {
+ updatePosition();
+
+ /*
+ * Check to see if we're still flinging with an appreciable velocity. The threshold is
+ * higher in the case of overscroll, so we bounce back eagerly when overscrolling but
+ * coast smoothly to a stop when not. In other words, require a greater velocity to
+ * maintain the fling once we enter overscroll.
+ */
+ float threshold = (overscrolled && !mSubscroller.scrolling() ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD);
+ if (getVelocity() >= threshold) {
+ // we're still flinging
+ return;
+ }
+
+ mX.stopFling();
+ mY.stopFling();
+ }
+
+ /* Perform a bounce-back animation if overscrolled. */
+ if (overscrolled) {
+ bounce();
+ } else {
+ finishAnimation();
+ mState = PanZoomState.NOTHING;
+ }
+ }
+ }
+
private void finishAnimation() {
+ checkMainThread();
+
Log.d(LOGTAG, "Finishing animation at " + mController.getViewportMetrics());
stopAnimationTimer();
@@ -517,6 +655,26 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
return viewportMetrics;
}
+ private class AxisX extends Axis {
+ AxisX(SubdocumentScrollHelper subscroller) { super(subscroller); }
+ @Override
+ public float getOrigin() { return mController.getOrigin().x; }
+ @Override
+ protected float getViewportLength() { return mController.getViewportSize().width; }
+ @Override
+ protected float getPageLength() { return mController.getPageSize().width; }
+ }
+
+ private class AxisY extends Axis {
+ AxisY(SubdocumentScrollHelper subscroller) { super(subscroller); }
+ @Override
+ public float getOrigin() { return mController.getOrigin().y; }
+ @Override
+ protected float getViewportLength() { return mController.getViewportSize().height; }
+ @Override
+ protected float getPageLength() { return mController.getPageSize().height; }
+ }
+
/*
* Zooming
*/
@@ -555,11 +713,10 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
* factor toward 1.0.
*/
float resistance = Math.min(mX.getEdgeResistance(), mY.getEdgeResistance());
- if (spanRatio > 1.0f) {
+ if (spanRatio > 1.0f)
spanRatio = 1.0f + (spanRatio - 1.0f) * resistance;
- } else {
+ else
spanRatio = 1.0f - (1.0f - spanRatio) * resistance;
- }
synchronized (mController) {
float newZoomFactor = mController.getZoomFactor() * spanRatio;
@@ -568,12 +725,12 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
// such that it asymptotically reaches MAX_ZOOM + 1.0
// but never exceeds that
float excessZoom = newZoomFactor - MAX_ZOOM;
- excessZoom = 1.0f - (float) Math.exp(-excessZoom);
+ excessZoom = 1.0f - (float)Math.exp(-excessZoom);
newZoomFactor = MAX_ZOOM + excessZoom;
}
mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
- mLastZoomFocus.y - detector.getFocusY()));
+ mLastZoomFocus.y - detector.getFocusY()));
PointF focus = new PointF(detector.getFocusX(), detector.getFocusY());
mController.scaleWithFocus(newZoomFactor, focus);
}
@@ -594,7 +751,6 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime());
// Force a viewport synchronisation
- //GeckoApp.mAppContext.showPlugins();
mController.setForceRedraw();
mController.notifyLayerClientOfGeometryChange();
}
@@ -663,193 +819,4 @@ public class PanZoomController extends GestureDetector.SimpleOnGestureListener i
bounce(finalMetrics);
return true;
}
-
- private enum PanZoomState {
- NOTHING, /* no touch-start events received */
- FLING, /* all touches removed, but we're still scrolling page */
- TOUCHING, /* one touch-start event received */
- PANNING_LOCKED, /* touch-start followed by move (i.e. panning with axis lock) */
- PANNING, /* panning without axis lock */
- PANNING_HOLD, /* in panning, but not moving.
- * similar to TOUCHING but after starting a pan */
- PANNING_HOLD_LOCKED, /* like PANNING_HOLD, but axis lock still in effect */
- PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
- ANIMATED_ZOOM /* animated zoom to a new rect */
- }
-
- private abstract class AnimationRunnable implements Runnable {
- private boolean mAnimationTerminated;
-
- /* This should always run on the UI thread */
- public final void run() {
- /*
- * Since the animation timer queues this runnable on the UI thread, it
- * is possible that even when the animation timer is cancelled, there
- * are multiple instances of this queued, so we need to have another
- * mechanism to abort. This is done by using the mAnimationTerminated flag.
- */
- if (mAnimationTerminated) {
- return;
- }
- animateFrame();
- }
-
- protected abstract void animateFrame();
-
- /* This should always run on the UI thread */
- protected final void terminate() {
- mAnimationTerminated = true;
- }
- }
-
- /* The callback that performs the bounce animation. */
- private class BounceRunnable extends AnimationRunnable {
- /* The current frame of the bounce-back animation */
- private int mBounceFrame;
- /*
- * The viewport metrics that represent the start and end of the bounce-back animation,
- * respectively.
- */
- private ViewportMetrics mBounceStartMetrics;
- private ViewportMetrics mBounceEndMetrics;
-
- BounceRunnable(ViewportMetrics startMetrics, ViewportMetrics endMetrics) {
- mBounceStartMetrics = startMetrics;
- mBounceEndMetrics = endMetrics;
- }
-
- protected void animateFrame() {
- /*
- * The pan/zoom controller might have signaled to us that it wants to abort the
- * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
- * out.
- */
- if (mState != PanZoomState.FLING) {
- finishAnimation();
- return;
- }
-
- /* Perform the next frame of the bounce-back animation. */
- if (mBounceFrame < EASE_OUT_ANIMATION_FRAMES.length) {
- advanceBounce();
- return;
- }
-
- /* Finally, if there's nothing else to do, complete the animation and go to sleep. */
- finishBounce();
- finishAnimation();
- mState = PanZoomState.NOTHING;
- }
-
- /* Performs one frame of a bounce animation. */
- private void advanceBounce() {
- synchronized (mController) {
- float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
- ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
- mController.setViewportMetrics(newMetrics);
- mController.notifyLayerClientOfGeometryChange();
- mBounceFrame++;
- }
- }
-
- /* Concludes a bounce animation and snaps the viewport into place. */
- private void finishBounce() {
- synchronized (mController) {
- mController.setViewportMetrics(mBounceEndMetrics);
- mController.notifyLayerClientOfGeometryChange();
- mBounceFrame = -1;
- }
- }
- }
-
- // The callback that performs the fling animation.
- private class FlingRunnable extends AnimationRunnable {
- protected void animateFrame() {
- /*
- * The pan/zoom controller might have signaled to us that it wants to abort the
- * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
- * out.
- */
- if (mState != PanZoomState.FLING) {
- finishAnimation();
- return;
- }
-
- /* Advance flings, if necessary. */
- boolean flingingX = mX.advanceFling();
- boolean flingingY = mY.advanceFling();
-
- boolean overscrolled = (mX.overscrolled() || mY.overscrolled());
-
- /* If we're still flinging in any direction, update the origin. */
- if (flingingX || flingingY) {
- updatePosition();
-
- /*
- * Check to see if we're still flinging with an appreciable velocity. The threshold is
- * higher in the case of overscroll, so we bounce back eagerly when overscrolling but
- * coast smoothly to a stop when not. In other words, require a greater velocity to
- * maintain the fling once we enter overscroll.
- */
- float threshold = (overscrolled && !mSubscroller.scrolling() ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD);
- if (getVelocity() >= threshold) {
- // we're still flinging
- return;
- }
-
- mX.stopFling();
- mY.stopFling();
- }
-
- /* Perform a bounce-back animation if overscrolled. */
- if (overscrolled) {
- bounce();
- } else {
- finishAnimation();
- mState = PanZoomState.NOTHING;
- }
- }
- }
-
- private class AxisX extends Axis {
- AxisX(SubdocumentScrollHelper subscroller) {
- super(subscroller);
- }
-
- @Override
- public float getOrigin() {
- return mController.getOrigin().x;
- }
-
- @Override
- protected float getViewportLength() {
- return mController.getViewportSize().width;
- }
-
- @Override
- protected float getPageLength() {
- return mController.getPageSize().width;
- }
- }
-
- private class AxisY extends Axis {
- AxisY(SubdocumentScrollHelper subscroller) {
- super(subscroller);
- }
-
- @Override
- public float getOrigin() {
- return mController.getOrigin().y;
- }
-
- @Override
- protected float getViewportLength() {
- return mController.getViewportSize().height;
- }
-
- @Override
- protected float getPageLength() {
- return mController.getPageSize().height;
- }
- }
}