Merge "Delegate horizontal scrolls from the Hotseat to the Workspace" into ub-launcher3-rvc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index f1b71e8..f1db144 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -48,12 +48,16 @@
     HotseatContainer hotseat = 2;
     FolderContainer folder = 3;
     AllAppsContainer all_apps_container = 4;
+    WidgetsContainer widgets_container = 5;
   }
 }
 
 message AllAppsContainer {
 }
 
+message WidgetsContainer {
+}
+
 enum Origin {
   UNKNOWN = 0;
   DEFAULT_LAYOUT = 1;       // icon automatically placed in workspace, folder, hotseat
diff --git a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
index ffe906c..7b3e378 100644
--- a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
+++ b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
@@ -18,6 +18,7 @@
     android:id="@+id/drag_layer"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:clipChildren="false"
     android:fitsSystemWindows="true">
 
     <com.android.quickstep.fallback.FallbackRecentsView
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 7a73e50..1fe3643 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -274,6 +274,7 @@
         }
         if (mAppPredictor != null) {
             mAppPredictor.destroy();
+            mAppPredictor = null;
         }
         WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this);
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index ad6a10b..95087ba 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -217,7 +217,9 @@
                 break;
             }
             case OVERVIEW_STATE_ORDINAL: {
-                DiscoveryBounce.showForOverviewIfNeeded(this);
+                RecentsView recentsView = getOverviewPanel();
+                DiscoveryBounce.showForOverviewIfNeeded(this,
+                        recentsView.getPagedOrientationHandler());
                 RecentsView rv = getOverviewPanel();
                 sendCustomAccessibilityEvent(
                         rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index a1cc60e..085b9b3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -71,7 +71,7 @@
             builder.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
             mRecentsView.updateEmptyMessage();
         } else {
-            builder.getAnim().addListener(
+            builder.addListener(
                     AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
         }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index fa0d3f3..8ff05f2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -45,8 +45,10 @@
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return super.getVerticalProgress(launcher);
         }
+        RecentsView recentsView = launcher.getOverviewPanel();
         int transitionLength = LayoutUtils.getShelfTrackingDistance(launcher,
-                launcher.getDeviceProfile());
+                launcher.getDeviceProfile(),
+                recentsView.getPagedOrientationHandler());
         AllAppsTransitionController controller = launcher.getAllAppsController();
         float scrollRange = Math.max(controller.getShiftRange(), 1);
         float progressDelta = (transitionLength / scrollRange);
@@ -73,9 +75,11 @@
     public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
         if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
             // Translate hotseat offscreen if we show it in overview.
+            RecentsView recentsView = launcher.getOverviewPanel();
             ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher);
             scaleAndTranslation.translationY += LayoutUtils.getShelfTrackingDistance(launcher,
-                    launcher.getDeviceProfile());
+                    launcher.getDeviceProfile(),
+                    recentsView.getPagedOrientationHandler());
             return scaleAndTranslation;
         }
         return super.getHotseatScaleAndTranslation(launcher);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 9f31608..d174bfd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.content.Context;
@@ -127,7 +128,9 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
+        RecentsView recentsView = launcher.getOverviewPanel();
+        if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher) ||
+                hideShelfInTwoButtonLandscape(launcher, recentsView.getPagedOrientationHandler())) {
             return OVERVIEW_BUTTONS;
         } else if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 11593a1..94c7771 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -49,64 +49,62 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.view.View;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.Hotseat;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.util.RecentsAtomicAnimationFactory;
 import com.android.quickstep.views.RecentsView;
 
 /**
  * Animation factory for quickstep specific transitions
  */
