diff options
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java')
-rw-r--r-- | mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java new file mode 100644 index 000000000..23f84f52a --- /dev/null +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java @@ -0,0 +1,423 @@ +/* -*- 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.pm.ActivityInfo; +import android.content.res.Configuration; +import android.util.Log; +import android.view.Surface; +import android.app.Activity; + +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. + */ +public class GeckoScreenOrientation { + private static final String LOGTAG = "GeckoScreenOrientation"; + + // Make sure that any change in dom/base/ScreenOrientation.h happens here too. + public enum ScreenOrientation { + NONE(0), + PORTRAIT_PRIMARY(1 << 0), + PORTRAIT_SECONDARY(1 << 1), + PORTRAIT(PORTRAIT_PRIMARY.value | PORTRAIT_SECONDARY.value), + LANDSCAPE_PRIMARY(1 << 2), + LANDSCAPE_SECONDARY(1 << 3), + LANDSCAPE(LANDSCAPE_PRIMARY.value | LANDSCAPE_SECONDARY.value), + DEFAULT(1 << 4); + + public final short value; + + private ScreenOrientation(int value) { + this.value = (short)value; + } + + private final static ScreenOrientation[] sValues = ScreenOrientation.values(); + + public static ScreenOrientation get(int value) { + for (ScreenOrientation orient: sValues) { + if (orient.value == value) { + return orient; + } + } + return NONE; + } + } + + // Singleton instance. + private static GeckoScreenOrientation sInstance; + // Default screen orientation, used for initialization and unlocking. + private static final ScreenOrientation DEFAULT_SCREEN_ORIENTATION = ScreenOrientation.DEFAULT; + // Default rotation, used when device rotation is unknown. + private static final int DEFAULT_ROTATION = Surface.ROTATION_0; + // Default orientation, used if screen orientation is unspecified. + private ScreenOrientation mDefaultScreenOrientation; + // Last updated screen orientation. + private ScreenOrientation mScreenOrientation; + // Whether the update should notify Gecko about screen orientation changes. + private boolean mShouldNotify = true; + // Configuration screen orientation preference path. + private static final String DEFAULT_SCREEN_ORIENTATION_PREF = "app.orientation.default"; + + public GeckoScreenOrientation() { + PrefsHelper.getPref(DEFAULT_SCREEN_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() { + @Override public void prefValue(String pref, String value) { + // Read and update the configuration default preference. + mDefaultScreenOrientation = screenOrientationFromArrayString(value); + setRequestedOrientation(mDefaultScreenOrientation); + } + }); + + mDefaultScreenOrientation = DEFAULT_SCREEN_ORIENTATION; + update(); + } + + public static GeckoScreenOrientation getInstance() { + if (sInstance == null) { + sInstance = new GeckoScreenOrientation(); + } + return sInstance; + } + + /* + * Enable Gecko screen orientation events on update. + */ + public void enableNotifications() { + update(); + mShouldNotify = true; + } + + /* + * Disable Gecko screen orientation events on update. + */ + public void disableNotifications() { + mShouldNotify = false; + } + + /* + * 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) { + return false; + } + Configuration config = activity.getResources().getConfiguration(); + return update(config.orientation); + } + + /* + * Update screen orientation given the android orientation. + * Retrieve rotation via GeckoAppShell. + * + * @param aAndroidOrientation + * Android screen orientation from Configuration.orientation. + * + * @return Whether the screen orientation has changed. + */ + public boolean update(int aAndroidOrientation) { + return update(getScreenOrientation(aAndroidOrientation, getRotation())); + } + + @WrapForJNI(dispatchTo = "gecko") + private static native void onOrientationChange(short screenOrientation, short angle); + + /* + * Update screen orientation given the screen orientation. + * + * @param aScreenOrientation + * Gecko screen orientation based on android orientation and rotation. + * + * @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); + 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; + } + + if (GeckoThread.isRunning()) { + onOrientationChange(aScreenOrientation.value, getAngle()); + } else { + GeckoThread.queueNativeCall(GeckoScreenOrientation.class, "onOrientationChange", + aScreenOrientation.value, getAngle()); + } + } + GeckoAppShell.resetScreenSize(); + return true; + } + + /* + * @return The Android orientation (Configuration.orientation). + */ + public int getAndroidOrientation() { + return screenOrientationToAndroidOrientation(getScreenOrientation()); + } + + /* + * @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); + } + + /* + * 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"); + 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 + * Device rotation from Display.getRotation(). + * + * @return Gecko screen orientation. + */ + private ScreenOrientation getScreenOrientation(int aAndroidOrientation, int aRotation) { + boolean isPrimary = aRotation == Surface.ROTATION_0 || aRotation == Surface.ROTATION_90; + if (aAndroidOrientation == Configuration.ORIENTATION_PORTRAIT) { + if (isPrimary) { + // Non-rotated portrait device or landscape device rotated + // to primary portrait mode counter-clockwise. + return ScreenOrientation.PORTRAIT_PRIMARY; + } + return ScreenOrientation.PORTRAIT_SECONDARY; + } + if (aAndroidOrientation == Configuration.ORIENTATION_LANDSCAPE) { + if (isPrimary) { + // Non-rotated landscape device or portrait device rotated + // to primary landscape mode counter-clockwise. + return ScreenOrientation.LANDSCAPE_PRIMARY; + } + return ScreenOrientation.LANDSCAPE_SECONDARY; + } + return ScreenOrientation.NONE; + } + + /* + * @return Device rotation converted to an angle. + */ + public short getAngle() { + switch (getRotation()) { + case Surface.ROTATION_0: + return 0; + case Surface.ROTATION_90: + return 90; + case Surface.ROTATION_180: + return 180; + case Surface.ROTATION_270: + return 270; + default: + Log.w(LOGTAG, "getAngle: unexpected rotation value"); + return 0; + } + } + + /* + * @return Device rotation from Display.getRotation(). + */ + private int getRotation() { + Activity activity = getActivity(); + if (activity == null) { + Log.w(LOGTAG, "getRotation: failed to get activity"); + return DEFAULT_ROTATION; + } + return activity.getWindowManager().getDefaultDisplay().getRotation(); + } + + /* + * Retrieve the screen orientation from an array string. + * + * @param aArray + * String containing comma-delimited strings. + * + * @return First parsed Gecko screen orientation. + */ + public static ScreenOrientation screenOrientationFromArrayString(String aArray) { + List<String> orientations = Arrays.asList(aArray.split(",")); + if (orientations.size() == 0) { + // If nothing is listed, return default. + Log.w(LOGTAG, "screenOrientationFromArrayString: no orientation in string"); + return DEFAULT_SCREEN_ORIENTATION; + } + + // We don't support multiple orientations yet. To avoid developer + // confusion, just take the first one listed. + return screenOrientationFromString(orientations.get(0)); + } + + /* + * Retrieve the screen orientation from a string. + * + * @param aStr + * String hopefully containing a screen orientation name. + * @return Gecko screen orientation if matched, DEFAULT_SCREEN_ORIENTATION + * otherwise. + */ + public static ScreenOrientation screenOrientationFromString(String aStr) { + switch (aStr) { + case "portrait": + return ScreenOrientation.PORTRAIT; + case "landscape": + return ScreenOrientation.LANDSCAPE; + case "portrait-primary": + return ScreenOrientation.PORTRAIT_PRIMARY; + case "portrait-secondary": + return ScreenOrientation.PORTRAIT_SECONDARY; + case "landscape-primary": + return ScreenOrientation.LANDSCAPE_PRIMARY; + case "landscape-secondary": + return ScreenOrientation.LANDSCAPE_SECONDARY; + } + + Log.w(LOGTAG, "screenOrientationFromString: unknown orientation string: " + aStr); + return DEFAULT_SCREEN_ORIENTATION; + } + + /* + * Convert Gecko screen orientation to Android orientation. + * + * @param aScreenOrientation + * Gecko screen orientation. + * @return Android orientation. This conversion is lossy, the Android + * orientation does not differentiate between primary and secondary + * orientations. + */ + public static int screenOrientationToAndroidOrientation(ScreenOrientation aScreenOrientation) { + switch (aScreenOrientation) { + case PORTRAIT: + case PORTRAIT_PRIMARY: + case PORTRAIT_SECONDARY: + return Configuration.ORIENTATION_PORTRAIT; + case LANDSCAPE: + case LANDSCAPE_PRIMARY: + case LANDSCAPE_SECONDARY: + 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. + * @return Android ActivityInfo orientation. + */ + public static int screenOrientationToActivityInfoOrientation(ScreenOrientation aScreenOrientation) { + switch (aScreenOrientation) { + case PORTRAIT: + case PORTRAIT_PRIMARY: + return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + case PORTRAIT_SECONDARY: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + case LANDSCAPE: + case LANDSCAPE_PRIMARY: + return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + case LANDSCAPE_SECONDARY: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + case DEFAULT: + case NONE: + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + default: + return ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; + } + } +} |