Merge "Temporarily disable visibility-gated hotseat and prediction row updates."
diff --git a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml b/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
new file mode 100644
index 0000000..9c95497
--- /dev/null
+++ b/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <solid android:color="@color/gesture_tutorial_fake_previous_task_view_color" />
+</shape>
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 2ff3a5e..9d06dfb 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -31,6 +31,14 @@
         android:visibility="invisible" />
 
     <View
+        android:id="@+id/gesture_tutorial_fake_previous_task_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleX="0.98"
+        android:scaleY="0.98"
+        android:visibility="invisible" />
+
+    <View
         android:id="@+id/gesture_tutorial_fake_task_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c7c0c7e..727fbd3 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -55,11 +55,9 @@
 
     <!-- Total space (start + end) between the task card and the edge of the screen
          in various configurations -->
-    <dimen name="task_card_vert_space">40dp</dimen>
     <dimen name="task_card_menu_option_vertical_padding">8dp</dimen>
     <dimen name="task_card_menu_shadow_height">3dp</dimen>
     <dimen name="task_card_menu_horizontal_padding">0dp</dimen>
-    <dimen name="portrait_task_card_horz_space">136dp</dimen>
     <dimen name="portrait_task_card_horz_space_big_overview">96dp</dimen>
     <dimen name="portrait_modal_task_card_horz_space">60dp</dimen>
     <dimen name="landscape_task_card_horz_space">200dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index f02acab..4204597 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -20,7 +20,6 @@
 import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
 import android.animation.AnimatorSet;
@@ -93,7 +92,7 @@
     @Override
     public void onNavigationModeChanged(Mode newMode) {
         getDragLayer().recreateControllers();
-        if (mActionsView != null && isOverviewActionsEnabled()) {
+        if (mActionsView != null) {
             mActionsView.updateVerticalMargin(newMode);
         }
     }
@@ -190,17 +189,7 @@
         SysUINavigationMode.INSTANCE.get(this).updateMode();
         mActionsView = findViewById(R.id.overview_actions_view);
         ((RecentsView) getOverviewPanel()).init(mActionsView);
-
-        if (isOverviewActionsEnabled()) {
-            // Overview is above all other launcher elements, including qsb, so move it to the top.
-            getOverviewPanel().bringToFront();
-            mActionsView.bringToFront();
-            mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
-        }
-    }
-
-    private boolean isOverviewActionsEnabled() {
-        return removeShelfFromOverview(this);
+        mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
     }
 
     public <T extends OverviewActionsView> T getActionsView() {
@@ -251,7 +240,7 @@
 
     @Override
     public float[] getNormalOverviewScaleAndOffset() {
-        return SysUINavigationMode.getMode(this) == Mode.NO_BUTTON
+        return SysUINavigationMode.getMode(this).hasGestures
                 ? new float[] {1, 1} : new float[] {1.1f, 0};
     }
 
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
index 8f31c22..080633a 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
@@ -52,8 +52,7 @@
     public static Bundle convertDataModelToAppTargetBundle(Context context, BgDataModel dataModel) {
         Bundle bundle = new Bundle();
         ArrayList<AppTargetEvent> events = new ArrayList<>();
-        ArrayList<ItemInfo> workspaceItems = new ArrayList<>(dataModel.workspaceItems);
-        workspaceItems.addAll(dataModel.appWidgets);
+        ArrayList<ItemInfo> workspaceItems = dataModel.getAllWorkspaceItems();
         for (ItemInfo item : workspaceItems) {
             AppTarget target = getAppTargetFromInfo(context, item);
             if (target != null && !isTrackedForPrediction(item)) continue;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 2d704f8..8c3b57a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -29,7 +29,6 @@
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
 import android.content.Intent;
@@ -43,7 +42,6 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.appprediction.PredictionRowView;
 import com.android.launcher3.config.FeatureFlags;
@@ -55,16 +53,15 @@
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
-import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
 import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarToOverviewTouchController;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.UiThreadHelper;
@@ -240,9 +237,6 @@
                 break;
             }
             case OVERVIEW_STATE_ORDINAL: {
-                RecentsView recentsView = getOverviewPanel();
-                DiscoveryBounce.showForOverviewIfNeeded(this,
-                        recentsView.getPagedOrientationHandler());
                 RecentsView rv = getOverviewPanel();
                 sendCustomAccessibilityEvent(
                         rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
@@ -275,24 +269,22 @@
 
         ArrayList<TouchController> list = new ArrayList<>();
         list.add(getDragController());
-        if (mode == NO_BUTTON) {
-            list.add(new NoButtonQuickSwitchTouchController(this));
-            list.add(new NavBarToHomeTouchController(this));
-            list.add(new NoButtonNavbarToOverviewTouchController(this));
-        } else {
-            if (getDeviceProfile().isVerticalBarLayout()) {
-                list.add(new OverviewToAllAppsTouchController(this));
-                list.add(new LandscapeEdgeSwipeController(this));
-                if (mode.hasGestures) {
-                    list.add(new TransposedQuickSwitchTouchController(this));
-                }
-            } else {
-                list.add(new PortraitStatesTouchController(this,
-                        mode.hasGestures /* allowDragToOverview */));
-                if (mode.hasGestures) {
-                    list.add(new QuickSwitchTouchController(this));
-                }
-            }
+        switch (mode) {
+            case NO_BUTTON:
+                list.add(new NoButtonQuickSwitchTouchController(this));
+                list.add(new NavBarToHomeTouchController(this));
+                list.add(new NoButtonNavbarToOverviewTouchController(this));
+                break;
+            case TWO_BUTTONS:
+                list.add(new TwoButtonNavbarToOverviewTouchController(this));
+                list.add(getDeviceProfile().isVerticalBarLayout()
+                        ? new TransposedQuickSwitchTouchController(this)
+                        : new QuickSwitchTouchController(this));
+                list.add(new PortraitStatesTouchController(this));
+                break;
+            case THREE_BUTTONS:
+            default:
+                list.add(new PortraitStatesTouchController(this));
         }
 
         if (!getDeviceProfile().isMultiWindowMode) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index a2e3bdf..37c774b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.content.Context;
 
@@ -84,8 +83,7 @@
 
     @Override
     public float[] getOverviewScaleAndOffset(Launcher launcher) {
-        float offset = removeShelfFromOverview(launcher) ? 1 : 0;
-        return new float[] {0.9f, offset};
+        return new float[] {0.9f, 1};
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 525ff58..b295e79 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -18,8 +18,6 @@
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 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;
 import android.graphics.Rect;
@@ -105,7 +103,7 @@
 
     @Override
     public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
-        if (this == OVERVIEW && removeShelfFromOverview(launcher)) {
+        if (this == OVERVIEW) {
             // Treat the QSB as part of the hotseat so they move together.
             return getHotseatScaleAndTranslation(launcher);
         }
@@ -124,18 +122,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        RecentsView recentsView = launcher.getOverviewPanel();
-        if (removeShelfFromOverview(launcher) ||
-                hideShelfInTwoButtonLandscape(launcher, recentsView.getPagedOrientationHandler())) {
-            return OVERVIEW_BUTTONS;
-        } else if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS;
-        } else {
-            boolean hasAllAppsHeaderExtra = launcher.getAppsView() != null
-                    && launcher.getAppsView().getFloatingHeaderView().hasVisibleContent();
-            return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS
-                    | (hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
-        }
+        return OVERVIEW_BUTTONS;
     }
 
     @Override
@@ -144,20 +131,6 @@
     }
 
     @Override
-    public float getVerticalProgress(Launcher launcher) {
-        if ((getVisibleElements(launcher) & ALL_APPS_HEADER_EXTRA) == 0) {
-            // We have no all apps content, so we're still at the fully down progress.
-            return super.getVerticalProgress(launcher);
-        }
-        return getDefaultVerticalProgress(launcher);
-    }
-
-    public static float getDefaultVerticalProgress(Launcher launcher) {
-        return 1 - (getDefaultSwipeHeight(launcher)
-                / launcher.getAllAppsController().getShiftRange());
-    }
-
-    @Override
     public String getDescription(Launcher launcher) {
         return launcher.getString(R.string.accessibility_recent_apps);
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 77fd103..efb91c6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -17,9 +17,7 @@
 
 import static android.view.View.VISIBLE;
 
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.HINT_STATE;
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
@@ -30,9 +28,7 @@
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
@@ -44,23 +40,17 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 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.RECENTS_SCALE_PROPERTY;
 
-import android.animation.Animator;
-import android.animation.AnimatorSet;
 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.states.StateAnimationConfig;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.quickstep.SysUINavigationMode;
@@ -76,57 +66,12 @@
     // Scale recents takes before animating in
     private static final float RECENTS_PREPARE_SCALE = 1.33f;
 
-    public static final int INDEX_SHELF_ANIM = RecentsAtomicAnimationFactory.NEXT_INDEX + 0;
-
-    private static final int MY_ANIM_COUNT = 1;
-    protected static final int NEXT_INDEX = RecentsAtomicAnimationFactory.NEXT_INDEX
-            + MY_ANIM_COUNT;
-
     // Due to use of physics, duration may differ between devices so we need to calculate and
     // cache the value.
     private int mHintToNormalDuration = -1;
 
     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 = mActivity.getAllAppsController();
-                Animator springAnim = aatc.createSpringAnimation(values);
-
-                if ((OVERVIEW.getVisibleElements(mActivity) & HOTSEAT_ICONS) != 0) {
-                    // Translate hotseat with the shelf until reaching overview.
-                    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]};
-                    }
-                    ValueAnimator hotseatAnim = ValueAnimator.ofFloat(values);
-                    hotseatAnim.addUpdateListener(anim -> {
-                        float progress = (Float) anim.getAnimatedValue();
-                        if (progress >= overviewProgress || mActivity.isInState(BACKGROUND_APP)) {
-                            float hotseatShift = (progress - overviewProgress) * shiftRange;
-                            mActivity.getHotseat().setTranslationY(hotseatShift + sat.translationY);
-                        }
-                    });
-                    hotseatAnim.setInterpolator(LINEAR);
-                    hotseatAnim.setDuration(springAnim.getDuration());
-
-                    AnimatorSet anim = new AnimatorSet();
-                    anim.play(hotseatAnim);
-                    anim.play(springAnim);
-                    return anim;
-                }
-
-                return springAnim;
-            }
-            default:
-                return super.createStateElementAnimation(index, values);
-        }
+        super(activity);
     }
 
     @Override
@@ -191,11 +136,8 @@
             config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
