6/ Update recents animation classes to have their respective responsibilities

- RecentsAnimationTargets: manages information about the targets only
  RecentsAnimationCallbacks: manages callbacks from WM about the animation
  RecentsAnimationWrapper: manages calls into WM to update the animation
                           (to be renamed accordingly in a follow up CL)
- Create the Callbacks as a part of starting the recents animation, and
  have the callbacks create the controller wrapper and the targets, which
  are both notified to the listeners through the callbacks.
- Instead of passing through a callback for recents animation finished,
  have it be a part of the recents animation callbacks.

Bug: 141886704

Change-Id: I4ff26a175654e82efe059fa74d1f310e93961dc9
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 5d7d2ff..d391c46 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -64,15 +64,16 @@
 import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
 import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.RemoteAnimationTargets;
 import com.android.quickstep.util.RecentsAnimationTargets;
 import com.android.quickstep.util.RecentsAnimationCallbacks.RecentsAnimationListener;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 
+import java.util.ArrayList;
 import java.util.function.Consumer;
 
 /**
@@ -115,7 +116,13 @@
     protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
 
     protected final ActivityInitListener mActivityInitListener;
-    protected final RecentsAnimationWrapper mRecentsAnimationWrapper;
+    protected final InputConsumerController mInputConsumer;
+
+    protected RecentsAnimationWrapper mRecentsAnimationWrapper;
+    protected RecentsAnimationTargets mRecentsAnimationTargets;
+
+    // Callbacks to be made once the recents animation starts
+    private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
 
     protected T mActivity;
     protected Q mRecentsView;
@@ -140,8 +147,7 @@
         mActivityInitListener =
                 mActivityControlHelper.createActivityInitListener(this::onActivityInit);
         mRunningTaskId = runningTaskId;
-        mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
-                this::createNewInputProxyHandler);
+        mInputConsumer = inputConsumer;
         mMode = SysUINavigationMode.getMode(context);
 
         mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
@@ -210,8 +216,8 @@
     protected void linkRecentsViewScroll() {
         SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> {
             mTransformParams.setSyncTransactionApplier(applier);
-            mRecentsAnimationWrapper.runOnInit(() ->
-                    mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier));
+            runOnRecentsAnimationStart(() ->
+                    mRecentsAnimationTargets.addDependentTransactionApplier(applier));
         });
 
         mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
@@ -219,8 +225,10 @@
                 updateFinalShift();
             }
         });
-        mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
         mRecentsView.setAppWindowAnimationHelper(mAppWindowAnimationHelper);
+        runOnRecentsAnimationStart(() ->
+                mRecentsView.setRecentsAnimationTargets(mRecentsAnimationWrapper,
+                        mRecentsAnimationTargets));
     }
 
     protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
@@ -256,8 +264,30 @@
         ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
     }
 
+    /**
+     * Runs the given {@param action} if the recents animation has already started, or queues it to
+     * be run when it is next started.
+     */
+    protected void runOnRecentsAnimationStart(Runnable action) {
+        if (mRecentsAnimationTargets == null) {
+            mRecentsAnimationStartCallbacks.add(action);
+        } else {
+            action.run();
+        }
+    }
+
+    /**
+     * @return whether the recents animation has started and there are valid app targets.
+     */
+    protected boolean hasTargets() {
+        return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
+    }
+
     @Override
-    public void onRecentsAnimationStart(RecentsAnimationTargets targetSet) {
+    public void onRecentsAnimationStart(RecentsAnimationWrapper recentsAnimationController,
+            RecentsAnimationTargets targetSet) {
+        mRecentsAnimationWrapper = recentsAnimationController;
+        mRecentsAnimationTargets = targetSet;
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
         final Rect overviewStackBounds;
         RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
@@ -281,7 +311,25 @@
         mAppWindowAnimationHelper.prepareAnimation(dp, false /* isOpening */);
         initTransitionEndpoints(dp);
 
-        mRecentsAnimationWrapper.setController(targetSet);
+        // Notify when the animation starts
+        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+            for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
+                action.run();
+            }
+            mRecentsAnimationStartCallbacks.clear();
+        }
+    }
+
+    @Override
+    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+        mRecentsAnimationWrapper = null;
+        mRecentsAnimationTargets = null;
+    }
+
+    @Override
+    public void onRecentsAnimationFinished(RecentsAnimationWrapper controller) {
+        mRecentsAnimationWrapper = null;
+        mRecentsAnimationTargets = null;
     }
 
     private Rect getStackBounds(DeviceProfile dp) {
@@ -370,7 +418,7 @@
         mTransformParams.setProgress(shift)
                 .setOffsetX(offsetX)
                 .setOffsetScale(offsetScale)
-                .setTargetSet(mRecentsAnimationWrapper.targetSet)
+                .setTargetSet(mRecentsAnimationTargets)
                 .setLauncherOnTop(true);
         mAppWindowAnimationHelper.applyTransform(mTransformParams);
     }
