Bug 1180295 - Update the ZoomedView calculations to account for the new dynamic toolbar model. r=rbarker
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 18 Aug 2015 14:27:20 -0400
changeset 258278 3afafc10b12a3f05b4626d6e04efdf01be4aa15a
parent 258277 e88f6a19cf800be7e94a36807c39a4d39f221d79
child 258279 64c4d2d55f1582d694bce8d19a414942a360d5d3
push id29249
push userryanvm@gmail.com
push dateWed, 19 Aug 2015 11:17:27 +0000
treeherdermozilla-central@706b23a03d1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrbarker
bugs1180295
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1180295 - Update the ZoomedView calculations to account for the new dynamic toolbar model. r=rbarker
mobile/android/base/ZoomedView.java
mobile/android/base/gfx/GeckoLayerClient.java
mobile/android/base/gfx/LayerView.java
--- a/mobile/android/base/ZoomedView.java
+++ b/mobile/android/base/ZoomedView.java
@@ -1,15 +1,16 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
+import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PanZoomController;
 import org.mozilla.gecko.gfx.PointUtils;
 import org.mozilla.gecko.mozglue.DirectBufferAllocator;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
@@ -43,17 +44,17 @@ import android.view.animation.ScaleAnima
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import java.nio.ByteBuffer;
 import java.text.DecimalFormat;
 
