diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index bbbc71fe8c1c9..520e40aa1e56c 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -55,6 +55,7 @@ import android.view.View; import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.ViewTreeObserver; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -97,6 +98,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { private DisplayCutoutView mCutoutTop; private DisplayCutoutView mCutoutBottom; private SecureSetting mColorInversionSetting; + private boolean mPendingRotationChange; @Override public void start() { @@ -130,6 +132,21 @@ public class ScreenDecorations extends SystemUI implements Tunable { @Override public void onDisplayChanged(int displayId) { + if ((hasRoundedCorners() || shouldDrawCutout()) && + mRotation != RotationUtils.getExactRotation(mContext)) { + // We cannot immediately update the orientation. Otherwise + // WindowManager is still deferring layout until it has finished dispatching + // the config changes, which may cause divergence between what we draw + // (new orientation), and where we are placed on the screen (old orientation). + // Instead we wait until either: + // - we are trying to redraw. This because WM resized our window and told us to. + // - the config change has been dispatched, so WM is no longer deferring layout. + mPendingRotationChange = true; + mOverlay.getViewTreeObserver().addOnPreDrawListener( + new RestartingPreDrawListener(mOverlay)); + mBottomOverlay.getViewTreeObserver().addOnPreDrawListener( + new RestartingPreDrawListener(mBottomOverlay)); + } updateOrientation(); } }; @@ -144,12 +161,12 @@ public class ScreenDecorations extends SystemUI implements Tunable { mOverlay = LayoutInflater.from(mContext) .inflate(R.layout.rounded_corners, null); mCutoutTop = new DisplayCutoutView(mContext, true, - this::updateWindowVisibilities); + this::updateWindowVisibilities, this); ((ViewGroup)mOverlay).addView(mCutoutTop); mBottomOverlay = LayoutInflater.from(mContext) .inflate(R.layout.rounded_corners, null); mCutoutBottom = new DisplayCutoutView(mContext, false, - this::updateWindowVisibilities); + this::updateWindowVisibilities, this); ((ViewGroup)mBottomOverlay).addView(mCutoutBottom); mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); @@ -229,6 +246,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { @Override protected void onConfigurationChanged(Configuration newConfig) { + mPendingRotationChange = false; updateOrientation(); if (shouldDrawCutout() && mOverlay == null) { setupDecorations(); @@ -236,6 +254,9 @@ public class ScreenDecorations extends SystemUI implements Tunable { } protected void updateOrientation() { + if (mPendingRotationChange) { + return; + } int newRotation = RotationUtils.getExactRotation(mContext); if (newRotation != mRotation) { mRotation = newRotation; @@ -451,15 +472,17 @@ public class ScreenDecorations extends SystemUI implements Tunable { private final int[] mLocation = new int[2]; private final boolean mInitialStart; private final Runnable mVisibilityChangedListener; + private final ScreenDecorations mDecorations; private int mColor = Color.BLACK; private boolean mStart; private int mRotation; public DisplayCutoutView(Context context, boolean start, - Runnable visibilityChangedListener) { + Runnable visibilityChangedListener, ScreenDecorations decorations) { super(context); mInitialStart = start; mVisibilityChangedListener = visibilityChangedListener; + mDecorations = decorations; setId(R.id.display_cutout); } @@ -522,10 +545,10 @@ public class ScreenDecorations extends SystemUI implements Tunable { } private void update() { - mStart = isStart(); - if (!isAttachedToWindow()) { + if (!isAttachedToWindow() || mDecorations.mPendingRotationChange) { return; } + mStart = isStart(); requestLayout(); getDisplay().getDisplayInfo(mInfo); mBounds.setEmpty(); @@ -688,4 +711,28 @@ public class ScreenDecorations extends SystemUI implements Tunable { return rotation == RotationUtils.ROTATION_LANDSCAPE || rotation == RotationUtils.ROTATION_SEASCAPE; } + + /** + * A pre-draw listener, that cancels the draw and restarts the traversal with the updated + * window attributes. + */ + private class RestartingPreDrawListener implements ViewTreeObserver.OnPreDrawListener { + + private final View mView; + + private RestartingPreDrawListener(View view) { + mView = view; + } + + @Override + public boolean onPreDraw() { + mPendingRotationChange = false; + mView.getViewTreeObserver().removeOnPreDrawListener(this); + // This changes the window attributes - we need to restart the traversal for them to + // take effect. + updateOrientation(); + mView.invalidate(); + return false; + } + } } diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 4f53ed49002b8..33525fdc52d2a 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -46,6 +46,7 @@ import android.view.SurfaceSession; import libcore.io.Streams; import com.android.server.LocalServices; +import com.android.server.policy.WindowManagerPolicy; /** *

@@ -63,7 +64,7 @@ final class ColorFade { // The layer for the electron beam surface. // This is currently hardcoded to be one layer above the boot animation. - private static final int COLOR_FADE_LAYER = 0x40000001; + private static final int COLOR_FADE_LAYER = WindowManagerPolicy.COLOR_FADE_LAYER; // The number of frames to draw when preparing the animation so that it will // be ready to run smoothly. We use 3 frames because we are triple-buffered. diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index d63433db73102..a26ea22961750 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -157,6 +157,8 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004; /** Need to recompute animations */ int FINISH_LAYOUT_REDO_ANIM = 0x0008; + /** Layer for the screen off animation */ + int COLOR_FADE_LAYER = 0x40000001; /** * Register shortcuts for window manager to dispatch. diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 8a1c4a4e88e97..fa51f80392d99 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1136,6 +1136,11 @@ class DisplayContent extends WindowContainer { + w.forceSeamlesslyRotateIfAllowed(oldRotation, rotation); + }, true /* traverseTopToBottom */); + + // TODO(b/111504081): Consolidate seamless rotation logic. if (rotateSeamlessly) { seamlesslyRotate(getPendingTransaction(), oldRotation, rotation); } @@ -3766,6 +3771,19 @@ class DisplayContent extends WindowContainer implements WindowManagerP private boolean mDragResizing; private boolean mDragResizingChangeReported = true; private int mResizeMode; + /** + * Special mode that is intended only for the rounded corner overlay: during rotation + * transition, we un-rotate the window token such that the window appears as it did before the + * rotation. + * TODO(b/111504081): Consolidate seamless rotation logic. + */ + final boolean mForceSeamlesslyRotate; + ForcedSeamlessRotator mPendingForcedSeamlessRotate; private RemoteCallbackList mFocusCallbacks; @@ -628,6 +636,14 @@ class WindowState extends WindowContainer implements WindowManagerP private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; + void forceSeamlesslyRotateIfAllowed(int oldRotation, int rotation) { + if (mForceSeamlesslyRotate) { + mPendingForcedSeamlessRotate = new ForcedSeamlessRotator( + oldRotation, rotation, getDisplayInfo()); + mPendingForcedSeamlessRotate.unrotate(this.mToken); + } + } + interface PowerManagerWrapper { void wakeUp(long time, String reason); @@ -674,6 +690,7 @@ class WindowState extends WindowContainer implements WindowManagerP mSeq = seq; mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0; mPowerManagerWrapper = powerManagerWrapper; + mForceSeamlesslyRotate = token.mRoundedCornerOverlay; if (localLOGV) Slog.v( TAG, "Window " + this + " client=" + c.asBinder() + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a); @@ -4680,7 +4697,10 @@ class WindowState extends WindowContainer implements WindowManagerP transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top, mSurfacePosition); - if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) { + // Freeze position while we're unrotated, so the surface remains at the position it was + // prior to the rotation. + if (!mSurfaceAnimator.hasLeash() && mPendingForcedSeamlessRotate == null && + !mLastSurfacePosition.equals(mSurfacePosition)) { t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y); if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) { @@ -4835,7 +4855,9 @@ class WindowState extends WindowContainer implements WindowManagerP @Override void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) { - if (!isVisibleNow() || mIsWallpaper) { + // Invisible windows, the wallpaper, and force seamlessly rotated windows do not participate + // in the regular seamless rotation animation. + if (!isVisibleNow() || mIsWallpaper || mForceSeamlesslyRotate) { return; } final Matrix transform = mTmpMatrix; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 5ba1da80abfec..7966d5bd07f17 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -686,8 +686,12 @@ class WindowStateAnimator { final int displayId = mWin.getDisplayId(); final ScreenRotationAnimation screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(displayId); - final boolean screenAnimation = - screenRotationAnimation != null && screenRotationAnimation.isAnimating(); + // TODO(b/111504081): Consolidate seamless rotation logic. + final boolean windowParticipatesInScreenRotationAnimation = + !mWin.mForceSeamlesslyRotate; + final boolean screenAnimation = screenRotationAnimation != null + && screenRotationAnimation.isAnimating() + && windowParticipatesInScreenRotationAnimation; if (screenAnimation) { // cache often used attributes locally @@ -799,6 +803,13 @@ class WindowStateAnimator { return false; } + // During forced seamless rotation, the surface bounds get updated with the crop in the + // new rotation, which is not compatible with showing the surface in the old rotation. + // To work around that we disable cropping for such windows, as it is not necessary anyways. + if (w.mForceSeamlesslyRotate) { + return false; + } + // If we're animating, the wallpaper should only // be updated at the end of the animation. if (w.mAttrs.type == TYPE_WALLPAPER) { @@ -1498,6 +1509,8 @@ class WindowStateAnimator { } } + // TODO(b/111504081): Consolidate seamless rotation logic. + @Deprecated void seamlesslyRotate(SurfaceControl.Transaction t, int oldRotation, int newRotation) { final WindowState w = mWin; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index b97460ae9eb83..e411c0adc75fa 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -270,12 +270,6 @@ class WindowToken extends WindowContainer { dc.reParentWindowToken(this); mDisplayContent = dc; - // The rounded corner overlay should not be rotated. We ensure that by moving it outside - // the windowing layer. - if (mRoundedCornerOverlay) { - mDisplayContent.reparentToOverlay(mPendingTransaction, mSurfaceControl); - } - // TODO(b/36740756): One day this should perhaps be hooked // up with goodToGo, so we don't move a window // to another display before the window behind diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java index 361522cfc8801..f82b01224f969 100644 --- a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java @@ -99,7 +99,7 @@ public class CoordinateTransformsTest { checkPoint(0, W).transformsTo(0, 0); checkPoint(H, 0).transformsTo(W, H); -} + } @Test public void transformLogicalToPhysicalCoordinates_rot180() {