-public class QuickstepAtomicAnimationFactory extends AtomicAnimationFactory<LauncherState> {
+public class QuickstepAtomicAnimationFactory extends
+        RecentsAtomicAnimationFactory<Launcher, LauncherState> {
 
     // Scale recents takes before animating in
     private static final float RECENTS_PREPARE_SCALE = 1.33f;
 
-    public static final int INDEX_SHELF_ANIM = 0;
-    public static final int INDEX_RECENTS_FADE_ANIM = 1;
-    public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2;
-    public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM = 3;
-    private static final int ANIM_COUNT = 4;
+    public static final int INDEX_SHELF_ANIM = RecentsAtomicAnimationFactory.NEXT_INDEX + 0;
+    public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM =
+            RecentsAtomicAnimationFactory.NEXT_INDEX + 1;
+
+    private static final int MY_ANIM_COUNT = 2;
+    protected static final int NEXT_INDEX = RecentsAtomicAnimationFactory.NEXT_INDEX
+            + MY_ANIM_COUNT;
 
     public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
 
-    private final QuickstepLauncher mLauncher;
-
-    public QuickstepAtomicAnimationFactory(QuickstepLauncher launcher) {
-        super(ANIM_COUNT);
-        mLauncher = launcher;
+    public QuickstepAtomicAnimationFactory(QuickstepLauncher activity) {
+        super(activity, MY_ANIM_COUNT);
     }
 
     @Override
     public Animator createStateElementAnimation(int index, float... values) {
         switch (index) {
             case INDEX_SHELF_ANIM: {
-                AllAppsTransitionController aatc = mLauncher.getAllAppsController();
+                AllAppsTransitionController aatc = mActivity.getAllAppsController();
                 Animator springAnim = aatc.createSpringAnimation(values);
 
-                if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+                if ((OVERVIEW.getVisibleElements(mActivity) & HOTSEAT_ICONS) != 0) {
                     // Translate hotseat with the shelf until reaching overview.
-                    float overviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
-                    ScaleAndTranslation sat = OVERVIEW.getHotseatScaleAndTranslation(mLauncher);
+                    float overviewProgress = OVERVIEW.getVerticalProgress(mActivity);
+                    ScaleAndTranslation sat = OVERVIEW.getHotseatScaleAndTranslation(mActivity);
                     float shiftRange = aatc.getShiftRange();
                     if (values.length == 1) {
                         values = new float[] {aatc.getProgress(), values[0]};
@@ -114,9 +112,9 @@
                     ValueAnimator hotseatAnim = ValueAnimator.ofFloat(values);
                     hotseatAnim.addUpdateListener(anim -> {
                         float progress = (Float) anim.getAnimatedValue();
-                        if (progress >= overviewProgress || mLauncher.isInState(BACKGROUND_APP)) {
+                        if (progress >= overviewProgress || mActivity.isInState(BACKGROUND_APP)) {
                             float hotseatShift = (progress - overviewProgress) * shiftRange;
-                            mLauncher.getHotseat().setTranslationY(hotseatShift + sat.translationY);
+                            mActivity.getHotseat().setTranslationY(hotseatShift + sat.translationY);
                         }
                     });
                     hotseatAnim.setInterpolator(LINEAR);
@@ -130,34 +128,21 @@
 
                 return springAnim;
             }
-            case INDEX_RECENTS_FADE_ANIM:
-                return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
-                        RecentsView.CONTENT_ALPHA, values);
-            case INDEX_RECENTS_TRANSLATE_X_ANIM: {
-                RecentsView rv = mLauncher.getOverviewPanel();
-                return new SpringAnimationBuilder(mLauncher)
-                        .setMinimumVisibleChange(1f / rv.getPageOffsetScale())
-                        .setDampingRatio(0.8f)
-                        .setStiffness(250)
-                        .setValues(values)
-                        .build(rv, ADJACENT_PAGE_OFFSET);
-            }
             case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
                 StateAnimationConfig config = new StateAnimationConfig();
                 config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
 
                 config.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
                 config.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
-                if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+                if ((OVERVIEW.getVisibleElements(mActivity) & HOTSEAT_ICONS) != 0) {
                     config.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
                     config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
                 }
 
-                StateManager<LauncherState> stateManager = mLauncher.getStateManager();
+                StateManager<LauncherState> stateManager = mActivity.getStateManager();
                 return stateManager.createAtomicAnimation(
                         stateManager.getCurrentStableState(), OVERVIEW, config);
             }
-
             default:
                 return super.createStateElementAnimation(index, values);
         }
@@ -172,7 +157,7 @@
             config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
             config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
-            Workspace workspace = mLauncher.getWorkspace();
+            Workspace workspace = mActivity.getWorkspace();
 
             // Start from a higher workspace scale, but only if we're invisible so we don't jump.
             boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
@@ -186,13 +171,13 @@
                 workspace.setScaleX(0.92f);
                 workspace.setScaleY(0.92f);
             }
-            Hotseat hotseat = mLauncher.getHotseat();
+            Hotseat hotseat = mActivity.getHotseat();
             boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
             if (!isHotseatVisible) {
                 hotseat.setScaleX(0.92f);
                 hotseat.setScaleY(0.92f);
                 if (ENABLE_OVERVIEW_ACTIONS.get()) {
-                    AllAppsContainerView qsbContainer = mLauncher.getAppsView();
+                    AllAppsContainerView qsbContainer = mActivity.getAppsView();
                     View qsb = qsbContainer.getSearchView();
                     boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
                     if (!qsbVisible) {
@@ -209,7 +194,7 @@
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
             config.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
         } else if ((fromState == NORMAL || fromState == HINT_STATE) && toState == OVERVIEW) {
-            if (SysUINavigationMode.getMode(mLauncher) == NO_BUTTON) {
+            if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
                 config.setInterpolator(ANIM_WORKSPACE_SCALE,
                         fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
                 config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
@@ -217,7 +202,7 @@
                 config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
 
                 // Scale up the recents, if it is not coming from the side
-                RecentsView overview = mLauncher.getOverviewPanel();
+                RecentsView overview = mActivity.getOverviewPanel();
                 if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
                     SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
                 }
@@ -225,7 +210,7 @@
             config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
             Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
-                    && removeShelfFromOverview(mLauncher)
+                    && removeShelfFromOverview(mActivity)
                     ? OVERSHOOT_1_2
                     : OVERSHOOT_1_7;
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 7385658..c1b68ab 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -116,7 +116,7 @@
         mRecentsView = mLauncher.getOverviewPanel();
         mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
         mYRange = LayoutUtils.getShelfTrackingDistance(
-            mLauncher, mLauncher.getDeviceProfile());
+            mLauncher, mLauncher.getDeviceProfile(), mRecentsView.getPagedOrientationHandler());
         mMotionPauseDetector = new MotionPauseDetector(mLauncher);
         mMotionPauseMinDisplacement = mLauncher.getResources().getDimension(
                 R.dimen.motion_pause_detector_min_displacement_from_app);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index f38ff10..f4d1629 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -18,29 +18,26 @@
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.graphics.Rect;
 import android.util.Log;
-import android.view.View;
+import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.TransactionCompat;
 
 /**
  * Provider for the atomic (for 3-button mode) remote window animation from the app to the overview.
@@ -80,7 +77,6 @@
                     controller.dispatchOnStart();
                     controller.getAnimationPlayer().end();
                 });
-        factory.onRemoteAnimationReceived(null);
         factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
         factory.setRecentsAttachedToAppWindow(true, false);
         mActivity = activity;
@@ -97,32 +93,25 @@
     @Override
     public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets) {
-        if (mRecentsView != null) {
-            mRecentsView.setRunningTaskIconScaledDown(true);
+        PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
+        if (mActivity == null) {
+            Log.e(TAG, "Animation created, before activity");
+            return pa.buildAnim();
         }
 
-        AnimatorSet anim = new AnimatorSet();
-        anim.addListener(new AnimationSuccessListener() {
+        mRecentsView.setRunningTaskIconScaledDown(true);
+        pa.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
                 mActivityInterface.onSwipeUpToRecentsComplete();
-                if (mRecentsView != null) {
-                    mRecentsView.animateUpRunningTaskIconScale();
-                }
+                mRecentsView.animateUpRunningTaskIconScale();
             }
         });
-        if (mActivity == null) {
-            Log.e(TAG, "Animation created, before activity");
-            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
-            return anim;
-        }
 
         DepthController depthController = mActivityInterface.getDepthController();
         if (depthController != null) {
-            anim.play(ObjectAnimator.ofFloat(depthController, DEPTH,
-                    BACKGROUND_APP.getDepth(mActivity),
-                    OVERVIEW.getDepth(mActivity))
-                    .setDuration(RECENTS_LAUNCH_DURATION));
+            pa.addFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(mActivity),
+                    OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
         }
 
         RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
@@ -132,53 +121,39 @@
         RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
         if (runningTaskTarget == null) {
             Log.e(TAG, "No closing app");
-            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
-            return anim;
+            return pa.buildAnim();
         }
 
-        final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper(
-            mRecentsView.getPagedViewOrientedState(), mActivity);
-
-        // At this point, the activity is already started and laid-out. Get the home-bounds
-        // relative to the screen using the rootView of the activity.
-        int loc[] = new int[2];
-        View rootView = mActivity.getRootView();
-        rootView.getLocationOnScreen(loc);
-        Rect homeBounds = new Rect(loc[0], loc[1],
-                loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
-        clipHelper.updateSource(homeBounds, runningTaskTarget);
-
-        Rect targetRect = new Rect();
-        mActivityInterface.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
-                targetRect);
-        clipHelper.updateTargetRect(targetRect);
-        clipHelper.prepareAnimation(mActivity.getDeviceProfile());
+        TaskViewSimulator tsv = new TaskViewSimulator(mActivity, mRecentsView.getSizeStrategy());
+        tsv.setDp(mActivity.getDeviceProfile());
+        tsv.setPreview(runningTaskTarget);
+        tsv.setLayoutRotation(mRecentsView.getPagedViewOrientedState().getTouchRotation(),
+                mRecentsView.getPagedViewOrientedState().getDisplayRotation());
 
         TransformParams params = new TransformParams()
-                .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
-        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
-        valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
-        valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
-        valueAnimator.addUpdateListener((v) -> {
-            params.setProgress((float) v.getAnimatedValue()).setTargetSet(targets);
-            clipHelper.applyTransform(params);
-        });
+                .setTargetSet(targets)
+                .setSyncTransactionApplier(
+                        new SyncRtSurfaceTransactionApplierCompat(mActivity.getRootView()));
 
+        AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { });
+        params.setBaseAlphaCallback((t, a) -> recentsAlpha.value);
+
+        Interpolator taskInterpolator;
         if (targets.isAnimatingHome()) {
-            // If we are animating home, fade in the opening targets
-            RemoteAnimationTargets openingSet = new RemoteAnimationTargets(appTargets,
-                    wallpaperTargets, MODE_OPENING);
-
-            TransactionCompat transaction = new TransactionCompat();
-            valueAnimator.addUpdateListener((v) -> {
-                for (RemoteAnimationTargetCompat app : openingSet.apps) {
-                    transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
-                }
-                transaction.apply();
-            });
+            taskInterpolator = TOUCH_RESPONSE_INTERPOLATOR;
+            pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1, TOUCH_RESPONSE_INTERPOLATOR);
+        } else {
+            // When animation from app to recents, the recents layer is drawn on top of the app. To
+            // prevent the overlap, we animate the task first and then quickly fade in the recents.
+            taskInterpolator = clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0, 0.8f);
+            pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1,
+                    clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0.8f, 1));
         }
-        anim.play(valueAnimator);
-        return anim;
+
+        pa.addFloat(params, TransformParams.PROGRESS, 0, 1, taskInterpolator);
+        tsv.addAppToOverviewAnim(pa, taskInterpolator);
+        pa.addOnFrameCallback(() -> tsv.apply(params));
+        return pa.buildAnim();
     }
 
     /**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index bbee67c..d957418 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,8 +15,6 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -25,8 +23,6 @@
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 
 import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
@@ -36,6 +32,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
@@ -49,7 +46,9 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.views.FloatingIconView;
@@ -357,12 +356,13 @@
     protected void initTransitionEndpoints(DeviceProfile dp) {
         mDp = dp;
 
-        mTransitionDragLength = mActivityInterface.getSwipeUpDestinationAndLength(
-                dp, mContext, TEMP_RECT);
         mTaskViewSimulator.setDp(dp);
         mTaskViewSimulator.setLayoutRotation(
                 mDeviceState.getCurrentActiveRotation(),
                 mDeviceState.getDisplayRotation());
+        mTransitionDragLength = mActivityInterface.getSwipeUpDestinationAndLength(
+                dp, mContext, TEMP_RECT,
+                mTaskViewSimulator.getOrientationState().getOrientationHandler());
 
         if (mDeviceState.isFullyGesturalNavMode()) {
             // We can drag all the way to the top of the screen.
@@ -377,18 +377,9 @@
             mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
         }
 
-        AnimatorSet anim = new AnimatorSet();
-        anim.setDuration(mTransitionDragLength * 2);
-        anim.setInterpolator(t -> t * mDragLengthFactor);
-        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.recentsViewScale,
-                AnimatedFloat.VALUE,
-                mTaskViewSimulator.getFullScreenScale(), 1));
-        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.fullScreenProgress,
-                AnimatedFloat.VALUE,
-                BACKGROUND_APP.getOverviewFullscreenProgress(),
-                OVERVIEW.getOverviewFullscreenProgress()));
-        mWindowTransitionController =
-                AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
+        PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
+        mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor);
+        mWindowTransitionController = pa.createPlaybackController();
     }
 
     /**
@@ -398,7 +389,16 @@
 
     protected boolean onActivityInit(Boolean alreadyOnHome) {
         T createdActivity = mActivityInterface.getCreatedActivity();
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
+        }
         if (createdActivity != null) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
+            }
+            ((RecentsView) createdActivity.getOverviewPanel())
+                    .setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
+                            mDeviceState.getDisplayRotation());
             initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
                 .getDeviceProfile(mContext));
         }
@@ -510,8 +510,8 @@
 
     public interface Factory {
 
-        BaseSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs,
-                boolean continuingLastGesture, boolean isLikelyToStartNewTask);
+        BaseSwipeUpHandler newHandler(
+                GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
     }
 
     protected interface RunningWindowAnim {
@@ -651,14 +651,11 @@
         }
 
         @Override
-        public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app, int targetMode,
-                TransformParams params) {
-            if (app.mode == targetMode
-                    && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-                builder.withMatrix(mMatrix)
-                        .withWindowCrop(mCropRect)
-                        .withCornerRadius(params.getCornerRadius());
-            }
+        public void onBuildTargetParams(
+                Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+            builder.withMatrix(mMatrix)
+                    .withWindowCrop(mCropRect)
+                    .withCornerRadius(params.getCornerRadius());
         }
 
         @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
similarity index 91%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
index 82a3e79..343f28a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
@@ -17,7 +17,6 @@
 
 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.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -38,29 +37,24 @@
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 
 import android.animation.Animator;
-import android.animation.AnimatorSet;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.PointF;
-import android.graphics.RectF;
 import android.os.Build;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.view.View;
 import android.view.View.OnApplyWindowInsetsListener;
 import android.view.ViewTreeObserver.OnDrawListener;
 import android.view.WindowInsets;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
@@ -72,7 +66,6 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.BaseActivityInterface.AnimationFactory;
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.inputconsumers.OverviewInputConsumer;
@@ -80,7 +73,6 @@
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.util.StaggeredWorkspaceAnim;
 import com.android.quickstep.util.TransformParams.TargetAlphaProvider;
 import com.android.quickstep.views.LiveTileOverlay;
 import com.android.quickstep.views.RecentsView;
@@ -92,11 +84,12 @@
 
 /**
  * Handles the navigation gestures when Launcher is the default home activity.
+ * TODO: Merge this with BaseSwipeUpHandler
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class LauncherSwipeHandler extends BaseSwipeUpHandler<Launcher, RecentsView>
-        implements OnApplyWindowInsetsListener {
-    private static final String TAG = LauncherSwipeHandler.class.getSimpleName();
+public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q extends RecentsView>
+        extends BaseSwipeUpHandler<T, Q> implements OnApplyWindowInsetsListener {
+    private static final String TAG = BaseSwipeUpHandlerV2.class.getSimpleName();
 
     private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
 
@@ -108,9 +101,11 @@
     }
 
     // Launcher UI related states
-    private static final int STATE_LAUNCHER_PRESENT = getFlagForIndex(0, "STATE_LAUNCHER_PRESENT");
-    private static final int STATE_LAUNCHER_STARTED = getFlagForIndex(1, "STATE_LAUNCHER_STARTED");
-    private static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
+    protected static final int STATE_LAUNCHER_PRESENT =
+            getFlagForIndex(0, "STATE_LAUNCHER_PRESENT");
+    protected static final int STATE_LAUNCHER_STARTED =
+            getFlagForIndex(1, "STATE_LAUNCHER_STARTED");
+    protected static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
 
     // Internal initialization states
     private static final int STATE_APP_CONTROLLER_RECEIVED =
@@ -122,7 +117,7 @@
     private static final int STATE_SCALED_CONTROLLER_RECENTS =
             getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS");
 
-    private static final int STATE_HANDLER_INVALIDATED =
+    protected static final int STATE_HANDLER_INVALIDATED =
             getFlagForIndex(6, "STATE_HANDLER_INVALIDATED");
     private static final int STATE_GESTURE_STARTED =
             getFlagForIndex(7, "STATE_GESTURE_STARTED");
@@ -163,7 +158,7 @@
      */
     private static final int LOG_NO_OP_PAGE_INDEX = -1;
 
-    private final TaskAnimationManager mTaskAnimationManager;
+    protected final TaskAnimationManager mTaskAnimationManager;
 
     // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
     private RunningWindowAnim mRunningWindowAnim;
@@ -193,7 +188,7 @@
 
     private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
 
-    public LauncherSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
+    public BaseSwipeUpHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             long touchTimeMs, boolean continuingLastGesture,
             InputConsumerController inputConsumer) {
@@ -222,9 +217,6 @@
                         | STATE_GESTURE_CANCELLED,
                 this::resetStateForAnimationCancel);
 
-        mStateCallback.runOnceAtState(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
-                this::sendRemoteAnimationsToAnimationFactory);
-
         mStateCallback.runOnceAtState(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
                 this::resumeLastTask);
         mStateCallback.runOnceAtState(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED,
@@ -272,10 +264,12 @@
     @Override
     protected boolean onActivityInit(Boolean alreadyOnHome) {
         super.onActivityInit(alreadyOnHome);
-        final Launcher activity = mActivityInterface.getCreatedActivity();
+        final T activity = mActivityInterface.getCreatedActivity();
         if (mActivity == activity) {
             return true;
         }
+        mTaskViewSimulator.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
+                mDeviceState.getDisplayRotation());
         if (mActivity != null) {
             // The launcher may have been recreated as a result of device rotation.
             int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
@@ -321,7 +315,7 @@
     }
 
     private void onLauncherStart() {
-        final Launcher activity = mActivityInterface.getCreatedActivity();
+        final T activity = mActivityInterface.getCreatedActivity();
         if (mActivity != activity) {
             return;
         }
@@ -409,6 +403,10 @@
             updateSysUiFlags(mCurrentShift.value);
             return;
         }
+        notifyGestureAnimationStartToRecents();
+    }
+
+    protected void notifyGestureAnimationStartToRecents() {
         mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId());
     }
 
@@ -416,10 +414,6 @@
         mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
     }
 
-    private void sendRemoteAnimationsToAnimationFactory() {
-        mAnimationFactory.onRemoteAnimationReceived(mRecentsAnimationTargets);
-    }
-
     private void initializeLauncherAnimationController() {
         buildAnimationController();
 
@@ -631,7 +625,7 @@
      */
     @UiThread
     private void notifyGestureStartedAsync() {
-        final Launcher curActivity = mActivity;
+        final T curActivity = mActivity;
         if (curActivity != null) {
             // Once the gesture starts, we can no longer transition home through the button, so
             // reset the force override of the activity visibility
@@ -679,7 +673,7 @@
         endLauncherTransitionController();
         if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             // Hide the task view, if not already hidden
-            setTargetAlphaProvider(LauncherSwipeHandler::getHiddenTargetAlpha);
+            setTargetAlphaProvider(BaseSwipeUpHandlerV2::getHiddenTargetAlpha);
         }
 
         StatefulActivity activity = mActivityInterface.getCreatedActivity();
@@ -909,6 +903,8 @@
                 interpolator, target, velocityPxPerMs));
     }
 
+    protected abstract HomeAnimationFactory createHomeAnimationFactory(long duration);
+
     @UiThread
     private void animateToProgressInternal(float start, float end, long duration,
             Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
@@ -917,67 +913,7 @@
         maybeUpdateRecentsAttachedState();
 
         if (mGestureState.getEndTarget() == HOME) {
-            HomeAnimationFactory homeAnimFactory;
-            if (mActivity != null) {
-                final TaskView runningTaskView = mRecentsView.getRunningTaskView();
-                final View workspaceView;
-                if (runningTaskView != null
-                        && runningTaskView.getTask().key.getComponent() != null) {
-                    workspaceView = mActivity.getWorkspace().getFirstMatchForAppClose(
-                            runningTaskView.getTask().key.getComponent().getPackageName(),
-                            UserHandle.of(runningTaskView.getTask().key.userId));
-                } else {
-                    workspaceView = null;
-                }
-                final RectF iconLocation = new RectF();
-                boolean canUseWorkspaceView =
-                        workspaceView != null && workspaceView.isAttachedToWindow();
-                FloatingIconView floatingIconView = canUseWorkspaceView
-                        ? FloatingIconView.getFloatingIconView(mActivity, workspaceView,
-                        true /* hideOriginal */, iconLocation, false /* isOpening */)
-                        : null;
-
-                mActivity.getRootView().setForceHideBackArrow(true);
-                mActivityInterface.setHintUserWillBeActive();
-
-                homeAnimFactory = new HomeAnimationFactory(floatingIconView) {
-
-                    @Override
-                    public RectF getWindowTargetRect() {
-                        if (canUseWorkspaceView) {
-                            return iconLocation;
-                        } else {
-                            return super.getWindowTargetRect();
-                        }
-                    }
-
-                    @NonNull
-                    @Override
-                    public AnimatorPlaybackController createActivityAnimationToHome() {
-                        // Return an empty APC here since we have an non-user controlled animation
-                        // to home.
-                        long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
-                        return mActivity.getStateManager().createAnimationToNewWorkspace(
-                                NORMAL, accuracy, 0 /* animComponents */);
-                    }
-
-                    @Override
-                    public void playAtomicAnimation(float velocity) {
-                        new StaggeredWorkspaceAnim(mActivity, velocity,
-                                true /* animateOverviewScrim */).start();
-                    }
-                };
-
-            } else {
-                homeAnimFactory = new HomeAnimationFactory(null) {
-                    @Override
-                    public AnimatorPlaybackController createActivityAnimationToHome() {
-                        return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
-                    }
-                };
-                mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
-                        isPresent -> mRecentsView.startHome());
-            }
+            HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration);
             RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
             windowAnim.addAnimatorListener(new AnimationSuccessListener() {
                 @Override
@@ -1301,14 +1237,15 @@
             // If there are no targets or the animation not started, then there is nothing to finish
             mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
         } else {
-            mRecentsAnimationController.finish(true /* toRecents */,
-                    () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED),
-                    true /* sendUserLeaveHint */);
+            finishRecentsControllerToHome(
+                    () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
         }
         ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
         doLogGesture(HOME);
     }
 
+    protected abstract void finishRecentsControllerToHome(Runnable callback);
+
     private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
         endLauncherTransitionController();
         mActivityInterface.onSwipeUpToRecentsComplete();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index 4b3af31..7d08fac 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -15,26 +15,21 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
-import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
-import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 
 import android.content.Context;
-import android.graphics.PointF;
 import android.graphics.Rect;
+import android.view.MotionEvent;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.views.RecentsView;
@@ -54,12 +49,14 @@
     public static final FallbackActivityInterface INSTANCE = new FallbackActivityInterface();
 
     private FallbackActivityInterface() {
-        super(false);
+        super(false, DEFAULT, BACKGROUND_APP);
     }
 
+    /** 2 */
     @Override
-    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
-        calculateTaskSize(context, dp, outRect);
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
+            PagedOrientationHandler orientationHandler) {
+        calculateTaskSize(context, dp, outRect, orientationHandler);
         if (dp.isVerticalBarLayout()
                 && SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
             Rect targetInsets = dp.getInsets();
@@ -70,6 +67,13 @@
         }
     }
 
+    /** 4 */
+    @Override
+    public void onSwipeUpToHomeComplete() {
+        onSwipeUpToRecentsComplete();
+    }
+
+    /** 5 */
     @Override
     public void onAssistantVisibilityChanged(float visibility) {
         // This class becomes active when the screen is locked.
@@ -77,50 +81,13 @@
         // set to zero prior to this class becoming active.
     }
 
+    /** 6 */
     @Override
     public AnimationFactory prepareRecentsUI(
             boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
-        RecentsActivity activity = getCreatedActivity();
-        if (activity == null) {
-            return (transitionLength) -> { };
-        }
-
-        activity.getStateManager().goToState(BACKGROUND_APP);
-        FallbackRecentsView rv = activity.getOverviewPanel();
-        rv.setContentAlpha(0);
-
-        return new AnimationFactory() {
-
-            boolean isAnimatingToRecents = false;
-
-            @Override
-            public void onRemoteAnimationReceived(RemoteAnimationTargets targets) {
-                isAnimatingToRecents = targets != null && targets.isAnimatingHome();
-                if (!isAnimatingToRecents) {
-                    rv.setContentAlpha(1);
-                }
-                createActivityInterface(getSwipeUpDestinationAndLength(
-                        activity.getDeviceProfile(), activity, new Rect()));
-            }
-
-            @Override
-            public void createActivityInterface(long transitionLength) {
-                PendingAnimation pa = new PendingAnimation(transitionLength * 2);
-
-                if (isAnimatingToRecents) {
-                    pa.addFloat(rv, CONTENT_ALPHA, 0, 1, LINEAR);
-                }
-
-                pa.addFloat(rv, SCALE_PROPERTY, rv.getMaxScaleForFullScreen(), 1, LINEAR);
-                pa.addFloat(rv, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
-                AnimatorPlaybackController controller = pa.createPlaybackController();
-
-                // Since we are changing the start position of the UI, reapply the state, at the end
-                controller.setEndAction(() -> activity.getStateManager().goToState(
-                        controller.getInterpolatedProgress() > 0.5 ? DEFAULT : BACKGROUND_APP));
-                callback.accept(controller);
-            }
-        };
+        DefaultAnimationFactory factory = new DefaultAnimationFactory(callback);
+        factory.initUI();
+        return factory;
     }
 
     @Override
@@ -164,6 +131,15 @@
     }
 
     @Override
+    public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+        // In non-gesture mode, user might be clicking on the home button which would directly
+        // start the home activity instead of going through recents. In that case, defer starting
+        // recents until we are sure it is a gesture.
+        return !deviceState.isFullyGesturalNavMode()
+                || super.deferStartingActivity(deviceState, ev);
+    }
+
+    @Override
     public int getContainerType() {
         RecentsActivity activity = getCreatedActivity();
         boolean visible = activity != null && activity.isStarted() && activity.hasWindowFocus();
@@ -188,12 +164,8 @@
     }
 
     @Override
-    public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
-        out.set(dp.widthPx, dp.heightPx);
-    }
-
-    @Override
-    protected float getExtraSpace(Context context, DeviceProfile dp) {
+    protected float getExtraSpace(Context context, DeviceProfile dp,
+            PagedOrientationHandler orientationHandler) {
         return showOverviewActions(context)
                 ? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
                 : 0;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index db41bd6..7b614c2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -15,537 +15,117 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
-import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
-import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
-import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
-import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
-import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
-import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
-import android.animation.Animator;
-import android.animation.AnimatorSet;
 import android.app.ActivityOptions;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.PointF;
-import android.os.Bundle;
-import android.util.ArrayMap;
-import android.view.MotionEvent;
 
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.AnimationSuccessListener;
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.util.ObjectWrapper;
-import com.android.quickstep.BaseActivityInterface.AnimationFactory;
-import com.android.quickstep.GestureState.GestureEndTarget;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.quickstep.fallback.FallbackRecentsView;
-import com.android.quickstep.util.RectFSpringAnim;
-import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.quickstep.util.TransformParams;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 /**
  * Handles the navigation gestures when a 3rd party launcher is the default home activity.
  */
-public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
+public class FallbackSwipeHandler extends
+        BaseSwipeUpHandlerV2<RecentsActivity, FallbackRecentsView> {
 
-    private static final String[] STATE_NAMES = DEBUG_STATES ? new String[5] : null;
-
-    private static int getFlagForIndex(int index, String name) {
-        if (DEBUG_STATES) {
-            STATE_NAMES[index] = name;
-        }
-        return 1 << index;
-    }
-
-    private static final int STATE_RECENTS_PRESENT =
-            getFlagForIndex(0, "STATE_RECENTS_PRESENT");
-    private static final int STATE_HANDLER_INVALIDATED =
-            getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
-
-    private static final int STATE_GESTURE_CANCELLED =
-            getFlagForIndex(2, "STATE_GESTURE_CANCELLED");
-    private static final int STATE_GESTURE_COMPLETED =
-            getFlagForIndex(3, "STATE_GESTURE_COMPLETED");
-    private static final int STATE_APP_CONTROLLER_RECEIVED =
-            getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
-
-    public static class EndTargetAnimationParams {
-        private final float mEndProgress;
-        private final long mDurationMultiplier;
-        private final float mLauncherAlpha;
-
-        EndTargetAnimationParams(float endProgress, long durationMultiplier, float launcherAlpha) {
-            mEndProgress = endProgress;
-            mDurationMultiplier = durationMultiplier;
-            mLauncherAlpha = launcherAlpha;
-        }
-    }
-    private final ArrayMap<GestureEndTarget, EndTargetAnimationParams>
-            mEndTargetAnimationParams = new ArrayMap();
-
-    private final AnimatedFloat mLauncherAlpha = new AnimatedFloat(this::onLauncherAlphaChanged);
-
-    private boolean mOverviewThresholdPassed = false;
-
-    private final boolean mInQuickSwitchMode;
-    private final boolean mContinuingLastGesture;
+    private FallbackHomeAnimationFactory mActiveAnimationFactory;
     private final boolean mRunningOverHome;
-    private final boolean mSwipeUpOverHome;
-    private boolean mTouchedHomeDuringTransition;
-
-    private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
-    private RunningWindowAnim mFinishAnimation;
-
-    // Used to control Recents components throughout the swipe gesture.
-    private AnimatorPlaybackController mLauncherTransitionController;
-    private boolean mHasLauncherTransitionControllerStarted;
-
-    private AnimationFactory mAnimationFactory = (t) -> { };
 
     public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
-            GestureState gestureState, InputConsumerController inputConsumer,
-            boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
-        super(context, deviceState, gestureState, inputConsumer);
+            TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
+            boolean continuingLastGesture, InputConsumerController inputConsumer) {
+        super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
+                continuingLastGesture, inputConsumer);
 
-        mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
-        mContinuingLastGesture = continuingLastGesture;
         mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
-        mSwipeUpOverHome = mRunningOverHome && !mInQuickSwitchMode;
-
-        // Keep the home launcher invisible until we decide to land there.
-        mLauncherAlpha.value = mRunningOverHome ? 1 : 0;
-        if (mSwipeUpOverHome) {
-            mTransformParams.setBaseAlphaCallback((t, a) -> 1 - mLauncherAlpha.value);
-        } else {
-            mTransformParams.setBaseAlphaCallback((t, a) -> mLauncherAlpha.value);
-        }
-
-        // Going home has an extra long progress to ensure that it animates into the screen
-        mEndTargetAnimationParams.put(HOME, new EndTargetAnimationParams(3, 100, 1));
-        mEndTargetAnimationParams.put(RECENTS, new EndTargetAnimationParams(1, 300, 0));
-        mEndTargetAnimationParams.put(LAST_TASK, new EndTargetAnimationParams(0, 150, 1));
-        mEndTargetAnimationParams.put(NEW_TASK, new EndTargetAnimationParams(0, 150, 1));
-
-        initAfterSubclassConstructor();
-        initStateCallbacks();
-    }
-
-    private void initStateCallbacks() {
-        mStateCallback = new MultiStateCallback(STATE_NAMES);
-
-        mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
-                this::onHandlerInvalidated);
-        mStateCallback.runOnceAtState(STATE_RECENTS_PRESENT | STATE_HANDLER_INVALIDATED,
-                this::onHandlerInvalidatedWithRecents);
-
-        mStateCallback.runOnceAtState(STATE_GESTURE_CANCELLED | STATE_APP_CONTROLLER_RECEIVED,
-                this::finishAnimationTargetSetAnimationComplete);
-
-        if (mInQuickSwitchMode) {
-            mStateCallback.runOnceAtState(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED
-                            | STATE_RECENTS_PRESENT,
-                    this::finishAnimationTargetSet);
-        } else {
-            mStateCallback.runOnceAtState(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED,
-                    this::finishAnimationTargetSet);
-        }
-    }
-
-    private void onLauncherAlphaChanged() {
-        if (mRecentsAnimationTargets != null && mGestureState.getEndTarget() == null) {
-            applyWindowTransform();
-        }
     }
 
     @Override