@@ -388,11 +436,10 @@
      */
     protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
             HomeAnimationFactory homeAnimationFactory) {
-        final RemoteAnimationTargets targetSet = mRecentsAnimationWrapper.targetSet;
         final RectF startRect = new RectF(
                 mAppWindowAnimationHelper.applyTransform(
                         mTransformParams.setProgress(startProgress)
-                                .setTargetSet(targetSet)
+                                .setTargetSet(mRecentsAnimationTargets)
                                 .setLauncherOnTop(false)));
         final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
index 43c4a0b..72aa0e7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -18,6 +18,8 @@
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.os.SystemClock;
 import android.util.Log;
@@ -29,10 +31,14 @@
 
 import com.android.launcher3.util.Preconditions;
 import com.android.quickstep.inputconsumers.InputConsumer;
+import com.android.quickstep.util.RecentsAnimationCallbacks;
 import com.android.quickstep.util.RecentsAnimationTargets;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InputConsumerController;
 
-import java.util.ArrayList;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 /**
@@ -42,56 +48,82 @@
 
     private static final String TAG = "RecentsAnimationWrapper";
 
-    // A list of callbacks to run when we receive the recents animation target. There are different
-    // than the state callbacks as these run on the current worker thread.
-    private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
-
-    public RecentsAnimationTargets targetSet;
+    private final RecentsAnimationControllerCompat mController;
+    private final Consumer<RecentsAnimationWrapper> mOnFinishedListener;
+    private final boolean mShouldMinimizeSplitScreen;
 
     private boolean mWindowThresholdCrossed = false;
 
-    private final InputConsumerController mInputConsumerController;
-    private final Supplier<InputConsumer> mInputProxySupplier;
-
+    private InputConsumerController mInputConsumerController;
+    private Supplier<InputConsumer> mInputProxySupplier;
     private InputConsumer mInputConsumer;
     private boolean mTouchInProgress;
-
     private boolean mFinishPending;
 
-    public RecentsAnimationWrapper(InputConsumerController inputConsumerController,
-            Supplier<InputConsumer> inputProxySupplier) {
-        mInputConsumerController = inputConsumerController;
-        mInputProxySupplier = inputProxySupplier;
+    public RecentsAnimationWrapper(RecentsAnimationControllerCompat controller,
+            boolean shouldMinimizeSplitScreen,
+            Consumer<RecentsAnimationWrapper> onFinishedListener) {
+        mController = controller;
+        mOnFinishedListener = onFinishedListener;
+        mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+
+        setWindowThresholdCrossed(mWindowThresholdCrossed);
     }
 
-    public boolean hasTargets() {
-        return targetSet != null && targetSet.hasTargets();
+    /**
+     * Synchronously takes a screenshot of the task with the given {@param taskId} if the task is
+     * currently being animated.
+     */
+    public ThumbnailData screenshotTask(int taskId) {
+        return mController != null ? mController.screenshotTask(taskId) : null;
+    }
+
+    /**
+     * Indicates that the gesture has crossed the window boundary threshold and system UI can be
+     * update the represent the window behind
+     */
+    public void setWindowThresholdCrossed(boolean windowThresholdCrossed) {
+        if (mWindowThresholdCrossed != windowThresholdCrossed) {
+            mWindowThresholdCrossed = windowThresholdCrossed;
+            UI_HELPER_EXECUTOR.execute(() -> {
+                mController.setAnimationTargetsBehindSystemBars(!windowThresholdCrossed);
+                if (mShouldMinimizeSplitScreen && windowThresholdCrossed) {
+                    // NOTE: As a workaround for conflicting animations (Launcher animating the task
+                    // leash, and SystemUI resizing the docked stack, which resizes the task), we
+                    // currently only set the minimized mode, and not the inverse.
+                    // TODO: Synchronize the minimize animation with the launcher animation
+                    mController.setSplitScreenMinimized(windowThresholdCrossed);
+                }
+            });
+        }
+    }
+
+    /**
+     * Notifies the controller that we want to defer cancel until the next app transition starts.
+     * If {@param screenshot} is set, then we will receive a screenshot on the next
+     * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call
+     * {@link #cleanupScreenshot()} when that screenshot is no longer used.
+     */
+    public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
+        mController.setDeferCancelUntilNextTransition(defer, screenshot);
+    }
+
+    /**
+     * Cleans up the screenshot previously returned from
+     * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}.
+     */
+    public void cleanupScreenshot() {
+        UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
     }
 
     @UiThread
