Fix issue #3041660: Camera image flips upside down when rotating device
Apps now must explicitly opt in to having their rotation changed while
forcing landscape mode. Also add some new orientation constants for other
things apps may want to do.
Change-Id: If64d84b5ef54793ee717ebda9b4c76408efc9bfd
diff --git a/api/current.xml b/api/current.xml
index bffdccc..e465df5 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -46755,6 +46755,17 @@
visibility="public"
>
</field>
+<field name="SCREEN_ORIENTATION_FULL_SENSOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SCREEN_ORIENTATION_LANDSCAPE"
type="int"
transient="false"
@@ -46788,6 +46799,28 @@
visibility="public"
>
</field>
+<field name="SCREEN_ORIENTATION_REVERSE_LANDSCAPE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCREEN_ORIENTATION_REVERSE_PORTRAIT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SCREEN_ORIENTATION_SENSOR"
type="int"
transient="false"
@@ -46799,6 +46832,28 @@
visibility="public"
>
</field>
+<field name="SCREEN_ORIENTATION_SENSOR_LANDSCAPE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCREEN_ORIENTATION_SENSOR_PORTRAIT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SCREEN_ORIENTATION_UNSPECIFIED"
type="int"
transient="false"
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 395c392..e26f160 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -194,10 +194,41 @@
public static final int SCREEN_ORIENTATION_SENSOR = 4;
/**
- * Constant corresponding to <code>sensor</code> in
+ * Constant corresponding to <code>nosensor</code> in
* the {@link android.R.attr#screenOrientation} attribute.
*/
public static final int SCREEN_ORIENTATION_NOSENSOR = 5;
+
+ /**
+ * Constant corresponding to <code>sensorLandscape</code> in
+ * the {@link android.R.attr#screenOrientation} attribute.
+ */
+ public static final int SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 6;
+
+ /**
+ * Constant corresponding to <code>sensorPortrait</code> in
+ * the {@link android.R.attr#screenOrientation} attribute.
+ */
+ public static final int SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7;
+
+ /**
+ * Constant corresponding to <code>reverseLandscape</code> in
+ * the {@link android.R.attr#screenOrientation} attribute.
+ */
+ public static final int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8;
+
+ /**
+ * Constant corresponding to <code>reversePortrait</code> in
+ * the {@link android.R.attr#screenOrientation} attribute.
+ */
+ public static final int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9;
+
+ /**
+ * Constant corresponding to <code>fullSensor</code> in
+ * the {@link android.R.attr#screenOrientation} attribute.
+ */
+ public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10;
+
/**
* The preferred screen orientation this activity would like to run in.
* From the {@link android.R.attr#screenOrientation} attribute, one of
@@ -207,7 +238,12 @@
* {@link #SCREEN_ORIENTATION_USER},
* {@link #SCREEN_ORIENTATION_BEHIND},
* {@link #SCREEN_ORIENTATION_SENSOR},
- * {@link #SCREEN_ORIENTATION_NOSENSOR}.
+ * {@link #SCREEN_ORIENTATION_NOSENSOR},
+ * {@link #SCREEN_ORIENTATION_SENSOR_LANDSCAPE},
+ * {@link #SCREEN_ORIENTATION_SENSOR_PORTRAIT},
+ * {@link #SCREEN_ORIENTATION_REVERSE_LANDSCAPE},
+ * {@link #SCREEN_ORIENTATION_REVERSE_PORTRAIT},
+ * {@link #SCREEN_ORIENTATION_FULL_SENSOR}.
*/
public int screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 8ffa158..3e2e92b 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -28,6 +28,11 @@
* A special helper class used by the WindowManager
* for receiving notifications from the SensorManager when
* the orientation of the device has changed.
+ *
+ * NOTE: If changing anything here, please run the API demo
+ * "App/Activity/Screen Orientation" to ensure that all orientation
+ * modes still work correctly.
+ *
* @hide
*/
public abstract class WindowOrientationListener {
@@ -103,6 +108,10 @@
}
}
+ public void setAllow180Rotation(boolean allowed) {
+ mSensorEventListener.setAllow180Rotation(allowed);
+ }
+
public int getCurrentRotation(int lastRotation) {
if (mEnabled) {
return mSensorEventListener.getCurrentRotation(lastRotation);
@@ -151,19 +160,20 @@
private static final int ROTATION_0 = 0;
private static final int ROTATION_90 = 1;
private static final int ROTATION_270 = 2;
+ private static final int ROTATION_180 = 3;
// Mapping our internal aliases into actual Surface rotation values
private static final int[] INTERNAL_TO_SURFACE_ROTATION = new int[] {
- Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+ Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270,
+ Surface.ROTATION_180};
// Mapping Surface rotation values to internal aliases.
- // We have no constant for Surface.ROTATION_180. That should never happen, but if it
- // does, we'll arbitrarily choose a mapping.
private static final int[] SURFACE_TO_INTERNAL_ROTATION = new int[] {
- ROTATION_0, ROTATION_90, ROTATION_90, ROTATION_270};
+ ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270};
// Threshold ranges of orientation angle to transition into other orientation states.
- // The first list is for transitions from ROTATION_0, the next for ROTATION_90, etc.
+ // The first list is for transitions from ROTATION_0, ROTATION_90, ROTATION_270,
+ // and then ROTATION_180.
// ROTATE_TO defines the orientation each threshold range transitions to, and must be kept
// in sync with this.
// We generally transition about the halfway point between two states with a swing of 30
@@ -172,13 +182,32 @@
{{60, 180}, {180, 300}},
{{0, 30}, {195, 315}, {315, 360}},
{{0, 45}, {45, 165}, {330, 360}},
- };
+ // Handle situation where we are currently doing 180 rotation
+ // but that is no longer allowed.
+ {{0, 45}, {45, 135}, {135, 225}, {225, 315}, {315, 360}},
+ };
// See THRESHOLDS
private static final int[][] ROTATE_TO = new int[][] {
{ROTATION_90, ROTATION_270},
{ROTATION_0, ROTATION_270, ROTATION_0},
{ROTATION_0, ROTATION_90, ROTATION_0},
+ {ROTATION_0, ROTATION_90, ROTATION_0, ROTATION_270, ROTATION_0},
+ };
+
+ // Thresholds that allow all 4 orientations.
+ private static final int[][][] THRESHOLDS_WITH_180 = new int[][][] {
+ {{60, 165}, {165, 195}, {195, 300}},
+ {{0, 30}, {165, 195}, {195, 315}, {315, 360}},
+ {{0, 45}, {45, 165}, {165, 195}, {330, 360}},
+ {{0, 45}, {45, 135}, {225, 315}, {315, 360}},
+ };
+ // See THRESHOLDS_WITH_180
+ private static final int[][] ROTATE_TO_WITH_180 = new int[][] {
+ {ROTATION_90, ROTATION_180, ROTATION_270},
+ {ROTATION_0, ROTATION_180, ROTATION_90, ROTATION_0},
+ {ROTATION_0, ROTATION_270, ROTATION_180, ROTATION_0},
+ {ROTATION_0, ROTATION_90, ROTATION_270, ROTATION_0},
};
// Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
@@ -188,7 +217,7 @@
// Additional limits on tilt angle to transition to each new orientation. We ignore all
// data with tilt beyond MAX_TILT, but we can set stricter limits on transitions to a
// particular orientation here.
- private static final int[] MAX_TRANSITION_TILT = new int[] {MAX_TILT, 65, 65};
+ private static final int[] MAX_TRANSITION_TILT = new int[] {MAX_TILT, 65, 65, 40};
// Between this tilt angle and MAX_TILT, we'll allow orientation changes, but we'll filter
// with a higher time constant, making us less sensitive to change. This primarily helps
@@ -228,6 +257,8 @@
private static final float ACCELERATING_LOWPASS_ALPHA =
computeLowpassAlpha(ACCELERATING_TIME_CONSTANT_MS);
+ private boolean mAllow180Rotation = false;
+
private WindowOrientationListener mOrientationListener;
private int mRotation = ROTATION_0; // Current orientation state
private float mTiltAngle = 0; // low-pass filtered
@@ -249,6 +280,10 @@
return (float) SAMPLING_PERIOD_MS / (timeConstantMs + SAMPLING_PERIOD_MS);
}
+ void setAllow180Rotation(boolean allowed) {
+ mAllow180Rotation = allowed;
+ }
+
int getCurrentRotation(int lastRotation) {
if (mTiltDistrust > 0) {
// we really don't know the current orientation, so trust what's currently displayed
@@ -259,7 +294,9 @@
private void calculateNewRotation(float orientation, float tiltAngle) {
if (localLOGV) Log.i(TAG, orientation + ", " + tiltAngle + ", " + mRotation);
- int thresholdRanges[][] = THRESHOLDS[mRotation];
+ final boolean allow180Rotation = mAllow180Rotation;
+ int thresholdRanges[][] = allow180Rotation
+ ? THRESHOLDS_WITH_180[mRotation] : THRESHOLDS[mRotation];
int row = -1;
for (int i = 0; i < thresholdRanges.length; i++) {
if (orientation >= thresholdRanges[i][0] && orientation < thresholdRanges[i][1]) {
@@ -269,13 +306,15 @@
}
if (row == -1) return; // no matching transition
- int rotation = ROTATE_TO[mRotation][row];
+ int rotation = allow180Rotation
+ ? ROTATE_TO_WITH_180[mRotation][row] : ROTATE_TO[mRotation][row];
if (tiltAngle > MAX_TRANSITION_TILT[rotation]) {
// tilted too far flat to go to this rotation
return;
}
- if (localLOGV) Log.i(TAG, " new rotation = " + rotation);
+ if (localLOGV) Log.i(TAG, "orientation " + orientation + " gives new rotation = "
+ + rotation);
mRotation = rotation;
mOrientationListener.onOrientationChanged(INTERNAL_TO_SURFACE_ROTATION[mRotation]);
}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index a8c00a6..298463a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -495,10 +495,10 @@
orientation will changed based on how the user rotates the device -->
<enum name="unspecified" value="-1" />
<!-- Would like to have the screen in a landscape orientation: that
- is, with the display wider than it is tall. -->
+ is, with the display wider than it is tall, ignoring sensor data. -->
<enum name="landscape" value="0" />
<!-- Would like to have the screen in a portrait orientation: that
- is, with the display taller than it is wide. -->
+ is, with the display taller than it is wide, ignoring sensor data. -->
<enum name="portrait" value="1" />
<!-- Use the user's current preferred orientation of the handset. -->
<enum name="user" value="2" />
@@ -511,6 +511,24 @@
<!-- Always ignore orientation determined by orientation sensor:
the display will not rotate when the user moves the device. -->
<enum name="nosensor" value="5" />
+ <!-- Would like to have the screen in landscape orientation, but can
+ use the sensor to change which direction the screen is facing. -->
+ <enum name="sensorLandscape" value="6" />
+ <!-- Would like to have the screen in portrait orientation, but can
+ use the sensor to change which direction the screen is facing. -->
+ <enum name="sensorPortait" value="7" />
+ <!-- Would like to have the screen in landscape orientation, turned in
+ the opposite direction from normal landscape. -->
+ <enum name="reverseLandscape" value="8" />
+ <!-- Would like to have the screen in portrait orientation, turned in
+ the opposite direction from normal portrait. -->
+ <enum name="reversePortait" value="9" />
+ <!-- Orientation is determined by a physical orientation sensor:
+ the display will rotate based on how the user moves the device.
+ This allows any of the 4 possible rotations, regardless of what
+ the device will normally do (for example some devices won't
+ normally use 180 degree rotation). -->
+ <enum name="fullSensor" value="10" />
</attr>
<!-- Specify one or more configuration changes that the activity will
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 538dae9..f21d357 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -288,7 +288,8 @@
int mLandscapeRotation = -1; // default landscape rotation
int mSeascapeRotation = -1; // "other" landscape rotation, 180 degrees from mLandscapeRotation
- int mPortraitRotation = -1;
+ int mPortraitRotation = -1; // default portrait rotation
+ int mUpsideDownRotation = -1; // "other" portrait rotation
// Nothing to see here, move along...
int mFancyRotationAnimation;
@@ -353,26 +354,25 @@
boolean useSensorForOrientationLp(int appOrientation) {
// The app says use the sensor.
- if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
+ if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
+ || appOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+ || appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
+ || appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
return true;
}
// The user preference says we can rotate, and the app is willing to rotate.
- // Note we include SCREEN_ORIENTATION_LANDSCAPE since we can use the sensor to choose
- // between the two possible landscape rotations.
if (mAccelerometerDefault != 0 &&
(appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
- || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
- || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) {
+ || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {
return true;
}
- // We're in a dock that has a rotation affinity, an the app is willing to rotate.
+ // We're in a dock that has a rotation affinity, and the app is willing to rotate.
if ((mCarDockEnablesAccelerometer && mDockMode == Intent.EXTRA_DOCK_STATE_CAR)
|| (mDeskDockEnablesAccelerometer && mDockMode == Intent.EXTRA_DOCK_STATE_DESK)) {
// Note we override the nosensor flag here.
if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
|| appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
- || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
- || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+ || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
return true;
}
}
@@ -386,7 +386,10 @@
* screen is switched off.
*/
boolean needSensorRunningLp() {
- if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
+ if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
+ || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+ || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
+ || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
// If the application has explicitly requested to follow the
// orientation, then we need to turn the sensor or.
return true;
@@ -2060,21 +2063,42 @@
if (d.getWidth() > d.getHeight()) {
mPortraitRotation = Surface.ROTATION_90;
mLandscapeRotation = Surface.ROTATION_0;
+ mUpsideDownRotation = Surface.ROTATION_270;
mSeascapeRotation = Surface.ROTATION_180;
} else {
mPortraitRotation = Surface.ROTATION_0;
mLandscapeRotation = Surface.ROTATION_90;
+ mUpsideDownRotation = Surface.ROTATION_180;
mSeascapeRotation = Surface.ROTATION_270;
}
}
synchronized (mLock) {
- if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
- //always return portrait if orientation set to portrait
- return mPortraitRotation;
- } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
- return getCurrentLandscapeRotation(lastRotation);
+ switch (orientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+ //always return portrait if orientation set to portrait
+ return mPortraitRotation;
+ case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+ //always return landscape if orientation set to landscape
+ return mLandscapeRotation;
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ //always return portrait if orientation set to portrait
+ return mUpsideDownRotation;
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ //always return seascape if orientation set to reverse landscape
+ return mSeascapeRotation;
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+ //return either landscape rotation based on the sensor
+ mOrientationListener.setAllow180Rotation(false);
+ return getCurrentLandscapeRotation(lastRotation);
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+ mOrientationListener.setAllow180Rotation(true);
+ return getCurrentPortraitRotation(lastRotation);
}
+
+ mOrientationListener.setAllow180Rotation(
+ orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
+
// case for nosensor meaning ignore sensor and consider only lid
// or orientation sensor disabled
//or case.unspecified
@@ -2094,18 +2118,15 @@
}
private int getCurrentLandscapeRotation(int lastRotation) {
- // landscape-only apps can take either landscape rotation
- if (useSensorForOrientationLp(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) {
- int sensorRotation = mOrientationListener.getCurrentRotation(lastRotation);
- if (isLandscapeOrSeascape(sensorRotation)) {
- return sensorRotation;
- }
+ int sensorRotation = mOrientationListener.getCurrentRotation(lastRotation);
+ if (isLandscapeOrSeascape(sensorRotation)) {
+ return sensorRotation;
}
// try to preserve the old rotation if it was landscape
if (isLandscapeOrSeascape(lastRotation)) {
return lastRotation;
}
- // default to one of the two landscape rotations
+ // default to one of the primary landscape rotation
return mLandscapeRotation;
}
@@ -2113,6 +2134,23 @@
return sensorRotation == mLandscapeRotation || sensorRotation == mSeascapeRotation;
}
+ private int getCurrentPortraitRotation(int lastRotation) {
+ int sensorRotation = mOrientationListener.getCurrentRotation(lastRotation);
+ if (isAnyPortrait(sensorRotation)) {
+ return sensorRotation;
+ }
+ // try to preserve the old rotation if it was portrait
+ if (isAnyPortrait(lastRotation)) {
+ return lastRotation;
+ }
+ // default to one of the primary portrait rotations
+ return mPortraitRotation;
+ }
+
+ private boolean isAnyPortrait(int sensorRotation) {
+ return sensorRotation == mPortraitRotation || sensorRotation == mUpsideDownRotation;
+ }
+
public boolean detectSafeMode() {
try {
int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 713524a..0de57ab 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -3066,11 +3066,8 @@
}
// If this application has requested an explicit orientation,
// then use it.
- if (or == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ||
- or == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ||
- or == ActivityInfo.SCREEN_ORIENTATION_SENSOR ||
- or == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR ||
- or == ActivityInfo.SCREEN_ORIENTATION_USER) {
+ if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
return or;
}
findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);