Fixing task open animation in split screen mode

Removing duplicate logic for task bounds calculation and using
ClipAnimationHelper everywhere

Bug: 78126501
Change-Id: I5ed8b2f9162f0bd6144a849f5b677963776d325c
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index bd1cdc6..85ccf29 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -64,6 +64,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.views.RecentsView;
@@ -233,12 +234,16 @@
             return false;
         }
 
+        ClipAnimationHelper helper = new ClipAnimationHelper();
+        target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
+                .setDuration(RECENTS_LAUNCH_DURATION));
+
         Animator childStateAnimation = null;
         // Found a visible recents task that matches the opening app, lets launch the app from there
         Animator launcherAnim;
         final AnimatorListenerAdapter windowAnimEndListener;
         if (launcherClosing) {
-            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
+            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView, helper);
             launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
             launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
 
@@ -258,9 +263,6 @@
                 }
             };
         }
-
-        target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets)
-                .setDuration(RECENTS_LAUNCH_DURATION));
         target.play(launcherAnim);
 
         // Set the current animation first, before adding windowAnimEndListener. Setting current
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index b780a3e..1359b3a 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -51,6 +51,7 @@
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsRootView;
+import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -193,13 +194,14 @@
             RemoteAnimationTargetCompat[] targets) {
         AnimatorSet target = new AnimatorSet();
         boolean activityClosing = taskIsATargetWithMode(targets, getTaskId(), MODE_CLOSING);
-        target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets)
+        ClipAnimationHelper helper = new ClipAnimationHelper();
+        target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets, helper)
                 .setDuration(RECENTS_LAUNCH_DURATION));
 
         // Found a visible recents task that matches the opening app, lets launch the app from there
         if (activityClosing) {
             Animator adjacentAnimation = mFallbackRecentsView
-                    .createAdjacentPageAnimForTaskLaunch(taskView);
+                    .createAdjacentPageAnimForTaskLaunch(taskView, helper);
             adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
             adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION);
             adjacentAnimation.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java b/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java
