1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view; 18 19 import static android.app.WindowConfiguration.ROTATION_UNDEFINED; 20 21 import android.annotation.NonNull; 22 import android.content.Context; 23 import android.content.res.CompatibilityInfo; 24 import android.hardware.Sensor; 25 import android.hardware.SensorEvent; 26 import android.hardware.SensorEventListener; 27 import android.hardware.SensorManager; 28 import android.util.Log; 29 import android.window.DesktopModeFlags; 30 31 /** 32 * Helper class for receiving notifications from the SensorManager when 33 * the orientation of the device has changed. 34 */ 35 public abstract class OrientationEventListener { 36 private static final String TAG = "OrientationEventListener"; 37 private static final boolean DEBUG = false; 38 private static final boolean localLOGV = false; 39 private int mOrientation = ORIENTATION_UNKNOWN; 40 private SensorManager mSensorManager; 41 private boolean mEnabled = false; 42 private int mRate; 43 private Sensor mSensor; 44 private SensorEventListener mSensorEventListener; 45 private OrientationListener mOldListener; 46 47 /** 48 * Returned from onOrientationChanged when the device orientation cannot be determined 49 * (typically when the device is in a close to flat position). 50 * 51 * @see #onOrientationChanged 52 */ 53 public static final int ORIENTATION_UNKNOWN = -1; 54 55 /** 56 * Creates a new OrientationEventListener. 57 * 58 * @param context for the OrientationEventListener. 59 */ OrientationEventListener(Context context)60 public OrientationEventListener(Context context) { 61 this(context, SensorManager.SENSOR_DELAY_NORMAL); 62 } 63 64 /** 65 * Creates a new OrientationEventListener. 66 * 67 * @param context for the OrientationEventListener. 68 * @param rate at which sensor events are processed (see also 69 * {@link android.hardware.SensorManager SensorManager}). Use the default 70 * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL 71 * SENSOR_DELAY_NORMAL} for simple screen orientation change detection. 72 */ OrientationEventListener(Context context, int rate)73 public OrientationEventListener(Context context, int rate) { 74 mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); 75 mRate = rate; 76 mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 77 if (mSensor != null) { 78 // Create listener only if sensors do exist. 79 mSensorEventListener = 80 DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue() 81 ? new CompatSensorEventListenerImpl(new SensorEventListenerImpl()) 82 : new SensorEventListenerImpl(); 83 } 84 } 85 registerListener(OrientationListener lis)86 void registerListener(OrientationListener lis) { 87 mOldListener = lis; 88 } 89 90 /** 91 * Enables the OrientationEventListener so it will monitor the sensor and call 92 * {@link #onOrientationChanged} when the device orientation changes. 93 */ enable()94 public void enable() { 95 if (mSensor == null) { 96 Log.w(TAG, "Cannot detect sensors. Not enabled"); 97 return; 98 } 99 if (mEnabled == false) { 100 if (localLOGV) Log.d(TAG, "OrientationEventListener enabled"); 101 mSensorManager.registerListener(mSensorEventListener, mSensor, mRate); 102 mEnabled = true; 103 } 104 } 105 106 /** 107 * Disables the OrientationEventListener. 108 */ disable()109 public void disable() { 110 if (mSensor == null) { 111 Log.w(TAG, "Cannot detect sensors. Invalid disable"); 112 return; 113 } 114 if (mEnabled == true) { 115 if (localLOGV) Log.d(TAG, "OrientationEventListener disabled"); 116 mSensorManager.unregisterListener(mSensorEventListener); 117 mEnabled = false; 118 } 119 } 120 121 class SensorEventListenerImpl implements SensorEventListener { 122 private static final int _DATA_X = 0; 123 private static final int _DATA_Y = 1; 124 private static final int _DATA_Z = 2; 125 onSensorChanged(SensorEvent event)126 public void onSensorChanged(SensorEvent event) { 127 float[] values = event.values; 128 int orientation = ORIENTATION_UNKNOWN; 129 float X = -values[_DATA_X]; 130 float Y = -values[_DATA_Y]; 131 float Z = -values[_DATA_Z]; 132 float magnitude = X*X + Y*Y; 133 // Don't trust the angle if the magnitude is small compared to the y value 134 if (magnitude * 4 >= Z*Z) { 135 float OneEightyOverPi = 57.29577957855f; 136 float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi; 137 orientation = 90 - (int)Math.round(angle); 138 // normalize to 0 - 359 range 139 while (orientation >= 360) { 140 orientation -= 360; 141 } 142 while (orientation < 0) { 143 orientation += 360; 144 } 145 } 146 if (mOldListener != null) { 147 mOldListener.onSensorChanged(Sensor.TYPE_ACCELEROMETER, event.values); 148 } 149 if (orientation != mOrientation) { 150 mOrientation = orientation; 151 onOrientationChanged(orientation); 152 } 153 } 154 onAccuracyChanged(Sensor sensor, int accuracy)155 public void onAccuracyChanged(Sensor sensor, int accuracy) { 156 157 } 158 } 159 160 /** Decorator to the {@link SensorEventListenerImpl}, which provides compat values if needed. */ 161 class CompatSensorEventListenerImpl implements SensorEventListener { 162 // SensorEventListener without compatibility capabilities. 163 final SensorEventListenerImpl mSensorEventListener; 164 CompatSensorEventListenerImpl(@onNull SensorEventListenerImpl sensorEventListener)165 CompatSensorEventListenerImpl(@NonNull SensorEventListenerImpl sensorEventListener) { 166 mSensorEventListener = sensorEventListener; 167 } 168 onSensorChanged(SensorEvent event)169 public void onSensorChanged(SensorEvent event) { 170 // If the display rotation override is set, the same override should be applied to 171 // this orientation too. This rotation override will only be set when an app has a 172 // camera open and it is in camera compat mode for desktop windowing (freeform mode). 173 // Values of this override is Surface.ROTATION_0/90/180/270, or 174 // WindowConfiguration.ROTATION_UNDEFINED when not set. 175 if (CompatibilityInfo.getOverrideDisplayRotation() != ROTATION_UNDEFINED) { 176 // SensorEventListener reports the rotation in the opposite direction from the 177 // display rotation. 178 int orientation = (360 - CompatibilityInfo.getOverrideDisplayRotation() * 90) % 360; 179 if (orientation != mOrientation) { 180 mOrientation = orientation; 181 onOrientationChanged(orientation); 182 } 183 // `mOldListener` is deprecated and returns 3D values, which are highly unlikely to 184 // be used for orienting camera image. Thus this listener is not called here, as 185 // opposed to extrapolating values from display rotation, from 1D->3D. 186 } else { 187 // Use the default implementation: calculate the orientation from event coordinates. 188 // This method will call OrientationEventListener.onOrientationChanged(orientation) 189 // if the orientation has changed. 190 mSensorEventListener.onSensorChanged(event); 191 } 192 } 193 onAccuracyChanged(Sensor sensor, int accuracy)194 public void onAccuracyChanged(Sensor sensor, int accuracy) { 195 196 } 197 } 198 199 /* 200 * Returns true if sensor is enabled and false otherwise 201 */ canDetectOrientation()202 public boolean canDetectOrientation() { 203 return mSensor != null; 204 } 205 206 /** 207 * Called when the orientation of the device has changed. 208 * orientation parameter is in degrees, ranging from 0 to 359. 209 * orientation is 0 degrees when the device is oriented in its natural position, 210 * 90 degrees when its left side is at the top, 180 degrees when it is upside down, 211 * and 270 degrees when its right side is to the top. 212 * {@link #ORIENTATION_UNKNOWN} is returned when the device is close to flat 213 * and the orientation cannot be determined. 214 * 215 * @param orientation The new orientation of the device. 216 * 217 * @see #ORIENTATION_UNKNOWN 218 */ onOrientationChanged(int orientation)219 abstract public void onOrientationChanged(int orientation); 220 } 221