diff -r 000000000000 -r 6474c204b198 mobile/android/base/GeckoScreenOrientation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/base/GeckoScreenOrientation.java Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,376 @@ +/* -*- 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 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 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), + LANDSCAPE_PRIMARY(1 << 2), + LANDSCAPE_SECONDARY(1 << 3), + DEFAULT(1 << 4); + + public final short value; + + private ScreenOrientation(int value) { + this.value = (short)value; + } + + public static ScreenOrientation get(short value) { + switch (value) { + case (1 << 0): return PORTRAIT_PRIMARY; + case (1 << 1): return PORTRAIT_SECONDARY; + case (1 << 2): return LANDSCAPE_PRIMARY; + case (1 << 3): return LANDSCAPE_SECONDARY; + case (1 << 4): return DEFAULT; + default: return NONE; + } + } + } + + // Singleton instance. + private static GeckoScreenOrientation sInstance = null; + // 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 = GeckoAppShell.getGeckoInterface().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())); + } + + /* + * 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) { + GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenOrientationEvent(mScreenOrientation.value)); + } + 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 Android orientation. + * Retrieve rotation via GeckoAppShell. + * + * @param aAndroidOrientation + * The Android orientation provided by Configuration.orientation. + */ + public void lock(int aAndroidOrientation) { + lock(getScreenOrientation(aAndroidOrientation, getRotation())); + } + + /* + * 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(); + } + + /* + * 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 vie GeckoAppShell. + * + */ + private boolean setRequestedOrientation(ScreenOrientation aScreenOrientation) { + int activityOrientation = screenOrientationToActivityInfoOrientation(aScreenOrientation); + Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); + if (activity == null) { + Log.w(LOGTAG, "setRequestOrientation: failed to get activity"); + } + 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 from Display.getRotation(). + */ + private int getRotation() { + Activity activity = GeckoAppShell.getGeckoInterface().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 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 scren 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) { + if ("portrait".equals(aStr)) { + return ScreenOrientation.PORTRAIT_PRIMARY; + } + else if ("landscape".equals(aStr)) { + return ScreenOrientation.LANDSCAPE_PRIMARY; + } + else if ("portrait-primary".equals(aStr)) { + return ScreenOrientation.PORTRAIT_PRIMARY; + } + else if ("portrait-secondary".equals(aStr)) { + return ScreenOrientation.PORTRAIT_SECONDARY; + } + else if ("landscape-primary".equals(aStr)) { + return ScreenOrientation.LANDSCAPE_PRIMARY; + } + else if ("landscape-secondary".equals(aStr)) { + return ScreenOrientation.LANDSCAPE_SECONDARY; + } + Log.w(LOGTAG, "screenOrientationFromString: unknown orientation string"); + 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_PRIMARY: + case PORTRAIT_SECONDARY: + return Configuration.ORIENTATION_PORTRAIT; + 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_PRIMARY: + return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + case PORTRAIT_SECONDARY: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + 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; + } + } +}