-            Interpolator translationInterpolator = removeShelfFromOverview(mActivity)
-                    ? OVERSHOOT_1_2
-                    : OVERSHOOT_1_7;
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
+            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
         } else if (fromState == HINT_STATE && toState == NORMAL) {
             config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
             if (mHintToNormalDuration == -1) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
deleted file mode 100644
index 7a0f634..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.android.launcher3.uioverrides.touchcontrollers;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.quickstep.SystemUiProxy;
-
-/**
- * Touch controller for handling edge swipes in landscape/seascape UI
- */
-public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchController {
-
-    private static final String TAG = "LandscapeEdgeSwipeCtrl";
-
-    public LandscapeEdgeSwipeController(Launcher l) {
-        super(l, SingleAxisSwipeDetector.HORIZONTAL);
-    }
-
-    @Override
-    protected boolean canInterceptTouch(MotionEvent ev) {
-        if (mCurrentAnimation != null) {
-            // If we are already animating from a previous state, we can intercept.
-            return true;
-        }
-        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
-            return false;
-        }
-        return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
-    }
-
-    @Override
-    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
-        boolean draggingFromNav = mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
-        return draggingFromNav ? OVERVIEW : NORMAL;
-    }
-
-    @Override
-    protected float getShiftRange() {
-        return mLauncher.getDragLayer().getWidth();
-    }
-
-    @Override
-    protected float initCurrentAnimation(@AnimationFlags int animComponent) {
-        float range = getShiftRange();
-        long maxAccuracy = (long) (2 * range);
-        mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
-                maxAccuracy, animComponent);
-        return (mLauncher.getDeviceProfile().isSeascape() ? 2 : -2) / range;
-    }
-
-    @Override
-    protected void onSwipeInteractionCompleted(LauncherState targetState) {
-        super.onSwipeInteractionCompleted(targetState);
-        if (mStartState == NORMAL && targetState == OVERVIEW) {
-            SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index addfe92..45cb46f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -84,7 +84,7 @@
     private ObjectAnimator mNormalToHintOverviewScrimAnimator;
 
     public NoButtonNavbarToOverviewTouchController(Launcher l) {
-        super(l, false /* allowDragToOverview */);
+        super(l);
         mRecentsView = l.getOverviewPanel();
         mMotionPauseDetector = new MotionPauseDetector(l);
         mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
deleted file mode 100644
index 45e5e2f..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides.touchcontrollers;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
-import com.android.quickstep.TouchInteractionService;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Touch controller from going from OVERVIEW to ALL_APPS.
- *
- * This is used in landscape mode. It is also used in portrait mode for the fallback recents.
- */
-public class OverviewToAllAppsTouchController extends PortraitStatesTouchController {
-
-    public OverviewToAllAppsTouchController(Launcher l) {
-        super(l, true /* allowDragToOverview */);
-    }
-
-    @Override
-    protected boolean canInterceptTouch(MotionEvent ev) {
-        if (mCurrentAnimation != null) {
-            // If we are already animating from a previous state, we can intercept.
-            return true;
-        }
-        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
-            return false;
-        }
-        if (mLauncher.isInState(ALL_APPS)) {
-            // In all-apps only listen if the container cannot scroll itself
-            return mLauncher.getAppsView().shouldContainerScroll(ev);
-        } else if (mLauncher.isInState(NORMAL)) {
-            return (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0;
-        } else if (mLauncher.isInState(OVERVIEW)) {
-            RecentsView rv = mLauncher.getOverviewPanel();
-            return ev.getY() > (rv.getBottom() - rv.getPaddingBottom());
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
-        if (fromState == ALL_APPS && !isDragTowardPositive) {
-            // Should swipe down go to OVERVIEW instead?
-            return TouchInteractionService.isConnected() ?
-                    mLauncher.getStateManager().getLastState() : NORMAL;
-        } else if (isDragTowardPositive) {
-            return ALL_APPS;
-        }
-        return fromState;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 3c9b808..73f4ff2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -28,8 +28,6 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
@@ -49,7 +47,6 @@
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.uioverrides.states.OverviewState;
 import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 
@@ -74,21 +71,18 @@
 
     private final InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
 
-    private final boolean mAllowDragToOverview;
-
     // If true, we will finish the current animation instantly on second touch.
     private boolean mFinishFastOnSecondTouch;
 
-    public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {
+    public PortraitStatesTouchController(Launcher l) {
         super(l, SingleAxisSwipeDetector.VERTICAL);
         mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
-        mAllowDragToOverview = allowDragToOverview;
     }
 
     @Override
     protected boolean canInterceptTouch(MotionEvent ev) {
         // If we are swiping to all apps instead of overview, allow it from anywhere.
-        boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview;
+        boolean interceptAnywhere = mLauncher.isInState(NORMAL);
         if (mCurrentAnimation != null) {
             if (mFinishFastOnSecondTouch) {
                 mCurrentAnimation.getAnimationPlayer().end();
@@ -135,37 +129,23 @@
             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");
             }
-            if (removeShelfFromOverview(mLauncher)) {
-                // Don't allow swiping down to overview.
-                return NORMAL;
-            }
-            return TouchInteractionService.isConnected() ?
-                    mLauncher.getStateManager().getLastState() : NORMAL;
+            return NORMAL;
         } else if (fromState == OVERVIEW) {
             if (TestProtocol.sDebugTracing) {
                 Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
                         "PortraitStatesTouchController.getTargetState 2");
             }
-            LauncherState positiveDragTarget = ALL_APPS;
-            if (removeShelfFromOverview(mLauncher)) {
-                // Don't allow swiping up to all apps.
-                positiveDragTarget = OVERVIEW;
-            }
-            return isDragTowardPositive ? positiveDragTarget : NORMAL;
+            return isDragTowardPositive ? OVERVIEW : 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
-                    ? OVERVIEW : ALL_APPS;
+            return ALL_APPS;
         }
         return fromState;
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 9a3a67f..7675a79 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -304,7 +304,8 @@
 
         mCurrentAnimation.setEndAction(this::clearState);
         mCurrentAnimation.startWithVelocity(mActivity, goingToEnd,
-                velocity, mEndDisplacement, animationDuration);
+                velocity * orientationHandler.getSecondaryTranslationDirectionFactor(),
+                mEndDisplacement, animationDuration);
     }
 
     private void clearState() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarToOverviewTouchController.java
new file mode 100644
index 0000000..ff4bfe6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarToOverviewTouchController.java
@@ -0,0 +1,89 @@
+/*
+ * 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.launcher3.uioverrides.touchcontrollers;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+
+import android.view.MotionEvent;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.quickstep.SystemUiProxy;
+
+/**
+ * Touch controller for handling edge swipes in 2-button mode
+ */
+public class TwoButtonNavbarToOverviewTouchController extends AbstractStateChangeTouchController {
+
+    private static final String TAG = "2BtnNavbarTouchCtrl";
+
+    public TwoButtonNavbarToOverviewTouchController(Launcher l) {
+        super(l, l.getDeviceProfile().isVerticalBarLayout()
+                ? SingleAxisSwipeDetector.HORIZONTAL : SingleAxisSwipeDetector.VERTICAL);
+    }
+
+    @Override
+    protected boolean canInterceptTouch(MotionEvent ev) {
+        if (mCurrentAnimation != null) {
+            // If we are already animating from a previous state, we can intercept.
+            return true;
+        }
+        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+            return false;
+        }
+        return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+    }
+
+    @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            boolean draggingFromNav =
+                    mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
+            return draggingFromNav ? OVERVIEW : NORMAL;
+        } else {
+            return isDragTowardPositive ? OVERVIEW : NORMAL;
+        }
+    }
+
+    @Override
+    protected float getShiftRange() {
+        return mLauncher.getDeviceProfile().isVerticalBarLayout()
+                ? mLauncher.getDragLayer().getWidth() : super.getShiftRange();
+    }
+
+    @Override
+    protected float initCurrentAnimation(@AnimationFlags int animComponent) {
+        float range = getShiftRange();
+        long maxAccuracy = (long) (2 * range);
+        mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
+                maxAccuracy, animComponent);
+        return (mLauncher.getDeviceProfile().isSeascape() ? 2 : -2) / range;
+    }
+
+    @Override
+    protected void onSwipeInteractionCompleted(LauncherState targetState) {
+        super.onSwipeInteractionCompleted(targetState);
+        if (mStartState == NORMAL && targetState == OVERVIEW) {
+            SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 3c6299f..f82bc2d 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -15,6 +15,9 @@
  */
 package com.android.quickstep;
 
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 import static android.widget.Toast.LENGTH_SHORT;
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
@@ -724,6 +727,7 @@
         setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
         mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
         mGestureStarted = true;
+        mTaskViewSimulator.setDrawsBelowRecents(true);
     }
 
     /**
@@ -1064,7 +1068,7 @@
                             runningTaskTarget.pictureInPictureParams) != null;
             if (mIsSwipingPipToHome) {
                 mSwipePipToHomeAnimator = getSwipePipToHomeAnimator(
-                        homeAnimFactory, runningTaskTarget);
+                        homeAnimFactory, runningTaskTarget, start);
                 mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION);
                 mSwipePipToHomeAnimator.setInterpolator(interpolator);
                 mSwipePipToHomeAnimator.setFloatValues(0f, 1f);
@@ -1134,23 +1138,34 @@
     }
 
     private SwipePipToHomeAnimator getSwipePipToHomeAnimator(HomeAnimationFactory homeAnimFactory,
-            RemoteAnimationTargetCompat runningTaskTarget) {
+            RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
         // Directly animate the app to PiP (picture-in-picture) mode
         final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
         final RecentsOrientedState orientationState = mTaskViewSimulator.getOrientationState();
+        final int windowRotation = orientationState.getDisplayRotation();
+        final int homeRotation = orientationState.getRecentsActivityRotation();
         final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
                 .startSwipePipToHome(taskInfo.topActivity,
                         TaskInfoCompat.getTopActivityInfo(taskInfo),
                         runningTaskTarget.pictureInPictureParams,
-                        orientationState.getRecentsActivityRotation(),
+                        homeRotation,
                         mDp.hotseatBarSizePx);
+        final Rect startBounds = new Rect();
+        updateProgressForStartRect(new Matrix(), startProgress).round(startBounds);
         final SwipePipToHomeAnimator swipePipToHomeAnimator = new SwipePipToHomeAnimator(
                 runningTaskTarget.taskId,
                 taskInfo.topActivity,
                 runningTaskTarget.leash.getSurfaceControl(),
                 TaskInfoCompat.getPipSourceRectHint(runningTaskTarget.pictureInPictureParams),
                 TaskInfoCompat.getWindowConfigurationBounds(taskInfo),
+                startBounds,
                 destinationBounds);
+        // We would assume home and app window always in the same rotation While homeRotation
+        // is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
+        if (homeRotation == ROTATION_0
+                && (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
+            swipePipToHomeAnimator.setFromRotation(mTaskViewSimulator, windowRotation);
+        }
         swipePipToHomeAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 9089ae5..b4f20d1 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -20,8 +20,6 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.AbsSwipeUpHandler.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;
@@ -186,33 +184,26 @@
      */
     public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
             PagedOrientationHandler orientedState) {
-        calculateTaskSize(context, dp, getExtraSpace(context, dp, orientedState),
-                outRect, orientedState);
+        calculateTaskSize(context, dp, getExtraSpace(context, dp, orientedState), outRect);
     }
 
     protected abstract float getExtraSpace(Context context, DeviceProfile dp,
             PagedOrientationHandler orientedState);
 
-    private void calculateTaskSize(
-            Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect,
-            PagedOrientationHandler orientationHandler) {
+    private void calculateTaskSize(Context context, DeviceProfile dp, float extraVerticalSpace,
+            Rect outRect) {
         Resources res = context.getResources();
-        final boolean showLargeTaskSize = showOverviewActions(context) ||
-                hideShelfInTwoButtonLandscape(context, orientationHandler);
 
         final int paddingResId;
         if (dp.isMultiWindowMode) {
             paddingResId = R.dimen.multi_window_task_card_horz_space;
         } else if (dp.isVerticalBarLayout()) {
             paddingResId = R.dimen.landscape_task_card_horz_space;
-        } else if (showLargeTaskSize) {
-            paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
         } else {
-            paddingResId = R.dimen.portrait_task_card_horz_space;
+            paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
         }
         float paddingHorz = res.getDimension(paddingResId);
-        float paddingVert = showLargeTaskSize
-                ? 0 : res.getDimension(R.dimen.task_card_vert_space);
+        float paddingVert = 0;
 
         calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
                 res.getDimension(R.dimen.task_thumbnail_top_margin), outRect);
@@ -405,8 +396,4 @@
             pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
         }
     }
-
-    protected static boolean showOverviewActions(Context context) {
-        return removeShelfFromOverview(context);
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 2885abf..96e4f38 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -58,9 +58,7 @@
         calculateTaskSize(context, dp, outRect, orientationHandler);
         if (dp.isVerticalBarLayout()
                 && SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
-            Rect targetInsets = dp.getInsets();
-            int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
-            return dp.hotseatBarSizePx + hotseatInset;
+            return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
         } else {
             return dp.heightPx - outRect.bottom;
         }
@@ -161,8 +159,6 @@
     @Override
     protected float getExtraSpace(Context context, DeviceProfile dp,
             PagedOrientationHandler orientationHandler) {
-        return showOverviewActions(context)
-                ? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
-                : 0;
+        return context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index ffb05df..901040d 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -294,8 +294,12 @@
             if (mSurfaceControl != null) {
                 currentRect.roundOut(mTempRect);
                 Transaction t = new Transaction();
-                t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
-                t.apply();
+                try {
+                    t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
+                    t.apply();
+                } catch (RuntimeException e) {
+                    // Ignore
+                }
             }
         }
 
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 98b8455..9f435f5 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -18,10 +18,7 @@
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
 import static com.android.quickstep.SysUINavigationMode.getMode;
-import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
-import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -37,7 +34,6 @@
 import com.android.launcher3.LauncherInitListener;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
-import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
@@ -72,26 +68,13 @@
             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;
+            return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
         } else {
             return LayoutUtils.getShelfTrackingDistance(context, dp, orientationHandler);
         }
     }
 
     @Override