-    protected boolean onActivityInit(Boolean alreadyOnHome) {
-        super.onActivityInit(alreadyOnHome);
-        mActivity = mActivityInterface.getCreatedActivity();
-        mRecentsView = mActivity.getOverviewPanel();
-        mRecentsView.setOnPageTransitionEndCallback(null);
-        linkRecentsViewScroll();
-        if (!mContinuingLastGesture) {
-            if (mRunningOverHome) {
-                mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
-            } else {
-                mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId());
-            }
-        }
-        mStateCallback.setStateOnUiThread(STATE_RECENTS_PRESENT);
-        mDeviceState.enableMultipleRegions(false);
-
-        mAnimationFactory = mActivityInterface.prepareRecentsUI(alreadyOnHome,
-                this::onAnimatorPlaybackControllerCreated);
-        mAnimationFactory.createActivityInterface(mTransitionDragLength);
-        return true;
-    }
-
-    @Override
-    protected void initTransitionEndpoints(DeviceProfile dp) {
-        super.initTransitionEndpoints(dp);
-        if (canCreateNewOrUpdateExistingLauncherTransitionController()) {
-            mAnimationFactory.createActivityInterface(mTransitionDragLength);
-        }
-    }
-
-    private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
-        mLauncherTransitionController = anim;
-        mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor);
-        mLauncherTransitionController.dispatchOnStart();
-        updateLauncherTransitionProgress();
-    }
-
-    private void updateLauncherTransitionProgress() {
-        if (mLauncherTransitionController == null
-                || !canCreateNewOrUpdateExistingLauncherTransitionController()) {
-            return;
-        }
-        // Normalize the progress to 0 to 1, as the animation controller will clamp it to that
-        // anyway. The controller mimics the drag length factor by applying it to its interpolators.
-        float progress = mCurrentShift.value / mDragLengthFactor;
-        mLauncherTransitionController.setPlayFraction(progress);
-    }
-
-    /**
-     * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
-     * (it has its own animation) or if we're already animating the current controller.
-     * @return Whether we can create the launcher controller or update its progress.
-     */
-    private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
-        return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted;
-    }
-
-    @Override
-    protected boolean moveWindowWithRecentsScroll() {
-        return mInQuickSwitchMode;
-    }
-
-    @Override
-    public void initWhenReady(Intent intent) {
-        if (mInQuickSwitchMode) {
-            // Only init if we are in quickswitch mode
-            super.initWhenReady(intent);
-        }
-    }
-
-    @Override
-    public void updateDisplacement(float displacement) {
-        if (!mInQuickSwitchMode) {
-            super.updateDisplacement(displacement);
-        }
-    }
-
-    @Override
-    protected InputConsumer createNewInputProxyHandler() {
-        // Just consume all input on the active task
-        return new InputConsumer() {
-            @Override
-            public int getType() {
-                return InputConsumer.TYPE_NO_OP;
-            }
-
-            @Override
-            public void onMotionEvent(MotionEvent ev) {
-                mTouchedHomeDuringTransition = true;
-            }
-        };
-    }
-
-    @Override
-    public void onMotionPauseChanged(boolean isPaused) {
-        if (!mInQuickSwitchMode && mDeviceState.isFullyGesturalNavMode()) {
-            updateOverviewThresholdPassed(isPaused);
-        }
-    }
-
-    private void updateOverviewThresholdPassed(boolean passed) {
-        if (passed != mOverviewThresholdPassed) {
-            mOverviewThresholdPassed = passed;
-            if (mSwipeUpOverHome) {
-                mLauncherAlpha.animateToValue(mLauncherAlpha.value, passed ? 0 : 1)
-                        .setDuration(150).start();
-            }
-            performHapticFeedback();
-        }
-    }
-
-    @Override
-    public Intent getLaunchIntent() {
-        if (mInQuickSwitchMode || mSwipeUpOverHome || !mDeviceState.isFullyGesturalNavMode()) {
-            return mGestureState.getOverviewIntent();
-        } else {
-            return mGestureState.getHomeIntent();
-        }
-    }
-
-    @Override
-    public void updateFinalShift() {
-        mTransformParams.setProgress(mCurrentShift.value);
-        if (mRecentsAnimationController != null) {
-            boolean swipeUpThresholdPassed = mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
-            mRecentsAnimationController.setUseLauncherSystemBarFlags(mInQuickSwitchMode
-                    || swipeUpThresholdPassed);
-            mRecentsAnimationController.setSplitScreenMinimized(!mInQuickSwitchMode
-                    && swipeUpThresholdPassed);
-        }
-
-        if (!mInQuickSwitchMode && !mDeviceState.isFullyGesturalNavMode()) {
-            updateOverviewThresholdPassed(mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW);
-        }
-
-        applyWindowTransform();
-        updateLauncherTransitionProgress();
-    }
-
-    @Override
-    public void onGestureCancelled() {
-        updateDisplacement(0);
-        mGestureState.setEndTarget(LAST_TASK);
-        mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED);
-    }
-
-    @Override
-    public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
-        mEndVelocityPxPerMs.set(0, velocity.y / 1000);
-        if (mInQuickSwitchMode) {
-            // For now set it to non-null, it will be reset before starting the animation
-            mGestureState.setEndTarget(LAST_TASK);
-        } else {
-            float flingThreshold = mContext.getResources()
-                    .getDimension(R.dimen.quickstep_fling_threshold_velocity);
-            boolean isFling = Math.abs(endVelocity) > flingThreshold;
-
-            if (mDeviceState.isFullyGesturalNavMode()) {
-                if (isFling) {
-                    mGestureState.setEndTarget(endVelocity < 0 ? HOME : LAST_TASK);
-                } else if (mOverviewThresholdPassed) {
-                    mGestureState.setEndTarget(RECENTS);
-                } else {
-                    mGestureState.setEndTarget(mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW
-                            ? HOME
-                            : LAST_TASK);
-                }
-            } else {
-                GestureEndTarget startState = mSwipeUpOverHome ? HOME : LAST_TASK;
-                if (isFling) {
-                    mGestureState.setEndTarget(endVelocity < 0 ? RECENTS : startState);
-                } else {
-                    mGestureState.setEndTarget(mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW
-                            ? RECENTS
-                            : startState);
-                }
-            }
-        }
-        mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
-    }
-
-    @Override
-    public void onConsumerAboutToBeSwitched() {
-        if (mInQuickSwitchMode && mGestureState.getEndTarget() != null) {
-            mGestureState.setEndTarget(NEW_TASK);
-
-            mCanceled = true;
-            mCurrentShift.cancelAnimation();
-            if (mFinishAnimation != null) {
-                mFinishAnimation.cancel();
-            }
-
-            if (mRecentsView != null) {
-                mRecentsView.setOnScrollChangeListener(null);
-            }
-        } else {
-            mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
-        }
-    }
-
-    private void onHandlerInvalidated() {
-        mActivityInitListener.unregister();
-        if (mGestureEndCallback != null) {
-            mGestureEndCallback.run();
-        }
-        if (mFinishAnimation != null) {
-            mFinishAnimation.end();
-        }
-    }
-
-    private void onHandlerInvalidatedWithRecents() {
-        mRecentsView.onGestureAnimationEnd();
-        mRecentsView.setDisallowScrollToClearAll(false);
-        mRecentsView.getClearAllButton().setVisibilityAlpha(1);
-    }
-
-    private void finishAnimationTargetSetAnimationComplete() {
-        switch (mGestureState.getEndTarget()) {
-            case HOME: {
-                if (mSwipeUpOverHome) {
-                    mRecentsAnimationController.finish(false, null, false);
-                    // Send a home intent to clear the task stack
-                    mContext.startActivity(mGestureState.getHomeIntent());
-                } else {
-                    // Notify swipe-to-home (recents animation) is finished
-                    SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished();
-                    mRecentsAnimationController.finish(true, () -> {
-                        if (!mTouchedHomeDuringTransition) {
-                            // If the user hasn't interacted with the screen during the transition,
-                            // send a home intent so launcher can go to the default home screen.
-                            // (If they are trying to touch something, we don't want to interfere.)
-                            mContext.startActivity(mGestureState.getHomeIntent());
-                        }
-                    }, true);
-                }
-                break;
-            }
-            case LAST_TASK:
-                mRecentsAnimationController.finish(false, null, false);
-                break;
-            case RECENTS: {
-                if (mSwipeUpOverHome || !mDeviceState.isFullyGesturalNavMode()) {
-                    mRecentsAnimationController.finish(true, null, true);
-                    break;
-                }
-
-                final int runningTaskId = mGestureState.getRunningTaskId();
-                ThumbnailData thumbnail = mRecentsAnimationController.screenshotTask(runningTaskId);
-                mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
-                        false /* screenshot */);
-
-                ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
-                ActivityOptionsCompat.setFreezeRecentTasksList(options);
-
-                Bundle extras = new Bundle();
-                extras.putBinder(EXTRA_THUMBNAIL, new ObjectWrapper<>(thumbnail));
-                extras.putInt(EXTRA_TASK_ID, runningTaskId);
-
-                Intent intent = new Intent(mGestureState.getOverviewIntent())
-                        .putExtras(extras);
-                mContext.startActivity(intent, options.toBundle());
-                mRecentsAnimationController.cleanupScreenshot();
-                break;
-            }
-            case NEW_TASK: {
-                startNewTask(success -> { });
-                break;
-            }
-        }
-
-        mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
-    }
-
-    private void finishAnimationTargetSet() {
-        if (mInQuickSwitchMode) {
-            // Recalculate the end target, some views might have been initialized after
-            // gesture has ended.
-            if (mRecentsView == null || !hasTargets()) {
-                mGestureState.setEndTarget(LAST_TASK);
-            } else {
-                final int runningTaskIndex = getLastAppearedTaskIndex();
-                final int taskToLaunch = mRecentsView.getNextPage();
-                mGestureState.setEndTarget(
-                        (runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex)
-                                ? NEW_TASK
-                                : LAST_TASK);
-            }
-        }
-
-        EndTargetAnimationParams params = mEndTargetAnimationParams.get(mGestureState.getEndTarget());
-        float endProgress = params.mEndProgress;
-        long duration = (long) (params.mDurationMultiplier *
-                Math.abs(endProgress - mCurrentShift.value));
-        if (mRecentsView != null) {
-            duration = Math.max(duration, mRecentsView.getScroller().getDuration());
-        }
-        if (mCurrentShift.value != endProgress || mInQuickSwitchMode) {
-            AnimationSuccessListener endListener = new AnimationSuccessListener() {
-
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    if (mRecentsView != null) {
-                        mRecentsView.setOnPageTransitionEndCallback(FallbackSwipeHandler.this
-                                ::finishAnimationTargetSetAnimationComplete);
-                    } else {
-                        finishAnimationTargetSetAnimationComplete();
-                    }
-                    mFinishAnimation = null;
-                }
-            };
-
-            if (mGestureState.getEndTarget() == HOME && !mRunningOverHome) {
-                mRecentsAnimationController.enableInputProxy(mInputConsumer,
-                        this::createNewInputProxyHandler);
-                RectFSpringAnim anim = createWindowAnimationToHome(mCurrentShift.value, duration);
-                anim.addAnimatorListener(endListener);
-                anim.start(mContext, mEndVelocityPxPerMs);
-                mFinishAnimation = RunningWindowAnim.wrap(anim);
-            } else {
-
-                AnimatorSet anim = new AnimatorSet();
-                anim.play(mLauncherAlpha.animateToValue(
-                        mLauncherAlpha.value, params.mLauncherAlpha));
-                anim.play(mCurrentShift.animateToValue(mCurrentShift.value, endProgress));
-
-                anim.setDuration(duration);
-                anim.addListener(endListener);
-                anim.start();
-                mFinishAnimation = RunningWindowAnim.wrap(anim);
-            }
-
-        } else {
-            finishAnimationTargetSetAnimationComplete();
-        }
-    }
-
-    @Override
-    public void onRecentsAnimationStart(RecentsAnimationController controller,
-            RecentsAnimationTargets targets) {
-        super.onRecentsAnimationStart(controller, targets);
-        mRecentsAnimationController.enableInputConsumer();
-        applyWindowTransform();
-
-        mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
-    }
-
-    @Override
-    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-        mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
-
-        // Defer clearing the controller and the targets until after we've updated the state
-        super.onRecentsAnimationCanceled(thumbnailData);
+    protected HomeAnimationFactory createHomeAnimationFactory(long duration) {
+        mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
+        ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
+        mContext.startActivity(new Intent(mGestureState.getHomeIntent()), options.toBundle());
+        return mActiveAnimationFactory;
     }
 
     @Override
     protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