-public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChangedListener,
+public class ZoomedView extends FrameLayout implements LayerView.DynamicToolbarListener,
         LayerView.ZoomedViewListener, GeckoEventListener {
     private static final String LOGTAG = "Gecko" + ZoomedView.class.getSimpleName();
 
     private static final float[] ZOOM_FACTORS_LIST = {2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 1.5f};
     private static final int W_CAPTURED_VIEW_IN_PERCENT = 50;
     private static final int H_CAPTURED_VIEW_IN_PERCENT = 50;
     private static final int MINIMUM_DELAY_BETWEEN_TWO_RENDER_CALLS_NS = 1000000;
     private static final int DELAY_BEFORE_NEXT_RENDER_REQUEST_MS = 2000;
@@ -80,16 +81,17 @@ public class ZoomedView extends FrameLay
     private PointF returnValue;
     private final PointF animationStart;
     private ImageView closeButton;
     private TextView changeZoomFactorButton;
     private boolean toolbarOnTop;
     private float offsetDueToToolBarPosition;
     private int toolbarHeight;
     private int cornerRadius;
+    private float dynamicToolbarOverlap;
 
     private boolean stopUpdateView;
 
     private int lastOrientation;
 
     private ByteBuffer buffer;
     private Runnable requestRenderRunnable;
     private long startTimeReRender;
@@ -295,113 +297,109 @@ public class ZoomedView extends FrameLay
 
         setOnTouchListener(null);
     }
     /*
      * Convert a click from ZoomedView. Return the position of the click in the
      * LayerView
      */
     private PointF getUnzoomedPositionFromPointInZoomedView(float x, float y) {
-        if (toolbarOnTop && y > toolbarHeight) {
-           y = y - toolbarHeight;
-        }
-
         ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
-        PointF offset = metrics.getMarginOffset();
         final float parentWidth = metrics.getWidth();
         final float parentHeight = metrics.getHeight();
         RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams();
 
-        returnValue.x = (int) ((x / zoomFactor) +     // Conversion of the x offset inside the zoomed view (using the scale factor)
-
-                        offset.x +               // The offset of the layerView
+        // The number of unzoomed content pixels that can be displayed in the
+        // zoomed area.
+        float visibleContentPixels = viewWidth / zoomFactor;
+        // The offset in content pixels of the leftmost zoomed pixel from the
+        // layerview's left edge when the zoomed view is moved to the right as
+        // far as it can go.
+        float maxContentOffset = parentWidth - visibleContentPixels;
+        // The maximum offset in screen pixels that the zoomed view can have
+        float maxZoomedViewOffset = parentWidth - viewContainerWidth;
 
-                        /* Conversion of the left side position of the zoomed view
-                         *   Minimum value for the left side of the zoomed view is 0
-                         *     and we return 0 after conversion
-                         *   Maximum value for the left side of the zoomed view is (parentWidth - offset.x - viewContainerWidth)
-                         *     and we return (parentWidth - offset.x - (viewWidth / zoomFactor)) after conversion.
-                         */
-                        (((float) params.leftMargin) - offset.x) *
-                            ((parentWidth - offset.x - (viewWidth / zoomFactor)) /
-                            (parentWidth - offset.x - viewContainerWidth)));
+        // The above values allow us to compute the term
+        //   maxContentOffset / maxZoomedViewOffset
+        // which is the number of content pixels that we should move over by
+        // for every screen pixel that the zoomed view is moved over by.
+        // This allows a smooth transition from when the zoomed view is at the
+        // leftmost extent to when it is at the rightmost extent.
+
+        // This is the offset in content pixels of the leftmost zoomed pixel
+        // visible in the zoomed view. This value is relative to the layerview
+        // edge.
+        float zoomedContentOffset = ((float)params.leftMargin) * maxContentOffset / maxZoomedViewOffset;
+        returnValue.x = (int)(zoomedContentOffset + (x / zoomFactor));
 
         // Same comments here vertically
-        returnValue.y = (int) ((y / zoomFactor) +
-                        offset.y -
-                        offsetDueToToolBarPosition +
-                        (((float) params.topMargin) - offset.y) *
-                            ((parentHeight - offset.y + offsetDueToToolBarPosition - (viewHeight / zoomFactor)) /
-                            (parentHeight - offset.y - viewContainerHeight)));
+        visibleContentPixels = viewHeight / zoomFactor;
+        maxContentOffset = parentHeight - visibleContentPixels;
+        maxZoomedViewOffset = parentHeight - (viewContainerHeight - toolbarHeight);
+        float zoomedAreaOffset = (float)params.topMargin + offsetDueToToolBarPosition - ViewHelper.getTranslationY(layerView);
+        zoomedContentOffset = zoomedAreaOffset * maxContentOffset / maxZoomedViewOffset;
+        returnValue.y = (int)(zoomedContentOffset + ((y - offsetDueToToolBarPosition) / zoomFactor));
 
         return returnValue;
     }
 
     /*
      * A touch point (x,y) occurs in LayerView, this point should be displayed
      * in the center of the zoomed view. The returned point is the position of
      * the Top-Left zoomed view point on the screen device
      */
     private PointF getZoomedViewTopLeftPositionFromTouchPosition(float x, float y) {
         ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
-        PointF offset = metrics.getMarginOffset();
         final float parentWidth = metrics.getWidth();
         final float parentHeight = metrics.getHeight();
 
-        returnValue.x = (int) ((((x - (viewWidth / (2 * zoomFactor)))) /   // Translation to get the left side position of the zoomed view
-                                                                        // centered on x (the value 2 to get the middle).
+        // See comments in getUnzoomedPositionFromPointInZoomedView, but the
+        // transformations here are largely the reverse of that function.
 
-                        /* Conversion of the left side position of the zoomed view.
-                         * See the comment in getUnzoomedPositionFromPointInZoomedView.
-                         * The proportional factor is the same. It is used in a division
-                         * and not in a multiplication to convert the position from
-                         * the LayerView to the ZoomedView.
-                         */
-                        ((parentWidth - offset.x - (viewWidth / zoomFactor)) /
-                        (parentWidth - offset.x - viewContainerWidth)))
+        float visibleContentPixels = viewWidth / zoomFactor;
+        float maxContentOffset = parentWidth - visibleContentPixels;
+        float maxZoomedViewOffset = parentWidth - viewContainerWidth;
+        float contentPixelOffset = x - (visibleContentPixels / 2.0f);
+        returnValue.x = (int)(contentPixelOffset * (maxZoomedViewOffset / maxContentOffset));
 
-                + offset.x);     // The offset of the layerView
-
-        // Same comments here vertically
-        returnValue.y = (int) ((((y + offsetDueToToolBarPosition - (viewHeight / (2 * zoomFactor)))) /
-                        ((parentHeight - offset.y + offsetDueToToolBarPosition - (viewHeight / zoomFactor)) /
-                        (parentHeight - offset.y - viewContainerHeight)))
-                + offset.y);
+        visibleContentPixels = viewHeight / zoomFactor;
+        maxContentOffset = parentHeight - visibleContentPixels;
+        maxZoomedViewOffset = parentHeight - (viewContainerHeight - toolbarHeight);
+        contentPixelOffset = y - (visibleContentPixels / 2.0f);
+        float unscaledViewOffset = ViewHelper.getTranslationY(layerView) - offsetDueToToolBarPosition;
+        returnValue.y = (int)((contentPixelOffset * (maxZoomedViewOffset / maxContentOffset)) + unscaledViewOffset);
 
         return returnValue;
     }
 
     private void moveZoomedView(ImmutableViewportMetrics metrics, float newLeftMargin, float newTopMargin,
             StartPointUpdate animateStartPoint) {
-        final float parentWidth = metrics.getWidth();
-        final float parentHeight = metrics.getHeight();
         RelativeLayout.LayoutParams newLayoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
         newLayoutParams.leftMargin = (int) newLeftMargin;
         newLayoutParams.topMargin = (int) newTopMargin;
-        int topMarginMin;
-        int leftMarginMin;
-        PointF offset = metrics.getMarginOffset();
-        topMarginMin = (int) offset.y;
-        leftMarginMin = (int) offset.x;
+        int topMarginMin = (int)(ViewHelper.getTranslationY(layerView) + dynamicToolbarOverlap);
+        int topMarginMax = layerView.getHeight() - viewContainerHeight;
+        int leftMarginMin = 0;
+        int leftMarginMax = layerView.getWidth() - viewContainerWidth;
 
         if (newTopMargin < topMarginMin) {
             newLayoutParams.topMargin = topMarginMin;
-        } else if (newTopMargin + viewContainerHeight > parentHeight) {
-            newLayoutParams.topMargin = (int) (parentHeight - viewContainerHeight);
+        } else if (newTopMargin > topMarginMax) {
+            newLayoutParams.topMargin = topMarginMax;
         }
 
         if (newLeftMargin < leftMarginMin) {
             newLayoutParams.leftMargin = leftMarginMin;
-        } else if (newLeftMargin + viewContainerWidth > parentWidth) {
-            newLayoutParams.leftMargin = (int) (parentWidth - viewContainerWidth);
+        } else if (newLeftMargin > leftMarginMax) {
+            newLayoutParams.leftMargin = leftMarginMax;
         }
 
         if (newLayoutParams.topMargin < topMarginMin + 1) {
             moveToolbar(false);
-        } else if (newLayoutParams.topMargin + viewContainerHeight > parentHeight - 1) {
+        } else if (newLayoutParams.topMargin > topMarginMax - 1) {
             moveToolbar(true);
         }
 
         if (animateStartPoint == StartPointUpdate.GECKO_POSITION) {
             // Before this point, the animationStart point is relative to the layerView.
             // The value is initialized in startZoomDisplay using the click point position coming from Gecko.
             // The position of the zoomed view is now calculated, so the position of the animation
             // can now be correctly set relative to the zoomed view
@@ -412,17 +410,17 @@ public class ZoomedView extends FrameLay
             // the zoomed view has been moved by the user.
             // In this case, the animationStart point is set to the center point of the zoomed view.
             PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(viewContainerWidth / 2, viewContainerHeight / 2);
             animationStart.x = convertedPosition.x - newLayoutParams.leftMargin;
             animationStart.y = convertedPosition.y - newLayoutParams.topMargin;
         }
 
         setLayoutParams(newLayoutParams);
-        PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(0, 0);
+        PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(0, offsetDueToToolBarPosition);
         lastPosition = PointUtils.round(convertedPosition);
         requestZoomedViewRender();
     }
 
     private void moveToolbar(boolean moveTop) {
         if (toolbarOnTop == moveTop) {
             return;
         }
@@ -531,45 +529,44 @@ public class ZoomedView extends FrameLay
 
             @Override
             public boolean isObserver() {
                 return true;
             }
         });
     }
 