-    public synchronized void setController(RecentsAnimationTargets targetSet) {
-        Preconditions.assertUIThread();
-        this.targetSet = targetSet;
-
-        if (targetSet == null) {
-            return;
-        }
-        targetSet.setWindowThresholdCrossed(mWindowThresholdCrossed);
-
-        if (!mCallbacks.isEmpty()) {
-            for (Runnable action : new ArrayList<>(mCallbacks)) {
-                action.run();
-            }
-            mCallbacks.clear();
-        }
+    public void finishAnimationToHome() {
+        finishAndClear(true /* toRecents */, null, false /* sendUserLeaveHint */);
     }
 
-    public synchronized void runOnInit(Runnable action) {
-        if (targetSet == null) {
-            mCallbacks.add(action);
-        } else {
-            action.run();
-        }
+    @UiThread
+    public void finishAnimationToApp() {
+        finishAndClear(false /* toRecents */, null, false /* sendUserLeaveHint */);
     }
 
     /** See {@link #finish(boolean, Runnable, boolean)} */
@@ -127,34 +159,36 @@
 
     private void finishAndClear(boolean toRecents, Runnable onFinishComplete,
             boolean sendUserLeaveHint) {
-        RecentsAnimationTargets controller = targetSet;
-        targetSet = null;
         disableInputProxy();
-        if (controller != null) {
-            controller.finishController(toRecents, onFinishComplete, sendUserLeaveHint);
-        }
+        finishController(toRecents, onFinishComplete, sendUserLeaveHint);
     }
 
-    public void enableInputConsumer() {
-        if (targetSet != null) {
-            targetSet.enableInputConsumer();
-        }
+    @UiThread
+    public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
+        mOnFinishedListener.accept(this);
+        UI_HELPER_EXECUTOR.execute(() -> {
+            mController.setInputConsumerEnabled(false);
+            mController.finish(toRecents, sendUserLeaveHint);
+            if (callback != null) {
+                MAIN_EXECUTOR.execute(callback);
+            }
+        });
     }
 
     /**
-     * Indicates that the gesture has crossed the window boundary threshold and system UI can be
-     * update the represent the window behind
+     * Enables the input consumer to start intercepting touches in the app window.
      */
-    public void setWindowThresholdCrossed(boolean windowThresholdCrossed) {
-        if (mWindowThresholdCrossed != windowThresholdCrossed) {
-            mWindowThresholdCrossed = windowThresholdCrossed;
-            if (targetSet != null) {
-                targetSet.setWindowThresholdCrossed(windowThresholdCrossed);
-            }
-        }
+    public void enableInputConsumer() {
+        UI_HELPER_EXECUTOR.submit(() -> {
+            mController.hideCurrentInputMethod();
+            mController.setInputConsumerEnabled(true);
+        });
     }
 
-    public void enableInputProxy() {
+    public void enableInputProxy(InputConsumerController inputConsumerController,
+            Supplier<InputConsumer> inputProxySupplier) {
+        mInputProxySupplier = inputProxySupplier;
+        mInputConsumerController = inputConsumerController;
         mInputConsumerController.setInputListener(this::onInputConsumerEvent);
     }
 
@@ -165,7 +199,9 @@
             mInputConsumer.onMotionEvent(dummyCancel);
             dummyCancel.recycle();
         }
-        mInputConsumerController.setInputListener(null);
+        if (mInputConsumerController != null) {
+            mInputConsumerController.setInputListener(null);
+        }
     }
 
     private boolean onInputConsumerEvent(InputEvent ev) {
@@ -214,14 +250,4 @@
 
         return true;
     }
-
-    public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
-        if (targetSet != null) {
-            targetSet.controller.setDeferCancelUntilNextTransition(defer, screenshot);
-        }
-    }
-
-    public RecentsAnimationTargets getController() {
-        return targetSet;
-    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
index 6b98a89..e09c9cb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
@@ -38,11 +38,9 @@
     private OverviewComponentObserver mOverviewComponentObserver;
 
     private RecentsAnimationCallbacks mRecentsAnimationListener;
+    private RecentsAnimationWrapper mLastRecentsAnimationController;
     private RecentsAnimationTargets mLastAnimationTarget;
 
-    // TODO: Remove
-    private Runnable mRecentsAnimationCanceledCallback;
-
     private boolean mLastAnimationCancelled = false;
     private boolean mLastAnimationRunning = false;
 
@@ -57,13 +55,35 @@
     }
 
     @Override
-    public final void onRecentsAnimationStart(RecentsAnimationTargets targetSet) {
+    public final void onRecentsAnimationStart(RecentsAnimationWrapper controller,
+            RecentsAnimationTargets targetSet) {
+        mLastRecentsAnimationController = controller;
         mLastAnimationTarget = targetSet;
 
         mLastAnimationCancelled = false;
         mLastAnimationRunning = true;
     }
 
+    @Override
+    public final void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+        if (thumbnailData != null) {
+            mOverviewComponentObserver.getActivityControlHelper().switchToScreenshot(thumbnailData,
+                    () -> {
+                        mLastRecentsAnimationController.cleanupScreenshot();
+                        clearAnimationState();
+                    });
+        } else {
+            clearAnimationState();
+        }
+    }
+
+    @Override
+    public final void onRecentsAnimationFinished(RecentsAnimationWrapper controller) {
+        if (mLastRecentsAnimationController == controller) {
+            mLastAnimationRunning = false;
+        }
+    }
+
     private void clearAnimationTarget() {
         if (mLastAnimationTarget != null) {
             mLastAnimationTarget.release();
@@ -71,42 +91,23 @@
         }
     }
 
-    @Override
-    public final void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-        if (thumbnailData != null) {
-            mOverviewComponentObserver.getActivityControlHelper().switchToScreenshot(thumbnailData,
-                    () -> {
-                        if (mRecentsAnimationCanceledCallback != null) {
-                            mRecentsAnimationCanceledCallback.run();
-                        }
-                        clearAnimationState();
-                    });
-        } else {
-            clearAnimationState();
-        }
-    }
-
-    public void setRecentsAnimationCanceledCallback(Runnable callback) {
-        mRecentsAnimationCanceledCallback = callback;
-    }
-
     private void clearAnimationState() {
         clearAnimationTarget();
 
         mLastAnimationCancelled = true;
         mLastAnimationRunning = false;
-        mRecentsAnimationCanceledCallback = null;
     }
 
     private void clearListenerState(boolean finishAnimation) {
         if (mRecentsAnimationListener != null) {
             mRecentsAnimationListener.removeListener(this);
-            mRecentsAnimationListener.cancelListener();
-            if (mLastAnimationRunning && mLastAnimationTarget != null) {
+            mRecentsAnimationListener.notifyAnimationCanceled();
+            if (mLastAnimationRunning && mLastRecentsAnimationController != null) {
                 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
                         finishAnimation
-                                ? mLastAnimationTarget::finishAnimation
-                                : mLastAnimationTarget::cancelAnimation);
+                                ? mLastRecentsAnimationController::finishAnimationToHome
+                                : mLastRecentsAnimationController::finishAnimationToApp);
+                mLastRecentsAnimationController = null;
                 mLastAnimationTarget = null;
             }
         }
