GeckoScreenOrientation.java (8067B)
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 static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 9 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 10 11 import android.content.Context; 12 import android.graphics.Rect; 13 import android.util.Log; 14 import android.view.Surface; 15 import java.util.ArrayList; 16 import java.util.List; 17 import org.mozilla.gecko.util.ThreadUtils; 18 19 /* 20 * Updates, locks and unlocks the screen orientation. 21 * 22 * Note: Replaces the OnOrientationChangeListener to avoid redundant rotation 23 * event handling. 24 */ 25 public class GeckoScreenOrientation { 26 private static final String LOGTAG = "GeckoScreenOrientation"; 27 28 // Make sure that any change in hal/HalScreenConfiguration.h happens here too. 29 public enum ScreenOrientation { 30 NONE(0), 31 PORTRAIT_PRIMARY(1 << 0), 32 PORTRAIT_SECONDARY(1 << 1), 33 PORTRAIT(PORTRAIT_PRIMARY.value | PORTRAIT_SECONDARY.value), 34 LANDSCAPE_PRIMARY(1 << 2), 35 LANDSCAPE_SECONDARY(1 << 3), 36 LANDSCAPE(LANDSCAPE_PRIMARY.value | LANDSCAPE_SECONDARY.value), 37 ANY( 38 PORTRAIT_PRIMARY.value 39 | PORTRAIT_SECONDARY.value 40 | LANDSCAPE_PRIMARY.value 41 | LANDSCAPE_SECONDARY.value), 42 DEFAULT(1 << 4); 43 44 public final short value; 45 46 ScreenOrientation(final int value) { 47 this.value = (short) value; 48 } 49 50 private static final ScreenOrientation[] sValues = ScreenOrientation.values(); 51 52 public static ScreenOrientation get(final int value) { 53 for (final ScreenOrientation orient : sValues) { 54 if (orient.value == value) { 55 return orient; 56 } 57 } 58 return NONE; 59 } 60 } 61 62 // Singleton instance. 63 private static GeckoScreenOrientation sInstance; 64 // Default rotation, used when device rotation is unknown. 65 private static final int DEFAULT_ROTATION = Surface.ROTATION_0; 66 // Last updated screen orientation with Gecko value space. 67 private ScreenOrientation mScreenOrientation = ScreenOrientation.PORTRAIT_PRIMARY; 68 69 public interface OrientationChangeListener { 70 void onScreenOrientationChanged(ScreenOrientation newOrientation); 71 } 72 73 private final List<OrientationChangeListener> mListeners; 74 75 public static GeckoScreenOrientation getInstance() { 76 if (sInstance == null) { 77 sInstance = new GeckoScreenOrientation(); 78 } 79 return sInstance; 80 } 81 82 private GeckoScreenOrientation() { 83 mListeners = new ArrayList<>(); 84 update(); 85 } 86 87 /** Add a listener that will be notified when the screen orientation has changed. */ 88 public void addListener(final OrientationChangeListener aListener) { 89 ThreadUtils.assertOnUiThread(); 90 mListeners.add(aListener); 91 } 92 93 /** Remove a OrientationChangeListener again. */ 94 public void removeListener(final OrientationChangeListener aListener) { 95 ThreadUtils.assertOnUiThread(); 96 mListeners.remove(aListener); 97 } 98 99 /** 100 * Update screen orientation by retrieving orientation and rotation via GeckoAppShell. 101 * 102 * @return Whether the screen orientation has changed. 103 */ 104 public boolean update() { 105 // Check whether we have the application context for fenix/a-c unit test. 106 final Context appContext = GeckoAppShell.getApplicationContext(); 107 if (appContext == null) { 108 return false; 109 } 110 final Rect rect = GeckoAppShell.getScreenSizeIgnoreOverride(); 111 final int orientation = 112 rect.width() >= rect.height() ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; 113 return update(getScreenOrientation(orientation, getRotation())); 114 } 115 116 /** 117 * Update screen orientation given the Android orientation by retrieving rotation via 118 * GeckoAppShell. 119 * 120 * @param aAndroidOrientation Android screen orientation from Configuration.orientation. 121 * @return Whether the screen orientation has changed. 122 */ 123 public boolean update(final int aAndroidOrientation) { 124 return update(getScreenOrientation(aAndroidOrientation, getRotation())); 125 } 126 127 /** 128 * Update screen orientation given the screen orientation. 129 * 130 * @param aScreenOrientation Gecko screen orientation based on Android orientation and rotation. 131 * @return Whether the screen orientation has changed. 132 */ 133 public synchronized boolean update(final ScreenOrientation aScreenOrientation) { 134 // Gecko expects a definite screen orientation, so we default to the 135 // primary orientations. 136 final ScreenOrientation screenOrientation; 137 if ((aScreenOrientation.value & ScreenOrientation.PORTRAIT_PRIMARY.value) != 0) { 138 screenOrientation = ScreenOrientation.PORTRAIT_PRIMARY; 139 } else if ((aScreenOrientation.value & ScreenOrientation.PORTRAIT_SECONDARY.value) != 0) { 140 screenOrientation = ScreenOrientation.PORTRAIT_SECONDARY; 141 } else if ((aScreenOrientation.value & ScreenOrientation.LANDSCAPE_PRIMARY.value) != 0) { 142 screenOrientation = ScreenOrientation.LANDSCAPE_PRIMARY; 143 } else if ((aScreenOrientation.value & ScreenOrientation.LANDSCAPE_SECONDARY.value) != 0) { 144 screenOrientation = ScreenOrientation.LANDSCAPE_SECONDARY; 145 } else { 146 screenOrientation = ScreenOrientation.PORTRAIT_PRIMARY; 147 } 148 if (mScreenOrientation == screenOrientation) { 149 return false; 150 } 151 mScreenOrientation = screenOrientation; 152 Log.d(LOGTAG, "updating to new orientation " + mScreenOrientation); 153 notifyListeners(mScreenOrientation); 154 ScreenManagerHelper.refreshScreenInfo(); 155 return true; 156 } 157 158 private void notifyListeners(final ScreenOrientation newOrientation) { 159 final Runnable notifier = 160 new Runnable() { 161 @Override 162 public void run() { 163 for (final OrientationChangeListener listener : mListeners) { 164 listener.onScreenOrientationChanged(newOrientation); 165 } 166 } 167 }; 168 169 if (ThreadUtils.isOnUiThread()) { 170 notifier.run(); 171 } else { 172 ThreadUtils.runOnUiThread(notifier); 173 } 174 } 175 176 /** 177 * @return The Gecko screen orientation derived from Android orientation and rotation. 178 */ 179 public ScreenOrientation getScreenOrientation() { 180 return mScreenOrientation; 181 } 182 183 /** 184 * Combine the Android orientation and rotation to the Gecko orientation. 185 * 186 * @param aAndroidOrientation Android orientation from Configuration.orientation. 187 * @param aRotation Device rotation from Display.getRotation(). 188 * @return Gecko screen orientation. 189 */ 190 private ScreenOrientation getScreenOrientation( 191 final int aAndroidOrientation, final int aRotation) { 192 final boolean isPrimary = aRotation == Surface.ROTATION_0 || aRotation == Surface.ROTATION_90; 193 if (aAndroidOrientation == ORIENTATION_PORTRAIT) { 194 if (isPrimary) { 195 // Non-rotated portrait device or landscape device rotated 196 // to primary portrait mode counter-clockwise. 197 return ScreenOrientation.PORTRAIT_PRIMARY; 198 } 199 return ScreenOrientation.PORTRAIT_SECONDARY; 200 } 201 if (aAndroidOrientation == ORIENTATION_LANDSCAPE) { 202 if (isPrimary) { 203 // Non-rotated landscape device or portrait device rotated 204 // to primary landscape mode counter-clockwise. 205 return ScreenOrientation.LANDSCAPE_PRIMARY; 206 } 207 return ScreenOrientation.LANDSCAPE_SECONDARY; 208 } 209 return ScreenOrientation.NONE; 210 } 211 212 /** 213 * @return Device rotation converted to an angle. 214 */ 215 public short getAngle() { 216 switch (getRotation()) { 217 case Surface.ROTATION_0: 218 return 0; 219 case Surface.ROTATION_90: 220 return 90; 221 case Surface.ROTATION_180: 222 return 180; 223 case Surface.ROTATION_270: 224 return 270; 225 default: 226 Log.w(LOGTAG, "getAngle: unexpected rotation value"); 227 return 0; 228 } 229 } 230 231 /** 232 * @return Device rotation. 233 */ 234 private int getRotation() { 235 return GeckoAppShell.getRotation(); 236 } 237 }