-        return true;
-    }
+        if (mActiveAnimationFactory != null
+                && mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) {
+            mActiveAnimationFactory = null;
+            return false;
+        }
 
-    /**
-     * Creates an animation that transforms the current app window into the home app.
-     * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
-     */
-    private RectFSpringAnim createWindowAnimationToHome(float startProgress, long duration) {
-        HomeAnimationFactory factory = new HomeAnimationFactory(null) {
-            @Override
-            public AnimatorPlaybackController createActivityAnimationToHome() {
-                AnimatorSet anim = new AnimatorSet();
-                Animator fadeInLauncher = mLauncherAlpha.animateToValue(mLauncherAlpha.value, 1);
-                fadeInLauncher.setInterpolator(ACCEL_2);
-                anim.play(fadeInLauncher);
-                anim.setDuration(duration);
-                return AnimatorPlaybackController.wrap(anim, duration);
-            }
-        };
-        return createWindowAnimationToHome(startProgress, factory);
+        return super.handleTaskAppeared(appearedTaskTarget);
     }
 
     @Override
-    protected float getWindowAlpha(float progress) {
-        return 1 - ACCEL_1_5.getInterpolation(progress);
+    protected void finishRecentsControllerToHome(Runnable callback) {
+        mRecentsAnimationController.finish(
+                false /* toRecents */, callback, true /* sendUserLeaveHint */);
+    }
+
+    @Override
+    protected void notifyGestureAnimationStartToRecents() {
+        if (mRunningOverHome) {
+            mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
+        } else {
+            super.notifyGestureAnimationStartToRecents();
+        }
+    }
+
+    private class FallbackHomeAnimationFactory extends HomeAnimationFactory
+            implements TransformParams.BuilderProxy {
+
+        private final TransformParams mHomeAlphaParams = new TransformParams();
+        private final AnimatedFloat mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha);
+
+        private final long mDuration;
+        FallbackHomeAnimationFactory(long duration) {
+            super(null);
+            mDuration = duration;
+        }
+
+        @NonNull
+        @Override
+        public AnimatorPlaybackController createActivityAnimationToHome() {
+            PendingAnimation pa = new PendingAnimation(mDuration);
+            pa.setFloat(mHomeAlpha, AnimatedFloat.VALUE, 1, LINEAR);
+            return pa.createPlaybackController();
+        }
+
+        private void updateHomeAlpha() {
+            mHomeAlphaParams.setProgress(mHomeAlpha.value);
+            if (mHomeAlphaParams.getTargetSet() != null) {
+                mHomeAlphaParams.applySurfaceParams(mHomeAlphaParams.createSurfaceParams(this));
+            }
+        }
+
+        public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+            if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) {
+                RemoteAnimationTargets targets = new RemoteAnimationTargets(
+                        new RemoteAnimationTargetCompat[] {appearedTaskTarget},
+                        new RemoteAnimationTargetCompat[0], appearedTaskTarget.mode);
+                mHomeAlphaParams.setTargetSet(targets);
+                updateHomeAlpha();
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app, int targetMode,
+                TransformParams params) { }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 4ebfbd6..dae2f41 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -15,30 +15,18 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
 import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
-import static com.android.quickstep.LauncherSwipeHandler.RECENTS_ATTACH_DURATION;
 import static com.android.quickstep.SysUINavigationMode.getMode;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
 import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
-import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 
-import android.animation.Animator;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.Log;
-import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
 import androidx.annotation.Nullable;
@@ -57,13 +45,12 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
 import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.plugins.shared.LauncherOverlayManager;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -75,23 +62,24 @@
  * {@link BaseActivityInterface} for the in-launcher recents.
  */
 public final class LauncherActivityInterface extends
-        BaseActivityInterface<LauncherState, Launcher> {
+        BaseActivityInterface<LauncherState, BaseQuickstepLauncher> {
 
     public static final LauncherActivityInterface INSTANCE = new LauncherActivityInterface();
 
     private LauncherActivityInterface() {
-        super(true);
+        super(true, OVERVIEW, BACKGROUND_APP);
     }
 
     @Override
-    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
-        calculateTaskSize(context, dp, outRect);
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
+            PagedOrientationHandler orientationHandler) {
+        calculateTaskSize(context, dp, outRect, orientationHandler);
         if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
             Rect targetInsets = dp.getInsets();
             int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
             return dp.hotseatBarSizePx + hotseatInset;
         } else {
-            return LayoutUtils.getShelfTrackingDistance(context, dp);
+            return LayoutUtils.getShelfTrackingDistance(context, dp, orientationHandler);
         }
     }
 
@@ -100,7 +88,9 @@
         super.onSwipeUpToRecentsComplete();
         Launcher launcher = getCreatedActivity();
         if (launcher != null) {
-            DiscoveryBounce.showForOverviewIfNeeded(launcher);
+            RecentsView recentsView = launcher.getOverviewPanel();
+            DiscoveryBounce.showForOverviewIfNeeded(launcher,
+                    recentsView.getPagedOrientationHandler());
         }
     }
 
@@ -128,119 +118,43 @@
     @Override
     public AnimationFactory prepareRecentsUI(
             boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
-        BaseQuickstepLauncher launcher = getCreatedActivity();
-        final LauncherState startState = launcher.getStateManager().getState();
-
-        LauncherState resetState = startState;
-        if (startState.shouldDisableRestore()) {
-            resetState = launcher.getStateManager().getRestState();
-        }
-        launcher.getStateManager().setRestState(resetState);
-
-        launcher.getStateManager().goToState(BACKGROUND_APP, false);
-        // Since all apps is not visible, we can safely reset the scroll position.
-        // This ensures then the next swipe up to all-apps starts from scroll 0.
-        launcher.getAppsView().reset(false /* animate */);
-
-        return new AnimationFactory() {
-            private final ShelfPeekAnim mShelfAnim = launcher.getShelfPeekAnim();
-            private boolean mIsAttachedToWindow;
-
-            @Override
-            public void createActivityInterface(long transitionLength) {
-                callback.accept(createBackgroundToOverviewAnim(launcher, transitionLength));
-                // Creating the activity controller animation sometimes reapplies the launcher state
-                // (because we set the animation as the current state animation), so we reapply the
-                // attached state here as well to ensure recents is shown/hidden appropriately.
-                if (SysUINavigationMode.getMode(launcher) == Mode.NO_BUTTON) {
-                    setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
-                }
-            }
-
-            @Override
-            public void onTransitionCancelled() {
-                launcher.getStateManager().goToState(startState, false /* animate */);
-            }
-
+        DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
             @Override
             public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
                     long duration) {
-                mShelfAnim.setShelfState(shelfState, interpolator, duration);
+                mActivity.getShelfPeekAnim().setShelfState(shelfState, interpolator, duration);
             }
 
             @Override
-            public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
-                if (mIsAttachedToWindow == attached && animate) {
-                    return;
-                }
-                mIsAttachedToWindow = attached;
-                LauncherRecentsView recentsView = launcher.getOverviewPanel();
-                Animator fadeAnim = launcher.getStateManager()
-                        .createStateElementAnimation(
-                        INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
+            protected void createBackgroundToOverviewAnim(BaseQuickstepLauncher activity,
+                    PendingAnimation pa) {
+                super.createBackgroundToOverviewAnim(activity, pa);
 
-                float fromTranslation = attached ? 1 : 0;
-                float toTranslation = attached ? 0 : 1;
-                launcher.getStateManager()
-                        .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
-                if (!recentsView.isShown() && animate) {
-                    ADJACENT_PAGE_OFFSET.set(recentsView, fromTranslation);
-                } else {
-                    fromTranslation = ADJACENT_PAGE_OFFSET.get(recentsView);
-                }
-                if (!animate) {
-                    ADJACENT_PAGE_OFFSET.set(recentsView, toTranslation);
-                } else {
-                    launcher.getStateManager().createStateElementAnimation(
-                            INDEX_RECENTS_TRANSLATE_X_ANIM,
-                            fromTranslation, toTranslation).start();
+                if (!activity.getDeviceProfile().isVerticalBarLayout()
+                        && SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) {
+                    // Don't animate the shelf when the mode is NO_BUTTON, because we
+                    // update it atomically.
+                    pa.add(activity.getStateManager().createStateElementAnimation(
+                            INDEX_SHELF_ANIM,
+                            BACKGROUND_APP.getVerticalProgress(activity),
+                            OVERVIEW.getVerticalProgress(activity)));
                 }
 
-                fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2);
-                fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
+                // Animate the blur and wallpaper zoom
+                float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
+                float toDepthRatio = OVERVIEW.getDepth(activity);
+                pa.addFloat(getDepthController(),
+                        new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
+                        fromDepthRatio, toDepthRatio, LINEAR);
+
             }
         };
-    }
 
-    private AnimatorPlaybackController createBackgroundToOverviewAnim(
-            Launcher activity, long transitionLength) {
-
-        PendingAnimation pa = new PendingAnimation(transitionLength * 2);
-
-        if (!activity.getDeviceProfile().isVerticalBarLayout()
-                && SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) {
-            // Don't animate the shelf when the mode is NO_BUTTON, because we update it atomically.
-            pa.add(activity.getStateManager().createStateElementAnimation(
-                    INDEX_SHELF_ANIM,
-                    BACKGROUND_APP.getVerticalProgress(activity),
-                    OVERVIEW.getVerticalProgress(activity)));
-        }
-
-        // Animate the blur and wallpaper zoom
-        float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
-        float toDepthRatio = OVERVIEW.getDepth(activity);
-        pa.addFloat(getDepthController(), new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
-                fromDepthRatio, toDepthRatio, LINEAR);
-
-
-        //  Scale down recents from being full screen to being in overview.
-        RecentsView recentsView = activity.getOverviewPanel();
-        pa.addFloat(recentsView, SCALE_PROPERTY,
-                BACKGROUND_APP.getOverviewScaleAndOffset(activity)[0],
-                OVERVIEW.getOverviewScaleAndOffset(activity)[0],
-                LINEAR);
-        pa.addFloat(recentsView, FULLSCREEN_PROGRESS,
-                BACKGROUND_APP.getOverviewFullscreenProgress(),
-                OVERVIEW.getOverviewFullscreenProgress(),
-                LINEAR);
-
-        AnimatorPlaybackController controller = pa.createPlaybackController();
-        activity.getStateManager().setCurrentUserControlledAnimation(controller);
-
-        // Since we are changing the start position of the UI, reapply the state, at the end
-        controller.setEndAction(() -> activity.getStateManager().goToState(
-                controller.getInterpolatedProgress() > 0.5 ? OVERVIEW : BACKGROUND_APP, false));
-        return controller;
+        BaseQuickstepLauncher launcher = factory.initUI();
+        // Since all apps is not visible, we can safely reset the scroll position.
+        // This ensures then the next swipe up to all-apps starts from scroll 0.
+        launcher.getAppsView().reset(false /* animate */);
+        return factory;
     }
 
     @Override
@@ -249,6 +163,15 @@
                 onInitListener.test(alreadyOnHome));
     }
 
+    @Override
+    public void setOnDeferredActivityLaunchCallback(Runnable r) {
+        Launcher launcher = getCreatedActivity();
+        if (launcher == null) {
+            return;
+        }
+        launcher.setOnDeferredActivityLaunchCallback(r);
+    }
+
     @Nullable
     @Override
     public BaseQuickstepLauncher getCreatedActivity() {
@@ -256,11 +179,13 @@
     }
 
     @Nullable
-    @UiThread
-    private Launcher getVisibleLauncher() {
-        Launcher launcher = getCreatedActivity();
-        return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ?
-                launcher : null;
+    @Override
+    public DepthController getDepthController() {
+        BaseQuickstepLauncher launcher = getCreatedActivity();
+        if (launcher == null) {
+            return null;
+        }
+        return launcher.getDepthController();
     }
 
     @Nullable
@@ -271,6 +196,14 @@
                 ? launcher.getOverviewPanel() : null;
     }
 
+    @Nullable
+    @UiThread
+    private Launcher getVisibleLauncher() {
+        Launcher launcher = getCreatedActivity();
+        return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus()
+                ? launcher : null;
+    }
+
     @Override
     public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
         if (TestProtocol.sDebugTracing) {
@@ -290,15 +223,6 @@
         return true;
     }
 
-    @Override
-    public void setHintUserWillBeActive() {
-        getCreatedActivity().setHintUserWillBeActive();
-    }
-
-    @Override
-    public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
-        return deviceState.isInDeferredGestureRegion(ev);
-    }
 
     @Override
     public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
@@ -311,6 +235,16 @@
     }
 
     @Override
+    public void updateOverviewPredictionState() {
+        Launcher launcher = getCreatedActivity();
+        if (launcher == null) {
+            return;
+        }
+        PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
+                PredictionUiStateManager.Client.OVERVIEW);
+    }
+
+    @Override
     public int getContainerType() {
         final Launcher launcher = getVisibleLauncher();
         return launcher != null ? launcher.getStateManager().getState().containerType
@@ -348,54 +282,11 @@
     }
 
     @Override
