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 17eee29c445c38217e83bf421faf0f4075322079a6Craig Mautnerpackage com.android.internal.policy.impl; 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; 24eee29c445c38217e83bf421faf0f4075322079a6Craig Mautnerimport android.os.Handler; 25daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brownimport android.os.SystemProperties; 265aa73ae58f049379a97bc86add541f27170c02a4Jeff Brownimport android.util.FloatMath; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 284519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brownimport android.util.Slog; 29600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brownimport android.util.TimeUtils; 30600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown 31600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brownimport java.io.PrintWriter; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * A special helper class used by the WindowManager 35c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * for receiving notifications from the SensorManager when 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the orientation of the device has changed. 37e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * 38e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * NOTE: If changing anything here, please run the API demo 39e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * "App/Activity/Screen Orientation" to ensure that all orientation 40e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * modes still work correctly. 41e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn * 42daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown * You can also visualize the behavior of the WindowOrientationListener. 43daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown * Refer to frameworks/base/tools/orientationplot/README.txt for details. 444519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class WindowOrientationListener { 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TAG = "WindowOrientationListener"; 49daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private static final boolean LOG = SystemProperties.getBoolean( 50daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown "debug.orientation.log", false); 514519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final boolean USE_GRAVITY_SENSOR = false; 535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 54eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private Handler mHandler; 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private SensorManager mSensorManager; 564519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private boolean mEnabled; 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mRate; 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Sensor mSensor; 5963104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu private SensorEventListenerImpl mSensorEventListener; 60eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private int mCurrentRotation = -1; 61eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner 62eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private final Object mLock = new Object(); 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Creates a new WindowOrientationListener. 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param context for the WindowOrientationListener. 68eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner * @param handler Provides the Looper for receiving sensor updates. 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 70eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner public WindowOrientationListener(Context context, Handler handler) { 71eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner this(context, handler, SensorManager.SENSOR_DELAY_UI); 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Creates a new WindowOrientationListener. 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param context for the WindowOrientationListener. 78eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner * @param handler Provides the Looper for receiving sensor updates. 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param rate at which sensor events are processed (see also 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link android.hardware.SensorManager SensorManager}). Use the default 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * SENSOR_DELAY_NORMAL} for simple screen orientation change detection. 831ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard * 844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * This constructor is private since no one uses it. 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 86eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private WindowOrientationListener(Context context, Handler handler, int rate) { 87eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mHandler = handler; 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mRate = rate; 905aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR 915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER); 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSensor != null) { 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Create listener only if sensors do exist 94eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mSensorEventListener = new SensorEventListenerImpl(); 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Enables the WindowOrientationListener so it will monitor the sensor and call 100eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner * {@link #onProposedRotationChanged(int)} when the device orientation changes. 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void enable() { 103eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner synchronized (mLock) { 104eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (mSensor == null) { 105eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Log.w(TAG, "Cannot detect sensors. Not enabled"); 106eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner return; 107eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 108eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (mEnabled == false) { 109eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (LOG) { 110eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Log.d(TAG, "WindowOrientationListener enabled"); 111eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 112eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mSensorEventListener.resetLocked(); 113eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler); 114eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mEnabled = true; 115daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown } 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Disables the WindowOrientationListener. 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void disable() { 123eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner synchronized (mLock) { 124eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (mSensor == null) { 125eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Log.w(TAG, "Cannot detect sensors. Invalid disable"); 126eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner return; 127eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 128eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (mEnabled == true) { 129eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (LOG) { 130eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Log.d(TAG, "WindowOrientationListener disabled"); 131eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 132eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mSensorManager.unregisterListener(mSensorEventListener); 133eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mEnabled = false; 134daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown } 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1384519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown /** 139c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * Sets the current rotation. 140c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 141c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * @param rotation The current rotation. 142c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown */ 143c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown public void setCurrentRotation(int rotation) { 144eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner synchronized (mLock) { 145eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mCurrentRotation = rotation; 146eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 147c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 148c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 149c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown /** 150c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * Gets the proposed rotation. 151c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 152c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * This method only returns a rotation if the orientation listener is certain 153c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * of its proposal. If the rotation is indeterminate, returns -1. 154c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 155c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * @return The proposed rotation, or -1 if unknown. 1564519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown */ 157c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown public int getProposedRotation() { 158eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner synchronized (mLock) { 159eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (mEnabled) { 160eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner return mSensorEventListener.getProposedRotationLocked(); 161eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 162eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner return -1; 16363104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu } 164e4fbd6235c8d1c5b0ed4883ec275dd3fc9c919fbDianne Hackborn } 1655f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 1665f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard /** 1674519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * Returns true if sensor is enabled and false otherwise 1684519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown */ 1694519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown public boolean canDetectOrientation() { 170eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner synchronized (mLock) { 171eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner return mSensor != null; 172eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 1734519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 1744519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 1754519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown /** 1764519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * Called when the rotation view of the device has changed. 1774519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 178c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * This method is called whenever the orientation becomes certain of an orientation. 179c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * It is called each time the orientation determination transitions from being 180c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * uncertain to being certain again, even if it is the same orientation as before. 181c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 1824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants. 183eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner * @see android.view.Surface 1844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown */ 185c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown public abstract void onProposedRotationChanged(int rotation); 1864519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 187600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown public void dump(PrintWriter pw, String prefix) { 188600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown synchronized (mLock) { 189600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + TAG); 190600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown prefix += " "; 191600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mEnabled=" + mEnabled); 192600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mCurrentRotation=" + mCurrentRotation); 193600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mSensor=" + mSensor); 194600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mRate=" + mRate); 195600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown 1960abc28e1141aa5afb1a4e3dc65a8c658dc7ebbf8Jaewan Kim if (mSensorEventListener != null) { 1970abc28e1141aa5afb1a4e3dc65a8c658dc7ebbf8Jaewan Kim mSensorEventListener.dumpLocked(pw, prefix); 1980abc28e1141aa5afb1a4e3dc65a8c658dc7ebbf8Jaewan Kim } 199600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown } 200600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown } 201600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown 2024519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown /** 2035f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * This class filters the raw accelerometer data and tries to detect actual changes in 2045f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters, 2055f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * but here's the outline: 2065f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * 2074519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * - Low-pass filter the accelerometer vector in cartesian coordinates. We do it in 2084519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * cartesian space because the orientation calculations are sensitive to the 2094519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * absolute magnitude of the acceleration. In particular, there are singularities 2104519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * in the calculation as the magnitude approaches 0. By performing the low-pass 2115aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * filtering early, we can eliminate most spurious high-frequency impulses due to noise. 2124519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 2134519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * - Convert the acceleromter vector from cartesian to spherical coordinates. 2144519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * Since we're dealing with rotation of the device, this is the sensible coordinate 2154519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * system to work in. The zenith direction is the Z-axis, the direction the screen 2164519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * is facing. The radial distance is referred to as the magnitude below. 2174519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * The elevation angle is referred to as the "tilt" below. 2184519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * The azimuth angle is referred to as the "orientation" below (and the azimuth axis is 2194519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * the Y-axis). 2204519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference. 2214519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 2224519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing. 2234519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * The orientation angle is not meaningful when the device is nearly horizontal. 2244519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * The tilt angle thresholds are set differently for each orientation and different 2254519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * limits are applied when the device is facing down as opposed to when it is facing 2264519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * forward or facing up. 2275f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * 2284519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * - When the orientation angle reaches a certain threshold, consider transitioning 2294519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * to the corresponding orientation. These thresholds have some hysteresis built-in 2304519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * to avoid oscillations between adjacent orientations. 2315f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard * 232c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * - Wait for the device to settle for a little bit. Once that happens, issue the 233c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * new orientation proposal. 2344519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * 2354519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * Details are explained inline. 2365aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * 2375aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for 2385aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * signal processing background. 2395f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard */ 240eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner final class SensorEventListenerImpl implements SensorEventListener { 2411ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard // We work with all angles in degrees in this class. 2421ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI); 2431ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 2445aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Number of nanoseconds per millisecond. 2455aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long NANOS_PER_MS = 1000000; 2465aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 2474519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Indices into SensorEvent.values for the accelerometer sensor. 2484519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final int ACCELEROMETER_DATA_X = 0; 2494519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final int ACCELEROMETER_DATA_Y = 1; 2504519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final int ACCELEROMETER_DATA_Z = 2; 2514519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 2525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The minimum amount of time that a predicted rotation must be stable before it 2535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // is accepted as a valid rotation proposal. This value can be quite small because 2545aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // the low-pass filter already suppresses most of the noise so we're really just 2555aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // looking for quick confirmation that the last few samples are in agreement as to 2565aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // the desired orientation. 2575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS; 2585aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 2595aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The minimum amount of time that must have elapsed since the device last exited 2605aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // the flat state (time since it was picked up) before the proposed rotation 2615aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // can change. 2625aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS; 2635aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 264daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // The minimum amount of time that must have elapsed since the device stopped 2655aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // swinging (time since device appeared to be in the process of being put down 2665aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // or put away into a pocket) before the proposed rotation can change. 2675aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS; 2685aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 269daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // The minimum amount of time that must have elapsed since the device stopped 270daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // undergoing external acceleration before the proposed rotation can change. 271daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS = 272daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 500 * NANOS_PER_MS; 273daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 2745aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // If the tilt angle remains greater than the specified angle for a minimum of 2755aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // the specified time, then the device is deemed to be lying flat 2765aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // (just chillin' on a table). 2775aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final float FLAT_ANGLE = 75; 2785aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS; 2795aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 2805aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // If the tilt angle has increased by at least delta degrees within the specified amount 2815aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // of time, then the device is deemed to be swinging away from the user 2825aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // down towards flat (tilt = 90). 2835aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final float SWING_AWAY_ANGLE_DELTA = 20; 2845aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS; 285c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 2864519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The maximum sample inter-arrival time in milliseconds. 2874519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // If the acceleration samples are further apart than this amount in time, we reset the 2884519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // state of the low-pass filter and orientation properties. This helps to handle 2894519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // boundary conditions when the device is turned on, wakes from suspend or there is 2904519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // a significant gap in samples. 2915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS; 2924519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 293c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // The acceleration filter time constant. 294c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // 295c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // This time constant is used to tune the acceleration filter such that 296c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // impulses and vibrational noise (think car dock) is suppressed before we 297c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // try to calculate the tilt and orientation angles. 298c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // 299c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // The filter time constant is related to the filter cutoff frequency, which is the 300c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // frequency at which signals are attenuated by 3dB (half the passband power). 3014519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Each successive octave beyond this frequency is attenuated by an additional 6dB. 3024519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 303c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz 304c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // is given by Fc = 1 / (2pi * t). 305c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // 306c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // The higher the time constant, the lower the cutoff frequency, so more noise 307c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // will be suppressed. 308c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // 309c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // Filtering adds latency proportional the time constant (inversely proportional 310c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // to the cutoff frequency) so we don't want to make the time constant too 3115aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // large or we can lose responsiveness. Likewise we don't want to make it too 3125aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // small or we do a poor job suppressing acceleration spikes. 3135aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Empirically, 100ms seems to be too small and 500ms is too large. 3145aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final float FILTER_TIME_CONSTANT_MS = 200.0f; 3154519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 3164519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown /* State for orientation detection. */ 3174519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 3184519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Thresholds for minimum and maximum allowable deviation from gravity. 3194519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 3204519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // If the device is undergoing external acceleration (being bumped, in a car 3214519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // that is turning around a corner or a plane taking off) then the magnitude 3224519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // may be substantially more or less than gravity. This can skew our orientation 3234519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // detection by making us think that up is pointed in a different direction. 3244519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 3254519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Conversely, if the device is in freefall, then there will be no gravity to 3264519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // measure at all. This is problematic because we cannot detect the orientation 3274519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // without gravity to tell us which way is up. A magnitude near 0 produces 3284519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // singularities in the tilt and orientation calculations. 3294519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // 3304519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // In both cases, we postpone choosing an orientation. 331daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // 332daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // However, we need to tolerate some acceleration because the angular momentum 333daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // of turning the device can skew the observed acceleration for a short period of time. 334daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private static final float NEAR_ZERO_MAGNITUDE = 1; // m/s^2 335daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private static final float ACCELERATION_TOLERANCE = 4; // m/s^2 3364519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final float MIN_ACCELERATION_MAGNITUDE = 337daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE; 3384519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown private static final float MAX_ACCELERATION_MAGNITUDE = 339daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE; 34063104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu 3415f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e. 3425f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard // when screen is facing the sky or ground), we completely ignore orientation data. 3435f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard private static final int MAX_TILT = 75; 3441ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 3454519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The tilt angle range in degrees for each orientation. 3464519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Beyond these tilt angles, we don't even consider transitioning into the 3474519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // specified orientation. We place more stringent requirements on unnatural 3484519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // orientations than natural ones to make it less likely to accidentally transition 3494519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // into those states. 3504519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The first value of each pair is negative so it applies a limit when the device is 3514519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // facing down (overhead reading in bed). 3524519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The second value of each pair is positive so it applies a limit when the device is 3534519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // facing up (resting on a table). 3544519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The ideal tilt angle is 0 (when the device is vertical) so the limits establish 3554519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // how close to vertical the device must be in order to change orientation. 356eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private final int[][] TILT_TOLERANCE = new int[][] { 3575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /* ROTATION_0 */ { -25, 70 }, 3585aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /* ROTATION_90 */ { -25, 65 }, 3595aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /* ROTATION_180 */ { -25, 60 }, 3605aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /* ROTATION_270 */ { -25, 65 } 3614519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown }; 3625f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 363600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown // The tilt angle below which we conclude that the user is holding the device 364600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown // overhead reading in bed and lock into that state. 365600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown private final int TILT_OVERHEAD_ENTER = -40; 366600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown 367600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown // The tilt angle above which we conclude that the user would like a rotation 368600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown // change to occur and unlock from the overhead state. 369600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown private final int TILT_OVERHEAD_EXIT = -15; 370600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown 3714519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // The gap angle in degrees between adjacent orientation angles for hysteresis. 3724519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // This creates a "dead zone" between the current orientation and a proposed 3734519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // adjacent orientation. No orientation proposal is made when the orientation 3744519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // angle is within the gap between the current orientation and the adjacent 3754519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // orientation. 376c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45; 37763104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu 3785aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Timestamp and value of the last accelerometer sample. 3795aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long mLastFilteredTimestampNanos; 3805aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private float mLastFilteredX, mLastFilteredY, mLastFilteredZ; 3815aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 3825aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The last proposed rotation, -1 if unknown. 3835aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private int mProposedRotation; 3845aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 3855aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Value of the current predicted rotation, -1 if unknown. 3865aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private int mPredictedRotation; 3875aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 3885aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Timestamp of when the predicted rotation most recently changed. 3895aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long mPredictedRotationTimestampNanos; 390e5439f228f603f60febe058f633d91d5af2fff76Dianne Hackborn 3915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed). 3925aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long mFlatTimestampNanos; 393600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown private boolean mFlat; 3941ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 3955aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Timestamp when the device last appeared to be swinging. 3965aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long mSwingTimestampNanos; 397600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown private boolean mSwinging; 3981ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 399daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // Timestamp when the device last appeared to be undergoing external acceleration. 400daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown private long mAccelerationTimestampNanos; 401600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown private boolean mAccelerating; 402600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown 403600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown // Whether we are locked into an overhead usage mode. 404600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown private boolean mOverhead; 405daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 4065aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // History of observed tilt angles. 4075aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private static final int TILT_HISTORY_SIZE = 40; 4085aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private float[] mTiltHistory = new float[TILT_HISTORY_SIZE]; 4095aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE]; 4105aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown private int mTiltHistoryIndex; 4111ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 412eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner public int getProposedRotationLocked() { 4135aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return mProposedRotation; 4141ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard } 4151ba101f82eae4e54293428480fbcbfd1c58359c8Steve Howard 416600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown public void dumpLocked(PrintWriter pw, String prefix) { 417600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mProposedRotation=" + mProposedRotation); 418600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mPredictedRotation=" + mPredictedRotation); 419600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mLastFilteredX=" + mLastFilteredX); 420600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mLastFilteredY=" + mLastFilteredY); 421600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ); 422600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}"); 423600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mFlat=" + mFlat); 424600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mSwinging=" + mSwinging); 425600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mAccelerating=" + mAccelerating); 426600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown pw.println(prefix + "mOverhead=" + mOverhead); 427600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown } 428600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown 4294519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown @Override 4304519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown public void onAccuracyChanged(Sensor sensor, int accuracy) { 43163104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu } 43263104edbf06e16642bd0a02d34e0ae3f9aa655aaSuchi Amalapurapu 4334519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown @Override 4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onSensorChanged(SensorEvent event) { 435eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner int proposedRotation; 436eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner int oldProposedRotation; 437eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner 438eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner synchronized (mLock) { 439eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // The vector given in the SensorEvent points straight up (towards the sky) under 440eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // ideal conditions (the phone is not accelerating). I'll call this up vector 441eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // elsewhere. 442eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner float x = event.values[ACCELEROMETER_DATA_X]; 443eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner float y = event.values[ACCELEROMETER_DATA_Y]; 444eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner float z = event.values[ACCELEROMETER_DATA_Z]; 4455f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 446daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 447eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Slog.v(TAG, "Raw acceleration vector: " 4485aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + "x=" + x + ", y=" + y + ", z=" + z 4495aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z)); 4504519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 451eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner 452eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Apply a low-pass filter to the acceleration up vector in cartesian space. 453eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Reset the orientation listener state if the samples are too far apart in time 454eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // or when we see values of (0, 0, 0) which indicates that we polled the 455eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // accelerometer too soon after turning it on and we don't have any data yet. 456eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner final long now = event.timestamp; 457eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner final long then = mLastFilteredTimestampNanos; 458eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner final float timeDeltaMS = (now - then) * 0.000001f; 459eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner final boolean skipSample; 460eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (now < then 461eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner || now > then + MAX_FILTER_DELTA_TIME_NANOS 462eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner || (x == 0 && y == 0 && z == 0)) { 463daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 464eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Slog.v(TAG, "Resetting orientation listener."); 4654519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 466eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner resetLocked(); 467eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner skipSample = true; 4684519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } else { 469eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS); 470eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner x = alpha * (x - mLastFilteredX) + mLastFilteredX; 471eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner y = alpha * (y - mLastFilteredY) + mLastFilteredY; 472eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner z = alpha * (z - mLastFilteredZ) + mLastFilteredZ; 473eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (LOG) { 474eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Slog.v(TAG, "Filtered acceleration vector: " 475eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + "x=" + x + ", y=" + y + ", z=" + z 476eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z)); 4775aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 478eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner skipSample = false; 479eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 480eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mLastFilteredTimestampNanos = now; 481eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mLastFilteredX = x; 482eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mLastFilteredY = y; 483eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mLastFilteredZ = z; 484eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner 485eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner boolean isAccelerating = false; 486eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner boolean isFlat = false; 487eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner boolean isSwinging = false; 488eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (!skipSample) { 489eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Calculate the magnitude of the acceleration vector. 490eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner final float magnitude = FloatMath.sqrt(x * x + y * y + z * z); 491eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (magnitude < NEAR_ZERO_MAGNITUDE) { 492daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 493eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero."); 4944519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 495eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner clearPredictedRotationLocked(); 4964519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } else { 497eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Determine whether the device appears to be undergoing external 498eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // acceleration. 499eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (isAcceleratingLocked(magnitude)) { 500eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner isAccelerating = true; 501eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mAccelerationTimestampNanos = now; 502c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 503c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 504eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Calculate the tilt angle. 505eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // This is the angle between the up vector and the x-y plane (the plane of 506eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // the screen) in a range of [-90, 90] degrees. 507eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // -90 degrees: screen horizontal and facing the ground (overhead) 508eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // 0 degrees: screen vertical 509eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // 90 degrees: screen horizontal and facing the sky (on table) 510eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner final int tiltAngle = (int) Math.round( 511eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Math.asin(z / magnitude) * RADIANS_TO_DEGREES); 512eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner addTiltHistoryEntryLocked(now, tiltAngle); 513eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner 514eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Determine whether the device appears to be flat or swinging. 515eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (isFlatLocked(now)) { 516eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner isFlat = true; 517eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mFlatTimestampNanos = now; 518eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 519eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (isSwingingLocked(now, tiltAngle)) { 520eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner isSwinging = true; 521eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mSwingTimestampNanos = now; 522c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 523c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 524eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // If the tilt angle is too close to horizontal then we cannot determine 525eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // the orientation angle of the screen. 526600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown if (tiltAngle <= TILT_OVERHEAD_ENTER) { 527600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown mOverhead = true; 528600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown } else if (tiltAngle >= TILT_OVERHEAD_EXIT) { 529600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown mOverhead = false; 530600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown } 531600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown if (mOverhead) { 532600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown if (LOG) { 533600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown Slog.v(TAG, "Ignoring sensor data, device is overhead: " 534600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown + "tiltAngle=" + tiltAngle); 535600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown } 536600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown clearPredictedRotationLocked(); 537600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown } else if (Math.abs(tiltAngle) > MAX_TILT) { 538daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 539eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " 540eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + "tiltAngle=" + tiltAngle); 541c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 542eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner clearPredictedRotationLocked(); 543c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } else { 544eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Calculate the orientation angle. 545eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // This is the angle between the x-y projection of the up vector onto 546eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // the +y-axis, increasing clockwise in a range of [0, 360] degrees. 547eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner int orientationAngle = (int) Math.round( 548eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner -Math.atan2(-x, y) * RADIANS_TO_DEGREES); 549eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (orientationAngle < 0) { 550eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // atan2 returns [-180, 180]; normalize to [0, 360] 551eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner orientationAngle += 360; 552eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 553eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner 554eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Find the nearest rotation. 555eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner int nearestRotation = (orientationAngle + 45) / 90; 556eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (nearestRotation == 4) { 557eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner nearestRotation = 0; 558eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 559eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner 560eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Determine the predicted orientation. 561eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (isTiltAngleAcceptableLocked(nearestRotation, tiltAngle) 562eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner && isOrientationAngleAcceptableLocked(nearestRotation, 563eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner orientationAngle)) { 564eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner updatePredictedRotationLocked(now, nearestRotation); 565eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (LOG) { 566eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Slog.v(TAG, "Predicted: " 567eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + "tiltAngle=" + tiltAngle 568eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", orientationAngle=" + orientationAngle 569eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", predictedRotation=" + mPredictedRotation 570eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", predictedRotationAgeMS=" 571eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ((now - mPredictedRotationTimestampNanos) 572eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner * 0.000001f)); 573eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 574eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } else { 575eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (LOG) { 576eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Slog.v(TAG, "Ignoring sensor data, no predicted rotation: " 577eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + "tiltAngle=" + tiltAngle 578eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", orientationAngle=" + orientationAngle); 579eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 580eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner clearPredictedRotationLocked(); 581c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 5824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5834519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 5844519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 585600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown mFlat = isFlat; 586600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown mSwinging = isSwinging; 587600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown mAccelerating = isAccelerating; 5885f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 589eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Determine new proposed rotation. 590eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner oldProposedRotation = mProposedRotation; 591eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (mPredictedRotation < 0 || isPredictedRotationAcceptableLocked(now)) { 592eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mProposedRotation = mPredictedRotation; 593eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 594eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner proposedRotation = mProposedRotation; 5955aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 596eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner // Write final statistics about where we are in the orientation detection process. 597eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (LOG) { 598eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Slog.v(TAG, "Result: currentRotation=" + mCurrentRotation 599eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", proposedRotation=" + proposedRotation 600eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", predictedRotation=" + mPredictedRotation 601eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", timeDeltaMS=" + timeDeltaMS 602eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", isAccelerating=" + isAccelerating 603eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", isFlat=" + isFlat 604eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", isSwinging=" + isSwinging 605600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown + ", isOverhead=" + mOverhead 606eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", timeUntilSettledMS=" + remainingMS(now, 607eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) 608eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now, 609eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) 610eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now, 611eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) 612eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now, 613eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)); 614eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner } 6154519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6164519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown 6174519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // Tell the listener. 618eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner if (proposedRotation != oldProposedRotation && proposedRotation >= 0) { 619daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (LOG) { 620eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation 621c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown + ", oldProposedRotation=" + oldProposedRotation); 622c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 623eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner onProposedRotationChanged(proposedRotation); 6244519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6255f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 6265f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 6275f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard /** 6285aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * Returns true if the tilt angle is acceptable for a given predicted rotation. 6295f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard */ 630eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private boolean isTiltAngleAcceptableLocked(int rotation, int tiltAngle) { 6315aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return tiltAngle >= TILT_TOLERANCE[rotation][0] 6325aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown && tiltAngle <= TILT_TOLERANCE[rotation][1]; 6335f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 6345f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 6355f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard /** 6365aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * Returns true if the orientation angle is acceptable for a given predicted rotation. 637c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown * 6384519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * This function takes into account the gap between adjacent orientations 6394519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown * for hysteresis. 6405f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard */ 641eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private boolean isOrientationAngleAcceptableLocked(int rotation, int orientationAngle) { 6424519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // If there is no current rotation, then there is no gap. 643c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // The gap is used only to introduce hysteresis among advertised orientation 644c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // changes to avoid flapping. 645eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner final int currentRotation = mCurrentRotation; 646c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown if (currentRotation >= 0) { 6475aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // If the specified rotation is the same or is counter-clockwise adjacent 6485aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // to the current rotation, then we set a lower bound on the orientation angle. 649c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90, 6504519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // then we want to check orientationAngle > 45 + GAP / 2. 6515aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (rotation == currentRotation 6525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown || rotation == (currentRotation + 1) % 4) { 6535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown int lowerBound = rotation * 90 - 45 6544519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown + ADJACENT_ORIENTATION_ANGLE_GAP / 2; 6555aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (rotation == 0) { 6564519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) { 6574519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return false; 6584519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6594519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } else { 6604519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown if (orientationAngle < lowerBound) { 6614519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return false; 6624519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6634519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6645f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 6655f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 6665aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // If the specified rotation is the same or is clockwise adjacent, 6674519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // then we set an upper bound on the orientation angle. 6685aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270, 6694519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown // then we want to check orientationAngle < 315 - GAP / 2. 6705aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (rotation == currentRotation 6715aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown || rotation == (currentRotation + 3) % 4) { 6725aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown int upperBound = rotation * 90 + 45 6734519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown - ADJACENT_ORIENTATION_ANGLE_GAP / 2; 6745aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (rotation == 0) { 6754519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown if (orientationAngle <= 45 && orientationAngle > upperBound) { 6764519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return false; 6774519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6784519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } else { 6794519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown if (orientationAngle > upperBound) { 6804519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return false; 6814519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6824519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown } 6835f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 6845f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 6854519f07e9c6b993fbe7a3d3df24d71d9450a54f1Jeff Brown return true; 6865f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard } 6875f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 6885aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown /** 6895aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * Returns true if the predicted rotation is ready to be advertised as a 6905aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown * proposed rotation. 6915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown */ 692eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private boolean isPredictedRotationAcceptableLocked(long now) { 6935aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The predicted rotation must have settled long enough. 6945aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) { 6955aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 6965aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 6975aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 6985aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The last flat state (time since picked up) must have been sufficiently long ago. 6995aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) { 7005aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 7015aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7025f531ae6b342697ba94ddb68b47f76ccddb75f7bSteve Howard 7035aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // The last swing state (time since last movement to put down) must have been 7045aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // sufficiently long ago. 7055aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) { 7065aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 707c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 708c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 709daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown // The last acceleration state must have been sufficiently long ago. 710daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown if (now < mAccelerationTimestampNanos 711daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) { 712daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown return false; 713daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown } 714daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 7155aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Looks good! 7165aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return true; 7175aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7185aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 719eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private void resetLocked() { 7205aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mLastFilteredTimestampNanos = Long.MIN_VALUE; 7215aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mProposedRotation = -1; 7225aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mFlatTimestampNanos = Long.MIN_VALUE; 723600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown mFlat = false; 7245aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mSwingTimestampNanos = Long.MIN_VALUE; 725600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown mSwinging = false; 726daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown mAccelerationTimestampNanos = Long.MIN_VALUE; 727600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown mAccelerating = false; 728600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown mOverhead = false; 729eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner clearPredictedRotationLocked(); 730eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner clearTiltHistoryLocked(); 7315aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7325aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 733eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private void clearPredictedRotationLocked() { 7345aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mPredictedRotation = -1; 7355aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mPredictedRotationTimestampNanos = Long.MIN_VALUE; 7365aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7375aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 738eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private void updatePredictedRotationLocked(long now, int rotation) { 7395aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mPredictedRotation != rotation) { 7405aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mPredictedRotation = rotation; 7415aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mPredictedRotationTimestampNanos = now; 742c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 7435aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 744c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 745eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private boolean isAcceleratingLocked(float magnitude) { 746daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown return magnitude < MIN_ACCELERATION_MAGNITUDE 747daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown || magnitude > MAX_ACCELERATION_MAGNITUDE; 748daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown } 749daf5d894ef71c5674e83b11de8b408e3bdabe4c7Jeff Brown 750eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private void clearTiltHistoryLocked() { 7515aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE; 7525aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryIndex = 1; 7535aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7545aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 755eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private void addTiltHistoryEntryLocked(long now, float tilt) { 7565aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistory[mTiltHistoryIndex] = tilt; 7575aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now; 7585aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE; 7595aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE; 7605aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7615aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 762eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private boolean isFlatLocked(long now) { 763eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) { 7645aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mTiltHistory[i] < FLAT_ANGLE) { 765c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown break; 766c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 7675aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) { 7685aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS. 7695aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return true; 770c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 7715aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7725aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 7735aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7745aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 775eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private boolean isSwingingLocked(long now, float tilt) { 776eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndexLocked(i)) >= 0; ) { 7775aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) { 778c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown break; 779c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 7805aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) { 7815aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS. 7825aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return true; 783c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 784c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 7855aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return false; 786c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown } 787c0347aa19f354a8e1ff4fcd5372b134c0c7c16adJeff Brown 788eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private int nextTiltHistoryIndexLocked(int index) { 7895aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1; 7905aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1; 7915aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown } 7925aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown 793600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown private float getLastTiltLocked() { 794600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex); 795600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown return index >= 0 ? mTiltHistory[index] : Float.NaN; 796600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown } 797600f0031331314faacedbdb4d8c3e7e7dee3895aJeff Brown 798eee29c445c38217e83bf421faf0f4075322079a6Craig Mautner private float remainingMS(long now, long until) { 7995aa73ae58f049379a97bc86add541f27170c02a4Jeff Brown return now >= until ? 0 : (until - now) * 0.000001f; 8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 803