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