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