-    public void setOnDeferredActivityLaunchCallback(Runnable r) {
-        Launcher launcher = getCreatedActivity();
-        if (launcher == null) {
-            return;
-        }
-        launcher.setOnDeferredActivityLaunchCallback(r);
-    }
-
-    @Override
-    public void updateOverviewPredictionState() {
-        Launcher launcher = getCreatedActivity();
-        if (launcher == null) {
-            return;
-        }
-        PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
-                PredictionUiStateManager.Client.OVERVIEW);
-    }
-
-    @Nullable
-    @Override
-    public DepthController getDepthController() {
-        BaseQuickstepLauncher launcher = getCreatedActivity();
-        if (launcher == null) {
-            return null;
-        }
-        return launcher.getDepthController();
-    }
-
-    @Override
-    public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
-        DeviceProfile fullDp = dp.getFullScreenProfile();
-        // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
-        // account for system insets
-        out.set(fullDp.availableWidthPx, fullDp.availableHeightPx);
-        float halfDividerSize = context.getResources()
-                .getDimension(R.dimen.multi_window_task_divider_size) / 2;
-
-        if (fullDp.isLandscape) {
-            out.x = out.x / 2 - halfDividerSize;
-        } else {
-            out.y = out.y / 2 - halfDividerSize;
-        }
-    }
-
-    @Override
-    protected float getExtraSpace(Context context, DeviceProfile dp) {
-        if (dp.isVerticalBarLayout()) {
-            return  0;
+    protected float getExtraSpace(Context context, DeviceProfile dp,
+            PagedOrientationHandler orientationHandler) {
+        if (dp.isVerticalBarLayout() ||
+                hideShelfInTwoButtonLandscape(context, orientationHandler)) {
+            return 0;
         } else {
             Resources res = context.getResources();
             if (showOverviewActions(context)) {
@@ -421,4 +312,5 @@
             }
         }
     }
+
 }
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
new file mode 100644
index 0000000..fa7d268
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import android.animation.AnimatorSet;
+import android.content.Context;
+import android.graphics.RectF;
+import android.os.UserHandle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.views.FloatingIconView;
+import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.InputConsumerController;
+
+/**
+ * Temporary class to allow easier refactoring
+ */
+public class LauncherSwipeHandlerV2 extends
+        BaseSwipeUpHandlerV2<BaseQuickstepLauncher, RecentsView> {
+
+    public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
+            TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
+            boolean continuingLastGesture, InputConsumerController inputConsumer) {
+        super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
+                continuingLastGesture, inputConsumer);
+    }
+
+
+    @Override
+    protected HomeAnimationFactory createHomeAnimationFactory(long duration) {
+        HomeAnimationFactory homeAnimFactory;
+        if (mActivity != null) {
+            final TaskView runningTaskView = mRecentsView.getRunningTaskView();
+            final View workspaceView;
+            if (runningTaskView != null
+                    && runningTaskView.getTask().key.getComponent() != null) {
+                workspaceView = mActivity.getWorkspace().getFirstMatchForAppClose(
+                        runningTaskView.getTask().key.getComponent().getPackageName(),
+                        UserHandle.of(runningTaskView.getTask().key.userId));
+            } else {
+                workspaceView = null;
+            }
+            final RectF iconLocation = new RectF();
+            boolean canUseWorkspaceView =
+                    workspaceView != null && workspaceView.isAttachedToWindow();
+            FloatingIconView floatingIconView = canUseWorkspaceView
+                    ? FloatingIconView.getFloatingIconView(mActivity, workspaceView,
+                    true /* hideOriginal */, iconLocation, false /* isOpening */)
+                    : null;
+
+            mActivity.getRootView().setForceHideBackArrow(true);
+            mActivity.setHintUserWillBeActive();
+
+            homeAnimFactory = new HomeAnimationFactory(floatingIconView) {
+
+                @Override
+                public RectF getWindowTargetRect() {
+                    if (canUseWorkspaceView) {
+                        return iconLocation;
+                    } else {
+                        return super.getWindowTargetRect();
+                    }
+                }
+
+                @NonNull
+                @Override
+                public AnimatorPlaybackController createActivityAnimationToHome() {
+                    // Return an empty APC here since we have an non-user controlled animation
+                    // to home.
+                    long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+                    return mActivity.getStateManager().createAnimationToNewWorkspace(
+                            NORMAL, accuracy, 0 /* animComponents */);
+                }
+
+                @Override
+                public void playAtomicAnimation(float velocity) {
+                    new StaggeredWorkspaceAnim(mActivity, velocity,
+                            true /* animateOverviewScrim */).start();
+                }
+            };
+
+        } else {
+            homeAnimFactory = new HomeAnimationFactory(null) {
+                @Override
+                public AnimatorPlaybackController createActivityAnimationToHome() {
+                    return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
+                }
+            };
+            mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+                    isPresent -> mRecentsView.startHome());
+        }
+        return homeAnimFactory;
+    }
+
+    @Override
+    protected void finishRecentsControllerToHome(Runnable callback) {
+        mRecentsAnimationController.finish(
+                true /* toRecents */, callback, true /* sendUserLeaveHint */);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
index d4c746f..0d49b2b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -7,6 +7,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.testing.TestInformationHandler;
 import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.systemui.shared.recents.model.Task;
@@ -35,7 +36,8 @@
 
             case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
                 final float swipeHeight =
-                        LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile);
+                        LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile,
+                                PagedOrientationHandler.HOME_ROTATED);
                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
                 return response;
             }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index a4670fd..33b7f12 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -46,6 +46,7 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.statemanager.StateManager;
+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.ActivityTracker;
@@ -57,6 +58,7 @@
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsRootView;
 import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.util.RecentsAtomicAnimationFactory;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -345,6 +347,11 @@
         dumpMisc(prefix + "\t", writer);
     }
 
+    @Override
+    public AtomicAnimationFactory<RecentsState> createAtomicAnimationFactory() {
+        return new RecentsAtomicAnimationFactory<>(this, 0);
+    }
+
     private AnimatorListenerAdapter resetStateListener() {
         return new AnimatorListenerAdapter() {
             @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 82b0097..a0bb631 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -85,7 +85,6 @@
 import com.android.quickstep.util.AssistantUtilities;
 import com.android.quickstep.util.ProtoTracer;
 import com.android.quickstep.util.SplitScreenBounds;
-import com.android.quickstep.views.RecentsView;
 import com.android.systemui.plugins.OverscrollPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.recents.IOverviewProxy;
@@ -616,21 +615,6 @@
             return;
         }
         mDeviceState.enableMultipleRegions(baseInputConsumer instanceof OtherActivityInputConsumer);
-        BaseDraggingActivity activity =
-                mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.2");
-        }
-        if (activity == null || !(activity.getOverviewPanel() instanceof RecentsView)) {
-            return;
-        }
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.3");
-        }
-        ((RecentsView) activity.getOverviewPanel())
-            .setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
-                mDeviceState.getDisplayRotation());
-        activity.getDragLayer().recreateControllers();
     }
 
     private InputConsumer newBaseConsumer(GestureState previousGestureState,
@@ -848,16 +832,16 @@
         }
     }
 
-    private BaseSwipeUpHandler createLauncherSwipeHandler(GestureState gestureState,
-            long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
-        return new LauncherSwipeHandler(this, mDeviceState, mTaskAnimationManager,
+    private BaseSwipeUpHandler createLauncherSwipeHandler(
+            GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
+        return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
                 gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
     }
 
-    private BaseSwipeUpHandler createFallbackSwipeHandler(GestureState gestureState,
-            long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
-        return new FallbackSwipeHandler(this, mDeviceState, gestureState,
-                mInputConsumer, isLikelyToStartNewTask, continuingLastGesture);
+    private BaseSwipeUpHandler createFallbackSwipeHandler(
+            GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
+        return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
+                gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
     }
 
     protected boolean shouldNotifyBackGesture() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index f958e6d..9242771 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -24,11 +24,13 @@
 import android.os.Build;
 import android.util.AttributeSet;
 
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.quickstep.FallbackActivityInterface;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 
@@ -38,7 +40,7 @@
 public class FallbackRecentsView extends RecentsView<RecentsActivity>
         implements StateListener<RecentsState> {
 
-    private RunningTaskInfo mRunningTaskInfo;
+    private RunningTaskInfo mHomeTaskInfo;
 
     public FallbackRecentsView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -67,16 +69,40 @@
         return false;
     }
 
-    public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
-        mRunningTaskInfo = runningTaskInfo;
-        onGestureAnimationStart(runningTaskInfo == null ? -1 : runningTaskInfo.taskId);
+    /**
+     * When starting gesture interaction from home, we add a temporary invisible tile corresponding
+     * to the home task. This allows us to handle quick-switch similarly to a quick-switching
+     * from a foreground task.
+     */
+    public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) {
+        mHomeTaskInfo = homeTaskInfo;
+        onGestureAnimationStart(homeTaskInfo == null ? -1 : homeTaskInfo.taskId);
+    }
+
+    /**
+     * When the gesture ends and recents view become interactive, we also remove the temporary
+     * invisible tile added for the home task. This also pushes the remaining tiles back
+     * to the center.
+     */
+    @Override
+    public void onGestureAnimationEnd() {
+        super.onGestureAnimationEnd();
+        if (mHomeTaskInfo != null) {
+            TaskView tv = getTaskView(mHomeTaskInfo.taskId);
+            if (tv != null) {
+                PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
+                pa.addEndListener(e -> setCurrentTask(-1));
+                runDismissAnimation(pa);
+            }
+        }
     }
 
     @Override
     public void setCurrentTask(int runningTaskId) {
         super.setCurrentTask(runningTaskId);
-        if (mRunningTaskInfo != null && mRunningTaskInfo.taskId != runningTaskId) {
-            mRunningTaskInfo = null;
+        if (mHomeTaskInfo != null && mHomeTaskInfo.taskId != runningTaskId) {
+            mHomeTaskInfo = null;
+            setRunningTaskHidden(false);
         }
     }
 
@@ -85,7 +111,7 @@
         // When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher
         // as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
         // track the index of the next task appropriately, as if we are switching on any other app.
-        if (mRunningTaskInfo != null && mRunningTaskInfo.taskId == mRunningTaskId) {
+        if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == mRunningTaskId) {
             // Check if the task list has running task
             boolean found = false;
             for (Task t : tasks) {
@@ -97,7 +123,7 @@
             if (!found) {
                 ArrayList<Task> newList = new ArrayList<>(tasks.size() + 1);
                 newList.addAll(tasks);
-                newList.add(Task.from(new TaskKey(mRunningTaskInfo), mRunningTaskInfo, false));
+                newList.add(Task.from(new TaskKey(mHomeTaskInfo), mHomeTaskInfo, false));
                 tasks = newList;
             }
         }
@@ -105,6 +131,15 @@
     }
 
     @Override
+    public void setRunningTaskHidden(boolean isHidden) {
+        if (mHomeTaskInfo != null) {
+            // Always keep the home task hidden
+            isHidden = true;
+        }
+        super.setRunningTaskHidden(isHidden);
+    }
+
+    @Override
     public void setModalStateEnabled(boolean isModalState) {
         super.setModalStateEnabled(isModalState);
         if (isModalState) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 2dc7f5f..3a97216 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -18,29 +18,31 @@
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
+
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.BaseSwipeUpHandlerV2.MIN_PROGRESS_FOR_OVERVIEW;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.PointF;
-import android.graphics.Rect;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
+
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.MultiStateCallback;
@@ -49,18 +51,19 @@
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.TransformParams.BuilderProxy;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 /**
  * A dummy input consumer used when the device is still locked, e.g. from secure camera.
  */
 public class DeviceLockedInputConsumer implements InputConsumer,
-        RecentsAnimationCallbacks.RecentsAnimationListener {
+        RecentsAnimationCallbacks.RecentsAnimationListener, BuilderProxy {
 
     private static final String[] STATE_NAMES = DEBUG_STATES ? new String[2] : null;
     private static int getFlagForIndex(int index, String name) {
@@ -83,19 +86,20 @@
     private final InputMonitorCompat mInputMonitorCompat;
 
     private final PointF mTouchDown = new PointF();
-    private final AppWindowAnimationHelper mAppWindowAnimationHelper;
     private final TransformParams mTransformParams;
-    private final Point mDisplaySize;
     private final MultiStateCallback mStateCallback;
 
+    private final Point mDisplaySize;
+    private final Matrix mMatrix = new Matrix();
+    private final float mMaxTranslationY;
+
     private VelocityTracker mVelocityTracker;
-    private float mProgress;
+    private final AnimatedFloat mProgress = new AnimatedFloat(this::applyTransform);
 
     private boolean mThresholdCrossed = false;
     private boolean mHomeLaunched = false;
 
     private RecentsAnimationController mRecentsAnimationController;
-    private RecentsAnimationTargets mRecentsAnimationTargets;
 
     public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
@@ -105,9 +109,10 @@
         mTaskAnimationManager = taskAnimationManager;
         mGestureState = gestureState;
         mTouchSlopSquared = squaredTouchSlop(context);
-        mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
         mTransformParams = new TransformParams();
         mInputMonitorCompat = inputMonitorCompat;
+        mMaxTranslationY = context.getResources().getDimensionPixelSize(
+                R.dimen.device_locked_y_offset);
 
         // Do not use DeviceProfile as the user data might be locked
         mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize;
@@ -158,9 +163,7 @@
                     }
                 } else {
                     float dy = Math.max(mTouchDown.y - y, 0);
-                    mProgress = dy / mDisplaySize.y;
-                    mTransformParams.setProgress(mProgress);
-                    mAppWindowAnimationHelper.applyTransform(mTransformParams);
+                    mProgress.updateValue(dy / mDisplaySize.y);
                 }
                 break;
             }
@@ -189,20 +192,13 @@
                 // Is fling
                 dismissTask = velocityY < 0;
             } else {
-                dismissTask = mProgress >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
+                dismissTask = mProgress.value >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
             }
 
             // Animate back to fullscreen before finishing
-            ValueAnimator animator = ValueAnimator.ofFloat(mTransformParams.getProgress(), 0f);
+            ObjectAnimator animator = mProgress.animateToValue(mProgress.value, 0);
             animator.setDuration(100);
             animator.setInterpolator(Interpolators.ACCEL);
-            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                    mTransformParams.setProgress((float) valueAnimator.getAnimatedValue());
-                    mAppWindowAnimationHelper.applyTransform(mTransformParams);
-                }
-            });
             animator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -239,29 +235,15 @@
     public void onRecentsAnimationStart(RecentsAnimationController controller,
             RecentsAnimationTargets targets) {
         mRecentsAnimationController = controller;
-        mRecentsAnimationTargets = targets;
-
-        Rect displaySize = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
-        RemoteAnimationTargetCompat targetCompat = targets.findTask(
-                mGestureState.getRunningTaskId());
-        if (targetCompat != null) {
-            mAppWindowAnimationHelper.updateSource(displaySize, targetCompat);
-        }
-
-        // Offset the surface slightly
-        displaySize.offset(0, mContext.getResources().getDimensionPixelSize(
-                R.dimen.device_locked_y_offset));
-        mTransformParams.setTargetSet(mRecentsAnimationTargets);
-        mAppWindowAnimationHelper.updateTargetRect(displaySize);
-        mAppWindowAnimationHelper.applyTransform(mTransformParams);
-
+        mTransformParams.setTargetSet(targets);
+        applyTransform();
         mStateCallback.setState(STATE_TARGET_RECEIVED);
     }
 
     @Override
     public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
         mRecentsAnimationController = null;
-        mRecentsAnimationTargets = null;
+        mTransformParams.setTargetSet(null);
     }
 
     private void endRemoteAnimation() {
@@ -273,6 +255,20 @@
         }
     }
 
