Bug 1269041 - Temporarily display the dynamic toolbar in onStart if it is not shown. r=ahunt,f=kats draft
authorMichael Comella <michael.l.comella@gmail.com>
Wed, 27 Apr 2016 17:33:07 -0700
changeset 358039 2de6f3c81ea45b629732cbc28016415bd7630a51
parent 357999 c91e633f84b5b8444061fd04a34bd6b17b09abd3
child 519764 1d316c91524cf5c73144efbaefda76a614aab4fa
push id16911
push usermichael.l.comella@gmail.com
push dateFri, 29 Apr 2016 23:42:11 +0000
reviewersahunt
bugs1269041, 1245493, 1245523
milestone49.0a1
Bug 1269041 - Temporarily display the dynamic toolbar in onStart if it is not shown. r=ahunt,f=kats There fixes the issue described in bug 1245493, however, there are graphical glitches in the page content as the toolbar is either hidden or shown so we couldn't land it - bug 1245523. MozReview-Commit-ID: 4VTEDvEFbNK
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/DynamicToolbar.java
mobile/android/base/java/org/mozilla/gecko/DynamicToolbarTemporaryShowTimer.java
mobile/android/base/moz.build
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -299,16 +299,17 @@ public class BrowserApp extends GeckoApp
 
     // The animator used to toggle HomePager visibility has a race where if the HomePager is shown
     // (starting the animation), the HomePager is hidden, and the HomePager animation completes,
     // both the web content and the HomePager will be hidden. This flag is used to prevent the
     // race by determining if the web content should be hidden at the animation's end.
     private boolean mHideWebContentOnAnimationEnd;
 
     private final DynamicToolbar mDynamicToolbar = new DynamicToolbar();
+    private DynamicToolbarTemporaryShowTimer mDynamicToolbarShowTimer;
 
     private final List<BrowserAppDelegate> delegates = Collections.unmodifiableList(Arrays.asList(
             (BrowserAppDelegate) new AddToHomeScreenPromotion(),
             (BrowserAppDelegate) new ScreenshotDelegate()
     ));
 
     @NonNull
     private SearchEngineManager searchEngineManager; // Contains reference to Context - DO NOT LEAK!
@@ -348,16 +349,17 @@ public class BrowserApp extends GeckoApp
                 if (mZoomedView != null) {
                     mZoomedView.stopZoomDisplay(false);
                 }
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     updateHomePagerForTab(tab);
                 }
 
                 mDynamicToolbar.persistTemporaryVisibility();
+                cancelDynamicToolbarShowTimer();
                 break;
             case START:
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     invalidateOptionsMenu();
 
                     if (mDynamicToolbar.isEnabled()) {
                         mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
                     }
@@ -1096,16 +1098,18 @@ public class BrowserApp extends GeckoApp
 
         // Needed for Adjust to get accurate session measurements
         AdjustConstants.getAdjustHelper().onPause();
 
         // Register for Prompt:ShowTop so we can foreground this activity even if it's hidden.
         EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this,
             "Prompt:ShowTop");
 
