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