+    private void applyTransform() {
+        mTransformParams.setProgress(mProgress.value);
+        if (mTransformParams.getTargetSet() != null) {
+            mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
+        }
+    }
+
+    @Override
+    public void onBuildTargetParams(
+            Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+        mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
+        builder.withMatrix(mMatrix);
+    }
+
     @Override
     public void onConsumerAboutToBeSwitched() {
         mStateCallback.setState(STATE_HANDLER_INVALIDATED);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 6b0d7a3..ab9ec21 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -207,7 +207,7 @@
                 // Start the window animation on down to give more time for launcher to draw if the
                 // user didn't start the gesture over the back button
                 if (!mIsDeferredDownTarget) {
-                    startTouchTrackingForWindowAnimation(ev.getEventTime(), false);
+                    startTouchTrackingForWindowAnimation(ev.getEventTime());
                 }
 
                 TraceHelper.INSTANCE.endSection(traceToken);
@@ -275,8 +275,7 @@
                         if (mIsDeferredDownTarget) {
                             // Deferred gesture, start the animation and gesture tracking once
                             // we pass the actual touch slop
-                            startTouchTrackingForWindowAnimation(
-                                    ev.getEventTime(), isLikelyToStartNewTask);
+                            startTouchTrackingForWindowAnimation(ev.getEventTime());
                         }
                         if (!mPassedWindowMoveSlop) {
                             mPassedWindowMoveSlop = true;
@@ -326,12 +325,11 @@
         mInteractionHandler.onGestureStarted();
     }
 
-    private void startTouchTrackingForWindowAnimation(
-            long touchTimeMs, boolean isLikelyToStartNewTask) {
+    private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
         ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
 
         mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs,
-                mTaskAnimationManager.isRecentsAnimationRunning(), isLikelyToStartNewTask);
+                mTaskAnimationManager.isRecentsAnimationRunning());
         mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
         mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler::onMotionPauseChanged);
         Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index a7979cc..b743d3f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -77,7 +77,6 @@
     private final Matrix mTmpMatrix = new Matrix();
     private final Rect mTmpRect = new Rect();
     private final RectF mTmpRectF = new RectF();
-    private final RectF mCurrentRectWithInsets = new RectF();
     private RecentsOrientedState mOrientedState;
     // Corner radius of windows, in pixels
     private final float mWindowCornerRadius;
@@ -88,9 +87,6 @@
     // Whether or not to actually use the rounded cornders on windows
     private boolean mUseRoundedCornersOnWindows;
 
-    // Corner radius currently applied to transformed window.
-    private float mCurrentCornerRadius;
-
     public AppWindowAnimationHelper(RecentsOrientedState orientedState, Context context) {
         Resources res = context.getResources();
         mOrientedState = orientedState;
@@ -100,25 +96,9 @@
         mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows;
     }
 
-    public AppWindowAnimationHelper(Context context) {
-        this(null, context);
-    }
-
     private void updateSourceStack(RemoteAnimationTargetCompat target) {
         mSourceInsets.set(target.contentInsets);
         mSourceStackBounds.set(target.screenSpaceBounds);
-
-        // TODO: Should sourceContainerBounds already have this offset?
-        mSourceStackBounds.offsetTo(target.position.x, target.position.y);
-    }
-
-    public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
-        updateSourceStack(target);
-        updateHomeBounds(homeStackBounds);
-    }
-
-    public void updateHomeBounds(Rect homeStackBounds) {
-        mHomeStackBounds.set(homeStackBounds);
     }
 
     public void updateTargetRect(Rect targetRect) {
@@ -186,14 +166,14 @@
         crop.offsetTo(0, 0);
         float cornerRadius = 0f;
         float scale = Math.max(mCurrentRect.width(), mTargetRect.width()) / crop.width();
+        mTmpMatrix.setTranslate(0, 0);
+        if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+            mTmpMatrix.setTranslate(app.localBounds.left, app.localBounds.top);
+        }
         if (app.mode == targetMode
                 && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
             mTmpMatrix.setRectToRect(mSourceRect, mCurrentRect, ScaleToFit.FILL);
-            if (app.localBounds != null) {
-                mTmpMatrix.postTranslate(app.localBounds.left, app.localBounds.top);
-            } else {
-                mTmpMatrix.postTranslate(app.position.x, app.position.y);
-            }
+            mTmpMatrix.postTranslate(app.localBounds.left, app.localBounds.top);
             mCurrentClipRectF.roundOut(crop);
             if (mSupportsRoundedCornersOnWindows) {
                 if (params.getCornerRadius() > -1) {
@@ -205,7 +185,6 @@
                     cornerRadius = mapRange(boundToRange(params.getProgress(), 0, 1),
                             windowCornerRadius, mTaskCornerRadius);
                 }
-                mCurrentCornerRadius = cornerRadius;
             }
 
             builder.withMatrix(mTmpMatrix)
@@ -241,11 +220,6 @@
                 mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress);
     }
 
-    public RectF getCurrentRectWithInsets() {
-        mTmpMatrix.mapRect(mCurrentRectWithInsets, mCurrentClipRectF);
-        return mCurrentRectWithInsets;
-    }
-
     public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
             @Nullable RemoteAnimationTargetCompat target) {
         BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
@@ -317,8 +291,4 @@
         return mTargetRect;
     }
 
-    public float getCurrentCornerRadius() {
-        return mCurrentCornerRadius;
-    }
-
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
new file mode 100644
index 0000000..5b0d503
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.quickstep.views.RecentsView;
+
+public class RecentsAtomicAnimationFactory<ACTIVITY_TYPE extends StatefulActivity, STATE_TYPE>
+        extends AtomicAnimationFactory<STATE_TYPE> {
+
+    public static final int INDEX_RECENTS_FADE_ANIM = AtomicAnimationFactory.NEXT_INDEX + 0;
+    public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = AtomicAnimationFactory.NEXT_INDEX + 1;
+
+    private static final int MY_ANIM_COUNT = 2;
+    protected static final int NEXT_INDEX = AtomicAnimationFactory.NEXT_INDEX + MY_ANIM_COUNT;
+
+    protected final ACTIVITY_TYPE mActivity;
+
+    /**
+     * @param extraAnims number of animations supported by the subclass. This should not include
+     *                  the 2 animations supported by this class.
+     */
+    public RecentsAtomicAnimationFactory(ACTIVITY_TYPE activity, int extraAnims) {
+        super(MY_ANIM_COUNT + extraAnims);
+        mActivity = activity;
+    }
+
+    @Override
+    public Animator createStateElementAnimation(int index, float... values) {
+        switch (index) {
+            case INDEX_RECENTS_FADE_ANIM:
+                return ObjectAnimator.ofFloat(mActivity.getOverviewPanel(),
+                        RecentsView.CONTENT_ALPHA, values);
+            case INDEX_RECENTS_TRANSLATE_X_ANIM: {
+                RecentsView rv = mActivity.getOverviewPanel();
+                return new SpringAnimationBuilder(mActivity)
+                        .setMinimumVisibleChange(1f / rv.getPageOffsetScale())
+                        .setDampingRatio(0.8f)
+                        .setStiffness(250)
+                        .setValues(values)
+                        .build(rv, ADJACENT_PAGE_OFFSET);
+            }
+            default:
+                return super.createStateElementAnimation(index, values);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 32fc0de..3e0daaf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -212,12 +212,13 @@
     }
 
     private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
-        PendingAnimation builder = new PendingAnimation(duration, mAnimators);
+        PendingAnimation builder = new PendingAnimation(duration);
         launcher.getWorkspace().getStateTransitionAnimation().setScrim(builder, state);
         builder.setFloat(
                 launcher.getDragLayer().getOverviewScrim(),
                 OverviewScrim.SCRIM_PROGRESS,
                 state.getOverviewScrimAlpha(launcher),
                 ACCEL_DEACCEL);
+        mAnimators.play(builder.buildAnim());
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index a3db940..348c22d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -20,6 +20,7 @@
 import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
+import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.PointF;
@@ -29,6 +30,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
@@ -114,7 +116,8 @@
         if (mDp == null) {
             return 1;
         }
-        mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect);
+        mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect,
+                mOrientationState.getOrientationHandler());
         return mOrientationState.getFullScreenScaleAndPivot(mTaskRect, mDp, mPivot);
     }
 
@@ -129,8 +132,6 @@
         mThumbnailData.windowingMode = WINDOWING_MODE_FULLSCREEN;
 
         mThumbnailPosition.set(runningTarget.screenSpaceBounds);
-        // TODO: Should sourceContainerBounds already have this offset?
-        mThumbnailPosition.offset(-mRunningTarget.position.x, -mRunningTarget.position.y);
         mLayoutValid = false;
     }
 
@@ -145,6 +146,14 @@
     }
 
     /**
+     * Adds animation for all the components corresponding to transition from an app to overview
+     */
+    public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
+        pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
+        pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, getFullScreenScale(), 1, interpolator);
+    }
+
+    /**
      * Returns the current clipped/visible window bounds in the window coordinate space
      */
     public RectF getCurrentCropRect() {
@@ -250,14 +259,11 @@
     }
 
     @Override
-    public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app,
-            int targetMode, TransformParams params) {
-        if (app.mode == targetMode
-                && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-            builder.withMatrix(mMatrix)
-                    .withWindowCrop(mTmpCropRect)
-                    .withCornerRadius(getCurrentCornerRadius());
-        }
+    public void onBuildTargetParams(
+            Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+        builder.withMatrix(mMatrix)
+                .withWindowCrop(mTmpCropRect)
+                .withCornerRadius(getCurrentCornerRadius());
     }
 
     /**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
index 83b64db..d837e54 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
@@ -16,6 +16,7 @@
 package com.android.quickstep.util;
 
 import android.graphics.RectF;
+import android.util.FloatProperty;
 
 import androidx.annotation.Nullable;
 
@@ -29,6 +30,19 @@
 
 public class TransformParams {
 
+    public static FloatProperty<TransformParams> PROGRESS =
+            new FloatProperty<TransformParams>("progress") {
+        @Override
+        public void setValue(TransformParams params, float v) {
+            params.setProgress(v);
+        }
+
+        @Override
+        public Float get(TransformParams params) {
+            return params.getProgress();
+        }
+    };
+
     private float mProgress;
     private @Nullable RectF mCurrentRect;
     private float mTargetAlpha;
@@ -176,10 +190,6 @@
         return mTargetSet;
     }
 
-    public SyncRtSurfaceTransactionApplierCompat getSyncTransactionApplier() {
-        return mSyncTransactionApplier;
-    }
-
     public void applySurfaceParams(SurfaceParams[] params) {
         if (mSyncTransactionApplier != null) {
             mSyncTransactionApplier.scheduleApply(params);
@@ -199,7 +209,15 @@
 
     public interface BuilderProxy {
 
-        void onBuildParams(SurfaceParams.Builder builder,
-                RemoteAnimationTargetCompat app, int targetMode, TransformParams params);
+        default void onBuildParams(SurfaceParams.Builder builder,
+                RemoteAnimationTargetCompat app, int targetMode, TransformParams params) {
+            if (app.mode == targetMode
+                    && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                onBuildTargetParams(builder, app, params);
+            }
+        }
+
+        default void onBuildTargetParams(SurfaceParams.Builder builder,
+                RemoteAnimationTargetCompat app, TransformParams params) { }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
index 2c85618..fd74357 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
@@ -47,24 +47,21 @@
     private boolean mIsRtl;
 
     private int mScrollOffset;
-    private RecentsView mParent;
 
     public ClearAllButton(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        PagedOrientationHandler orientationHandler = mParent.getPagedOrientationHandler();
-        mScrollOffset = orientationHandler.getClearAllScrollOffset(mParent, mIsRtl);
+        PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
+        mScrollOffset = orientationHandler.getClearAllScrollOffset(getRecentsView(), mIsRtl);
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mParent = (RecentsView) getParent();
-        mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+    private RecentsView getRecentsView() {
+        return (RecentsView) getParent();
     }
 
     @Override
@@ -94,7 +91,7 @@
 
     @Override
     public void onPageScroll(ScrollState scrollState) {
-        PagedOrientationHandler orientationHandler = mParent.getPagedOrientationHandler();
+        PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
         float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight());
         if (orientationSize == 0) {
             return;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 253e83c..3273e85 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -17,6 +17,8 @@
 package com.android.quickstep.views;
 
 import static android.view.Surface.ROTATION_0;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
 
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
 import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
@@ -827,6 +829,10 @@
     @Override
     public void setInsets(Rect insets) {
         mInsets.set(insets);
+        resetPaddingFromTaskSize();
+    }
+
+    private void resetPaddingFromTaskSize() {
         DeviceProfile dp = mActivity.getDeviceProfile();
         mOrientationState.setMultiWindowMode(dp.isMultiWindowMode);
         getTaskSize(mTempRect);
@@ -840,7 +846,8 @@
     }
 
     public void getTaskSize(Rect outRect) {
-        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
+        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect,
+                mOrientationHandler);
     }
 
     /** Gets the task size for modal state. */
@@ -1075,9 +1082,9 @@
      * Called when a gesture from an app has finished.
      */
     public void onGestureAnimationEnd() {
+        setOnScrollChangeListener(null);
         setEnableFreeScroll(true);
         setEnableDrawingLiveTile(true);
-        setOnScrollChangeListener(null);
         if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             setRunningTaskViewShowScreenshot(true);
         }
@@ -1108,6 +1115,12 @@
                     false, true, false, false, new ActivityManager.TaskDescription(), 0,
                     new ComponentName("", ""), false);
             taskView.bind(mTmpRunningTask, mOrientationState);
+
+            // Measure and layout immediately so that the scroll values is updated instantly
+            // as the user might be quick-switching
+            measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
+                    makeMeasureSpec(getMeasuredHeight(), EXACTLY));
+            layout(getLeft(), getTop(), getRight(), getBottom());
         }
 
         boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -1484,7 +1497,7 @@
         return true;
     }
 
-    private void runDismissAnimation(PendingAnimation pendingAnim) {
+    protected void runDismissAnimation(PendingAnimation pendingAnim) {
         AnimatorPlaybackController controller = pendingAnim.createPlaybackController();
         controller.dispatchOnStart();
         controller.setEndAction(() -> pendingAnim.finish(true, Touch.SWIPE));
@@ -1618,6 +1631,7 @@
             mActivity.getDragLayer().recreateControllers();
             mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
                     touchRotation != 0 || mOrientationState.getLauncherRotation() != ROTATION_0);
+            resetPaddingFromTaskSize();
             requestLayout();
         }
     }
@@ -2190,6 +2204,10 @@
      */
     public void setModalStateEnabled(boolean isModalState) { }
 
