Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fbcd78e6 authored by Trevor Bunker's avatar Trevor Bunker Committed by Android (Google) Code Review
Browse files

Merge "Add support for new window orientation sensor." into mnc-dr-dev

parents b5e47228 814de9b0
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -619,6 +619,17 @@
        <!-- rotation: 270 (rotate CW)  --> <item>-25</item> <item>65</item>
    </integer-array>

    <!-- Indicate the name of the window orientation sensor type if present. A
         window orientation sensor produces values to be used in lieu of the
         typical, accelerometer based sensor. It must only produce integral
         values between 0 and 3, inclusive, with each one corresponding to a
         given rotation:
            0: 0 degrees of rotation (natural)
            1: 90 degrees of rotation (rotate CCW)
            2: 180 degrees of rotation (reverse)
            3: 270 degrees of rotation (rotate CW) -->
    <string name="config_orientationSensorType" translatable="false">@null</string>

    <!-- Lid switch behavior -->

    <!-- The number of degrees to rotate the display when the keyboard is open.
+1 −0
Original line number Diff line number Diff line
@@ -1547,6 +1547,7 @@
  <java-symbol type="string" name="bugreport_title" />
  <java-symbol type="string" name="bugreport_message" />
  <java-symbol type="string" name="bugreport_status" />
  <java-symbol type="string" name="config_orientationSensorType" />
  <java-symbol type="string" name="faceunlock_multiple_failures" />
  <java-symbol type="string" name="global_action_power_off" />
  <java-symbol type="string" name="global_actions_airplane_mode_off_status" />
+263 −45
Original line number Diff line number Diff line
@@ -24,10 +24,12 @@ import android.hardware.SensorManager;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;

import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;