deleted file mode 100644
index fdeb0c1..0000000
--- a/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import android.graphics.Rect;
-
-import com.android.launcher3.Utilities;
-
-/**
- * Helper class to interpolate the animation between a task view representation and an actual
- * window.
- */
-public class RecentsAnimationInterpolator {
-
-    public static class TaskWindowBounds {
-        public float taskScale = 1f;
-        public float taskX = 0f;
-        public float taskY = 0f;
-
-        public float winScale = 1f;
-        public float winX = 0f;
-        public float winY = 0f;
-        public Rect winCrop = new Rect();
-
-        @Override
-        public String toString() {
-            return "taskScale=" + taskScale + " taskX=" + taskX + " taskY=" + taskY
-                    + " winScale=" + winScale + " winX=" + winX + " winY=" + winY
-                    + " winCrop=" + winCrop;
-        }
-    }
-
-    private TaskWindowBounds mTmpTaskWindowBounds = new TaskWindowBounds();
-    private Rect mTmpInsets = new Rect();
-
-    private Rect mWindow;
-    private Rect mInsetWindow;
-    private Rect mInsets;
-    private Rect mTask;
-    private Rect mTaskInsets;
-    private Rect mThumbnail;
-
-    private float mInitialTaskScale;
-    private float mInitialTaskTranslationX;
-    private float mFinalTaskScale;
-    private Rect mScaledTask;
-    private Rect mTargetTask;
-    private Rect mSrcWindow;
-
-    public RecentsAnimationInterpolator(Rect window, Rect insets, Rect task, Rect taskInsets,
-            float taskScale, float taskTranslationX) {
-        mWindow = window;
-        mInsets = insets;
-        mTask = task;
-        mTaskInsets = taskInsets;
-        mInsetWindow = new Rect(window);
-        Utilities.insetRect(mInsetWindow, insets);
-
-        mThumbnail = new Rect(task);
-        Utilities.insetRect(mThumbnail, taskInsets);
-        mInitialTaskScale = taskScale;
-        mInitialTaskTranslationX = taskTranslationX;
-        mFinalTaskScale = (float) mInsetWindow.width() / mThumbnail.width();
-        mScaledTask = new Rect(task);
-        Utilities.scaleRectAboutCenter(mScaledTask, mFinalTaskScale);
-        Rect finalScaledTaskInsets = new Rect(taskInsets);
-        Utilities.scaleRect(finalScaledTaskInsets, mFinalTaskScale);
-        mTargetTask = new Rect(mInsetWindow);
-        mTargetTask.offsetTo(window.left + insets.left - finalScaledTaskInsets.left,
-                window.top + insets.top - finalScaledTaskInsets.top);
-
-        float initialWinScale = 1f / mFinalTaskScale;
-        Rect scaledWindow = new Rect(mInsetWindow);
-        Utilities.scaleRectAboutCenter(scaledWindow, initialWinScale);
-        Rect scaledInsets = new Rect(insets);
-        Utilities.scaleRect(scaledInsets, initialWinScale);
-        mSrcWindow = new Rect(scaledWindow);
-        mSrcWindow.offsetTo(mThumbnail.left - scaledInsets.left,
-                mThumbnail.top - scaledInsets.top);
-    }
-
-    public TaskWindowBounds interpolate(float t) {
-        mTmpTaskWindowBounds.taskScale = Utilities.mapRange(t,
-                mInitialTaskScale, mFinalTaskScale);
-        mTmpTaskWindowBounds.taskX = Utilities.mapRange(t,
-                mInitialTaskTranslationX, mTargetTask.left - mScaledTask.left);
-        mTmpTaskWindowBounds.taskY = Utilities.mapRange(t,
-                0, mTargetTask.top - mScaledTask.top);
-
-        float taskScale = Utilities.mapRange(t, 1, mFinalTaskScale);
-        mTmpTaskWindowBounds.winScale = taskScale / mFinalTaskScale;
-        mTmpTaskWindowBounds.winX = Utilities.mapRange(t,
-                mSrcWindow.left, 0);
-        mTmpTaskWindowBounds.winY = Utilities.mapRange(t,
-                mSrcWindow.top, 0);
-
-        mTmpInsets.set(mInsets);
-        Utilities.scaleRect(mTmpInsets, (1f - t));
-        mTmpTaskWindowBounds.winCrop.set(mWindow);
-        Utilities.insetRect(mTmpTaskWindowBounds.winCrop, mTmpInsets);
-
-        return mTmpTaskWindowBounds;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index 559236d..f9628d7 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -27,8 +27,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.graphics.Matrix;
-import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.Surface;
@@ -36,17 +35,17 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
-import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
+import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.MultiValueUpdateListener;
-import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
 
 /**
  * Contains helpful methods for retrieving data from {@link Task}s.
@@ -136,74 +135,64 @@
         return taskView;
     }
 
-
     /**
      * @return Animator that controls the window of the opening targets for the recents launch
      * animation.
      */
     public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
-            RemoteAnimationTargetCompat[] targets) {
-        final RecentsAnimationInterpolator recentsInterpolator = v.getRecentsInterpolator();
-
-        Rect crop = new Rect();
-        Matrix matrix = new Matrix();
-
-        ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+            RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
+        final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
         appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
         appAnimator.addUpdateListener(new MultiValueUpdateListener() {
 
             // Defer fading out the view until after the app window gets faded in
-            FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
-            FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
+            final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
+            final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
 
-            boolean isFirstFrame = true;
+            final RemoteAnimationTargetSet mTargetSet;
+
+            final RectF mThumbnailRect;
+            private Surface mSurface;
+            private long mFrameNumber;
+
+            {
+                mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
+                inOutHelper.setTaskTransformCallback((t, app) -> {
+                    t.setAlpha(app.leash, mTaskAlpha.value);
+
+                    if (!skipViewChanges) {
+                        t.deferTransactionUntil(app.leash, mSurface, mFrameNumber);
+                    }
+                });
+
+                inOutHelper.prepareAnimation(true /* isOpening */);
+                inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
+                        mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]);
+
+                mThumbnailRect = new RectF(inOutHelper.getTargetRect());
+                mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
+                Utilities.scaleRectFAboutCenter(mThumbnailRect, 1 / v.getScaleX());
+            }
 
             @Override
             public void onUpdate(float percent) {
-                final Surface surface = getSurface(v);
-                final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
-                if (frameNumber == -1) {
+                mSurface = getSurface(v);
+                mFrameNumber = mSurface != null ? getNextFrameNumber(mSurface) : -1;
+                if (mFrameNumber == -1) {
                     // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
                     Log.w(TAG, "Failed to animate, surface got destroyed.");
                     return;
                 }
-                TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
 
+                RectF taskBounds = inOutHelper.applyTransform(mTargetSet, 1 - percent);
                 if (!skipViewChanges) {
-                    v.setScaleX(tw.taskScale);
-                    v.setScaleY(tw.taskScale);
-                    v.setTranslationX(tw.taskX);
-                    v.setTranslationY(tw.taskY);
+                    float scale = taskBounds.width() / mThumbnailRect.width();
+                    v.setScaleX(scale);
+                    v.setScaleY(scale);
+                    v.setTranslationX(taskBounds.centerX() - mThumbnailRect.centerX());
+                    v.setTranslationY(taskBounds.centerY() - mThumbnailRect.centerY());
                     v.setAlpha(mViewAlpha.value);
                 }
-
-                matrix.setScale(tw.winScale, tw.winScale);
-                matrix.postTranslate(tw.winX, tw.winY);
-                crop.set(tw.winCrop);
-
-                TransactionCompat t = new TransactionCompat();
-                if (isFirstFrame) {
-                    RemoteAnimationProvider.prepareTargetsForFirstFrame(targets, t, MODE_OPENING);
-                    isFirstFrame = false;
-                }
-                for (RemoteAnimationTargetCompat target : targets) {
-                    if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
-                        t.setAlpha(target.leash, mTaskAlpha.value);
-
-                        // TODO: This isn't correct at the beginning of the animation, but better
-                        // than nothing.
-                        matrix.postTranslate(target.position.x, target.position.y);
-                        t.setMatrix(target.leash, matrix);
-                        t.setWindowCrop(target.leash, crop);
-
-                        if (!skipViewChanges) {
-                            t.deferTransactionUntil(target.leash, surface, frameNumber);
-                        }
-                    }
-                }
-                t.apply();
-
-                matrix.reset();
             }
         });
         return appAnimator;
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index 8b9903d..5e21467 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -20,13 +20,16 @@
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
+import android.annotation.TargetApi;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Matrix.ScaleToFit;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.os.Build;
 import android.os.RemoteException;
