Split LauncherAppTransitionManagerImpl for Go

Split LauncherAppTransitionManagerImpl common functionality into a base
quickstep app transition class and subclass for recents-specific
implementation. In addition, override the app transition manager to the
appropriate manager depending on the build.

Bug: 114136250
Test: Manual test NexusLauncher, Launcher3GoWithQuickstep
Test: Build Launcher3GoIconRecents
Change-Id: Iab1b5ae0c75af276879d5e12df95f62a514bf571
diff --git a/quickstep/res/values/override.xml b/go/quickstep/res/values/override.xml
similarity index 88%
copy from quickstep/res/values/override.xml
copy to go/quickstep/res/values/override.xml
index d683659..7636fb3 100644
--- a/quickstep/res/values/override.xml
+++ b/go/quickstep/res/values/override.xml
@@ -14,8 +14,10 @@
      limitations under the License.
 -->
 
+<!-- Class overrides for Go version of launcher with Go recents. -->
+
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-  <string name="app_transition_manager_class" translatable="false">com.android.launcher3.LauncherAppTransitionManagerImpl</string>
+  <string name="app_transition_manager_class" translatable="false">com.android.launcher3.GoLauncherAppTransitionManagerImpl</string>
 
   <string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
 
diff --git a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
new file mode 100644
index 0000000..95d8c4e
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
@@ -0,0 +1,65 @@
+package com.android.launcher3;
+
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.view.View;
+
+import com.android.quickstep.views.IconRecentsView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * A {@link QuickstepAppTransitionManagerImpl} with recents-specific app transitions based off
+ * {@link com.android.quickstep.views.IconRecentsView}.
+ */
+public final class GoLauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
+
+    public GoLauncherAppTransitionManagerImpl(Context context) {
+        super(context);
+    }
+
+    @Override
+    protected boolean isLaunchingFromRecents(View v, RemoteAnimationTargetCompat[] targets) {
+        return mLauncher.getStateManager().getState().overviewUi;
+    }
+
+    @Override
+    protected boolean isQuickSwitchInProgress() {
+        // Go does not support quick scrub.
+        return false;
+    }
+
+    @Override
+    protected ActivityOptions getQuickSwitchActivityOptions() {
+        // Go does not support quick scrub.
+        return null;
+    }
+
+    @Override
+    protected void composeRecentsLaunchAnimator(AnimatorSet anim, View v,
+            RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+        //TODO: Implement this based off IconRecentsView
+    }
+
+    @Override
+    protected Runnable composeViewContentAnimator(AnimatorSet anim, float[] alphas, float[] trans) {
+        IconRecentsView overview = mLauncher.getOverviewPanel();
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
+                CONTENT_ALPHA, alphas);
+        alpha.setDuration(CONTENT_ALPHA_DURATION);
+        alpha.setInterpolator(LINEAR);
+        anim.play(alpha);
+
+        ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
+        transY.setInterpolator(AGGRESSIVE_EASE);
+        transY.setDuration(CONTENT_TRANSLATION_DURATION);
+        anim.play(transY);
+
+        return mLauncher.getStateManager()::reapplyState;
+    }
+}
diff --git a/quickstep/res/values/override.xml b/quickstep/recents_ui_overrides/res/values/override.xml
similarity index 95%
rename from quickstep/res/values/override.xml
rename to quickstep/recents_ui_overrides/res/values/override.xml
index d683659..c60cf5a 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/recents_ui_overrides/res/values/override.xml
@@ -14,6 +14,8 @@
      limitations under the License.
 -->
 