-    public void onSwipeUpToRecentsComplete() {
-        super.onSwipeUpToRecentsComplete();
-        Launcher launcher = getCreatedActivity();
-        if (launcher != null) {
-            RecentsView recentsView = launcher.getOverviewPanel();
-            DiscoveryBounce.showForOverviewIfNeeded(launcher,
-                    recentsView.getPagedOrientationHandler());
-        }
-    }
-
-    @Override
     public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {
         Launcher launcher = getCreatedActivity();
         if (launcher == null) {
@@ -123,16 +106,6 @@
                     PendingAnimation pa) {
                 super.createBackgroundToOverviewAnim(activity, pa);
 
-                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);
@@ -280,35 +253,21 @@
     @Override
     protected float getExtraSpace(Context context, DeviceProfile dp,
             PagedOrientationHandler orientationHandler) {
-        if ((dp.isVerticalBarLayout() && !showOverviewActions(context))
-                || hideShelfInTwoButtonLandscape(context, orientationHandler)) {
-            return 0;
-        } else {
-            Resources res = context.getResources();
-            if (showOverviewActions(context)) {
-                //TODO: this needs to account for the swipe gesture height and accessibility
-                // UI when shown.
-                float actionsBottomMargin = 0;
-                if (!dp.isVerticalBarLayout()) {
-                    if (getMode(context) == Mode.THREE_BUTTONS) {
-                        actionsBottomMargin = res.getDimensionPixelSize(
-                            R.dimen.overview_actions_bottom_margin_three_button);
-                    } else {
-                        actionsBottomMargin = res.getDimensionPixelSize(
-                            R.dimen.overview_actions_bottom_margin_gesture);
-                    }
-                }
-                float actionsHeight = actionsBottomMargin
-                        + res.getDimensionPixelSize(R.dimen.overview_actions_height);
-                return actionsHeight;
+        Resources res = context.getResources();
+        //TODO: this needs to account for the swipe gesture height and accessibility
+        // UI when shown.
+        float actionsBottomMargin = 0;
+        if (!dp.isVerticalBarLayout()) {
+            if (getMode(context) == Mode.THREE_BUTTONS) {
+                actionsBottomMargin = res.getDimensionPixelSize(
+                    R.dimen.overview_actions_bottom_margin_three_button);
             } else {
-                return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
-                        + res.getDimensionPixelSize(
-                        R.dimen.dynamic_grid_hotseat_extra_vertical_size)
-                        + res.getDimensionPixelSize(
-                        R.dimen.dynamic_grid_hotseat_bottom_padding);
+                actionsBottomMargin = res.getDimensionPixelSize(
+                    R.dimen.overview_actions_bottom_margin_gesture);
             }
         }
+        float actionsHeight = actionsBottomMargin
+                + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+        return actionsHeight;
     }
-
 }
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 2559a6f..fd9c315 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -175,8 +175,7 @@
                 return;
             }
 
-            InteractionJankMonitorWrapper.begin(
-                    InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timout */);
+            InteractionJankMonitorWrapper.begin(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
 
             // Otherwise, start overview.
             mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 2f55f14..d050030 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -65,7 +65,6 @@
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
-import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.io.FileDescriptor;
@@ -332,7 +331,7 @@
 
     @Override
     public AtomicAnimationFactory<RecentsState> createAtomicAnimationFactory() {
-        return new RecentsAtomicAnimationFactory<>(this, 0);
+        return new RecentsAtomicAnimationFactory<>(this);
     }
 
     private AnimatorListenerAdapter resetStateListener() {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index d8064a2..960abeb 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -21,7 +21,6 @@
 import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_FRAME_DELAY;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
@@ -296,7 +295,7 @@
      * @return whether the current nav mode has some gestures (either 2 or 0 button mode).
      */
     public boolean isGesturalNavMode() {
-        return mMode == TWO_BUTTONS || mMode == NO_BUTTON;
+        return mMode.hasGestures;
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 4bb1bb5..c0087b0 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -82,7 +82,6 @@
         mTaskViewSimulator.getOrientationState().update(
                 mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
                 mDeviceState.getRotationTouchHelper().getDisplayRotation());
-        mTaskViewSimulator.setDrawsBelowRecents(true);
 
         mMaxShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.max_shadow_radius);
         mTransformParams.setShadowRadius(mMaxShadowRadius);
@@ -176,6 +175,24 @@
     }
 
     /**
+     * Update with start progress for window animation to home.
+     * @param outMatrix {@link Matrix} to map a rect in Launcher space to window space.
+     * @param startProgress The progress of {@link #mCurrentShift} to start thw window from.
+     * @return {@link RectF} represents the bounds as starting point in window space.
+     */
+    protected RectF updateProgressForStartRect(Matrix outMatrix, float startProgress) {
+        mCurrentShift.updateValue(startProgress);
+        mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
+        RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
+
+        mTaskViewSimulator.applyWindowToHomeRotation(outMatrix);
+
+        final RectF startRect = new RectF(cropRectF);
+        mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
+        return startRect;
+    }
+
+    /**
      * 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.
      * @param homeAnimationFactory The home animation factory.
@@ -184,16 +201,11 @@
             HomeAnimationFactory homeAnimationFactory) {
         final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
 
-        mCurrentShift.updateValue(startProgress);
-        mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
+        Matrix homeToWindowPositionMap = new Matrix();
+        final RectF startRect = updateProgressForStartRect(
+                homeToWindowPositionMap, startProgress);
         RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
 
-        // Matrix to map a rect in Launcher space to window space
-        Matrix homeToWindowPositionMap = new Matrix();
-        mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap);
-
-        final RectF startRect = new RectF(cropRectF);
-        mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
         // Move the startRect to Launcher space as floatingIconView runs in Launcher
         Matrix windowToHomePositionMap = new Matrix();
         homeToWindowPositionMap.invert(windowToHomePositionMap);
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index 6b50218..71bf1bb 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -29,7 +29,6 @@
 
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
-import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.MainThreadInitializedObject;
 
 import java.io.PrintWriter;
@@ -170,18 +169,6 @@
         return mMode;
     }
 
-    /** @return Whether we can remove the shelf from overview. */
-    public static boolean removeShelfFromOverview(Context context) {
-        // The shelf is core to the two-button mode model, so we need to continue supporting it.
-        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/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index d1b0a70..a9a9e2a 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -56,7 +56,7 @@
 
 /** Utility class to handle Home and Assistant gestures. */
 public class NavBarGestureHandler implements OnTouchListener,
-        TriggerSwipeUpTouchTracker.OnSwipeUpListener {
+        TriggerSwipeUpTouchTracker.OnSwipeUpListener, MotionPauseDetector.OnMotionPauseListener {
 
     private static final String LOG_TAG = "NavBarGestureHandler";
     private static final long RETRACT_GESTURE_ANIMATION_DURATION_MS = 300;
@@ -181,7 +181,7 @@
                 mLaunchedAssistant = false;
                 mSwipeUpTouchTracker.init();
                 mMotionPauseDetector.clear();
-                mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
+                mMotionPauseDetector.setOnMotionPauseListener(this);
                 break;
             case MotionEvent.ACTION_MOVE:
                 mLastPos.set(event.getX(), event.getY());
@@ -256,7 +256,13 @@
                 || event.getY() >= mDisplaySize.y - mBottomGestureHeight;
     }
 
-    protected void onMotionPauseDetected() {
+    @Override
+    public void onMotionPauseChanged(boolean isPaused) {
+        mGestureCallback.onMotionPaused(isPaused);
+    }
+
+    @Override
+    public void onMotionPauseDetected() {
         VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
     }
 
@@ -311,6 +317,9 @@
         /** Called whenever any touch is completed. */
         void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity);
 
+        /** Called when a motion stops or resumes */
+        default void onMotionPaused(boolean isPaused) {}
+
         /** Indicates how far a touch originating in the nav bar has moved from the nav bar. */
         default void setNavBarGestureProgress(@Nullable Float displacement) {}
 
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 865b66e..68c63bf 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -61,16 +61,21 @@
 
 @TargetApi(Build.VERSION_CODES.R)
 abstract class SwipeUpGestureTutorialController extends TutorialController {
-    private final ViewSwipeUpAnimation mViewSwipeUpAnimation;
+
+    private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(12);
+
+    private final ViewSwipeUpAnimation mTaskViewSwipeUpAnimation;
     private float mFakeTaskViewRadius;
     private Rect mFakeTaskViewRect = new Rect();
     private RunningWindowAnim mRunningWindowAnim;
+    private boolean mShowTasks = false;
+    private boolean mShowPreviousTasks = false;
 
     SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
         super(tutorialFragment, tutorialType);
         RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext);
         OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState);
-        mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
+        mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
                 new GestureState(observer, -1));
         observer.onDestroy();
         deviceState.destroy();
@@ -83,16 +88,22 @@
                 .getWindowInsets()
                 .getInsets(WindowInsets.Type.systemBars());
         dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom));
-        mViewSwipeUpAnimation.initDp(dp);
+        mTaskViewSwipeUpAnimation.initDp(dp);
 
         mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources());
-        mFakeTaskView.setClipToOutline(true);
-        mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() {
+
+        ViewOutlineProvider outlineProvider = new ViewOutlineProvider() {
             @Override
             public void getOutline(View view, Outline outline) {
                 outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius);
             }
-        });
+        };
+
+        mFakeTaskView.setClipToOutline(true);
+        mFakeTaskView.setOutlineProvider(outlineProvider);
+
+        mFakePreviousTaskView.setClipToOutline(true);
+        mFakePreviousTaskView.setOutlineProvider(outlineProvider);
     }
 
     private void cancelRunningAnimation() {
@@ -114,16 +125,22 @@
                 mFakeIconView.setVisibility(View.INVISIBLE);
                 mFakeTaskView.setVisibility(View.INVISIBLE);
                 mFakeTaskView.setAlpha(1);
+                mFakePreviousTaskView.setVisibility(View.INVISIBLE);
+                mFakePreviousTaskView.setAlpha(1);
+                mShowTasks = false;
+                mShowPreviousTasks = false;
                 mRunningWindowAnim = null;
             }
         };
         if (toOverviewFirst) {
-            anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
+            anim.setFloat(mTaskViewSwipeUpAnimation
+                    .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation, boolean isReverse) {
                     PendingAnimation fadeAnim = new PendingAnimation(300);
                     fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL);
+                    fadeAnim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL);
                     fadeAnim.addListener(resetTaskView);
                     AnimatorSet animset = fadeAnim.buildAnim();
                     animset.setStartDelay(100);
@@ -133,6 +150,7 @@
             });
         } else {
             anim.setViewAlpha(mFakeTaskView, 0, ACCEL);
+            anim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL);
             anim.setViewAlpha(mFakeIconView, 0, ACCEL);
             anim.addListener(resetTaskView);
         }
@@ -148,8 +166,10 @@
         hideFeedback();
         hideHandCoachingAnimation();
         cancelRunningAnimation();
+        mFakePreviousTaskView.setVisibility(View.INVISIBLE);
+        mShowPreviousTasks = false;
         RectFSpringAnim rectAnim =
-                mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity);
+                mTaskViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity);
         // After home animation finishes, fade out and run onEndRunnable.
         rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable(
                 () -> fadeOutFakeTaskView(false, onEndRunnable)));
@@ -161,11 +181,31 @@
         if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE
                 || mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) {
             mFakeTaskView.setVisibility(View.INVISIBLE);
+            mFakePreviousTaskView.setVisibility(View.INVISIBLE);
         } else {
+            mShowTasks = true;
             mFakeTaskView.setVisibility(View.VISIBLE);
-            if (mRunningWindowAnim == null) {
-                mViewSwipeUpAnimation.updateDisplacement(displacement);
+            if (mShowPreviousTasks) {
+                mFakePreviousTaskView.setVisibility(View.VISIBLE);
             }
+            if (mRunningWindowAnim == null) {
+                mTaskViewSwipeUpAnimation.updateDisplacement(displacement);
+            }
+        }
+    }
+
+    @Override
+    public void onMotionPaused(boolean unused) {
+        if (mShowTasks) {
+            if (!mShowPreviousTasks) {
+                mFakePreviousTaskView.setTranslationX(
+                        -(2 * mFakePreviousTaskView.getWidth() + FAKE_PREVIOUS_TASK_MARGIN));
+                mFakePreviousTaskView.animate()
+                    .setDuration(300)
+                    .translationX(-(mFakePreviousTaskView.getWidth() + FAKE_PREVIOUS_TASK_MARGIN))
+                    .start();
+            }
+            mShowPreviousTasks = true;
         }
     }
 
@@ -232,6 +272,7 @@
                             false /* isVerticalBarLayout */);
                     mFakeIconView.setAlpha(1);
                     mFakeTaskView.setAlpha(getWindowAlpha(progress));