-private void startZoomDisplay(LayerView aLayerView, final int leftFromGecko, final int topFromGecko) {
+    private void startZoomDisplay(LayerView aLayerView, final int leftFromGecko, final int topFromGecko) {
         if (layerView == null) {
             layerView = aLayerView;
             layerView.addZoomedViewListener(this);
-            layerView.setOnMetricsChangedZoomedViewportListener(this);
+            layerView.getDynamicToolbarAnimator().addTranslationListener(this);
             ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
             setCapturedSize(metrics);
         }
         startTimeReRender = 0;
         shouldSetVisibleOnUpdate = true;
 
         ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
-        PointF offset = metrics.getMarginOffset();
         // At this point, the start point is relative to the layerView.
         // Later, it will be converted relative to the zoomed view as soon as
         // the position of the zoomed view will be calculated.
-        animationStart.x = (float) leftFromGecko * metrics.zoomFactor + offset.x;
-        animationStart.y = (float) topFromGecko * metrics.zoomFactor + offset.y;
+        animationStart.x = (float) leftFromGecko * metrics.zoomFactor;
+        animationStart.y = (float) topFromGecko * metrics.zoomFactor + ViewHelper.getTranslationY(layerView);
 
         moveUsingGeckoPosition(leftFromGecko, topFromGecko);
     }
 
     public void stopZoomDisplay(boolean withAnimation) {
         if (getVisibility() == View.VISIBLE) {
             shouldSetVisibleOnUpdate = false;
             hideZoomedView(withAnimation);
             ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
             if (layerView != null) {
-                layerView.setOnMetricsChangedZoomedViewportListener(null);
+                layerView.getDynamicToolbarAnimator().removeTranslationListener(this);
                 layerView.removeZoomedViewListener(this);
                 layerView = null;
             }
         }
     }
 
     private void changeZoomFactor(boolean zoomIn) {
         if (zoomIn && currentZoomFactorIndex < ZOOM_FACTORS_LIST.length - 1) {
@@ -630,16 +627,25 @@ private void startZoomDisplay(LayerView 
         // correctly center vertically the zoomed area
         moveToolbar((topFromGecko * metrics.zoomFactor > parentHeight / 2));
         PointF convertedPosition = getZoomedViewTopLeftPositionFromTouchPosition((leftFromGecko * metrics.zoomFactor),
                 (topFromGecko * metrics.zoomFactor));
         moveZoomedView(metrics, convertedPosition.x, convertedPosition.y, StartPointUpdate.GECKO_POSITION);
     }
 
     @Override
+    public void onTranslationChanged(float aToolbarTranslation, float aLayerViewTranslation) {
+        ThreadUtils.assertOnUiThread();
+        if (layerView != null) {
+            dynamicToolbarOverlap = aLayerViewTranslation - aToolbarTranslation;
+            refreshZoomedViewSize(layerView.getViewportMetrics());
+        }
+    }
+
+    @Override
     public void onMetricsChanged(final ImmutableViewportMetrics viewport) {
         // It can be called from a Gecko thread (forceViewportMetrics in GeckoLayerClient).
         // Post to UI Thread to avoid Exception:
         //    "Only the original thread that created a view hierarchy can touch its views."
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 shouldBlockUpdate(false);
@@ -783,19 +789,18 @@ private void startZoomDisplay(LayerView 
         // Allocate the buffer if it's the first call.
         // Change the buffer size if it's not the right size.
         updateBufferSize();
 
         int tabId = Tabs.getInstance().getSelectedTab().getId();
 
         ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
         PointF origin = metrics.getOrigin();
-        PointF offset = metrics.getMarginOffset();
 
-        final int xPos = (int) (origin.x - offset.x) + lastPosition.x;
-        final int yPos = (int) (origin.y - offset.y) + lastPosition.y;
+        final int xPos = (int)origin.x + lastPosition.x;
+        final int yPos = (int)origin.y + lastPosition.y;
 
         GeckoEvent e = GeckoEvent.createZoomedViewEvent(tabId, xPos, yPos, viewWidth,
                 viewHeight, zoomFactor * metrics.zoomFactor, buffer);
         GeckoAppShell.sendEventToGecko(e);
     }
 
 }
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -83,17 +83,16 @@ class GeckoLayerClient implements LayerV
      * Specifically:
      * 1) reading mViewportMetrics from any thread is fine without synchronization
      * 2) writing to mViewportMetrics requires synchronizing on the layer controller object
      * 3) whenever reading multiple fields from mViewportMetrics without synchronization (i.e. in
      *    case 1 above) you should always first 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;
