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