+        cancelDynamicToolbarShowTimer();
+
         for (BrowserAppDelegate delegate : delegates) {
             delegate.onPause(this);
         }
     }
 
     @Override
     public void onRestart() {
         super.onRestart();
@@ -1114,16 +1118,19 @@ public class BrowserApp extends GeckoApp
             delegate.onRestart(this);
         }
     }
 
     @Override
     public void onStart() {
         super.onStart();
 
+        mDynamicToolbarShowTimer = new DynamicToolbarTemporaryShowTimer(mDynamicToolbar);
+        mDynamicToolbarShowTimer.start(VisibilityTransition.IMMEDIATE);
+
         // Queue this work so that the first launch of the activity doesn't
         // trigger profile init too early.
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 final GeckoProfile profile = getProfile();
                 if (profile.inGuestMode()) {
                     GuestSession.showNotification(BrowserApp.this);
@@ -1173,16 +1180,28 @@ public class BrowserApp extends GeckoApp
 
         // Sending a message to the toolbar when the browser window gains focus
         // This is needed for qr code input
         if (hasFocus) {
             mBrowserToolbar.onParentFocus();
         }
     }
 
+    @Override
+    public void onUserInteraction() {
+        cancelDynamicToolbarShowTimer();
+    }
+
+    private void cancelDynamicToolbarShowTimer() {
+        if (mDynamicToolbarShowTimer != null) {
+            mDynamicToolbarShowTimer.cancel();
+            mDynamicToolbarShowTimer = null; // to prevent us from calling cancel unnecessarily & to free memory.
+        }
+    }
+
     private void setBrowserToolbarListeners() {
         mBrowserToolbar.setOnActivateListener(new BrowserToolbar.OnActivateListener() {
             @Override
             public void onActivate() {
                 enterEditingMode();
             }
         });
 
--- a/mobile/android/base/java/org/mozilla/gecko/DynamicToolbar.java
+++ b/mobile/android/base/java/org/mozilla/gecko/DynamicToolbar.java
@@ -1,13 +1,15 @@
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.PrefsHelper.PrefHandlerBase;
+import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
 import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
 import org.mozilla.gecko.gfx.LayerView;
+import org.mozilla.gecko.util.FloatUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.os.Build;
 import android.os.Bundle;
 import android.util.Log;
 
 public class DynamicToolbar {
     private static final String LOGTAG = "DynamicToolbar";
@@ -136,16 +138,27 @@ public class DynamicToolbar {
         final boolean isImmediate = transition == VisibilityTransition.IMMEDIATE;
         if (visible) {
             layerView.getDynamicToolbarAnimator().showToolbar(isImmediate);
         } else {
             layerView.getDynamicToolbarAnimator().hideToolbar(isImmediate);
         }
     }
 
+    public boolean isVisible() {
+        if (layerView == null) {
+            return true; // occurs when not in memory - toolbar defaults to visible
+        }
+        final DynamicToolbarAnimator animator = layerView.getDynamicToolbarAnimator();
+        if (animator == null) {
+            return true; // occurs when not in memory - toolbar defaults to visible
+        }
+        return FloatUtils.fuzzyEquals(0, animator.getToolbarTranslation());
+    }
+
     public void setTemporarilyVisible(boolean visible, VisibilityTransition transition) {
         ThreadUtils.assertOnUiThread();
 
         if (layerView == null) {
             return;
         }
 
         if (visible == temporarilyVisible) {
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/DynamicToolbarTemporaryShowTimer.java
@@ -0,0 +1,61 @@
+/*
+ * 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 java.util.concurrent.TimeUnit;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
+import org.mozilla.gecko.util.ThreadUtils;
+
+/**
+ * A container to temporarily show the DynamicToolbar and hide it after a short duration. If the toolbar
+ * is already visible, method calls are a no-op.
+ *
+ * Call {@link #start(VisibilityTransition)} to begin the timer and {@link #cancel()} to cancel it.
+ *
+ * For simplicity, this timer is single-use. This class should only be used from the UIThread.
+ */
+public class DynamicToolbarTemporaryShowTimer {
+    private static final long HIDE_MILLIS = TimeUnit.SECONDS.toMillis(3);
+
+    private final DynamicToolbar toolbar;
+    private Runnable showRunnable;
+
+    private boolean wasStartCalled = false;
+
+    public DynamicToolbarTemporaryShowTimer(final DynamicToolbar toolbar) {
+        this.toolbar = toolbar;
+    }
+
+    @UiThread
+    public void start(@NonNull final VisibilityTransition initialTransition) {
+        if (wasStartCalled) {
+            throw new IllegalStateException("Tried to start already-started timer - these are single-use");
+        }
+        wasStartCalled = true;
+
+        if (!toolbar.isEnabled() || toolbar.isVisible()) {
+            return;
+        }
+
+        toolbar.setVisible(true, initialTransition);
+        showRunnable = new Runnable() {
+            @Override
+            public void run() {
+                toolbar.setVisible(false, VisibilityTransition.ANIMATE);
+            }
+        };
+        ThreadUtils.postDelayedToUiThread(showRunnable, HIDE_MILLIS);
+    }
+
+    @UiThread
+    public void cancel() {
+        ThreadUtils.getUiHandler().removeCallbacks(showRunnable);
+    }
+}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -260,16 +260,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'dlc/DownloadAction.java',
     'dlc/DownloadContentService.java',
     'dlc/StudyAction.java',
     'dlc/SyncAction.java',
     'dlc/VerifyAction.java',
     'DoorHangerPopup.java',
     'DownloadsIntegration.java',
     'DynamicToolbar.java',
+    'DynamicToolbarTemporaryShowTimer.java',
     'EditBookmarkDialog.java',
     'EventDispatcher.java',
     'favicons/cache/FaviconCache.java',
     'favicons/cache/FaviconCacheElement.java',
     'favicons/cache/FaviconsForURL.java',
     'favicons/decoders/FaviconDecoder.java',
     'favicons/decoders/ICODecoder.java',
     'favicons/decoders/IconDirectoryEntry.java',