+    public BaseActivityInterface getSizeStrategy() {
+        return mSizeStrategy;
+    }
+
     /**
      * Used to register callbacks for when our empty message state changes.
      *
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index a3e360f..e3c1b42 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -472,7 +472,6 @@
             } else {
                 setThumbnailRotation(deltaRotate, thumbnailInsets, scale, thumbnailPosition);
             }
-            mMatrix.postTranslate(-thumbnailPosition.left, -thumbnailPosition.top);
 
             final float widthWithInsets;
             final float heightWithInsets;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index e5c9fc9..8745814 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -32,6 +32,7 @@
 
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
@@ -43,6 +44,7 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.uioverrides.states.OverviewState;
@@ -51,6 +53,7 @@
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.views.RecentsView;
 
 /**
  * Touch controller for handling various state transitions in portrait UI.
@@ -129,11 +132,22 @@
 
     @Override
     protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "PortraitStatesTouchController.getTargetState");
+        }
         if (fromState == ALL_APPS && !isDragTowardPositive) {
             // Should swipe down go to OVERVIEW instead?
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
+                        "PortraitStatesTouchController.getTargetState 1");
+            }
             return TouchInteractionService.isConnected() ?
                     mLauncher.getStateManager().getLastState() : NORMAL;
         } else if (fromState == OVERVIEW) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
+                        "PortraitStatesTouchController.getTargetState 2");
+            }
             LauncherState positiveDragTarget = ALL_APPS;
             if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(mLauncher)) {
                 // Don't allow swiping up to all apps.
@@ -141,6 +155,10 @@
             }
             return isDragTowardPositive ? positiveDragTarget : NORMAL;
         } else if (fromState == NORMAL && isDragTowardPositive) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
+                        "PortraitStatesTouchController.getTargetState 3");
+            }
             int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
             return mAllowDragToOverview && TouchInteractionService.isConnected()
                     && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0
@@ -244,8 +262,9 @@
             mCurrentAnimation = mPendingAnimation.createPlaybackController()
                     .setOnCancelRunnable(onCancelRunnable);
             mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
+            RecentsView recentsView = mLauncher.getOverviewPanel();
             totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
-                    mLauncher.getDeviceProfile());
+                    mLauncher.getDeviceProfile(), recentsView.getPagedOrientationHandler());
         } else {
             mCurrentAnimation = mLauncher.getStateManager()
                     .createAnimationToNewWorkspace(mToState, config)
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index f29f0ff..022977b 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -15,14 +15,24 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.quickstep.BaseSwipeUpHandlerV2.RECENTS_ATTACH_DURATION;
 import static com.android.quickstep.SysUINavigationMode.getMode;
+import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
+import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 
+import android.animation.Animator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
 import android.view.MotionEvent;
@@ -34,9 +44,11 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActivityInitListener;
@@ -56,11 +68,15 @@
 public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_TYPE>,
         ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>> {
 
-    private final PointF mTempPoint = new PointF();
     public final boolean rotationSupportedByActivity;
 
-    protected BaseActivityInterface(boolean rotationSupportedByActivity) {
+    private final STATE_TYPE mOverviewState, mBackgroundState;
+
+    protected BaseActivityInterface(boolean rotationSupportedByActivity,
+            STATE_TYPE overviewState, STATE_TYPE backgroundState) {
         this.rotationSupportedByActivity = rotationSupportedByActivity;
+        mOverviewState = overviewState;
+        mBackgroundState = backgroundState;
     }
 
     public void onTransitionCancelled(boolean activityVisible) {
@@ -73,7 +89,8 @@
     }
 
     public abstract int getSwipeUpDestinationAndLength(
-            DeviceProfile dp, Context context, Rect outRect);
+            DeviceProfile dp, Context context, Rect outRect,
+            PagedOrientationHandler orientationHandler);
 
     public void onSwipeUpToRecentsComplete() {
         // Re apply state in case we did something funky during the transition.
@@ -84,7 +101,7 @@
         activity.getStateManager().reapplyState();
     }
 
-    public void onSwipeUpToHomeComplete() { }
+    public abstract void onSwipeUpToHomeComplete();
 
     public abstract void onAssistantVisibilityChanged(float visibility);
 
@@ -130,7 +147,7 @@
     public abstract boolean allowMinimizeSplitScreen();
 
     public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
-        return true;
+        return deviceState.isInDeferredGestureRegion(ev);
     }
 
     /**
@@ -174,26 +191,24 @@
         recentsView.switchToScreenshot(thumbnailData, runnable);
     }
 
-    public void setHintUserWillBeActive() {}
-
-    /**
-     * Sets the expected window size in multi-window mode
-     */
-    public abstract void getMultiWindowSize(Context context, DeviceProfile dp, PointF out);
-
     /**
      * Calculates the taskView size for the provided device configuration
      */
-    public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
-        calculateTaskSize(context, dp, getExtraSpace(context, dp), outRect);
+    public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
+            PagedOrientationHandler orientedState) {
+        calculateTaskSize(context, dp, getExtraSpace(context, dp, orientedState),
+                outRect, orientedState);
     }
 
-    protected abstract float getExtraSpace(Context context, DeviceProfile dp);
+    protected abstract float getExtraSpace(Context context, DeviceProfile dp,
+            PagedOrientationHandler orientedState);
 
     private void calculateTaskSize(
-            Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect) {
+            Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect,
+            PagedOrientationHandler orientationHandler) {
         Resources res = context.getResources();
-        final boolean showLargeTaskSize = showOverviewActions(context);
+        final boolean showLargeTaskSize = showOverviewActions(context) ||
+                hideShelfInTwoButtonLandscape(context, orientationHandler);
 
         final int paddingResId;
         if (dp.isMultiWindowMode) {
@@ -251,7 +266,7 @@
     /**
      * Calculates the modal taskView size for the provided device configuration
      */
-    public void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+    public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
         float paddingHorz = context.getResources().getDimension(dp.isMultiWindowMode
                 ? R.dimen.multi_window_task_card_horz_space
                 : dp.isVerticalBarLayout()
@@ -265,7 +280,7 @@
     }
 
     /** Gets the space that the overview actions will take, including margins. */
-    public float getOverviewActionsHeight(Context context) {
+    public final float getOverviewActionsHeight(Context context) {
         Resources res = context.getResources();
         float actionsBottomMargin = 0;
         if (getMode(context) == Mode.THREE_BUTTONS) {
@@ -282,8 +297,6 @@
 
     public interface AnimationFactory {
 
-        default void onRemoteAnimationReceived(RemoteAnimationTargets targets) { }
-
         void createActivityInterface(long transitionLength);
 
         default void onTransitionCancelled() { }
@@ -299,6 +312,97 @@
         default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
     }
 
+    class DefaultAnimationFactory implements AnimationFactory {
+
+        protected final ACTIVITY_TYPE mActivity;
+        private final STATE_TYPE mStartState;
+        private final Consumer<AnimatorPlaybackController> mCallback;
+
+        private boolean mIsAttachedToWindow;
+
+        DefaultAnimationFactory(Consumer<AnimatorPlaybackController> callback) {
+            mCallback = callback;
+
+            mActivity = getCreatedActivity();
+            mStartState = mActivity.getStateManager().getState();
+        }
+
+        protected ACTIVITY_TYPE initUI() {
+            STATE_TYPE resetState = mStartState;
+            if (mStartState.shouldDisableRestore()) {
+                resetState = mActivity.getStateManager().getRestState();
+            }
+            mActivity.getStateManager().setRestState(resetState);
+            mActivity.getStateManager().goToState(mBackgroundState, false);
+            return mActivity;
+        }
+
+        @Override
+        public void createActivityInterface(long transitionLength) {
+            PendingAnimation pa = new PendingAnimation(transitionLength * 2);
+            createBackgroundToOverviewAnim(mActivity, pa);
+            AnimatorPlaybackController controller = pa.createPlaybackController();
+            mActivity.getStateManager().setCurrentUserControlledAnimation(controller);
+
+            // Since we are changing the start position of the UI, reapply the state, at the end
+            controller.setEndAction(() -> mActivity.getStateManager().goToState(
+                    controller.getInterpolatedProgress() > 0.5 ? mOverviewState : mBackgroundState,
+                    false));
+            mCallback.accept(controller);
+
+            // Creating the activity controller animation sometimes reapplies the launcher state
+            // (because we set the animation as the current state animation), so we reapply the
+            // attached state here as well to ensure recents is shown/hidden appropriately.
+            if (SysUINavigationMode.getMode(mActivity) == Mode.NO_BUTTON) {
+                setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
+            }
+        }
+
+        @Override
+        public void onTransitionCancelled() {
+            mActivity.getStateManager().goToState(mStartState, false /* animate */);
+        }
+
+        @Override
+        public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
+            if (mIsAttachedToWindow == attached && animate) {
+                return;
+            }
+            mIsAttachedToWindow = attached;
+            RecentsView recentsView = mActivity.getOverviewPanel();
+            Animator fadeAnim = mActivity.getStateManager()
+                    .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
+
+            float fromTranslation = attached ? 1 : 0;
+            float toTranslation = attached ? 0 : 1;
+            mActivity.getStateManager()
+                    .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
+            if (!recentsView.isShown() && animate) {
+                ADJACENT_PAGE_OFFSET.set(recentsView, fromTranslation);
+            } else {
+                fromTranslation = ADJACENT_PAGE_OFFSET.get(recentsView);
+            }
+            if (!animate) {
+                ADJACENT_PAGE_OFFSET.set(recentsView, toTranslation);
+            } else {
+                mActivity.getStateManager().createStateElementAnimation(
+                        INDEX_RECENTS_TRANSLATE_X_ANIM,
+                        fromTranslation, toTranslation).start();
+            }
+
+            fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2);
+            fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
+        }
+
+        protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
+            //  Scale down recents from being full screen to being in overview.
+            RecentsView recentsView = activity.getOverviewPanel();
+            pa.addFloat(recentsView, SCALE_PROPERTY,
+                    recentsView.getMaxScaleForFullScreen(), 1, LINEAR);
+            pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
+        }
+    }
+
     protected static boolean showOverviewActions(Context context) {
         return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
     }
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 8e14bbb..016ffea 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -322,7 +322,7 @@
         pw.println("  gestureID=" + mGestureId);
         pw.println("  runningTask=" + mRunningTask);
         pw.println("  endTarget=" + mEndTarget);
-        pw.println("  lastAppearedTaskTarget=" + mLastAppearedTaskTarget);
+        pw.println("  lastAppearedTaskTargetId=" + getLastAppearedTaskId());
         pw.println("  lastStartedTaskId=" + mLastStartedTaskId);
         pw.println("  isRecentsAnimationRunning=" + isRecentsAnimationRunning());
     }
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 879fd1d..4cf7aab 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -317,13 +317,6 @@
 
     private class OrientationRectF extends RectF {
 
-        /**
-         * Delta to subtract width and height by because if we report the translated touch
-         * bounds as the width and height, calling {@link RectF#contains(float, float)} will
-         * be false
-         */
-        private float maxDelta = 0.001f;
-
         private int mRotation;
         private float mHeight;
         private float mWidth;
@@ -331,8 +324,8 @@
         OrientationRectF(float left, float top, float right, float bottom, int rotation) {
             super(left, top, right, bottom);
             this.mRotation = rotation;
-            mHeight = bottom - maxDelta;
-            mWidth = right - maxDelta;
+            mHeight = bottom;
+            mWidth = right;
         }
 
         @Override
@@ -342,6 +335,13 @@
             return s;
         }
 
+        @Override
+        public boolean contains(float x, float y) {
+            // Mark bottom right as included in the Rect (copied from Rect src, added "=" in "<=")
+            return left < right && top < bottom  // check for empty first
+                    && x >= left && x <= right && y >= top && y <= bottom;
+        }
+
         boolean applyTransform(MotionEvent event, boolean forceTransform) {
             mTmpMatrix.reset();
             postDisplayRotation(deltaRotation(mCurrentDisplayRotation, mRotation),
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 5fa6bc7..f90df45 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -68,7 +68,7 @@
     }
 
     public boolean isAnimatingHome() {
-        for (RemoteAnimationTargetCompat target : apps) {
+        for (RemoteAnimationTargetCompat target : unfilteredApps) {
             if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
                 return true;
             }
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index c715c93..05ce2a2 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -24,6 +24,7 @@
 import android.content.res.Resources;
 import android.util.Log;
 
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.MainThreadInitializedObject;
 
 import java.io.PrintWriter;
@@ -134,6 +135,12 @@
         return getMode(context) != Mode.TWO_BUTTONS;
     }
 
+    public static boolean hideShelfInTwoButtonLandscape(Context context,
+            PagedOrientationHandler pagedOrientationHandler) {
+        return  getMode(context) == Mode.TWO_BUTTONS &&
+                !pagedOrientationHandler.isLayoutNaturalToLauncher();
+    }
+
     public void dump(PrintWriter pw) {
         pw.println("SysUINavigationMode:");
         pw.println("  mode=" + mMode.name());
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index c1b276a..3f58e01 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -25,6 +25,7 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.SysUINavigationMode;
 
@@ -41,11 +42,13 @@
         return swipeHeight;
     }
 
-    public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
+    public static int getShelfTrackingDistance(Context context, DeviceProfile dp,
+            PagedOrientationHandler orientationHandler) {
         // Track the bottom of the window.
         if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
             Rect taskSize = new Rect();
-            LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize);
+            LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize,
+                    orientationHandler);
             return (dp.heightPx - taskSize.height()) / 2;
         }
         int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index e03f4b8..4c47d7f 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -27,6 +27,7 @@
 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.content.ContentResolver;
@@ -53,6 +54,7 @@
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.SysUINavigationMode;
 
 import java.lang.annotation.Retention;
 import java.util.function.IntConsumer;
@@ -118,6 +120,9 @@
             MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
                     | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
 
+    private SysUINavigationMode.NavigationModeChangeListener mNavModeChangeListener =
+            newMode -> setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, newMode != TWO_BUTTONS);
+
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private final SharedPreferences mSharedPrefs;
@@ -163,13 +168,7 @@
         if (isFixedRotationTransformEnabled(context)) {
             mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG;
         }
-        if (mOrientationListener.canDetectOrientation()) {
-            mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
-        }
-
-        // initialize external flags
-        updateAutoRotateSetting();
-        updateHomeRotationSetting();
+        initFlags();
     }
 
     /**
@@ -273,6 +272,18 @@
                 mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false));
     }
 
+    private void initFlags() {
+        SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
+        if (mOrientationListener.canDetectOrientation() &&
+                currentMode != TWO_BUTTONS) {
+            mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
+        }
+
+        // initialize external flags
+        updateAutoRotateSetting();
+        updateHomeRotationSetting();
+    }
+
     /**
      * Initializes any system values and registers corresponding change listeners. It must be
      * paired with {@link #destroyListeners()} call
@@ -283,9 +294,11 @@
             mContentResolver.registerContentObserver(
                     Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
                     false, mSystemAutoRotateObserver);
+            SysUINavigationMode.Mode currentMode =
+                    SysUINavigationMode.INSTANCE.get(mContext)
+                            .addModeChangeListener(mNavModeChangeListener);
         }
-        updateAutoRotateSetting();
-        updateHomeRotationSetting();
+        initFlags();
     }
 
     /**
@@ -295,6 +308,8 @@
         if (isMultipleOrientationSupportedByDevice()) {
             mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
             mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
+            SysUINavigationMode.INSTANCE.get(mContext)
+                    .removeModeChangeListener(mNavModeChangeListener);
         }
         setRotationWatcherEnabled(false);
     }
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index faa18b8..9a66d32 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -91,6 +91,9 @@
     @Override
     public void onViewAdded(View child) {
         super.onViewAdded(child);
+        if (!isAttachedToWindow()) {
+            return;
+        }
         setFrameLayoutChildInsets(child, mInsets, new Rect());
     }
 
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 87ead9e..535c9e6 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -153,12 +153,16 @@
         public static final int CONTAINER_PREDICTION = -102;
         public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
         public static final int CONTAINER_ALL_APPS = -104;
+        public static final int CONTAINER_WIDGETS_TRAY = -105;
+
 
         public static final String containerToString(int container) {
             switch (container) {
                 case CONTAINER_DESKTOP: return "desktop";
                 case CONTAINER_HOTSEAT: return "hotseat";
                 case CONTAINER_PREDICTION: return "prediction";
+                case CONTAINER_ALL_APPS: return "all_apps";
+                case CONTAINER_WIDGETS_TRAY: return "widgets_tray";
                 default: return String.valueOf(container);
             }
         }
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index 29c9d93..be994ee 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -18,12 +18,15 @@
 
 import android.content.ComponentName;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.model.data.ItemInfo;
 
+import java.util.Optional;
+
 /**
- * Meta data that is used for deferred binding.
- * e.g., this object is used to pass information on draggable targets when they are dropped onto
- * the workspace from another container.
+ * Meta data that is used for deferred binding. e.g., this object is used to pass information on
+ * draggable targets when they are dropped onto the workspace from another container.
  */
 public class PendingAddItemInfo extends ItemInfo {
 
@@ -36,4 +39,22 @@
     protected String dumpProperties() {
         return super.dumpProperties() + " componentName=" + componentName;
     }
+
+    /**
+     * Returns shallow copy of the object.
+     */
+    @Override
+    public ItemInfo makeShallowCopy() {
+        PendingAddItemInfo itemInfo = new PendingAddItemInfo();
+        itemInfo.copyFrom(this);
+        itemInfo.componentName = this.componentName;
+        return itemInfo;
+    }
+
+    @Nullable
+    @Override
+    public ComponentName getTargetComponent() {
+        return Optional.ofNullable(super.getTargetComponent()).orElse(componentName);
+    }
+
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 286b522..6b660c1 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2438,6 +2438,10 @@
                     // widgets/shortcuts/folders in a slightly different way
                     mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
                             item.spanX, item.spanY);
