Merge "Add support for new window orientation sensor." into mnc-dr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
fbcd78e6ed
@@ -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.
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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();
|
||||
@@ -67,7 +70,7 @@ public abstract class WindowOrientationListener {
|
||||
public WindowOrientationListener(Context context, Handler handler) {
|
||||
this(context, handler, SensorManager.SENSOR_DELAY_UI);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new WindowOrientationListener.
|
||||
*
|
||||
@@ -84,11 +87,31 @@ public abstract class WindowOrientationListener {
|
||||
mHandler = handler;
|
||||
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||
mRate = rate;
|
||||
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);
|
||||
|
||||
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
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user