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