Bug 1321638 - Part 3: Add ScreenOrientationDelegate. r=jchen
This patch abstracts setting the screen orientation into an interface
(but not into the already overloaded GeckoInterface).
I feel this form obscures the (eventual) connection between the
GeckoView widget and its embedding Activity, but it removes the
explicit getActivity() and it's certainly simpler than previous
iterations.
MozReview-Commit-ID: 8a8bPTlcp3T
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -128,16 +128,17 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
public abstract class GeckoApp
extends GeckoActivity
implements
BundleEventListener,
ContextGetter,
GeckoAppShell.GeckoInterface,
+ ScreenOrientationDelegate,
GeckoMenu.Callback,
GeckoMenu.MenuPresenter,
Tabs.OnTabsChangedListener,
ViewTreeObserver.OnGlobalLayoutListener {
private static final String LOGTAG = "GeckoApp";
private static final long ONE_DAY_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS);
@@ -1124,16 +1125,17 @@ public abstract class GeckoApp
// GeckoAppShell is tightly coupled to us, rather than
// the app context, because various parts of Fennec (e.g.,
// GeckoScreenOrientation) use GAS to access the Activity in
// the guise of fetching a Context.
// When that's fixed, `this` can change to
// `(GeckoApplication) getApplication()` here.
GeckoAppShell.setContextGetter(this);
GeckoAppShell.setGeckoInterface(this);
+ GeckoAppShell.setScreenOrientationDelegate(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.
getApplicationContext().sendBroadcast(
new Intent(INTENT_REGISTER_STUMBLER_LISTENER)
);
@@ -2075,16 +2077,17 @@ public abstract class GeckoApp
if (mIsAbortingAppLaunch) {
return;
}
foregrounded = true;
GeckoAppShell.setGeckoInterface(this);
+ GeckoAppShell.setScreenOrientationDelegate(this);
if (lastSelectedTabId >= 0 && (lastActiveGeckoApp == null || lastActiveGeckoApp.get() != this)) {
Tabs.getInstance().selectTab(lastSelectedTabId);
}
int newOrientation = getResources().getConfiguration().orientation;
if (GeckoScreenOrientation.getInstance().update(newOrientation)) {
refreshChrome();
@@ -2858,9 +2861,20 @@ public abstract class GeckoApp
public String getDefaultChromeURI() {
// Use the chrome URI specified by Gecko's defaultChromeURI pref.
return null;
}
public GeckoView getGeckoView() {
return mLayerView;
}
+
+ @Override
+ public boolean setRequestedOrientationForCurrentActivity(int requestedActivityInfoOrientation) {
+ // 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;
+ }
}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -275,16 +275,17 @@ gvjar.sources += [geckoview_source_dir +
'NSSBridge.java',
'permissions/PermissionBlock.java',
'permissions/Permissions.java',
'permissions/PermissionsHelper.java',
'PrefsHelper.java',
'process/GeckoProcessManager.java',
'process/GeckoServiceChildProcess.java',
'ScreenManagerHelper.java',
+ 'ScreenOrientationDelegate.java',
'sqlite/ByteBufferInputStream.java',
'sqlite/MatrixBlobCursor.java',
'sqlite/SQLiteBridge.java',
'sqlite/SQLiteBridgeException.java',
'TouchEventInterceptor.java',
]]
gvjar.sources += [geckoview_thirdparty_source_dir + f for f in [
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -469,17 +469,17 @@ public class GeckoAppShell
float w, int accuracy, long time);
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
/* package */ static native void onLocationChanged(double latitude, double longitude,
double altitude, float accuracy,
float bearing, float speed, long time);
private static class DefaultListeners
- implements SensorEventListener, LocationListener, NotificationListener {
+ implements SensorEventListener, LocationListener, NotificationListener, ScreenOrientationDelegate {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private static int HalSensorAccuracyFor(int androidAccuracy) {
switch (androidAccuracy) {
case SensorManager.SENSOR_STATUS_UNRELIABLE:
return GeckoHalDefines.SENSOR_ACCURACY_UNRELIABLE;
@@ -599,23 +599,34 @@ public class GeckoAppShell
// Default is to not show the notification, and immediate send close message.
GeckoAppShell.onNotificationClose(name, cookie);
}
@Override // NotificationListener
public void closeNotification(String name) {
// Do nothing.
}
+
+ @Override
+ public boolean setRequestedOrientationForCurrentActivity(int requestedActivityInfoOrientation) {
+ // Do nothing, and report that the orientation was not set.
+ return false;
+ }
}
private static final DefaultListeners DEFAULT_LISTENERS = new DefaultListeners();
private static SensorEventListener sSensorListener = DEFAULT_LISTENERS;
private static LocationListener sLocationListener = DEFAULT_LISTENERS;
private static NotificationListener sNotificationListener = DEFAULT_LISTENERS;
+ /**
+ * A delegate for supporting the Screen Orientation API.
+ */
+ private static ScreenOrientationDelegate sScreenOrientationDelegate = DEFAULT_LISTENERS;
+
public static SensorEventListener getSensorListener() {
return sSensorListener;
}
public static void setSensorListener(final SensorEventListener listener) {
sSensorListener = (listener != null) ? listener : DEFAULT_LISTENERS;
}
@@ -630,16 +641,24 @@ public class GeckoAppShell
public static NotificationListener getNotificationListener() {
return sNotificationListener;
}
public static void setNotificationListener(final NotificationListener listener) {
sNotificationListener = (listener != null) ? listener : DEFAULT_LISTENERS;
}
+ public static ScreenOrientationDelegate getScreenOrientationDelegate() {
+ return sScreenOrientationDelegate;
+ }
+
+ public static void setScreenOrientationDelegate(ScreenOrientationDelegate screenOrientationDelegate) {
+ sScreenOrientationDelegate = (screenOrientationDelegate != null) ? screenOrientationDelegate : DEFAULT_LISTENERS;
+ }
+
@WrapForJNI(calledFrom = "gecko")
private static void enableSensor(int aSensortype) {
GeckoInterface gi = getGeckoInterface();
if (gi == null) {
return;
}
SensorManager sm = (SensorManager)
getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
@@ -2067,21 +2086,23 @@ public class GeckoAppShell
@WrapForJNI(calledFrom = "gecko")
private static void disableScreenOrientationNotifications() {
GeckoScreenOrientation.getInstance().disableNotifications();
}
@WrapForJNI(calledFrom = "gecko")
private static void lockScreenOrientation(int aOrientation) {
+ // TODO: don't vector through GeckoAppShell.
GeckoScreenOrientation.getInstance().lock(aOrientation);
}
@WrapForJNI(calledFrom = "gecko")
private static void unlockScreenOrientation() {
+ // TODO: don't vector through GeckoAppShell.
GeckoScreenOrientation.getInstance().unlock();
}
@WrapForJNI(calledFrom = "gecko")
private static void notifyWakeLockChanged(String topic, String state) {
if (getGeckoInterface() != null)
getGeckoInterface().notifyWakeLockChanged(topic, state);
}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
@@ -1,22 +1,23 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.annotation.WrapForJNI;
-
+import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.util.Log;
import android.view.Surface;
-import android.app.Activity;
+import android.view.WindowManager;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
import java.util.Arrays;
import java.util.List;
/*
* Updates, locks and unlocks the screen orientation.
*
* Note: Replaces the OnOrientationChangeListener to avoid redundant rotation
@@ -87,21 +88,21 @@ public class GeckoScreenOrientation {
/*
* Update screen orientation.
* Retrieve orientation and rotation via GeckoAppShell.
*
* @return Whether the screen orientation has changed.
*/
public boolean update() {
- Activity activity = getActivity();
- if (activity == null) {
+ final Context appContext = GeckoAppShell.getApplicationContext();
+ if (appContext == null) {
return false;
}
- Configuration config = activity.getResources().getConfiguration();
+ Configuration config = appContext.getResources().getConfiguration();
return update(config.orientation);
}
/*
* Update screen orientation given the android orientation.
* Retrieve rotation via GeckoAppShell.
*
* @param aAndroidOrientation
@@ -160,83 +161,62 @@ public class GeckoScreenOrientation {
/*
* @return The Gecko screen orientation derived from Android orientation and
* rotation.
*/
public ScreenOrientation getScreenOrientation() {
return mScreenOrientation;
}
- /*
+ /**
* Lock screen orientation given the Gecko screen orientation.
*
* @param aGeckoOrientation
* The Gecko orientation provided.
*/
public void lock(int aGeckoOrientation) {
lock(ScreenOrientation.get(aGeckoOrientation));
}
- /*
+ /**
* Lock screen orientation given the Gecko screen orientation.
- * Retrieve rotation via GeckoAppShell.
*
* @param aScreenOrientation
* Gecko screen orientation derived from Android orientation and
* rotation.
*
* @return Whether the locking was successful.
*/
public boolean lock(ScreenOrientation aScreenOrientation) {
Log.d(LOGTAG, "locking to " + aScreenOrientation);
- update(aScreenOrientation);
- return setRequestedOrientation(aScreenOrientation);
+ final ScreenOrientationDelegate delegate = GeckoAppShell.getScreenOrientationDelegate();
+ final int activityInfoOrientation = screenOrientationToActivityInfoOrientation(aScreenOrientation);
+ if (delegate.setRequestedOrientationForCurrentActivity(activityInfoOrientation)) {
+ update(aScreenOrientation);
+ return true;
+ } else {
+ return false;
+ }
}
- /*
+ /**
* Unlock and update screen orientation.
*
* @return Whether the unlocking was successful.
*/
public boolean unlock() {
Log.d(LOGTAG, "unlocking");
- setRequestedOrientation(mDefaultScreenOrientation);
- return update();
- }
-
- private Activity getActivity() {
- if (GeckoAppShell.getGeckoInterface() == null) {
- return null;
- }
- return GeckoAppShell.getGeckoInterface().getActivity();
- }
-
- /*
- * Set the given requested orientation for the current activity.
- * This is essentially an unlock without an update.
- *
- * @param aScreenOrientation
- * Gecko screen orientation.
- *
- * @return Whether the requested orientation was set. This can only fail if
- * the current activity cannot be retrieved via GeckoAppShell.
- *
- */
- private boolean setRequestedOrientation(ScreenOrientation aScreenOrientation) {
- int activityOrientation = screenOrientationToActivityInfoOrientation(aScreenOrientation);
- Activity activity = getActivity();
- if (activity == null) {
- Log.w(LOGTAG, "setRequestOrientation: failed to get activity");
+ final ScreenOrientationDelegate delegate = GeckoAppShell.getScreenOrientationDelegate();
+ final int activityInfoOrientation = screenOrientationToActivityInfoOrientation(ScreenOrientation.DEFAULT);
+ if (delegate.setRequestedOrientationForCurrentActivity(activityInfoOrientation)) {
+ update();
+ return true;
+ } else {
return false;
}
- if (activity.getRequestedOrientation() == activityOrientation) {
- return false;
- }
- activity.setRequestedOrientation(activityOrientation);
- return true;
}
/*
* Combine the Android orientation and rotation to the Gecko orientation.
*
* @param aAndroidOrientation
* Android orientation from Configuration.orientation.
* @param aRotation
@@ -280,25 +260,26 @@ public class GeckoScreenOrientation {
return 270;
default:
Log.w(LOGTAG, "getAngle: unexpected rotation value");
return 0;
}
}
/*
- * @return Device rotation from Display.getRotation().
+ * @return Device rotation.
*/
private int getRotation() {
- Activity activity = getActivity();
- if (activity == null) {
- Log.w(LOGTAG, "getRotation: failed to get activity");
+ final Context appContext = GeckoAppShell.getApplicationContext();
+ if (appContext == null) {
return DEFAULT_ROTATION;
}
- return activity.getWindowManager().getDefaultDisplay().getRotation();
+ final WindowManager windowManager =
+ (WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE);
+ return windowManager.getDefaultDisplay().getRotation();
}
/*
* Retrieve the screen orientation from an array string.
*
* @param aArray
* String containing comma-delimited strings.
*
@@ -366,17 +347,16 @@ public class GeckoScreenOrientation {
return Configuration.ORIENTATION_LANDSCAPE;
case NONE:
case DEFAULT:
default:
return Configuration.ORIENTATION_UNDEFINED;
}
}
-
/*
* Convert Gecko screen orientation to Android ActivityInfo orientation.
* This is yet another orientation used by Android, but it's more detailed
* than the Android orientation.
* It is required for screen orientation locking and unlocking.
*
* @param aScreenOrientation
* Gecko screen orientation.
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/ScreenOrientationDelegate.java
@@ -0,0 +1,26 @@
+/* -*- 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;
+
+/**
+ * A <code>ScreenOrientationDelegate</code> is responsible for setting the screen orientation.
+ * <p>
+ * A browser that wants to support the <a href="https://w3c.github.io/screen-orientation/">Screen
+ * Orientation API</a> MUST implement these methods. A GeckoView consumer MAY implement these
+ * methods.
+ * <p> To implement, consider registering an
+ * {@link android.app.Application.ActivityLifecycleCallbacks} handler to track the current
+ * foreground {@link android.app.Activity}.
+ */
+public interface ScreenOrientationDelegate {
+ /**
+ * If possible, set the current screen orientation.
+ *
+ * @param requestedActivityInfoOrientation An orientation constant as used in {@link android.content.pm.ActivityInfo#screenOrientation}.
+ * @return true if screen orientation could be set; false otherwise.
+ */
+ boolean setRequestedOrientationForCurrentActivity(int requestedActivityInfoOrientation);
+}