19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.view; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.hardware.Sensor; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.hardware.SensorEvent; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.hardware.SensorEventListener; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.hardware.SensorManager; 24daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brownimport android.os.SystemProperties; 255aa73ae58f049379a97bc86add541f27170c02a4Jeff Brownimport android.util.FloatMath; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 274519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brownimport android.util.Slog; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * A special helper class used by the WindowManager 31c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * for receiving notifications from the SensorManager when 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the orientation of the device has changed. 33e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * 34e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * NOTE: If changing anything here, please run the API demo 35e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * "App/Activity/Screen Orientation" to ensure that all orientation 36e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * modes still work correctly. 37e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * 38daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown * You can also visualize the behavior of the WindowOrientationListener. 39daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown * Refer to frameworks/base/tools/orientationplot/README.txt for details. 404519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class WindowOrientationListener { 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TAG = "WindowOrientationListener"; 45daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private static final boolean LOG = SystemProperties.getBoolean( 46daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown "debug.orientation.log", false); 474519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 485aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final boolean USE_GRAVITY_SENSOR = false; 495aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private SensorManager mSensorManager; 514519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private boolean mEnabled; 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mRate; 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Sensor mSensor; 5463104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu private SensorEventListenerImpl mSensorEventListener; 55c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown int mCurrentRotation = -1; 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Creates a new WindowOrientationListener. 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param context for the WindowOrientationListener. 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public WindowOrientationListener(Context context) { 634519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown this(context, SensorManager.SENSOR_DELAY_UI); 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Creates a new WindowOrientationListener. 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param context for the WindowOrientationListener. 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param rate at which sensor events are processed (see also 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link android.hardware.SensorManager SensorManager}). Use the default 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * SENSOR_DELAY_NORMAL} for simple screen orientation change detection. 741ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard * 754519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * This constructor is private since no one uses it. 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 771ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard private WindowOrientationListener(Context context, int rate) { 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mRate = rate; 805aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR 815aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER); 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSensor != null) { 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Create listener only if sensors do exist 845f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard mSensorEventListener = new SensorEventListenerImpl(this); 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Enables the WindowOrientationListener so it will monitor the sensor and call 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link #onOrientationChanged} when the device orientation changes. 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void enable() { 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSensor == null) { 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(TAG, "Cannot detect sensors. Not enabled"); 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mEnabled == false) { 98daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 99daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown Log.d(TAG, "WindowOrientationListener enabled"); 100daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown } 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSensorManager.registerListener(mSensorEventListener, mSensor, mRate); 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mEnabled = true; 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Disables the WindowOrientationListener. 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void disable() { 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSensor == null) { 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(TAG, "Cannot detect sensors. Invalid disable"); 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mEnabled == true) { 115daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 116daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown Log.d(TAG, "WindowOrientationListener disabled"); 117daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown } 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSensorManager.unregisterListener(mSensorEventListener); 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mEnabled = false; 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1234519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown /** 124c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * Sets the current rotation. 125c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 126c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * @param rotation The current rotation. 127c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown */ 128c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown public void setCurrentRotation(int rotation) { 129c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown mCurrentRotation = rotation; 130c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 131c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 132c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown /** 133c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * Gets the proposed rotation. 134c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 135c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * This method only returns a rotation if the orientation listener is certain 136c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * of its proposal. If the rotation is indeterminate, returns -1. 137c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 138c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * @return The proposed rotation, or -1 if unknown. 1394519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown */ 140c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown public int getProposedRotation() { 14163104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu if (mEnabled) { 142c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown return mSensorEventListener.getProposedRotation(); 14363104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu } 14401a98ddbdfbaf1f0d2bc602537e6e314364902a3Jeff Brown return -1; 145e4fbd6235c8d1c5b0ed4883ec275dd3fc9c919fbDianne Hackborn } 1465f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 1475f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard /** 1484519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * Returns true if sensor is enabled and false otherwise 1494519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown */ 1504519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown public boolean canDetectOrientation() { 1514519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return mSensor != null; 1524519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 1534519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 1544519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown /** 1554519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * Called when the rotation view of the device has changed. 1564519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 157c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * This method is called whenever the orientation becomes certain of an orientation. 158c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * It is called each time the orientation determination transitions from being 159c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * uncertain to being certain again, even if it is the same orientation as before. 160c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 1614519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants. 1624519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * @see Surface 1634519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown */ 164c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown public abstract void onProposedRotationChanged(int rotation); 1654519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 1664519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown /** 1675f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * This class filters the raw accelerometer data and tries to detect actual changes in 1685f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters, 1695f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * but here's the outline: 1705f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * 1714519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * - Low-pass filter the accelerometer vector in cartesian coordinates. We do it in 1724519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * cartesian space because the orientation calculations are sensitive to the 1734519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * absolute magnitude of the acceleration. In particular, there are singularities 1744519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * in the calculation as the magnitude approaches 0. By performing the low-pass 1755aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * filtering early, we can eliminate most spurious high-frequency impulses due to noise. 1764519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 1774519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * - Convert the acceleromter vector from cartesian to spherical coordinates. 1784519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * Since we're dealing with rotation of the device, this is the sensible coordinate 1794519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * system to work in. The zenith direction is the Z-axis, the direction the screen 1804519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * is facing. The radial distance is referred to as the magnitude below. 1814519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * The elevation angle is referred to as the "tilt" below. 1824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * The azimuth angle is referred to as the "orientation" below (and the azimuth axis is 1834519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * the Y-axis). 1844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference. 1854519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 1864519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing. 1874519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * The orientation angle is not meaningful when the device is nearly horizontal. 1884519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * The tilt angle thresholds are set differently for each orientation and different 1894519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * limits are applied when the device is facing down as opposed to when it is facing 1904519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * forward or facing up. 1915f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * 1924519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * - When the orientation angle reaches a certain threshold, consider transitioning 1934519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * to the corresponding orientation. These thresholds have some hysteresis built-in 1944519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * to avoid oscillations between adjacent orientations. 1955f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * 196c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * - Wait for the device to settle for a little bit. Once that happens, issue the 197c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * new orientation proposal. 1984519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 1994519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * Details are explained inline. 2005aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * 2015aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for 2025aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * signal processing background. 2035f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard */ 2044519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown static final class SensorEventListenerImpl implements SensorEventListener { 2051ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard // We work with all angles in degrees in this class. 2061ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI); 2071ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 2085aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Number of nanoseconds per millisecond. 2095aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long NANOS_PER_MS = 1000000; 2105aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 2114519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Indices into SensorEvent.values for the accelerometer sensor. 2124519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final int ACCELEROMETER_DATA_X = 0; 2134519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final int ACCELEROMETER_DATA_Y = 1; 2144519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final int ACCELEROMETER_DATA_Z = 2; 2154519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 2164519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private final WindowOrientationListener mOrientationListener; 2174519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 2185aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The minimum amount of time that a predicted rotation must be stable before it 2195aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // is accepted as a valid rotation proposal. This value can be quite small because 2205aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // the low-pass filter already suppresses most of the noise so we're really just 2215aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // looking for quick confirmation that the last few samples are in agreement as to 2225aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // the desired orientation. 2235aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS; 2245aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 2255aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The minimum amount of time that must have elapsed since the device last exited 2265aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // the flat state (time since it was picked up) before the proposed rotation 2275aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // can change. 2285aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS; 2295aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 230daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // The minimum amount of time that must have elapsed since the device stopped 2315aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // swinging (time since device appeared to be in the process of being put down 2325aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // or put away into a pocket) before the proposed rotation can change. 2335aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS; 2345aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 235daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // The minimum amount of time that must have elapsed since the device stopped 236daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // undergoing external acceleration before the proposed rotation can change. 237daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS = 238daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 500 * NANOS_PER_MS; 239daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 2405aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // If the tilt angle remains greater than the specified angle for a minimum of 2415aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // the specified time, then the device is deemed to be lying flat 2425aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // (just chillin' on a table). 2435aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final float FLAT_ANGLE = 75; 2445aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS; 2455aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 2465aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // If the tilt angle has increased by at least delta degrees within the specified amount 2475aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // of time, then the device is deemed to be swinging away from the user 2485aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // down towards flat (tilt = 90). 2495aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final float SWING_AWAY_ANGLE_DELTA = 20; 2505aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS; 251c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 2524519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The maximum sample inter-arrival time in milliseconds. 2534519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // If the acceleration samples are further apart than this amount in time, we reset the 2544519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // state of the low-pass filter and orientation properties. This helps to handle 2554519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // boundary conditions when the device is turned on, wakes from suspend or there is 2564519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // a significant gap in samples. 2575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS; 2584519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 259c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // The acceleration filter time constant. 260c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // 261c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // This time constant is used to tune the acceleration filter such that 262c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // impulses and vibrational noise (think car dock) is suppressed before we 263c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // try to calculate the tilt and orientation angles. 264c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // 265c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // The filter time constant is related to the filter cutoff frequency, which is the 266c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // frequency at which signals are attenuated by 3dB (half the passband power). 2674519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Each successive octave beyond this frequency is attenuated by an additional 6dB. 2684519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 269c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz 270c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // is given by Fc = 1 / (2pi * t). 271c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // 272c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // The higher the time constant, the lower the cutoff frequency, so more noise 273c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // will be suppressed. 274c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // 275c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // Filtering adds latency proportional the time constant (inversely proportional 276c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // to the cutoff frequency) so we don't want to make the time constant too 2775aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // large or we can lose responsiveness. Likewise we don't want to make it too 2785aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // small or we do a poor job suppressing acceleration spikes. 2795aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Empirically, 100ms seems to be too small and 500ms is too large. 2805aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final float FILTER_TIME_CONSTANT_MS = 200.0f; 2814519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 2824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown /* State for orientation detection. */ 2834519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 2844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Thresholds for minimum and maximum allowable deviation from gravity. 2854519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 2864519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // If the device is undergoing external acceleration (being bumped, in a car 2874519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // that is turning around a corner or a plane taking off) then the magnitude 2884519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // may be substantially more or less than gravity. This can skew our orientation 2894519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // detection by making us think that up is pointed in a different direction. 2904519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 2914519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Conversely, if the device is in freefall, then there will be no gravity to 2924519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // measure at all. This is problematic because we cannot detect the orientation 2934519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // without gravity to tell us which way is up. A magnitude near 0 produces 2944519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // singularities in the tilt and orientation calculations. 2954519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 2964519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // In both cases, we postpone choosing an orientation. 297daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // 298daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // However, we need to tolerate some acceleration because the angular momentum 299daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // of turning the device can skew the observed acceleration for a short period of time. 300daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2 301daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private static final float ACCELERATION_TOLERANCE = 4; // m/s^2 3024519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final float MIN_ACCELERATION_MAGNITUDE = 303daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE; 3044519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final float MAX_ACCELERATION_MAGNITUDE = 305daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE; 30663104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu 3075f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e. 3085f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard // when screen is facing the sky or ground), we completely ignore orientation data. 3095f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard private static final int MAX_TILT = 75; 3101ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 3114519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The tilt angle range in degrees for each orientation. 3124519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Beyond these tilt angles, we don't even consider transitioning into the 3134519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // specified orientation. We place more stringent requirements on unnatural 3144519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // orientations than natural ones to make it less likely to accidentally transition 3154519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // into those states. 3164519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The first value of each pair is negative so it applies a limit when the device is 3174519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // facing down (overhead reading in bed). 3184519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The second value of each pair is positive so it applies a limit when the device is 3194519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // facing up (resting on a table). 3204519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The ideal tilt angle is 0 (when the device is vertical) so the limits establish 3214519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // how close to vertical the device must be in order to change orientation. 3224519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final int[][] TILT_TOLERANCE = new int[][] { 3235aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /* ROTATION_0 */ { -25, 70 }, 3245aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /* ROTATION_90 */ { -25, 65 }, 3255aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /* ROTATION_180 */ { -25, 60 }, 3265aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /* ROTATION_270 */ { -25, 65 } 3274519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown }; 3285f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 3294519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The gap angle in degrees between adjacent orientation angles for hysteresis. 3304519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // This creates a "dead zone" between the current orientation and a proposed 3314519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // adjacent orientation. No orientation proposal is made when the orientation 3324519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // angle is within the gap between the current orientation and the adjacent 3334519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // orientation. 334c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45; 33563104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu 3365aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Timestamp and value of the last accelerometer sample. 3375aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long mLastFilteredTimestampNanos; 3385aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private float mLastFilteredX, mLastFilteredY, mLastFilteredZ; 3395aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 3405aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The last proposed rotation, -1 if unknown. 3415aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private int mProposedRotation; 3425aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 3435aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Value of the current predicted rotation, -1 if unknown. 3445aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private int mPredictedRotation; 3455aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 3465aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Timestamp of when the predicted rotation most recently changed. 3475aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long mPredictedRotationTimestampNanos; 348e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn 3495aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed). 3505aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long mFlatTimestampNanos; 3511ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 3525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Timestamp when the device last appeared to be swinging. 3535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long mSwingTimestampNanos; 3541ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 355daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // Timestamp when the device last appeared to be undergoing external acceleration. 356daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private long mAccelerationTimestampNanos; 357daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 3585aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // History of observed tilt angles. 3595aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final int TILT_HISTORY_SIZE = 40; 3605aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private float[] mTiltHistory = new float[TILT_HISTORY_SIZE]; 3615aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE]; 3625aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private int mTiltHistoryIndex; 3631ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 3644519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown public SensorEventListenerImpl(WindowOrientationListener orientationListener) { 3654519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown mOrientationListener = orientationListener; 3665aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown reset(); 3671ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard } 3681ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 369c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown public int getProposedRotation() { 3705aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return mProposedRotation; 3711ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard } 3721ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 3734519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown @Override 3744519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown public void onAccuracyChanged(Sensor sensor, int accuracy) { 37563104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu } 37663104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu 3774519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown @Override 3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onSensorChanged(SensorEvent event) { 3794519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The vector given in the SensorEvent points straight up (towards the sky) under ideal 3804519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // conditions (the phone is not accelerating). I'll call this up vector elsewhere. 3814519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown float x = event.values[ACCELEROMETER_DATA_X]; 3824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown float y = event.values[ACCELEROMETER_DATA_Y]; 3834519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown float z = event.values[ACCELEROMETER_DATA_Z]; 3844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 385daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 3865aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown Slog.v(TAG, "Raw acceleration vector: " 3875aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + "x=" + x + ", y=" + y + ", z=" + z 3885aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z)); 389fcbbb31f27f00efe7314031bac23e1e011954ad6Niclas Kellgren } 3905f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 3914519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Apply a low-pass filter to the acceleration up vector in cartesian space. 3924519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Reset the orientation listener state if the samples are too far apart in time 3934519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // or when we see values of (0, 0, 0) which indicates that we polled the 3944519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // accelerometer too soon after turning it on and we don't have any data yet. 395c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown final long now = event.timestamp; 3965aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown final long then = mLastFilteredTimestampNanos; 3975aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown final float timeDeltaMS = (now - then) * 0.000001f; 3985aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown final boolean skipSample; 3995aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (now < then 4005aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown || now > then + MAX_FILTER_DELTA_TIME_NANOS 4014519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown || (x == 0 && y == 0 && z == 0)) { 402daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 4034519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown Slog.v(TAG, "Resetting orientation listener."); 4044519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 4055aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown reset(); 4064519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown skipSample = true; 4074519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } else { 408c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS); 4094519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown x = alpha * (x - mLastFilteredX) + mLastFilteredX; 4104519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown y = alpha * (y - mLastFilteredY) + mLastFilteredY; 4114519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown z = alpha * (z - mLastFilteredZ) + mLastFilteredZ; 412daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 4135aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown Slog.v(TAG, "Filtered acceleration vector: " 4145aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + "x=" + x + ", y=" + y + ", z=" + z 4155aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z)); 4164519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 4174519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown skipSample = false; 4185f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 4195aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mLastFilteredTimestampNanos = now; 4204519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown mLastFilteredX = x; 4214519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown mLastFilteredY = y; 4224519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown mLastFilteredZ = z; 4234519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 424daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown boolean isAccelerating = false; 4255aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown boolean isFlat = false; 4265aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown boolean isSwinging = false; 4274519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown if (!skipSample) { 4284519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Calculate the magnitude of the acceleration vector. 4295aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown final float magnitude = FloatMath.sqrt(x * x + y * y + z * z); 430daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (magnitude < NEAR_ZERO_MAGNITUDE) { 431daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 432daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero."); 4334519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 4345aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown clearPredictedRotation(); 4354519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } else { 436daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // Determine whether the device appears to be undergoing external acceleration. 437daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (isAccelerating(magnitude)) { 438daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown isAccelerating = true; 439daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown mAccelerationTimestampNanos = now; 440daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown } 441daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 4424519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Calculate the tilt angle. 4434519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // This is the angle between the up vector and the x-y plane (the plane of 4444519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // the screen) in a range of [-90, 90] degrees. 4454519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // -90 degrees: screen horizontal and facing the ground (overhead) 4464519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 0 degrees: screen vertical 4474519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 90 degrees: screen horizontal and facing the sky (on table) 448c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown final int tiltAngle = (int) Math.round( 449c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown Math.asin(z / magnitude) * RADIANS_TO_DEGREES); 450daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown addTiltHistoryEntry(now, tiltAngle); 451c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 4525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Determine whether the device appears to be flat or swinging. 4535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (isFlat(now)) { 4545aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown isFlat = true; 4555aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mFlatTimestampNanos = now; 4565aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 4575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (isSwinging(now, tiltAngle)) { 4585aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown isSwinging = true; 4595aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mSwingTimestampNanos = now; 4605aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 4615aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 462c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // If the tilt angle is too close to horizontal then we cannot determine 463c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // the orientation angle of the screen. 464c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown if (Math.abs(tiltAngle) > MAX_TILT) { 465daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 466c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " 4675aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + "tiltAngle=" + tiltAngle); 4684519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 4695aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown clearPredictedRotation(); 4704519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } else { 471c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // Calculate the orientation angle. 472c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // This is the angle between the x-y projection of the up vector onto 473c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // the +y-axis, increasing clockwise in a range of [0, 360] degrees. 474c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown int orientationAngle = (int) Math.round( 475c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown -Math.atan2(-x, y) * RADIANS_TO_DEGREES); 476c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown if (orientationAngle < 0) { 477c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // atan2 returns [-180, 180]; normalize to [0, 360] 478c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown orientationAngle += 360; 479c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 480c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 481c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // Find the nearest rotation. 482c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown int nearestRotation = (orientationAngle + 45) / 90; 483c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown if (nearestRotation == 4) { 484c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown nearestRotation = 0; 485c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 486c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 4875aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Determine the predicted orientation. 4885aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (isTiltAngleAcceptable(nearestRotation, tiltAngle) 4895aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown && isOrientationAngleAcceptable(nearestRotation, 490c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown orientationAngle)) { 4915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown updatePredictedRotation(now, nearestRotation); 492daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 4935aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown Slog.v(TAG, "Predicted: " 4945aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + "tiltAngle=" + tiltAngle 4955aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", orientationAngle=" + orientationAngle 4965aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", predictedRotation=" + mPredictedRotation 4975aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", predictedRotationAgeMS=" 4985aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ((now - mPredictedRotationTimestampNanos) 4995aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * 0.000001f)); 500c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 501c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } else { 502daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 5035aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown Slog.v(TAG, "Ignoring sensor data, no predicted rotation: " 5045aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + "tiltAngle=" + tiltAngle 5055aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", orientationAngle=" + orientationAngle); 506c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 5075aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown clearPredictedRotation(); 5084519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5094519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5104519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5115f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 5125f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 5135aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Determine new proposed rotation. 5145aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown final int oldProposedRotation = mProposedRotation; 5155aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mPredictedRotation < 0 || isPredictedRotationAcceptable(now)) { 5165aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mProposedRotation = mPredictedRotation; 5175aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 5185aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 5194519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Write final statistics about where we are in the orientation detection process. 520daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 521c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown Slog.v(TAG, "Result: currentRotation=" + mOrientationListener.mCurrentRotation 5225aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", proposedRotation=" + mProposedRotation 5235aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", predictedRotation=" + mPredictedRotation 524c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown + ", timeDeltaMS=" + timeDeltaMS 525daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown + ", isAccelerating=" + isAccelerating 5265aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", isFlat=" + isFlat 5275aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", isSwinging=" + isSwinging 5285aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", timeUntilSettledMS=" + remainingMS(now, 5295aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) 530daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now, 531daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) 5325aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now, 5335aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) 5345aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now, 5355aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)); 5364519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5374519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 5384519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Tell the listener. 5395aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mProposedRotation != oldProposedRotation && mProposedRotation >= 0) { 540daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 5415aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + mProposedRotation 542c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown + ", oldProposedRotation=" + oldProposedRotation); 543c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 5445aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mOrientationListener.onProposedRotationChanged(mProposedRotation); 5454519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5465f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 5475f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 5485f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard /** 5495aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * Returns true if the tilt angle is acceptable for a given predicted rotation. 5505f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard */ 5515aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private boolean isTiltAngleAcceptable(int rotation, int tiltAngle) { 5525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return tiltAngle >= TILT_TOLERANCE[rotation][0] 5535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown && tiltAngle <= TILT_TOLERANCE[rotation][1]; 5545f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 5555f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 5565f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard /** 5575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * Returns true if the orientation angle is acceptable for a given predicted rotation. 558c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 5594519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * This function takes into account the gap between adjacent orientations 5604519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * for hysteresis. 5615f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard */ 5625aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private boolean isOrientationAngleAcceptable(int rotation, int orientationAngle) { 5634519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // If there is no current rotation, then there is no gap. 564c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // The gap is used only to introduce hysteresis among advertised orientation 565c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // changes to avoid flapping. 566c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown final int currentRotation = mOrientationListener.mCurrentRotation; 567c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown if (currentRotation >= 0) { 5685aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // If the specified rotation is the same or is counter-clockwise adjacent 5695aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // to the current rotation, then we set a lower bound on the orientation angle. 570c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90, 5714519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // then we want to check orientationAngle > 45 + GAP / 2. 5725aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (rotation == currentRotation 5735aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown || rotation == (currentRotation + 1) % 4) { 5745aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown int lowerBound = rotation * 90 - 45 5754519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown + ADJACENT_ORIENTATION_ANGLE_GAP / 2; 5765aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (rotation == 0) { 5774519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) { 5784519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return false; 5794519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5804519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } else { 5814519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown if (orientationAngle < lowerBound) { 5824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return false; 5834519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5855f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 5865f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 5875aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // If the specified rotation is the same or is clockwise adjacent, 5884519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // then we set an upper bound on the orientation angle. 5895aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270, 5904519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // then we want to check orientationAngle < 315 - GAP / 2. 5915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (rotation == currentRotation 5925aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown || rotation == (currentRotation + 3) % 4) { 5935aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown int upperBound = rotation * 90 + 45 5944519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown - ADJACENT_ORIENTATION_ANGLE_GAP / 2; 5955aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (rotation == 0) { 5964519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown if (orientationAngle <= 45 && orientationAngle > upperBound) { 5974519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return false; 5984519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5994519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } else { 6004519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown if (orientationAngle > upperBound) { 6014519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return false; 6024519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6034519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6045f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 6055f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 6064519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return true; 6075f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 6085f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 6095aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /** 6105aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * Returns true if the predicted rotation is ready to be advertised as a 6115aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * proposed rotation. 6125aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown */ 6135aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private boolean isPredictedRotationAcceptable(long now) { 6145aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The predicted rotation must have settled long enough. 6155aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) { 6165aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 6175aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6185aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 6195aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The last flat state (time since picked up) must have been sufficiently long ago. 6205aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) { 6215aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 6225aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6235f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 6245aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The last swing state (time since last movement to put down) must have been 6255aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // sufficiently long ago. 6265aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) { 6275aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 628c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 629c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 630daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // The last acceleration state must have been sufficiently long ago. 631daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (now < mAccelerationTimestampNanos 632daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) { 633daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown return false; 634daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown } 635daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 6365aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Looks good! 6375aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return true; 6385aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6395aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 6405aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private void reset() { 6415aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mLastFilteredTimestampNanos = Long.MIN_VALUE; 6425aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mProposedRotation = -1; 6435aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mFlatTimestampNanos = Long.MIN_VALUE; 6445aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mSwingTimestampNanos = Long.MIN_VALUE; 645daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown mAccelerationTimestampNanos = Long.MIN_VALUE; 6465aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown clearPredictedRotation(); 6475aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown clearTiltHistory(); 6485aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6495aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 6505aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private void clearPredictedRotation() { 6515aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mPredictedRotation = -1; 6525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mPredictedRotationTimestampNanos = Long.MIN_VALUE; 6535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6545aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 6555aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private void updatePredictedRotation(long now, int rotation) { 6565aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mPredictedRotation != rotation) { 6575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mPredictedRotation = rotation; 6585aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mPredictedRotationTimestampNanos = now; 659c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 6605aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 661c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 662daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private boolean isAccelerating(float magnitude) { 663daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown return magnitude < MIN_ACCELERATION_MAGNITUDE 664daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown || magnitude > MAX_ACCELERATION_MAGNITUDE; 665daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown } 666daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 6675aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private void clearTiltHistory() { 6685aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE; 6695aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryIndex = 1; 6705aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6715aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 6725aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private void addTiltHistoryEntry(long now, float tilt) { 6735aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistory[mTiltHistoryIndex] = tilt; 6745aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now; 6755aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE; 6765aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE; 6775aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6785aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 6795aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private boolean isFlat(long now) { 6805aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndex(i)) >= 0; ) { 6815aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mTiltHistory[i] < FLAT_ANGLE) { 682c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown break; 683c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 6845aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) { 6855aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS. 6865aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return true; 687c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 6885aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6895aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 6905aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 6925aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private boolean isSwinging(long now, float tilt) { 6935aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndex(i)) >= 0; ) { 6945aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) { 695c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown break; 696c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 6975aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) { 6985aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS. 6995aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return true; 700c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 701c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 7025aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 703c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 704c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 7055aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private int nextTiltHistoryIndex(int index) { 7065aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1; 7075aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1; 7085aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7095aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 7105aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static float remainingMS(long now, long until) { 7115aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return now >= until ? 0 : (until - now) * 0.000001f; 7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 715