+                    mFakePreviousTaskView.setAlpha(getWindowAlpha(progress));
                 }
 
                 @Override
@@ -258,9 +299,11 @@
         public void applySurfaceParams(SurfaceParams[] params) {
             SurfaceParams p = params[0];
             mFakeTaskView.setAnimationMatrix(p.matrix);
+            mFakePreviousTaskView.setAnimationMatrix(p.matrix);
             mFakeTaskViewRect.set(p.windowCrop);
             mFakeTaskViewRadius = p.cornerRadius;
             mFakeTaskView.invalidateOutline();
+            mFakePreviousTaskView.invalidateOutline();
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 0d5a110..12d2efc 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -52,6 +52,7 @@
     final View mLauncherView;
     final ClipIconView mFakeIconView;
     final View mFakeTaskView;
+    final View mFakePreviousTaskView;
     final View mRippleView;
     final RippleDrawable mRippleDrawable;
     @Nullable final TutorialHandAnimation mHandCoachingAnimation;
@@ -74,6 +75,8 @@
         mLauncherView = getMockLauncherView();
         mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
         mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
+        mFakePreviousTaskView =
+                rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
         mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
         mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
         mHandCoachingAnimation = tutorialFragment.getHandAnimation();
@@ -93,6 +96,8 @@
         if (mContext != null) {
             rootView.setBackground(mContext.getDrawable(getMockWallpaperResId()));
             mFakeTaskView.setBackground(mContext.getDrawable(getMockAppTaskThumbnailResId()));
+            mFakePreviousTaskView.setBackground(
+                    mContext.getDrawable(getMockPreviousAppTaskThumbnailResId()));
             mFakeIconView.setBackground(mContext.getDrawable(getMockAppIconResId()));
         }
     }
@@ -126,6 +131,11 @@
         return R.drawable.default_sandbox_app_task_thumbnail;
     }
 
+    @DrawableRes
+    protected int getMockPreviousAppTaskThumbnailResId() {
+        return R.drawable.default_sandbox_app_previous_task_thumbnail;
+    }
+
     @Nullable
     public View getMockLauncherView() {
         InvariantDeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext);
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index b88a195..8834dc2 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -15,15 +15,12 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewGroup;
 
 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;
@@ -44,16 +41,10 @@
     public static int getShelfTrackingDistance(Context context, DeviceProfile dp,
             PagedOrientationHandler orientationHandler) {
         // Track the bottom of the window.
-        if (removeShelfFromOverview(context)) {
-            Rect taskSize = new Rect();
-            LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize,
-                    orientationHandler);
-            return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
-        }
-        int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
-        int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
-                R.dimen.task_card_vert_space);
-        return shelfHeight + spaceBetweenShelfAndRecents;
+        Rect taskSize = new Rect();
+        LauncherActivityInterface.INSTANCE.calculateTaskSize(
+                context, dp, taskSize, orientationHandler);
+        return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index b10adb4..a85f0d2 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -22,7 +22,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.content.SharedPreferences;
 
@@ -64,27 +63,6 @@
             });
         }
 
-        boolean shelfBounceSeen = getBoolean(SHELF_BOUNCE_SEEN);
-        if (!shelfBounceSeen && removeShelfFromOverview(launcher)) {
-            // There's no shelf in overview, so don't bounce it (can't get to all apps anyway).
-            shelfBounceSeen = true;
-            mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, shelfBounceSeen).apply();
-        }
-        if (!shelfBounceSeen) {
-            stateManager.addStateListener(new StateListener<LauncherState>() {
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    LauncherState prevState = stateManager.getLastState();
-
-                    if ((finalState == ALL_APPS && prevState == OVERVIEW) ||
-                            hasReachedMaxCount(SHELF_BOUNCE_COUNT)) {
-                        mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
-                        stateManager.removeStateListener(this);
-                    }
-                }
-            });
-        }
-
         if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() && !hasReachedMaxCount(
                 HOTSEAT_DISCOVERY_TIP_COUNT)) {
             stateManager.addStateListener(new StateListener<LauncherState>() {
diff --git a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
index 5b0d503..ba70bf7 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -32,16 +32,11 @@
     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);
+    public RecentsAtomicAnimationFactory(ACTIVITY_TYPE activity) {
+        super(MY_ANIM_COUNT);
         mActivity = activity;
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index facc99a..dd3e31f 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -26,7 +26,6 @@
 import static com.android.launcher3.Utilities.newContentObserver;
 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;
 
@@ -55,7 +54,6 @@
 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;
@@ -281,10 +279,7 @@
     }
 
     private void initFlags() {
-        SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
-        boolean rotationWatcherSupported = mOrientationListener.canDetectOrientation() &&
-                currentMode != TWO_BUTTONS;
-        setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, rotationWatcherSupported);
+        setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, mOrientationListener.canDetectOrientation());
 
         // initialize external flags
         updateAutoRotateSetting();
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 87fee79..8fbd645 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -16,17 +16,24 @@
 
 package com.android.quickstep.util;
 
+import static com.android.systemui.shared.system.InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_PIP;
+
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
 import android.content.ComponentName;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.Log;
+import android.view.Surface;
 import android.view.SurfaceControl;
 
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.systemui.shared.pip.PipSurfaceTransactionHelper;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
 /**
  * An {@link Animator} that animates an Activity to PiP (picture-in-picture) window when
@@ -39,9 +46,12 @@
  */
 public class SwipePipToHomeAnimator extends ValueAnimator implements
         ValueAnimator.AnimatorUpdateListener {
+    private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName();
+
     private final int mTaskId;
     private final ComponentName mComponentName;
     private final SurfaceControl mLeash;
+    private final Rect mAppBounds = new Rect();
     private final Rect mStartBounds = new Rect();
     private final Rect mDestinationBounds = new Rect();
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
@@ -52,49 +62,134 @@
     private final Rect mSourceHintRectInsets = new Rect();
     private final Rect mSourceInsets = new Rect();
 
+    /** for rotation via {@link #setFromRotation(TaskViewSimulator, int)} */
+    private @RecentsOrientedState.SurfaceRotation int mFromRotation = Surface.ROTATION_0;
+    private final Rect mDestinationBoundsTransformed = new Rect();
+    private final Rect mDestinationBoundsAnimation = new Rect();
+
     /**
      * Flag to avoid the double-end problem since the leash would have been released
      * after the first end call and any further operations upon it would lead to NPE.
      */
     private boolean mHasAnimationEnded;
 
+    /**
+     * @param taskId Task id associated with this animator, see also {@link #getTaskId()}
+     * @param componentName Component associated with this animator,
+     *                      see also {@link #getComponentName()}
+     * @param leash {@link SurfaceControl} this animator operates on
+     * @param sourceRectHint See the definition in {@link android.app.PictureInPictureParams}
+     * @param appBounds Bounds of the application, sourceRectHint is based on this bounds
+     * @param startBounds Bounds of the application when this animator starts. This can be
+     *                    different from the appBounds if user has swiped a certain distance and
+     *                    Launcher has performed transform on the leash.
+     * @param destinationBounds Bounds of the destination this animator ends to
+     */
     public SwipePipToHomeAnimator(int taskId,
             @NonNull ComponentName componentName,
             @NonNull SurfaceControl leash,
             @NonNull Rect sourceRectHint,
+            @NonNull Rect appBounds,
             @NonNull Rect startBounds,
             @NonNull Rect destinationBounds) {
         mTaskId = taskId;
         mComponentName = componentName;
         mLeash = leash;
+        mAppBounds.set(appBounds);
         mStartBounds.set(startBounds);
         mDestinationBounds.set(destinationBounds);
+        mDestinationBoundsTransformed.set(mDestinationBounds);
+        mDestinationBoundsAnimation.set(mDestinationBounds);
         mSurfaceTransactionHelper = new PipSurfaceTransactionHelper();
 
-        mSourceHintRectInsets.set(sourceRectHint.left - startBounds.left,
-                sourceRectHint.top - startBounds.top,
-                startBounds.right - sourceRectHint.right,
-                startBounds.bottom - sourceRectHint.bottom);
+        mSourceHintRectInsets.set(sourceRectHint.left - appBounds.left,
+                sourceRectHint.top - appBounds.top,
+                appBounds.right - sourceRectHint.right,
+                appBounds.bottom - sourceRectHint.bottom);
 
-        addListener(new AnimatorListenerAdapter() {
+        addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                InteractionJankMonitorWrapper.begin(CUJ_APP_CLOSE_TO_PIP);
+                super.onAnimationStart(animation);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                InteractionJankMonitorWrapper.cancel(CUJ_APP_CLOSE_TO_PIP);
+            }
+
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                InteractionJankMonitorWrapper.end(CUJ_APP_CLOSE_TO_PIP);
+            }
+
             @Override
             public void onAnimationEnd(Animator animation) {
+                if (!mHasAnimationEnded) super.onAnimationEnd(animation);
                 SwipePipToHomeAnimator.this.onAnimationEnd();
             }
         });
         addUpdateListener(this);
     }
 
+    /** sets the from rotation if it's different from the target rotation. */
+    public void setFromRotation(TaskViewSimulator taskViewSimulator,
+            @RecentsOrientedState.SurfaceRotation int fromRotation) {
+        if (fromRotation != Surface.ROTATION_90 && fromRotation != Surface.ROTATION_270) {
+            Log.wtf(TAG, "Not a supported rotation, rotation=" + fromRotation);
+            return;
+        }
+        mFromRotation = fromRotation;
+        final Matrix matrix = new Matrix();
+        taskViewSimulator.applyWindowToHomeRotation(matrix);
+
+        // map the destination bounds into window space. mDestinationBounds is always calculated
+        // in the final home space and the animation runs in original window space.
+        final RectF transformed = new RectF(mDestinationBounds);
+        matrix.mapRect(transformed, new RectF(mDestinationBounds));
+        transformed.round(mDestinationBoundsTransformed);
+
+        // set the animation destination bounds for RectEvaluator calculation.
+        // bounds and insets are calculated as if the transition is from mAppBounds to
+        // mDestinationBoundsAnimation, separated from rotate / scale / position.
+        mDestinationBoundsAnimation.set(mAppBounds.left, mAppBounds.top,
+                mAppBounds.left + mDestinationBounds.width(),
+                mAppBounds.top + mDestinationBounds.height());
+    }
+
     @Override
     public void onAnimationUpdate(ValueAnimator animator) {
         if (mHasAnimationEnded) return;
 
         final float fraction = animator.getAnimatedFraction();
-        final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds, mDestinationBounds);
+        final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds,
+                mDestinationBoundsAnimation);
         final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets,
                 mSourceHintRectInsets);
-        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-        mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mStartBounds, bounds, insets);
+        final SurfaceControl.Transaction tx =
+                PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+        if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
+            final float degree, positionX, positionY;
+            if (mFromRotation == Surface.ROTATION_90) {
+                degree = -90 * fraction;
+                positionX = fraction * (mDestinationBoundsTransformed.left - mAppBounds.left)
+                        + mAppBounds.left;
+                positionY = fraction * (mDestinationBoundsTransformed.bottom - mAppBounds.top)
+                        + mAppBounds.top;
+            } else {
+                degree = 90 * fraction;
+                positionX = fraction * (mDestinationBoundsTransformed.right - mAppBounds.left)
+                        + mAppBounds.left;
+                positionY = fraction * (mDestinationBoundsTransformed.top - mAppBounds.top)
+                        + mAppBounds.top;
+            }
+            mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
+                    degree, positionX, positionY);
+        } else {
+            mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets);
+        }
         mSurfaceTransactionHelper.resetCornerRadius(tx, mLeash);
         tx.apply();
     }
