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.Display.INVALID_DISPLAY; 20 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; 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 118 public AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service) { 119 mAppToken = atoken; 120 mService = service; 121 mAnimator = mService.mAnimator; 122 } 123 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.isVisible() ? 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 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.isVisible() ? 1 : 0); 176 } 177 178 void setNullAnimation() { 179 animation = null; 180 usingTransferredAnimation = false; 181 } 182 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 197 public boolean isAnimating() { 198 return animation != null || mAppToken.inPendingTransaction; 199 } 200 201 /** 202 * @return whether an animation is about to start, i.e. the animation is set already but we 203 * haven't processed the first frame yet. 204 */ 205 boolean isAnimationStarting() { 206 return animation != null && !animating; 207 } 208 209 public int getTransit() { 210 return mTransit; 211 } 212 213 int getTransitFlags() { 214 return mTransitFlags; 215 } 216 217 public void clearThumbnail() { 218 if (thumbnail != null) { 219 thumbnail.hide(); 220 mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail); 221 thumbnail = null; 222 } 223 deferThumbnailDestruction = false; 224 } 225 226 int getStackClip() { 227 return mStackClip; 228 } 229 230 void transferCurrentAnimation( 231 AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) { 232 233 if (animation != null) { 234 toAppAnimator.animation = animation; 235 toAppAnimator.animating = animating; 236 toAppAnimator.animLayerAdjustment = animLayerAdjustment; 237 setNullAnimation(); 238 animLayerAdjustment = 0; 239 toAppAnimator.updateLayers(); 240 updateLayers(); 241 toAppAnimator.usingTransferredAnimation = true; 242 toAppAnimator.mTransit = mTransit; 243 } 244 if (transferWinAnimator != null) { 245 mAllAppWinAnimators.remove(transferWinAnimator); 246 toAppAnimator.mAllAppWinAnimators.add(transferWinAnimator); 247 toAppAnimator.hasTransformation = transferWinAnimator.mAppAnimator.hasTransformation; 248 if (toAppAnimator.hasTransformation) { 249 toAppAnimator.transformation.set(transferWinAnimator.mAppAnimator.transformation); 250 } else { 251 toAppAnimator.transformation.clear(); 252 } 253 transferWinAnimator.mAppAnimator = toAppAnimator; 254 } 255 } 256 257 private void updateLayers() { 258 mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */); 259 thumbnailLayer = mAppToken.getHighestAnimLayer(); 260 } 261 262 private void stepThumbnailAnimation(long currentTime) { 263 thumbnailTransformation.clear(); 264 final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime); 265 thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation); 266 267 ScreenRotationAnimation screenRotationAnimation = 268 mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); 269 final boolean screenAnimation = screenRotationAnimation != null 270 && screenRotationAnimation.isAnimating(); 271 if (screenAnimation) { 272 thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation()); 273 } 274 // cache often used attributes locally 275 final float tmpFloats[] = mService.mTmpFloats; 276 thumbnailTransformation.getMatrix().getValues(tmpFloats); 277 if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail, 278 "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X] 279 + ", " + tmpFloats[Matrix.MTRANS_Y]); 280 thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]); 281 if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail, 282 "thumbnail", "alpha=" + thumbnailTransformation.getAlpha() 283 + " layer=" + thumbnailLayer 284 + " matrix=[" + tmpFloats[Matrix.MSCALE_X] 285 + "," + tmpFloats[Matrix.MSKEW_Y] 286 + "][" + tmpFloats[Matrix.MSKEW_X] 287 + "," + tmpFloats[Matrix.MSCALE_Y] + "]"); 288 thumbnail.setAlpha(thumbnailTransformation.getAlpha()); 289 if (thumbnailForceAboveLayer > 0) { 290 thumbnail.setLayer(thumbnailForceAboveLayer + 1); 291 } else { 292 // The thumbnail is layered below the window immediately above this 293 // token's anim layer. 294 thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER 295 - WindowManagerService.LAYER_OFFSET_THUMBNAIL); 296 } 297 thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y], 298 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]); 299 thumbnail.setWindowCrop(thumbnailTransformation.getClipRect()); 300 } 301 302 /** 303 * Sometimes we need to synchronize the first frame of animation with some external event, e.g. 304 * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton 305 * and keep producing the first frame of the animation. 306 */ 307 private long getAnimationFrameTime(Animation animation, long currentTime) { 308 if (mProlongAnimation == PROLONG_ANIMATION_AT_START) { 309 animation.setStartTime(currentTime); 310 return currentTime + 1; 311 } 312 return currentTime; 313 } 314 315 private boolean stepAnimation(long currentTime) { 316 if (animation == null) { 317 return false; 318 } 319 transformation.clear(); 320 final long animationFrameTime = getAnimationFrameTime(animation, currentTime); 321 boolean hasMoreFrames = animation.getTransformation(animationFrameTime, transformation); 322 if (!hasMoreFrames) { 323 if (deferThumbnailDestruction && !deferFinalFrameCleanup) { 324 // We are deferring the thumbnail destruction, so extend the animation for one more 325 // (dummy) frame before we clean up 326 deferFinalFrameCleanup = true; 327 hasMoreFrames = true; 328 } else { 329 if (false && DEBUG_ANIM) Slog.v(TAG, 330 "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames + 331 ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation); 332 deferFinalFrameCleanup = false; 333 if (mProlongAnimation == PROLONG_ANIMATION_AT_END) { 334 hasMoreFrames = true; 335 } else { 336 setNullAnimation(); 337 clearThumbnail(); 338 if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ " 339 + currentTime); 340 } 341 } 342 } 343 hasTransformation = hasMoreFrames; 344 return hasMoreFrames; 345 } 346 347 private long getStartTimeCorrection() { 348 if (mSkipFirstFrame) { 349 350 // If the transition is an animation in which the first frame doesn't change the screen 351 // contents at all, we can just skip it and start at the second frame. So we shift the 352 // start time of the animation forward by minus the frame duration. 353 return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS; 354 } else { 355 return 0; 356 } 357 } 358 359 // This must be called while inside a transaction. 360 boolean stepAnimationLocked(long currentTime) { 361 if (mAppToken.okToAnimate()) { 362 // We will run animations as long as the display isn't frozen. 363 364 if (animation == sDummyAnimation) { 365 // This guy is going to animate, but not yet. For now count 366 // it as not animating for purposes of scheduling transactions; 367 // when it is really time to animate, this will be set to 368 // a real animation and the next call will execute normally. 369 return false; 370 } 371 372 if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed) 373 && animation != null) { 374 if (!animating) { 375 if (DEBUG_ANIM) Slog.v(TAG, 376 "Starting animation in " + mAppToken + 377 " @ " + currentTime + " scale=" 378 + mService.getTransitionAnimationScaleLocked() 379 + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating); 380 long correction = getStartTimeCorrection(); 381 animation.setStartTime(currentTime + correction); 382 animating = true; 383 if (thumbnail != null) { 384 thumbnail.show(); 385 thumbnailAnimation.setStartTime(currentTime + correction); 386 } 387 mSkipFirstFrame = false; 388 } 389 if (stepAnimation(currentTime)) { 390 // animation isn't over, step any thumbnail and that's 391 // it for now. 392 if (thumbnail != null) { 393 stepThumbnailAnimation(currentTime); 394 } 395 return true; 396 } 397 } 398 } else if (animation != null) { 399 // If the display is frozen, and there is a pending animation, 400 // clear it and make sure we run the cleanup code. 401 animating = true; 402 animation = null; 403 } 404 405 hasTransformation = false; 406 407 if (!animating && animation == null) { 408 return false; 409 } 410 411 mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "AppWindowToken"); 412 413 clearAnimation(); 414 animating = false; 415 if (animLayerAdjustment != 0) { 416 animLayerAdjustment = 0; 417 updateLayers(); 418 } 419 if (mService.mInputMethodTarget != null 420 && mService.mInputMethodTarget.mAppToken == mAppToken) { 421 mAppToken.getDisplayContent().computeImeTarget(true /* updateImeTarget */); 422 } 423 424 if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken 425 + ": reportedVisible=" + mAppToken.reportedVisible 426 + " okToDisplay=" + mAppToken.okToDisplay() 427 + " okToAnimate=" + mAppToken.okToAnimate() 428 + " startingDisplayed=" + mAppToken.startingDisplayed); 429 430 transformation.clear(); 431 432 final int numAllAppWinAnimators = mAllAppWinAnimators.size(); 433 for (int i = 0; i < numAllAppWinAnimators; i++) { 434 mAllAppWinAnimators.get(i).mWin.onExitAnimationDone(); 435 } 436 mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token); 437 return false; 438 } 439 440 // This must be called while inside a transaction. 441 boolean showAllWindowsLocked() { 442 boolean isAnimating = false; 443 final int NW = mAllAppWinAnimators.size(); 444 for (int i=0; i<NW; i++) { 445 WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i); 446 if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + winAnimator); 447 winAnimator.mWin.performShowLocked(); 448 isAnimating |= winAnimator.isAnimationSet(); 449 } 450 return isAnimating; 451 } 452 453 void dump(PrintWriter pw, String prefix) { 454 pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); 455 pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator); 456 pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen); 457 pw.print(" allDrawn="); pw.print(allDrawn); 458 pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment); 459 if (lastFreezeDuration != 0) { 460 pw.print(prefix); pw.print("lastFreezeDuration="); 461 TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println(); 462 } 463 if (animating || animation != null) { 464 pw.print(prefix); pw.print("animating="); pw.println(animating); 465 pw.print(prefix); pw.print("animation="); pw.println(animation); 466 pw.print(prefix); pw.print("mTransit="); pw.println(mTransit); 467 pw.print(prefix); pw.print("mTransitFlags="); pw.println(mTransitFlags); 468 } 469 if (hasTransformation) { 470 pw.print(prefix); pw.print("XForm: "); 471 transformation.printShortString(pw); 472 pw.println(); 473 } 474 if (thumbnail != null) { 475 pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail); 476 pw.print(" layer="); pw.println(thumbnailLayer); 477 pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation); 478 pw.print(prefix); pw.print("thumbnailTransformation="); 479 pw.println(thumbnailTransformation.toShortString()); 480 } 481 for (int i=0; i<mAllAppWinAnimators.size(); i++) { 482 WindowStateAnimator wanim = mAllAppWinAnimators.get(i); 483 pw.print(prefix); pw.print("App Win Anim #"); pw.print(i); 484 pw.print(": "); pw.println(wanim); 485 } 486 } 487 488 void startProlongAnimation(int prolongType) { 489 mProlongAnimation = prolongType; 490 mClearProlongedAnimation = false; 491 } 492 493 void endProlongedAnimation() { 494 mProlongAnimation = PROLONG_ANIMATION_DISABLED; 495 } 496 497 // This is an animation that does nothing: it just immediately finishes 498 // itself every time it is called. It is used as a stub animation in cases 499 // where we want to synchronize multiple things that may be animating. 500 static final class DummyAnimation extends Animation { 501 @Override 502 public boolean getTransformation(long currentTime, Transformation outTransformation) { 503 return false; 504 } 505 } 506 507 } 508