-    private LayerView.OnMetricsChangedListener mZoomedViewViewportChangeListener;
 
     private ZoomConstraints mZoomConstraints;
 
     private boolean mGeckoIsReady;
 
     private final PanZoomController mPanZoomController;
     private final DynamicToolbarAnimator mToolbarAnimator;
     private final LayerView mView;
@@ -831,19 +830,16 @@ class GeckoLayerClient implements LayerV
         viewportMetricsChanged(notifyGecko);
     }
 
     /*
      * You must hold the monitor while calling this.
      */
     private void viewportMetricsChanged(boolean notifyGecko) {
         mToolbarAnimator.onMetricsChanged(mViewportMetrics);
-        if (mZoomedViewViewportChangeListener != null) {
-            mZoomedViewViewportChangeListener.onMetricsChanged(mViewportMetrics);
-        }
 
         mView.requestRender();
         if (notifyGecko && mGeckoIsReady) {
             geometryChanged(null);
         }
     }
 
     /*
@@ -874,19 +870,16 @@ class GeckoLayerClient implements LayerV
         mViewportMetrics = mViewportMetrics.offsetViewportBy(dx, dy);
         viewportMetricsChanged(true);
     }
 
     /** Implementation of PanZoomTarget */
     @Override
     public void panZoomStopped() {
         mToolbarAnimator.onPanZoomStopped();
-        if (mZoomedViewViewportChangeListener != null) {
-            mZoomedViewViewportChangeListener.onPanZoomStopped();
-        }
     }
 
     /** Implementation of PanZoomTarget */
     @Override
     public void forceRedraw(DisplayPortMetrics displayPort) {
         mForceRedraw = true;
         if (mGeckoIsReady) {
             geometryChanged(displayPort);
@@ -946,20 +939,16 @@ class GeckoLayerClient implements LayerV
         // the current Gecko coordinate in CSS pixels.
         PointF layerPoint = new PointF(
                 ((viewPoint.x + origin.x) / zoom) - (geckoOrigin.x / geckoZoom),
                 ((viewPoint.y + origin.y) / zoom) - (geckoOrigin.y / geckoZoom));
 
         return layerPoint;
     }
 
-    void setOnMetricsChangedZoomedViewportListener(LayerView.OnMetricsChangedListener listener) {
-    	mZoomedViewViewportChangeListener = listener;
-    }
-
     public void addDrawListener(DrawListener listener) {
         mDrawListeners.add(listener);
     }
 
     public void removeDrawListener(DrawListener listener) {
         mDrawListeners.remove(listener);
     }
 }
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -668,27 +668,16 @@ public class LayerView extends FrameLayo
     // Public hooks for dynamic toolbar translation
 
     public interface DynamicToolbarListener {
         public void onTranslationChanged(float aToolbarTranslation, float aLayerViewTranslation);
         public void onPanZoomStopped();
         public void onMetricsChanged(ImmutableViewportMetrics viewport);
     }
 
-    // Public hooks for listening to metrics changing
-
-    public interface OnMetricsChangedListener {
-        public void onMetricsChanged(ImmutableViewportMetrics viewport);
-        public void onPanZoomStopped();
-    }
-
-    public void setOnMetricsChangedZoomedViewportListener(OnMetricsChangedListener listener) {
-        mLayerClient.setOnMetricsChangedZoomedViewportListener(listener);
-    }
-
     // Public hooks for zoomed view
 
     public interface ZoomedViewListener {
         public void requestZoomedViewRender();
         public void updateView(ByteBuffer data);
     }
 
     public void addZoomedViewListener(ZoomedViewListener listener) {