@@ -114,8 +209,9 @@
     private void onAnimationEnd() {
         if (mHasAnimationEnded) return;
 
-        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-        mSurfaceTransactionHelper.reset(tx, mLeash, mDestinationBounds);
+        final SurfaceControl.Transaction tx =
+                PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+        mSurfaceTransactionHelper.reset(tx, mLeash, mDestinationBoundsTransformed, mFromRotation);
         tx.apply();
         mHasAnimationEnded = true;
     }
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index b8e07cb..8fb7e03 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -17,7 +17,6 @@
 package com.android.quickstep.views;
 
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SHARE;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -51,17 +50,15 @@
     private final Rect mInsets = new Rect();
 
     @IntDef(flag = true, value = {
-            HIDDEN_UNSUPPORTED_NAVIGATION,
             HIDDEN_NON_ZERO_ROTATION,
             HIDDEN_NO_TASKS,
             HIDDEN_NO_RECENTS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActionsHiddenFlags { }
 
-    public static final int HIDDEN_UNSUPPORTED_NAVIGATION = 1 << 0;
-    public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 1;
-    public static final int HIDDEN_NO_TASKS = 1 << 2;
-    public static final int HIDDEN_NO_RECENTS = 1 << 3;
+    public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 0;
+    public static final int HIDDEN_NO_TASKS = 1 << 1;
+    public static final int HIDDEN_NO_RECENTS = 1 << 2;
 
     @IntDef(flag = true, value = {
             DISABLED_SCROLLING,
@@ -138,12 +135,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        updateHiddenFlags(HIDDEN_UNSUPPORTED_NAVIGATION, !removeShelfFromOverview(getContext()));
-    }
-
-    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index e6613eb..db04fc0 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -15,7 +15,6 @@
  */
 package com.android.quickstep.views;
 
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
@@ -30,7 +29,6 @@
 import android.graphics.Path;
 import android.graphics.Path.Direction;
 import android.graphics.Path.Op;
-import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.animation.Interpolator;
 
@@ -45,7 +43,6 @@
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
-import com.android.quickstep.util.LayoutUtils;
 
 /**
  * Scrim used for all-apps and shelf in Overview
@@ -149,25 +146,11 @@
             mRemainingScreenPathValid = false;
             mShiftRange = mLauncher.getAllAppsController().getShiftRange();
 
-            Context context = getContext();
-            if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
-                if (SysUINavigationMode.removeShelfFromOverview(context)) {
-                    // Fade in all apps background quickly to distinguish from swiping from nav bar.
-                    mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
-                    mMidProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
-                } else {
-                    mMidAlpha = 0;
-                    mMidProgress = 1;
-                }
-            } else {
-                mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
-                mMidProgress =  OVERVIEW.getVerticalProgress(mLauncher);
-                Rect hotseatPadding = dp.getHotseatLayoutPadding();
-                int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
-                        + hotseatPadding.bottom + hotseatPadding.top;
-                float dragHandleTop =
-                        Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp));
-            }
+            // Fade in all apps background quickly to distinguish from swiping from nav bar.
+            mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
+            mMidProgress = 1 - (OverviewState.getDefaultSwipeHeight(mLauncher)
+                    / mLauncher.getAllAppsController().getShiftRange());
+
             mTopOffset = dp.getInsets().top;
             mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
         }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index cc97f4b..0fe5432 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -31,14 +31,11 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.tapl.AllApps;
-import com.android.launcher3.tapl.AllAppsFromOverview;
 import com.android.launcher3.tapl.Background;
 import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
 import com.android.launcher3.tapl.Overview;
 import com.android.launcher3.tapl.OverviewActions;
 import com.android.launcher3.tapl.OverviewTask;
-import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.ui.TaplTestsLauncher3;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 import com.android.quickstep.views.RecentsView;
@@ -94,19 +91,6 @@
     }
 
     @Test
-    public void testAllAppsFromOverview() throws Exception {
-        if (!mLauncher.hasAllAppsInOverview()) {
-            return;
-        }
-
-        // Test opening all apps from Overview.
-        assertNotNull("switchToAllApps() returned null",
-                mLauncher.getWorkspace().switchToOverview().switchToAllApps());
-
-        TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllAppsFromOverview());
-    }
-
-    @Test
     @PortraitLandscape
     public void testOverview() throws Exception {
         startTestAppsWithCheck();
@@ -159,28 +143,6 @@
                 launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
                         numTasks - 1, getTaskCount(launcher)));
 
-        if (mLauncher.hasAllAppsInOverview() && (!TestHelpers.isInLauncherProcess()
-                || getFromLauncher(launcher -> !launcher.getDeviceProfile().isLandscape))) {
-            // Test switching to all apps and back.
-            final AllAppsFromOverview allApps = overview.switchToAllApps();
-            assertNotNull("overview.switchToAllApps() returned null (1)", allApps);
-            assertTrue("Launcher internal state is not All Apps (1)",
-                    isInState(() -> LauncherState.ALL_APPS));
-
-            overview = allApps.switchBackToOverview();
-            assertNotNull("allApps.switchBackToOverview() returned null", overview);
-            assertTrue("Launcher internal state didn't switch to Overview",
-                    isInState(() -> LauncherState.OVERVIEW));
-
-            // Test UIDevice.pressBack()
-            overview.switchToAllApps();
-            assertNotNull("overview.switchToAllApps() returned null (2)", allApps);
-            assertTrue("Launcher internal state is not All Apps (2)",
-                    isInState(() -> LauncherState.ALL_APPS));
-            mDevice.pressBack();
-            mLauncher.getOverview();
-        }
-
         // Test UIDevice.pressHome, once we are in AllApps.
         mDevice.pressHome();
         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
@@ -200,19 +162,17 @@
     @NavigationModeSwitch
     @PortraitLandscape
     public void testOverviewActions() throws Exception {
-        if (mLauncher.getNavigationModel() != NavigationModel.TWO_BUTTON) {
-            // Experimenting for b/165029151:
-            final Overview overview = mLauncher.pressHome().switchToOverview();
-            if (overview.hasTasks()) overview.dismissAllTasks();
-            mLauncher.pressHome();
-            //
+        // Experimenting for b/165029151:
+        final Overview overview = mLauncher.pressHome().switchToOverview();
+        if (overview.hasTasks()) overview.dismissAllTasks();
+        mLauncher.pressHome();
+        //
 
-            startTestAppsWithCheck();
-            OverviewActions actionsView =
-                    mLauncher.pressHome().switchToOverview().getOverviewActions();
-            actionsView.clickAndDismissScreenshot();
-            actionsView.clickAndDismissShare();
-        }
+        startTestAppsWithCheck();
+        OverviewActions actionsView =
+                mLauncher.pressHome().switchToOverview().getOverviewActions();
+        actionsView.clickAndDismissScreenshot();
+        actionsView.clickAndDismissShare();
     }
 
     private int getCurrentOverviewPage(Launcher launcher) {
@@ -224,20 +184,6 @@
     }
 
     @Test
-    public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
-        if (!mLauncher.hasAllAppsInOverview()) {
-            return;
-        }
-
-        final AllApps allApps =
-                mLauncher.getWorkspace().switchToOverview().switchToAllApps();
-        assertTrue("Launcher internal state is not All Apps",
-                isInState(() -> LauncherState.ALL_APPS));
-
-        TaplTestsLauncher3.runIconLaunchFromAllAppsTest(this, allApps);
-    }
-
-    @Test
     @NavigationModeSwitch
     @PortraitLandscape
     public void testSwitchToOverview() throws Exception {
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 0c18c8a..8451b77 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -49,11 +49,6 @@
             android:id="@+id/hotseat"
             layout="@layout/hotseat" />
 
-        <include
-            android:id="@+id/overview_panel"
-            layout="@layout/overview_panel" />
-
-
         <!-- Keep these behind the workspace so that they are not visible when
          we go into AllApps -->
         <com.android.launcher3.pageindicators.WorkspacePageIndicator
@@ -77,6 +72,10 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
 
+        <include
+            android:id="@+id/overview_panel"
+            layout="@layout/overview_panel" />
+
     </com.android.launcher3.dragndrop.DragLayer>
 
 </com.android.launcher3.LauncherRootView>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index f56fbaa..78c2df6 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -37,6 +37,7 @@
 
     <color name="gesture_tutorial_ripple_color">#A0C2F9</color> <!-- Light Blue -->
     <color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
+    <color name="gesture_tutorial_fake_previous_task_view_color">#9CCC65</color> <!-- Light Green -->
     <color name="gesture_tutorial_action_button_label_color">#FFFFFFFF</color>
     <color name="gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
 </resources>
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index e43df21..412ace0 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -67,7 +67,7 @@
         for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
             if (info instanceof WorkspaceItemInfo) {
                 assertEquals(updates.contains(info.id) ? progress: 0,
-                        ((WorkspaceItemInfo) info).getInstallProgress());
+                        ((WorkspaceItemInfo) info).getProgressLevel());
             } else {
                 assertEquals(updates.contains(info.id) ? progress: -1,
                         ((LauncherAppWidgetInfo) info).installProgress);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3eb52ad..f44f88b 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -50,6 +50,7 @@
 import android.view.ViewDebug;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.Launcher.OnResumeCallback;
@@ -71,7 +72,6 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.RemoteActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.SafeCloseable;
@@ -287,10 +287,7 @@
     public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info);
         setTag(info);
-        if (promiseStateChanged || (info.hasPromiseIconUi())) {
-            applyPromiseState(promiseStateChanged);
-        }
-
+        applyLoadingState(promiseStateChanged);
         applyDotState(info, false /* animate */);
     }
 
@@ -303,9 +300,8 @@
         // Verify high res immediately
         verifyHighRes();
 
-        if (info instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
-            applyProgressLevel(promiseAppInfo.level);
+        if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+            applyProgressLevel(info.getProgressLevel());
         }
         applyDotState(info, false /* animate */);
     }
@@ -335,6 +331,10 @@
         mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
 
         setIcon(iconDrawable);
+        applyLabel(info);
+    }
+
+    private void applyLabel(ItemInfoWithIcon info) {
         setText(info.title);
         if (info.contentDescription != null) {
             setContentDescription(info.isDisabled()
@@ -595,21 +595,35 @@
         mLongPressHelper.cancelLongPress();
     }
 
-    public void applyPromiseState(boolean promiseStateChanged) {
+    /** Applies the loading progress value to the progress bar.
+     *
+     * If this app is installing, the progress bar will be updated with the installation progress.
+     * If this app is installed and downloading incrementally, the progress bar will be updated
+     * with the total download progress.
+     */
+    public void applyLoadingState(boolean promiseStateChanged) {
         if (getTag() instanceof WorkspaceItemInfo) {
             WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
-            final boolean isPromise = info.hasPromiseIconUi();
-            final int progressLevel = isPromise ?
-                    ((info.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
-                            info.getInstallProgress() : 0)) : 100;
-
-            PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
-            if (preloadDrawable != null && promiseStateChanged) {
-                preloadDrawable.maybePerformFinishedAnimation();
+            int progressLevel = info.getProgressLevel();
+            if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE)
+                    != 0) {
+                updateProgressBarUi(progressLevel, progressLevel == 100);
+            } else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags
+                        & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+                updateProgressBarUi(progressLevel, promiseStateChanged);
             }
         }
     }
 
+    private void updateProgressBarUi(int progressLevel, boolean maybePerformFinishedAnimation) {
+        PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
+        if (preloadDrawable != null && maybePerformFinishedAnimation) {
+            preloadDrawable.maybePerformFinishedAnimation();
+        }
+    }
+
+    /** Applies the given progress level to the this icon's progress bar. */
+    @Nullable
     public PreloadIconDrawable applyProgressLevel(int progressLevel) {
         if (getTag() instanceof ItemInfoWithIcon) {
             ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
@@ -629,9 +643,11 @@
                 if (mIcon instanceof PreloadIconDrawable) {
                     preloadDrawable = (PreloadIconDrawable) mIcon;
                     preloadDrawable.setLevel(progressLevel);
+                    preloadDrawable.setIsDisabled(!info.isAppStartable());
                 } else {
                     preloadDrawable = newPendingIcon(getContext(), info);
                     preloadDrawable.setLevel(progressLevel);
+                    preloadDrawable.setIsDisabled(!info.isAppStartable());
                     setIcon(preloadDrawable);
                 }
                 return preloadDrawable;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a96fabd..5b55c4b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -137,7 +137,6 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pm.PinRequestHelper;
@@ -2516,8 +2515,8 @@
     }
 
     @Override
-    public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
-        mAppsView.getAppsStore().updatePromiseAppProgress(app);
+    public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
+        mAppsView.getAppsStore().updateProgressBar(app);
     }
 
     @Override
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 8458152..e89b9b0 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -48,6 +48,7 @@
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDelegate;
 import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.PackageIncrementalDownloadUpdatedTask;
 import com.android.launcher3.model.PackageInstallStateChangedTask;
 import com.android.launcher3.model.PackageUpdatedTask;
 import com.android.launcher3.model.ShortcutsChangedTask;
@@ -196,6 +197,15 @@
     }
 
     @Override
