Adding support for listening for app launch animation completion

Bug: 181165935
Bug: 179065491
Test: Verified on device
Change-Id: Ifa6a91560cb31b4dfb72a0f582607e873d8a002d
diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml
index 605774d..705ec9d 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -17,8 +17,6 @@
 <!-- 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>
-
   <string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
 
   <string name="app_launch_tracker_class" translatable="false">com.android.launcher3.appprediction.PredictionAppTracker</string>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 641e108..6eb1498 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -25,7 +25,6 @@
 
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
-import android.app.ActivityOptions;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.os.Bundle;
@@ -47,6 +46,7 @@
 import com.android.launcher3.taskbar.TaskbarController;
 import com.android.launcher3.taskbar.TaskbarStateHandler;
 import com.android.launcher3.uioverrides.RecentsViewStateController;
+import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.RecentsModel;
@@ -73,6 +73,7 @@
         implements NavigationModeChangeListener {
 
     private DepthController mDepthController = new DepthController(this);
+    private QuickstepTransitionManager mAppTransitionManager;
 
     /**
      * Reusable command for applying the back button alpha on the background thread.
@@ -91,6 +92,8 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mAppTransitionManager = new QuickstepTransitionManager(this);
+        mAppTransitionManager.registerRemoteAnimations();
 
         SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
         addMultiWindowModeChangedListener(mDepthController);
@@ -98,8 +101,9 @@
 
     @Override
     public void onDestroy() {
-        SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+        mAppTransitionManager.onActivityDestroyed();
 
+        SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
         if (mTaskbarController != null) {
             mTaskbarController.cleanup();
         }
@@ -107,6 +111,10 @@
         super.onDestroy();
     }
 
+    public QuickstepTransitionManager getAppTransitionManager() {
+        return mAppTransitionManager;
+    }
+
     @Override
     public void onNavigationModeChanged(Mode newMode) {
         getDragLayer().recreateControllers();
@@ -270,6 +278,12 @@
         return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v);
     }
 
+    public boolean supportsAdaptiveIconAnimation(View clickedView) {
+        return mAppTransitionManager.hasControlRemoteAppTransitionPermission()
+                && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get()
+                && !isViewInTaskbar(clickedView);
+    }
+
     @Override
     public DragOptions getDefaultWorkspaceDragOptions() {
         if (mNextWorkspaceDragOptions != null) {
@@ -286,8 +300,7 @@
 
     @Override
     public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
-        QuickstepAppTransitionManagerImpl appTransitionManager =
-                (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
+        QuickstepTransitionManager appTransitionManager = getAppTransitionManager();
         appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
             @Override
             public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
@@ -379,10 +392,14 @@
     }
 
     @Override
-    public ActivityOptions getActivityLaunchOptions(View v) {
-        ActivityOptions activityOptions = super.getActivityLaunchOptions(v);
-        if (activityOptions != null && mLastTouchUpTime > 0) {
-            ActivityOptionsCompat.setLauncherSourceInfo(activityOptions, mLastTouchUpTime);
+    public ActivityOptionsWrapper getActivityLaunchOptions(View v) {
+        ActivityOptionsWrapper activityOptions =
+                mAppTransitionManager.hasControlRemoteAppTransitionPermission()
+                        ? mAppTransitionManager.getActivityLaunchOptions(this, v)
+                        : super.getActivityLaunchOptions(v);
+        if (mLastTouchUpTime > 0) {
+            ActivityOptionsCompat.setLauncherSourceInfo(
+                    activityOptions.options, mLastTouchUpTime);
         }
         return activityOptions;
     }
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 588d676..be98157 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+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.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
 
@@ -29,6 +30,7 @@
 import android.os.Handler;
 
 import androidx.annotation.BinderThread;
+import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
@@ -37,8 +39,6 @@
 @TargetApi(Build.VERSION_CODES.P)
 public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
 
-    private static final String TAG = "LauncherAnimationRunner";
-
     private final Handler mHandler;
     private final boolean mStartAtFrontOfQueue;
     private AnimationResult mAnimationResult;
@@ -66,10 +66,7 @@
             Runnable runnable) {
         Runnable r = () -> {
             finishExistingAnimation();
-            mAnimationResult = new AnimationResult(() -> {
-                UI_HELPER_EXECUTOR.execute(runnable);
-                mAnimationResult = null;
-            });
+            mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
             onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
                     mAnimationResult);
         };
@@ -126,37 +123,60 @@
 
     public static final class AnimationResult {
 
-        private final Runnable mFinishRunnable;
+        private final Runnable mSyncFinishRunnable;
+        private final Runnable mASyncFinishRunnable;
 
         private AnimatorSet mAnimator;
+        private Runnable mOnCompleteCallback;
         private boolean mFinished = false;
         private boolean mInitialized = false;
 
-        private AnimationResult(Runnable finishRunnable) {
-            mFinishRunnable = finishRunnable;
+        private AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable) {
+            mSyncFinishRunnable = syncFinishRunnable;
+            mASyncFinishRunnable = asyncFinishRunnable;
         }
 
         @UiThread
         private void finish() {
             if (!mFinished) {
-                mFinishRunnable.run();
+                mSyncFinishRunnable.run();
+                UI_HELPER_EXECUTOR.execute(() -> {
+                    mASyncFinishRunnable.run();
+                    if (mOnCompleteCallback != null) {
+                        MAIN_EXECUTOR.execute(mOnCompleteCallback);
+                    }
+                });
                 mFinished = true;
             }
         }
 
         @UiThread
         public void setAnimation(AnimatorSet animation, Context context) {
+            setAnimation(animation, context, null);
+
+        }
+
+        /**
+         * Sets the animation to play for this app launch
+         */
+        @UiThread
+        public void setAnimation(AnimatorSet animation, Context context,
+                @Nullable Runnable onCompleteCallback) {
             if (mInitialized) {
                 throw new IllegalStateException("Animation already initialized");
             }
             mInitialized = true;
             mAnimator = animation;
+            mOnCompleteCallback = onCompleteCallback;
             if (mAnimator == null) {
                 finish();
             } else if (mFinished) {
                 // Animation callback was already finished, skip the animation.
                 mAnimator.start();
                 mAnimator.end();
+                if (mOnCompleteCallback != null) {
+                    mOnCompleteCallback.run();
+                }
             } else {
                 // Start the animation
                 mAnimator.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
deleted file mode 100644
index ae4bd96..0000000
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ /dev/null
@@ -1,83 +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.launcher3;
-
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
-
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.quickstep.TaskViewUtils;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
- * {@link RecentsView}.
- */
-public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
-
-    public LauncherAppTransitionManagerImpl(Context context) {
-        super(context);
-    }
-
-    @Override
-    protected boolean isLaunchingFromRecents(@NonNull View v,
-            @Nullable RemoteAnimationTargetCompat[] targets) {
-        return mLauncher.getStateManager().getState().overviewUi
-                && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
-    }
-
-    @Override
-    protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
-        TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
-                launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(),
-                mLauncher.getDepthController());
-    }
-
-    @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);
-        overview.setFreezeViewVisibility(true);
-
-        ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
-        transY.setInterpolator(AGGRESSIVE_EASE);
-        transY.setDuration(CONTENT_TRANSLATION_DURATION);
-        anim.play(transY);
-
-        return () -> {
-            overview.setFreezeViewVisibility(false);
-            overview.setTranslationY(0);
-            mLauncher.getStateManager().reapplyState();
-        };
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 7fb0d43..5fc79f0 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -46,8 +46,8 @@
     @Override
     public boolean handleInit(Launcher launcher, boolean alreadyOnHome) {
         if (mRemoteAnimationProvider != null) {
-            QuickstepAppTransitionManagerImpl appTransitionManager =
-                    (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
+            QuickstepTransitionManager appTransitionManager =
+                    ((BaseQuickstepLauncher) 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/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
similarity index 92%
rename from quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
rename to quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index f14c5bf..343b87e 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -37,6 +37,7 @@
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
+import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
 import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -47,7 +48,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
-import android.app.ActivityOptions;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -69,20 +69,23 @@
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DynamicResource;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
 import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.IStartingWindowListener;
 import com.android.systemui.shared.system.ActivityCompat;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -99,12 +102,9 @@
 import java.util.LinkedHashMap;
 
 /**
- * {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
- * home and/or all-apps.  Not used for 3p launchers.
+ * Manages the opening and closing app transitions from Launcher
  */
-@SuppressWarnings("unused")
-public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTransitionManager
-        implements OnDeviceProfileChangeListener {
+public class QuickstepTransitionManager implements OnDeviceProfileChangeListener {
 
     private static final String TAG = "QuickstepTransition";
 
@@ -115,8 +115,8 @@
     public static final int STATUS_BAR_TRANSITION_DURATION = 120;
 
     /**
-     * Since our animations decelerate heavily when finishing, we want to start status bar animations
-     * x ms before the ending.
+     * Since our animations decelerate heavily when finishing, we want to start status bar
+     * animations x ms before the ending.
      */
     public static final int STATUS_BAR_TRANSITION_PRE_DELAY = 96;
 
@@ -191,7 +191,7 @@
     // Will never be larger than MAX_NUM_TASKS
     private LinkedHashMap<Integer, Integer> mTypeForTaskId;
 
-    public QuickstepAppTransitionManagerImpl(Context context) {
+    public QuickstepTransitionManager(Context context) {
         mLauncher = Launcher.cast(Launcher.getLauncher(context));
         mDragLayer = mLauncher.getDragLayer();
         mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
@@ -229,37 +229,29 @@
         mDeviceProfile = dp;
     }
 
-    @Override
-    public boolean supportsAdaptiveIconAnimation(View clickedView) {
-        return hasControlRemoteAppTransitionPermission()
-                && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get()
-                && !mLauncher.isViewInTaskbar(clickedView);
-    }
-
     /**
      * @return ActivityOptions with remote animations that controls how the window of the opening
      *         targets are displayed.
      */
-    @Override
-    public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
-        if (hasControlRemoteAppTransitionPermission()) {
-            boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
-            mAppLaunchRunner = new AppLaunchAnimationRunner(mHandler, v);
-            RemoteAnimationRunnerCompat runner = new WrappedLauncherAnimationRunner<>(
-                    mAppLaunchRunner, true /* startAtFrontOfQueue */);
+    public ActivityOptionsWrapper getActivityLaunchOptions(Launcher launcher, View v) {
+        boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
+        RunnableList onEndCallback = new RunnableList();
+        mAppLaunchRunner = new AppLaunchAnimationRunner(mHandler, v, onEndCallback);
+        RemoteAnimationRunnerCompat runner = new WrappedLauncherAnimationRunner<>(
+                mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */);
 
-            // 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.
-            long duration = fromRecents
-                    ? RECENTS_LAUNCH_DURATION
-                    : APP_LAUNCH_DURATION;
+        // 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.
+        long duration = fromRecents
+                ? RECENTS_LAUNCH_DURATION
+                : APP_LAUNCH_DURATION;
 
-            long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
-                    - STATUS_BAR_TRANSITION_PRE_DELAY;
-            return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
-                    runner, duration, statusBarTransitionDelay));
-        }
-        return super.getActivityLaunchOptions(launcher, v);
+        long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
+                - STATUS_BAR_TRANSITION_PRE_DELAY;
+        RemoteAnimationAdapterCompat adapterCompat =
+                new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay);
+        return new ActivityOptionsWrapper(
+                ActivityOptionsCompat.makeRemoteAnimation(adapterCompat), onEndCallback);
     }
 
     /**
@@ -272,8 +264,11 @@
      * @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);
+    protected boolean isLaunchingFromRecents(@NonNull View v,
+            @Nullable RemoteAnimationTargetCompat[] targets) {
+        return mLauncher.getStateManager().getState().overviewUi
+                && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
+    }
 
     /**
      * Composes the animations for a launch from the recents list.
@@ -283,9 +278,13 @@
      * @param appTargets 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,
+    protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
             @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing);
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
+        TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
+                launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(),
+                mLauncher.getDepthController());
+    }
 
     private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTargetCompat[] targets) {
         boolean isAllOpeningTargetTrs = true;
@@ -483,8 +482,27 @@
      * @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);
+    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);
+        overview.setFreezeViewVisibility(true);
+
+        ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
+        transY.setInterpolator(AGGRESSIVE_EASE);
+        transY.setDuration(CONTENT_TRANSLATION_DURATION);
+        anim.play(transY);
+
+        return () -> {
+            overview.setFreezeViewVisibility(false);
+            overview.setTranslationY(0);
+            mLauncher.getStateManager().reapplyState();
+        };
+    }
 
     /**
      * @return Animator that controls the window of the opening targets from app icons.
@@ -686,7 +704,6 @@
     /**
      * Registers remote animations used when closing apps to home screen.
      */
-    @Override
     public void registerRemoteAnimations() {
         if (SEPARATE_RECENTS_ACTIVITY.get()) {
             return;
@@ -698,7 +715,7 @@
             definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
                     WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
                     new RemoteAnimationAdapterCompat(
-                            new WrappedLauncherAnimationRunner<>(mWallpaperOpenRunner,
+                            new WrappedLauncherAnimationRunner<>(mHandler, mWallpaperOpenRunner,
                                     false /* startAtFrontOfQueue */),
                             CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
 
@@ -707,7 +724,8 @@
                 definition.addRemoteAnimation(
                         WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
                         new RemoteAnimationAdapterCompat(
-                                new WrappedLauncherAnimationRunner<>(mKeyguardGoingAwayRunner,
+                                new WrappedLauncherAnimationRunner<>(
+                                        mHandler, mKeyguardGoingAwayRunner,
                                         true /* startAtFrontOfQueue */),
                                 CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
             }
@@ -719,7 +737,6 @@
     /**
      * Registers remote animations used when closing apps to home screen.
      */
-    @Override
     public void registerRemoteTransitions() {
         if (SEPARATE_RECENTS_ACTIVITY.get()) {
             return;
@@ -727,26 +744,20 @@
         if (hasControlRemoteAppTransitionPermission()) {
             mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
             mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
-                    new WrappedLauncherAnimationRunner<>(mWallpaperOpenTransitionRunner,
+                    new WrappedLauncherAnimationRunner<>(mHandler, mWallpaperOpenTransitionRunner,
                             false /* startAtFrontOfQueue */));
             mLauncherOpenTransition.addHomeOpenCheck();
             SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
         }
     }
 
-    @Override
     public void onActivityDestroyed() {
-        super.onActivityDestroyed();
         unregisterRemoteAnimations();
         unregisterRemoteTransitions();
         SystemUiProxy.INSTANCE.getNoCreate().setStartingWindowListener(null);
     }
 
-    /**
-     * Unregisters all remote animations.
-     */
-    @Override
-    public void unregisterRemoteAnimations() {
+    private void unregisterRemoteAnimations() {
         if (SEPARATE_RECENTS_ACTIVITY.get()) {
             return;
         }
@@ -761,8 +772,7 @@
         }
     }
 
-    @Override
-    public void unregisterRemoteTransitions() {
+    private void unregisterRemoteTransitions() {
         if (SEPARATE_RECENTS_ACTIVITY.get()) {
             return;
         }
@@ -883,7 +893,10 @@
                 && ENABLE_SHELL_STARTING_SURFACE;
     }
 
-    private boolean hasControlRemoteAppTransitionPermission() {
+    /**
+     * Returns true if we have permission to control remote app transisions
+     */
+    public boolean hasControlRemoteAppTransitionPermission() {
         return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
                 == PackageManager.PERMISSION_GRANTED;
     }
@@ -923,11 +936,6 @@
         }
 
         @Override
-        public Handler getHandler() {
-            return mHandler;
-        }
-
-        @Override
         public void onCreateAnimation(int transit,
                 RemoteAnimationTargetCompat[] appTargets,
                 RemoteAnimationTargetCompat[] wallpaperTargets,
@@ -1017,15 +1025,12 @@
 
         private final Handler mHandler;
         private final View mV;
+        private final RunnableList mOnEndCallback;
 
-        AppLaunchAnimationRunner(Handler handler, View v) {
+        AppLaunchAnimationRunner(Handler handler, View v, RunnableList onEndCallback) {
             mHandler = handler;
             mV = v;
-        }
-
-        @Override
-        public Handler getHandler() {
-            return mHandler;
+            mOnEndCallback = onEndCallback;
         }
 
         @Override
@@ -1060,7 +1065,7 @@
                 anim.addListener(mForceInvisibleListener);
             }
 
-            result.setAnimation(anim, mLauncher);
+            result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy);
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
index 03cc28e..16727ec 100644
--- a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
+++ b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import android.os.Handler;
-
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 /**
@@ -25,7 +23,7 @@
  * implementation.
  */
 public interface WrappedAnimationRunnerImpl {
-    Handler getHandler();
+
     void onCreateAnimation(int transit,
             RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets,
diff --git a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
index 1e1631b..e319275 100644
--- a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import android.os.Handler;
+
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.lang.ref.WeakReference;
@@ -40,8 +42,9 @@
         extends LauncherAnimationRunner {
     private WeakReference<R> mImpl;
 
-    public WrappedLauncherAnimationRunner(R animationRunnerImpl, boolean startAtFrontOfQueue) {
-        super(animationRunnerImpl.getHandler(), startAtFrontOfQueue);
+    public WrappedLauncherAnimationRunner(
+            Handler handler, R animationRunnerImpl, boolean startAtFrontOfQueue) {
+        super(handler, startAtFrontOfQueue);
         mImpl = new WeakReference<>(animationRunnerImpl);
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 544835c..ca5d3c3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -41,7 +41,7 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.QuickstepAppTransitionManagerImpl;
+import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.folder.Folder;
@@ -298,7 +298,7 @@
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
     public void onLauncherResumedOrPaused(boolean isResumed) {
-        long duration = QuickstepAppTransitionManagerImpl.CONTENT_ALPHA_DURATION;
+        long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
         if (mAnimator != null) {
             mAnimator.cancel();
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index b009629..e02f2c2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -30,7 +30,6 @@
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
 import android.content.Intent;
@@ -262,14 +261,13 @@
                 RecentsView rv = getOverviewPanel();
                 TaskView tasktolaunch = rv.getTaskViewAt(0);
                 if (tasktolaunch != null) {
-                    tasktolaunch.launchTask(false, success -> {
+                    tasktolaunch.launchTask(success -> {
                         if (!success) {
                             getStateManager().goToState(OVERVIEW);
-                            tasktolaunch.notifyTaskLaunchFailed(TAG);
                         } else {
                             getStateManager().moveToRestState();
                         }
-                    }, MAIN_EXECUTOR.getHandler());
+                    });
                 } else {
                     getStateManager().goToState(NORMAL);
                 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index d480b6d..1f68a04 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -155,7 +155,7 @@
     public void onBackPressed(Launcher launcher) {
         TaskView taskView = launcher.<RecentsView>getOverviewPanel().getRunningTaskView();
         if (taskView != null) {
-            taskView.launchTask(true);
+            taskView.launchTaskAnimated();
         } else {
             super.onBackPressed(launcher);
         }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index e0c041e..cf345e6 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -22,7 +22,7 @@
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -1624,19 +1624,17 @@
                 mGestureState.updateLastStartedTaskId(taskId);
                 boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
                         .contains(taskId);
-                nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
-                        success -> {
-                            resultCallback.accept(success);
-                            if (success) {
-                                if (hasTaskPreviouslyAppeared) {
-                                    onRestartPreviouslyAppearedTask();
-                                }
-                            } else {
-                                mActivityInterface.onLaunchTaskFailed();
-                                nextTask.notifyTaskLaunchFailed(TAG);
-                                mRecentsAnimationController.finish(true /* toRecents */, null);
-                            }
-                        }, MAIN_EXECUTOR.getHandler());
+                nextTask.launchTask(success -> {
+                    resultCallback.accept(success);
+                    if (success) {
+                        if (hasTaskPreviouslyAppeared) {
+                            onRestartPreviouslyAppearedTask();
+                        }
+                    } else {
+                        mActivityInterface.onLaunchTaskFailed();
+                        mRecentsAnimationController.finish(true /* toRecents */, null);
+                    }
+                }, true /* freezeTaskList */);
             } else {
                 mActivityInterface.onLaunchTaskFailed();
                 Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 985389e..63fdd0b 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -132,7 +132,7 @@
             }
             int currentPage = recents.getNextPage();
             if (currentPage >= 0 && currentPage < recents.getTaskViewCount()) {
-                ((TaskView) recents.getPageAt(currentPage)).launchTask(true);
+                ((TaskView) recents.getPageAt(currentPage)).launchTaskAnimated();
             } else {
                 recents.startHome();
             }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 1b2fd41..3d68d64 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -18,9 +18,9 @@
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 
-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.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
 import static com.android.launcher3.Utilities.createHomeIntent;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
@@ -30,7 +30,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.app.ActivityOptions;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
@@ -46,7 +45,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.WrappedAnimationRunnerImpl;
 import com.android.launcher3.WrappedLauncherAnimationRunner;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -54,7 +52,9 @@
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.ActivityTracker;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
@@ -94,7 +94,6 @@
 
     // Strong refs to runners which are cleared when the activity is destroyed
     private WrappedAnimationRunnerImpl mActivityLaunchAnimationRunner;
-    private SearchAdapterProvider mSearchAdapterProvider;
 
     /**
      * Init drag layer and overview panel views.
@@ -172,42 +171,40 @@
     }
 
     @Override
-    public ActivityOptions getActivityLaunchOptions(final View v) {
+    public ActivityOptionsWrapper getActivityLaunchOptions(final View v) {
         if (!(v instanceof TaskView)) {
-            return null;
+            return super.getActivityLaunchOptions(v);
         }
 
         final TaskView taskView = (TaskView) v;
-        mActivityLaunchAnimationRunner = new WrappedAnimationRunnerImpl() {
-            @Override
-            public Handler getHandler() {
-                return mUiHandler;
-            }
+        RunnableList onEndCallback = new RunnableList();
 
-            @Override
-            public void onCreateAnimation(int transit,
+        mActivityLaunchAnimationRunner = (int transit,
                     RemoteAnimationTargetCompat[] appTargets,
                     RemoteAnimationTargetCompat[] wallpaperTargets,
                     RemoteAnimationTargetCompat[] nonAppTargets,
-                    AnimationResult result) {
-                AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
-                        wallpaperTargets);
-                anim.addListener(resetStateListener());
-                result.setAnimation(anim, RecentsActivity.this);
-            }
+                    AnimationResult result) -> {
+            AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
+                    wallpaperTargets);
+            anim.addListener(resetStateListener());
+            result.setAnimation(anim, RecentsActivity.this, onEndCallback::executeAllAndDestroy);
         };
+
         final LauncherAnimationRunner wrapper = new WrappedLauncherAnimationRunner<>(
-                mActivityLaunchAnimationRunner, true /* startAtFrontOfQueue */);
-        return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
+                mUiHandler, mActivityLaunchAnimationRunner, true /* startAtFrontOfQueue */);
+        RemoteAnimationAdapterCompat adapterCompat = new RemoteAnimationAdapterCompat(
                 wrapper, RECENTS_LAUNCH_DURATION,
                 RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
-                        - STATUS_BAR_TRANSITION_PRE_DELAY));
+                        - STATUS_BAR_TRANSITION_PRE_DELAY);
+        return new ActivityOptionsWrapper(
+                ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
+                onEndCallback);
     }
 
     /**
      * Composes the animations for a launch from the recents list if possible.
      */
-    private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
+    private AnimatorSet  composeRecentsLaunchAnimator(TaskView taskView,
             RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets) {
         AnimatorSet target = new AnimatorSet();
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 65bb0f3..d81f07f 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -38,7 +38,6 @@
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.popup.SystemShortcut.AppInfo;
-import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
@@ -54,7 +53,6 @@
 
 import java.util.Collections;
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * Represents a system shortcut that can be shown for a recent task.
@@ -281,15 +279,9 @@
 
         @Override
         public void onClick(View view) {
-            Consumer<Boolean> resultCallback = success -> {
-                if (success) {
-                    SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(
-                            mTaskView.getTask().key.id);
-                } else {
-                    mTaskView.notifyTaskLaunchFailed(TAG);
-                }
-            };
-            mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
+            if (mTaskView.launchTaskAnimated() != null) {
+                SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(mTaskView.getTask().key.id);
+            }
             dismissTaskMenuView(mTarget);
             mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
                     .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP);
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index aa1b8e1..2feeffa 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -18,7 +18,7 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 3adb459..5c6da16 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -21,7 +21,6 @@
 import android.os.Handler;
 
 import com.android.launcher3.LauncherAnimationRunner;
-import com.android.launcher3.LauncherAnimationRunner.AnimationResult;
 import com.android.launcher3.WrappedAnimationRunnerImpl;
 import com.android.launcher3.WrappedLauncherAnimationRunner;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -36,23 +35,10 @@
             RemoteAnimationTargetCompat[] wallpaperTargets);
 
     ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
-        mAnimationRunner = new WrappedAnimationRunnerImpl() {
-            @Override
-            public Handler getHandler() {
-                return handler;
-            }
-
-            @Override
-            public void onCreateAnimation(int transit,
-                    RemoteAnimationTargetCompat[] appTargets,
-                    RemoteAnimationTargetCompat[] wallpaperTargets,
-                    RemoteAnimationTargetCompat[] nonApps,
-                    AnimationResult result) {
+        mAnimationRunner = (transit, appTargets, wallpaperTargets, nonApps, result) ->
                 result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
-            }
-        };
         final LauncherAnimationRunner wrapper = new WrappedLauncherAnimationRunner(
-                mAnimationRunner, false /* startAtFrontOfQueue */);
+                handler, mAnimationRunner, false /* startAtFrontOfQueue */);
         return ActivityOptionsCompat.makeRemoteAnimation(
                 new RemoteAnimationAdapterCompat(wrapper, duration, 0));
     }
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index c62f3e2..ceb343d 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -21,7 +21,7 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
+import static com.android.launcher3.QuickstepTransitionManager.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index efe1f75..d4548df 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1467,7 +1467,7 @@
             }
         }
         targetTask.setEndQuickswitchCuj(true);
-        targetTask.launchTask(true);
+        targetTask.launchTaskAnimated();
     }
 
     public void setRunningTaskIconScaledDown(boolean isScaledDown) {
@@ -2494,17 +2494,11 @@
         }
         mPendingAnimation.addEndListener(isSuccess -> {
             if (isSuccess) {
-                Consumer<Boolean> onLaunchResult = (result) -> {
-                    onTaskLaunchAnimationEnd(result);
-                    if (!result) {
-                        tv.notifyTaskLaunchFailed(TAG);
-                    }
-                };
                 if (LIVE_TILE.get()) {
                     finishRecentsAnimation(false /* toRecents */, null);
-                    onLaunchResult.accept(true /* success */);
+                    onTaskLaunchAnimationEnd(true /* success */);
                 } else {
-                    tv.launchTask(false, onLaunchResult, getHandler());
+                    tv.launchTask(this::onTaskLaunchAnimationEnd);
                 }
                 Task task = tv.getTask();
                 if (task != null) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 88545c6..e8cf29a 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -27,7 +27,7 @@
 import static android.view.Surface.ROTATION_90;
 import static android.widget.Toast.LENGTH_SHORT;
 
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.Utilities.comp;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
@@ -37,6 +37,7 @@
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 
@@ -52,7 +53,6 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -66,6 +66,8 @@
 import android.widget.FrameLayout;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
@@ -79,7 +81,9 @@
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.TransformingTouchDelegate;
 import com.android.launcher3.util.ViewPool.Reusable;
 import com.android.quickstep.RecentsModel;
@@ -335,7 +339,7 @@
                 });
                 anim.start();
             } else {
-                launchTask(true /* animate */);
+                launchTaskAnimated();
             }
             mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
                     .log(LAUNCHER_TASK_LAUNCH_TAP);
@@ -483,63 +487,62 @@
                 .createPlaybackController();
     }
 
-    public void launchTask(boolean animate) {
-        launchTask(animate, false /* freezeTaskList */);
-    }
-
-    public void launchTask(boolean animate, boolean freezeTaskList) {
-        launchTask(animate, freezeTaskList, (result) -> {
-            if (!result) {
-                notifyTaskLaunchFailed(TAG);
-            }
-        }, getHandler());
-    }
-
-    public void launchTask(boolean animate, Consumer<Boolean> resultCallback,
-            Handler resultCallbackHandler) {
-        launchTask(animate, false /* freezeTaskList */, resultCallback, resultCallbackHandler);
-    }
-
-    public void launchTask(boolean animate, boolean freezeTaskList, Consumer<Boolean> resultCallback,
-            Handler resultCallbackHandler) {
+    /**
+     * Starts the task associated with this view and animates the startup.
+     * @return CompletionStage to indicate the animation completion or null if the launch failed.
+     */
+    public RunnableList launchTaskAnimated() {
         if (mTask != null) {
-            final ActivityOptions opts;
             TestLogging.recordEvent(
                     TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
-            if (animate) {
-                opts = mActivity.getActivityLaunchOptions(this);
-                if (freezeTaskList) {
-                    ActivityOptionsCompat.setFreezeRecentTasksList(opts);
-                }
-                ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
-                        opts, resultCallback, resultCallbackHandler);
+            ActivityOptionsWrapper opts =  mActivity.getActivityLaunchOptions(this);
+            if (ActivityManagerWrapper.getInstance()
+                    .startActivityFromRecents(mTask.key, opts.options)) {
+                return opts.onEndCallback;
             } else {
-                opts = ActivityOptionsCompat.makeCustomAnimation(getContext(), 0, 0, () -> {
-                    if (resultCallback != null) {
-                        // Only post the animation start after the system has indicated that the
-                        // transition has started
-                        resultCallbackHandler.post(() -> resultCallback.accept(true));
-                    }
-                }, resultCallbackHandler);
-                if (freezeTaskList) {
-                    ActivityOptionsCompat.setFreezeRecentTasksList(opts);
-                }
-                UI_HELPER_EXECUTOR.execute(
-                        () -> ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(
-                                mTask.key,
-                                opts,
-                                (success) -> {
-                                    if (resultCallback != null && !success) {
-                                        // If the call to start activity failed, then post the
-                                        // result
-                                        // immediately, otherwise, wait for the animation start
-                                        // callback
-                                        // from the activity options above
-                                        resultCallbackHandler.post(
-                                                () -> resultCallback.accept(false));
-                                    }
-                                }, resultCallbackHandler));
+                notifyTaskLaunchFailed(TAG);
+                return null;
             }
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Starts the task associated with this view without any animation
+     */
+    public void launchTask(@NonNull Consumer<Boolean> callback) {
+        launchTask(callback, false /* freezeTaskList */);
+    }
+
+    /**
+     * Starts the task associated with this view without any animation
+     */
+    public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+        if (mTask != null) {
+            TestLogging.recordEvent(
+                    TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
+
+            // Indicate success once the system has indicated that the transition has started
+            ActivityOptions opts = ActivityOptionsCompat.makeCustomAnimation(
+                    getContext(), 0, 0, () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
+            if (freezeTaskList) {
+                ActivityOptionsCompat.setFreezeRecentTasksList(opts);
+            }
+            Task.TaskKey key = mTask.key;
+            UI_HELPER_EXECUTOR.execute(() -> {
+                if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) {
+                    // If the call to start activity failed, then post the result immediately,
+                    // otherwise, wait for the animation start callback from the activity options
+                    // above
+                    MAIN_EXECUTOR.post(() -> {
+                        notifyTaskLaunchFailed(TAG);
+                        callback.accept(false);
+                    });
+                }
+            });
+        } else {
+            callback.accept(false);
         }
     }
 
@@ -1123,7 +1126,7 @@
         return getRecentsView().mOrientationState.getOrientationHandler();
     }
 
-    public void notifyTaskLaunchFailed(String tag) {
+    private void notifyTaskLaunchFailed(String tag) {
         String msg = "Failed to launch task";
         if (mTask != null) {
             msg += " (task=" + mTask.key.baseIntent + " userId=" + mTask.key.userId + ")";
diff --git a/res/values/config.xml b/res/values/config.xml
index 89415b8..23294c8 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -61,7 +61,6 @@
     <!-- Various classes overriden by projects/build flavors. -->
     <string name="folder_name_provider_class" translatable="false"></string>
     <string name="stats_log_manager_class" translatable="false"></string>
-    <string name="app_transition_manager_class" translatable="false"></string>
     <string name="instant_app_resolver_class" translatable="false"></string>
     <string name="main_process_initializer_class" translatable="false"></string>
     <string name="app_launch_tracker_class" translatable="false"></string>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 062ab71..f77f7e8 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -279,7 +279,7 @@
     /**
      * Used to set the override visibility state, used only to handle the transition home with the
      * recents animation.
-     * @see QuickstepAppTransitionManagerImpl#createWallpaperOpenRunner
+     * @see QuickstepTransitionManager#createWallpaperOpenRunner
      */
     public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
         mForceInvisible |= flag;
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 5bfde15..e38ab74 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -27,6 +27,7 @@
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Process;
 import android.os.StrictMode;
@@ -40,6 +41,7 @@
 import android.view.WindowMetrics;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -52,10 +54,12 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.WindowBounds;
@@ -76,6 +80,7 @@
     protected boolean mIsSafeModeEnabled;
 
     private Runnable mOnStartCallback;
+    private RunnableList mOnResumeCallbacks = new RunnableList();
 
     private int mThemeRes = R.style.AppTheme;
 
@@ -98,6 +103,16 @@
     }
 
     @Override
+    protected void onResume() {
+        super.onResume();
+        mOnResumeCallbacks.executeAllAndClear();
+    }
+
+    public void addOnResumeCallback(Runnable callback) {
+        mOnResumeCallbacks.add(callback);
+    }
+
+    @Override
     public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
         updateTheme();
     }
@@ -149,20 +164,35 @@
         return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
     }
 
-    public final Bundle getActivityLaunchOptionsAsBundle(View v) {
-        ActivityOptions activityOptions = getActivityLaunchOptions(v);
-        return activityOptions == null ? null : activityOptions.toBundle();
+    @NonNull
+    public ActivityOptionsWrapper getActivityLaunchOptions(View v) {
+        int left = 0, top = 0;
+        int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+        if (v instanceof BubbleTextView) {
+            // Launch from center of icon, not entire view
+            Drawable icon = ((BubbleTextView) v).getIcon();
+            if (icon != null) {
+                Rect bounds = icon.getBounds();
+                left = (width - bounds.width()) / 2;
+                top = v.getPaddingTop();
+                width = bounds.width();
+                height = bounds.height();
+            }
+        }
+        ActivityOptions options =
+                ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+        RunnableList callback = new RunnableList();
+        addOnResumeCallback(callback::executeAllAndDestroy);
+        return new ActivityOptionsWrapper(options, callback);
     }
 
-    public abstract ActivityOptions getActivityLaunchOptions(View v);
-
     public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item) {
         if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
             return false;
         }
 
-        Bundle optsBundle = (v != null) ? getActivityLaunchOptionsAsBundle(v) : null;
+        Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v).toBundle() : null;
         UserHandle user = item == null ? null : item.user;
 
         // Prepare intent
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index e0be6de..2b58fb6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -50,7 +50,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
-import com.android.launcher3.Launcher.OnResumeCallback;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dot.DotInfo;
@@ -81,7 +80,7 @@
  * because we want to make the bubble taller than the text and TextView's clip is
  * too aggressive.
  */
-public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
+public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
         IconLabelDotView, DraggableView, Reorderable {
 
     private static final int DISPLAY_WORKSPACE = 0;
@@ -431,13 +430,6 @@
         }
     }
 
-    @Override
-    public void onLauncherResume() {
-        // Reset the pressed state of icon that was locked in the press state while activity
-        // was launching
-        setStayPressed(false);
-    }
-
     void clearPressedBackground() {
         setPressed(false);
         setStayPressed(false);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 78e6f68..1546ee3 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -61,7 +61,6 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
-import android.app.ActivityOptions;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.content.ActivityNotFoundException;
@@ -276,7 +275,6 @@
 
     private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
 
-    private LauncherAppTransitionManager mAppTransitionManager;
     private Configuration mOldConfig;
 
     private LiveSearchManager mLiveSearchManager;
@@ -312,8 +310,6 @@
     @Thunk
     boolean mWorkspaceLoading = true;
 
-    private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
-
     // Used to notify when an activity launch has been deferred because launcher is not yet resumed
     // TODO: See if we can remove this later
     private Runnable mOnDeferredActivityLaunchCallback;
@@ -419,9 +415,6 @@
         crossFadeWithPreviousAppearance();
         mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
 
-        mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
-        mAppTransitionManager.registerRemoteAnimations();
-
         boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
         if (internalStateHandled) {
             if (savedInstanceState != null) {
@@ -1090,15 +1083,6 @@
                 TraceHelper.FLAG_UI_EVENT);
         super.onResume();
 
-        if (!mOnResumeCallbacks.isEmpty()) {
-            final ArrayList<OnResumeCallback> resumeCallbacks = new ArrayList<>(mOnResumeCallbacks);
-            mOnResumeCallbacks.clear();
-            for (int i = resumeCallbacks.size() - 1; i >= 0; i--) {
-                resumeCallbacks.get(i).onLauncherResume();
-            }
-            resumeCallbacks.clear();
-        }
-
         if (mDeferOverlayCallbacks) {
             scheduleDeferredCheck();
         } else {
@@ -1609,7 +1593,6 @@
         LauncherAppState.getIDP(this).removeOnChangeListener(this);
 
         mOverlayManager.onActivityDestroyed(this);
-        mAppTransitionManager.onActivityDestroyed();
         mUserChangedCallbackCloseable.close();
         mLiveSearchManager.stop();
     }
@@ -1935,16 +1918,6 @@
 
     @TargetApi(Build.VERSION_CODES.M)
     @Override
-    public ActivityOptions getActivityLaunchOptions(View v) {
-        return mAppTransitionManager.getActivityLaunchOptions(this, v);
-    }
-
-    public LauncherAppTransitionManager getAppTransitionManager() {
-        return mAppTransitionManager;
-    }
-
-    @TargetApi(Build.VERSION_CODES.M)
-    @Override
     protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
         // Due to legacy reasons, direct call shortcuts require Launchers to have the
         // corresponding permission. Show the appropriate permission prompt if that
@@ -1993,7 +1966,7 @@
             // state when we return to launcher.
             BubbleTextView btv = (BubbleTextView) v;
             btv.setStayPressed(true);
-            addOnResumeCallback(btv);
+            addOnResumeCallback(() -> btv.setStayPressed(false));
         }
         return success;
     }
@@ -2037,10 +2010,6 @@
         return result;
     }
 
-    public void addOnResumeCallback(OnResumeCallback callback) {
-        mOnResumeCallbacks.add(callback);
-    }
-
     /**
      * Persistant callback which notifies when an activity launch is deferred because the activity
      * was not yet resumed.
@@ -2814,15 +2783,6 @@
         return (T) activityContext;
     }
 
-
-    /**
-     * Callback for listening for onResume
-     */
-    public interface OnResumeCallback {
-
-        void onLauncherResume();
-    }
-
     /**
      * Cross-fades the launcher's updated appearance with its previous appearance.
      *
@@ -2865,6 +2825,10 @@
         return false;
     }
 
+    public boolean supportsAdaptiveIconAnimation(View clickedView) {
+        return false;
+    }
+
     public DragOptions getDefaultWorkspaceDragOptions() {
         return new DragOptions();
     }
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
deleted file mode 100644
index d0bf577..0000000
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ /dev/null
@@ -1,91 +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.launcher3;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-
-import com.android.launcher3.util.ResourceBasedOverride;
-
-/**
- * Manages the opening and closing app transitions from Launcher.
- */
-public class LauncherAppTransitionManager implements ResourceBasedOverride {
-
-    public static LauncherAppTransitionManager newInstance(Context context) {
-        return Overrides.getObject(LauncherAppTransitionManager.class,
-                context, R.string.app_transition_manager_class);
-    }
-
-    public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
-        int left = 0, top = 0;
-        int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
-        if (v instanceof BubbleTextView) {
-            // Launch from center of icon, not entire view
-            Drawable icon = ((BubbleTextView) v).getIcon();
-            if (icon != null) {
-                Rect bounds = icon.getBounds();
-                left = (width - bounds.width()) / 2;
-                top = v.getPaddingTop();
-                width = bounds.width();
-                height = bounds.height();
-            }
-        }
-        return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
-    }
-
-    public boolean supportsAdaptiveIconAnimation(View clickedView) {
-        return false;
-    }
-
-    /**
-     * Registers remote animations for certain system transitions.
-     */
-    public void registerRemoteAnimations() {
-        // Do nothing
-    }
-
-    /**
-     * Handles clean up when activity is destroyed.
-     */
-    public void onActivityDestroyed() {
-        // Do nothing
-    }
-
-    /**
-     * Unregisters all remote animations.
-     */
-    public void unregisterRemoteAnimations() {
-        // Do nothing
-    }
-
-    /**
-     * Registers remote transitions for certain system transitions.
-     */
-    public void registerRemoteTransitions() {
-        // Do nothing
-    }
-
-    /**
-     * Unregisters all remote transitions.
-     */
-    public void unregisterRemoteTransitions() {
-        // Do nothing
-    }
-}
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 4fd87cb..8bc5ad0 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -33,7 +33,6 @@
 import android.view.View;
 import android.widget.Toast;
 
-import com.android.launcher3.Launcher.OnResumeCallback;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.FileLog;
@@ -228,7 +227,7 @@
             DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
             if (target != null) {
                 deferred.mPackageName = target.getPackageName();
-                mLauncher.addOnResumeCallback(deferred);
+                mLauncher.addOnResumeCallback(deferred::onLauncherResume);
             } else {
                 deferred.sendFailure();
             }
@@ -311,7 +310,7 @@
      * A wrapper around {@link DragSource} which delays the {@link #onDropCompleted} action until
      * {@link #onLauncherResume}
      */
-    private class DeferredOnComplete implements DragSource, OnResumeCallback {
+    private class DeferredOnComplete implements DragSource {
 
         private final DragSource mOriginal;
         private final Context mContext;
@@ -330,7 +329,6 @@
             mDragObject = d;
         }
 
-        @Override
         public void onLauncherResume() {
             // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
             if (new PackageManagerHelper(mContext).getApplicationInfo(mPackageName,
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 0e8d4ae..f5e74b7 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -17,7 +17,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityOptions;
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
@@ -168,11 +167,6 @@
     }
 
     @Override
-    public ActivityOptions getActivityLaunchOptions(View v) {
-        return null;
-    }
-
-    @Override
     protected void reapplyUi() { }
 
     @Override
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 61bd30a..2e54904 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -180,7 +180,7 @@
                 LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
                 try {
                     launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
-                            launcher.getActivityLaunchOptionsAsBundle(v));
+                            launcher.getActivityLaunchOptions(v).toBundle());
                     return;
                 } catch (Exception e) {
                     Log.e(TAG, "Unable to launch market intent for package=" + packageName, e);
@@ -304,7 +304,7 @@
                 intent.setPackage(null);
             }
         }
-        if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation(v)) {
+        if (v != null && launcher.supportsAdaptiveIconAnimation(v)) {
             // Preload the icon to reduce latency b/w swapping the floating view with the original.
             FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
         }
diff --git a/src/com/android/launcher3/util/ActivityOptionsWrapper.java b/src/com/android/launcher3/util/ActivityOptionsWrapper.java
new file mode 100644
index 0000000..99cc1f7
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityOptionsWrapper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.util;
+
+
+import android.app.ActivityOptions;
+import android.os.Bundle;
+
+/**
+ * A wrapper around {@link ActivityOptions} to allow custom functionality in launcher
+ */
+public class ActivityOptionsWrapper {
+
+    public final ActivityOptions options;
+    public final RunnableList onEndCallback;
+
+    public ActivityOptionsWrapper(ActivityOptions options, RunnableList onEndCallback) {
+        this.options = options;
+        this.onEndCallback = onEndCallback;
+    }
+
+    /**
+     * @see {@link ActivityOptions#toBundle()}
+     */
+    public Bundle toBundle() {
+        return options.toBundle();
+    }
+}
diff --git a/src/com/android/launcher3/util/RunnableList.java b/src/com/android/launcher3/util/RunnableList.java
new file mode 100644
index 0000000..55add14
--- /dev/null
+++ b/src/com/android/launcher3/util/RunnableList.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 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.util;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to hold a list of runnable
+ */
+public class RunnableList {
+
+    private ArrayList<Runnable> mList = null;
+    private boolean mDestroyed = false;
+
+    /**
+     * Ads a runnable to this list
+     */
+    public void add(Runnable runnable) {
+        if (mDestroyed) {
+            runnable.run();
+            return;
+        }
+        if (mList == null) {
+            mList = new ArrayList<>();
+        }
+        mList.add(runnable);
+    }
+
+    /**
+     * Destroys the list, executing any pending callbacks. All new callbacks are
+     * immediately executed
+     */
+    public void executeAllAndDestroy() {
+        mDestroyed = true;
+        executeAllAndClear();
+    }
+
+    /**
+     * Executes all previously added runnable and clears the list
+     */
+    public void executeAllAndClear() {
+        if (mList != null) {
+            ArrayList<Runnable> list = mList;
+            mList = null;
+            int count = list.size();
+            for (int i = 0; i < count; i++) {
+                list.get(i).run();
+            }
+        }
+    }
+}