Bug 1416319 - 3. Clean up DynamicToolbarAnimator API; r?rbarker draft
authorJim Chen <nchen@mozilla.com>
Wed, 22 Nov 2017 14:12:22 -0500
changeset 702126 ba491771124baaea743acbf22908d122bcc6ce41
parent 702125 7c0d22091ad36f188bc8603e4086b18f91848815
child 702127 85d42387419012bbe66a9e5d70b890333e8f0923
push id90386
push userbmo:nchen@mozilla.com
push dateWed, 22 Nov 2017 19:12:37 +0000
reviewersrbarker
bugs1416319
milestone59.0a1
Bug 1416319 - 3. Clean up DynamicToolbarAnimator API; r?rbarker The plan is to incorporate DynamicToolbarAnimator into the official GeckoView API and expose getter/setter in LayerSession. This patch cleans up the class to make sure only public APIs are public, and to assert they are called on the UI thread. MozReview-Commit-ID: D3ePV3k2HvX
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -1,16 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * vim: ts=4 sw=4 expandtab:
  * 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.gfx.DynamicToolbarAnimator;
 import org.mozilla.gecko.gfx.GeckoDisplay;
 import org.mozilla.gecko.gfx.LayerView;
 
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Handler;
@@ -199,16 +200,20 @@ public class GeckoView extends LayerView
     public EventDispatcher getEventDispatcher() {
         return mSession.getEventDispatcher();
     }
 
     public GeckoSessionSettings getSettings() {
         return mSession.getSettings();
     }
 
