Bug 1476106 - Part 1 - Make it possible to notify Java listeners from GeckoScreenOrientation, too. r?snorp draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Thu, 02 Aug 2018 21:10:33 +0200
changeset 828048 258b4c5028b965b1e82a5b175b320b133fcd2cea
parent 826609 b84daa76ff9ce75551d137fd3436bfdaef02aab8
child 828049 99af621bca81b8798f03ef575eaf7c198523b6be
push id118622
push usermozilla@buttercookie.de
push dateThu, 09 Aug 2018 19:41:12 +0000
reviewerssnorp
bugs1476106
milestone63.0a1
Bug 1476106 - Part 1 - Make it possible to notify Java listeners from GeckoScreenOrientation, too. r?snorp Once responsibility for notifying GeckoScreenOrientation of potentially changed screen orientations moves from GeckoApp into GeckoView, the former will no longer be able to benefit from the return value of calling GeckoScreen- Orientation.update(), indicating whether the orientation actually changed or in fact remained the same. GeckoApp however requires that information in order to reset/refresh parts of the UI when the orientation changes, and since GeckoScreenOrientation is already doing all the work of tracking screen orientation changes, we don't want to have to partially duplicate those efforts again in GeckoApp. Instead, we introduce a mechanism for GeckoScreenOrientation to notify interested parties on the Android app side as well. The GeckoScreenOrientation.update() call in GeckoApp.onResume() is removed completely (as opposed to merely removing the refreshChrome() bit) at this stage already because it is unnecessary. If any screen rotation happened while the activity was in background, it will receive an onConfigurationChanged() call anyway before being resumed again. MozReview-Commit-ID: Ila1evcj8Ud
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -2,16 +2,17 @@
  * 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.AppConstants.Versions;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
+import org.mozilla.gecko.GeckoScreenOrientation.ScreenOrientation;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.health.HealthRecorder;
 import org.mozilla.gecko.health.SessionInformation;
 import org.mozilla.gecko.health.StubbedHealthRecorder;
 import org.mozilla.gecko.home.HomeConfig.PanelType;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
@@ -107,16 +108,17 @@ import static org.mozilla.gecko.Tabs.INV
 import static org.mozilla.gecko.mma.MmaDelegate.DOWNLOAD_MEDIA_SAVED_IMAGE;
 import static org.mozilla.gecko.mma.MmaDelegate.READER_AVAILABLE;
 
 public abstract class GeckoApp extends GeckoActivity
                                implements AnchoredPopup.OnVisibilityChangeListener,
                                           BundleEventListener,
                                           GeckoMenu.Callback,
                                           GeckoMenu.MenuPresenter,
+                                          GeckoScreenOrientation.OrientationChangeListener,
                                           GeckoSession.ContentDelegate,
                                           ScreenOrientationDelegate,
                                           Tabs.OnTabsChangedListener,
                                           ViewTreeObserver.OnGlobalLayoutListener {
 
     private static final String LOGTAG = "GeckoApp";
     private static final long ONE_DAY_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS);
 
@@ -976,16 +978,17 @@ public abstract class GeckoApp extends G
         earlyStartJavaSampler(intent);
 
         // Workaround for <http://code.google.com/p/android/issues/detail?id=20915>.
         try {
             Class.forName("android.os.AsyncTask");
         } catch (ClassNotFoundException e) { }
 
         GeckoAppShell.setScreenOrientationDelegate(this);
+        GeckoScreenOrientation.getInstance().addListener(this);
 
         // Tell Stumbler to register a local broadcast listener to listen for preference intents.
         // We do this via intents since we can't easily access Stumbler directly,
         // as it might be compiled outside of Fennec.
         final Intent stumblerIntent = new Intent(getApplicationContext(), SafeReceiver.class);
         stumblerIntent.setAction(INTENT_REGISTER_STUMBLER_LISTENER);
         getApplicationContext().sendBroadcast(stumblerIntent);
 
@@ -1924,21 +1927,16 @@ public abstract class GeckoApp extends G
         if (mIsAbortingAppLaunch) {
             return;
         }
 
         foregrounded = true;
 
         GeckoAppShell.setScreenOrientationDelegate(this);
 
-        int newOrientation = getResources().getConfiguration().orientation;
-        if (GeckoScreenOrientation.getInstance().update(newOrientation)) {
-            refreshChrome();
-        }
-
         // We use two times: a pseudo-unique wall-clock time to identify the
         // current session across power cycles, and the elapsed realtime to
         // track the duration of the session.
         final long now = System.currentTimeMillis();
         final long realTime = android.os.SystemClock.elapsedRealtime();
 
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
@@ -2128,16 +2126,18 @@ public abstract class GeckoApp extends G
             });
         }
 
         super.onDestroy();
 
         Tabs.unregisterOnTabsChangedListener(this);
         Tabs.getInstance().detachFromContext();
 