+                    mStatsLogManager.log(
+                            LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
+                            d.logInstanceId,
+                            d.dragInfo.buildProto(null));
                 }
             };
             boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
@@ -2526,11 +2530,12 @@
                 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this);
                 resetTransitionTransform();
             }
+            mStatsLogManager.log(
+                    LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
+                    d.logInstanceId,
+                    d.dragInfo.buildProto(null));
         }
-        mStatsLogManager.log(
-                LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
-                d.logInstanceId,
-                d.dragInfo.buildProto(null));
+
     }
 
     public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 5397942..b4ff5ea 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.OnboardingPrefs;
 
 /**
@@ -153,16 +154,19 @@
         new DiscoveryBounce(launcher, 0).show(HOTSEAT);
     }
 
-    public static void showForOverviewIfNeeded(Launcher launcher) {
-        showForOverviewIfNeeded(launcher, true);
+    public static void showForOverviewIfNeeded(Launcher launcher,
+                                               PagedOrientationHandler orientationHandler) {
+        showForOverviewIfNeeded(launcher, true, orientationHandler);
     }
 
-    private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay) {
+    private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay,
+                                                PagedOrientationHandler orientationHandler) {
         OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs();
         if (!launcher.isInState(OVERVIEW)
                 || !launcher.hasBeenResumed()
                 || launcher.isForceInvisible()
                 || launcher.getDeviceProfile().isVerticalBarLayout()
+                || !orientationHandler.isLayoutNaturalToLauncher()
                 || onboardingPrefs.getBoolean(OnboardingPrefs.SHELF_BOUNCE_SEEN)
                 || launcher.getSystemService(UserManager.class).isDemoUser()
                 || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -170,7 +174,8 @@
         }
 
         if (withDelay) {
-            new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false), DELAY_MS);
+            new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false,
+                    orientationHandler), DELAY_MS);
             return;
         } else if (AbstractFloatingView.getTopOpenView(launcher) != null) {
             // TODO: Move these checks to the top and call this method after invalidate handler.
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 740f7f2..0f04104 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
 
 import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -51,12 +52,8 @@
     private ValueAnimator mProgressAnimator;
 
     public PendingAnimation(long  duration) {
-        this(duration, new AnimatorSet());
-    }
-
-    public PendingAnimation(long  duration, AnimatorSet targetSet) {
         mDuration = duration;
-        mAnim = targetSet;
+        mAnim = new AnimatorSet();
     }
 
     /**
@@ -129,13 +126,32 @@
     public void addOnFrameCallback(Runnable runnable) {
         if (mProgressAnimator == null) {
             mProgressAnimator = ValueAnimator.ofFloat(0, 1).setDuration(mDuration);
-            add(mProgressAnimator);
         }
 
         mProgressAnimator.addUpdateListener(anim -> runnable.run());
     }
 
-    public AnimatorSet getAnim() {
+    /**
+     * @see AnimatorSet#addListener(AnimatorListener)
+     */
+    public void addListener(Animator.AnimatorListener listener) {
+        mAnim.addListener(listener);
+    }
+
+    /**
+     * Creates and returns the underlying AnimatorSet
+     */
+    public AnimatorSet buildAnim() {
+        // Add progress animation to the end, so that frame callback is called after all the other
+        // animation update.
+        if (mProgressAnimator != null) {
+            add(mProgressAnimator);
+            mProgressAnimator = null;
+        }
+        if (mAnimHolders.isEmpty()) {
+            // Add a dummy animation to that the duration is respected
+            add(ValueAnimator.ofFloat(0, 1));
+        }
         return mAnim;
     }
 
@@ -143,7 +159,7 @@
      * Creates a controller for this animation
      */
     public AnimatorPlaybackController createPlaybackController() {
-        return new AnimatorPlaybackController(mAnim, mDuration, mAnimHolders);
+        return new AnimatorPlaybackController(buildAnim(), mDuration, mAnimHolders);
     }
 
     /**
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index b240f0b..475305f 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -35,50 +35,50 @@
     }
 
     public enum LauncherEvent implements EventEnum {
-        @LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
+        @UiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
         LAUNCHER_APP_LAUNCH_TAP(338),
 
-        @LauncherUiEvent(doc = "Task launched from overview using TAP")
+        @UiEvent(doc = "Task launched from overview using TAP")
         LAUNCHER_TASK_LAUNCH_TAP(339),
 
-        @LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
+        @UiEvent(doc = "Task launched from overview using SWIPE DOWN")
         LAUNCHER_TASK_LAUNCH_SWIPE_DOWN(340),
 
-        @LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
+        @UiEvent(doc = "TASK dismissed from overview using SWIPE UP")
         LAUNCHER_TASK_DISMISS_SWIPE_UP(341),
 
-        @LauncherUiEvent(doc = "User dragged a launcher item")
+        @UiEvent(doc = "User dragged a launcher item")
         LAUNCHER_ITEM_DRAG_STARTED(383),
 
-        @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped")
+        @UiEvent(doc = "A dragged launcher item is successfully dropped")
         LAUNCHER_ITEM_DROP_COMPLETED(385),
 
-        @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped on another item "
+        @UiEvent(doc = "A dragged launcher item is successfully dropped on another item "
                 + "resulting in a new folder creation")
         LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
 
-        @LauncherUiEvent(doc = "User action resulted in or manually updated the folder label to "
+        @UiEvent(doc = "User action resulted in or manually updated the folder label to "
                 + "new/same value.")
         LAUNCHER_FOLDER_LABEL_UPDATED(460),
 
-        @LauncherUiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
+        @UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
 
-        @LauncherUiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
+        @UiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_CANCEL(466),
 
-        @LauncherUiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
+        @UiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
                 + " button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST(467),
 
-        @LauncherUiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
+        @UiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
         LAUNCHER_ITEM_DROPPED_ON_UNINSTALL(468),
 
-        @LauncherUiEvent(doc = "User completed uninstalling the package after dropping on "
+        @UiEvent(doc = "User completed uninstalling the package after dropping on "
                 + "the icon onto 'Uninstall' button in the target bar")
         LAUNCHER_ITEM_UNINSTALL_COMPLETED(469),
 
-        @LauncherUiEvent(doc = "User cancelled uninstalling the package after dropping on "
+        @UiEvent(doc = "User cancelled uninstalling the package after dropping on "
                 + "the icon onto 'Uninstall' button in the target bar")
         LAUNCHER_ITEM_UNINSTALL_CANCELLED(470);
         // ADD MORE
diff --git a/src/com/android/launcher3/logging/LauncherUiEvent.java b/src/com/android/launcher3/logging/UiEvent.java
similarity index 80%
rename from src/com/android/launcher3/logging/LauncherUiEvent.java
rename to src/com/android/launcher3/logging/UiEvent.java
index 4507ff7..20d6c72 100644
--- a/src/com/android/launcher3/logging/LauncherUiEvent.java
+++ b/src/com/android/launcher3/logging/UiEvent.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.launcher3.logging;
 
 import static java.lang.annotation.ElementType.FIELD;
@@ -23,8 +24,11 @@
 
 @Retention(SOURCE)
 @Target(FIELD)
-public @interface LauncherUiEvent {
-    /** An explanation, suitable for Android analysts, of the UI event that this log represents. */
+//  Copy of frameworks/base/core/java/com/android/internal/logging/UiEvent.java
+public @interface UiEvent {
+
+    /**
+     * An explanation, suitable for Android analysts, of the UI event that this log represents.
+     */
     String doc();
 }
-
diff --git a/src/com/android/launcher3/model/PredictionModel.java b/src/com/android/launcher3/model/PredictionModel.java
index f8140eb..1429843 100644
--- a/src/com/android/launcher3/model/PredictionModel.java
+++ b/src/com/android/launcher3/model/PredictionModel.java
@@ -43,7 +43,6 @@
     private static final int MAX_CACHE_ITEMS = 5;
 
     protected Context mContext;
-    private ArrayList<ComponentKey> mCachedComponentKeys;
     private SharedPreferences mDevicePrefs;
     private UserCache mUserCache;
 
@@ -78,7 +77,6 @@
                 builder.append("\n");
             }
             mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
-            mCachedComponentKeys = null;
         });
     }
 
@@ -89,17 +87,16 @@
     @WorkerThread
     public List<ComponentKey> getPredictionComponentKeys() {
         Preconditions.assertWorkerThread();
-        if (mCachedComponentKeys == null) {
-            mCachedComponentKeys = new ArrayList<>();
-            String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
-            for (String line : cachedBlob.split("\n")) {
-                ComponentKey key = getComponentKeyFromSerializedString(line);
-                if (key != null) {
-                    mCachedComponentKeys.add(key);
-                }
+        ArrayList<ComponentKey> items = new ArrayList<>();
+        String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
+        for (String line : cachedBlob.split("\n")) {
+            ComponentKey key = getComponentKeyFromSerializedString(line);
+            if (key != null) {
+                items.add(key);
             }
+
         }
-        return mCachedComponentKeys;
+        return items;
     }
 
     private String serializeComponentKeyToString(ComponentKey componentKey) {
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index f2b7e54..a97d529 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
@@ -342,6 +343,11 @@
                         .setAllAppsContainer(
                                 AllAppsContainer.getDefaultInstance())
                         .build();
+            case CONTAINER_WIDGETS_TRAY:
+                return ContainerInfo.newBuilder()
+                        .setWidgetsContainer(
+                                LauncherAtom.WidgetsContainer.getDefaultInstance())
+                        .build();
         }
         return ContainerInfo.getDefaultInstance();
     }
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 4447166..44f7db9 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -239,7 +239,7 @@
                 ? fromState.getTransitionDuration(mActivity)
                 : state.getTransitionDuration(mActivity);
         prepareForAtomicAnimation(fromState, state, mConfig);
-        AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).getAnim();
+        AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).buildAnim();
         if (onCompleteRunnable != null) {
             animation.addListener(AnimationSuccessListener.forRunnable(onCompleteRunnable));
         }
@@ -267,7 +267,7 @@
         for (StateHandler handler : mActivity.getStateManager().getStateHandlers()) {
             handler.setStateWithAnimation(toState, config, builder);
         }
-        return builder.getAnim();
+        return builder.buildAnim();
     }
 
     /**
@@ -309,7 +309,7 @@
         for (StateHandler handler : getStateHandlers()) {
             handler.setStateWithAnimation(state, mConfig, builder);
         }
-        builder.getAnim().addListener(new AnimationSuccessListener() {
+        builder.addListener(new AnimationSuccessListener() {
 
             @Override
             public void onAnimationStart(Animator animation) {
@@ -325,7 +325,7 @@
                 onStateTransitionEnd(state);
             }
         });
-        mConfig.setAnimation(builder.getAnim(), state);
+        mConfig.setAnimation(builder.buildAnim(), state);
         return builder;
     }
 
@@ -554,6 +554,8 @@
      */
     public static class AtomicAnimationFactory<STATE_TYPE> {
 
+        protected static final int NEXT_INDEX = 0;
+
         private final Animator[] mStateElementAnimators;
 
         /**
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 52e2ab8..2e63ccf 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -201,7 +201,7 @@
         mToState = newToState;
         if (TestProtocol.sDebugTracing) {
             Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "reinitCurrentAnimation: "
-                    + newToState.ordinal);
+                    + newToState.ordinal + " " + getClass().getSimpleName());
         }
 
         mStartProgress = 0;
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index 1ec0690..91cf835 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -34,7 +34,7 @@
      * Returns true when we should show depp shortcuts in shortcut menu for the item.
      */
     public static boolean supportsDeepShortcuts(ItemInfo info) {
-        return isActive(info) && isApp(info);
+        return isActive(info) && isApp(info) && !WidgetsModel.GO_DISABLE_WIDGETS;
     }
 
     /**
@@ -64,7 +64,7 @@
     private static boolean isActive(ItemInfo info) {
         boolean isLoading = info instanceof WorkspaceItemInfo
                 && ((WorkspaceItemInfo) info).hasPromiseIconUi();
-        return !isLoading && !info.isDisabled() && !WidgetsModel.GO_DISABLE_WIDGETS;
+        return !isLoading && !info.isDisabled();
     }
 
     private static boolean isApp(ItemInfo info) {
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
index 6e21a41..9601652 100644
--- a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.widget;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
+
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 
@@ -32,5 +34,6 @@
         componentName = activityInfo.getComponent();
         user = activityInfo.getUser();
         itemType = activityInfo.getItemType();
+        this.container = CONTAINER_WIDGETS_TRAY;
     }
 }
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index bc40484..bef9a08 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.widget;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
+
 import android.appwidget.AppWidgetHostView;
 import android.os.Bundle;
 
@@ -50,6 +52,7 @@
         spanY = i.spanY;
         minSpanX = i.minSpanX;
         minSpanY = i.minSpanY;
+        this.container = CONTAINER_WIDGETS_TRAY;
     }
 
     public WidgetAddFlowHandler getHandler() {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index b333100..546ff0b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -362,7 +362,7 @@
     public void checkForAnomaly() {
         final String systemAnomalyMessage = getSystemAnomalyMessage();
         if (systemAnomalyMessage != null) {
-            Assert.fail(formatSystemHealthMessage(closeEvents(
+            Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
                     "http://go/tapl : Tests are broken by a non-Launcher system error: "
                             + systemAnomalyMessage, false)));
         }
@@ -424,7 +424,7 @@
         return message;
     }
 
-    private String closeEvents(String message, boolean checkEvents) {
+    private String formatErrorWithEvents(String message, boolean checkEvents) {
         if (sCheckingEvents) {
             sCheckingEvents = false;
             if (checkEvents) {
@@ -454,7 +454,7 @@
 
     private void fail(String message) {
         checkForAnomaly();
-        Assert.fail(formatSystemHealthMessage(closeEvents(
+        Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
                 "http://go/tapl : " + getContextDescription() + message
                         + " (visible state: " + getVisibleStateMessage() + ")", true)));
     }
@@ -632,8 +632,6 @@
      * @return the Workspace object.
      */
     public Workspace pressHome() {
-        mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(
-                e -> Log.d("b/155926212", e.toString()));
         try (LauncherInstrumentation.Closable e = eventsCheck()) {
             waitForLauncherInitialized();
             // Click home, then wait for any accessibility event, then wait until accessibility
@@ -642,9 +640,7 @@
             // otherwise waitForIdle may return immediately in case when there was a big enough
             // pause in accessibility events prior to pressing Home.
             final String action;
-            Log.d("b/155926212", "Before isLauncherVisible()");
             final boolean launcherWasVisible = isLauncherVisible();
-            Log.d("b/155926212", "After isLauncherVisible(): " + launcherWasVisible);
             if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
                 checkForAnomaly();
 
@@ -700,8 +696,6 @@
                     "performed action to switch to Home - " + action)) {
                 return getWorkspace();
             }
-        } finally {
-            mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(null);
         }
     }
 
@@ -1315,7 +1309,8 @@
                 if (mOnLauncherCrashed != null) mOnLauncherCrashed.run();
                 checkForAnomaly();
                 Assert.fail(
-                        formatSystemHealthMessage(closeEvents("Launcher crashed", false)));
+                        formatSystemHealthMessage(
+                                formatErrorWithEvents("Launcher crashed", false)));
             }
 
             if (sCheckingEvents) {
@@ -1323,6 +1318,16 @@
                 if (mCheckEventsForSuccessfulGestures) {
                     final String message = sEventChecker.verify(WAIT_TIME_MS, true);
                     if (message != null) {
+                        try {
+                            Log.e("b/156287114", "Input:");
+                            for (String line : mDevice.executeShellCommand("dumpsys input").split(
+                                    "\\n")) {
+                                Log.d("b/156287114", line);
+                            }
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+
                         checkForAnomaly();
                         Assert.fail(formatSystemHealthMessage(
                                 "http://go/tapl : successful gesture produced " + message));