+    public void onPackageLoadingProgressChanged(
+                String packageName, UserHandle user, float progress) {
+        if (Utilities.ATLEAST_S) {
+            enqueueModelUpdateTask(new PackageIncrementalDownloadUpdatedTask(
+                    packageName, user, progress));
+        }
+    }
+
+    @Override
     public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
             UserHandle user) {
         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index aeed16a..e151777 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -1099,11 +1099,20 @@
      * @return the max _id in the provided table.
      */
     @Thunk static int getMaxId(SQLiteDatabase db, String query, Object... args) {
-        int max = (int) DatabaseUtils.longForQuery(db,
-                String.format(Locale.ENGLISH, query, args),
-                null);
-        if (max < 0) {
-            throw new RuntimeException("Error: could not query max id");
+        int max = 0;
+        try (SQLiteStatement prog = db.compileStatement(
+                String.format(Locale.ENGLISH, query, args))) {
+            max = (int) DatabaseUtils.longForQuery(prog, null);
+            if (max < 0) {
+                throw new RuntimeException("Error: could not query max id");
+            }
+        } catch (IllegalArgumentException exception) {
+            String message = exception.getMessage();
+            if (message.contains("re-open") && message.contains("already-closed")) {
+                // Don't crash trying to end a transaction an an already closed DB. See b/173162852.
+            } else {
+                throw exception;
+            }
         }
         return max;
     }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 1e023df..df5d234 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -107,12 +107,13 @@
     public static final String[] EMPTY_STRING_ARRAY = new String[0];
     public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
 
-    public static final boolean ATLEAST_R = BuildCompat.isAtLeastR();
+    public static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
 
     public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
-    public static final boolean ATLEAST_P =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
+    public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
+
+    public static final boolean ATLEAST_S = BuildCompat.isAtLeastS();
 
     /**
      * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
@@ -404,6 +405,11 @@
         return (size / densityRatio);
     }
 
+    /** Converts a dp value to pixels for the current device. */
+    public static int dpToPx(float dp) {
+        return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
+    }
+
     public static int pxFromSp(float size, DisplayMetrics metrics) {
         return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                 size, metrics));
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 777ea3c..65eba20 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3154,7 +3154,7 @@
         ItemOperator op = (info, v) -> {
             if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
                     && updates.contains(info)) {
-                ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
+                ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */);
             } else if (v instanceof PendingAppWidgetHostView
                     && info instanceof LauncherAppWidgetInfo
                     && updates.contains(info)) {
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 3ae0a18..00bdb70 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -24,7 +24,6 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -145,10 +144,17 @@
         });
     }
 
-    public void updatePromiseAppProgress(PromiseAppInfo app) {
+    /**
+     * Sets the AppInfo's associated icon's progress bar.
+     *
+     * If this app is installed and supports incremental downloads, the progress bar will be updated
+     * the app's total download progress. Otherwise, the progress bar will be updated to the app's
+     * installation progress.
+     */
+    public void updateProgressBar(AppInfo app) {
         updateAllIcons((child) -> {
             if (child.getTag() == app) {
-                child.applyProgressLevel(app.level);
+                child.applyProgressLevel(app.getProgressLevel());
             }
         });
     }
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 0005db8..d8ef18e 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
 
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
@@ -32,7 +31,6 @@
 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;
 
 /**
@@ -147,39 +145,6 @@
         new DiscoveryBounce(launcher, 0).show();
     }
 
-    public static void showForOverviewIfNeeded(Launcher launcher,
-                                               PagedOrientationHandler orientationHandler) {
-        showForOverviewIfNeeded(launcher, true, orientationHandler);
-    }
-
-    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) {
-            return;
-        }
-
-        if (withDelay) {
-            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.
-            return;
-        }
-        onboardingPrefs.incrementEventCount(OnboardingPrefs.SHELF_BOUNCE_COUNT);
-
-        new DiscoveryBounce(launcher, (1 - OVERVIEW.getVerticalProgress(launcher)))
-                .show();
-    }
-
     /**
      * A wrapper around {@link AllAppsTransitionController} allowing a fixed shift in the value.
      */
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index edaf51d..f6d1651 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -29,6 +29,8 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 
+import com.android.launcher3.Utilities;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -133,15 +135,20 @@
 
     /**
      * Starts playing the animation with the provided velocity optionally playing any
-     * physics based animations
+     * physics based animations.
+     * @param goingToEnd Whether we are going to the end (progress = 1) or not (progress = 0).
+     * @param velocityPxPerMs The velocity at which to start the animation, in pixels / millisecond.
+     * @param endDistance The distance (pixels) that the animation will travel from progress 0 to 1.
+     * @param animationDuration The duration of the non-physics based animation.
      */
     public void startWithVelocity(Context context, boolean goingToEnd,
-            float velocity, float scale, long animationDuration) {
-        float scaleInverse = 1 / Math.abs(scale);
-        float scaledVelocity = velocity * scaleInverse;
+            float velocityPxPerMs, float endDistance, long animationDuration) {
+        float distanceInverse = 1 / Math.abs(endDistance);
+        float velocityProgressPerMs = velocityPxPerMs * distanceInverse;
 
+        float oneFrameProgress = velocityProgressPerMs * getSingleFrameMs(context);
         float nextFrameProgress = boundToRange(getProgressFraction()
-                + scaledVelocity * getSingleFrameMs(context), 0f, 1f);
+                + oneFrameProgress, 0f, 1f);
 
         // Update setters for spring
         int springFlag = goingToEnd
@@ -154,8 +161,8 @@
                 SpringAnimationBuilder s = new SpringAnimationBuilder(context)
                         .setStartValue(mCurrentFraction)
                         .setEndValue(goingToEnd ? 1 : 0)
-                        .setStartVelocity(scaledVelocity)
-                        .setMinimumVisibleChange(scaleInverse)
+                        .setStartVelocity(velocityProgressPerMs)
+                        .setMinimumVisibleChange(distanceInverse)
                         .setDampingRatio(h.springProperty.mDampingRatio)
                         .setStiffness(h.springProperty.mStiffness)
                         .computeParams();
@@ -164,8 +171,18 @@
                 springDuration = Math.max(expectedDurationL, springDuration);
 
                 float expectedDuration = expectedDurationL;
-                h.mapper = (progress, globalEndProgress) ->
-                        mAnimationPlayer.getCurrentPlayTime() / expectedDuration;
+                h.mapper = (progress, globalEndProgress) -> {
+                    if (expectedDuration <= 0 || oneFrameProgress >= 1) {
+                        return 1;
+                    } else {
+                        // Start from one frame ahead of the current position.
+                        return Utilities.mapToRange(
+                                mAnimationPlayer.getCurrentPlayTime() / expectedDuration,
+                                0, 1,
+                                Math.abs(oneFrameProgress), 1,
+                                LINEAR);
+                    }
+                };
                 h.anim.setInterpolator(s::getInterpolatedValue);
             }
         }
@@ -174,7 +191,7 @@
 
         if (springDuration <= animationDuration) {
             mAnimationPlayer.setDuration(animationDuration);
-            mAnimationPlayer.setInterpolator(scrollInterpolatorForVelocity(velocity));
+            mAnimationPlayer.setInterpolator(scrollInterpolatorForVelocity(velocityPxPerMs));
         } else {
             // Since spring requires more time to run, we let the other animations play with
             // current time and interpolation and by clamping the duration.
@@ -182,7 +199,7 @@
 
             float cutOff = animationDuration / (float) springDuration;
             mAnimationPlayer.setInterpolator(
-                    clampToProgress(scrollInterpolatorForVelocity(velocity), 0, cutOff));
+                    clampToProgress(scrollInterpolatorForVelocity(velocityPxPerMs), 0, cutOff));
         }
         mAnimationPlayer.start();
     }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 839f0f4..68641d9 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -149,6 +149,10 @@
     public static final BooleanFlag ENABLE_OVERVIEW_SHARE = getDebugFlag(
             "ENABLE_OVERVIEW_SHARE", false, "Show Share button in Overview Actions");
 
+    public static final BooleanFlag ENABLE_OVERVIEW_SHARING_TO_PEOPLE = getDebugFlag(
+            "ENABLE_OVERVIEW_SHARING_TO_PEOPLE", false,
+            "Show indicators for content on Overview to share with top people. ");
+
     public static final BooleanFlag ENABLE_OVERVIEW_CONTENT_PUSH = getDebugFlag(
             "ENABLE_OVERVIEW_CONTENT_PUSH", false, "Show Content Push button in Overview Actions");
 
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 7f8a15c..9ae7faf 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -39,6 +39,7 @@
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
 
@@ -394,9 +395,10 @@
     }
 
     private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
-        if (item.hasPromiseIconUi()) {
+        if (item.hasPromiseIconUi() || (item.runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
             PreloadIconDrawable drawable = newPendingIcon(mContext, item);
-            drawable.setLevel(item.getInstallProgress());
+            drawable.setLevel(item.getProgressLevel());
             p.drawable = drawable;
         } else {
             p.drawable = newIcon(mContext, item);
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index effb3a4..efc1201 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -629,11 +629,13 @@
         private WorkspaceResult(BgDataModel dataModel,
                 WidgetsModel widgetsModel,
                 Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
-            mWorkspaceItems = dataModel.workspaceItems;
-            mAppWidgets = dataModel.appWidgets;
-            mHotseatPredictions = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
-            mWidgetsModel = widgetsModel;
-            mWidgetProvidersMap = widgetProviderInfoMap;
+            synchronized (dataModel) {
+                mWorkspaceItems = dataModel.workspaceItems;
+                mAppWidgets = dataModel.appWidgets;
+                mHotseatPredictions = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
+                mWidgetsModel = widgetsModel;
+                mWidgetProvidersMap = widgetProviderInfoMap;
+            }
         }
     }
 }
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index e85b056..9971990 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -117,6 +117,8 @@
         mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
 
         setInternalProgress(0);
+
+        setIsDisabled(!info.isAppStartable());
     }
 
     @Override
@@ -266,14 +268,12 @@
             mIconScale = SMALL_SCALE;
             mScaledTrackPath.reset();
             mTrackAlpha = MAX_PAINT_ALPHA;
-            setIsDisabled(true);
         }
 
         if (progress < 1 && progress > 0) {
             mPathMeasure.getSegment(0, progress * mTrackLength, mScaledProgressPath, true);
             mIconScale = SMALL_SCALE;
             mTrackAlpha = MAX_PAINT_ALPHA;
-            setIsDisabled(true);
         } else if (progress >= 1) {
             setIsDisabled(mItem.isDisabled());
             mScaledTrackPath.set(mScaledProgressPath);
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index c236fa6..56dbbd3 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -132,7 +133,9 @@
                             continue;
                         }
                     } else {
-                        workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
+                        workspaceInfo.setProgressLevel(
+                                (int) (sessionInfo.getProgress() * 100),
+                                PackageInstallInfo.STATUS_INSTALLING);
                     }
 
                     if (hasActivity) {
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 2695e66..8d5cf74 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -21,11 +21,11 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.os.LocaleList;
-import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
 
@@ -37,7 +37,6 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -150,7 +149,7 @@
                 .getApplicationInfo(installInfo.packageName, installInfo.user, 0);
         // only if not yet installed
         if (applicationInfo == null) {
-            PromiseAppInfo info = new PromiseAppInfo(installInfo);
+            AppInfo info = new AppInfo(installInfo);
             mIconCache.getTitleAndIcon(info, info.usingLowResIcon());
             info.sectionName = mIndex.computeSectionName(info.title);
 
@@ -159,24 +158,26 @@
         }
     }
 
