• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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