diff options
10 files changed, 203 insertions, 17 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index bbbc71fe8c1c..520e40aa1e56 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 4f53ed49002b..33525fdc52d2 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; /** * <p> @@ -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 7ea620039026..3b568e895e46 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -156,6 +156,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 efe4d6f48da1..fb090929c00c 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<DisplayContent.DisplayChildWindowCo } } + forAllWindows(w -> { + w.forceSeamlesslyRotateIfAllowed(oldRotation, rotation); + }, true /* traverseTopToBottom */); + + // TODO(b/111504081): Consolidate seamless rotation logic. if (rotateSeamlessly) { seamlesslyRotate(getPendingTransaction(), oldRotation, rotation); } @@ -3767,6 +3772,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } @Override + SurfaceControl.Builder makeChildSurface(WindowContainer child) { + final SurfaceControl.Builder builder = super.makeChildSurface(child); + if (child instanceof WindowToken && ((WindowToken) child).mRoundedCornerOverlay) { + // To draw above the ColorFade layer during the screen off transition, the + // rounded corner overlays need to be at the root of the surface hierarchy. + // TODO: move the ColorLayer into the display overlay layer such that this is not + // necessary anymore. + builder.setParent(null); + } + return builder; + } + + @Override void assignChildLayers(SurfaceControl.Transaction t) { assignChildLayers(t, null /* imeContainer */); } @@ -3782,6 +3800,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo wt.assignRelativeLayer(t, mTaskStackContainers.getSplitScreenDividerAnchor(), 1); continue; } + if (wt.mRoundedCornerOverlay) { + wt.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1); + continue; + } wt.assignLayer(t, j); wt.assignChildLayers(t); diff --git a/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java b/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java new file mode 100644 index 000000000000..546edaad6a62 --- /dev/null +++ b/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import android.graphics.Matrix; +import android.view.DisplayInfo; + +import com.android.server.wm.utils.CoordinateTransforms; + +/** + * Helper class for forced seamless rotation. + * + * Works by transforming the window token back into the old display rotation. + * + * Uses deferTransactionUntil instead of latching on the buffer size to allow for seamless 180 + * degree rotations. + * TODO(b/111504081): Consolidate seamless rotation logic. + */ +public class ForcedSeamlessRotator { + + private final Matrix mTransform = new Matrix(); + private final float[] mFloat9 = new float[9]; + + public ForcedSeamlessRotator(int oldRotation, int newRotation, DisplayInfo info) { + final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270; + final int h = flipped ? info.logicalWidth : info.logicalHeight; + final int w = flipped ? info.logicalHeight : info.logicalWidth; + + final Matrix tmp = new Matrix(); + CoordinateTransforms.transformLogicalToPhysicalCoordinates(oldRotation, w, h, mTransform); + CoordinateTransforms.transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp); + mTransform.postConcat(tmp); + } + + /** + * Applies a transform to the window token's surface that undoes the effect of the global + * display rotation. + */ + public void unrotate(WindowToken token) { + token.getPendingTransaction().setMatrix(token.getSurfaceControl(), mTransform, mFloat9); + } + + /** + * Removes the transform to the window token's surface that undoes the effect of the global + * display rotation. + * + * Removing the transform and the result of the WindowState's layout are both tied to the + * WindowState's next frame, such that they apply at the same time the client draws the + * window in the new orientation. + */ + public void finish(WindowToken token, WindowState win) { + mTransform.reset(); + token.getPendingTransaction().setMatrix(token.mSurfaceControl, mTransform, mFloat9); + token.getPendingTransaction().deferTransactionUntil(token.mSurfaceControl, + win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(), + win.getFrameNumber()); + win.getPendingTransaction().deferTransactionUntil(win.mSurfaceControl, + win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(), + win.getFrameNumber()); + } +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f315999356a8..3cdda7467e24 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1893,6 +1893,13 @@ public class WindowManagerService extends IWindowManager.Stub } win.setFrameNumber(frameNumber); + + // TODO(b/111504081): Consolidate seamless rotation logic. + if (win.mPendingForcedSeamlessRotate != null && !mWaitingForConfig) { + win.mPendingForcedSeamlessRotate.finish(win.mToken, win); + win.mPendingForcedSeamlessRotate = null; + } + int attrChanges = 0; int flagChanges = 0; if (attrs != null) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index fb0c3bcb1e8f..ff4a5830e7cf 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -282,6 +282,14 @@ class WindowState extends WindowContainer<WindowState> 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<IWindowFocusObserver> mFocusCallbacks; @@ -671,6 +679,14 @@ class WindowState extends WindowContainer<WindowState> 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); @@ -717,6 +733,7 @@ class WindowState extends WindowContainer<WindowState> 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); @@ -4710,7 +4727,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP transformFrameToSurfacePosition(mFrame.left, 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()) { @@ -4865,7 +4885,9 @@ class WindowState extends WindowContainer<WindowState> 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 14e0e13414a9..1673270a1509 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 b97460ae9eb8..e411c0adc75f 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<WindowState> { 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 361522cfc880..f82b01224f96 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() { |