+import android.support.annotation.Nullable;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
@@ -42,9 +45,12 @@
 import com.android.systemui.shared.system.TransactionCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
+import java.util.function.BiConsumer;
+
 /**
  * Utility class to handle window clip animation
  */
+@TargetApi(Build.VERSION_CODES.P)
 public class ClipAnimationHelper {
 
     // The bounds of the source app in device coordinates
@@ -78,13 +84,21 @@
     // Wether or not applyTransform has been called yet since prepareAnimation()
     private boolean mIsFirstFrame = true;
 
-    public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
-        mHomeStackBounds.set(homeStackBounds);
+    private BiConsumer<TransactionCompat, RemoteAnimationTargetCompat> mTaskTransformCallback =
+            (t, a) -> { };
+
+    private void updateSourceStack(RemoteAnimationTargetCompat target) {
         mSourceInsets.set(target.contentInsets);
         mSourceStackBounds.set(target.sourceContainerBounds);
 
         // TODO: Should sourceContainerBounds already have this offset?
         mSourceStackBounds.offsetTo(target.position.x, target.position.y);
+
+    }
+
+    public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
+        mHomeStackBounds.set(homeStackBounds);
+        updateSourceStack(target);
     }
 
     public void updateTargetRect(Rect targetRect) {
@@ -116,7 +130,7 @@
         mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
     }
 
-    public void applyTransform(RemoteAnimationTargetSet targetSet, float progress) {
+    public RectF applyTransform(RemoteAnimationTargetSet targetSet, float progress) {
         RectF currentRect;
         mTmpRectF.set(mTargetRect);
         Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
@@ -153,8 +167,16 @@
                     || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
                 transaction.setAlpha(app.leash, 1 - progress);
             }
+
+            mTaskTransformCallback.accept(transaction, app);
         }
         transaction.apply();
+        return currentRect;
+    }
+
+    public void setTaskTransformCallback
+            (BiConsumer<TransactionCompat, RemoteAnimationTargetCompat> callback) {
+        mTaskTransformCallback = callback;
     }
 
     public void offsetTarget(float scale, float offsetX, float offsetY) {
@@ -165,6 +187,11 @@
     }
 
     public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
