1 /* 2 * Copyright (C) 2016 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.TAG_WITH_CLASS_NAME; 21 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 22 23 import android.animation.AnimationHandler; 24 import android.animation.Animator; 25 import android.animation.ValueAnimator; 26 import android.annotation.IntDef; 27 import android.content.Context; 28 import android.graphics.Rect; 29 import android.os.Debug; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.util.ArrayMap; 33 import android.util.Slog; 34 import android.view.Choreographer; 35 import android.view.animation.AnimationUtils; 36 import android.view.animation.Interpolator; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.internal.graphics.SfVsyncFrameCallbackProvider; 40 41 import java.lang.annotation.Retention; 42 import java.lang.annotation.RetentionPolicy; 43 44 /** 45 * Enables animating bounds of objects. 46 * 47 * In multi-window world bounds of both stack and tasks can change. When we need these bounds to 48 * change smoothly and not require the app to relaunch (e.g. because it handles resizes and 49 * relaunching it would cause poorer experience), these class provides a way to directly animate 50 * the bounds of the resized object. 51 * 52 * The object that is resized needs to implement {@link BoundsAnimationTarget} interface. 53 * 54 * NOTE: All calls to methods in this class should be done on the Animation thread 55 */ 56 public class BoundsAnimationController { 57 private static final boolean DEBUG_LOCAL = false; 58 private static final boolean DEBUG = DEBUG_LOCAL || DEBUG_ANIM; 59 private static final String TAG = TAG_WITH_CLASS_NAME || DEBUG_LOCAL 60 ? "BoundsAnimationController" : TAG_WM; 61 private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1; 62 63 private static final int DEFAULT_TRANSITION_DURATION = 425; 64 65 @Retention(RetentionPolicy.SOURCE) 66 @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START, 67 SCHEDULE_PIP_MODE_CHANGED_ON_END}) 68 public @interface SchedulePipModeChangedState {} 69 /** Do not schedule any PiP mode changed callbacks as a part of this animation. */ 70 public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0; 71 /** Schedule a PiP mode changed callback when this animation starts. */ 72 public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1; 73 /** Schedule a PiP mode changed callback when this animation ends. */ 74 public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2; 75 76 public static final int BOUNDS = 0; 77 public static final int FADE_IN = 1; 78 79 @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {} 80 81 private static final int FADE_IN_DURATION = 500; 82 83 // Only accessed on UI thread. 84 private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>(); 85 86 private final class AppTransitionNotifier 87 extends WindowManagerInternal.AppTransitionListener implements Runnable { 88 onAppTransitionCancelledLocked()89 public void onAppTransitionCancelledLocked() { 90 if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:" 91 + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); 92 animationFinished(); 93 } onAppTransitionFinishedLocked(IBinder token)94 public void onAppTransitionFinishedLocked(IBinder token) { 95 if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:" 96 + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); 97 animationFinished(); 98 } animationFinished()99 private void animationFinished() { 100 if (mFinishAnimationAfterTransition) { 101 mHandler.removeCallbacks(this); 102 // This might end up calling into activity manager which will be bad since we have 103 // the window manager lock held at this point. Post a message to take care of the 104 // processing so we don't deadlock. 105 mHandler.post(this); 106 } 107 } 108 109 @Override run()110 public void run() { 111 for (int i = 0; i < mRunningAnimations.size(); i++) { 112 final BoundsAnimator b = mRunningAnimations.valueAt(i); 113 b.onAnimationEnd(null); 114 } 115 } 116 } 117 118 private final Handler mHandler; 119 private final AppTransition mAppTransition; 120 private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier(); 121 private final Interpolator mFastOutSlowInInterpolator; 122 private boolean mFinishAnimationAfterTransition = false; 123 private final AnimationHandler mAnimationHandler; 124 private Choreographer mChoreographer; 125 private @AnimationType int mAnimationType; 126 127 private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000; 128 BoundsAnimationController(Context context, AppTransition transition, Handler handler, AnimationHandler animationHandler)129 BoundsAnimationController(Context context, AppTransition transition, Handler handler, 130 AnimationHandler animationHandler) { 131 mHandler = handler; 132 mAppTransition = transition; 133 mAppTransition.registerListenerLocked(mAppTransitionNotifier); 134 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 135 com.android.internal.R.interpolator.fast_out_slow_in); 136 mAnimationHandler = animationHandler; 137 if (animationHandler != null) { 138 // If an animation handler is provided, then ensure that it runs on the sf vsync tick 139 handler.post(() -> { 140 mChoreographer = Choreographer.getSfInstance(); 141 animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer)); 142 }); 143 } 144 } 145 146 @VisibleForTesting 147 final class BoundsAnimator extends ValueAnimator 148 implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { 149 150 private final BoundsAnimationTarget mTarget; 151 private final @AnimationType int mAnimationType; 152 private final Rect mFrom = new Rect(); 153 private final Rect mTo = new Rect(); 154 private final Rect mTmpRect = new Rect(); 155 private final Rect mTmpTaskBounds = new Rect(); 156 157 // True if this this animation was canceled and will be replaced the another animation from 158 // the same {@link #BoundsAnimationTarget} target. 159 private boolean mSkipFinalResize; 160 // True if this animation was canceled by the user, not as a part of a replacing animation 161 private boolean mSkipAnimationEnd; 162 163 // True if the animation target is animating from the fullscreen. Only one of 164 // {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be true at any time in the 165 // animation. 166 private boolean mMoveFromFullscreen; 167 // True if the animation target should be moved to the fullscreen stack at the end of this 168 // animation. Only one of {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be 169 // true at any time in the animation. 170 private boolean mMoveToFullscreen; 171 172 // Whether to schedule PiP mode changes on animation start/end 173 private @SchedulePipModeChangedState int mSchedulePipModeChangedState; 174 private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState; 175 176 // Depending on whether we are animating from 177 // a smaller to a larger size 178 private int mFrozenTaskWidth; 179 private int mFrozenTaskHeight; 180 181 // Timeout callback to ensure we continue the animation if waiting for resuming or app 182 // windows drawn fails 183 private final Runnable mResumeRunnable = () -> { 184 if (DEBUG) Slog.d(TAG, "pause: timed out waiting for windows drawn"); 185 resume(); 186 }; 187 BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from, Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState, @SchedulePipModeChangedState int prevShedulePipModeChangedState, boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask)188 BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from, 189 Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState, 190 @SchedulePipModeChangedState int prevShedulePipModeChangedState, 191 boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) { 192 super(); 193 mTarget = target; 194 mAnimationType = animationType; 195 mFrom.set(from); 196 mTo.set(to); 197 mSchedulePipModeChangedState = schedulePipModeChangedState; 198 mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState; 199 mMoveFromFullscreen = moveFromFullscreen; 200 mMoveToFullscreen = moveToFullscreen; 201 addUpdateListener(this); 202 addListener(this); 203 204 // If we are animating from smaller to larger, we want to change the task bounds 205 // to their final size immediately so we can use scaling to make the window 206 // larger. Likewise if we are going from bigger to smaller, we want to wait until 207 // the end so we don't have to upscale from the smaller finished size. 208 if (mAnimationType == BOUNDS) { 209 if (animatingToLargerSize()) { 210 mFrozenTaskWidth = mTo.width(); 211 mFrozenTaskHeight = mTo.height(); 212 } else { 213 mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width(); 214 mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height(); 215 } 216 } 217 } 218 219 @Override onAnimationStart(Animator animation)220 public void onAnimationStart(Animator animation) { 221 if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget 222 + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState 223 + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState); 224 mFinishAnimationAfterTransition = false; 225 mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth, 226 mFrom.top + mFrozenTaskHeight); 227 228 // Boost the thread priority of the animation thread while the bounds animation is 229 // running 230 updateBooster(); 231 232 // Ensure that we have prepared the target for animation before we trigger any size 233 // changes, so it can swap surfaces in to appropriate modes, or do as it wishes 234 // otherwise. 235 boolean continueAnimation; 236 if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) { 237 continueAnimation = mTarget.onAnimationStart( 238 mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START, 239 false /* forceUpdate */, mAnimationType); 240 241 // When starting an animation from fullscreen, pause here and wait for the 242 // windows-drawn signal before we start the rest of the transition down into PiP. 243 if (continueAnimation && mMoveFromFullscreen 244 && mTarget.shouldDeferStartOnMoveToFullscreen()) { 245 pause(); 246 } 247 } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END && 248 mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { 249 // We are replacing a running animation into PiP, but since it hasn't completed, the 250 // client will not currently receive any picture-in-picture mode change callbacks. 251 // However, we still need to report to them that they are leaving PiP, so this will 252 // force an update via a mode changed callback. 253 continueAnimation = mTarget.onAnimationStart( 254 true /* schedulePipModeChangedCallback */, true /* forceUpdate */, 255 mAnimationType); 256 } else { 257 // The animation is already running, but we should check that the TaskStack is still 258 // valid before continuing with the animation 259 continueAnimation = mTarget.isAttached(); 260 } 261 262 if (!continueAnimation) { 263 // No point of trying to animate something that isn't attached to the hierarchy 264 // anymore. 265 cancel(); 266 return; 267 } 268 269 // Immediately update the task bounds if they have to become larger, but preserve 270 // the starting position so we don't jump at the beginning of the animation. 271 if (animatingToLargerSize()) { 272 mTarget.setPinnedStackSize(mFrom, mTmpRect); 273 274 // We pause the animation until the app has drawn at the new size. 275 // The target will notify us via BoundsAnimationController#resume. 276 // We do this here and pause the animation, rather than just defer starting it 277 // so we can enter the animating state and have WindowStateAnimator apply the 278 // correct logic to make this resize seamless. 279 if (mMoveToFullscreen) { 280 pause(); 281 } 282 } 283 } 284 285 @Override pause()286 public void pause() { 287 if (DEBUG) Slog.d(TAG, "pause: waiting for windows drawn"); 288 super.pause(); 289 mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS); 290 } 291 292 @Override resume()293 public void resume() { 294 if (DEBUG) Slog.d(TAG, "resume:"); 295 mHandler.removeCallbacks(mResumeRunnable); 296 super.resume(); 297 } 298 299 @Override onAnimationUpdate(ValueAnimator animation)300 public void onAnimationUpdate(ValueAnimator animation) { 301 final float value = (Float) animation.getAnimatedValue(); 302 if (mAnimationType == FADE_IN) { 303 if (!mTarget.setPinnedStackAlpha(value)) { 304 cancelAndCallAnimationEnd(); 305 } 306 return; 307 } 308 309 final float remains = 1 - value; 310 mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f); 311 mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f); 312 mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f); 313 mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f); 314 if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds=" 315 + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value 316 + " remains=" + remains); 317 318 mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top, 319 mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight); 320 321 if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) { 322 // Whoops, the target doesn't feel like animating anymore. Let's immediately finish 323 // any further animation. 324 if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled"); 325 326 // If we have already scheduled a PiP mode changed at the start of the animation, 327 // then we need to clean up and schedule one at the end, since we have canceled the 328 // animation to the final state. 329 if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { 330 mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; 331 } 332 333 // Since we are cancelling immediately without a replacement animation, send the 334 // animation end to maintain callback parity, but also skip any further resizes 335 cancelAndCallAnimationEnd(); 336 } 337 } 338 339 @Override onAnimationEnd(Animator animation)340 public void onAnimationEnd(Animator animation) { 341 if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget 342 + " mSkipFinalResize=" + mSkipFinalResize 343 + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition 344 + " mAppTransitionIsRunning=" + mAppTransition.isRunning() 345 + " callers=" + Debug.getCallers(2)); 346 347 // There could be another animation running. For example in the 348 // move to fullscreen case, recents will also be closing while the 349 // previous task will be taking its place in the fullscreen stack. 350 // we have to ensure this is completed before we finish the animation 351 // and take our place in the fullscreen stack. 352 if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) { 353 mFinishAnimationAfterTransition = true; 354 return; 355 } 356 357 if (!mSkipAnimationEnd) { 358 // If this animation has already scheduled the picture-in-picture mode on start, and 359 // we are not skipping the final resize due to being canceled, then move the PiP to 360 // fullscreen once the animation ends 361 if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget 362 + " moveToFullscreen=" + mMoveToFullscreen); 363 mTarget.onAnimationEnd(mSchedulePipModeChangedState == 364 SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null, 365 mMoveToFullscreen); 366 } 367 368 // Clean up this animation 369 removeListener(this); 370 removeUpdateListener(this); 371 mRunningAnimations.remove(mTarget); 372 373 // Reset the thread priority of the animation thread after the bounds animation is done 374 updateBooster(); 375 } 376 377 @Override onAnimationCancel(Animator animation)378 public void onAnimationCancel(Animator animation) { 379 // Always skip the final resize when the animation is canceled 380 mSkipFinalResize = true; 381 mMoveToFullscreen = false; 382 } 383 cancelAndCallAnimationEnd()384 private void cancelAndCallAnimationEnd() { 385 if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget); 386 mSkipAnimationEnd = false; 387 super.cancel(); 388 } 389 390 @Override cancel()391 public void cancel() { 392 if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget); 393 mSkipAnimationEnd = true; 394 super.cancel(); 395 396 // Reset the thread priority of the animation thread if the bounds animation is canceled 397 updateBooster(); 398 } 399 400 /** 401 * @return true if the animation target is the same as the input bounds. 402 */ isAnimatingTo(Rect bounds)403 boolean isAnimatingTo(Rect bounds) { 404 return mTo.equals(bounds); 405 } 406 407 /** 408 * @return true if we are animating to a larger surface size 409 */ 410 @VisibleForTesting animatingToLargerSize()411 boolean animatingToLargerSize() { 412 // TODO: Fix this check for aspect ratio changes 413 return (mFrom.width() * mFrom.height() < mTo.width() * mTo.height()); 414 } 415 416 @Override onAnimationRepeat(Animator animation)417 public void onAnimationRepeat(Animator animation) { 418 // Do nothing 419 } 420 421 @Override getAnimationHandler()422 public AnimationHandler getAnimationHandler() { 423 if (mAnimationHandler != null) { 424 return mAnimationHandler; 425 } 426 return super.getAnimationHandler(); 427 } 428 } 429 animateBounds(final BoundsAnimationTarget target, Rect from, Rect to, int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, boolean moveFromFullscreen, boolean moveToFullscreen, @AnimationType int animationType)430 public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to, 431 int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, 432 boolean moveFromFullscreen, boolean moveToFullscreen, 433 @AnimationType int animationType) { 434 animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState, 435 moveFromFullscreen, moveToFullscreen, animationType); 436 } 437 438 /** 439 * Cancel existing animation if the destination was modified. 440 */ cancel(final BoundsAnimationTarget target)441 void cancel(final BoundsAnimationTarget target) { 442 final BoundsAnimator existing = mRunningAnimations.get(target); 443 if (existing != null) { 444 // Cancel animation. Since its already started, send animation end to client. 445 if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target); 446 existing.cancelAndCallAnimationEnd(); 447 } 448 } 449 450 @VisibleForTesting animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, boolean moveFromFullscreen, boolean moveToFullscreen, @AnimationType int animationType)451 BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, 452 int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, 453 boolean moveFromFullscreen, boolean moveToFullscreen, 454 @AnimationType int animationType) { 455 final BoundsAnimator existing = mRunningAnimations.get(target); 456 457 if (isRunningFadeInAnimation(target) && from.width() == to.width() 458 && from.height() == to.height()) { 459 animationType = FADE_IN; 460 } 461 final boolean replacing = existing != null; 462 @SchedulePipModeChangedState int prevSchedulePipModeChangedState = 463 NO_PIP_MODE_CHANGED_CALLBACKS; 464 465 if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to 466 + " schedulePipModeChangedState=" + schedulePipModeChangedState 467 + " replacing=" + replacing); 468 469 Rect frozenTask = new Rect(); 470 if (replacing) { 471 if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen) 472 && (!moveFromFullscreen || existing.mMoveFromFullscreen)) { 473 // Just let the current animation complete if it has the same destination as the 474 // one we are trying to start, and, if moveTo/FromFullscreen was requested, already 475 // has that flag set. 476 if (DEBUG) Slog.d(TAG, "animateBounds: same destination and moveTo/From flags as " 477 + "existing=" + existing + ", ignoring..."); 478 return existing; 479 } 480 481 // Save the previous state 482 prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState; 483 484 // Update the PiP callback states if we are replacing the animation 485 if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { 486 if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { 487 if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep" 488 + " existing deferred state"); 489 } else { 490 if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback" 491 + " on start already processed, schedule deferred update on end"); 492 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; 493 } 494 } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) { 495 if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { 496 if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled," 497 + " callback on start will be processed"); 498 } else { 499 if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep" 500 + " existing deferred state"); 501 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; 502 } 503 } 504 505 // We need to keep the previous moveTo/FromFullscreen flag, unless the new animation 506 // specifies a direction. 507 if (!moveFromFullscreen && !moveToFullscreen) { 508 moveToFullscreen = existing.mMoveToFullscreen; 509 moveFromFullscreen = existing.mMoveFromFullscreen; 510 } 511 512 // We are in the middle of an existing animation, so that this new animation may 513 // start from an interpolated bounds. We should keep using the existing frozen task 514 // width/height for consistent configurations. 515 frozenTask.set(0, 0, existing.mFrozenTaskWidth, existing.mFrozenTaskHeight); 516 517 // Since we are replacing, we skip both animation start and end callbacks 518 existing.cancel(); 519 } 520 if (animationType == FADE_IN) { 521 target.setPinnedStackSize(to, null); 522 } 523 524 final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to, 525 schedulePipModeChangedState, prevSchedulePipModeChangedState, 526 moveFromFullscreen, moveToFullscreen, frozenTask); 527 mRunningAnimations.put(target, animator); 528 animator.setFloatValues(0f, 1f); 529 animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION 530 : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION) 531 * DEBUG_ANIMATION_SLOW_DOWN_FACTOR); 532 animator.setInterpolator(mFastOutSlowInInterpolator); 533 animator.start(); 534 return animator; 535 } 536 setAnimationType(@nimationType int animationType)537 public void setAnimationType(@AnimationType int animationType) { 538 mAnimationType = animationType; 539 } 540 541 /** return the current animation type. */ getAnimationType()542 public @AnimationType int getAnimationType() { 543 @AnimationType int animationType = mAnimationType; 544 // Default to BOUNDS. 545 mAnimationType = BOUNDS; 546 return animationType; 547 } 548 getHandler()549 public Handler getHandler() { 550 return mHandler; 551 } 552 onAllWindowsDrawn()553 public void onAllWindowsDrawn() { 554 if (DEBUG) Slog.d(TAG, "onAllWindowsDrawn:"); 555 mHandler.post(this::resume); 556 } 557 isRunningFadeInAnimation(final BoundsAnimationTarget target)558 private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) { 559 final BoundsAnimator existing = mRunningAnimations.get(target); 560 return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted(); 561 } 562 resume()563 private void resume() { 564 for (int i = 0; i < mRunningAnimations.size(); i++) { 565 final BoundsAnimator b = mRunningAnimations.valueAt(i); 566 b.resume(); 567 } 568 } 569 updateBooster()570 private void updateBooster() { 571 WindowManagerService.sThreadPriorityBooster.setBoundsAnimationRunning( 572 !mRunningAnimations.isEmpty()); 573 } 574 } 575