+        GeckoScreenOrientation.getInstance().removeListener(this);
+
         if (mShutdownOnDestroy) {
             GeckoApplication.shutdown(!mRestartOnShutdown ? null : new Intent(
                     Intent.ACTION_MAIN, /* uri */ null, getApplicationContext(), getClass()));
         }
     }
 
     public void showSDKVersionError() {
         final String message = getString(R.string.unsupported_sdk_version,
@@ -2175,21 +2175,17 @@ public abstract class GeckoApp extends G
         final Locale changed = localeManager.onSystemConfigurationChanged(this, getResources(), newConfig, mLastLocale);
         if (changed != null) {
             onLocaleChanged(Locales.getLanguageTag(changed));
         }
 
         // onConfigurationChanged is not called for 180 degree orientation changes,
         // we will miss such rotations and the screen orientation will not be
         // updated.
-        if (GeckoScreenOrientation.getInstance().update(newConfig.orientation)) {
-            if (mFormAssistPopup != null)
-                mFormAssistPopup.hide();
-            refreshChrome();
-        }
+        GeckoScreenOrientation.getInstance().update(newConfig.orientation);
 
         if (mPromptService != null) {
             mPromptService.changePromptOrientation(newConfig.orientation);
         }
 
         super.onConfigurationChanged(newConfig);
     }
 
@@ -2522,9 +2518,17 @@ public abstract class GeckoApp extends G
         // We want to support the Screen Orientation API, and it always makes sense to lock the
         // orientation of a browser Activity, so we support locking.
         if (getRequestedOrientation() == requestedActivityInfoOrientation) {
             return false;
         }
         setRequestedOrientation(requestedActivityInfoOrientation);
         return true;
     }
+
+    @Override
+    public void onScreenOrientationChanged(ScreenOrientation newOrientation) {
+        if (mFormAssistPopup != null) {
+            mFormAssistPopup.hide();
+        }
+        refreshChrome();
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
@@ -8,17 +8,19 @@ package org.mozilla.gecko;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.util.Log;
 import android.view.Surface;
 import android.view.WindowManager;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.util.ThreadUtils;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
 /*
  * Updates, locks and unlocks the screen orientation.
  *
  * Note: Replaces the OnOrientationChangeListener to avoid redundant rotation
  * event handling.
@@ -59,27 +61,50 @@ public class GeckoScreenOrientation {
     private static GeckoScreenOrientation sInstance;
     // Default rotation, used when device rotation is unknown.
     private static final int DEFAULT_ROTATION = Surface.ROTATION_0;
     // Last updated screen orientation.
     private ScreenOrientation mScreenOrientation;
     // Whether the update should notify Gecko about screen orientation changes.
     private boolean mShouldNotify = true;
 
+    public interface OrientationChangeListener {
+        void onScreenOrientationChanged(ScreenOrientation newOrientation);
+    }
+
+    private final List<OrientationChangeListener> mListeners;
+
     public static GeckoScreenOrientation getInstance() {
         if (sInstance == null) {
             sInstance = new GeckoScreenOrientation();
         }
         return sInstance;
     }
 
     private GeckoScreenOrientation() {
+        mListeners = new ArrayList<>();
         update();
     }
 
+    /**
+     * Add a listener that will be notified when the screen orientation has changed.
+     */
+    public void addListener(OrientationChangeListener aListener) {
+        ThreadUtils.assertOnUiThread();
+        mListeners.add(aListener);
+    }
+
+    /**
+     * Remove a OrientationChangeListener again.
+     */
+    public void removeListener(OrientationChangeListener aListener) {
+        ThreadUtils.assertOnUiThread();
+        mListeners.remove(aListener);
+    }
+
     /*
      * Enable Gecko screen orientation events on update.
      */
     public void enableNotifications() {
         update();
         mShouldNotify = true;
     }
 
@@ -130,16 +155,17 @@ public class GeckoScreenOrientation {
      * @return Whether the screen orientation has changed.
      */
     public boolean update(ScreenOrientation aScreenOrientation) {
         if (mScreenOrientation == aScreenOrientation) {
             return false;
         }
         mScreenOrientation = aScreenOrientation;
         Log.d(LOGTAG, "updating to new orientation " + mScreenOrientation);
+        notifyListeners(mScreenOrientation);
         if (mShouldNotify) {
             // Gecko expects a definite screen orientation, so we default to the
             // primary orientations.
             if (aScreenOrientation == ScreenOrientation.PORTRAIT) {
                 aScreenOrientation = ScreenOrientation.PORTRAIT_PRIMARY;
             } else if (aScreenOrientation == ScreenOrientation.LANDSCAPE) {
                 aScreenOrientation = ScreenOrientation.LANDSCAPE_PRIMARY;
             }
@@ -150,16 +176,33 @@ public class GeckoScreenOrientation {
                 GeckoThread.queueNativeCall(GeckoScreenOrientation.class, "onOrientationChange",
                                             aScreenOrientation.value, getAngle());
             }
         }
         GeckoAppShell.resetScreenSize();
         return true;
     }
 
+    private void notifyListeners(final ScreenOrientation newOrientation) {
+        final Runnable notifier = new Runnable() {
+            @Override
+            public void run() {
+                for (OrientationChangeListener listener : mListeners) {
+                    listener.onScreenOrientationChanged(newOrientation);
+                }
+            }
+        };
+
+        if (ThreadUtils.isOnUiThread()) {
+            notifier.run();
+        } else {
+            ThreadUtils.postToUiThread(notifier);
+        }
+    }
+
     /*
      * @return The Android orientation (Configuration.orientation).
      */
     public int getAndroidOrientation() {
         return screenOrientationToAndroidOrientation(getScreenOrientation());
     }
 
     /*