/**
 * A special helper class used by the WindowManager
@@ -52,8 +54,9 @@ public abstract class WindowOrientationListener {
    private SensorManager mSensorManager;
    private boolean mEnabled;
    private int mRate;
    private String mSensorType;
    private Sensor mSensor;
    private SensorEventListenerImpl mSensorEventListener;
    private OrientationJudge mOrientationJudge;
    private int mCurrentRotation = -1;

    private final Object mLock = new Object();
@@ -84,11 +87,31 @@ public abstract class WindowOrientationListener {
        mHandler = handler;
        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
        mRate = rate;

        mSensorType = context.getResources().getString(
                com.android.internal.R.string.config_orientationSensorType);
        if (!TextUtils.isEmpty(mSensorType)) {
            List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
            final int N = sensors.size();
            for (int i = 0; i < N; i++) {
                Sensor sensor = sensors.get(i);
                if (mSensorType.equals(sensor.getStringType())) {
                    mSensor = sensor;
                    break;
                }
            }
            if (mSensor != null) {
                mOrientationJudge = new OrientationSensorJudge();
            }
        }

        if (mOrientationJudge == null) {
            mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
                    ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
            if (mSensor != null) {
                // Create listener only if sensors do exist
            mSensorEventListener = new SensorEventListenerImpl(context);
                mOrientationJudge = new AccelSensorJudge(context);
            }
        }
    }

@@ -106,8 +129,8 @@ public abstract class WindowOrientationListener {
                if (LOG) {
                    Slog.d(TAG, "WindowOrientationListener enabled");
                }
                mSensorEventListener.resetLocked();
                mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler);
                mOrientationJudge.resetLocked();
                mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
                mEnabled = true;
            }
        }
@@ -126,7 +149,7 @@ public abstract class WindowOrientationListener {
                if (LOG) {
                    Slog.d(TAG, "WindowOrientationListener disabled");
                }
                mSensorManager.unregisterListener(mSensorEventListener);
                mSensorManager.unregisterListener(mOrientationJudge);
                mEnabled = false;
            }
        }
@@ -134,8 +157,8 @@ public abstract class WindowOrientationListener {

    public void onTouchStart() {
        synchronized (mLock) {
            if (mSensorEventListener != null) {
                mSensorEventListener.onTouchStartLocked();
            if (mOrientationJudge != null) {
                mOrientationJudge.onTouchStartLocked();
            }
        }
    }
@@ -144,8 +167,8 @@ public abstract class WindowOrientationListener {
        long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();

        synchronized (mLock) {
            if (mSensorEventListener != null) {
                mSensorEventListener.onTouchEndLocked(whenElapsedNanos);
            if (mOrientationJudge != null) {
                mOrientationJudge.onTouchEndLocked(whenElapsedNanos);
            }
        }
    }
@@ -172,7 +195,7 @@ public abstract class WindowOrientationListener {
    public int getProposedRotation() {
        synchronized (mLock) {
            if (mEnabled) {
                return mSensorEventListener.getProposedRotationLocked();
                return mOrientationJudge.getProposedRotationLocked();
            }
            return -1;
        }
@@ -205,15 +228,77 @@ public abstract class WindowOrientationListener {
            prefix += "  ";
            pw.println(prefix + "mEnabled=" + mEnabled);
            pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
            pw.println(prefix + "mSensorType=" + mSensorType);
            pw.println(prefix + "mSensor=" + mSensor);
            pw.println(prefix + "mRate=" + mRate);

            if (mSensorEventListener != null) {
                mSensorEventListener.dumpLocked(pw, prefix);
            if (mOrientationJudge != null) {
                mOrientationJudge.dumpLocked(pw, prefix);
            }
        }
    }

    abstract class OrientationJudge implements SensorEventListener {
        // Number of nanoseconds per millisecond.
        protected static final long NANOS_PER_MS = 1000000;

        // Number of milliseconds per nano second.
        protected static final float MILLIS_PER_NANO = 0.000001f;

        // The minimum amount of time that must have elapsed since the screen was last touched
        // before the proposed rotation can change.
        protected static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
                500 * NANOS_PER_MS;

        /**
         * Gets the proposed rotation.
         *
         * This method only returns a rotation if the orientation listener is certain
         * of its proposal.  If the rotation is indeterminate, returns -1.
         *
         * Should only be called when holding WindowOrientationListener lock.
         *
         * @return The proposed rotation, or -1 if unknown.
         */
        public abstract int getProposedRotationLocked();

        /**
         * Notifies the orientation judge that the screen is being touched.
         *
         * Should only be called when holding WindowOrientationListener lock.
         */
        public abstract void onTouchStartLocked();

        /**
         * Notifies the orientation judge that the screen is no longer being touched.
         *
         * Should only be called when holding WindowOrientationListener lock.
         *
         * @param whenElapsedNanos Given in the elapsed realtime nanos time base.
         */
        public abstract void onTouchEndLocked(long whenElapsedNanos);

        /**
         * Resets the state of the judge.
         *
         * Should only be called when holding WindowOrientationListener lock.
         */
        public abstract void resetLocked();

        /**
         * Dumps internal state of the orientation judge.
         *
         * Should only be called when holding WindowOrientationListener lock.
         */
        public abstract void dumpLocked(PrintWriter pw, String prefix);

        @Override
        public abstract void onAccuracyChanged(Sensor sensor, int accuracy);

        @Override
        public abstract void onSensorChanged(SensorEvent event);
    }

    /**
     * This class filters the raw accelerometer data and tries to detect actual changes in
     * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
@@ -252,13 +337,10 @@ public abstract class WindowOrientationListener {
     * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
     * signal processing background.
     */
    final class SensorEventListenerImpl implements SensorEventListener {
    final class AccelSensorJudge extends OrientationJudge {
        // We work with all angles in degrees in this class.
        private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);

        // Number of nanoseconds per millisecond.
        private static final long NANOS_PER_MS = 1000000;

        // Indices into SensorEvent.values for the accelerometer sensor.
        private static final int ACCELEROMETER_DATA_X = 0;
        private static final int ACCELEROMETER_DATA_Y = 1;
@@ -286,11 +368,6 @@ public abstract class WindowOrientationListener {
        private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
                500 * NANOS_PER_MS;

        // The minimum amount of time that must have elapsed since the screen was last touched
        // before the proposed rotation can change.
        private static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
                500 * NANOS_PER_MS;

        // If the tilt angle remains greater than the specified angle for a minimum of
        // the specified time, then the device is deemed to be lying flat
        // (just chillin' on a table).
@@ -434,7 +511,7 @@ public abstract class WindowOrientationListener {
        private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE];
        private int mTiltHistoryIndex;

        public SensorEventListenerImpl(Context context) {
        public AccelSensorJudge(Context context) {
            // Load tilt tolerance configuration.
            int[] tiltTolerance = context.getResources().getIntArray(
                    com.android.internal.R.array.config_autoRotationTiltTolerance);
@@ -455,11 +532,15 @@ public abstract class WindowOrientationListener {
            }
        }

        @Override
        public int getProposedRotationLocked() {
            return mProposedRotation;
        }

        @Override
        public void dumpLocked(PrintWriter pw, String prefix) {
            pw.println(prefix + "AccelSensorJudge");
            prefix += "  ";
            pw.println(prefix + "mProposedRotation=" + mProposedRotation);
            pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
            pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
@@ -689,6 +770,33 @@ public abstract class WindowOrientationListener {
            }
        }

        @Override
        public void onTouchStartLocked() {
            mTouched = true;
        }

        @Override
        public void onTouchEndLocked(long whenElapsedNanos) {
            mTouched = false;
            mTouchEndedTimestampNanos = whenElapsedNanos;
        }

        @Override
        public void resetLocked() {
            mLastFilteredTimestampNanos = Long.MIN_VALUE;
            mProposedRotation = -1;
            mFlatTimestampNanos = Long.MIN_VALUE;
            mFlat = false;
            mSwingTimestampNanos = Long.MIN_VALUE;
            mSwinging = false;
            mAccelerationTimestampNanos = Long.MIN_VALUE;
            mAccelerating = false;
            mOverhead = false;
            clearPredictedRotationLocked();
            clearTiltHistoryLocked();
        }


        /**
         * Returns true if the tilt angle is acceptable for a given predicted rotation.
         */
@@ -787,20 +895,6 @@ public abstract class WindowOrientationListener {
            return true;
        }

        private void resetLocked() {
            mLastFilteredTimestampNanos = Long.MIN_VALUE;
            mProposedRotation = -1;
            mFlatTimestampNanos = Long.MIN_VALUE;
            mFlat = false;
            mSwingTimestampNanos = Long.MIN_VALUE;
            mSwinging = false;
            mAccelerationTimestampNanos = Long.MIN_VALUE;
            mAccelerating = false;
            mOverhead = false;
            clearPredictedRotationLocked();
            clearTiltHistoryLocked();
        }

        private void clearPredictedRotationLocked() {
            mPredictedRotation = -1;
            mPredictedRotationTimestampNanos = Long.MIN_VALUE;
@@ -869,14 +963,138 @@ public abstract class WindowOrientationListener {
        private float remainingMS(long now, long until) {
            return now >= until ? 0 : (until - now) * 0.000001f;
        }
    }

        private void onTouchStartLocked() {
            mTouched = true;
    final class OrientationSensorJudge extends OrientationJudge {
        private boolean mTouching;
        private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
        private int mProposedRotation = -1;
        private int mDesiredRotation = -1;
        private boolean mRotationEvaluationScheduled;

        @Override
        public int getProposedRotationLocked() {
            return mProposedRotation;
        }

        private void onTouchEndLocked(long whenElapsedNanos) {
            mTouched = false;
        @Override
        public void onTouchStartLocked() {
            mTouching = true;
        }

        @Override
        public void onTouchEndLocked(long whenElapsedNanos) {
            mTouching = false;
            mTouchEndedTimestampNanos = whenElapsedNanos;
            if (mDesiredRotation != mProposedRotation) {
                final long now = SystemClock.elapsedRealtimeNanos();
                scheduleRotationEvaluationIfNecessaryLocked(now);
            }
        }


        @Override
        public void onSensorChanged(SensorEvent event) {
            synchronized (mLock) {
                mDesiredRotation = (int) event.values[0];
                evaluateRotationChangeLocked();
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) { }

        @Override
        public void dumpLocked(PrintWriter pw, String prefix) {
            pw.println(prefix + "OrientationSensorJudge");
            prefix += "  ";
            pw.println(prefix + "mDesiredRotation=" + mDesiredRotation);
            pw.println(prefix + "mProposedRotation=" + mProposedRotation);
            pw.println(prefix + "mTouching=" + mTouching);
            pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos);
        }

        @Override
        public void resetLocked() {
            mProposedRotation = -1;
            mDesiredRotation = -1;
            mTouching = false;
            mTouchEndedTimestampNanos = Long.MIN_VALUE;
            unscheduleRotationEvaluationLocked();
        }

        public void evaluateRotationChangeLocked() {
            unscheduleRotationEvaluationLocked();
            if (mDesiredRotation == mProposedRotation) {
                return;
            }
            final long now = SystemClock.elapsedRealtimeNanos();
            if (isDesiredRotationAcceptableLocked(now)) {
                mProposedRotation = mDesiredRotation;
                onProposedRotationChanged(mProposedRotation);
            } else {
                scheduleRotationEvaluationIfNecessaryLocked(now);
            }
        }

        private boolean isDesiredRotationAcceptableLocked(long now) {
            if (mTouching) {
                return false;
            }
            if (now < mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
                return false;
            }
            return true;
        }

        private void scheduleRotationEvaluationIfNecessaryLocked(long now) {
            if (mRotationEvaluationScheduled || mDesiredRotation == mProposedRotation) {
                if (LOG) {
                    Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
                            "ignoring, an evaluation is already scheduled or is unnecessary.");
                }
                return;
            }
            if (mTouching) {
                if (LOG) {
                    Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
                            "ignoring, user is still touching the screen.");
                }
                return;
            }
            long timeOfNextPossibleRotationNanos =
                mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS;
            if (now >= timeOfNextPossibleRotationNanos) {
                if (LOG) {
                    Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
                            "ignoring, already past the next possible time of rotation.");
                }
                return;
            }
            // Use a delay instead of an absolute time since handlers are in uptime millis and we
            // use elapsed realtime.
            final long delayMs =
                    (long) Math.ceil((timeOfNextPossibleRotationNanos - now) * MILLIS_PER_NANO);
            mHandler.postDelayed(mRotationEvaluator, delayMs);
            mRotationEvaluationScheduled = true;
        }

        private void unscheduleRotationEvaluationLocked() {
            if (!mRotationEvaluationScheduled) {
                return;
            }
            mHandler.removeCallbacks(mRotationEvaluator);
            mRotationEvaluationScheduled = false;
        }

        private Runnable mRotationEvaluator = new Runnable() {
            @Override
            public void run() {
                synchronized (mLock) {
                    mRotationEvaluationScheduled = false;
                    evaluateRotationChangeLocked();
                }
            }
        };
    }
}