+    public DynamicToolbarAnimator getDynamicToolbarAnimator() {
+        return mSession.getDynamicToolbarAnimator();
+    }
+
     @Override
     public void onAttachedToWindow() {
         if (mSession == null) {
             setSession(new GeckoSession());
         }
 
         if (!mSession.isOpen()) {
             mSession.openWindow(getContext().getApplicationContext());
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
@@ -5,195 +5,192 @@
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.graphics.Bitmap;
 import android.graphics.PointF;
 import android.util.Log;
-import android.view.MotionEvent;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.EnumSet;
-import java.util.List;
 import java.util.Set;
 
 public class DynamicToolbarAnimator {
     private static final String LOGTAG = "GeckoDynamicToolbarAnimator";
 
     public static enum PinReason {
         DISABLED(0),
         RELAYOUT(1),
         ACTION_MODE(2),
         FULL_SCREEN(3),
         CARET_DRAG(4),
         PAGE_LOADING(5),
         CUSTOM_TAB(6);
 
         public final int value;
+
         PinReason(final int aValue) {
             value = aValue;
         }
     }
 
-    public interface MetricsListener {
-        public void onMetricsChanged(ImmutableViewportMetrics viewport);
-    }
-
     public interface ToolbarChromeProxy {
         public Bitmap getBitmapOfToolbarChrome();
         public boolean isToolbarChromeVisible();
         public void toggleToolbarChrome(boolean aShow);
     }
 
-    private final Set<PinReason> mPinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class));
+    private final Set<PinReason> mPinFlags = EnumSet.noneOf(PinReason.class);
 
-    private final LayerView mTarget;
-    private LayerSession.Compositor mCompositor;
-    private final List<MetricsListener> mListeners;
+    private final LayerSession mTarget;
+    private final LayerSession.Compositor mCompositor;
     private ToolbarChromeProxy mToolbarChromeProxy;
     private int mMaxToolbarHeight;
 
-    public DynamicToolbarAnimator(final LayerView aTarget) {
+    /* package */ DynamicToolbarAnimator(final LayerSession aTarget) {
         mTarget = aTarget;
-        mListeners = new ArrayList<MetricsListener>();
+        mCompositor = aTarget.mCompositor;
     }
 
-    public void addMetricsListener(MetricsListener aListener) {
-        mListeners.add(aListener);
-    }
-
-    public void removeMetricsListener(MetricsListener aListener) {
-        mListeners.remove(aListener);
+    public ToolbarChromeProxy getToolbarChromeProxy() {
+        ThreadUtils.assertOnUiThread();
+        return mToolbarChromeProxy;
     }
 
     public void setToolbarChromeProxy(ToolbarChromeProxy aToolbarChromeProxy) {
+        ThreadUtils.assertOnUiThread();
         mToolbarChromeProxy = aToolbarChromeProxy;
     }
 
-    /* package-private */ void onToggleChrome(boolean aShow) {
-        if (mToolbarChromeProxy != null) {
-            mToolbarChromeProxy.toggleToolbarChrome(aShow);
-        }
-    }
-
-    /* package-private */ void onMetricsChanged(ImmutableViewportMetrics aMetrics) {
-        for (MetricsListener listener : mListeners) {
-            listener.onMetricsChanged(aMetrics);
-        }
-    }
-
     public void setMaxToolbarHeight(int maxToolbarHeight) {
         ThreadUtils.assertOnUiThread();
+
         mMaxToolbarHeight = maxToolbarHeight;
-        if (isCompositorReady()) {
+        if (mCompositor.isReady()) {
             mCompositor.setMaxToolbarHeight(mMaxToolbarHeight);
         }
     }
 
-    public int getCurrentToolbarHeight() {
+    // Keep this package-private because applications should use one of LayerSession's
+    // coordinates APIs instead of dealing with the dynamic toolbar manually.
+    /* package */ int getCurrentToolbarHeight() {
+        ThreadUtils.assertOnUiThread();
+
         if ((mToolbarChromeProxy != null) && mToolbarChromeProxy.isToolbarChromeVisible()) {
             return mMaxToolbarHeight;
         }
         return 0;
     }
 
     /**
      * If true, scroll changes will not affect translation.
      */
     public boolean isPinned() {
+        ThreadUtils.assertOnUiThread();
+
         return !mPinFlags.isEmpty();
     }
 
     public boolean isPinnedBy(PinReason reason) {
-        return mPinFlags.contains(reason);
-    }
+        ThreadUtils.assertOnUiThread();
 
-    /* package */ void sendPinValueToCompositor(final boolean pinned, final PinReason reason) {
-        if (isCompositorReady()) {
-             mCompositor.setPinned(pinned, reason.value);
-        }
+        return mPinFlags.contains(reason);
     }
 
     public void setPinned(final boolean pinned, final PinReason reason) {
-        // setPinned may be called from the main thread but compositor can only be accessed on UI thread
-        if (pinned != mPinFlags.contains(reason)) {
-            if (ThreadUtils.isOnUiThread() == true) {
-                sendPinValueToCompositor(pinned, reason);
-            } else {
-                ThreadUtils.postToUiThread(new Runnable() {
-                        @Override
-                    public void run() {
-                        sendPinValueToCompositor(pinned, reason);
-                    }
-                });
-            }
+        ThreadUtils.assertOnUiThread();
+
+        if (pinned != mPinFlags.contains(reason) && mCompositor.isReady()) {
+            mCompositor.setPinned(pinned, reason.value);
         }
 
         if (pinned) {
             mPinFlags.add(reason);
         } else {
             mPinFlags.remove(reason);
         }
     }
 
     public void showToolbar(boolean immediately) {
-        if (isCompositorReady()) {
-            mCompositor.sendToolbarAnimatorMessage(immediately ?
-                LayerView.REQUEST_SHOW_TOOLBAR_IMMEDIATELY : LayerView.REQUEST_SHOW_TOOLBAR_ANIMATED);
+        ThreadUtils.assertOnUiThread();
+
+        if (mCompositor.isReady()) {
+            mCompositor.sendToolbarAnimatorMessage(
+                    immediately ? LayerSession.REQUEST_SHOW_TOOLBAR_IMMEDIATELY
+                                : LayerSession.REQUEST_SHOW_TOOLBAR_ANIMATED);
         }
     }
 
     public void hideToolbar(boolean immediately) {
-        if (isCompositorReady()) {
-            mCompositor.sendToolbarAnimatorMessage(immediately ?
-                LayerView.REQUEST_HIDE_TOOLBAR_IMMEDIATELY : LayerView.REQUEST_HIDE_TOOLBAR_ANIMATED);
+        ThreadUtils.assertOnUiThread();
+
+        if (mCompositor.isReady()) {
+            mCompositor.sendToolbarAnimatorMessage(
+                    immediately ? LayerSession.REQUEST_HIDE_TOOLBAR_IMMEDIATELY
+                                : LayerSession.REQUEST_HIDE_TOOLBAR_ANIMATED);
+        }
+    }
+
+    /* package */ void onCompositorReady() {
+        mCompositor.setMaxToolbarHeight(mMaxToolbarHeight);
+
+        if ((mToolbarChromeProxy != null) && mToolbarChromeProxy.isToolbarChromeVisible()) {
+            mCompositor.sendToolbarAnimatorMessage(
+                    LayerSession.REQUEST_SHOW_TOOLBAR_IMMEDIATELY);
+        } else {
+            mCompositor.sendToolbarAnimatorMessage(
+                    LayerSession.REQUEST_HIDE_TOOLBAR_IMMEDIATELY);
+        }
+
+        for (final PinReason reason : PinReason.values()) {
+            mCompositor.setPinned(mPinFlags.contains(reason), reason.value);
         }
     }
 
-    /* package-private */ IntSize getViewportSize() {
-        ThreadUtils.assertOnUiThread();
+    /* package */ void handleToolbarAnimatorMessage(final int message) {
+        if (mToolbarChromeProxy == null || !mCompositor.isReady()) {
+            return;
+        }
 
-        int viewWidth = mTarget.getWidth();
-        int viewHeight = mTarget.getHeight();
-        if ((mToolbarChromeProxy != null) && mToolbarChromeProxy.isToolbarChromeVisible()) {
-          viewHeight -= mMaxToolbarHeight;
-        }
-        return new IntSize(viewWidth, viewHeight);
-    }
+        switch (message) {
+            case LayerSession.STATIC_TOOLBAR_NEEDS_UPDATE: {
+                // Send updated toolbar image to compositor.
+                final Bitmap bm = mToolbarChromeProxy.getBitmapOfToolbarChrome();
+                if (bm == null) {
+                    mCompositor.sendToolbarAnimatorMessage(
+                            LayerSession.TOOLBAR_SNAPSHOT_FAILED);
+                    break;
+                }
 
-    public PointF getVisibleEndOfLayerView() {
-        return new PointF(mTarget.getWidth(), mTarget.getHeight());
-    }
+                final int width = bm.getWidth();
+                final int height = bm.getHeight();
+                final int[] pixels = new int[bm.getByteCount() / 4];
+                try {
+                    bm.getPixels(pixels, /* offset */ 0, /* stride */ width,
+                                 /* x */ 0, /* y */ 0, width, height);
+                    mCompositor.sendToolbarPixelsToCompositor(width, height, pixels);
+                } catch (final Exception e) {
+                    Log.e(LOGTAG, "Cannot get toolbar pixels", e);
+                }
+                break;
+            }
 
-    /* package-private */ void updateCompositor() {
-        if (isCompositorReady()) {
-            mCompositor.setMaxToolbarHeight(mMaxToolbarHeight);
-            if ((mToolbarChromeProxy != null) && mToolbarChromeProxy.isToolbarChromeVisible()) {
-                mCompositor.sendToolbarAnimatorMessage(LayerView.REQUEST_SHOW_TOOLBAR_IMMEDIATELY);
-            } else {
-                mCompositor.sendToolbarAnimatorMessage(LayerView.REQUEST_HIDE_TOOLBAR_IMMEDIATELY);
+            case LayerSession.STATIC_TOOLBAR_READY: {
+                // Hide toolbar and send TOOLBAR_HIDDEN message to compositor
+                mToolbarChromeProxy.toggleToolbarChrome(false);
+                mCompositor.sendToolbarAnimatorMessage(LayerSession.TOOLBAR_HIDDEN);
+                break;
             }
-            for (PinReason reason : PinReason.values()) {
-                mCompositor.setPinned(mPinFlags.contains(reason), reason.value);
+
+            case LayerSession.TOOLBAR_SHOW: {
+                // Show toolbar.
+                mToolbarChromeProxy.toggleToolbarChrome(true);
+                mCompositor.sendToolbarAnimatorMessage(LayerSession.TOOLBAR_VISIBLE);
+                break;
             }
+
+            default:
+                Log.e(LOGTAG, "Unhandled Toolbar Animator Message: " + message);
+                break;
         }
     }
-
-    /* package-private */ void notifyCompositorCreated(LayerSession.Compositor aCompositor) {
-        ThreadUtils.assertOnUiThread();
-        mCompositor = aCompositor;
-    }
-
-    private boolean isCompositorReady() {
-        return ((mCompositor != null) && (mCompositor.isReady()));
-    }
-
-
-    /* package-private */ Bitmap getBitmapOfToolbarChrome() {
-        if (mToolbarChromeProxy != null) {
-            return mToolbarChromeProxy.getBitmapOfToolbarChrome();
-        }
-        return null;
-    }
 }