@@ -116,12 +117,6 @@
         mLastAnimationRunning = false;
     }
 
-    private void onSwipeAnimationFinished(RecentsAnimationTargets targetSet) {
-        if (mLastAnimationTarget == targetSet) {
-            mLastAnimationRunning = false;
-        }
-    }
-
     public RecentsAnimationCallbacks newRecentsAnimationListenerSet() {
         Preconditions.assertUIThread();
 
@@ -137,8 +132,7 @@
         clearListenerState(false /* finishAnimation */);
         boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
                 : mOverviewComponentObserver.getActivityControlHelper().shouldMinimizeSplitScreen();
-        mRecentsAnimationListener = new RecentsAnimationCallbacks(
-                shouldMinimiseSplitScreen, this::onSwipeAnimationFinished);
+        mRecentsAnimationListener = new RecentsAnimationCallbacks(shouldMinimiseSplitScreen);
         mRecentsAnimationListener.addListener(this);
         return mRecentsAnimationListener;
     }
@@ -148,8 +142,9 @@
     }
 
     public void applyActiveRecentsAnimationState(RecentsAnimationListener listener) {
-        if (mLastAnimationTarget != null) {
-            listener.onRecentsAnimationStart(mLastAnimationTarget);
+        if (mLastRecentsAnimationController != null) {
+            listener.onRecentsAnimationStart(mLastRecentsAnimationController,
+                    mLastAnimationTarget);
         } else if (mLastAnimationCancelled) {
             listener.onRecentsAnimationCanceled(null);
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 0700f7b..41d9471 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -414,10 +414,9 @@
                 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we should
                 // not interrupt it. QuickSwitch assumes that interruption can only happen if the
                 // next gesture is also quick switch.
-                mUncheckedConsumer =
-                        new AssistantInputConsumer(this,
-                                mOverviewComponentObserver.getActivityControlHelper(),
-                                InputConsumer.NO_OP, mInputMonitorCompat);
+                mUncheckedConsumer = new AssistantInputConsumer(this,
+                        mOverviewComponentObserver.getActivityControlHelper(),
+                        InputConsumer.NO_OP, mInputMonitorCompat);
             } else {
                 mUncheckedConsumer = InputConsumer.NO_OP;
             }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index b0628e5..69928f3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -291,9 +291,6 @@
         mStateCallback.addCallback(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
                 this::notifyTransitionCancelled);
 
-        mStateCallback.addCallback(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
-                mRecentsAnimationWrapper::enableInputConsumer);
-
         if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             mStateCallback.addChangeHandler(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
                             | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
@@ -417,7 +414,7 @@
     }
 
     private void sendRemoteAnimationsToAnimationFactory() {
-        mAnimationFactory.onRemoteAnimationReceived(mRecentsAnimationWrapper.targetSet);
+        mAnimationFactory.onRemoteAnimationReceived(mRecentsAnimationTargets);
     }
 
     private void initializeLauncherAnimationController() {
@@ -464,9 +461,9 @@
         if (mMode != Mode.NO_BUTTON || mRecentsView == null) {
             return;
         }
-        RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationWrapper.targetSet == null
+        RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets == null
                 ? null
-                : mRecentsAnimationWrapper.targetSet.findTask(mRunningTaskId);
+                : mRecentsAnimationTargets.findTask(mRunningTaskId);
         final boolean recentsAttachedToAppWindow;
         int runningTaskIndex = mRecentsView.getRunningTaskIndex();
         if (mGestureEndTarget != null) {
@@ -548,15 +545,13 @@
 
     @Override
     public void updateFinalShift() {
-
-        RecentsAnimationTargets controller = mRecentsAnimationWrapper.getController();
-        if (controller != null) {
+        if (mRecentsAnimationTargets != null) {
             applyTransformUnchecked();
             updateSysUiFlags(mCurrentShift.value);
         }
 
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (mRecentsAnimationWrapper.getController() != null) {
+            if (mRecentsAnimationTargets != null) {
                 mLiveTileOverlay.update(mAppWindowAnimationHelper.getCurrentRectWithInsets(),
                         mAppWindowAnimationHelper.getCurrentCornerRadius());
             }
@@ -599,17 +594,24 @@
                     : centermostTask.getThumbnail().getSysUiStatusNavFlags();
             boolean useHomeScreenFlags = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
             // We will handle the sysui flags based on the centermost task view.
-            mRecentsAnimationWrapper.setWindowThresholdCrossed(centermostTaskFlags != 0
-                    || useHomeScreenFlags);
+            if (mRecentsAnimationWrapper != null) {
+                mRecentsAnimationWrapper.setWindowThresholdCrossed(centermostTaskFlags != 0
+                        || useHomeScreenFlags);
+            }
             int sysuiFlags = useHomeScreenFlags ? 0 : centermostTaskFlags;
             mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
         }
     }
 
     @Override
-    public void onRecentsAnimationStart(RecentsAnimationTargets targetSet) {
-        super.onRecentsAnimationStart(targetSet);
+    public void onRecentsAnimationStart(RecentsAnimationWrapper controller,
+            RecentsAnimationTargets targetSet) {
         ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targetSet.apps.length);
+        super.onRecentsAnimationStart(controller, targetSet);
+
+        // Only add the callback to enable the input consumer after we actually have the controller
+        mStateCallback.addCallback(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
+                mRecentsAnimationWrapper::enableInputConsumer);
         setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
 
         mPassedOverviewThreshold = false;
@@ -617,7 +619,8 @@
 
     @Override
     public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-        mRecentsAnimationWrapper.setController(null);
+        super.onRecentsAnimationCanceled(thumbnailData);
+        mRecentsView.setRecentsAnimationTargets(null, null);
         mActivityInitListener.unregister();
         setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
         ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
@@ -707,7 +710,7 @@
         final GestureEndTarget endTarget;
         final boolean goingToNewTask;
         if (mRecentsView != null) {
-            if (!mRecentsAnimationWrapper.hasTargets()) {
+            if (!hasTargets()) {
                 // If there are no running tasks, then we can assume that this is a continuation of
                 // the last gesture, but after the recents animation has finished
                 goingToNewTask = true;
@@ -810,8 +813,9 @@
             }
         }
 
-        if (endTarget.isLauncher) {
-            mRecentsAnimationWrapper.enableInputProxy();
+        if (endTarget.isLauncher && mRecentsAnimationWrapper != null) {
+            mRecentsAnimationWrapper.enableInputProxy(mInputConsumer,
+                    this::createNewInputProxyHandler);
         }
 
         if (endTarget == HOME) {
@@ -866,7 +870,7 @@
     @UiThread
     private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
             GestureEndTarget target, PointF velocityPxPerMs) {
-        mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
+        runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration,
                 interpolator, target, velocityPxPerMs));
     }
 
@@ -1108,25 +1112,24 @@
     }
 
     private void switchToScreenshot() {
-        RecentsAnimationTargets controller = mRecentsAnimationWrapper.getController();
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (controller != null) {
+            if (mRecentsAnimationWrapper != null) {
                 // Update the screenshot of the task
                 if (mTaskSnapshot == null) {
-                    mTaskSnapshot = controller.screenshotTask(mRunningTaskId);
+                    mTaskSnapshot = mRecentsAnimationWrapper.screenshotTask(mRunningTaskId);
                 }
                 mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot, false /* refreshNow */);
             }
             setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
-        } else if (!mRecentsAnimationWrapper.hasTargets()) {
+        } else if (!hasTargets()) {
             // If there are no targets, then we don't need to capture anything
             setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
         } else {
             boolean finishTransitionPosted = false;
-            if (controller != null) {
+            if (mRecentsAnimationWrapper != null) {
                 // Update the screenshot of the task
                 if (mTaskSnapshot == null) {
-                    mTaskSnapshot = controller.screenshotTask(mRunningTaskId);
+                    mTaskSnapshot = mRecentsAnimationWrapper.screenshotTask(mRunningTaskId);
                 }
                 final TaskView taskView;
                 if (mGestureEndTarget == HOME) {
@@ -1155,7 +1158,7 @@
     private void finishCurrentTransitionToRecents() {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
-        } else if (!mRecentsAnimationWrapper.hasTargets()) {
+        } else if (!hasTargets()) {
             // If there are no targets, then there is nothing to finish
             setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
         } else {
@@ -1180,8 +1183,10 @@
     private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
         endLauncherTransitionController();
         mActivityControlHelper.onSwipeUpToRecentsComplete(mActivity);
-        mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */,
-                true /* screenshot */);
+        if (mRecentsAnimationWrapper != null) {
+            mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */,
+                    true /* screenshot */);
+        }
         mRecentsView.onSwipeUpAnimationSuccess();
 
         RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 25b4fe4..2d95d75 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -42,6 +42,7 @@
 import com.android.quickstep.LockScreenRecentsActivity;
 import com.android.quickstep.MultiStateCallback;
 import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RecentsAnimationWrapper;
 import com.android.quickstep.SwipeSharedState;
 import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.RecentsAnimationCallbacks;
@@ -91,7 +92,8 @@
 
     private boolean mThresholdCrossed = false;
 
-    private RecentsAnimationTargets mTargetSet;
+    private RecentsAnimationWrapper mRecentsAnimationController;
+    private RecentsAnimationTargets mRecentsAnimationTargets;
 
     public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
             SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
@@ -216,8 +218,10 @@
     }
 
     @Override
-    public void onRecentsAnimationStart(RecentsAnimationTargets targetSet) {
-        mTargetSet = targetSet;
+    public void onRecentsAnimationStart(RecentsAnimationWrapper controller,
+            RecentsAnimationTargets targetSet) {
+        mRecentsAnimationController = controller;
+        mRecentsAnimationTargets = targetSet;
 
         Rect displaySize = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
         RemoteAnimationTargetCompat targetCompat = targetSet.findTask(mRunningTaskId);
@@ -227,7 +231,7 @@
 
         Utilities.scaleRectAboutCenter(displaySize, SCALE_DOWN);
         displaySize.offsetTo(displaySize.left, 0);
-        mTransformParams.setTargetSet(mTargetSet)
+        mTransformParams.setTargetSet(mRecentsAnimationTargets)
                 .setLauncherOnTop(true);
         mAppWindowAnimationHelper.updateTargetRect(displaySize);
         mAppWindowAnimationHelper.applyTransform(mTransformParams);
@@ -237,12 +241,13 @@
 
     @Override
     public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-        mTargetSet = null;
+        mRecentsAnimationController = null;
+        mRecentsAnimationTargets = null;
     }
 
     private void endRemoteAnimation() {
-        if (mTargetSet != null) {
-            mTargetSet.finishController(
+        if (mRecentsAnimationController != null) {
+            mRecentsAnimationController.finishController(
                     false /* toRecents */, null /* callback */, false /* sendUserLeaveHint */);
         }
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
index 59efb2d..4d9ed53 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
@@ -45,6 +45,7 @@
 import com.android.quickstep.MultiStateCallback;
 import com.android.quickstep.OverviewComponentObserver;
 import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.RecentsAnimationWrapper;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.SwipeSharedState;
 import com.android.quickstep.fallback.FallbackRecentsView;
@@ -157,7 +158,7 @@
     }
 
     private void onLauncherAlphaChanged() {
-        if (mRecentsAnimationWrapper.targetSet != null && mEndTarget == null) {
+        if (mRecentsAnimationTargets != null && mEndTarget == null) {
             applyTransformUnchecked();
         }
     }
@@ -231,9 +232,11 @@
     @Override
     public void updateFinalShift() {
         mTransformParams.setProgress(mCurrentShift.value);
-        mRecentsAnimationWrapper.setWindowThresholdCrossed(!mInQuickSwitchMode
-                && (mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD));
-        if (mRecentsAnimationWrapper.targetSet != null) {
+        if (mRecentsAnimationWrapper != null) {
+            mRecentsAnimationWrapper.setWindowThresholdCrossed(!mInQuickSwitchMode
+                    && (mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD));
+        }
+        if (mRecentsAnimationTargets != null) {
             applyTransformUnchecked();
         }
     }
@@ -333,8 +336,7 @@
                     break;
                 }
 
-                ThumbnailData thumbnail =
-                        mRecentsAnimationWrapper.targetSet.controller.screenshotTask(mRunningTaskId);
+                ThumbnailData thumbnail = mRecentsAnimationWrapper.screenshotTask(mRunningTaskId);
                 mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */,
                         false /* screenshot */);
 
@@ -348,7 +350,7 @@
                 Intent intent = new Intent(mOverviewComponentObserver.getOverviewIntent())
                         .putExtras(extras);
                 mContext.startActivity(intent, options.toBundle());
-                mRecentsAnimationWrapper.targetSet.controller.cleanupScreenshot();
+                mRecentsAnimationWrapper.cleanupScreenshot();
                 break;
             }
             case NEW_TASK: {
@@ -364,7 +366,7 @@
         if (mInQuickSwitchMode) {
             // Recalculate the end target, some views might have been initialized after
             // gesture has ended.
-            if (mRecentsView == null || !mRecentsAnimationWrapper.hasTargets()) {
+            if (mRecentsView == null || !hasTargets()) {
                 mEndTarget = LAST_TASK;
             } else {
                 final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
@@ -414,8 +416,9 @@
     }
 
     @Override
-    public void onRecentsAnimationStart(RecentsAnimationTargets targetSet) {
-        super.onRecentsAnimationStart(targetSet);
+    public void onRecentsAnimationStart(RecentsAnimationWrapper controller,
+            RecentsAnimationTargets targetSet) {
+        super.onRecentsAnimationStart(controller, targetSet);
         mRecentsAnimationWrapper.enableInputConsumer();
 
         if (mRunningOverHome) {
@@ -428,7 +431,7 @@
 
     @Override
     public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-        mRecentsAnimationWrapper.setController(null);
+        mRecentsView.setRecentsAnimationTargets(null, null);
         setStateOnUiThread(STATE_HANDLER_INVALIDATED);
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationCallbacks.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationCallbacks.java
index 415f767..824b34d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationCallbacks.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationCallbacks.java
@@ -26,33 +26,30 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Preconditions;
 import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.RecentsAnimationWrapper;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.Set;
-import java.util.function.Consumer;
 
 /**
- * Wrapper around {@link com.android.systemui.shared.system.RecentsAnimationListener} which delegates callbacks to multiple listeners
- * on the main thread
+ * Wrapper around {@link com.android.systemui.shared.system.RecentsAnimationListener} which
+ * delegates callbacks to multiple listeners on the main thread
  */
 public class RecentsAnimationCallbacks implements
         com.android.systemui.shared.system.RecentsAnimationListener {
 
     private final Set<RecentsAnimationListener> mListeners = new ArraySet<>();
     private final boolean mShouldMinimizeSplitScreen;
-    private final Consumer<RecentsAnimationTargets> mOnFinishListener;
-    private RecentsAnimationControllerCompat mController;
+
+    // TODO(141886704): Remove these references when they are no longer needed
+    private RecentsAnimationWrapper mController;
 
     private boolean mCancelled;
 
-    public RecentsAnimationCallbacks(boolean shouldMinimizeSplitScreen,
-            Consumer<RecentsAnimationTargets> onFinishListener) {
+    public RecentsAnimationCallbacks(boolean shouldMinimizeSplitScreen) {
         mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
-        mOnFinishListener = onFinishListener;
-        TouchInteractionService.getSwipeSharedState().setRecentsAnimationCanceledCallback(
-                () -> mController.cleanupScreenshot());
     }
 
     @UiThread
@@ -67,26 +64,9 @@
         mListeners.remove(listener);
     }
 
-    // Called only in R+ platform
-    @BinderThread
-    public final void onAnimationStart(RecentsAnimationControllerCompat controller,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            Rect homeContentInsets, Rect minimizedHomeBounds) {
-        mController = controller;
-        RecentsAnimationTargets targetSet = new RecentsAnimationTargets(controller, appTargets,
-                wallpaperTargets, homeContentInsets, minimizedHomeBounds,
-                mShouldMinimizeSplitScreen, mOnFinishListener);
-
-        if (mCancelled) {
-            targetSet.cancelAnimation();
-        } else {
-            Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
-                for (RecentsAnimationListener listener : getListeners()) {
-                    listener.onRecentsAnimationStart(targetSet);
-                }
-            });
-        }
+    public void notifyAnimationCanceled() {
+        mCancelled = true;
+        onAnimationCanceled(null);
     }
 
     // Called only in Q platform
@@ -99,6 +79,29 @@
                 homeContentInsets, minimizedHomeBounds);
     }
 
+    // Called only in R+ platform
+    @BinderThread
+    public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            Rect homeContentInsets, Rect minimizedHomeBounds) {
+        RecentsAnimationTargets targetSet = new RecentsAnimationTargets(appTargets,
+                wallpaperTargets, homeContentInsets, minimizedHomeBounds);
+        mController = new RecentsAnimationWrapper(animationController, mShouldMinimizeSplitScreen,
+                this::onAnimationFinished);
+
+        if (mCancelled) {
+            Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
+                    mController::finishAnimationToApp);
+        } else {
+            Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+                for (RecentsAnimationListener listener : getListeners()) {
+                    listener.onRecentsAnimationStart(mController, targetSet);
+                }
+            });
+        }
+    }
+
     @BinderThread
     @Override
     public final void onAnimationCanceled(ThumbnailData thumbnailData) {
@@ -109,25 +112,31 @@
         });
     }
 
-    private RecentsAnimationListener[] getListeners() {
-        return mListeners.toArray(new RecentsAnimationListener[mListeners.size()]);
+    private final void onAnimationFinished(RecentsAnimationWrapper controller) {
+        Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+            for (RecentsAnimationListener listener : getListeners()) {
+                listener.onRecentsAnimationFinished(controller);
+            }
+        });
     }
 
-    public void cancelListener() {
-        mCancelled = true;
-        onAnimationCanceled(null);
+    private RecentsAnimationListener[] getListeners() {
+        return mListeners.toArray(new RecentsAnimationListener[mListeners.size()]);
     }
 
     /**
      * Listener for the recents animation callbacks.
      */
     public interface RecentsAnimationListener {
-        void onRecentsAnimationStart(RecentsAnimationTargets targetSet);
+        default void onRecentsAnimationStart(RecentsAnimationWrapper controller,
+                RecentsAnimationTargets targetSet) {}
 
         /**
          * Callback from the system when the recents animation is canceled. {@param thumbnailData}
          * is passed back for rendering screenshot to replace live tile.
          */
-        void onRecentsAnimationCanceled(ThumbnailData thumbnailData);
+        default void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {}
+
+        default void onRecentsAnimationFinished(RecentsAnimationWrapper controller) {}
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationTargets.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationTargets.java
index 187a404..4013e92 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationTargets.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationTargets.java
@@ -15,41 +15,27 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
 import android.graphics.Rect;
 
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
-import java.util.function.Consumer;
-
 /**
  * Extension of {@link RemoteAnimationTargets} with additional information about swipe
  * up animation
  */
 public class RecentsAnimationTargets extends RemoteAnimationTargets {
 
-    private final boolean mShouldMinimizeSplitScreen;
-    private final Consumer<RecentsAnimationTargets> mOnFinishListener;
-
-    public final RecentsAnimationControllerCompat controller;
     public final Rect homeContentInsets;
     public final Rect minimizedHomeBounds;
 
-    public RecentsAnimationTargets(RecentsAnimationControllerCompat controller,
-            RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
-            Rect homeContentInsets, Rect minimizedHomeBounds, boolean shouldMinimizeSplitScreen,
-            Consumer<RecentsAnimationTargets> onFinishListener) {
+    public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps,
+            RemoteAnimationTargetCompat[] wallpapers, Rect homeContentInsets,
+            Rect minimizedHomeBounds) {
         super(apps, wallpapers, MODE_CLOSING);
-        this.controller = controller;
         this.homeContentInsets = homeContentInsets;
         this.minimizedHomeBounds = minimizedHomeBounds;
-        this.mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
-        this.mOnFinishListener = onFinishListener;
     }
 
     public boolean hasTargets() {
@@ -61,52 +47,7 @@
      * the actual recents animation has finished.
      */
     public RecentsAnimationTargets cloneWithoutTargets() {
-        return new RecentsAnimationTargets(controller, new RemoteAnimationTargetCompat[0],
-                new RemoteAnimationTargetCompat[0], homeContentInsets, minimizedHomeBounds,
-                mShouldMinimizeSplitScreen, mOnFinishListener);
-    }
-
-    public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
-        mOnFinishListener.accept(this);
-        UI_HELPER_EXECUTOR.execute(() -> {
-            controller.setInputConsumerEnabled(false);
-            controller.finish(toRecents, sendUserLeaveHint);
-
-            if (callback != null) {
-                MAIN_EXECUTOR.execute(callback);
-            }
-        });
-    }
-
-    public void enableInputConsumer() {
-        UI_HELPER_EXECUTOR.submit(() -> {
-            controller.hideCurrentInputMethod();
-            controller.setInputConsumerEnabled(true);
-        });
-    }
-
-    public void setWindowThresholdCrossed(boolean thresholdCrossed) {
-        UI_HELPER_EXECUTOR.execute(() -> {
-            controller.setAnimationTargetsBehindSystemBars(!thresholdCrossed);
-            if (mShouldMinimizeSplitScreen && thresholdCrossed) {
-                // NOTE: As a workaround for conflicting animations (Launcher animating the task
-                // leash, and SystemUI resizing the docked stack, which resizes the task), we
-                // currently only set the minimized mode, and not the inverse.
-                // TODO: Synchronize the minimize animation with the launcher animation
-                controller.setSplitScreenMinimized(thresholdCrossed);
-            }
-        });
-    }
-
-    public ThumbnailData screenshotTask(int taskId) {
-        return controller != null ? controller.screenshotTask(taskId) : null;
-    }
-
-    public void cancelAnimation() {
-        finishController(false /* toRecents */, null, false /* sendUserLeaveHint */);
-    }
-
-    public void finishAnimation() {
-        finishController(true /* toRecents */, null, false /* sendUserLeaveHint */);
+        return new RecentsAnimationTargets(new RemoteAnimationTargetCompat[0],
+                new RemoteAnimationTargetCompat[0], homeContentInsets, minimizedHomeBounds);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 0f9184f..6a6043c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -221,7 +221,7 @@
     public AppWindowAnimationHelper.TransformParams getLiveTileParams(
             boolean mightNeedToRefill) {
         if (!mEnableDrawingLiveTile || mRecentsAnimationWrapper == null
-                || mAppWindowAnimationHelper == null) {
+                || mRecentsAnimationTargets == null || mAppWindowAnimationHelper == null) {
             return null;
         }
         TaskView taskView = getRunningTaskView();
@@ -245,7 +245,7 @@
             mTransformParams.setProgress(1f)
                     .setCurrentRectAndTargetAlpha(mTempRectF, taskView.getAlpha())
                     .setSyncTransactionApplier(mSyncTransactionApplier)
-                    .setTargetSet(mRecentsAnimationWrapper.targetSet)
+                    .setTargetSet(mRecentsAnimationTargets)
                     .setLauncherOnTop(true);
         }
         return mTransformParams;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index cefe264..67cd3d6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -108,6 +108,7 @@
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.ViewUtils;
 import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.quickstep.util.RecentsAnimationTargets;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -156,6 +157,7 @@
             };
 
     protected RecentsAnimationWrapper mRecentsAnimationWrapper;
+    protected RecentsAnimationTargets mRecentsAnimationTargets;
     protected AppWindowAnimationHelper mAppWindowAnimationHelper;
     protected SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
     protected int mTaskWidth;
@@ -808,6 +810,7 @@
         mTaskListChangeId = -1;
 
         mRecentsAnimationWrapper = null;
+        mRecentsAnimationTargets = null;
         mAppWindowAnimationHelper = null;
 
         unloadVisibleTaskData();
@@ -1692,10 +1695,14 @@
 
     public void redrawLiveTile(boolean mightNeedToRefill) { }
 
-    public void setRecentsAnimationWrapper(RecentsAnimationWrapper recentsAnimationWrapper) {
+    // TODO: To be removed in a follow up CL
+    public void setRecentsAnimationTargets(RecentsAnimationWrapper recentsAnimationWrapper,
+            RecentsAnimationTargets recentsAnimationTargets) {
         mRecentsAnimationWrapper = recentsAnimationWrapper;
+        mRecentsAnimationTargets = recentsAnimationTargets;
     }
 
+    // TODO: To be removed in a follow up CL
     public void setAppWindowAnimationHelper(AppWindowAnimationHelper appWindowAnimationHelper) {
         mAppWindowAnimationHelper = appWindowAnimationHelper;
     }
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargets.java
index ff726a1..f971c47 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargets.java
@@ -86,6 +86,9 @@
             for (RemoteAnimationTargetCompat target : unfilteredApps) {
                 target.release();
             }
+            for (RemoteAnimationTargetCompat target : wallpapers) {
+                target.release();
+            }
         } else {
             applier.addAfterApplyCallback(this::release);
         }