1 /* 2 * Copyright (C) 2020 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 package com.android.quickstep; 17 18 import static com.android.app.animation.Interpolators.ACCELERATE_1_5; 19 import static com.android.app.animation.Interpolators.LINEAR; 20 import static com.android.launcher3.Flags.enableAdditionalHomeAnimations; 21 import static com.android.launcher3.PagedView.INVALID_PAGE; 22 23 import android.animation.Animator; 24 import android.content.Context; 25 import android.graphics.Matrix; 26 import android.graphics.Matrix.ScaleToFit; 27 import android.graphics.Rect; 28 import android.graphics.RectF; 29 import android.util.Log; 30 import android.view.RemoteAnimationTarget; 31 32 import androidx.annotation.NonNull; 33 import androidx.annotation.Nullable; 34 import androidx.annotation.UiThread; 35 36 import com.android.launcher3.DeviceProfile; 37 import com.android.launcher3.Utilities; 38 import com.android.launcher3.anim.AnimatedFloat; 39 import com.android.launcher3.anim.AnimationSuccessListener; 40 import com.android.launcher3.anim.AnimatorPlaybackController; 41 import com.android.launcher3.anim.PendingAnimation; 42 import com.android.launcher3.touch.PagedOrientationHandler; 43 import com.android.launcher3.views.ClipIconView; 44 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle; 45 import com.android.quickstep.orientation.RecentsPagedOrientationHandler; 46 import com.android.quickstep.util.AnimatorControllerWithResistance; 47 import com.android.quickstep.util.RectFSpringAnim; 48 import com.android.quickstep.util.RectFSpringAnim.DefaultSpringConfig; 49 import com.android.quickstep.util.RectFSpringAnim.TaskbarHotseatSpringConfig; 50 import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties; 51 import com.android.quickstep.util.TaskViewSimulator; 52 import com.android.quickstep.util.TransformParams; 53 import com.android.quickstep.util.TransformParams.BuilderProxy; 54 import com.android.quickstep.views.RecentsView; 55 import com.android.quickstep.views.TaskView; 56 57 import java.util.Arrays; 58 import java.util.function.Consumer; 59 60 public abstract class SwipeUpAnimationLogic implements 61 RecentsAnimationCallbacks.RecentsAnimationListener { 62 63 protected static final Rect TEMP_RECT = new Rect(); 64 protected final RemoteTargetGluer mTargetGluer; 65 66 protected DeviceProfile mDp; 67 68 protected final Context mContext; 69 protected final GestureState mGestureState; 70 71 protected RemoteTargetHandle[] mRemoteTargetHandles; 72 73 // Shift in the range of [0, 1]. 74 // 0 => preview snapShot is completely visible, and hotseat is completely translated down 75 // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely 76 // visible. 77 protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::onCurrentShiftUpdated); 78 protected float mCurrentDisplacement; 79 80 // The distance needed to drag to reach the task size in recents. 81 protected int mTransitionDragLength; 82 // How much further we can drag past recents, as a factor of mTransitionDragLength. 83 protected float mDragLengthFactor = 1; 84 85 protected boolean mIsSwipeForSplit; 86 SwipeUpAnimationLogic(Context context, GestureState gestureState)87 public SwipeUpAnimationLogic(Context context, GestureState gestureState) { 88 mContext = context; 89 mGestureState = gestureState; 90 updateIsGestureForSplit(TopTaskTracker.INSTANCE.get(context) 91 .getRunningSplitTaskIds().length); 92 93 mTargetGluer = new RemoteTargetGluer(mContext, mGestureState.getContainerInterface()); 94 mRemoteTargetHandles = mTargetGluer.getRemoteTargetHandles(); 95 RotationTouchHelper rotationTouchHelper = RotationTouchHelper.INSTANCE.get(context); 96 runActionOnRemoteHandles(remoteTargetHandle -> 97 remoteTargetHandle.getTaskViewSimulator().getOrientationState().update( 98 rotationTouchHelper.getCurrentActiveRotation(), 99 rotationTouchHelper.getDisplayRotation() 100 )); 101 } 102 initTransitionEndpoints(DeviceProfile dp)103 protected void initTransitionEndpoints(DeviceProfile dp) { 104 mDp = dp; 105 mTransitionDragLength = mGestureState.getContainerInterface() 106 .getSwipeUpDestinationAndLength(dp, mContext, TEMP_RECT, 107 mRemoteTargetHandles[0].getTaskViewSimulator().getOrientationState() 108 .getOrientationHandler()); 109 mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; 110 111 for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) { 112 PendingAnimation pendingAnimation = new PendingAnimation(mTransitionDragLength * 2); 113 TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator(); 114 taskViewSimulator.setDp(dp); 115 taskViewSimulator.addAppToCarouselAnim(pendingAnimation, LINEAR); 116 AnimatorPlaybackController playbackController = 117 pendingAnimation.createPlaybackController(); 118 119 remoteHandle.setPlaybackController(AnimatorControllerWithResistance.createForRecents( 120 playbackController, mContext, taskViewSimulator.getOrientationState(), 121 mDp, taskViewSimulator.recentsViewScale, AnimatedFloat.VALUE, 122 taskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE 123 )); 124 } 125 } 126 127 @UiThread updateDisplacement(float displacement)128 public void updateDisplacement(float displacement) { 129 // We are moving in the negative x/y direction 130 displacement = overrideDisplacementForTransientTaskbar(-displacement); 131 mCurrentDisplacement = displacement; 132 133 float shift; 134 if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) { 135 shift = mDragLengthFactor; 136 } else { 137 float translation = Math.max(displacement, 0); 138 shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength; 139 } 140 141 mCurrentShift.updateValue(shift); 142 } 143 144 /** 145 * When Transient Taskbar is enabled, subclasses can override the displacement to keep the app 146 * window at the bottom of the screen while taskbar is being swiped in. 147 * @param displacement The distance the user has swiped up from the bottom of the screen. This 148 * value will be positive unless the user swipe downwards. 149 * @return the overridden displacement. 150 */ overrideDisplacementForTransientTaskbar(float displacement)151 protected float overrideDisplacementForTransientTaskbar(float displacement) { 152 return displacement; 153 } 154 155 /** 156 * Called when the value of {@link #mCurrentShift} changes 157 */ 158 @UiThread onCurrentShiftUpdated()159 public abstract void onCurrentShiftUpdated(); 160 getOrientationHandler()161 protected RecentsPagedOrientationHandler getOrientationHandler() { 162 // OrientationHandler should be independent of remote target, can directly take one 163 return mRemoteTargetHandles[0].getTaskViewSimulator() 164 .getOrientationState().getOrientationHandler(); 165 } 166 167 protected abstract class HomeAnimationFactory { 168 protected float mSwipeVelocity; 169 170 /** 171 * Returns true if we know the home animation involves an item in the hotseat. 172 */ isInHotseat()173 public boolean isInHotseat() { 174 return false; 175 } 176 getWindowTargetRect()177 public @NonNull RectF getWindowTargetRect() { 178 PagedOrientationHandler orientationHandler = getOrientationHandler(); 179 DeviceProfile dp = mDp; 180 final int halfIconSize = dp.iconSizePx / 2; 181 float primaryDimension = orientationHandler 182 .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx); 183 float secondaryDimension = orientationHandler 184 .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx); 185 final float targetX = primaryDimension / 2f; 186 final float targetY = secondaryDimension - dp.hotseatBarSizePx; 187 // Fallback to animate to center of screen. 188 return new RectF(targetX - halfIconSize, targetY - halfIconSize, 189 targetX + halfIconSize, targetY + halfIconSize); 190 } 191 192 /** Returns the corner radius of the window at the end of the animation. */ getEndRadius(RectF cropRectF)193 public float getEndRadius(RectF cropRectF) { 194 return cropRectF.width() / 2f; 195 } 196 createActivityAnimationToHome()197 public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome(); 198 setSwipeVelocity(float velocity)199 public void setSwipeVelocity(float velocity) { 200 mSwipeVelocity = velocity; 201 } 202 playAtomicAnimation(float velocity)203 public void playAtomicAnimation(float velocity) { 204 // No-op 205 } 206 setAnimation(RectFSpringAnim anim)207 public void setAnimation(RectFSpringAnim anim) { } 208 update(RectF currentRect, float progress, float radius, int overlayAlpha)209 public void update(RectF currentRect, float progress, float radius, int overlayAlpha) { } 210 onCancel()211 public void onCancel() { } 212 213 /** 214 * @param progress The progress of the animation to the home screen. 215 * @return The current alpha to set on the animating app window. 216 */ getWindowAlpha(float progress)217 protected float getWindowAlpha(float progress) { 218 // Alpha interpolates between [1, 0] between progress values [start, end] 219 final float start = 0f; 220 final float end = 0.85f; 221 222 if (progress <= start) { 223 return 1f; 224 } 225 if (progress >= end) { 226 return 0f; 227 } 228 return Utilities.mapToRange(progress, start, end, 1, 0, ACCELERATE_1_5); 229 } 230 231 /** 232 * Sets a {@link com.android.launcher3.views.ClipIconView.TaskViewArtist} that should be 233 * used draw a {@link TaskView} during this home animation. 234 */ setTaskViewArtist(ClipIconView.TaskViewArtist taskViewArtist)235 public void setTaskViewArtist(ClipIconView.TaskViewArtist taskViewArtist) { } 236 isAnimationReady()237 public boolean isAnimationReady() { 238 return true; 239 } 240 isAnimatingIntoIcon()241 public boolean isAnimatingIntoIcon() { 242 return false; 243 } 244 245 @Nullable getTargetTaskView()246 public TaskView getTargetTaskView() { 247 return null; 248 } 249 isRtl()250 public boolean isRtl() { 251 return Utilities.isRtl(mContext.getResources()); 252 } 253 isPortrait()254 public boolean isPortrait() { 255 return !mDp.isLandscape && !mDp.isSeascape(); 256 } 257 } 258 259 /** 260 * Update with start progress for window animation to home. 261 * @param outMatrix {@link Matrix} to map a rect in Launcher space to window space. 262 * @param startProgress The progress of {@link #mCurrentShift} to start thw window from. 263 * @return {@link RectF} represents the bounds as starting point in window space. 264 */ updateProgressForStartRect(Matrix[] outMatrix, float startProgress)265 protected RectF[] updateProgressForStartRect(Matrix[] outMatrix, float startProgress) { 266 mCurrentShift.updateValue(startProgress); 267 RectF[] startRects = new RectF[mRemoteTargetHandles.length]; 268 for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length; 269 i < mRemoteTargetHandlesLength; i++) { 270 RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i]; 271 TaskViewSimulator tvs = remoteHandle.getTaskViewSimulator(); 272 tvs.apply(remoteHandle.getTransformParams().setProgress(startProgress)); 273 274 startRects[i] = new RectF(tvs.getCurrentCropRect()); 275 outMatrix[i] = new Matrix(); 276 tvs.applyWindowToHomeRotation(outMatrix[i]); 277 tvs.getCurrentMatrix().mapRect(startRects[i]); 278 } 279 return startRects; 280 } 281 282 /** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */ runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer)283 protected void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) { 284 for (RemoteTargetHandle handle : mRemoteTargetHandles) { 285 consumer.accept(handle); 286 } 287 } 288 289 /** @return only the TaskViewSimulators from {@link #mRemoteTargetHandles} */ getRemoteTaskViewSimulators()290 protected TaskViewSimulator[] getRemoteTaskViewSimulators() { 291 return Arrays.stream(mRemoteTargetHandles) 292 .map(remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()) 293 .toArray(TaskViewSimulator[]::new); 294 } 295 296 /** 297 * Creates an animation that transforms the current app window into the home app. 298 * @param startProgress The progress of {@link #mCurrentShift} to start the window from. 299 * @param homeAnimationFactory The home animation factory. 300 */ createWindowAnimationToHome(float startProgress, HomeAnimationFactory homeAnimationFactory)301 protected RectFSpringAnim[] createWindowAnimationToHome(float startProgress, 302 HomeAnimationFactory homeAnimationFactory) { 303 // TODO(b/195473584) compute separate end targets for different staged split 304 final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); 305 RectFSpringAnim[] out = new RectFSpringAnim[mRemoteTargetHandles.length]; 306 Matrix[] homeToWindowPositionMap = new Matrix[mRemoteTargetHandles.length]; 307 RectF[] startRects = updateProgressForStartRect(homeToWindowPositionMap, startProgress); 308 for (int i = 0, mRemoteTargetHandlesLength = mRemoteTargetHandles.length; 309 i < mRemoteTargetHandlesLength; i++) { 310 RemoteTargetHandle remoteHandle = mRemoteTargetHandles[i]; 311 out[i] = getWindowAnimationToHomeInternal( 312 homeAnimationFactory, 313 targetRect, 314 remoteHandle.getTransformParams(), 315 remoteHandle.getTaskViewSimulator(), 316 startRects[i], 317 homeToWindowPositionMap[i]); 318 } 319 return out; 320 } 321 updateIsGestureForSplit(int targetCount)322 protected void updateIsGestureForSplit(int targetCount) { 323 mIsSwipeForSplit = targetCount > 1; 324 } 325 getWindowAnimationToHomeInternal( HomeAnimationFactory homeAnimationFactory, RectF targetRect, TransformParams transformParams, TaskViewSimulator taskViewSimulator, RectF startRect, Matrix homeToWindowPositionMap)326 private RectFSpringAnim getWindowAnimationToHomeInternal( 327 HomeAnimationFactory homeAnimationFactory, 328 RectF targetRect, 329 TransformParams transformParams, 330 TaskViewSimulator taskViewSimulator, 331 RectF startRect, 332 Matrix homeToWindowPositionMap) { 333 RectF cropRectF = new RectF(taskViewSimulator.getCurrentCropRect()); 334 // Move the startRect to Launcher space as floatingIconView runs in Launcher 335 Matrix windowToHomePositionMap = new Matrix(); 336 337 TaskView targetTaskView = homeAnimationFactory.getTargetTaskView(); 338 if (targetTaskView == null) { 339 // If the start rect ends up overshooting too much to the left/right offscreen, bring it 340 // back to fullscreen. This can happen when the recentsScroll value isn't aligned with 341 // the pageScroll value for a given taskView, see b/228829958#comment12 342 mRemoteTargetHandles[0].getTaskViewSimulator() 343 .getOrientationState() 344 .getOrientationHandler() 345 .fixBoundsForHomeAnimStartRect(startRect, mDp); 346 347 } 348 homeToWindowPositionMap.invert(windowToHomePositionMap); 349 windowToHomePositionMap.mapRect(startRect); 350 RectF invariantStartRect = new RectF(startRect); 351 352 if (targetTaskView != null) { 353 Rect thumbnailBounds = new Rect(); 354 targetTaskView.getThumbnailBounds(thumbnailBounds, /* relativeToDragLayer= */ true); 355 356 invariantStartRect = new RectF(thumbnailBounds); 357 invariantStartRect.offsetTo(startRect.left, thumbnailBounds.top); 358 startRect = new RectF(thumbnailBounds); 359 } 360 361 boolean useTaskbarHotseatParams = mDp.isTaskbarPresent 362 && homeAnimationFactory.isInHotseat(); 363 RectFSpringAnim anim = new RectFSpringAnim(useTaskbarHotseatParams 364 ? new TaskbarHotseatSpringConfig(mContext, startRect, targetRect) 365 : new DefaultSpringConfig(mContext, mDp, startRect, targetRect)); 366 homeAnimationFactory.setAnimation(anim); 367 368 SpringAnimationRunner runner = new SpringAnimationRunner( 369 homeAnimationFactory, 370 cropRectF, 371 homeToWindowPositionMap, 372 transformParams, 373 taskViewSimulator, 374 invariantStartRect); 375 anim.addAnimatorListener(runner); 376 anim.addOnUpdateListener(runner); 377 return anim; 378 } 379 380 protected class SpringAnimationRunner extends AnimationSuccessListener 381 implements RectFSpringAnim.OnUpdateListener, BuilderProxy { 382 383 private static final String TAG = "SpringAnimationRunner"; 384 385 final Rect mCropRect = new Rect(); 386 final Matrix mMatrix = new Matrix(); 387 388 final RectF mWindowCurrentRect = new RectF(); 389 final Matrix mHomeToWindowPositionMap; 390 private final TransformParams mLocalTransformParams; 391 final HomeAnimationFactory mAnimationFactory; 392 393 final AnimatorPlaybackController mHomeAnim; 394 final RectF mCropRectF; 395 396 final float mStartRadius; 397 final float mEndRadius; 398 399 final RectF mRunningTaskViewStartRectF; 400 @Nullable 401 final TaskView mTargetTaskView; 402 final float mRunningTaskViewScrollOffset; 403 final float mTaskViewWidth; 404 final float mTaskViewHeight; 405 final boolean mIsPortrait; 406 final Rect mThumbnailStartBounds = new Rect(); 407 408 // Store the mTargetTaskView view properties onAnimationStart so that we can reset them 409 // when cleaning up. 410 float mTaskViewAlpha; 411 float mTaskViewTranslationX; 412 float mTaskViewTranslationY; 413 float mTaskViewScaleX; 414 float mTaskViewScaleY; 415 SpringAnimationRunner( HomeAnimationFactory factory, RectF cropRectF, Matrix homeToWindowPositionMap, TransformParams transformParams, TaskViewSimulator taskViewSimulator, RectF invariantStartRect)416 SpringAnimationRunner( 417 HomeAnimationFactory factory, 418 RectF cropRectF, 419 Matrix homeToWindowPositionMap, 420 TransformParams transformParams, 421 TaskViewSimulator taskViewSimulator, 422 RectF invariantStartRect) { 423 mAnimationFactory = factory; 424 mHomeAnim = factory.createActivityAnimationToHome(); 425 mCropRectF = cropRectF; 426 mHomeToWindowPositionMap = homeToWindowPositionMap; 427 mLocalTransformParams = transformParams; 428 429 cropRectF.roundOut(mCropRect); 430 431 // End on a "round-enough" radius so that the shape reveal doesn't have to do too much 432 // rounding at the end of the animation. 433 mStartRadius = taskViewSimulator.getCurrentCornerRadius(); 434 mEndRadius = factory.getEndRadius(cropRectF); 435 436 mRunningTaskViewStartRectF = invariantStartRect; 437 mTargetTaskView = factory.getTargetTaskView(); 438 mTaskViewWidth = mTargetTaskView == null ? 0 : mTargetTaskView.getWidth(); 439 mTaskViewHeight = mTargetTaskView == null ? 0 : mTargetTaskView.getHeight(); 440 mIsPortrait = factory.isPortrait(); 441 // Use the running task's start position to determine how much it needs to be offset 442 // to end up offscreen. 443 mRunningTaskViewScrollOffset = factory.isRtl() 444 ? (Math.min(0, -invariantStartRect.right)) 445 : (Math.max(0, mDp.widthPx - invariantStartRect.left)); 446 } 447 448 @Override onUpdate(RectF currentRect, float progress)449 public void onUpdate(RectF currentRect, float progress) { 450 float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius); 451 float alpha = mAnimationFactory.getWindowAlpha(progress); 452 453 mHomeAnim.setPlayFraction(progress); 454 if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) { 455 mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect); 456 mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL); 457 mLocalTransformParams 458 .setTargetAlpha(alpha) 459 .setCornerRadius(cornerRadius); 460 } else { 461 mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, mRunningTaskViewStartRectF); 462 mWindowCurrentRect.offset(mRunningTaskViewScrollOffset * progress, 0f); 463 mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL); 464 mLocalTransformParams.setCornerRadius(mStartRadius); 465 } 466 467 mLocalTransformParams.applySurfaceParams( 468 mLocalTransformParams.createSurfaceParams(this)); 469 470 mAnimationFactory.update( 471 currentRect, 472 progress, 473 mMatrix.mapRadius(cornerRadius), 474 !enableAdditionalHomeAnimations() || mTargetTaskView == null 475 ? 0 : (int) (alpha * 255)); 476 477 if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) { 478 return; 479 } 480 if (mAnimationFactory.isAnimatingIntoIcon() && mAnimationFactory.isAnimationReady()) { 481 mTargetTaskView.setAlpha(0f); 482 return; 483 } 484 mTargetTaskView.setAlpha(mAnimationFactory.isAnimatingIntoIcon() ? 1f : alpha); 485 float startWidth = mThumbnailStartBounds.width(); 486 float startHeight = mThumbnailStartBounds.height(); 487 float currentWidth = currentRect.width(); 488 float currentHeight = currentRect.height(); 489 float scale; 490 491 boolean isStartWidthValid = Float.compare(startWidth, 0f) > 0; 492 boolean isStartHeightValid = Float.compare(startHeight, 0f) > 0; 493 if (isStartWidthValid && isStartHeightValid) { 494 scale = Math.min(currentWidth, currentHeight) / Math.min(startWidth, startHeight); 495 } else { 496 Log.e(TAG, "TaskView starting bounds are invalid: " + mThumbnailStartBounds); 497 if (isStartWidthValid) { 498 scale = currentWidth / startWidth; 499 } else if (isStartHeightValid) { 500 scale = currentHeight / startHeight; 501 } else { 502 scale = 1f; 503 } 504 } 505 506 if (Float.isNaN(scale)) { 507 Log.e(TAG, "Scale is NaN: starting dimensions=[" + startWidth + ", " + startHeight 508 + "], current dimensions=[" + currentWidth + ", " + currentHeight + "]"); 509 } 510 511 mTargetTaskView.setScaleX(scale); 512 mTargetTaskView.setScaleY(scale); 513 mTargetTaskView.setTranslationX( 514 currentRect.centerX() - mThumbnailStartBounds.centerX()); 515 mTargetTaskView.setTranslationY( 516 currentRect.centerY() - mThumbnailStartBounds.centerY()); 517 } 518 519 @Override onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params)520 public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTarget app, 521 TransformParams params) { 522 builder.setMatrix(mMatrix) 523 .setWindowCrop(mCropRect) 524 .setCornerRadius(params.getCornerRadius()); 525 } 526 527 @Override onCancel()528 public void onCancel() { 529 cleanUp(); 530 mAnimationFactory.onCancel(); 531 } 532 533 @Override onAnimationStart(Animator animation)534 public void onAnimationStart(Animator animation) { 535 setUp(); 536 mHomeAnim.dispatchOnStart(); 537 if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) { 538 return; 539 } 540 Rect thumbnailBounds = new Rect(); 541 // Use bounds relative to mTargetTaskView since it will be scaled afterwards 542 mTargetTaskView.getThumbnailBounds(thumbnailBounds); 543 mAnimationFactory.setTaskViewArtist(new ClipIconView.TaskViewArtist( 544 mTargetTaskView::draw, 545 0f, 546 -thumbnailBounds.top, 547 Math.min(mTaskViewHeight, mTaskViewWidth), 548 mIsPortrait)); 549 } 550 setUp()551 private void setUp() { 552 if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) { 553 return; 554 } 555 RecentsView recentsView = mTargetTaskView.getRecentsView(); 556 if (recentsView != null) { 557 recentsView.setOffsetMidpointIndexOverride( 558 recentsView.indexOfChild(mTargetTaskView)); 559 } 560 mTargetTaskView.getThumbnailBounds( 561 mThumbnailStartBounds, /* relativeToDragLayer= */ true); 562 mTaskViewAlpha = mTargetTaskView.getAlpha(); 563 if (mAnimationFactory.isAnimatingIntoIcon()) { 564 return; 565 } 566 mTaskViewTranslationX = mTargetTaskView.getTranslationX(); 567 mTaskViewTranslationY = mTargetTaskView.getTranslationY(); 568 mTaskViewScaleX = mTargetTaskView.getScaleX(); 569 mTaskViewScaleY = mTargetTaskView.getScaleY(); 570 } 571 cleanUp()572 private void cleanUp() { 573 if (!enableAdditionalHomeAnimations() || mTargetTaskView == null) { 574 return; 575 } 576 RecentsView recentsView = mTargetTaskView.getRecentsView(); 577 if (recentsView != null) { 578 recentsView.setOffsetMidpointIndexOverride(INVALID_PAGE); 579 } 580 mTargetTaskView.setAlpha(mTaskViewAlpha); 581 if (!mAnimationFactory.isAnimatingIntoIcon()) { 582 mTargetTaskView.setTranslationX(mTaskViewTranslationX); 583 mTargetTaskView.setTranslationY(mTaskViewTranslationY); 584 mTargetTaskView.setScaleX(mTaskViewScaleX); 585 mTargetTaskView.setScaleY(mTaskViewScaleY); 586 return; 587 } 588 mAnimationFactory.setTaskViewArtist(null); 589 } 590 591 @Override onAnimationSuccess(Animator animator)592 public void onAnimationSuccess(Animator animator) { 593 cleanUp(); 594 mHomeAnim.getAnimationPlayer().end(); 595 } 596 } 597 598 public interface RunningWindowAnim { end()599 void end(); 600 cancel()601 void cancel(); 602 wrap(Animator animator)603 static RunningWindowAnim wrap(Animator animator) { 604 return new RunningWindowAnim() { 605 @Override 606 public void end() { 607 animator.end(); 608 } 609 610 @Override 611 public void cancel() { 612 animator.cancel(); 613 } 614 }; 615 } 616 wrap(RectFSpringAnim rectFSpringAnim)617 static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) { 618 return new RunningWindowAnim() { 619 @Override 620 public void end() { 621 rectFSpringAnim.end(); 622 } 623 624 @Override 625 public void cancel() { 626 rectFSpringAnim.cancel(); 627 } 628 }; 629 } 630 } 631 } 632