-    public PromiseAppInfo updatePromiseInstallInfo(PackageInstallInfo installInfo) {
-        UserHandle user = Process.myUserHandle();
-        for (int i=0; i < data.size(); i++) {
+    /** Updates the given PackageInstallInfo's associated AppInfo's installation info. */
+    public List<AppInfo> updatePromiseInstallInfo(PackageInstallInfo installInfo) {
+        List<AppInfo> updatedAppInfos = new ArrayList<>();
+        UserHandle user = installInfo.user;
+        for (int i = data.size() - 1; i >= 0; i--) {
             final AppInfo appInfo = data.get(i);
             final ComponentName tgtComp = appInfo.getTargetComponent();
             if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName)
-                    && appInfo.user.equals(user)
-                    && appInfo instanceof PromiseAppInfo) {
-                final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
-                if (installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
-                    promiseAppInfo.level = installInfo.progress;
-                    return promiseAppInfo;
+                    && appInfo.user.equals(user)) {
+                if (installInfo.state == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING
+                            || installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
+                    appInfo.setProgressLevel(installInfo);
+
+                    updatedAppInfos.add(appInfo);
                 } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED) {
                     removeApp(i);
                 }
             }
         }
-        return null;
+        return updatedAppInfos;
     }
 
     private void removeApp(int index) {
@@ -268,8 +269,14 @@
                 if (applicationInfo == null) {
                     add(new AppInfo(context, info, user), info);
                 } else {
+                    Intent launchIntent = AppInfo.makeLaunchIntent(info);
+
                     mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
                     applicationInfo.sectionName = mIndex.computeSectionName(applicationInfo.title);
+                    applicationInfo.setProgressLevel(
+                            PackageManagerHelper.getLoadingProgress(info),
+                            PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
+                    applicationInfo.intent = launchIntent;
 
                     mDataChanged = true;
                 }
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 49b40ed..2d860a4 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -39,7 +39,6 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
@@ -376,6 +375,16 @@
     }
 
     /**
+     * Returns a list containing all workspace items including widgets.
+     */
+    public synchronized ArrayList<ItemInfo> getAllWorkspaceItems() {
+        ArrayList<ItemInfo> items = new ArrayList<>(workspaceItems.size() + appWidgets.size());
+        items.addAll(workspaceItems);
+        items.addAll(appWidgets);
+        return items;
+    }
+
+    /**
      * Calls the provided {@code op} for all workspaceItems in the in-memory model (both persisted
      * items and dynamic/predicted items for the provided {@code userHandle}.
      * Note the call is not synchronized over the model, that should be handled by the called.
@@ -449,7 +458,11 @@
         void preAddApps();
         void bindAppsAdded(IntArray newScreens,
                 ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
-        void bindPromiseAppProgressUpdated(PromiseAppInfo app);
+
+        /**
+         * Binds updated incremental download progress
+         */
+        void bindIncrementalDownloadProgressUpdated(AppInfo app);
         void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated);
         void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
         void bindRestoreItemsChange(HashSet<ItemInfo> updates);
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 532834e..19d9af9 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -35,11 +35,13 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
@@ -92,6 +94,9 @@
     private final int restoredIndex;
     private final int intentIndex;
 
+    @Nullable
+    private LauncherActivityInfo mActivityInfo;
+
     // Properties loaded per iteration
     public long serialNumber;
     public UserHandle user;
@@ -132,6 +137,8 @@
     public boolean moveToNext() {
         boolean result = super.moveToNext();
         if (result) {
+            mActivityInfo = null;
+
             // Load common properties.
             itemType = getInt(itemTypeIndex);
             container = getInt(containerIndex);
@@ -245,6 +252,10 @@
         return info;
     }
 
+    public LauncherActivityInfo getLauncherActivityInfo() {
+        return mActivityInfo;
+    }
+
     /**
      * Make an WorkspaceItemInfo object for a shortcut that is an application.
      */
@@ -264,25 +275,25 @@
         Intent newIntent = new Intent(Intent.ACTION_MAIN, null);
         newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
         newIntent.setComponent(componentName);
-        LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
+        mActivityInfo = mContext.getSystemService(LauncherApps.class)
                 .resolveActivity(newIntent, user);
-        if ((lai == null) && !allowMissingTarget) {
+        if ((mActivityInfo == null) && !allowMissingTarget) {
             Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
             return null;
         }
 
         final WorkspaceItemInfo info = new WorkspaceItemInfo();
-        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        info.itemType = Favorites.ITEM_TYPE_APPLICATION;
         info.user = user;
         info.intent = newIntent;
 
-        mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
+        mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
         if (mIconCache.isDefaultIcon(info.bitmap, user)) {
             loadIcon(info);
         }
 
-        if (lai != null) {
-            AppInfo.updateRuntimeFlagsForActivityTarget(info, lai);
+        if (mActivityInfo != null) {
+            AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo);
         }
 
         // from the db
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index b108788..f74c8b5 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -69,6 +69,7 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -166,12 +167,7 @@
 
     private void sendFirstScreenActiveInstallsBroadcast() {
         ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
-
-        ArrayList<ItemInfo> allItems = new ArrayList<>();
-        synchronized (mBgDataModel) {
-            allItems.addAll(mBgDataModel.workspaceItems);
-            allItems.addAll(mBgDataModel.appWidgets);
-        }
+        ArrayList<ItemInfo> allItems = mBgDataModel.getAllWorkspaceItems();
         // Screen set is never empty
         final int firstScreen = mBgDataModel.collectWorkspaceScreens().get(0);
 
@@ -596,11 +592,24 @@
                                 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
                                     tempPackageKey.update(targetPkg, c.user);
                                     SessionInfo si = installingPkgs.get(tempPackageKey);
-                                    if (si == null) {
-                                        info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
-                                    } else {
-                                        info.setInstallProgress((int) (si.getProgress() * 100));
-                                    }
+                                        LauncherActivityInfo activityInfo =
+                                                c.getLauncherActivityInfo();
+                                        if (si == null) {
+                                            info.runtimeStatusFlags &=
+                                                    ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+                                        } else if (activityInfo == null) {
+                                            int installProgress = (int) (si.getProgress() * 100);
+
+                                            info.setProgressLevel(
+                                                    installProgress,
+                                                    PackageInstallInfo.STATUS_INSTALLING);
+                                        } else {
+                                            info.setProgressLevel(
+                                                    PackageManagerHelper
+                                                            .getLoadingProgress(activityInfo),
+                                                    PackageInstallInfo
+                                                            .STATUS_INSTALLED_DOWNLOADING);
+                                        }
                                 }
 
                                 c.checkAndAddItem(info, mBgDataModel);
@@ -858,10 +867,12 @@
                     .call(contentResolver,
                             LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
                     .getIntArray(LauncherSettings.Settings.EXTRA_VALUE);
-            for (int folderId : deletedFolderIds) {
-                mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
-                mBgDataModel.folders.remove(folderId);
-                mBgDataModel.itemsIdMap.remove(folderId);
+            synchronized (mBgDataModel) {
+                for (int folderId : deletedFolderIds) {
+                    mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
+                    mBgDataModel.folders.remove(folderId);
+                    mBgDataModel.itemsIdMap.remove(folderId);
+                }
             }
 
             // Remove any ghost widgets
diff --git a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
new file mode 100644
index 0000000..e3e8769
--- /dev/null
+++ b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
@@ -0,0 +1,79 @@
+/*
+ * 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.launcher3.model;
+
+import android.content.ComponentName;
+import android.os.UserHandle;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.PackageInstallInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles updates due to incremental download progress updates.
+ */
+public class PackageIncrementalDownloadUpdatedTask extends BaseModelUpdateTask {
+
+    private final UserHandle mUser;
+    private final int mProgress;
+    private final String mPackageName;
+
+    public PackageIncrementalDownloadUpdatedTask(
+            String packageName, UserHandle user, float progress) {
+        mUser = user;
+        mProgress = 1 - progress > 0.001 ? (int) (100 * progress) : 100;
+        mPackageName = packageName;
+    }
+
+    @Override
+    public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+        PackageInstallInfo downloadInfo = new PackageInstallInfo(
+                mPackageName,
+                PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING,
+                mProgress,
+                mUser);
+
+        synchronized (appsList) {
+            List<AppInfo> updatedAppInfos = appsList.updatePromiseInstallInfo(downloadInfo);
+            if (!updatedAppInfos.isEmpty()) {
+                for (AppInfo appInfo : updatedAppInfos) {
+                    appInfo.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+                    scheduleCallbackTask(
+                            c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
+                }
+            }
+            bindApplicationsIfNeeded();
+        }
+
+        final ArrayList<WorkspaceItemInfo> updatedWorkspaceItems = new ArrayList<>();
+        synchronized (dataModel) {
+            dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+                ComponentName cn = si.getTargetComponent();
+                if ((cn != null) && cn.getPackageName().equals(mPackageName)) {
+                    si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+                    si.setProgressLevel(downloadInfo);
+                    updatedWorkspaceItems.add(si);
+                }
+            });
+        }
+        bindUpdatedWorkspaceItems(updatedWorkspaceItems);
+    }
+}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 8369c48..8215edd 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -20,14 +20,15 @@
 import android.content.pm.PackageManager;
 
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.InstantAppResolver;
 
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Handles changes due to a sessions updates for a currently installing app.
@@ -59,9 +60,11 @@
         }
 
         synchronized (apps) {
-            PromiseAppInfo updated = apps.updatePromiseInstallInfo(mInstallInfo);
-            if (updated != null) {
-                scheduleCallbackTask(c -> c.bindPromiseAppProgressUpdated(updated));
+            List<AppInfo> updatedAppInfos = apps.updatePromiseInstallInfo(mInstallInfo);
+            if (!updatedAppInfos.isEmpty()) {
+                for (AppInfo appInfo : updatedAppInfos) {
+                    scheduleCallbackTask(c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
+                }
             }
             bindApplicationsIfNeeded();
         }
@@ -71,11 +74,13 @@
             dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
                 ComponentName cn = si.getTargetComponent();
                 if (si.hasPromiseIconUi() && (cn != null)
-                        && mInstallInfo.packageName.equals(cn.getPackageName())) {
-                    si.setInstallProgress(mInstallInfo.progress);
+                        && cn.getPackageName().equals(mInstallInfo.packageName)) {
+                    int installProgress = mInstallInfo.progress;
+
+                    si.setProgressLevel(installProgress, PackageInstallInfo.STATUS_INSTALLING);
                     if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
                         // Mark this info as broken.
-                        si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                        si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
                     }
                     updates.add(si);
                 }
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index aee1f2a..39247c2 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -28,10 +28,13 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
 
@@ -104,13 +107,37 @@
         this.intent = intent;
     }
 
+    public AppInfo(@NonNull PackageInstallInfo installInfo) {
+        componentName = installInfo.componentName;
+        intent = new Intent(Intent.ACTION_MAIN)
+            .addCategory(Intent.CATEGORY_LAUNCHER)
+            .setComponent(componentName)
+            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        user = installInfo.user;
+    }
+
     @Override
     protected String dumpProperties() {
         return super.dumpProperties() + " componentName=" + componentName;
     }
 
     public WorkspaceItemInfo makeWorkspaceItem() {
-        return new WorkspaceItemInfo(this);
+        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(this);
+
+        if ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+            // We need to update the component name when the apk is installed
+            workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+            // Since the user is manually placing it on homescreen, it should not be auto-removed
+            // later
+            workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
+            workspaceItemInfo.status |= FLAG_INSTALL_SESSION_ACTIVE;
+        }
+        if ((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
+            workspaceItemInfo.runtimeStatusFlags |= FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+        }
+
+        return workspaceItemInfo;
     }
 
     public ComponentKey toComponentKey() {
@@ -129,6 +156,12 @@
                         | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
     }
 
+    @Nullable
+    @Override
+    public ComponentName getTargetComponent() {
+        return componentName;
+    }
+
     public static void updateRuntimeFlagsForActivityTarget(
             ItemInfoWithIcon info, LauncherActivityInfo lai) {
         ApplicationInfo appInfo = lai.getApplicationInfo();
@@ -143,6 +176,11 @@
             // The icon for a non-primary user is badged, hence it's not exactly an adaptive icon.
             info.runtimeStatusFlags |= FLAG_ADAPTIVE_ICON;
         }
+
+        // Sets the progress level, installation and incremental download flags.
+        info.setProgressLevel(
+                PackageManagerHelper.getLoadingProgress(lai),
+                PackageInstallInfo.STATUS_INSTALLED);
     }
 
     @Override
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index d95f94f..b8a71d3 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -16,7 +16,15 @@
 
 package com.android.launcher3.model.data;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.util.PackageManagerHelper;
 
 /**
  * Represents an ItemInfo which also holds an icon.
@@ -88,11 +96,34 @@
     public static final int FLAG_ICON_BADGED = 1 << 9;
 
     /**
+     * The icon is being installed. If {@link WorkspaceItemInfo#FLAG_RESTORED_ICON} or
+     * {@link WorkspaceItemInfo#FLAG_AUTOINSTALL_ICON} is set, then the icon is either being
+     * installed or is in a broken state.
+     */
+    public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 10;
+
+    /**
+     * This icon is still being downloaded.
+     */
+    public static final int FLAG_INCREMENTAL_DOWNLOAD_ACTIVE = 1 << 11;
+
+    public static final int FLAG_SHOW_DOWNLOAD_PROGRESS_MASK = FLAG_INSTALL_SESSION_ACTIVE
+            | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+
+    /**
      * Status associated with the system state of the underlying item. This is calculated every
      * time a new info is created and not persisted on the disk.
      */
     public int runtimeStatusFlags = 0;
 