+        fromTaskThumbnailView(ttv, rv, null);
+    }
+
+    public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
+            @Nullable RemoteAnimationTargetCompat target) {
         BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
         BaseDragLayer dl = activity.getDragLayer();
 
@@ -173,7 +200,9 @@
         mHomeStackBounds.set(0, 0, dl.getWidth(), dl.getHeight());
         mHomeStackBounds.offset(pos[0], pos[1]);
 
-        if (rv.shouldUseMultiWindowTaskSizeStrategy()) {
+        if (target != null) {
+            updateSourceStack(target);
+        } else  if (rv.shouldUseMultiWindowTaskSizeStrategy()) {
             updateStackBoundsToMultiWindowTaskSize(activity);
         } else {
             mSourceStackBounds.set(mHomeStackBounds);
@@ -226,7 +255,6 @@
                 insets.top + fullDp.availableHeightPx - taskHeight);
     }
 
-
     public void drawForProgress(TaskThumbnailView ttv, Canvas canvas, float progress) {
         RectF currentRect =  mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
         canvas.translate(mSourceStackBounds.left - mHomeStackBounds.left,
@@ -244,4 +272,12 @@
                 ttv.getMeasuredHeight() + mSourceWindowClipInsets.bottom * insetProgress,
                 ttv.getCornerRadius() * progress);
     }
+
+    public RectF getTargetRect() {
+        return mTargetRect;
+    }
+
+    public RectF getSourceRect() {
+        return mSourceRect;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java b/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
index e1910a6..8549df3 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
@@ -87,6 +87,10 @@
         canvas.restore();
     }
 
+    public ClipAnimationHelper getClipAnimationHelper() {
+        return mClipAnimationHelper;
+    }
+
     @Override
     public void setAlpha(int i) { }
 
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 90749eb..e237500 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.LayoutUtils;
 
 /**
@@ -116,8 +117,9 @@
      * Animates adjacent tasks and translate hotseat off screen as well.
      */
     @Override
-    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
-        AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv);
+    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv,
+            ClipAnimationHelper helper) {
+        AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv, helper);
 
         if (!OverviewInteractionState.getInstance(mActivity).isSwipeUpGestureEnabled()) {
             // Hotseat doesn't move when opening recents with the button,
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 7d707e3..b0185e3 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -65,9 +65,9 @@
 import com.android.launcher3.util.Themes;
 import com.android.quickstep.OverviewCallbacks;
 import com.android.quickstep.QuickScrubController;
-import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.TaskUtils;
+import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.TaskViewDrawable;
 import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.shared.recents.model.RecentsTaskLoader;
@@ -1085,17 +1085,18 @@
      * If launching one of the adjacent tasks, parallax the center task and other adjacent task
      * to the right.
      */
-    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
+    public AnimatorSet createAdjacentPageAnimForTaskLaunch(
+            TaskView tv, ClipAnimationHelper clipAnimationHelper) {
         AnimatorSet anim = new AnimatorSet();
 
         int taskIndex = indexOfChild(tv);
         int centerTaskIndex = getCurrentPage();
         boolean launchingCenterTask = taskIndex == centerTaskIndex;
 
-        TaskWindowBounds endInterpolation = tv.getRecentsInterpolator().interpolate(1);
-        float toScale = endInterpolation.taskScale;
-        float toTranslationY = endInterpolation.taskY;
-
+        float toScale = clipAnimationHelper.getSourceRect().width()
+                / clipAnimationHelper.getTargetRect().width();
+        float toTranslationY = clipAnimationHelper.getSourceRect().centerY()
+                - clipAnimationHelper.getTargetRect().centerY();
         if (launchingCenterTask) {
             TaskView centerTask = getPageAt(centerTaskIndex);
             if (taskIndex - 1 >= 0) {
@@ -1142,11 +1143,10 @@
         if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
             throw new IllegalStateException("Another pending animation is still running");
         }
-        AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv);
 
         int count = getChildCount();
         if (count == 0) {
-            return new PendingAnimation(anim);
+            return new PendingAnimation(new AnimatorSet());
         }
 
         tv.setVisibility(INVISIBLE);
@@ -1157,6 +1157,8 @@
                 ObjectAnimator.ofFloat(drawable, TaskViewDrawable.PROGRESS, 1, 0);
         drawableAnim.setInterpolator(LINEAR);
 
+        AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv,
+                drawable.getClipAnimationHelper());
         anim.play(drawableAnim);
         anim.setDuration(duration);
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index a9b24e5..93bdab2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -26,7 +26,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Outline;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.AttributeSet;
@@ -39,11 +38,9 @@
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.RecentsAnimationInterpolator;
 import com.android.quickstep.TaskSystemShortcut;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.views.RecentsView.PageCallbacks;
@@ -231,6 +228,13 @@
         setScaleY(mCurveScale);
     }
 
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        setPivotX((right - left) * 0.5f);
+        setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
+    }
+
     public float getCurveScaleForInterpolation(float linearInterpolation) {
         float curveInterpolation = CURVE_INTERPOLATOR.getInterpolation(linearInterpolation);
         return getCurveScaleForCurveInterpolation(curveInterpolation);
@@ -250,23 +254,6 @@
         return false;
     }
 
-    public RecentsAnimationInterpolator getRecentsInterpolator() {
-        Rect taskViewBounds = new Rect();
-        BaseDraggingActivity activity = BaseDraggingActivity.fromContext(getContext());
-        DeviceProfile dp = activity.getDeviceProfile();
-        activity.getDragLayer().getDescendantRectRelativeToSelf(this, taskViewBounds);
-
-        // TODO: Use the actual target insets instead of the current thumbnail insets in case the
-        // device state has changed
-        return new RecentsAnimationInterpolator(
-                new Rect(0, 0, dp.widthPx, dp.heightPx),
-                getThumbnail().getInsets(),
-                taskViewBounds,
-                new Rect(0, getThumbnail().getTop(), 0, 0),
-                getScaleX(),
-                getTranslationX());
-    }
-
     private static final class TaskOutlineProvider extends ViewOutlineProvider {
 
         private final int mMarginTop;