|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko; |
|
7 |
|
8 import android.content.Context; |
|
9 import android.content.pm.ActivityInfo; |
|
10 import android.content.res.Configuration; |
|
11 import android.util.Log; |
|
12 import android.view.Surface; |
|
13 import android.app.Activity; |
|
14 |
|
15 import java.util.Arrays; |
|
16 import java.util.List; |
|
17 |
|
18 /* |
|
19 * Updates, locks and unlocks the screen orientation. |
|
20 * |
|
21 * Note: Replaces the OnOrientationChangeListener to avoid redundant rotation |
|
22 * event handling. |
|
23 */ |
|
24 public class GeckoScreenOrientation { |
|
25 private static final String LOGTAG = "GeckoScreenOrientation"; |
|
26 |
|
27 // Make sure that any change in dom/base/ScreenOrientation.h happens here too. |
|
28 public enum ScreenOrientation { |
|
29 NONE(0), |
|
30 PORTRAIT_PRIMARY(1 << 0), |
|
31 PORTRAIT_SECONDARY(1 << 1), |
|
32 LANDSCAPE_PRIMARY(1 << 2), |
|
33 LANDSCAPE_SECONDARY(1 << 3), |
|
34 DEFAULT(1 << 4); |
|
35 |
|
36 public final short value; |
|
37 |
|
38 private ScreenOrientation(int value) { |
|
39 this.value = (short)value; |
|
40 } |
|
41 |
|
42 public static ScreenOrientation get(short value) { |
|
43 switch (value) { |
|
44 case (1 << 0): return PORTRAIT_PRIMARY; |
|
45 case (1 << 1): return PORTRAIT_SECONDARY; |
|
46 case (1 << 2): return LANDSCAPE_PRIMARY; |
|
47 case (1 << 3): return LANDSCAPE_SECONDARY; |
|
48 case (1 << 4): return DEFAULT; |
|
49 default: return NONE; |
|
50 } |
|
51 } |
|
52 } |
|
53 |
|
54 // Singleton instance. |
|
55 private static GeckoScreenOrientation sInstance = null; |
|
56 // Default screen orientation, used for initialization and unlocking. |
|
57 private static final ScreenOrientation DEFAULT_SCREEN_ORIENTATION = ScreenOrientation.DEFAULT; |
|
58 // Default rotation, used when device rotation is unknown. |
|
59 private static final int DEFAULT_ROTATION = Surface.ROTATION_0; |
|
60 // Default orientation, used if screen orientation is unspecified. |
|
61 private ScreenOrientation mDefaultScreenOrientation; |
|
62 // Last updated screen orientation. |
|
63 private ScreenOrientation mScreenOrientation; |
|
64 // Whether the update should notify Gecko about screen orientation changes. |
|
65 private boolean mShouldNotify = true; |
|
66 // Configuration screen orientation preference path. |
|
67 private static final String DEFAULT_SCREEN_ORIENTATION_PREF = "app.orientation.default"; |
|
68 |
|
69 public GeckoScreenOrientation() { |
|
70 PrefsHelper.getPref(DEFAULT_SCREEN_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() { |
|
71 @Override public void prefValue(String pref, String value) { |
|
72 // Read and update the configuration default preference. |
|
73 mDefaultScreenOrientation = screenOrientationFromArrayString(value); |
|
74 setRequestedOrientation(mDefaultScreenOrientation); |
|
75 } |
|
76 }); |
|
77 |
|
78 mDefaultScreenOrientation = DEFAULT_SCREEN_ORIENTATION; |
|
79 update(); |
|
80 } |
|
81 |
|
82 public static GeckoScreenOrientation getInstance() { |
|
83 if (sInstance == null) { |
|
84 sInstance = new GeckoScreenOrientation(); |
|
85 } |
|
86 return sInstance; |
|
87 } |
|
88 |
|
89 /* |
|
90 * Enable Gecko screen orientation events on update. |
|
91 */ |
|
92 public void enableNotifications() { |
|
93 update(); |
|
94 mShouldNotify = true; |
|
95 } |
|
96 |
|
97 /* |
|
98 * Disable Gecko screen orientation events on update. |
|
99 */ |
|
100 public void disableNotifications() { |
|
101 mShouldNotify = false; |
|
102 } |
|
103 |
|
104 /* |
|
105 * Update screen orientation. |
|
106 * Retrieve orientation and rotation via GeckoAppShell. |
|
107 * |
|
108 * @return Whether the screen orientation has changed. |
|
109 */ |
|
110 public boolean update() { |
|
111 Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); |
|
112 if (activity == null) { |
|
113 return false; |
|
114 } |
|
115 Configuration config = activity.getResources().getConfiguration(); |
|
116 return update(config.orientation); |
|
117 } |
|
118 |
|
119 /* |
|
120 * Update screen orientation given the android orientation. |
|
121 * Retrieve rotation via GeckoAppShell. |
|
122 * |
|
123 * @param aAndroidOrientation |
|
124 * Android screen orientation from Configuration.orientation. |
|
125 * |
|
126 * @return Whether the screen orientation has changed. |
|
127 */ |
|
128 public boolean update(int aAndroidOrientation) { |
|
129 return update(getScreenOrientation(aAndroidOrientation, getRotation())); |
|
130 } |
|
131 |
|
132 /* |
|
133 * Update screen orientation given the screen orientation. |
|
134 * |
|
135 * @param aScreenOrientation |
|
136 * Gecko screen orientation based on android orientation and rotation. |
|
137 * |
|
138 * @return Whether the screen orientation has changed. |
|
139 */ |
|
140 public boolean update(ScreenOrientation aScreenOrientation) { |
|
141 if (mScreenOrientation == aScreenOrientation) { |
|
142 return false; |
|
143 } |
|
144 mScreenOrientation = aScreenOrientation; |
|
145 Log.d(LOGTAG, "updating to new orientation " + mScreenOrientation); |
|
146 if (mShouldNotify) { |
|
147 GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenOrientationEvent(mScreenOrientation.value)); |
|
148 } |
|
149 return true; |
|
150 } |
|
151 |
|
152 /* |
|
153 * @return The Android orientation (Configuration.orientation). |
|
154 */ |
|
155 public int getAndroidOrientation() { |
|
156 return screenOrientationToAndroidOrientation(getScreenOrientation()); |
|
157 } |
|
158 |
|
159 /* |
|
160 * @return The Gecko screen orientation derived from Android orientation and |
|
161 * rotation. |
|
162 */ |
|
163 public ScreenOrientation getScreenOrientation() { |
|
164 return mScreenOrientation; |
|
165 } |
|
166 |
|
167 /* |
|
168 * Lock screen orientation given the Android orientation. |
|
169 * Retrieve rotation via GeckoAppShell. |
|
170 * |
|
171 * @param aAndroidOrientation |
|
172 * The Android orientation provided by Configuration.orientation. |
|
173 */ |
|
174 public void lock(int aAndroidOrientation) { |
|
175 lock(getScreenOrientation(aAndroidOrientation, getRotation())); |
|
176 } |
|
177 |
|
178 /* |
|
179 * Lock screen orientation given the Gecko screen orientation. |
|
180 * Retrieve rotation via GeckoAppShell. |
|
181 * |
|
182 * @param aScreenOrientation |
|
183 * Gecko screen orientation derived from Android orientation and |
|
184 * rotation. |
|
185 * |
|
186 * @return Whether the locking was successful. |
|
187 */ |
|
188 public boolean lock(ScreenOrientation aScreenOrientation) { |
|
189 Log.d(LOGTAG, "locking to " + aScreenOrientation); |
|
190 update(aScreenOrientation); |
|
191 return setRequestedOrientation(aScreenOrientation); |
|
192 } |
|
193 |
|
194 /* |
|
195 * Unlock and update screen orientation. |
|
196 * |
|
197 * @return Whether the unlocking was successful. |
|
198 */ |
|
199 public boolean unlock() { |
|
200 Log.d(LOGTAG, "unlocking"); |
|
201 setRequestedOrientation(mDefaultScreenOrientation); |
|
202 return update(); |
|
203 } |
|
204 |
|
205 /* |
|
206 * Set the given requested orientation for the current activity. |
|
207 * This is essentially an unlock without an update. |
|
208 * |
|
209 * @param aScreenOrientation |
|
210 * Gecko screen orientation. |
|
211 * |
|
212 * @return Whether the requested orientation was set. This can only fail if |
|
213 * the current activity cannot be retrieved vie GeckoAppShell. |
|
214 * |
|
215 */ |
|
216 private boolean setRequestedOrientation(ScreenOrientation aScreenOrientation) { |
|
217 int activityOrientation = screenOrientationToActivityInfoOrientation(aScreenOrientation); |
|
218 Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); |
|
219 if (activity == null) { |
|
220 Log.w(LOGTAG, "setRequestOrientation: failed to get activity"); |
|
221 } |
|
222 if (activity.getRequestedOrientation() == activityOrientation) { |
|
223 return false; |
|
224 } |
|
225 activity.setRequestedOrientation(activityOrientation); |
|
226 return true; |
|
227 } |
|
228 |
|
229 /* |
|
230 * Combine the Android orientation and rotation to the Gecko orientation. |
|
231 * |
|
232 * @param aAndroidOrientation |
|
233 * Android orientation from Configuration.orientation. |
|
234 * @param aRotation |
|
235 * Device rotation from Display.getRotation(). |
|
236 * |
|
237 * @return Gecko screen orientation. |
|
238 */ |
|
239 private ScreenOrientation getScreenOrientation(int aAndroidOrientation, int aRotation) { |
|
240 boolean isPrimary = aRotation == Surface.ROTATION_0 || aRotation == Surface.ROTATION_90; |
|
241 if (aAndroidOrientation == Configuration.ORIENTATION_PORTRAIT) { |
|
242 if (isPrimary) { |
|
243 // Non-rotated portrait device or landscape device rotated |
|
244 // to primary portrait mode counter-clockwise. |
|
245 return ScreenOrientation.PORTRAIT_PRIMARY; |
|
246 } |
|
247 return ScreenOrientation.PORTRAIT_SECONDARY; |
|
248 } |
|
249 if (aAndroidOrientation == Configuration.ORIENTATION_LANDSCAPE) { |
|
250 if (isPrimary) { |
|
251 // Non-rotated landscape device or portrait device rotated |
|
252 // to primary landscape mode counter-clockwise. |
|
253 return ScreenOrientation.LANDSCAPE_PRIMARY; |
|
254 } |
|
255 return ScreenOrientation.LANDSCAPE_SECONDARY; |
|
256 } |
|
257 return ScreenOrientation.NONE; |
|
258 } |
|
259 |
|
260 /* |
|
261 * @return Device rotation from Display.getRotation(). |
|
262 */ |
|
263 private int getRotation() { |
|
264 Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); |
|
265 if (activity == null) { |
|
266 Log.w(LOGTAG, "getRotation: failed to get activity"); |
|
267 return DEFAULT_ROTATION; |
|
268 } |
|
269 return activity.getWindowManager().getDefaultDisplay().getRotation(); |
|
270 } |
|
271 |
|
272 /* |
|
273 * Retrieve the screen orientation from an array string. |
|
274 * |
|
275 * @param aArray |
|
276 * String containing comma-delimited strings. |
|
277 * |
|
278 * @return First parsed Gecko screen orientation. |
|
279 */ |
|
280 public static ScreenOrientation screenOrientationFromArrayString(String aArray) { |
|
281 List<String> orientations = Arrays.asList(aArray.split(",")); |
|
282 if (orientations.size() == 0) { |
|
283 // If nothing is listed, return default. |
|
284 Log.w(LOGTAG, "screenOrientationFromArrayString: no orientation in string"); |
|
285 return DEFAULT_SCREEN_ORIENTATION; |
|
286 } |
|
287 |
|
288 // We don't support multiple orientations yet. To avoid developer |
|
289 // confusion, just take the first one listed. |
|
290 return screenOrientationFromString(orientations.get(0)); |
|
291 } |
|
292 |
|
293 /* |
|
294 * Retrieve the scren orientation from a string. |
|
295 * |
|
296 * @param aStr |
|
297 * String hopefully containing a screen orientation name. |
|
298 * @return Gecko screen orientation if matched, DEFAULT_SCREEN_ORIENTATION |
|
299 * otherwise. |
|
300 */ |
|
301 public static ScreenOrientation screenOrientationFromString(String aStr) { |
|
302 if ("portrait".equals(aStr)) { |
|
303 return ScreenOrientation.PORTRAIT_PRIMARY; |
|
304 } |
|
305 else if ("landscape".equals(aStr)) { |
|
306 return ScreenOrientation.LANDSCAPE_PRIMARY; |
|
307 } |
|
308 else if ("portrait-primary".equals(aStr)) { |
|
309 return ScreenOrientation.PORTRAIT_PRIMARY; |
|
310 } |
|
311 else if ("portrait-secondary".equals(aStr)) { |
|
312 return ScreenOrientation.PORTRAIT_SECONDARY; |
|
313 } |
|
314 else if ("landscape-primary".equals(aStr)) { |
|
315 return ScreenOrientation.LANDSCAPE_PRIMARY; |
|
316 } |
|
317 else if ("landscape-secondary".equals(aStr)) { |
|
318 return ScreenOrientation.LANDSCAPE_SECONDARY; |
|
319 } |
|
320 Log.w(LOGTAG, "screenOrientationFromString: unknown orientation string"); |
|
321 return DEFAULT_SCREEN_ORIENTATION; |
|
322 } |
|
323 |
|
324 /* |
|
325 * Convert Gecko screen orientation to Android orientation. |
|
326 * |
|
327 * @param aScreenOrientation |
|
328 * Gecko screen orientation. |
|
329 * @return Android orientation. This conversion is lossy, the Android |
|
330 * orientation does not differentiate between primary and secondary |
|
331 * orientations. |
|
332 */ |
|
333 public static int screenOrientationToAndroidOrientation(ScreenOrientation aScreenOrientation) { |
|
334 switch (aScreenOrientation) { |
|
335 case PORTRAIT_PRIMARY: |
|
336 case PORTRAIT_SECONDARY: |
|
337 return Configuration.ORIENTATION_PORTRAIT; |
|
338 case LANDSCAPE_PRIMARY: |
|
339 case LANDSCAPE_SECONDARY: |
|
340 return Configuration.ORIENTATION_LANDSCAPE; |
|
341 case NONE: |
|
342 case DEFAULT: |
|
343 default: |
|
344 return Configuration.ORIENTATION_UNDEFINED; |
|
345 } |
|
346 } |
|
347 |
|
348 |
|
349 /* |
|
350 * Convert Gecko screen orientation to Android ActivityInfo orientation. |
|
351 * This is yet another orientation used by Android, but it's more detailed |
|
352 * than the Android orientation. |
|
353 * It is required for screen orientation locking and unlocking. |
|
354 * |
|
355 * @param aScreenOrientation |
|
356 * Gecko screen orientation. |
|
357 * @return Android ActivityInfo orientation. |
|
358 */ |
|
359 public static int screenOrientationToActivityInfoOrientation(ScreenOrientation aScreenOrientation) { |
|
360 switch (aScreenOrientation) { |
|
361 case PORTRAIT_PRIMARY: |
|
362 return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; |
|
363 case PORTRAIT_SECONDARY: |
|
364 return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; |
|
365 case LANDSCAPE_PRIMARY: |
|
366 return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; |
|
367 case LANDSCAPE_SECONDARY: |
|
368 return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; |
|
369 case DEFAULT: |
|
370 case NONE: |
|
371 return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; |
|
372 default: |
|
373 return ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; |
|
374 } |
|
375 } |
|
376 } |