+    /**
+     * The download progress of the package that this shortcut represents. For legacy apps, this
+     * will always be the installation progress. For apps that support incremental downloads, this
+     * will only match be the installation progress until the app is installed, then this will the
+     * total download progress.
+     */
+    private int mProgressLevel = 100;
+
     protected ItemInfoWithIcon() { }
 
     protected ItemInfoWithIcon(ItemInfoWithIcon info) {
@@ -114,6 +145,72 @@
     }
 
     /**
+     * Returns whether the app this shortcut represents is able to be started. For legacy apps,
+     * this returns whether it is fully installed. For apps that support incremental downloads,
+     * this returns whether the app is either fully downloaded or has installed and is downloading
+     * incrementally.
+     */
+    public boolean isAppStartable() {
+        return ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) == 0)
+                && (((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0)
+                    || mProgressLevel == 100);
+    }
+
+    /**
+     * Returns the download progress for the app this shortcut represents. If this app is not yet
+     * installed or does not support incremental downloads, this will return the installation
+     * progress.
+     */
+    public int getProgressLevel() {
+        if ((runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+            return mProgressLevel;
+        }
+        return 100;
+    }
+
+    /**
+     * Sets the download progress for the app this shortcut represents. If this app is not yet
+     * installed or does not support incremental downloads, this will set
+     * {@code FLAG_INSTALL_SESSION_ACTIVE}. If this app is downloading incrementally, this will
+     * set {@code FLAG_INCREMENTAL_DOWNLOAD_ACTIVE}. Otherwise, this will remove both flags.
+     */
+    public void setProgressLevel(PackageInstallInfo installInfo) {
+        setProgressLevel(installInfo.progress, installInfo.state);
+    }
+
+    /**
+     * Sets the download progress for the app this shortcut represents.
+     */
+    public void setProgressLevel(int progress, int status) {
+        if (status == PackageInstallInfo.STATUS_INSTALLING) {
+            mProgressLevel = progress;
+            runtimeStatusFlags = progress < 100
+                    ? runtimeStatusFlags | FLAG_INSTALL_SESSION_ACTIVE
+                    : runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+        } else if (status == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING) {
+            mProgressLevel = progress;
+            runtimeStatusFlags = runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+            runtimeStatusFlags = progress < 100
+                    ? runtimeStatusFlags | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE
+                    : runtimeStatusFlags & ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+        } else {
+            mProgressLevel = status == PackageInstallInfo.STATUS_INSTALLED ? 100 : 0;
+            runtimeStatusFlags = runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+            runtimeStatusFlags = runtimeStatusFlags & ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+        }
+    }
+
+    /** Creates an intent to that launches the app store at this app's page. */
+    @Nullable
+    public Intent getMarketIntent(Context context) {
+        ComponentName componentName = getTargetComponent();
+
+        return componentName != null
+                ? new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName())
+                : null;
+    }
+
+    /**
      * @return a copy of this
      */
     public abstract ItemInfoWithIcon clone();
diff --git a/src/com/android/launcher3/model/data/PromiseAppInfo.java b/src/com/android/launcher3/model/data/PromiseAppInfo.java
deleted file mode 100644
index b6231ed..0000000
--- a/src/com/android/launcher3/model/data/PromiseAppInfo.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.model.data;
-
-import android.content.Context;
-import android.content.Intent;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.pm.PackageInstallInfo;
-import com.android.launcher3.util.PackageManagerHelper;
-
-public class PromiseAppInfo extends AppInfo {
-
-    public int level = 0;
-
-    public PromiseAppInfo(@NonNull PackageInstallInfo installInfo) {
-        componentName = installInfo.componentName;
-        intent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_LAUNCHER)
-                .setComponent(componentName)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-    }
-
-    @Override
-    public WorkspaceItemInfo makeWorkspaceItem() {
-        WorkspaceItemInfo shortcut = new WorkspaceItemInfo(this);
-        shortcut.setInstallProgress(level);
-        // We need to update the component name when the apk is installed
-        shortcut.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
-        // Since the user is manually placing it on homescreen, it should not be auto-removed later
-        shortcut.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
-        return shortcut;
-    }
-
-    public Intent getMarketIntent(Context context) {
-        return new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName());
-    }
-}
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 1e1d093..690e904 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -58,20 +58,14 @@
     public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
 
     /**
-     * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
-     * is set, then the icon is either being installed or is in a broken state.
-     */
-    public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2;
-
-    /**
      * Indicates that the widget restore has started.
      */
-    public static final int FLAG_RESTORE_STARTED = 1 << 3;
+    public static final int FLAG_RESTORE_STARTED = 1 << 2;
 
     /**
      * Web UI supported.
      */
-    public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4;
+    public static final int FLAG_SUPPORTS_WEB_UI = 1 << 3;
 
     /**
      * The intent used to start the application.
@@ -98,11 +92,6 @@
      */
     @NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
 
-    /**
-     * The installation progress [0-100] of the package that this shortcut represents.
-     */
-    private int mInstallProgress;
-
 
     public WorkspaceItemInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -114,7 +103,6 @@
         intent = new Intent(info.intent);
         iconResource = info.iconResource;
         status = info.status;
-        mInstallProgress = info.mInstallProgress;
         personKeys = info.personKeys.clone();
     }
 
@@ -168,15 +156,6 @@
         return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
     }
 
-    public int getInstallProgress() {
-        return mInstallProgress;
-    }
-
-    public void setInstallProgress(int progress) {
-        mInstallProgress = progress;
-        status |= FLAG_INSTALL_SESSION_ACTIVE;
-    }
-
     public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) {
         // {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
         intent = ShortcutKey.makeIntent(shortcutInfo);
diff --git a/src/com/android/launcher3/pm/PackageInstallInfo.java b/src/com/android/launcher3/pm/PackageInstallInfo.java
index 7997d16..fad904f 100644
--- a/src/com/android/launcher3/pm/PackageInstallInfo.java
+++ b/src/com/android/launcher3/pm/PackageInstallInfo.java
@@ -25,7 +25,8 @@
 
     public static final int STATUS_INSTALLED = 0;
     public static final int STATUS_INSTALLING = 1;
-    public static final int STATUS_FAILED = 2;
+    public static final int STATUS_INSTALLED_DOWNLOADING = 2;
+    public static final int STATUS_FAILED = 3;
 
     public final ComponentName componentName;
     public final String packageName;
@@ -56,5 +57,4 @@
     public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
         return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
     }
-
 }
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index b496608..0266345 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -35,8 +35,8 @@
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
@@ -215,8 +215,8 @@
             ArrayList<ItemInfo> addAnimated) { }
 
     @Override
-    public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
-        mAppsView.getAppsStore().updatePromiseAppProgress(app);
+    public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
+        mAppsView.getAppsStore().updateProgressBar(app);
     }
 
     @Override
@@ -315,9 +315,11 @@
         if (tag instanceof ItemInfo) {
             ItemInfo item = (ItemInfo) tag;
             Intent intent;
-            if (item instanceof PromiseAppInfo) {
-                PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
-                intent = promiseAppInfo.getMarketIntent(this);
+            if (item instanceof ItemInfoWithIcon
+                    && (((ItemInfoWithIcon) item).runtimeStatusFlags
+                        & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+                ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
+                intent = appInfo.getMarketIntent(this);
             } else {
                 intent = item.getIntent();
             }
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 9b9cb0a..4158735 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -49,8 +49,8 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.RemoteActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
@@ -231,8 +231,12 @@
                     ? shortcut.getIntent().getComponent().getPackageName()
                     : shortcut.getIntent().getPackage();
             if (!TextUtils.isEmpty(packageName)) {
-                onClickPendingAppItem(v, launcher, packageName,
-                        shortcut.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE));
+                onClickPendingAppItem(
+                        v,
+                        launcher,
+                        packageName,
+                        (shortcut.runtimeStatusFlags
+                                & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0);
                 return;
             }
         }
@@ -266,9 +270,12 @@
         TestLogging.recordEvent(
                 TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
         Intent intent;
-        if (item instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
-            intent = promiseAppInfo.getMarketIntent(launcher);
+        if (item instanceof ItemInfoWithIcon
+                && (((ItemInfoWithIcon) item).runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+            ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
+            intent = new PackageManagerHelper(launcher)
+                    .getMarketIntent(appInfo.getTargetComponent().getPackageName());
         } else {
             intent = item.getIntent();
         }
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 1b33197..539768e 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -33,7 +33,6 @@
 public class OnboardingPrefs<T extends Launcher> {
 
     public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
-    public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
     public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
     public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
     public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
@@ -44,7 +43,6 @@
      */
     @StringDef(value = {
             HOME_BOUNCE_SEEN,
-            SHELF_BOUNCE_SEEN,
             HOTSEAT_LONGPRESS_TIP_SEEN
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -55,7 +53,6 @@
      */
     @StringDef(value = {
             HOME_BOUNCE_COUNT,
-            SHELF_BOUNCE_COUNT,
             HOTSEAT_DISCOVERY_TIP_COUNT
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -65,7 +62,6 @@
     static {
         Map<String, Integer> maxCounts = new ArrayMap<>(4);
         maxCounts.put(HOME_BOUNCE_COUNT, 3);
-        maxCounts.put(SHELF_BOUNCE_COUNT, 3);
         maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
         MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
     }
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 523545a..7b26427 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -45,10 +45,11 @@
 
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 import java.net.URISyntaxException;
@@ -200,9 +201,12 @@
      * Starts the details activity for {@code info}
      */
     public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
-        if (info instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
-            mContext.startActivity(promiseAppInfo.getMarketIntent(mContext));
+        if (info instanceof ItemInfoWithIcon
+                && (((ItemInfoWithIcon) info).runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+            ItemInfoWithIcon appInfo = (ItemInfoWithIcon) info;
+            mContext.startActivity(new PackageManagerHelper(mContext)
+                    .getMarketIntent(appInfo.getTargetComponent().getPackageName()));
             return;
         }
         ComponentName componentName = null;
@@ -319,4 +323,12 @@
         }
         return false;
     }
+
+    /** Returns the incremental download progress for the given shortcut's app. */
+    public static int getLoadingProgress(LauncherActivityInfo info) {
+        if (Utilities.ATLEAST_S) {
+            return (int) (100 * info.getLoadingProgress());
+        }
+        return 100;
+    }
 }
diff --git a/src/com/android/launcher3/util/SecureSettingsObserver.java b/src/com/android/launcher3/util/SecureSettingsObserver.java
index 4b22429..9fe72ad 100644
--- a/src/com/android/launcher3/util/SecureSettingsObserver.java
+++ b/src/com/android/launcher3/util/SecureSettingsObserver.java
@@ -88,7 +88,7 @@
     public static SecureSettingsObserver newOneHandedSettingsObserver(Context context,
             OnChangeListener listener) {
         return new SecureSettingsObserver(
-                context.getContentResolver(), listener, ONE_HANDED_ENABLED, 1);
+                context.getContentResolver(), listener, ONE_HANDED_ENABLED, 0);
     }
 
     /**
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index a39f215..1648bdd 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -299,7 +299,9 @@
                     launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
                             isOptionsPopupVisible(launcher)));
 
-            menu.getMenuItem(1).launch(getAppPackageName());
+            final AppIconMenuItem menuItem = menu.getMenuItem(1);
+            assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText());
+            menuItem.launch(getAppPackageName());
         } finally {
             allApps.unfreeze();
         }
@@ -342,6 +344,7 @@
                     openMenu().
                     getMenuItem(0);
             final String shortcutName = menuItem.getText();
+            assertEquals("Wrong menu item", "Shortcut 3", shortcutName);
 
             menuItem.dragToWorkspace(false, false);
             mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName());
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 3c89cfd..e2a442d 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -595,11 +595,7 @@
                     return waitForLauncherObject(APPS_RES_ID);
                 }
                 case OVERVIEW: {
-                    if (hasAllAppsInOverview()) {
-                        waitForLauncherObject(APPS_RES_ID);
-                    } else {
-                        waitUntilLauncherObjectGone(APPS_RES_ID);
-                    }
+                    waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
 
@@ -1297,19 +1293,6 @@
         getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
     }
 
-    public boolean hasAllAppsInOverview() {
-        // Vertical bar layouts don't contain all apps
-        if (!mDevice.isNaturalOrientation()) {
-            return false;
-        }
-        // Portrait two button (quickstep) always has all apps.
-        if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
-            return true;
-        }
-        // ...otherwise there are overview actions, which hide all apps
-        return false;
-    }
-
     boolean overviewShareEnabled() {
         return getTestInfo(TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED).getBoolean(
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);