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