michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko; michael@0: michael@0: import android.content.Context; michael@0: import android.content.pm.ActivityInfo; michael@0: import android.content.res.Configuration; michael@0: import android.util.Log; michael@0: import android.view.Surface; michael@0: import android.app.Activity; michael@0: michael@0: import java.util.Arrays; michael@0: import java.util.List; michael@0: michael@0: /* michael@0: * Updates, locks and unlocks the screen orientation. michael@0: * michael@0: * Note: Replaces the OnOrientationChangeListener to avoid redundant rotation michael@0: * event handling. michael@0: */ michael@0: public class GeckoScreenOrientation { michael@0: private static final String LOGTAG = "GeckoScreenOrientation"; michael@0: michael@0: // Make sure that any change in dom/base/ScreenOrientation.h happens here too. michael@0: public enum ScreenOrientation { michael@0: NONE(0), michael@0: PORTRAIT_PRIMARY(1 << 0), michael@0: PORTRAIT_SECONDARY(1 << 1), michael@0: LANDSCAPE_PRIMARY(1 << 2), michael@0: LANDSCAPE_SECONDARY(1 << 3), michael@0: DEFAULT(1 << 4); michael@0: michael@0: public final short value; michael@0: michael@0: private ScreenOrientation(int value) { michael@0: this.value = (short)value; michael@0: } michael@0: michael@0: public static ScreenOrientation get(short value) { michael@0: switch (value) { michael@0: case (1 << 0): return PORTRAIT_PRIMARY; michael@0: case (1 << 1): return PORTRAIT_SECONDARY; michael@0: case (1 << 2): return LANDSCAPE_PRIMARY; michael@0: case (1 << 3): return LANDSCAPE_SECONDARY; michael@0: case (1 << 4): return DEFAULT; michael@0: default: return NONE; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Singleton instance. michael@0: private static GeckoScreenOrientation sInstance = null; michael@0: // Default screen orientation, used for initialization and unlocking. michael@0: private static final ScreenOrientation DEFAULT_SCREEN_ORIENTATION = ScreenOrientation.DEFAULT; michael@0: // Default rotation, used when device rotation is unknown. michael@0: private static final int DEFAULT_ROTATION = Surface.ROTATION_0; michael@0: // Default orientation, used if screen orientation is unspecified. michael@0: private ScreenOrientation mDefaultScreenOrientation; michael@0: // Last updated screen orientation. michael@0: private ScreenOrientation mScreenOrientation; michael@0: // Whether the update should notify Gecko about screen orientation changes. michael@0: private boolean mShouldNotify = true; michael@0: // Configuration screen orientation preference path. michael@0: private static final String DEFAULT_SCREEN_ORIENTATION_PREF = "app.orientation.default"; michael@0: michael@0: public GeckoScreenOrientation() { michael@0: PrefsHelper.getPref(DEFAULT_SCREEN_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() { michael@0: @Override public void prefValue(String pref, String value) { michael@0: // Read and update the configuration default preference. michael@0: mDefaultScreenOrientation = screenOrientationFromArrayString(value); michael@0: setRequestedOrientation(mDefaultScreenOrientation); michael@0: } michael@0: }); michael@0: michael@0: mDefaultScreenOrientation = DEFAULT_SCREEN_ORIENTATION; michael@0: update(); michael@0: } michael@0: michael@0: public static GeckoScreenOrientation getInstance() { michael@0: if (sInstance == null) { michael@0: sInstance = new GeckoScreenOrientation(); michael@0: } michael@0: return sInstance; michael@0: } michael@0: michael@0: /* michael@0: * Enable Gecko screen orientation events on update. michael@0: */ michael@0: public void enableNotifications() { michael@0: update(); michael@0: mShouldNotify = true; michael@0: } michael@0: michael@0: /* michael@0: * Disable Gecko screen orientation events on update. michael@0: */ michael@0: public void disableNotifications() { michael@0: mShouldNotify = false; michael@0: } michael@0: michael@0: /* michael@0: * Update screen orientation. michael@0: * Retrieve orientation and rotation via GeckoAppShell. michael@0: * michael@0: * @return Whether the screen orientation has changed. michael@0: */ michael@0: public boolean update() { michael@0: Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); michael@0: if (activity == null) { michael@0: return false; michael@0: } michael@0: Configuration config = activity.getResources().getConfiguration(); michael@0: return update(config.orientation); michael@0: } michael@0: michael@0: /* michael@0: * Update screen orientation given the android orientation. michael@0: * Retrieve rotation via GeckoAppShell. michael@0: * michael@0: * @param aAndroidOrientation michael@0: * Android screen orientation from Configuration.orientation. michael@0: * michael@0: * @return Whether the screen orientation has changed. michael@0: */ michael@0: public boolean update(int aAndroidOrientation) { michael@0: return update(getScreenOrientation(aAndroidOrientation, getRotation())); michael@0: } michael@0: michael@0: /* michael@0: * Update screen orientation given the screen orientation. michael@0: * michael@0: * @param aScreenOrientation michael@0: * Gecko screen orientation based on android orientation and rotation. michael@0: * michael@0: * @return Whether the screen orientation has changed. michael@0: */ michael@0: public boolean update(ScreenOrientation aScreenOrientation) { michael@0: if (mScreenOrientation == aScreenOrientation) { michael@0: return false; michael@0: } michael@0: mScreenOrientation = aScreenOrientation; michael@0: Log.d(LOGTAG, "updating to new orientation " + mScreenOrientation); michael@0: if (mShouldNotify) { michael@0: GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenOrientationEvent(mScreenOrientation.value)); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * @return The Android orientation (Configuration.orientation). michael@0: */ michael@0: public int getAndroidOrientation() { michael@0: return screenOrientationToAndroidOrientation(getScreenOrientation()); michael@0: } michael@0: michael@0: /* michael@0: * @return The Gecko screen orientation derived from Android orientation and michael@0: * rotation. michael@0: */ michael@0: public ScreenOrientation getScreenOrientation() { michael@0: return mScreenOrientation; michael@0: } michael@0: michael@0: /* michael@0: * Lock screen orientation given the Android orientation. michael@0: * Retrieve rotation via GeckoAppShell. michael@0: * michael@0: * @param aAndroidOrientation michael@0: * The Android orientation provided by Configuration.orientation. michael@0: */ michael@0: public void lock(int aAndroidOrientation) { michael@0: lock(getScreenOrientation(aAndroidOrientation, getRotation())); michael@0: } michael@0: michael@0: /* michael@0: * Lock screen orientation given the Gecko screen orientation. michael@0: * Retrieve rotation via GeckoAppShell. michael@0: * michael@0: * @param aScreenOrientation michael@0: * Gecko screen orientation derived from Android orientation and michael@0: * rotation. michael@0: * michael@0: * @return Whether the locking was successful. michael@0: */ michael@0: public boolean lock(ScreenOrientation aScreenOrientation) { michael@0: Log.d(LOGTAG, "locking to " + aScreenOrientation); michael@0: update(aScreenOrientation); michael@0: return setRequestedOrientation(aScreenOrientation); michael@0: } michael@0: michael@0: /* michael@0: * Unlock and update screen orientation. michael@0: * michael@0: * @return Whether the unlocking was successful. michael@0: */ michael@0: public boolean unlock() { michael@0: Log.d(LOGTAG, "unlocking"); michael@0: setRequestedOrientation(mDefaultScreenOrientation); michael@0: return update(); michael@0: } michael@0: michael@0: /* michael@0: * Set the given requested orientation for the current activity. michael@0: * This is essentially an unlock without an update. michael@0: * michael@0: * @param aScreenOrientation michael@0: * Gecko screen orientation. michael@0: * michael@0: * @return Whether the requested orientation was set. This can only fail if michael@0: * the current activity cannot be retrieved vie GeckoAppShell. michael@0: * michael@0: */ michael@0: private boolean setRequestedOrientation(ScreenOrientation aScreenOrientation) { michael@0: int activityOrientation = screenOrientationToActivityInfoOrientation(aScreenOrientation); michael@0: Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); michael@0: if (activity == null) { michael@0: Log.w(LOGTAG, "setRequestOrientation: failed to get activity"); michael@0: } michael@0: if (activity.getRequestedOrientation() == activityOrientation) { michael@0: return false; michael@0: } michael@0: activity.setRequestedOrientation(activityOrientation); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Combine the Android orientation and rotation to the Gecko orientation. michael@0: * michael@0: * @param aAndroidOrientation michael@0: * Android orientation from Configuration.orientation. michael@0: * @param aRotation michael@0: * Device rotation from Display.getRotation(). michael@0: * michael@0: * @return Gecko screen orientation. michael@0: */ michael@0: private ScreenOrientation getScreenOrientation(int aAndroidOrientation, int aRotation) { michael@0: boolean isPrimary = aRotation == Surface.ROTATION_0 || aRotation == Surface.ROTATION_90; michael@0: if (aAndroidOrientation == Configuration.ORIENTATION_PORTRAIT) { michael@0: if (isPrimary) { michael@0: // Non-rotated portrait device or landscape device rotated michael@0: // to primary portrait mode counter-clockwise. michael@0: return ScreenOrientation.PORTRAIT_PRIMARY; michael@0: } michael@0: return ScreenOrientation.PORTRAIT_SECONDARY; michael@0: } michael@0: if (aAndroidOrientation == Configuration.ORIENTATION_LANDSCAPE) { michael@0: if (isPrimary) { michael@0: // Non-rotated landscape device or portrait device rotated michael@0: // to primary landscape mode counter-clockwise. michael@0: return ScreenOrientation.LANDSCAPE_PRIMARY; michael@0: } michael@0: return ScreenOrientation.LANDSCAPE_SECONDARY; michael@0: } michael@0: return ScreenOrientation.NONE; michael@0: } michael@0: michael@0: /* michael@0: * @return Device rotation from Display.getRotation(). michael@0: */ michael@0: private int getRotation() { michael@0: Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); michael@0: if (activity == null) { michael@0: Log.w(LOGTAG, "getRotation: failed to get activity"); michael@0: return DEFAULT_ROTATION; michael@0: } michael@0: return activity.getWindowManager().getDefaultDisplay().getRotation(); michael@0: } michael@0: michael@0: /* michael@0: * Retrieve the screen orientation from an array string. michael@0: * michael@0: * @param aArray michael@0: * String containing comma-delimited strings. michael@0: * michael@0: * @return First parsed Gecko screen orientation. michael@0: */ michael@0: public static ScreenOrientation screenOrientationFromArrayString(String aArray) { michael@0: List orientations = Arrays.asList(aArray.split(",")); michael@0: if (orientations.size() == 0) { michael@0: // If nothing is listed, return default. michael@0: Log.w(LOGTAG, "screenOrientationFromArrayString: no orientation in string"); michael@0: return DEFAULT_SCREEN_ORIENTATION; michael@0: } michael@0: michael@0: // We don't support multiple orientations yet. To avoid developer michael@0: // confusion, just take the first one listed. michael@0: return screenOrientationFromString(orientations.get(0)); michael@0: } michael@0: michael@0: /* michael@0: * Retrieve the scren orientation from a string. michael@0: * michael@0: * @param aStr michael@0: * String hopefully containing a screen orientation name. michael@0: * @return Gecko screen orientation if matched, DEFAULT_SCREEN_ORIENTATION michael@0: * otherwise. michael@0: */ michael@0: public static ScreenOrientation screenOrientationFromString(String aStr) { michael@0: if ("portrait".equals(aStr)) { michael@0: return ScreenOrientation.PORTRAIT_PRIMARY; michael@0: } michael@0: else if ("landscape".equals(aStr)) { michael@0: return ScreenOrientation.LANDSCAPE_PRIMARY; michael@0: } michael@0: else if ("portrait-primary".equals(aStr)) { michael@0: return ScreenOrientation.PORTRAIT_PRIMARY; michael@0: } michael@0: else if ("portrait-secondary".equals(aStr)) { michael@0: return ScreenOrientation.PORTRAIT_SECONDARY; michael@0: } michael@0: else if ("landscape-primary".equals(aStr)) { michael@0: return ScreenOrientation.LANDSCAPE_PRIMARY; michael@0: } michael@0: else if ("landscape-secondary".equals(aStr)) { michael@0: return ScreenOrientation.LANDSCAPE_SECONDARY; michael@0: } michael@0: Log.w(LOGTAG, "screenOrientationFromString: unknown orientation string"); michael@0: return DEFAULT_SCREEN_ORIENTATION; michael@0: } michael@0: michael@0: /* michael@0: * Convert Gecko screen orientation to Android orientation. michael@0: * michael@0: * @param aScreenOrientation michael@0: * Gecko screen orientation. michael@0: * @return Android orientation. This conversion is lossy, the Android michael@0: * orientation does not differentiate between primary and secondary michael@0: * orientations. michael@0: */ michael@0: public static int screenOrientationToAndroidOrientation(ScreenOrientation aScreenOrientation) { michael@0: switch (aScreenOrientation) { michael@0: case PORTRAIT_PRIMARY: michael@0: case PORTRAIT_SECONDARY: michael@0: return Configuration.ORIENTATION_PORTRAIT; michael@0: case LANDSCAPE_PRIMARY: michael@0: case LANDSCAPE_SECONDARY: michael@0: return Configuration.ORIENTATION_LANDSCAPE; michael@0: case NONE: michael@0: case DEFAULT: michael@0: default: michael@0: return Configuration.ORIENTATION_UNDEFINED; michael@0: } michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Convert Gecko screen orientation to Android ActivityInfo orientation. michael@0: * This is yet another orientation used by Android, but it's more detailed michael@0: * than the Android orientation. michael@0: * It is required for screen orientation locking and unlocking. michael@0: * michael@0: * @param aScreenOrientation michael@0: * Gecko screen orientation. michael@0: * @return Android ActivityInfo orientation. michael@0: */ michael@0: public static int screenOrientationToActivityInfoOrientation(ScreenOrientation aScreenOrientation) { michael@0: switch (aScreenOrientation) { michael@0: case PORTRAIT_PRIMARY: michael@0: return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; michael@0: case PORTRAIT_SECONDARY: michael@0: return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; michael@0: case LANDSCAPE_PRIMARY: michael@0: return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; michael@0: case LANDSCAPE_SECONDARY: michael@0: return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; michael@0: case DEFAULT: michael@0: case NONE: michael@0: return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; michael@0: default: michael@0: return ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; michael@0: } michael@0: } michael@0: }