+<!-- Class overrides for launcher with quickstep. -->
+
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string name="app_transition_manager_class" translatable="false">com.android.launcher3.LauncherAppTransitionManagerImpl</string>
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
new file mode 100644
index 0000000..9921455
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -0,0 +1,148 @@
+/*
+ * 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.launcher3;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
+import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
+ * {@link RecentsView}.
+ */
+public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
+
+    private RecentsView mRecentsView;
+
+    public LauncherAppTransitionManagerImpl(Context context) {
+        super(context);
+        mRecentsView = mLauncher.getOverviewPanel();
+    }
+
+    @Override
+    protected boolean isLaunchingFromRecents(@NonNull View v,
+            @Nullable RemoteAnimationTargetCompat[] targets) {
+        return mLauncher.getStateManager().getState().overviewUi
+                && findTaskViewToLaunch(mLauncher, v, targets) != null;
+    }
+
+    @Override
+    protected boolean isQuickSwitchInProgress() {
+        return mRecentsView.getQuickScrubController().isQuickSwitch();
+    }
+
+    @Override
+    protected ActivityOptions getQuickSwitchActivityOptions() {
+        return ActivityOptions.makeCustomAnimation(mLauncher, R.anim.no_anim,
+                R.anim.no_anim);
+    }
+
+    @Override
+    protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+            @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+        RecentsView recentsView = mLauncher.getOverviewPanel();
+        boolean skipLauncherChanges = !launcherClosing;
+        boolean isLaunchingFromQuickscrub =
+                recentsView.getQuickScrubController().isWaitingForTaskLaunch();
+
+        TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
+
+        int duration = isLaunchingFromQuickscrub
+                ? RECENTS_QUICKSCRUB_LAUNCH_DURATION
+                : RECENTS_LAUNCH_DURATION;
+
+        ClipAnimationHelper helper = new ClipAnimationHelper(mLauncher);
+        anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
+                .setDuration(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, helper);
+            launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+            launcherAnim.setDuration(duration);
+
+            // Make sure recents gets fixed up by resetting task alphas and scales, etc.
+            windowAnimEndListener = new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mLauncher.getStateManager().moveToRestState();
+                    mLauncher.getStateManager().reapplyState();
+                }
+            };
+        } else {
+            AnimatorPlaybackController controller =
+                    mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL, duration);
+            controller.dispatchOnStart();
+            childStateAnimation = controller.getTarget();
+            launcherAnim = controller.getAnimationPlayer().setDuration(duration);
+            windowAnimEndListener = new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mLauncher.getStateManager().goToState(NORMAL, false);
+                }
+            };
+        }
+        anim.play(launcherAnim);
+
+        // Set the current animation first, before adding windowAnimEndListener. Setting current
+        // animation adds some listeners which need to be called before windowAnimEndListener
+        // (the ordering of listeners matter in this case).
+        mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation);
+        anim.addListener(windowAnimEndListener);
+    }
+
+    @Override
+    protected Runnable composeViewContentAnimator(@NonNull AnimatorSet anim, float[] alphas,
+            float[] trans) {
+        RecentsView overview = mLauncher.getOverviewPanel();
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
+                RecentsView.CONTENT_ALPHA, alphas);
+        alpha.setDuration(CONTENT_ALPHA_DURATION);
+        alpha.setInterpolator(LINEAR);
+        anim.play(alpha);
+
+        ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
+        transY.setInterpolator(AGGRESSIVE_EASE);
+        transY.setDuration(CONTENT_TRANSLATION_DURATION);
+        anim.play(transY);
+
+        return mLauncher.getStateManager()::reapplyState;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 08b6bfc..c5c5323 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -44,8 +44,8 @@
     @Override
     protected boolean init(Launcher launcher, boolean alreadyOnHome) {
         if (mRemoteAnimationProvider != null) {
-            LauncherAppTransitionManagerImpl appTransitionManager =
-                    (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager();
+            QuickstepAppTransitionManagerImpl appTransitionManager =
+                    (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
 
             // Set a one-time animation provider. After the first call, this will get cleared.
             // TODO: Probably also check the intended target id.
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
similarity index 84%
rename from quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
rename to quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 264ad5a..73a7c08 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -22,7 +22,6 @@
 import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
@@ -30,8 +29,6 @@
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
-import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
-import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -57,10 +54,12 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.InsettableFrameLayout.LayoutParams;
 import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.graphics.DrawableFactory;
@@ -68,12 +67,9 @@
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.RecentsModel;
-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.system.ActivityCompat;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -85,14 +81,15 @@
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
 /**
- * Manages the opening and closing app transitions from Launcher.
+ * {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
+ * home and/or all-apps.
  */
 @TargetApi(Build.VERSION_CODES.O)
 @SuppressWarnings("unused")
-public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManager
+public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTransitionManager
         implements OnDeviceProfileChangeListener {
 
-    private static final String TAG = "LauncherTransition";
+    private static final String TAG = "QuickstepTransition";
 
     /** Duration of status bar animations. */
     public static final int STATUS_BAR_TRANSITION_DURATION = 120;
@@ -119,10 +116,14 @@
     private static final int LAUNCHER_RESUME_START_DELAY = 100;
     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
 
+    protected static final int CONTENT_ALPHA_DURATION = 217;
+    protected static final int CONTENT_TRANSLATION_DURATION = 350;
+
     // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
     public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
 
-    private final Launcher mLauncher;
+    protected final Launcher mLauncher;
+
     private final DragLayer mDragLayer;
     private final AlphaProperty mDragLayerAlpha;
 
@@ -150,7 +151,7 @@
         }
     };
 
-    public LauncherAppTransitionManagerImpl(Context context) {
+    public QuickstepAppTransitionManagerImpl(Context context) {
         mLauncher = Launcher.getLauncher(context);
         mDragLayer = mLauncher.getDragLayer();
         mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
@@ -179,12 +180,9 @@
     @Override
     public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
         if (hasControlRemoteAppTransitionPermission()) {
-            boolean fromRecents = mLauncher.getStateManager().getState().overviewUi
-                    && findTaskViewToLaunch(launcher, v, null) != null;
-            RecentsView recentsView = mLauncher.getOverviewPanel();
-            if (fromRecents && recentsView.getQuickScrubController().isQuickSwitch()) {
-                return ActivityOptions.makeCustomAnimation(mLauncher, R.anim.no_anim,
-                        R.anim.no_anim);
+            boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
+            if (fromRecents && isQuickSwitchInProgress()) {
+                return getQuickSwitchActivityOptions();
             }
 
             RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
@@ -198,34 +196,10 @@
                     boolean launcherClosing =
                             launcherIsATargetWithMode(targetCompats, MODE_CLOSING);
 
-                    if (!composeRecentsLaunchAnimator(v, targetCompats, anim)) {
-                        // Set the state animation first so that any state listeners are called
-                        // before our internal listeners.
-                        mLauncher.getStateManager().setCurrentAnimation(anim);
-
-                        Rect windowTargetBounds = getWindowTargetBounds(targetCompats);
-                        boolean isAllOpeningTargetTrs = true;
-                        for (int i = 0; i < targetCompats.length; i++) {
-                            RemoteAnimationTargetCompat target = targetCompats[i];
-                            if (target.mode == MODE_OPENING) {
-                                isAllOpeningTargetTrs &= target.isTranslucent;
-                            }
-                            if (!isAllOpeningTargetTrs) break;
-                        }
-                        playIconAnimators(anim, v, windowTargetBounds, !isAllOpeningTargetTrs);
-                        if (launcherClosing) {
-                            Pair<AnimatorSet, Runnable> launcherContentAnimator =
-                                    getLauncherContentAnimator(true /* isAppOpening */,
-                                            new float[] {0, mContentTransY});
-                            anim.play(launcherContentAnimator.first);
-                            anim.addListener(new AnimatorListenerAdapter() {
-                                @Override
-                                public void onAnimationEnd(Animator animation) {
-                                    launcherContentAnimator.second.run();
-                                }
-                            });
-                        }
-                        anim.play(getOpeningWindowAnimators(v, targetCompats, windowTargetBounds));
+                    if (isLaunchingFromRecents(v, targetCompats)) {
+                        composeRecentsLaunchAnimator(anim, v, targetCompats, launcherClosing);
+                    } else {
+                        composeIconLaunchAnimator(anim, v, targetCompats, launcherClosing);
                     }
 
                     if (launcherClosing) {
@@ -236,6 +210,8 @@
                 }
             };
 
+            // Note that this duration is a guess as we do not know if the animation will be a
+            // recents launch or not for sure until we know the opening app targets.
             int duration = fromRecents
                     ? RECENTS_LAUNCH_DURATION
                     : APP_LAUNCH_DURATION;
@@ -249,6 +225,83 @@
     }
 
     /**
+     * Whether the launch is a recents app transition and we should do a launch animation
+     * from the recents view. Note that if the remote animation targets are not provided, this
+     * may not always be correct as we may resolve the opening app to a task when the animation
+     * starts.
+     *
+     * @param v the view to launch from
+     * @param targets apps that are opening/closing
+     * @return true if the app is launching from recents, false if it most likely is not
+     */
+    protected abstract boolean isLaunchingFromRecents(@NonNull View v,
+            @Nullable RemoteAnimationTargetCompat[] targets);
+
+    /**
+     * Whether a quick scrub is in progress.
+     *
+     * @return true if in progress
+     */
+    protected abstract boolean isQuickSwitchInProgress();
+
+    /**
+     * Get activity options for a quick switch launch that include the launch animation.
+     *
+     * @return the activity options for a quick switch recents launch
+     */
+    protected abstract ActivityOptions getQuickSwitchActivityOptions();
+
+    /**
+     * Composes the animations for a launch from the recents list.
+     *
+     * @param anim the animator set to add to
+     * @param v the launching view
+     * @param targets the apps that are opening/closing
+     * @param launcherClosing true if the launcher app is closing
+     */
+    protected abstract void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+            @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing);
+
+    /**
+     * Compose the animations for a launch from the app icon.
+     *
+     * @param anim the animation to add to
+     * @param v the launching view with the icon
+     * @param targets the list of opening/closing apps
+     * @param launcherClosing true if launcher is closing
+     */
+    private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+            @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+        // Set the state animation first so that any state listeners are called
+        // before our internal listeners.
+        mLauncher.getStateManager().setCurrentAnimation(anim);
+
+        Rect windowTargetBounds = getWindowTargetBounds(targets);
+        boolean isAllOpeningTargetTrs = true;
+        for (int i = 0; i < targets.length; i++) {
+            RemoteAnimationTargetCompat target = targets[i];
+            if (target.mode == MODE_OPENING) {
+                isAllOpeningTargetTrs &= target.isTranslucent;
+            }
+            if (!isAllOpeningTargetTrs) break;
+        }
+        playIconAnimators(anim, v, windowTargetBounds, !isAllOpeningTargetTrs);
+        if (launcherClosing) {
+            Pair<AnimatorSet, Runnable> launcherContentAnimator =
+                    getLauncherContentAnimator(true /* isAppOpening */,
+                            new float[] {0, mContentTransY});
+            anim.play(launcherContentAnimator.first);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    launcherContentAnimator.second.run();
+                }
+            });
+        }
+        anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds));
+    }
+
+    /**
      * Return the window bounds of the opening target.
      * In multiwindow mode, we need to get the final size of the opening app window target to help
      * figure out where the floating view should animate to.
@@ -278,75 +331,6 @@
     }
 
     /**
-     * Composes the animations for a launch from the recents list if possible.
-     */
-    private boolean composeRecentsLaunchAnimator(View v,
-            RemoteAnimationTargetCompat[] targets, AnimatorSet target) {
-        // Ensure recents is actually visible
-        if (!mLauncher.getStateManager().getState().overviewUi) {
-            return false;
-        }
-
-        RecentsView recentsView = mLauncher.getOverviewPanel();
-        boolean launcherClosing = launcherIsATargetWithMode(targets, MODE_CLOSING);
-        boolean skipLauncherChanges = !launcherClosing;
-        boolean isLaunchingFromQuickscrub =
-                recentsView.getQuickScrubController().isWaitingForTaskLaunch();
-
-        TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
-        if (taskView == null) {
-            return false;
-        }
-
-        int duration = isLaunchingFromQuickscrub
-                ? RECENTS_QUICKSCRUB_LAUNCH_DURATION
-                : RECENTS_LAUNCH_DURATION;
-
-        ClipAnimationHelper helper = new ClipAnimationHelper(mLauncher);
-        target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
-                .setDuration(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, helper);
-            launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
-            launcherAnim.setDuration(duration);
-
-            // Make sure recents gets fixed up by resetting task alphas and scales, etc.
-            windowAnimEndListener = new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mLauncher.getStateManager().moveToRestState();
-                    mLauncher.getStateManager().reapplyState();
-                }
-            };
-        } else {
-            AnimatorPlaybackController controller =
-                    mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL, duration);
-            controller.dispatchOnStart();
-            childStateAnimation = controller.getTarget();
-            launcherAnim = controller.getAnimationPlayer().setDuration(duration);
-            windowAnimEndListener = new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mLauncher.getStateManager().goToState(NORMAL, false);
-                }
-            };
-        }
-        target.play(launcherAnim);
-
-        // Set the current animation first, before adding windowAnimEndListener. Setting current
-        // animation adds some listeners which need to be called before windowAnimEndListener
-        // (the ordering of listeners matter in this case).
-        mLauncher.getStateManager().setCurrentAnimation(target, childStateAnimation);
-        target.addListener(windowAnimEndListener);
-        return true;
-    }
-
-    /**
      * Content is everything on screen except the background and the floating view (if any).
      *
      * @param isAppOpening True when this is called when an app is opening.
@@ -371,7 +355,7 @@
             appsView.setTranslationY(trans[0]);
 
             ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
-            alpha.setDuration(217);
+            alpha.setDuration(CONTENT_ALPHA_DURATION);
             alpha.setInterpolator(LINEAR);
             appsView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             alpha.addListener(new AnimatorListenerAdapter() {
@@ -382,7 +366,7 @@
             });
             ObjectAnimator transY = ObjectAnimator.ofFloat(appsView, View.TRANSLATION_Y, trans);
             transY.setInterpolator(AGGRESSIVE_EASE);
-            transY.setDuration(350);
+            transY.setDuration(CONTENT_TRANSLATION_DURATION);
 
             launcherAnimator.play(alpha);
             launcherAnimator.play(transY);
@@ -396,32 +380,19 @@
             AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
             launcherAnimator.play(ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
                     allAppsController.getProgress(), ALL_APPS_PROGRESS_OFF_SCREEN));
-
-            RecentsView overview = mLauncher.getOverviewPanel();
-            ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
-                    RecentsView.CONTENT_ALPHA, alphas);
-            alpha.setDuration(217);
-            alpha.setInterpolator(LINEAR);
-            launcherAnimator.play(alpha);
-
-            ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
-            transY.setInterpolator(AGGRESSIVE_EASE);
-            transY.setDuration(350);
-            launcherAnimator.play(transY);
-
-            endListener = mLauncher.getStateManager()::reapplyState;
+            endListener = composeViewContentAnimator(launcherAnimator, alphas, trans);
         } else {
             mDragLayerAlpha.setValue(alphas[0]);
             ObjectAnimator alpha =
                     ObjectAnimator.ofFloat(mDragLayerAlpha, MultiValueAlpha.VALUE, alphas);
-            alpha.setDuration(217);
+            alpha.setDuration(CONTENT_ALPHA_DURATION);
             alpha.setInterpolator(LINEAR);
             launcherAnimator.play(alpha);
 
             mDragLayer.setTranslationY(trans[0]);
             ObjectAnimator transY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y, trans);
             transY.setInterpolator(AGGRESSIVE_EASE);
-            transY.setDuration(350);
+            transY.setDuration(CONTENT_TRANSLATION_DURATION);
             launcherAnimator.play(transY);
 
             mDragLayer.getScrim().hideSysUiScrim(true);
@@ -435,6 +406,17 @@
     }
 
     /**
+     * Compose recents view alpha and translation Y animation when launcher opens/closes apps.
+     *
+     * @param anim the animator set to add to
+     * @param alphas the alphas to animate to over time
+     * @param trans the translation Y values to animator to over time
+     * @return listener to run when the animation ends
+     */
+    protected abstract Runnable composeViewContentAnimator(@NonNull AnimatorSet anim,
+            float[] alphas, float[] trans);
+
+    /**
      * Animators for the "floating view" of the view used to launch the target.
      */
     private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 3aa6482..ff9d601 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -34,10 +34,10 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppTransitionManagerImpl;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.QuickstepAppTransitionManagerImpl;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.TouchController;
@@ -198,8 +198,8 @@
 
     public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
             CancellationSignal cancellationSignal) {
-        LauncherAppTransitionManagerImpl appTransitionManager =
-                (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager();
+        QuickstepAppTransitionManagerImpl appTransitionManager =
+                (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
         appTransitionManager.setRemoteAnimationProvider((targets) -> {
 
             // On the first call clear the reference.
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index deedd32..89c7aba 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -18,9 +18,11 @@
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl
+        .STATUS_BAR_TRANSITION_DURATION;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl
+        .STATUS_BAR_TRANSITION_PRE_DELAY;
 import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 722c721..f8eced0 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -16,9 +16,9 @@
 package com.android.quickstep.views;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_QUICKSTEP_PREVIEW;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 1b953d4..5137774 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -226,7 +226,7 @@
     /**
      * Used to set the override visibility state, used only to handle the transition home with the
      * recents animation.
-     * @see LauncherAppTransitionManagerImpl#getWallpaperOpenRunner()
+     * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner()
      */
     public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
         mForceInvisible |= flag;