Centralize rotation control to DisplayRotation
- Move seamless rotation logic. The scattered conditions
(mayRotateSeamlessly) are also combined into shouldRotateSeamlessly.
- Move DisplayContent#updateRotationUnchecked and the related fields.
- Consolidate DisplayContent#updateOrientationFromAppTokens and
DisplayRotation#setCurrentOrientation to DR#updateOrientation.
Bug: 117593656
Test: go/wm-smoke
Test: atest DisplayContentTests DisplayPolicyTests DisplayRotationTests
Change-Id: Ifd978a20a2773a39000a90edf683e6459adf0d2d
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 410cc94..4d188f4 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -16,11 +16,21 @@
package com.android.server.wm;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
+import static com.android.server.wm.WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION;
+import android.annotation.AnimRes;
import android.annotation.IntDef;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -28,6 +38,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -41,6 +52,7 @@
import android.util.SparseArray;
import android.view.Surface;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.UiThread;
@@ -59,6 +71,13 @@
public class DisplayRotation {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
+ private static class RotationAnimationPair {
+ @AnimRes
+ int mEnter;
+ @AnimRes
+ int mExit;
+ }
+
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final DisplayPolicy mDisplayPolicy;
@@ -72,12 +91,30 @@
private final int mCarDockRotation;
private final int mDeskDockRotation;
private final int mUndockedHdmiRotation;
+ private final RotationAnimationPair mTmpRotationAnim = new RotationAnimationPair();
private OrientationListener mOrientationListener;
private StatusBarManagerInternal mStatusBarManagerInternal;
private SettingsObserver mSettingsObserver;
- private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ @ScreenOrientation
+ private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+ /**
+ * Last applied orientation of the display.
+ *
+ * @see #updateOrientationFromApp
+ */
+ @ScreenOrientation
+ private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+ /**
+ * Current rotation of the display.
+ *
+ * @see #updateRotationUnchecked
+ */
+ @Surface.Rotation
+ private int mRotation;
@VisibleForTesting
int mLandscapeRotation; // default landscape
@@ -88,11 +125,52 @@
@VisibleForTesting
int mUpsideDownRotation; // "other" portrait
- // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION)
+ private boolean mAllowSeamlessRotationDespiteNavBarMoving;
+
+ private int mDeferredRotationPauseCount;
+
+ /**
+ * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
+ * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
+ * so we need to track when this hits zero so we can apply deferred orientation updates.
+ */
+ private int mSeamlessRotationCount;
+
+ /**
+ * True in the interval from starting seamless rotation until the last rotated window draws in
+ * the new orientation.
+ */
+ private boolean mRotatingSeamlessly;
+
+ /**
+ * Behavior of rotation suggestions.
+ *
+ * @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
+ */
private int mShowRotationSuggestions;
- private int mAllowAllRotations = -1;
+ private static final int ALLOW_ALL_ROTATIONS_UNDEFINED = -1;
+ private static final int ALLOW_ALL_ROTATIONS_DISABLED = 0;
+ private static final int ALLOW_ALL_ROTATIONS_ENABLED = 1;
+
+ @IntDef({ ALLOW_ALL_ROTATIONS_UNDEFINED, ALLOW_ALL_ROTATIONS_DISABLED,
+ ALLOW_ALL_ROTATIONS_ENABLED })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface AllowAllRotations {}
+
+ /**
+ * Whether to allow the screen to rotate to all rotations (including 180 degree) according to
+ * the sensor even when the current orientation is not
+ * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_SENSOR} or
+ * {@link ActivityInfo#SCREEN_ORIENTATION_FULL_USER}.
+ */
+ @AllowAllRotations
+ private int mAllowAllRotations = ALLOW_ALL_ROTATIONS_UNDEFINED;
+
+ @WindowManagerPolicy.UserRotationMode
private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
+
+ @Surface.Rotation
private int mUserRotation = Surface.ROTATION_0;
/**
@@ -125,6 +203,7 @@
* regardless of all other states (including app requrested orientation). {@code true} the
* display rotation should be fixed to user specified rotation, {@code false} otherwise.
*/
+ @FixedToUserRotation
private int mFixedToUserRotation = FIXED_TO_USER_ROTATION_DEFAULT;
private int mDemoHdmiRotation;
@@ -149,21 +228,17 @@
mLock = lock;
isDefaultDisplay = displayContent.isDefaultDisplay;
- mSupportAutoRotation = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_supportAutoRotation);
- mLidOpenRotation = readRotation(
- com.android.internal.R.integer.config_lidOpenRotation);
- mCarDockRotation = readRotation(
- com.android.internal.R.integer.config_carDockRotation);
- mDeskDockRotation = readRotation(
- com.android.internal.R.integer.config_deskDockRotation);
- mUndockedHdmiRotation = readRotation(
- com.android.internal.R.integer.config_undockedHdmiRotation);
+ mSupportAutoRotation =
+ mContext.getResources().getBoolean(R.bool.config_supportAutoRotation);
+ mLidOpenRotation = readRotation(R.integer.config_lidOpenRotation);
+ mCarDockRotation = readRotation(R.integer.config_carDockRotation);
+ mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
+ mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation);
if (isDefaultDisplay) {
final Handler uiHandler = UiThread.getHandler();
mOrientationListener = new OrientationListener(mContext, uiHandler);
- mOrientationListener.setCurrentRotation(displayContent.getRotation());
+ mOrientationListener.setCurrentRotation(mRotation);
mSettingsObserver = new SettingsObserver(uiHandler);
mSettingsObserver.observe();
}
@@ -188,12 +263,21 @@
return -1;
}
+ /**
+ * Updates the configuration which may have different values depending on current user, e.g.
+ * runtime resource overlay.
+ */
+ void updateUserDependentConfiguration(Resources currentUserRes) {
+ mAllowSeamlessRotationDespiteNavBarMoving =
+ currentUserRes.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
+ }
+
void configure(int width, int height, int shortSizeDp, int longSizeDp) {
final Resources res = mContext.getResources();
if (width > height) {
mLandscapeRotation = Surface.ROTATION_0;
mSeascapeRotation = Surface.ROTATION_180;
- if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
+ if (res.getBoolean(R.bool.config_reverseDefaultRotation)) {
mPortraitRotation = Surface.ROTATION_90;
mUpsideDownRotation = Surface.ROTATION_270;
} else {
@@ -203,7 +287,7 @@
} else {
mPortraitRotation = Surface.ROTATION_0;
mUpsideDownRotation = Surface.ROTATION_180;
- if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
+ if (res.getBoolean(R.bool.config_reverseDefaultRotation)) {
mLandscapeRotation = Surface.ROTATION_270;
mSeascapeRotation = Surface.ROTATION_90;
} else {
@@ -246,19 +330,325 @@
&& !"true".equals(SystemProperties.get("config.override_forced_orient"));
}
- void setRotation(int rotation) {
+ void applyCurrentRotation(@Surface.Rotation int rotation) {
if (mOrientationListener != null) {
mOrientationListener.setCurrentRotation(rotation);
}
}
- void setCurrentOrientation(int newOrientation) {
+ @VisibleForTesting
+ void setRotation(@Surface.Rotation int rotation) {
+ mRotation = rotation;
+ }
+
+ @Surface.Rotation
+ int getRotation() {
+ return mRotation;
+ }
+
+ @ScreenOrientation
+ int getLastOrientation() {
+ return mLastOrientation;
+ }
+
+ boolean updateOrientation(@ScreenOrientation int newOrientation, boolean forceUpdate) {
+ if (newOrientation == mLastOrientation && !forceUpdate) {
+ return false;
+ }
+ mLastOrientation = newOrientation;
if (newOrientation != mCurrentAppOrientation) {
mCurrentAppOrientation = newOrientation;
if (isDefaultDisplay) {
updateOrientationListenerLw();
}
}
+ return updateRotationUnchecked(forceUpdate);
+ }
+
+ /**
+ * Update rotation of the display and send configuration if the rotation is changed.
+ *
+ * @return {@code true} if the rotation has been changed and the new config is sent.
+ */
+ boolean updateRotationAndSendNewConfigIfChanged() {
+ final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
+ if (changed) {
+ mDisplayContent.sendNewConfiguration();
+ }
+ return changed;
+ }
+
+ /**
+ * Update rotation with an option to force the update. This updates the container's perception
+ * of rotation and, depending on the top activities, will freeze the screen or start seamless
+ * rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked}
+ * during {@link DisplayContent#sendNewConfiguration}.
+ *
+ * @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating
+ * orientation because we're waiting for some rotation to finish or display
+ * to unfreeze, which results in configuration of the previously visible
+ * activity being applied to a newly visible one. Forcing the rotation
+ * update allows to workaround this issue.
+ * @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
+ * {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE
+ * THE SCREEN.
+ */
+ boolean updateRotationUnchecked(boolean forceUpdate) {
+ final int displayId = mDisplayContent.getDisplayId();
+ if (!forceUpdate) {
+ if (mDeferredRotationPauseCount > 0) {
+ // Rotation updates have been paused temporarily. Defer the update until updates
+ // have been resumed.
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
+ return false;
+ }
+
+ final ScreenRotationAnimation screenRotationAnimation =
+ mService.mAnimator.getScreenRotationAnimationLocked(displayId);
+ if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
+ // Rotation updates cannot be performed while the previous rotation change animation
+ // is still in progress. Skip this update. We will try updating again after the
+ // animation is finished and the display is unfrozen.
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
+ return false;
+ }
+ if (mService.mDisplayFrozen) {
+ // Even if the screen rotation animation has finished (e.g. isAnimating returns
+ // false), there is still some time where we haven't yet unfrozen the display. We
+ // also need to abort rotation here.
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+ "Deferring rotation, still finishing previous rotation");
+ return false;
+ }
+ }
+
+ if (!mService.mDisplayEnabled) {
+ // No point choosing a rotation if the display is not enabled.
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled.");
+ return false;
+ }
+
+ final int oldRotation = mRotation;
+ final int lastOrientation = mLastOrientation;
+ final int rotation = rotationForOrientation(lastOrientation, oldRotation);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
+ + displayId + " based on lastOrientation=" + lastOrientation
+ + " and oldRotation=" + oldRotation);
+
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + displayId
+ + " selected orientation " + lastOrientation
+ + ", got rotation " + rotation);
+
+ if (oldRotation == rotation) {
+ // No change.
+ return false;
+ }
+
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + displayId
+ + " rotation changed to " + rotation
+ + " from " + oldRotation
+ + ", lastOrientation=" + lastOrientation);
+
+ if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
+ mDisplayContent.mWaitingForConfig = true;
+ }
+
+ mRotation = rotation;
+
+ mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
+ mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
+ mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
+
+ mDisplayContent.setLayoutNeeded();
+
+ if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
+ // The screen rotation animation uses a screenshot to freeze the screen while windows
+ // resize underneath. When we are rotating seamlessly, we allow the elements to
+ // transition to their rotated state independently and without a freeze required.
+ prepareSeamlessRotation();
+ } else {
+ prepareNormalRotationAnimation();
+ }
+
+ return true;
+ }
+
+ void prepareNormalRotationAnimation() {
+ final RotationAnimationPair anim = selectRotationAnimation();
+ mService.startFreezingDisplayLocked(anim.mExit, anim.mEnter, mDisplayContent);
+ }
+
+ private void prepareSeamlessRotation() {
+ // We are careful to reset this in case a window was removed before it finished
+ // seamless rotation.
+ mSeamlessRotationCount = 0;
+ mRotatingSeamlessly = true;
+ }
+
+ boolean isRotatingSeamlessly() {
+ return mRotatingSeamlessly;
+ }
+
+ @VisibleForTesting
+ boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
+ final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
+ if (w == null || w != mDisplayContent.mCurrentFocus) {
+ return false;
+ }
+ // We only enable seamless rotation if the top window has requested it and is in the
+ // fullscreen opaque state. Seamless rotation requires freezing various Surface states and
+ // won't work well with animations, so we disable it in the animation case for now.
+ if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) {
+ return false;
+ }
+
+ // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
+ // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
+ // will not enter the reverse portrait orientation, so actually the orientation won't change
+ // at all.
+ if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
+ return false;
+ }
+
+ // If the navigation bar can't change sides, then it will jump when we change orientations
+ // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation
+ // where the navbar is low-profile enough that this isn't very noticeable.
+ if (!mAllowSeamlessRotationDespiteNavBarMoving && !mDisplayPolicy.navigationBarCanMove()) {
+ return false;
+ }
+
+ // If the bounds of activity window is different from its parent, then reject to be seamless
+ // because the window position may change after rotation that will look like a sudden jump.
+ if (w.mAppToken != null && !w.mAppToken.matchParentBounds()) {
+ return false;
+ }
+
+ // In the presence of the PINNED stack or System Alert windows we unfortunately can not
+ // seamlessly rotate.
+ if (mDisplayContent.hasPinnedStack() || mDisplayContent.hasAlertWindowSurfaces()) {
+ return false;
+ }
+
+ // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
+ // complete (that is, waiting for windows to redraw). It's tempting to check
+ // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
+ if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
+ if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
+ return;
+ }
+
+ w.mSeamlesslyRotated = seamlesslyRotated;
+ if (seamlesslyRotated) {
+ mSeamlessRotationCount++;
+ } else {
+ mSeamlessRotationCount--;
+ }
+ if (mSeamlessRotationCount == 0) {
+ if (DEBUG_ORIENTATION) {
+ Slog.i(TAG, "Performing post-rotate rotation after seamless rotation");
+ }
+ // Finish seamless rotation.
+ mRotatingSeamlessly = false;
+
+ updateRotationAndSendNewConfigIfChanged();
+ }
+ }
+
+ void onSeamlessRotationTimeout() {
+ final boolean[] isLayoutNeeded = { false };
+
+ mDisplayContent.forAllWindows(w -> {
+ if (!w.mSeamlesslyRotated) {
+ return;
+ }
+ isLayoutNeeded[0] = true;
+ w.setDisplayLayoutNeeded();
+ w.finishSeamlessRotation(true /* timeout */);
+ markForSeamlessRotation(w, false /* seamlesslyRotated */);
+ }, true /* traverseTopToBottom */);
+
+ if (isLayoutNeeded[0]) {
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+ }
+
+ /**
+ * Returns the animation to run for a rotation transition based on the top fullscreen windows
+ * {@link android.view.WindowManager.LayoutParams#rotationAnimation} and whether it is currently
+ * fullscreen and frontmost.
+ */
+ private RotationAnimationPair selectRotationAnimation() {
+ // If the screen is off or non-interactive, force a jumpcut.
+ final boolean forceJumpcut = !mDisplayPolicy.isScreenOnFully()
+ || !mService.mPolicy.okToAnimate();
+ final WindowState topFullscreen = mDisplayPolicy.getTopFullscreenOpaqueWindow();
+ if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation topFullscreen="
+ + topFullscreen + " rotationAnimation="
+ + (topFullscreen == null ? 0 : topFullscreen.getAttrs().rotationAnimation)
+ + " forceJumpcut=" + forceJumpcut);
+ if (forceJumpcut) {
+ mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
+ mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
+ return mTmpRotationAnim;
+ }
+ if (topFullscreen != null) {
+ int animationHint = topFullscreen.getRotationAnimationHint();
+ if (animationHint < 0 && mDisplayPolicy.isTopLayoutFullscreen()) {
+ animationHint = topFullscreen.getAttrs().rotationAnimation;
+ }
+ switch (animationHint) {
+ case ROTATION_ANIMATION_CROSSFADE:
+ case ROTATION_ANIMATION_SEAMLESS: // Crossfade is fallback for seamless.
+ mTmpRotationAnim.mExit = R.anim.rotation_animation_xfade_exit;
+ mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
+ break;
+ case ROTATION_ANIMATION_JUMPCUT:
+ mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
+ mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
+ break;
+ case ROTATION_ANIMATION_ROTATE:
+ default:
+ mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0;
+ break;
+ }
+ } else {
+ mTmpRotationAnim.mExit = mTmpRotationAnim.mEnter = 0;
+ }
+ return mTmpRotationAnim;
+ }
+
+ /**
+ * Validate whether the current top fullscreen has specified the same
+ * {@link android.view.WindowManager.LayoutParams#rotationAnimation} value as that being passed
+ * in from the previous top fullscreen window.
+ *
+ * @param exitAnimId exiting resource id from the previous window.
+ * @param enterAnimId entering resource id from the previous window.
+ * @param forceDefault For rotation animations only, if true ignore the animation values and
+ * just return false.
+ * @return {@code true} if the previous values are still valid, false if they should be replaced
+ * with the default.
+ */
+ boolean validateRotationAnimation(int exitAnimId, int enterAnimId, boolean forceDefault) {
+ switch (exitAnimId) {
+ case R.anim.rotation_animation_xfade_exit:
+ case R.anim.rotation_animation_jump_exit:
+ // These are the only cases that matter.
+ if (forceDefault) {
+ return false;
+ }
+ final RotationAnimationPair anim = selectRotationAnimation();
+ return exitAnimId == anim.mExit && enterAnimId == anim.mEnter;
+ default:
+ return true;
+ }
}
void restoreSettings(int userRotationMode, int userRotation,
@@ -327,7 +717,7 @@
}
void freezeRotation(int rotation) {
- rotation = (rotation == -1) ? mDisplayContent.getRotation() : rotation;
+ rotation = (rotation == -1) ? mRotation : rotation;
setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
}
@@ -407,6 +797,30 @@
}
/**
+ * Temporarily pauses rotation changes until resumed.
+ * <p>
+ * This can be used to prevent rotation changes from occurring while the user is performing
+ * certain operations, such as drag and drop.
+ * <p>
+ * This call nests and must be matched by an equal number of calls to {@link #resume}.
+ */
+ void pause() {
+ mDeferredRotationPauseCount++;
+ }
+
+ /** Resumes normal rotation changes after being paused. */
+ void resume() {
+ if (mDeferredRotationPauseCount <= 0) {
+ return;
+ }
+
+ mDeferredRotationPauseCount--;
+ if (mDeferredRotationPauseCount == 0) {
+ updateRotationAndSendNewConfigIfChanged();
+ }
+ }
+
+ /**
* Various use cases for invoking this function:
* <li>Screen turning off, should always disable listeners if already enabled.</li>
* <li>Screen turned on and current app has sensor based orientation, enable listeners
@@ -515,14 +929,28 @@
}
/**
- * Given an orientation constant, returns the appropriate surface rotation,
- * taking into account sensors, docking mode, rotation lock, and other factors.
+ * If this is true we have updated our desired orientation, but not yet changed the real
+ * orientation our applied our screen rotation animation. For example, because a previous
+ * screen rotation was in progress.
+ *
+ * @return {@code true} if the there is an ongoing rotation change.
+ */
+ boolean needsUpdate() {
+ final int oldRotation = mRotation;
+ final int rotation = rotationForOrientation(mLastOrientation, oldRotation);
+ return oldRotation != rotation;
+ }
+
+ /**
+ * Given an orientation constant, returns the appropriate surface rotation, taking into account
+ * sensors, docking mode, rotation lock, and other factors.
*
* @param orientation An orientation constant, such as
- * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
+ * {@link ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
* @param lastRotation The most recently used rotation.
* @return The surface rotation to use.
*/
+ @VisibleForTesting
int rotationForOrientation(int orientation, int lastRotation) {
if (DEBUG_ORIENTATION) {
Slog.v(TAG, "rotationForOrientation(orient="
@@ -614,15 +1042,16 @@
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
// Otherwise, use sensor only if requested by the application or enabled
// by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
- if (mAllowAllRotations < 0) {
- // Can't read this during init() because the context doesn't
- // have display metrics at that time so we cannot determine
- // tablet vs. phone then.
+ if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) {
+ // Can't read this during init() because the context doesn't have display metrics at
+ // that time so we cannot determine tablet vs. phone then.
mAllowAllRotations = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
+ R.bool.config_allowAllRotations)
+ ? ALLOW_ALL_ROTATIONS_ENABLED
+ : ALLOW_ALL_ROTATIONS_DISABLED;
}
if (sensorRotation != Surface.ROTATION_180
- || mAllowAllRotations == 1
+ || mAllowAllRotations == ALLOW_ALL_ROTATIONS_ENABLED
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
preferredRotation = sensorRotation;
@@ -883,6 +1312,10 @@
pw.println(prefix + "DisplayRotation");
pw.println(prefix + " mCurrentAppOrientation="
+ ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
+ pw.println(prefix + " mLastOrientation=" + mLastOrientation);
+ pw.print(prefix + " mRotation=" + mRotation);
+ pw.println(" mDeferredRotationPauseCount=" + mDeferredRotationPauseCount);
+
pw.print(prefix + " mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));