From f5f9e12146a3857c28b8c0fc96c2fa68a2ae40de Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Tue, 24 Oct 2017 18:21:09 +0200 Subject: [PATCH] Lock free app animations (2/n): Use SurfaceAninimator This is the main CL that switches over from using the legacy animation system to using the new SurfaceAnimator for app window animations. Also moves applyAnimationLocked to AppWindowToken. AppWindowAnimator still has a bunch of state that needs to be moved into AppWindowToken in future CL's. Test: go/wm-smoke Bug: 64674361 Change-Id: Ifc83cbac281ce0654ecd8a1c2ca9c24a4d87c1d1 --- .../server/policy/PhoneWindowManager.java | 18 +- .../server/policy/StatusBarController.java | 80 +--- .../android/server/wm/AnimationAdapter.java | 11 + .../com/android/server/wm/AppTransition.java | 32 +- .../android/server/wm/AppWindowAnimator.java | 423 +----------------- .../wm/AppWindowContainerController.java | 30 +- .../com/android/server/wm/AppWindowToken.java | 307 ++++++++++--- .../com/android/server/wm/DisplayContent.java | 32 +- .../server/wm/LocalAnimationAdapter.java | 20 +- .../server/wm/RootWindowContainer.java | 4 +- .../core/java/com/android/server/wm/Task.java | 10 +- .../java/com/android/server/wm/TaskStack.java | 36 +- .../server/wm/WallpaperController.java | 21 +- .../server/wm/WallpaperWindowToken.java | 14 +- .../server/wm/WindowAnimationSpec.java | 63 +++ .../com/android/server/wm/WindowAnimator.java | 6 +- .../android/server/wm/WindowContainer.java | 23 +- .../server/wm/WindowManagerInternal.java | 9 +- .../server/wm/WindowManagerService.java | 114 +---- .../com/android/server/wm/WindowState.java | 62 +-- .../server/wm/WindowStateAnimator.java | 19 +- .../server/wm/WindowSurfacePlacer.java | 34 +- .../com/android/server/wm/WindowToken.java | 21 +- .../server/wm/AppWindowTokenTests.java | 2 +- 24 files changed, 488 insertions(+), 903 deletions(-) diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 9752a57e24238..076c0e4d2d4a6 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2126,14 +2126,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() { @Override public int onAppTransitionStartingLocked(int transit, IBinder openToken, - IBinder closeToken, - Animation openAnimation, Animation closeAnimation) { - return handleStartTransitionForKeyguardLw(transit, openAnimation); + IBinder closeToken, long duration, long statusBarAnimationStartTime, + long statusBarAnimationDuration) { + return handleStartTransitionForKeyguardLw(transit, duration); } @Override public void onAppTransitionCancelledLocked(int transit) { - handleStartTransitionForKeyguardLw(transit, null /* transit */); + handleStartTransitionForKeyguardLw(transit, 0 /* duration */); } }); mKeyguardDelegate = new KeyguardServiceDelegate(mContext, @@ -3989,7 +3989,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private int handleStartTransitionForKeyguardLw(int transit, @Nullable Animation anim) { + private int handleStartTransitionForKeyguardLw(int transit, long duration) { if (mKeyguardOccludedChanged) { if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded=" + mPendingKeyguardOccluded); @@ -4000,13 +4000,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (AppTransition.isKeyguardGoingAwayTransit(transit)) { if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation"); - final long startTime = anim != null - ? SystemClock.uptimeMillis() + anim.getStartOffset() - : SystemClock.uptimeMillis(); - final long duration = anim != null - ? anim.getDuration() - : 0; - startKeyguardExitAnimation(startTime, duration); + startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration); } return 0; } diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java index af7e91c5bfa60..e6e4d7f98250b 100644 --- a/services/core/java/com/android/server/policy/StatusBarController.java +++ b/services/core/java/com/android/server/policy/StatusBarController.java @@ -37,8 +37,6 @@ import com.android.server.statusbar.StatusBarManagerInternal; */ public class StatusBarController extends BarController { - private static final long TRANSITION_DURATION = 120L; - private final AppTransitionListener mAppTransitionListener = new AppTransitionListener() { @@ -57,17 +55,15 @@ public class StatusBarController extends BarController { @Override public int onAppTransitionStartingLocked(int transit, IBinder openToken, - IBinder closeToken, final Animation openAnimation, final Animation closeAnimation) { + IBinder closeToken, long duration, long statusBarAnimationStartTime, + long statusBarAnimationDuration) { mHandler.post(new Runnable() { @Override public void run() { StatusBarManagerInternal statusbar = getStatusBarInternal(); if (statusbar != null) { - long startTime = calculateStatusBarTransitionStartTime(openAnimation, - closeAnimation); - long duration = closeAnimation != null || openAnimation != null - ? TRANSITION_DURATION : 0; - statusbar.appTransitionStarting(startTime, duration); + statusbar.appTransitionStarting(statusBarAnimationStartTime, + statusBarAnimationDuration); } } }); @@ -128,72 +124,4 @@ public class StatusBarController extends BarController { public AppTransitionListener getAppTransitionListener() { return mAppTransitionListener; } - - /** - * For a given app transition with {@code openAnimation} and {@code closeAnimation}, this - * calculates the timings for the corresponding status bar transition. - * - * @return the desired start time of the status bar transition, in uptime millis - */ - private static long calculateStatusBarTransitionStartTime(Animation openAnimation, - Animation closeAnimation) { - if (openAnimation != null && closeAnimation != null) { - TranslateAnimation openTranslateAnimation = findTranslateAnimation(openAnimation); - TranslateAnimation closeTranslateAnimation = findTranslateAnimation(closeAnimation); - if (openTranslateAnimation != null) { - - // Some interpolators are extremely quickly mostly finished, but not completely. For - // our purposes, we need to find the fraction for which ther interpolator is mostly - // there, and use that value for the calculation. - float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator()); - return SystemClock.uptimeMillis() - + openTranslateAnimation.getStartOffset() - + (long)(openTranslateAnimation.getDuration()*t) - TRANSITION_DURATION; - } else if (closeTranslateAnimation != null) { - return SystemClock.uptimeMillis(); - } else { - return SystemClock.uptimeMillis(); - } - } else { - return SystemClock.uptimeMillis(); - } - } - - /** - * Tries to find a {@link TranslateAnimation} inside the {@code animation}. - * - * @return the found animation, {@code null} otherwise - */ - private static TranslateAnimation findTranslateAnimation(Animation animation) { - if (animation instanceof TranslateAnimation) { - return (TranslateAnimation) animation; - } else if (animation instanceof AnimationSet) { - AnimationSet set = (AnimationSet) animation; - for (int i = 0; i < set.getAnimations().size(); i++) { - Animation a = set.getAnimations().get(i); - if (a instanceof TranslateAnimation) { - return (TranslateAnimation) a; - } - } - } - return null; - } - - /** - * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which - * {@code interpolator(t + eps) > 0.99}. - */ - private static float findAlmostThereFraction(Interpolator interpolator) { - float val = 0.5f; - float adj = 0.25f; - while (adj >= 0.01f) { - if (interpolator.getInterpolation(val) < 0.99f) { - val += adj; - } else { - val -= adj; - } - adj /= 2; - } - return val; - } } diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java index 84d47b4d66b5d..ed4543ef82feb 100644 --- a/services/core/java/com/android/server/wm/AnimationAdapter.java +++ b/services/core/java/com/android/server/wm/AnimationAdapter.java @@ -30,6 +30,8 @@ import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; */ interface AnimationAdapter { + long STATUS_BAR_TRANSITION_DURATION = 120L; + /** * @return Whether we should detach the wallpaper during the animation. * @see Animation#setDetachWallpaper @@ -66,4 +68,13 @@ interface AnimationAdapter { * @return The approximate duration of the animation, in milliseconds. */ long getDurationHint(); + + /** + * If this animation is run as an app opening animation, this calculates the start time for all + * status bar transitions that happen as part of the app opening animation, which will be + * forwarded to SystemUI. + * + * @return the desired start time of the status bar transition, in uptime millis + */ + long getStatusBarTransitionsStartTime(); } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 4a74e295c8fcd..2ac758344f52e 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -38,7 +38,6 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpe import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation; import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation; import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation; -import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START; 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; @@ -63,6 +62,7 @@ import android.os.Debug; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.RemoteException; +import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.util.ArraySet; @@ -415,17 +415,23 @@ public class AppTransition implements Dump { * @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another * layout pass needs to be done */ - int goodToGo(int transit, AppWindowAnimator topOpeningAppAnimator, - AppWindowAnimator topClosingAppAnimator, ArraySet openingApps, + int goodToGo(int transit, AppWindowToken topOpeningApp, + AppWindowToken topClosingApp, ArraySet openingApps, ArraySet closingApps) { mNextAppTransition = TRANSIT_UNSET; mNextAppTransitionFlags = 0; setAppTransitionState(APP_STATE_RUNNING); + final AnimationAdapter topOpeningAnim = topOpeningApp != null + ? topOpeningApp.getAnimation() + : null; int redoLayout = notifyAppTransitionStartingLocked(transit, - topOpeningAppAnimator != null ? topOpeningAppAnimator.mAppToken.token : null, - topClosingAppAnimator != null ? topClosingAppAnimator.mAppToken.token : null, - topOpeningAppAnimator != null ? topOpeningAppAnimator.animation : null, - topClosingAppAnimator != null ? topClosingAppAnimator.animation : null); + topOpeningApp != null ? topOpeningApp.token : null, + topClosingApp != null ? topClosingApp.token : null, + topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0, + topOpeningAnim != null + ? topOpeningAnim.getStatusBarTransitionsStartTime() + : SystemClock.uptimeMillis(), + AnimationAdapter.STATUS_BAR_TRANSITION_DURATION); mService.getDefaultDisplayContentLocked().getDockedDividerController() .notifyAppTransitionStarting(openingApps, transit); @@ -433,8 +439,8 @@ public class AppTransition implements Dump { // ended it already then we don't need to wait. if (transit == TRANSIT_DOCK_TASK_FROM_RECENTS && !mProlongedAnimationsEnded) { for (int i = openingApps.size() - 1; i >= 0; i--) { - final AppWindowAnimator appAnimator = openingApps.valueAt(i).mAppAnimator; - appAnimator.startProlongAnimation(PROLONG_ANIMATION_AT_START); + final AppWindowToken app = openingApps.valueAt(i); + app.startDelayingAnimationStart(); } } return redoLayout; @@ -504,11 +510,12 @@ public class AppTransition implements Dump { } private int notifyAppTransitionStartingLocked(int transit, IBinder openToken, - IBinder closeToken, Animation openAnimation, Animation closeAnimation) { + IBinder closeToken, long duration, long statusBarAnimationStartTime, + long statusBarAnimationDuration) { int redoLayout = 0; for (int i = 0; i < mListeners.size(); i++) { redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(transit, openToken, - closeToken, openAnimation, closeAnimation); + closeToken, duration, statusBarAnimationStartTime, statusBarAnimationDuration); } return redoLayout; } @@ -1885,9 +1892,6 @@ public class AppTransition implements Dump { mNextAppTransitionFutureCallback, null /* finishedCallback */, mNextAppTransitionScaleUp); mNextAppTransitionFutureCallback = null; - if (specs != null) { - mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp); - } } mService.requestTraversal(); }); diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java index fbefd59dcefe2..627474585e05c 100644 --- a/services/core/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2017 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. @@ -11,51 +11,25 @@ * 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. + * limitations under the License */ package com.android.server.wm; -import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; -import static com.android.server.wm.AppTransition.TRANSIT_UNSET; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; -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; -import android.util.TimeUtils; -import android.view.Choreographer; -import android.view.Display; import android.view.SurfaceControl; import android.view.animation.Animation; import android.view.animation.Transformation; -import java.io.PrintWriter; -import java.util.ArrayList; - +// TODO: Move all remaining fields to AppWindowToken and remove this class. public class AppWindowAnimator { static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowAnimator" : TAG_WM; - private static final int PROLONG_ANIMATION_DISABLED = 0; static final int PROLONG_ANIMATION_AT_END = 1; static final int PROLONG_ANIMATION_AT_START = 2; - final AppWindowToken mAppToken; - final WindowManagerService mService; - final WindowAnimator mAnimator; - - boolean animating; - boolean wasAnimating; - Animation animation; - boolean hasTransformation; - final Transformation transformation = new Transformation(); - // Have we been asked to have this token keep the screen frozen? // Protect with mAnimator. boolean freezingScreen; @@ -78,400 +52,11 @@ public class AppWindowAnimator { // requires that the duration of the two animations are the same. SurfaceControl thumbnail; int thumbnailTransactionSeq; - private int mThumbnailLayer; Animation thumbnailAnimation; - final Transformation thumbnailTransformation = new Transformation(); + // This flag indicates that the destruction of the thumbnail surface is synchronized with // another animation, so defer the destruction of this thumbnail surface for a single frame // after the secondary animation completes. boolean deferThumbnailDestruction; - // This flag is set if the animator has deferThumbnailDestruction set and has reached the final - // frame of animation. It will extend the animation by one frame and then clean up afterwards. - boolean deferFinalFrameCleanup; - // If true when the animation hits the last frame, it will keep running on that last frame. - // This is used to synchronize animation with Recents and we wait for Recents to tell us to - // finish or for a new animation be set as fail-safe mechanism. - private int mProlongAnimation; - // Whether the prolong animation can be removed when animation is set. The purpose of this is - // that if recents doesn't tell us to remove the prolonged animation, we will get rid of it - // when new animation is set. - private boolean mClearProlongedAnimation; - private int mTransit; - private int mTransitFlags; - - /** True if the current animation was transferred from another AppWindowAnimator. - * See {@link #transferCurrentAnimation}*/ - boolean usingTransferredAnimation = false; - - private boolean mSkipFirstFrame = false; - private int mStackClip = STACK_CLIP_BEFORE_ANIM; - - static final Animation sDummyAnimation = new DummyAnimation(); - - public AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service) { - mAppToken = atoken; - mService = service; - mAnimator = mService.mAnimator; - } - - public void setAnimation(Animation anim, int width, int height, int parentWidth, - int parentHeight, boolean skipFirstFrame, int stackClip, int transit, - int transitFlags) { - if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken - + ": " + anim + " wxh=" + width + "x" + height - + " hasContentToDisplay=" + mAppToken.hasContentToDisplay()); - animation = anim; - animating = false; - if (!anim.isInitialized()) { - anim.initialize(width, height, parentWidth, parentHeight); - } - anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION); - anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); - int zorder = anim.getZAdjustment(); - int adj = 0; - if (zorder == Animation.ZORDER_TOP) { - adj = TYPE_LAYER_OFFSET; - } else if (zorder == Animation.ZORDER_BOTTOM) { - adj = -TYPE_LAYER_OFFSET; - } - - if (animLayerAdjustment != adj) { - animLayerAdjustment = adj; - updateLayers(); - } - // Start out animation gone if window is gone, or visible if window is visible. - transformation.clear(); - transformation.setAlpha(mAppToken.isVisible() ? 1 : 0); - hasTransformation = true; - mStackClip = stackClip; - - mSkipFirstFrame = skipFirstFrame; - mTransit = transit; - mTransitFlags = transitFlags; - - if (!mAppToken.fillsParent()) { - anim.setBackgroundColor(0); - } - if (mClearProlongedAnimation) { - mProlongAnimation = PROLONG_ANIMATION_DISABLED; - } else { - mClearProlongedAnimation = true; - } - } - - public void setDummyAnimation() { - if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken - + " hasContentToDisplay=" + mAppToken.hasContentToDisplay()); - animation = sDummyAnimation; - hasTransformation = true; - transformation.clear(); - transformation.setAlpha(mAppToken.isVisible() ? 1 : 0); - } - - void setNullAnimation() { - animation = null; - usingTransferredAnimation = false; - } - - public void clearAnimation() { - if (animation != null) { - animating = true; - } - clearThumbnail(); - setNullAnimation(); - if (mAppToken.deferClearAllDrawn) { - mAppToken.clearAllDrawn(); - } - mStackClip = STACK_CLIP_BEFORE_ANIM; - mTransit = TRANSIT_UNSET; - mTransitFlags = 0; - } - - public boolean isAnimating() { - return animation != null || mAppToken.inPendingTransaction; - } - - /** - * @return whether an animation is about to start, i.e. the animation is set already but we - * haven't processed the first frame yet. - */ - boolean isAnimationStarting() { - return animation != null && !animating; - } - - public int getTransit() { - return mTransit; - } - - int getTransitFlags() { - return mTransitFlags; - } - - public void clearThumbnail() { - if (thumbnail != null) { - thumbnail.hide(); - mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail); - thumbnail = null; - } - deferThumbnailDestruction = false; - } - - int getStackClip() { - return mStackClip; - } - - void transferCurrentAnimation( - AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) { - - if (animation != null) { - toAppAnimator.animation = animation; - toAppAnimator.animating = animating; - toAppAnimator.animLayerAdjustment = animLayerAdjustment; - setNullAnimation(); - animLayerAdjustment = 0; - toAppAnimator.updateLayers(); - updateLayers(); - toAppAnimator.usingTransferredAnimation = true; - toAppAnimator.mTransit = mTransit; - } - if (transferWinAnimator != null) { - toAppAnimator.hasTransformation = transferWinAnimator.mAppAnimator.hasTransformation; - if (toAppAnimator.hasTransformation) { - toAppAnimator.transformation.set(transferWinAnimator.mAppAnimator.transformation); - } else { - toAppAnimator.transformation.clear(); - } - transferWinAnimator.mAppAnimator = toAppAnimator; - } - } - - private void updateLayers() { - mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */); - } - - private void stepThumbnailAnimation(long currentTime) { - thumbnailTransformation.clear(); - final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime); - thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation); - - ScreenRotationAnimation screenRotationAnimation = - mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); - final boolean screenAnimation = screenRotationAnimation != null - && screenRotationAnimation.isAnimating(); - if (screenAnimation) { - thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation()); - } - // cache often used attributes locally - final float tmpFloats[] = mService.mTmpFloats; - thumbnailTransformation.getMatrix().getValues(tmpFloats); - if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail, - "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X] - + ", " + tmpFloats[Matrix.MTRANS_Y]); - thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]); - if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail, - "thumbnail", "alpha=" + thumbnailTransformation.getAlpha() - + " layer=" + mThumbnailLayer - + " matrix=[" + tmpFloats[Matrix.MSCALE_X] - + "," + tmpFloats[Matrix.MSKEW_Y] - + "][" + tmpFloats[Matrix.MSKEW_X] - + "," + tmpFloats[Matrix.MSCALE_Y] + "]"); - thumbnail.setAlpha(thumbnailTransformation.getAlpha()); - thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y], - tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]); - thumbnail.setWindowCrop(thumbnailTransformation.getClipRect()); - } - - /** - * Sometimes we need to synchronize the first frame of animation with some external event, e.g. - * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton - * and keep producing the first frame of the animation. - */ - private long getAnimationFrameTime(Animation animation, long currentTime) { - if (mProlongAnimation == PROLONG_ANIMATION_AT_START) { - animation.setStartTime(currentTime); - return currentTime + 1; - } - return currentTime; - } - - private boolean stepAnimation(long currentTime) { - if (animation == null) { - return false; - } - transformation.clear(); - final long animationFrameTime = getAnimationFrameTime(animation, currentTime); - boolean hasMoreFrames = animation.getTransformation(animationFrameTime, transformation); - if (!hasMoreFrames) { - if (deferThumbnailDestruction && !deferFinalFrameCleanup) { - // We are deferring the thumbnail destruction, so extend the animation for one more - // (dummy) frame before we clean up - deferFinalFrameCleanup = true; - hasMoreFrames = true; - } else { - if (false && DEBUG_ANIM) Slog.v(TAG, - "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames + - ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation); - deferFinalFrameCleanup = false; - if (mProlongAnimation == PROLONG_ANIMATION_AT_END) { - hasMoreFrames = true; - } else { - setNullAnimation(); - clearThumbnail(); - if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ " - + currentTime); - } - } - } - hasTransformation = hasMoreFrames; - return hasMoreFrames; - } - - private long getStartTimeCorrection() { - if (mSkipFirstFrame) { - - // If the transition is an animation in which the first frame doesn't change the screen - // contents at all, we can just skip it and start at the second frame. So we shift the - // start time of the animation forward by minus the frame duration. - return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS; - } else { - return 0; - } - } - - // This must be called while inside a transaction. - boolean stepAnimationLocked(long currentTime) { - if (mAppToken.okToAnimate()) { - // We will run animations as long as the display isn't frozen. - - if (animation == sDummyAnimation) { - // This guy is going to animate, but not yet. For now count - // it as not animating for purposes of scheduling transactions; - // when it is really time to animate, this will be set to - // a real animation and the next call will execute normally. - return false; - } - - if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed) - && animation != null) { - if (!animating) { - if (DEBUG_ANIM) Slog.v(TAG, - "Starting animation in " + mAppToken + - " @ " + currentTime + " scale=" - + mService.getTransitionAnimationScaleLocked() - + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating); - long correction = getStartTimeCorrection(); - animation.setStartTime(currentTime + correction); - animating = true; - if (thumbnail != null) { - thumbnail.show(); - thumbnailAnimation.setStartTime(currentTime + correction); - } - mSkipFirstFrame = false; - } - if (stepAnimation(currentTime)) { - // animation isn't over, step any thumbnail and that's - // it for now. - if (thumbnail != null) { - stepThumbnailAnimation(currentTime); - } - return true; - } - } - } else if (animation != null) { - // If the display is frozen, and there is a pending animation, - // clear it and make sure we run the cleanup code. - animating = true; - animation = null; - } - - hasTransformation = false; - - if (!animating && animation == null) { - return false; - } - - mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "AppWindowToken"); - - clearAnimation(); - animating = false; - if (animLayerAdjustment != 0) { - animLayerAdjustment = 0; - updateLayers(); - } - if (mService.mInputMethodTarget != null - && mService.mInputMethodTarget.mAppToken == mAppToken) { - mAppToken.getDisplayContent().computeImeTarget(true /* updateImeTarget */); - } - - if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken - + ": reportedVisible=" + mAppToken.reportedVisible - + " okToDisplay=" + mAppToken.okToDisplay() - + " okToAnimate=" + mAppToken.okToAnimate() - + " startingDisplayed=" + mAppToken.startingDisplayed); - - transformation.clear(); - - mAppToken.forAllWindows(WindowState::onExitAnimationDone, false /* traverseTopToBottom */); - mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token); - return false; - } - - // This must be called while inside a transaction. - void showAllWindowsLocked() { - mAppToken.forAllWindows(windowState -> { - if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + windowState); - windowState.performShowLocked(); - }, false /* traverseTopToBottom */); - } - - void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); - pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator); - pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen); - pw.print(" allDrawn="); pw.print(allDrawn); - pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment); - if (lastFreezeDuration != 0) { - pw.print(prefix); pw.print("lastFreezeDuration="); - TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println(); - } - if (animating || animation != null) { - pw.print(prefix); pw.print("animating="); pw.println(animating); - pw.print(prefix); pw.print("animation="); pw.println(animation); - pw.print(prefix); pw.print("mTransit="); pw.println(mTransit); - pw.print(prefix); pw.print("mTransitFlags="); pw.println(mTransitFlags); - } - if (hasTransformation) { - pw.print(prefix); pw.print("XForm: "); - transformation.printShortString(pw); - pw.println(); - } - if (thumbnail != null) { - pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail); - pw.print(" layer="); pw.println(mThumbnailLayer); - pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation); - pw.print(prefix); pw.print("thumbnailTransformation="); - pw.println(thumbnailTransformation.toShortString()); - } - } - - void startProlongAnimation(int prolongType) { - mProlongAnimation = prolongType; - mClearProlongedAnimation = false; - } - - void endProlongedAnimation() { - mProlongAnimation = PROLONG_ANIMATION_DISABLED; - } - - // This is an animation that does nothing: it just immediately finishes - // itself every time it is called. It is used as a stub animation in cases - // where we want to synchronize multiple things that may be animating. - static final class DummyAnimation extends Animation { - @Override - public boolean getTransformation(long currentTime, Transformation outTransformation) { - return false; - } - } - } diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 00a0d3d18f9ca..73732dd83e6eb 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS; @@ -25,7 +24,6 @@ import static com.android.server.wm.AppTransition.TRANSIT_UNSET; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; @@ -34,16 +32,13 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.app.ActivityManager.TaskSnapshot; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; -import android.graphics.Bitmap; import android.graphics.Rect; import android.os.Debug; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.Trace; import android.util.Slog; -import android.view.DisplayInfo; import android.view.IApplicationToken; import com.android.internal.annotations.VisibleForTesting; @@ -339,7 +334,7 @@ public class AppWindowContainerController if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility(" + mToken + ", visible=" + visible + "): " + mService.mAppTransition - + " hidden=" + wtoken.hidden + " hiddenRequested=" + + " hidden=" + wtoken.isHidden() + " hiddenRequested=" + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6)); mService.mOpeningApps.remove(wtoken); @@ -364,11 +359,11 @@ public class AppWindowContainerController wtoken.startingMoved = false; // If the token is currently hidden (should be the common case), or has been // stopped, then we need to set up to wait for its windows to be ready. - if (wtoken.hidden || wtoken.mAppStopped) { + if (wtoken.isHidden() || wtoken.mAppStopped) { wtoken.clearAllDrawn(); // If the app was already visible, don't reset the waitingToShow state. - if (wtoken.hidden) { + if (wtoken.isHidden()) { wtoken.waitingToShow = true; } @@ -389,21 +384,6 @@ public class AppWindowContainerController // If we are preparing an app transition, then delay changing // the visibility of this token until we execute that transition. if (wtoken.okToAnimate() && mService.mAppTransition.isTransitionSet()) { - // A dummy animation is a placeholder animation which informs others that an - // animation is going on (in this case an application transition). If the animation - // was transferred from another application/animator, no dummy animator should be - // created since an animation is already in progress. - if (wtoken.mAppAnimator.usingTransferredAnimation - && wtoken.mAppAnimator.animation == null) { - Slog.wtf(TAG_WM, "Will NOT set dummy animation on: " + wtoken - + ", using null transferred animation!"); - } - if (!wtoken.mAppAnimator.usingTransferredAnimation && - (!wtoken.startingDisplayed || mService.mSkipAppTransitionAnimation)) { - if (DEBUG_APP_TRANSITIONS) Slog.v( - TAG_WM, "Setting dummy animation on: " + wtoken); - wtoken.mAppAnimator.setDummyAnimation(); - } wtoken.inPendingTransaction = true; if (visible) { mService.mOpeningApps.add(wtoken); @@ -423,7 +403,7 @@ public class AppWindowContainerController if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, " + " adding " + focusedToken + " to mOpeningApps"); // Force animation to be loaded. - focusedToken.hidden = true; + focusedToken.setHidden(true); mService.mOpeningApps.add(focusedToken); } } @@ -710,7 +690,7 @@ public class AppWindowContainerController return; } if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden=" - + mContainer.hidden + " freezing=" + mContainer.mAppAnimator.freezingScreen); + + mContainer.isHidden() + " freezing=" + mContainer.mAppAnimator.freezingScreen); mContainer.stopFreezingScreen(true, force); } } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 80c302bcdaf3a..da144e6b66ac0 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -20,6 +20,7 @@ import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; @@ -54,19 +55,22 @@ import android.annotation.CallSuper; import android.annotation.NonNull; import android.app.Activity; import android.content.res.Configuration; +import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import android.view.DisplayInfo; +import android.view.animation.Animation; import android.view.IApplicationToken; import android.view.SurfaceControl; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; -import android.view.animation.Transformation; import com.android.internal.util.ToBooleanFunction; import com.android.server.input.InputApplicationHandle; @@ -189,6 +193,21 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree */ private boolean mCanTurnScreenOn = true; + /** + * If we are running an animation, this determines the transition type. Must be one of + * AppTransition.TRANSIT_* constants. + */ + private int mTransit; + + /** + * If we are running an animation, this determines the flags during this animation. Must be a + * bitwise combination of AppTransition.TRANSIT_FLAG_* constants. + */ + private int mTransitFlags; + + /** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */ + private boolean mLastSurfaceShowing; + AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, @@ -206,7 +225,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mRotationAnimationHint = rotationAnimationHint; // Application tokens start out hidden. - hidden = true; + setHidden(true); hiddenRequested = true; } @@ -218,7 +237,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mVoiceInteraction = voiceInteraction; mFillsParent = fillsParent; mInputApplicationHandle = new InputApplicationHandle(this); - mAppAnimator = new AppWindowAnimator(this, service); + mAppAnimator = new AppWindowAnimator(); } void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) { @@ -262,7 +281,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree boolean nowGone = mReportedVisibilityResults.nowGone; boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting; - boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && !hidden; + boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && !isHidden(); if (!nowGone) { // If the app is not yet gone, then it can only become visible/drawn. if (!nowDrawn) { @@ -325,19 +344,16 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // transition animation // * or this is an opening app and windows are being replaced. boolean visibilityChanged = false; - if (hidden == visible || (hidden && mIsExiting) || (visible && waitingForReplacement())) { + if (isHidden() == visible || (isHidden() && mIsExiting) || (visible && waitingForReplacement())) { final AccessibilityController accessibilityController = mService.mAccessibilityController; boolean changed = false; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, - "Changing app " + this + " hidden=" + hidden + " performLayout=" + performLayout); + "Changing app " + this + " hidden=" + isHidden() + " performLayout=" + performLayout); boolean runningAppAnimation = false; - if (mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) { - mAppAnimator.setNullAnimation(); - } if (transit != AppTransition.TRANSIT_UNSET) { - if (mService.applyAnimationLocked(this, lp, transit, visible, isVoiceInteraction)) { + if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) { delayed = runningAppAnimation = true; } final WindowState window = findMainWindow(); @@ -355,7 +371,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree changed |= win.onAppVisibilityChanged(visible, runningAppAnimation); } - hidden = hiddenRequested = !visible; + setHidden(!visible); + hiddenRequested = !visible; visibilityChanged = true; if (!visible) { stopFreezingScreen(true, true); @@ -373,7 +390,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this - + ": hidden=" + hidden + " hiddenRequested=" + hiddenRequested); + + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested); if (changed) { mService.mInputMonitor.setUpdateInputWindowsNeededLw(); @@ -386,7 +403,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } - if (mAppAnimator.animation != null) { + if (isReallyAnimating()) { delayed = true; } @@ -414,7 +431,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // no animation but there will still be a transition set. // We still need to delay hiding the surface such that it // can be synchronized with showing the next surface in the transition. - if (hidden && !delayed && !mService.mAppTransition.isTransitionSet()) { + if (isHidden() && !delayed && !mService.mAppTransition.isTransitionSet()) { SurfaceControl.openTransaction(); for (int i = mChildren.size() - 1; i >= 0; i--) { mChildren.get(i).mWinAnimator.hide("immediately hidden"); @@ -487,7 +504,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree boolean isVisible() { // If the app token isn't hidden then it is considered visible and there is no need to check // its children windows to see if they are visible. - return !hidden; + return !isHidden(); } @Override @@ -533,7 +550,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app " + this + " delayed=" + delayed - + " animation=" + mAppAnimator.animation + " animating=" + mAppAnimator.animating); + + " animation=" + getAnimation() + " animating=" + isSelfAnimating()); if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: " + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4)); @@ -545,7 +562,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // If this window was animating, then we need to ensure that the app transition notifies // that animations have completed in WMS.handleAnimatingStoppedAndTransitionLocked(), so // add to that list now - if (mAppAnimator.animating) { + if (isSelfAnimating()) { mService.mNoAnimationNotifyOnTransitionFinished.add(token); } @@ -561,8 +578,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } else { // Make sure there is no animation running on this token, so any windows associated // with it will be removed as soon as their animations are complete - mAppAnimator.clearAnimation(); - mAppAnimator.animating = false; + cancelAnimation(); if (stack != null) { stack.mExitingAppTokens.remove(this); } @@ -710,7 +726,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // We set the hidden state to false for the token from a transferred starting window. // We now reset it back to true since the starting window was the last window in the // token. - hidden = true; + setHidden(true); } } else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) { // If this is the last window except for a starting transition window, @@ -756,14 +772,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final WindowState w = mChildren.get(i); w.setWillReplaceWindow(animate); } - if (animate) { - // Set-up dummy animation so we can start treating windows associated with this - // token like they are in transition before the new app window is ready for us to - // run the real transition animation. - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, - "setWillReplaceWindow() Setting dummy animation on: " + this); - mAppAnimator.setDummyAnimation(); - } } void setWillReplaceChildWindows() { @@ -1007,7 +1015,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void startFreezingScreen() { if (DEBUG_ORIENTATION) logWithStack(TAG, "Set freezing of " + appToken + ": hidden=" - + hidden + " freezing=" + mAppAnimator.freezingScreen + " hiddenRequested=" + + isHidden() + " freezing=" + mAppAnimator.freezingScreen + " hiddenRequested=" + hiddenRequested); if (!hiddenRequested) { if (!mAppAnimator.freezingScreen) { @@ -1111,14 +1119,14 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (fromToken.firstWindowDrawn) { firstWindowDrawn = true; } - if (!fromToken.hidden) { - hidden = false; + if (!fromToken.isHidden()) { + setHidden(false); hiddenRequested = false; mHiddenSetFromTransferredStartingWindow = true; } setClientHidden(fromToken.mClientHidden); - fromToken.mAppAnimator.transferCurrentAnimation( - mAppAnimator, tStartingWindow.mWinAnimator); + + // TODO: Transfer animation mService.updateFocusedWindowLocked( UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/); @@ -1142,17 +1150,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return true; } - final AppWindowAnimator tAppAnimator = fromToken.mAppAnimator; - final AppWindowAnimator wAppAnimator = mAppAnimator; - if (tAppAnimator.thumbnail != null) { - // The old token is animating with a thumbnail, transfer that to the new token. - if (wAppAnimator.thumbnail != null) { - wAppAnimator.thumbnail.destroy(); - } - wAppAnimator.thumbnail = tAppAnimator.thumbnail; - wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation; - tAppAnimator.thumbnail = null; - } + // TODO: Transfer thumbnail + return false; } @@ -1207,7 +1206,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // The token has now changed state to having all windows shown... what to do, what to do? if (mAppAnimator.freezingScreen) { - mAppAnimator.showAllWindowsLocked(); + showAllWindowsLocked(); stopFreezingScreen(false, true); if (DEBUG_ORIENTATION) Slog.i(TAG, "Setting mOrientationChangeComplete=true because wtoken " + this @@ -1220,7 +1219,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // We can now show all of the drawn windows! if (!mService.mOpeningApps.contains(this)) { - mAppAnimator.showAllWindowsLocked(); + showAllWindowsLocked(); } } } @@ -1310,13 +1309,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (!allDrawn && w.mightAffectAllDrawn()) { if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) { Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw() - + ", isAnimationSet=" + winAnimator.isAnimationSet()); + + ", isAnimationSet=" + isSelfAnimating()); if (!w.isDrawnLw()) { Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController + " pv=" + w.mPolicyVisibility + " mDrawState=" + winAnimator.drawStateToString() + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested - + " a=" + winAnimator.isAnimationSet()); + + " a=" + isSelfAnimating()); } } @@ -1345,21 +1344,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return isInterestingAndDrawn; } - @Override - void stepAppWindowsAnimation(long currentTime) { - mAppAnimator.wasAnimating = mAppAnimator.animating; - if (mAppAnimator.stepAnimationLocked(currentTime)) { - mAppAnimator.animating = true; - mService.mAnimator.setAnimating(true); - mService.mAnimator.mAppWindowAnimating = true; - } else if (mAppAnimator.wasAnimating) { - // stopped animating, do one more pass through the layout - setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER, - DEBUG_LAYOUT_REPEATS ? "appToken " + this + " done" : null); - if (DEBUG_ANIM) Slog.v(TAG, "updateWindowsApps...: done animating " + this); - } - } - @Override boolean forAllWindows(ToBooleanFunction callback, boolean traverseTopToBottom) { // For legacy reasons we process the TaskStack.mExitingAppTokens first in DisplayContent @@ -1515,14 +1499,179 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return mAppAnimator.animLayerAdjustment; } - @Override - boolean isSelfAnimating() { - return mAppAnimator.isAnimating(); + boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter, + boolean isVoiceInteraction) { + + if (mService.mDisableTransitionAnimation) { + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { + Slog.v(TAG_WM, "applyAnimation: transition animation is disabled. atoken=" + this); + } + cancelAnimation(); + return false; + } + + // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason + // to animate and it can cause strange artifacts when we unfreeze the display if some + // different animation is running. + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked"); + if (okToAnimate()) { + final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction); + if (a != null) { + final AnimationAdapter adapter = new LocalAnimationAdapter( + new WindowAnimationSpec(a, new Point()), mService.mSurfaceAnimationRunner); + startAnimation(getPendingTransaction(), adapter, !isVisible()); + if (a.getZAdjustment() == Animation.ZORDER_TOP) { + mAppAnimator.animLayerAdjustment = 1; + getDisplayContent().assignWindowLayers(false /* setLayoutNeeded */); + } + mTransit = transit; + mTransitFlags = mService.mAppTransition.getTransitFlags(); + // TODO: Skip first frame and app stack clip mode. + } + } else { + cancelAnimation(); + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + + return isReallyAnimating(); + } + + private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, + boolean isVoiceInteraction) { + final DisplayContent displayContent = getTask().getDisplayContent(); + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final int width = displayInfo.appWidth; + final int height = displayInfo.appHeight; + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG_WM, + "applyAnimation: atoken=" + this); + + // Determine the visible rect to calculate the thumbnail clip + final WindowState win = findMainWindow(); + final Rect frame = new Rect(0, 0, width, height); + final Rect displayFrame = new Rect(0, 0, + displayInfo.logicalWidth, displayInfo.logicalHeight); + final Rect insets = new Rect(); + final Rect stableInsets = new Rect(); + Rect surfaceInsets = null; + final boolean freeform = win != null && win.inFreeformWindowingMode(); + if (win != null) { + // Containing frame will usually cover the whole screen, including dialog windows. + // For freeform workspace windows it will not cover the whole screen and it also + // won't exactly match the final freeform window frame (e.g. when overlapping with + // the status bar). In that case we need to use the final frame. + if (freeform) { + frame.set(win.mFrame); + } else { + frame.set(win.mContainingFrame); + } + surfaceInsets = win.getAttrs().surfaceInsets; + insets.set(win.mContentInsets); + stableInsets.set(win.mStableInsets); + } + + if (mLaunchTaskBehind) { + // Differentiate the two animations. This one which is briefly on the screen + // gets the !enter animation, and the other activity which remains on the + // screen gets the enter animation. Both appear in the mOpeningApps set. + enter = false; + } + if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "Loading animation for app transition." + + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter + + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets); + final Configuration displayConfig = displayContent.getConfiguration(); + final Animation a = mService.mAppTransition.loadAnimation(lp, transit, enter, + displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets, + surfaceInsets, stableInsets, isVoiceInteraction, freeform, getTask().mTaskId); + if (a != null) { + if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this); + final int containingWidth = frame.width(); + final int containingHeight = frame.height(); + a.initialize(containingWidth, containingHeight, width, height); + a.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); + } + return a; + } + + /** + * This must be called while inside a transaction. + */ + void showAllWindowsLocked() { + forAllWindows(windowState -> { + if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + windowState); + windowState.performShowLocked(); + }, false /* traverseTopToBottom */); } @Override - void dump(PrintWriter pw, String prefix) { - super.dump(pw, prefix); + protected void onAnimationFinished() { + super.onAnimationFinished(); + + mTransit = TRANSIT_UNSET; + mTransitFlags = 0; + mAppAnimator.animLayerAdjustment = 0; + + setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER, + "AppWindowToken"); + + if (mService.mInputMethodTarget != null && mService.mInputMethodTarget.mAppToken == this) { + getDisplayContent().computeImeTarget(true /* updateImeTarget */); + } + + if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + this + + ": reportedVisible=" + reportedVisible + + " okToDisplay=" + okToDisplay() + + " okToAnimate=" + okToAnimate() + + " startingDisplayed=" + startingDisplayed); + + // WindowState.onExitAnimationDone might modify the children list, so make a copy and then + // traverse the copy. + final ArrayList children = new ArrayList<>(mChildren); + children.forEach(WindowState::onExitAnimationDone); + + mService.mAppTransition.notifyAppTransitionFinishedLocked(token); + scheduleAnimation(); + } + + @Override + boolean isAppAnimating() { + return isSelfAnimating(); + } + + @Override + boolean isSelfAnimating() { + // If we are about to start a transition, we also need to be considered animating. + return isWaitingForTransitionStart() || isReallyAnimating(); + } + + /** + * @return True if and only if we are actually running an animation. Note that + * {@link #isSelfAnimating} also returns true if we are waiting for an animation to + * start. + */ + private boolean isReallyAnimating() { + return super.isSelfAnimating(); + } + + boolean isWaitingForTransitionStart() { + return mService.mAppTransition.isTransitionSet() + && (mService.mOpeningApps.contains(this) || mService.mClosingApps.contains(this)); + } + + public int getTransit() { + return mTransit; + } + + int getTransitFlags() { + return mTransitFlags; + } + + void clearThumbnail() { + // TODO + } + + @Override + void dump(PrintWriter pw, String prefix, boolean dumpAll) { + super.dump(pw, prefix, dumpAll); if (appToken != null) { pw.println(prefix + "app=true mVoiceInteraction=" + mVoiceInteraction); } @@ -1580,9 +1729,27 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (mRemovingFromDisplay) { pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay); } - if (mAppAnimator.isAnimating()) { - mAppAnimator.dump(pw, prefix + " "); + } + + @Override + void setHidden(boolean hidden) { + super.setHidden(hidden); + scheduleAnimation(); + } + + @Override + void prepareSurfaces() { + // isSelfAnimating also returns true when we are about to start a transition, so we need + // to check super here. + final boolean reallyAnimating = super.isSelfAnimating(); + final boolean show = !isHidden() || reallyAnimating; + if (show && !mLastSurfaceShowing) { + mPendingTransaction.show(mSurfaceControl); + } else if (!show && mLastSurfaceShowing) { + mPendingTransaction.hide(mSurfaceControl); } + mLastSurfaceShowing = show; + super.prepareSurfaces(); } @CallSuper diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 627af699f9424..274ac04946cf8 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -437,15 +437,14 @@ class DisplayContent extends WindowContainer