From 6a7c90a12b5e5250e0350d35ca6547b26630653f Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Fri, 11 Mar 2016 15:04:59 +0100 Subject: [PATCH] Implement clip modes during animation Introduce modes how surfaces are clipped to the stack bounds during transitions. Use setFinalCrop to implement STACK_CLIP_AFTER_ANIM. Add logic to determine which stack clip mode to use. Bug: 26559810 Change-Id: I8edc47de3aaf1ef12055cefd8ceb8df536c5109a --- .../java/com/android/server/wm/AppTransition.java | 8 ++ .../com/android/server/wm/AppWindowAnimator.java | 11 +- .../java/com/android/server/wm/WindowAnimator.java | 9 +- .../android/server/wm/WindowManagerService.java | 7 +- .../com/android/server/wm/WindowStateAnimator.java | 123 ++++++++++++++------- .../android/server/wm/WindowSurfaceController.java | 25 +++++ 6 files changed, 139 insertions(+), 44 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 5cb709936bd1..bea300add980 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -43,6 +43,8 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; 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.WindowStateAnimator.STACK_CLIP_NONE; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import android.annotation.Nullable; import android.content.Context; @@ -1467,6 +1469,12 @@ public class AppTransition implements Dump { return a; } + int getAppStackClipMode() { + return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH + ? STACK_CLIP_NONE + : STACK_CLIP_AFTER_ANIM; + } + void postAnimationCallback() { if (mNextAppTransitionCallback != null) { mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java index 3a5dec939721..6225fc61afc2 100644 --- a/services/core/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java @@ -23,6 +23,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 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.TYPE_LAYER_OFFSET; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; import android.graphics.Matrix; import android.util.Slog; @@ -106,6 +107,7 @@ public class AppWindowAnimator { boolean usingTransferredAnimation = false; private boolean mSkipFirstFrame = false; + private int mStackClip = STACK_CLIP_BEFORE_ANIM; static final Animation sDummyAnimation = new DummyAnimation(); @@ -115,7 +117,8 @@ public class AppWindowAnimator { mAnimator = mService.mAnimator; } - public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame) { + public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame, + int stackClip) { if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken + ": " + anim + " wxh=" + width + "x" + height + " isVisible=" + mAppToken.isVisible()); @@ -142,6 +145,7 @@ public class AppWindowAnimator { transformation.clear(); transformation.setAlpha(mAppToken.isVisible() ? 1 : 0); hasTransformation = true; + mStackClip = stackClip; this.mSkipFirstFrame = skipFirstFrame; @@ -186,6 +190,7 @@ public class AppWindowAnimator { mAppToken.allDrawn = false; mAppToken.deferClearAllDrawn = false; } + mStackClip = STACK_CLIP_BEFORE_ANIM; } public boolean isAnimating() { @@ -201,6 +206,10 @@ public class AppWindowAnimator { deferThumbnailDestruction = false; } + int getStackClip() { + return mStackClip; + } + void transferCurrentAnimation( AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) { diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index f24376195eb8..eae783881c9f 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -36,6 +36,8 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 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.WindowStateAnimator.READY_TO_SHOW; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION; @@ -406,7 +408,8 @@ public class WindowAnimator { Animation a = mPolicy.createForceHideEnterAnimation(false, keyguardGoingAwayToShade); - winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime()); + winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime(), + STACK_CLIP_BEFORE_ANIM); winAnimator.mKeyguardGoingAwayAnimation = true; winAnimator.mKeyguardGoingAwayWithWallpaper = keyguardGoingAwayWithWallpaper; @@ -445,7 +448,7 @@ public class WindowAnimator { } final AppWindowToken atoken = win.mAppToken; - if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) { + if (winAnimator.mDrawState == READY_TO_SHOW) { if (atoken == null || atoken.allDrawn) { if (winAnimator.performShowLocked()) { setPendingLayoutChanges(displayId, @@ -487,7 +490,7 @@ public class WindowAnimator { if (a != null) { if (DEBUG_KEYGUARD) Slog.v(TAG, "Starting keyguard exit animation on window " + winAnimator.mWin); - winAnimator.setAnimation(a); + winAnimator.setAnimation(a, STACK_CLIP_BEFORE_ANIM); winAnimator.mKeyguardGoingAwayAnimation = true; winAnimator.mKeyguardGoingAwayWithWallpaper = keyguardGoingAwayWithWallpaper; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6da0f625d2e1..708ddff14107 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -236,6 +236,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS; 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.WindowStateAnimator.DRAW_PENDING; /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub @@ -3039,7 +3040,7 @@ public class WindowManagerService extends IWindowManager.Stub final int containingWidth = frame.width(); final int containingHeight = frame.height(); atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight, - mAppTransition.canSkipFirstFrame()); + mAppTransition.canSkipFirstFrame(), mAppTransition.getAppStackClipMode()); } } else { atoken.mAppAnimator.clearAnimation(); @@ -8973,7 +8974,7 @@ public class WindowManagerService extends IWindowManager.Stub + ", mDrawState=DRAW_PENDING in " + w + ", surfaceController " + winAnimator.mSurfaceController); } - winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING; + winAnimator.mDrawState = DRAW_PENDING; if (w.mAppToken != null) { w.mAppToken.allDrawn = false; w.mAppToken.deferClearAllDrawn = false; @@ -10787,7 +10788,7 @@ public class WindowManagerService extends IWindowManager.Stub final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs); if (win.isVisibleLw() && (win.mAppToken != null || isForceHiding)) { - win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING; + win.mWinAnimator.mDrawState = DRAW_PENDING; // Force add to mResizingWindows. win.mLastContentInsets.set(-1, -1, -1, -1); mWaitingForDrawn.add(win); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 22d3f984d323..83b61046b7c2 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -75,6 +75,25 @@ class WindowStateAnimator { static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM; static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200; + /** + * Mode how the window gets clipped by the stack bounds during an animation: The clipping should + * be applied after applying the animation transformation, i.e. the stack bounds don't move + * during the animation. + */ + static final int STACK_CLIP_AFTER_ANIM = 0; + + /** + * Mode how the window gets clipped by the stack bounds: The clipping should be applied before + * applying the animation transformation, i.e. the stack bounds move with the window. + */ + static final int STACK_CLIP_BEFORE_ANIM = 1; + + /** + * Mode how window gets clipped by the stack bounds during an animation: Don't clip the window + * by the stack bounds. + */ + static final int STACK_CLIP_NONE = 2; + // Unchanging local convenience fields. final WindowManagerService mService; final WindowState mWin; @@ -100,6 +119,7 @@ class WindowStateAnimator { int mLastLayer; long mAnimationStartTime; long mLastAnimationTime; + int mStackClip = STACK_CLIP_BEFORE_ANIM; /** * Set when we have changed the size of the surface, to know that @@ -128,7 +148,9 @@ class WindowStateAnimator { boolean mHasClipRect; Rect mClipRect = new Rect(); Rect mTmpClipRect = new Rect(); + Rect mTmpFinalClipRect = new Rect(); Rect mLastClipRect = new Rect(); + Rect mLastFinalClipRect = new Rect(); Rect mTmpStackBounds = new Rect(); /** @@ -226,7 +248,7 @@ class WindowStateAnimator { mWallpaperControllerLocked = mService.mWallpaperControllerLocked; } - public void setAnimation(Animation anim, long startTime) { + public void setAnimation(Animation anim, long startTime, int stackClip) { if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ": " + anim); mAnimating = false; mLocalAnimating = false; @@ -238,10 +260,15 @@ class WindowStateAnimator { mTransformation.setAlpha(mLastHidden ? 0 : 1); mHasLocalTransformation = true; mAnimationStartTime = startTime; + mStackClip = stackClip; + } + + public void setAnimation(Animation anim, int stackClip) { + setAnimation(anim, -1, stackClip); } public void setAnimation(Animation anim) { - setAnimation(anim, -1); + setAnimation(anim, -1, STACK_CLIP_AFTER_ANIM); } public void clearAnimation() { @@ -252,6 +279,7 @@ class WindowStateAnimator { mAnimation = null; mKeyguardGoingAwayAnimation = false; mKeyguardGoingAwayWithWallpaper = false; + mStackClip = STACK_CLIP_BEFORE_ANIM; } } @@ -397,6 +425,7 @@ class WindowStateAnimator { if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer); mHasTransformation = false; mHasLocalTransformation = false; + mStackClip = STACK_CLIP_BEFORE_ANIM; mWin.checkPolicyVisibilityChange(); mTransformation.clear(); if (mDrawState == HAS_DRAWN @@ -1130,11 +1159,13 @@ class WindowStateAnimator { } } - Rect calculateSurfaceWindowCrop() { + void calculateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect) { final WindowState w = mWin; final DisplayContent displayContent = w.getDisplayContent(); if (displayContent == null) { - return null; + clipRect.setEmpty(); + finalClipRect.setEmpty(); + return; } final DisplayInfo displayInfo = displayContent.getDisplayInfo(); if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Updating crop for window: " + w + ", " + "mLastCrop=" + @@ -1170,7 +1201,6 @@ class WindowStateAnimator { final boolean fullscreen = w.isFrameFullscreen(displayInfo); final boolean isFreeformResizing = w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM; - final Rect clipRect = mTmpClipRect; // We use the clip rect as provided by the tranformation for non-fullscreen windows to // avoid premature clipping with the system decor rect. @@ -1201,7 +1231,8 @@ class WindowStateAnimator { // so we need to translate to match the actual surface coordinates. clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top); - adjustCropToStackBounds(w, clipRect, isFreeformResizing); + finalClipRect.setEmpty(); + adjustCropToStackBounds(w, clipRect, finalClipRect, isFreeformResizing); if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Clip rect after stack adjustment=" + clipRect); w.transformFromScreenToSurfaceSpace(clipRect); @@ -1210,35 +1241,39 @@ class WindowStateAnimator { if (w.hasJustMovedInStack() && mLastClipRect.isEmpty() && !clipRect.isEmpty()) { clipRect.setEmpty(); } - - return clipRect; } - void updateSurfaceWindowCrop(Rect clipRect, boolean recoveringMemory) { + void updateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect, boolean recoveringMemory) { if (!clipRect.equals(mLastClipRect)) { mLastClipRect.set(clipRect); mSurfaceController.setCropInTransaction(clipRect, recoveringMemory); } + if (!finalClipRect.equals(mLastFinalClipRect)) { + mLastFinalClipRect.set(finalClipRect); + mSurfaceController.setFinalCropInTransaction(finalClipRect); + } } - private void adjustCropToStackBounds(WindowState w, Rect clipRect, boolean isFreeformResizing) { + private int resolveStackClip() { + + // App animation overrides window animation stack clip mode. + if (mAppAnimator != null && mAppAnimator.animation != null) { + return mAppAnimator.getStackClip(); + } else { + return mStackClip; + } + } + private void adjustCropToStackBounds(WindowState w, Rect clipRect, Rect finalClipRect, + boolean isFreeformResizing) { final Task task = w.getTask(); if (task == null || !task.cropWindowsToStackBounds()) { return; } - // We don't apply the stack bounds crop if: - // 1. The window is currently animating in freeform mode, otherwise the animating window - // will be suddenly (docked) or for whole animation (freeform) cut off. - // 2. The window that is being replaced during animation, because it was living in a - // different stack. If we suddenly crop it to the new stack bounds, it might get cut off. - // We don't want it to happen, so we let it ignore the stack bounds until it gets removed. - // The window that will replace it will abide them. - // TODO: identify animations where we don't want to apply docked stack crop to the docked - // task. For example, if the app is going from freeform to docked mode, we may not - // want to apply the crop during the animation, since it will make the app appear - // cropped prematurely. - if (isAnimating() && (w.mWillReplaceWindow || w.inFreeformWorkspace())) { + final int stackClip = resolveStackClip(); + + // It's animating and we don't want to clip it to stack bounds during animation - abort. + if (isAnimating() && stackClip == STACK_CLIP_NONE) { return; } @@ -1257,16 +1292,24 @@ class WindowStateAnimator { final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() : w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top; + // If we are animating, we either apply the clip before applying all the animation + // transformation or after all the transformation. + final boolean useFinalClipRect = isAnimating() && stackClip == STACK_CLIP_AFTER_ANIM; + // We need to do some acrobatics with surface position, because their clip region is // relative to the inside of the surface, but the stack bounds aren't. - clipRect.left = Math.max(0, - Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX); - clipRect.top = Math.max(0, - Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY); - clipRect.right = Math.max(0, - Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX); - clipRect.bottom = Math.max(0, - Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY); + if (useFinalClipRect) { + finalClipRect.set(mTmpStackBounds); + } else { + clipRect.left = Math.max(0, + Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX); + clipRect.top = Math.max(0, + Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY); + clipRect.right = Math.max(0, + Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX); + clipRect.bottom = Math.max(0, + Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY); + } } void setSurfaceBoundariesLocked(final boolean recoveringMemory) { @@ -1279,10 +1322,10 @@ class WindowStateAnimator { float extraHScale = (float) 1.0; float extraVScale = (float) 1.0; - final Rect crop = calculateSurfaceWindowCrop(); + calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect); if (task != null && task.mStack.getForceScaleToCrop()) { - extraHScale = crop.width() / (float)mTmpSize.width(); - extraVScale = crop.height() / (float)mTmpSize.height(); + extraHScale = mTmpClipRect.width() / (float)mTmpSize.width(); + extraVScale = mTmpClipRect.height() / (float)mTmpSize.height(); // In the case of ForceScaleToCrop we scale entire tasks together, // and so we need to scale our offsets relative to the task bounds @@ -1296,12 +1339,13 @@ class WindowStateAnimator { // Since we are scaled to fit in our previously desired crop, we can now // expose the whole window in buffer space, and not risk extending // past where the system would have cropped us - crop.set(0, 0, mTmpSize.width(), mTmpSize.height()); - updateSurfaceWindowCrop(crop, recoveringMemory); + mTmpClipRect.set(0, 0, mTmpSize.width(), mTmpSize.height()); + mTmpFinalClipRect.setEmpty(); + updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory); } else { mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, recoveringMemory); - updateSurfaceWindowCrop(crop, recoveringMemory); + updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory); } @@ -1458,7 +1502,8 @@ class WindowStateAnimator { SurfaceControl.openTransaction(); mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left, mWin.mFrame.top + top, false); - updateSurfaceWindowCrop(calculateSurfaceWindowCrop(), false); + calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect); + updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, false); } catch (RuntimeException e) { Slog.w(TAG, "Error positioning surface of " + mWin + " pos=(" + left + "," + top + ")", e); @@ -1721,6 +1766,7 @@ class WindowStateAnimator { pw.print(" mLocalAnimating="); pw.print(mLocalAnimating); pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance); pw.print(" mAnimation="); pw.println(mAnimation); + pw.print(" mStackClip="); pw.println(mStackClip); } if (mHasTransformation || mHasLocalTransformation) { pw.print(prefix); pw.print("XForm: has="); @@ -1740,6 +1786,9 @@ class WindowStateAnimator { if (mHasClipRect) { pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw); } + if (!mLastFinalClipRect.isEmpty()) { + pw.print(" mLastFinalClipRect="); mLastFinalClipRect.printShortString(pw); + } pw.println(); } diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index be3ad3bef631..8799c614cf25 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -189,6 +189,16 @@ class WindowSurfaceController { } } + void setFinalCropInTransaction(Rect clipRect) { + if (SHOW_TRANSACTIONS) logSurface( + "FINAL CROP " + clipRect.toShortString(), null); + try { + mSurfaceControl.setFinalCrop(clipRect); + } catch (RuntimeException e) { + Slog.w(TAG, "Error disconnecting surface in: " + this, e); + } + } + void setLayer(int layer) { if (mSurfaceControl != null) { SurfaceControl.openTransaction(); @@ -460,6 +470,7 @@ class WindowSurfaceController { private final PointF mPosition = new PointF(); private final Point mSize = new Point(); private final Rect mWindowCrop = new Rect(); + private final Rect mFinalCrop = new Rect(); private boolean mShown = false; private int mLayerStack; private boolean mIsOpaque; @@ -545,6 +556,19 @@ class WindowSurfaceController { } @Override + public void setFinalCrop(Rect crop) { + if (crop != null) { + if (!crop.equals(mFinalCrop)) { + if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setFinalCrop(" + + crop.toShortString() + "): OLD:" + this + ". Called by " + + Debug.getCallers(3)); + mFinalCrop.set(crop); + } + } + super.setFinalCrop(crop); + } + + @Override public void setLayerStack(int layerStack) { if (layerStack != mLayerStack) { if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayerStack(" + layerStack + "): OLD:" @@ -655,6 +679,7 @@ class WindowSurfaceController { pw.print(" mSize="); pw.print(s.mSize.x); pw.print("x"); pw.println(s.mSize.y); pw.print(" mCrop="); s.mWindowCrop.printShortString(pw); pw.println(); + pw.print(" mFinalCrop="); s.mFinalCrop.printShortString(pw); pw.println(); pw.print(" Transform: ("); pw.print(s.mDsdx); pw.print(", "); pw.print(s.mDtdx); pw.print(", "); pw.print(s.mDsdy); pw.print(", "); pw.print(s.mDtdy); pw.println(")"); -- 2.11.0