1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/GeckoScreenOrientation.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,376 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko; 1.10 + 1.11 +import android.content.Context; 1.12 +import android.content.pm.ActivityInfo; 1.13 +import android.content.res.Configuration; 1.14 +import android.util.Log; 1.15 +import android.view.Surface; 1.16 +import android.app.Activity; 1.17 + 1.18 +import java.util.Arrays; 1.19 +import java.util.List; 1.20 + 1.21 +/* 1.22 + * Updates, locks and unlocks the screen orientation. 1.23 + * 1.24 + * Note: Replaces the OnOrientationChangeListener to avoid redundant rotation 1.25 + * event handling. 1.26 + */ 1.27 +public class GeckoScreenOrientation { 1.28 + private static final String LOGTAG = "GeckoScreenOrientation"; 1.29 + 1.30 + // Make sure that any change in dom/base/ScreenOrientation.h happens here too. 1.31 + public enum ScreenOrientation { 1.32 + NONE(0), 1.33 + PORTRAIT_PRIMARY(1 << 0), 1.34 + PORTRAIT_SECONDARY(1 << 1), 1.35 + LANDSCAPE_PRIMARY(1 << 2), 1.36 + LANDSCAPE_SECONDARY(1 << 3), 1.37 + DEFAULT(1 << 4); 1.38 + 1.39 + public final short value; 1.40 + 1.41 + private ScreenOrientation(int value) { 1.42 + this.value = (short)value; 1.43 + } 1.44 + 1.45 + public static ScreenOrientation get(short value) { 1.46 + switch (value) { 1.47 + case (1 << 0): return PORTRAIT_PRIMARY; 1.48 + case (1 << 1): return PORTRAIT_SECONDARY; 1.49 + case (1 << 2): return LANDSCAPE_PRIMARY; 1.50 + case (1 << 3): return LANDSCAPE_SECONDARY; 1.51 + case (1 << 4): return DEFAULT; 1.52 + default: return NONE; 1.53 + } 1.54 + } 1.55 + } 1.56 + 1.57 + // Singleton instance. 1.58 + private static GeckoScreenOrientation sInstance = null; 1.59 + // Default screen orientation, used for initialization and unlocking. 1.60 + private static final ScreenOrientation DEFAULT_SCREEN_ORIENTATION = ScreenOrientation.DEFAULT; 1.61 + // Default rotation, used when device rotation is unknown. 1.62 + private static final int DEFAULT_ROTATION = Surface.ROTATION_0; 1.63 + // Default orientation, used if screen orientation is unspecified. 1.64 + private ScreenOrientation mDefaultScreenOrientation; 1.65 + // Last updated screen orientation. 1.66 + private ScreenOrientation mScreenOrientation; 1.67 + // Whether the update should notify Gecko about screen orientation changes. 1.68 + private boolean mShouldNotify = true; 1.69 + // Configuration screen orientation preference path. 1.70 + private static final String DEFAULT_SCREEN_ORIENTATION_PREF = "app.orientation.default"; 1.71 + 1.72 + public GeckoScreenOrientation() { 1.73 + PrefsHelper.getPref(DEFAULT_SCREEN_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() { 1.74 + @Override public void prefValue(String pref, String value) { 1.75 + // Read and update the configuration default preference. 1.76 + mDefaultScreenOrientation = screenOrientationFromArrayString(value); 1.77 + setRequestedOrientation(mDefaultScreenOrientation); 1.78 + } 1.79 + }); 1.80 + 1.81 + mDefaultScreenOrientation = DEFAULT_SCREEN_ORIENTATION; 1.82 + update(); 1.83 + } 1.84 + 1.85 + public static GeckoScreenOrientation getInstance() { 1.86 + if (sInstance == null) { 1.87 + sInstance = new GeckoScreenOrientation(); 1.88 + } 1.89 + return sInstance; 1.90 + } 1.91 + 1.92 + /* 1.93 + * Enable Gecko screen orientation events on update. 1.94 + */ 1.95 + public void enableNotifications() { 1.96 + update(); 1.97 + mShouldNotify = true; 1.98 + } 1.99 + 1.100 + /* 1.101 + * Disable Gecko screen orientation events on update. 1.102 + */ 1.103 + public void disableNotifications() { 1.104 + mShouldNotify = false; 1.105 + } 1.106 + 1.107 + /* 1.108 + * Update screen orientation. 1.109 + * Retrieve orientation and rotation via GeckoAppShell. 1.110 + * 1.111 + * @return Whether the screen orientation has changed. 1.112 + */ 1.113 + public boolean update() { 1.114 + Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); 1.115 + if (activity == null) { 1.116 + return false; 1.117 + } 1.118 + Configuration config = activity.getResources().getConfiguration(); 1.119 + return update(config.orientation); 1.120 + } 1.121 + 1.122 + /* 1.123 + * Update screen orientation given the android orientation. 1.124 + * Retrieve rotation via GeckoAppShell. 1.125 + * 1.126 + * @param aAndroidOrientation 1.127 + * Android screen orientation from Configuration.orientation. 1.128 + * 1.129 + * @return Whether the screen orientation has changed. 1.130 + */ 1.131 + public boolean update(int aAndroidOrientation) { 1.132 + return update(getScreenOrientation(aAndroidOrientation, getRotation())); 1.133 + } 1.134 + 1.135 + /* 1.136 + * Update screen orientation given the screen orientation. 1.137 + * 1.138 + * @param aScreenOrientation 1.139 + * Gecko screen orientation based on android orientation and rotation. 1.140 + * 1.141 + * @return Whether the screen orientation has changed. 1.142 + */ 1.143 + public boolean update(ScreenOrientation aScreenOrientation) { 1.144 + if (mScreenOrientation == aScreenOrientation) { 1.145 + return false; 1.146 + } 1.147 + mScreenOrientation = aScreenOrientation; 1.148 + Log.d(LOGTAG, "updating to new orientation " + mScreenOrientation); 1.149 + if (mShouldNotify) { 1.150 + GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenOrientationEvent(mScreenOrientation.value)); 1.151 + } 1.152 + return true; 1.153 + } 1.154 + 1.155 + /* 1.156 + * @return The Android orientation (Configuration.orientation). 1.157 + */ 1.158 + public int getAndroidOrientation() { 1.159 + return screenOrientationToAndroidOrientation(getScreenOrientation()); 1.160 + } 1.161 + 1.162 + /* 1.163 + * @return The Gecko screen orientation derived from Android orientation and 1.164 + * rotation. 1.165 + */ 1.166 + public ScreenOrientation getScreenOrientation() { 1.167 + return mScreenOrientation; 1.168 + } 1.169 + 1.170 + /* 1.171 + * Lock screen orientation given the Android orientation. 1.172 + * Retrieve rotation via GeckoAppShell. 1.173 + * 1.174 + * @param aAndroidOrientation 1.175 + * The Android orientation provided by Configuration.orientation. 1.176 + */ 1.177 + public void lock(int aAndroidOrientation) { 1.178 + lock(getScreenOrientation(aAndroidOrientation, getRotation())); 1.179 + } 1.180 + 1.181 + /* 1.182 + * Lock screen orientation given the Gecko screen orientation. 1.183 + * Retrieve rotation via GeckoAppShell. 1.184 + * 1.185 + * @param aScreenOrientation 1.186 + * Gecko screen orientation derived from Android orientation and 1.187 + * rotation. 1.188 + * 1.189 + * @return Whether the locking was successful. 1.190 + */ 1.191 + public boolean lock(ScreenOrientation aScreenOrientation) { 1.192 + Log.d(LOGTAG, "locking to " + aScreenOrientation); 1.193 + update(aScreenOrientation); 1.194 + return setRequestedOrientation(aScreenOrientation); 1.195 + } 1.196 + 1.197 + /* 1.198 + * Unlock and update screen orientation. 1.199 + * 1.200 + * @return Whether the unlocking was successful. 1.201 + */ 1.202 + public boolean unlock() { 1.203 + Log.d(LOGTAG, "unlocking"); 1.204 + setRequestedOrientation(mDefaultScreenOrientation); 1.205 + return update(); 1.206 + } 1.207 + 1.208 + /* 1.209 + * Set the given requested orientation for the current activity. 1.210 + * This is essentially an unlock without an update. 1.211 + * 1.212 + * @param aScreenOrientation 1.213 + * Gecko screen orientation. 1.214 + * 1.215 + * @return Whether the requested orientation was set. This can only fail if 1.216 + * the current activity cannot be retrieved vie GeckoAppShell. 1.217 + * 1.218 + */ 1.219 + private boolean setRequestedOrientation(ScreenOrientation aScreenOrientation) { 1.220 + int activityOrientation = screenOrientationToActivityInfoOrientation(aScreenOrientation); 1.221 + Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); 1.222 + if (activity == null) { 1.223 + Log.w(LOGTAG, "setRequestOrientation: failed to get activity"); 1.224 + } 1.225 + if (activity.getRequestedOrientation() == activityOrientation) { 1.226 + return false; 1.227 + } 1.228 + activity.setRequestedOrientation(activityOrientation); 1.229 + return true; 1.230 + } 1.231 + 1.232 + /* 1.233 + * Combine the Android orientation and rotation to the Gecko orientation. 1.234 + * 1.235 + * @param aAndroidOrientation 1.236 + * Android orientation from Configuration.orientation. 1.237 + * @param aRotation 1.238 + * Device rotation from Display.getRotation(). 1.239 + * 1.240 + * @return Gecko screen orientation. 1.241 + */ 1.242 + private ScreenOrientation getScreenOrientation(int aAndroidOrientation, int aRotation) { 1.243 + boolean isPrimary = aRotation == Surface.ROTATION_0 || aRotation == Surface.ROTATION_90; 1.244 + if (aAndroidOrientation == Configuration.ORIENTATION_PORTRAIT) { 1.245 + if (isPrimary) { 1.246 + // Non-rotated portrait device or landscape device rotated 1.247 + // to primary portrait mode counter-clockwise. 1.248 + return ScreenOrientation.PORTRAIT_PRIMARY; 1.249 + } 1.250 + return ScreenOrientation.PORTRAIT_SECONDARY; 1.251 + } 1.252 + if (aAndroidOrientation == Configuration.ORIENTATION_LANDSCAPE) { 1.253 + if (isPrimary) { 1.254 + // Non-rotated landscape device or portrait device rotated 1.255 + // to primary landscape mode counter-clockwise. 1.256 + return ScreenOrientation.LANDSCAPE_PRIMARY; 1.257 + } 1.258 + return ScreenOrientation.LANDSCAPE_SECONDARY; 1.259 + } 1.260 + return ScreenOrientation.NONE; 1.261 + } 1.262 + 1.263 + /* 1.264 + * @return Device rotation from Display.getRotation(). 1.265 + */ 1.266 + private int getRotation() { 1.267 + Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); 1.268 + if (activity == null) { 1.269 + Log.w(LOGTAG, "getRotation: failed to get activity"); 1.270 + return DEFAULT_ROTATION; 1.271 + } 1.272 + return activity.getWindowManager().getDefaultDisplay().getRotation(); 1.273 + } 1.274 + 1.275 + /* 1.276 + * Retrieve the screen orientation from an array string. 1.277 + * 1.278 + * @param aArray 1.279 + * String containing comma-delimited strings. 1.280 + * 1.281 + * @return First parsed Gecko screen orientation. 1.282 + */ 1.283 + public static ScreenOrientation screenOrientationFromArrayString(String aArray) { 1.284 + List<String> orientations = Arrays.asList(aArray.split(",")); 1.285 + if (orientations.size() == 0) { 1.286 + // If nothing is listed, return default. 1.287 + Log.w(LOGTAG, "screenOrientationFromArrayString: no orientation in string"); 1.288 + return DEFAULT_SCREEN_ORIENTATION; 1.289 + } 1.290 + 1.291 + // We don't support multiple orientations yet. To avoid developer 1.292 + // confusion, just take the first one listed. 1.293 + return screenOrientationFromString(orientations.get(0)); 1.294 + } 1.295 + 1.296 + /* 1.297 + * Retrieve the scren orientation from a string. 1.298 + * 1.299 + * @param aStr 1.300 + * String hopefully containing a screen orientation name. 1.301 + * @return Gecko screen orientation if matched, DEFAULT_SCREEN_ORIENTATION 1.302 + * otherwise. 1.303 + */ 1.304 + public static ScreenOrientation screenOrientationFromString(String aStr) { 1.305 + if ("portrait".equals(aStr)) { 1.306 + return ScreenOrientation.PORTRAIT_PRIMARY; 1.307 + } 1.308 + else if ("landscape".equals(aStr)) { 1.309 + return ScreenOrientation.LANDSCAPE_PRIMARY; 1.310 + } 1.311 + else if ("portrait-primary".equals(aStr)) { 1.312 + return ScreenOrientation.PORTRAIT_PRIMARY; 1.313 + } 1.314 + else if ("portrait-secondary".equals(aStr)) { 1.315 + return ScreenOrientation.PORTRAIT_SECONDARY; 1.316 + } 1.317 + else if ("landscape-primary".equals(aStr)) { 1.318 + return ScreenOrientation.LANDSCAPE_PRIMARY; 1.319 + } 1.320 + else if ("landscape-secondary".equals(aStr)) { 1.321 + return ScreenOrientation.LANDSCAPE_SECONDARY; 1.322 + } 1.323 + Log.w(LOGTAG, "screenOrientationFromString: unknown orientation string"); 1.324 + return DEFAULT_SCREEN_ORIENTATION; 1.325 + } 1.326 + 1.327 + /* 1.328 + * Convert Gecko screen orientation to Android orientation. 1.329 + * 1.330 + * @param aScreenOrientation 1.331 + * Gecko screen orientation. 1.332 + * @return Android orientation. This conversion is lossy, the Android 1.333 + * orientation does not differentiate between primary and secondary 1.334 + * orientations. 1.335 + */ 1.336 + public static int screenOrientationToAndroidOrientation(ScreenOrientation aScreenOrientation) { 1.337 + switch (aScreenOrientation) { 1.338 + case PORTRAIT_PRIMARY: 1.339 + case PORTRAIT_SECONDARY: 1.340 + return Configuration.ORIENTATION_PORTRAIT; 1.341 + case LANDSCAPE_PRIMARY: 1.342 + case LANDSCAPE_SECONDARY: 1.343 + return Configuration.ORIENTATION_LANDSCAPE; 1.344 + case NONE: 1.345 + case DEFAULT: 1.346 + default: 1.347 + return Configuration.ORIENTATION_UNDEFINED; 1.348 + } 1.349 + } 1.350 + 1.351 + 1.352 + /* 1.353 + * Convert Gecko screen orientation to Android ActivityInfo orientation. 1.354 + * This is yet another orientation used by Android, but it's more detailed 1.355 + * than the Android orientation. 1.356 + * It is required for screen orientation locking and unlocking. 1.357 + * 1.358 + * @param aScreenOrientation 1.359 + * Gecko screen orientation. 1.360 + * @return Android ActivityInfo orientation. 1.361 + */ 1.362 + public static int screenOrientationToActivityInfoOrientation(ScreenOrientation aScreenOrientation) { 1.363 + switch (aScreenOrientation) { 1.364 + case PORTRAIT_PRIMARY: 1.365 + return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 1.366 + case PORTRAIT_SECONDARY: 1.367 + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; 1.368 + case LANDSCAPE_PRIMARY: 1.369 + return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 1.370 + case LANDSCAPE_SECONDARY: 1.371 + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; 1.372 + case DEFAULT: 1.373 + case NONE: 1.374 + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 1.375 + default: 1.376 + return ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 1.377 + } 1.378 + } 1.379 +}