1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; 20 21 import static com.android.server.wm.AppTransition.TRANSIT_UNSET; 22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; 23 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; 24 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 26 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 27 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET; 28 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; 29 30 import android.graphics.Matrix; 31 import android.util.Slog; 32 import android.util.TimeUtils; 33 import android.view.Choreographer; 34 import android.view.Display; 35 import android.view.SurfaceControl; 36 import android.view.animation.Animation; 37 import android.view.animation.Transformation; 38 39 import java.io.PrintWriter; 40 import java.util.ArrayList; 41 42 public class AppWindowAnimator { 43 static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowAnimator" : TAG_WM; 44 45 private static final int PROLONG_ANIMATION_DISABLED = 0; 46 static final int PROLONG_ANIMATION_AT_END = 1; 47 static final int PROLONG_ANIMATION_AT_START = 2; 48 49 final AppWindowToken mAppToken; 50 final WindowManagerService mService; 51 final WindowAnimator mAnimator; 52 53 boolean animating; 54 boolean wasAnimating; 55 Animation animation; 56 boolean hasTransformation; 57 final Transformation transformation = new Transformation(); 58 59 // Have we been asked to have this token keep the screen frozen? 60 // Protect with mAnimator. 61 boolean freezingScreen; 62 63 /** 64 * How long we last kept the screen frozen. 65 */ 66 int lastFreezeDuration; 67 68 // Offset to the window of all layers in the token, for use by 69 // AppWindowToken animations. 70 int animLayerAdjustment; 71 72 // Propagated from AppWindowToken.allDrawn, to determine when 73 // the state changes. 74 boolean allDrawn; 75 76 // Special surface for thumbnail animation. If deferThumbnailDestruction is enabled, then we 77 // will make sure that the thumbnail is destroyed after the other surface is completed. This 78 // requires that the duration of the two animations are the same. 79 SurfaceControl thumbnail; 80 int thumbnailTransactionSeq; 81 // TODO(b/62029108): combine both members into a private one. Create a member function to set 82 // the thumbnail layer to +1 to the highest layer position and replace all setter instances 83 // with this function. Remove all unnecessary calls to both variables in other classes. 84 int thumbnailLayer; 85 int thumbnailForceAboveLayer; 86 Animation thumbnailAnimation; 87 final Transformation thumbnailTransformation = new Transformation(); 88 // This flag indicates that the destruction of the thumbnail surface is synchronized with 89 // another animation, so defer the destruction of this thumbnail surface for a single frame 90 // after the secondary animation completes. 91 boolean deferThumbnailDestruction; 92 // This flag is set if the animator has deferThumbnailDestruction set and has reached the final 93 // frame of animation. It will extend the animation by one frame and then clean up afterwards. 94 boolean deferFinalFrameCleanup; 95 // If true when the animation hits the last frame, it will keep running on that last frame. 96 // This is used to synchronize animation with Recents and we wait for Recents to tell us to 97 // finish or for a new animation be set as fail-safe mechanism. 98 private int mProlongAnimation; 99 // Whether the prolong animation can be removed when animation is set. The purpose of this is 100 // that if recents doesn't tell us to remove the prolonged animation, we will get rid of it 101 // when new animation is set. 102 private boolean mClearProlongedAnimation; 103 private int mTransit; 104 private int mTransitFlags; 105 106 /** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */ 107 ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<>(); 108 109 /** True if the current animation was transferred from another AppWindowAnimator. 110 * See {@link #transferCurrentAnimation}*/ 111 boolean usingTransferredAnimation = false; 112 113 private boolean mSkipFirstFrame = false; 114 private int mStackClip = STACK_CLIP_BEFORE_ANIM; 115 116 static final Animation sDummyAnimation = new DummyAnimation(); 117 AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service)118 public AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service) { 119 mAppToken = atoken; 120 mService = service; 121 mAnimator = mService.mAnimator; 122 } 123 setAnimation(Animation anim, int width, int height, int parentWidth, int parentHeight, boolean skipFirstFrame, int stackClip, int transit, int transitFlags)124 public void setAnimation(Animation anim, int width, int height, int parentWidth, 125 int parentHeight, boolean skipFirstFrame, int stackClip, int transit, 126 int transitFlags) { 127 if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken 128 + ": " + anim + " wxh=" + width + "x" + height 129 + " hasContentToDisplay=" + mAppToken.hasContentToDisplay()); 130 animation = anim; 131 animating = false; 132 if (!anim.isInitialized()) { 133 anim.initialize(width, height, parentWidth, parentHeight); 134 } 135 anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION); 136 anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); 137 int zorder = anim.getZAdjustment(); 138 int adj = 0; 139 if (zorder == Animation.ZORDER_TOP) { 140 adj = TYPE_LAYER_OFFSET; 141 } else if (zorder == Animation.ZORDER_BOTTOM) { 142 adj = -TYPE_LAYER_OFFSET; 143 } 144 145 if (animLayerAdjustment != adj) { 146 animLayerAdjustment = adj; 147 updateLayers(); 148 } 149 // Start out animation gone if window is gone, or visible if window is visible. 150 transformation.clear(); 151 transformation.setAlpha(mAppToken.hasContentToDisplay() ? 1 : 0); 152 hasTransformation = true; 153 mStackClip = stackClip; 154 155 mSkipFirstFrame = skipFirstFrame; 156 mTransit = transit; 157 mTransitFlags = transitFlags; 158 159 if (!mAppToken.fillsParent()) { 160 anim.setBackgroundColor(0); 161 } 162 if (mClearProlongedAnimation) { 163 mProlongAnimation = PROLONG_ANIMATION_DISABLED; 164 } else { 165 mClearProlongedAnimation = true; 166 } 167 } 168 setDummyAnimation()169 public void setDummyAnimation() { 170 if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken 171 + " hasContentToDisplay=" + mAppToken.hasContentToDisplay()); 172 animation = sDummyAnimation; 173 hasTransformation = true; 174 transformation.clear(); 175 transformation.setAlpha(mAppToken.hasContentToDisplay() ? 1 : 0); 176 } 177 setNullAnimation()178 void setNullAnimation() { 179 animation = null; 180 usingTransferredAnimation = false; 181 } 182 clearAnimation()183 public void clearAnimation() { 184 if (animation != null) { 185 animating = true; 186 } 187 clearThumbnail(); 188 setNullAnimation(); 189 if (mAppToken.deferClearAllDrawn) { 190 mAppToken.clearAllDrawn(); 191 } 192 mStackClip = STACK_CLIP_BEFORE_ANIM; 193 mTransit = TRANSIT_UNSET; 194 mTransitFlags = 0; 195 } 196 isAnimating()197 public boolean isAnimating() { 198 return animation != null || mAppToken.inPendingTransaction; 199 } 200 getTransit()201 public int getTransit() { 202 return mTransit; 203 } 204 getTransitFlags()205 int getTransitFlags() { 206 return mTransitFlags; 207 } 208 clearThumbnail()209 public void clearThumbnail() { 210 if (thumbnail != null) { 211 thumbnail.hide(); 212 mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail); 213 thumbnail = null; 214 } 215 deferThumbnailDestruction = false; 216 } 217 getStackClip()218 int getStackClip() { 219 return mStackClip; 220 } 221 transferCurrentAnimation( AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator)222 void transferCurrentAnimation( 223 AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) { 224 225 if (animation != null) { 226 toAppAnimator.animation = animation; 227 toAppAnimator.animating = animating; 228 toAppAnimator.animLayerAdjustment = animLayerAdjustment; 229 setNullAnimation(); 230 animLayerAdjustment = 0; 231 toAppAnimator.updateLayers(); 232 updateLayers(); 233 toAppAnimator.usingTransferredAnimation = true; 234 toAppAnimator.mTransit = mTransit; 235 } 236 if (transferWinAnimator != null) { 237 mAllAppWinAnimators.remove(transferWinAnimator); 238 toAppAnimator.mAllAppWinAnimators.add(transferWinAnimator); 239 toAppAnimator.hasTransformation = transferWinAnimator.mAppAnimator.hasTransformation; 240 if (toAppAnimator.hasTransformation) { 241 toAppAnimator.transformation.set(transferWinAnimator.mAppAnimator.transformation); 242 } else { 243 toAppAnimator.transformation.clear(); 244 } 245 transferWinAnimator.mAppAnimator = toAppAnimator; 246 } 247 } 248 updateLayers()249 private void updateLayers() { 250 mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */); 251 thumbnailLayer = mAppToken.getHighestAnimLayer(); 252 } 253 stepThumbnailAnimation(long currentTime)254 private void stepThumbnailAnimation(long currentTime) { 255 thumbnailTransformation.clear(); 256 final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime); 257 thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation); 258 259 ScreenRotationAnimation screenRotationAnimation = 260 mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); 261 final boolean screenAnimation = screenRotationAnimation != null 262 && screenRotationAnimation.isAnimating(); 263 if (screenAnimation) { 264 thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation()); 265 } 266 // cache often used attributes locally 267 final float tmpFloats[] = mService.mTmpFloats; 268 thumbnailTransformation.getMatrix().getValues(tmpFloats); 269 if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail, 270 "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X] 271 + ", " + tmpFloats[Matrix.MTRANS_Y]); 272 thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]); 273 if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail, 274 "thumbnail", "alpha=" + thumbnailTransformation.getAlpha() 275 + " layer=" + thumbnailLayer 276 + " matrix=[" + tmpFloats[Matrix.MSCALE_X] 277 + "," + tmpFloats[Matrix.MSKEW_Y] 278 + "][" + tmpFloats[Matrix.MSKEW_X] 279 + "," + tmpFloats[Matrix.MSCALE_Y] + "]"); 280 thumbnail.setAlpha(thumbnailTransformation.getAlpha()); 281 if (thumbnailForceAboveLayer > 0) { 282 thumbnail.setLayer(thumbnailForceAboveLayer + 1); 283 } else { 284 // The thumbnail is layered below the window immediately above this 285 // token's anim layer. 286 thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER 287 - WindowManagerService.LAYER_OFFSET_THUMBNAIL); 288 } 289 thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y], 290 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]); 291 thumbnail.setWindowCrop(thumbnailTransformation.getClipRect()); 292 } 293 294 /** 295 * Sometimes we need to synchronize the first frame of animation with some external event, e.g. 296 * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton 297 * and keep producing the first frame of the animation. 298 */ getAnimationFrameTime(Animation animation, long currentTime)299 private long getAnimationFrameTime(Animation animation, long currentTime) { 300 if (mProlongAnimation == PROLONG_ANIMATION_AT_START) { 301 animation.setStartTime(currentTime); 302 return currentTime + 1; 303 } 304 return currentTime; 305 } 306 stepAnimation(long currentTime)307 private boolean stepAnimation(long currentTime) { 308 if (animation == null) { 309 return false; 310 } 311 transformation.clear(); 312 final long animationFrameTime = getAnimationFrameTime(animation, currentTime); 313 boolean hasMoreFrames = animation.getTransformation(animationFrameTime, transformation); 314 if (!hasMoreFrames) { 315 if (deferThumbnailDestruction && !deferFinalFrameCleanup) { 316 // We are deferring the thumbnail destruction, so extend the animation for one more 317 // (dummy) frame before we clean up 318 deferFinalFrameCleanup = true; 319 hasMoreFrames = true; 320 } else { 321 if (false && DEBUG_ANIM) Slog.v(TAG, 322 "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames + 323 ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation); 324 deferFinalFrameCleanup = false; 325 if (mProlongAnimation == PROLONG_ANIMATION_AT_END) { 326 hasMoreFrames = true; 327 } else { 328 setNullAnimation(); 329 clearThumbnail(); 330 if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ " 331 + currentTime); 332 } 333 } 334 } 335 hasTransformation = hasMoreFrames; 336 return hasMoreFrames; 337 } 338 getStartTimeCorrection()339 private long getStartTimeCorrection() { 340 if (mSkipFirstFrame) { 341 342 // If the transition is an animation in which the first frame doesn't change the screen 343 // contents at all, we can just skip it and start at the second frame. So we shift the 344 // start time of the animation forward by minus the frame duration. 345 return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS; 346 } else { 347 return 0; 348 } 349 } 350 351 // This must be called while inside a transaction. stepAnimationLocked(long currentTime)352 boolean stepAnimationLocked(long currentTime) { 353 if (mService.okToDisplay()) { 354 // We will run animations as long as the display isn't frozen. 355 356 if (animation == sDummyAnimation) { 357 // This guy is going to animate, but not yet. For now count 358 // it as not animating for purposes of scheduling transactions; 359 // when it is really time to animate, this will be set to 360 // a real animation and the next call will execute normally. 361 return false; 362 } 363 364 if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed) 365 && animation != null) { 366 if (!animating) { 367 if (DEBUG_ANIM) Slog.v(TAG, 368 "Starting animation in " + mAppToken + 369 " @ " + currentTime + " scale=" 370 + mService.getTransitionAnimationScaleLocked() 371 + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating); 372 long correction = getStartTimeCorrection(); 373 animation.setStartTime(currentTime + correction); 374 animating = true; 375 if (thumbnail != null) { 376 thumbnail.show(); 377 thumbnailAnimation.setStartTime(currentTime + correction); 378 } 379 mSkipFirstFrame = false; 380 } 381 if (stepAnimation(currentTime)) { 382 // animation isn't over, step any thumbnail and that's 383 // it for now. 384 if (thumbnail != null) { 385 stepThumbnailAnimation(currentTime); 386 } 387 return true; 388 } 389 } 390 } else if (animation != null) { 391 // If the display is frozen, and there is a pending animation, 392 // clear it and make sure we run the cleanup code. 393 animating = true; 394 animation = null; 395 } 396 397 hasTransformation = false; 398 399 if (!animating && animation == null) { 400 return false; 401 } 402 403 mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "AppWindowToken"); 404 405 clearAnimation(); 406 animating = false; 407 if (animLayerAdjustment != 0) { 408 animLayerAdjustment = 0; 409 updateLayers(); 410 } 411 if (mService.mInputMethodTarget != null 412 && mService.mInputMethodTarget.mAppToken == mAppToken) { 413 mAppToken.getDisplayContent().computeImeTarget(true /* updateImeTarget */); 414 } 415 416 if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken 417 + ": reportedVisible=" + mAppToken.reportedVisible 418 + " okToDisplay=" + mService.okToDisplay() 419 + " startingDisplayed=" + mAppToken.startingDisplayed); 420 421 transformation.clear(); 422 423 final int numAllAppWinAnimators = mAllAppWinAnimators.size(); 424 for (int i = 0; i < numAllAppWinAnimators; i++) { 425 mAllAppWinAnimators.get(i).mWin.onExitAnimationDone(); 426 } 427 mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token); 428 return false; 429 } 430 431 // This must be called while inside a transaction. showAllWindowsLocked()432 boolean showAllWindowsLocked() { 433 boolean isAnimating = false; 434 final int NW = mAllAppWinAnimators.size(); 435 for (int i=0; i<NW; i++) { 436 WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i); 437 if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + winAnimator); 438 winAnimator.mWin.performShowLocked(); 439 isAnimating |= winAnimator.isAnimationSet(); 440 } 441 return isAnimating; 442 } 443 dump(PrintWriter pw, String prefix, boolean dumpAll)444 void dump(PrintWriter pw, String prefix, boolean dumpAll) { 445 pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); 446 pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator); 447 pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen); 448 pw.print(" allDrawn="); pw.print(allDrawn); 449 pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment); 450 if (lastFreezeDuration != 0) { 451 pw.print(prefix); pw.print("lastFreezeDuration="); 452 TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println(); 453 } 454 if (animating || animation != null) { 455 pw.print(prefix); pw.print("animating="); pw.println(animating); 456 pw.print(prefix); pw.print("animation="); pw.println(animation); 457 pw.print(prefix); pw.print("mTransit="); pw.println(mTransit); 458 pw.print(prefix); pw.print("mTransitFlags="); pw.println(mTransitFlags); 459 } 460 if (hasTransformation) { 461 pw.print(prefix); pw.print("XForm: "); 462 transformation.printShortString(pw); 463 pw.println(); 464 } 465 if (thumbnail != null) { 466 pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail); 467 pw.print(" layer="); pw.println(thumbnailLayer); 468 pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation); 469 pw.print(prefix); pw.print("thumbnailTransformation="); 470 pw.println(thumbnailTransformation.toShortString()); 471 } 472 for (int i=0; i<mAllAppWinAnimators.size(); i++) { 473 WindowStateAnimator wanim = mAllAppWinAnimators.get(i); 474 pw.print(prefix); pw.print("App Win Anim #"); pw.print(i); 475 pw.print(": "); pw.println(wanim); 476 } 477 } 478 startProlongAnimation(int prolongType)479 void startProlongAnimation(int prolongType) { 480 mProlongAnimation = prolongType; 481 mClearProlongedAnimation = false; 482 } 483 endProlongedAnimation()484 void endProlongedAnimation() { 485 mProlongAnimation = PROLONG_ANIMATION_DISABLED; 486 } 487 488 // This is an animation that does nothing: it just immediately finishes 489 // itself every time it is called. It is used as a stub animation in cases 490 // where we want to synchronize multiple things that may be animating. 491 static final class DummyAnimation extends Animation { 492 @Override getTransformation(long currentTime, Transformation outTransformation)493 public boolean getTransformation(long currentTime, Transformation outTransformation) { 494 return false; 495 } 496 } 497 498 } 499