Merge "Fix touch focus not updated correctly after launching overview with live tile." into udc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index bf84820..55301ff 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -135,7 +135,7 @@
   }
 }
 
-// Next value 44
+// Next value 45
 enum Attribute {
   option allow_alias = true;
 
@@ -173,6 +173,7 @@
   ALL_APPS_SEARCH_RESULT_SLICE = 19;
   ALL_APPS_SEARCH_RESULT_WIDGETS = 20;
   ALL_APPS_SEARCH_RESULT_PLAY = 21;
+  ALL_APPS_SEARCH_RESULT_PLAY_GMS = 44;
   ALL_APPS_SEARCH_RESULT_FALLBACK = 22;
   ALL_APPS_SEARCH_RESULT_SUGGEST = 22 [deprecated = true];
   ALL_APPS_SEARCH_RESULT_ASSISTANT = 23;
diff --git a/protos/launcher_trace.proto b/protos/launcher_trace.proto
index 65fcfe5..e5a86a0 100644
--- a/protos/launcher_trace.proto
+++ b/protos/launcher_trace.proto
@@ -63,5 +63,6 @@
         RECENTS = 2;
         NEW_TASK = 3;
         LAST_TASK = 4;
+        ALL_APPS = 5;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 18fe30d..5d4e19d 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -196,14 +196,13 @@
                         finish();
                     }
                 });
-                mAnimator.start();
-
                 if (skipFirstFrame) {
                     // Because t=0 has the app icon in its original spot, we can skip the
                     // first frame and have the same movement one frame earlier.
                     mAnimator.setCurrentPlayTime(
                             Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));
                 }
+                mAnimator.start();
             }
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
index 8dbc51a..83a3343 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
@@ -38,14 +38,21 @@
         view.post {
             val popupView = createAndPopulate(view, context)
             popupView.requestFocus()
-            popupView.onCloseCallback = {
-                context.onPopupVisibilityChanged(false)
-                if (launcherPrefs.get(TASKBAR_PINNING)) {
-                    animateTransientToPersistentTaskBar()
-                } else {
-                    animatePersistentToTransientTaskbar()
+
+            popupView.onCloseCallback =
+                callback@{ didPreferenceChange ->
+                    context.dragLayer.post { context.onPopupVisibilityChanged(false) }
+
+                    if (!didPreferenceChange) {
+                        return@callback
+                    }
+
+                    if (launcherPrefs.get(TASKBAR_PINNING)) {
+                        animateTransientToPersistentTaskbar()
+                    } else {
+                        animatePersistentToTransientTaskbar()
+                    }
                 }
-            }
             popupView.changePreference = {
                 launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING))
             }
@@ -55,7 +62,7 @@
     }
 
     // TODO(b/265436799): provide animation/transition from transient taskbar to persistent one
-    private fun animateTransientToPersistentTaskBar() {}
+    private fun animateTransientToPersistentTaskbar() {}
 
     // TODO(b/265436799): provide animation/transition from persistent taskbar to transient one
     private fun animatePersistentToTransientTaskbar() {}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index 2000d98..e07f0c0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -17,6 +17,7 @@
 
 import android.annotation.SuppressLint
 import android.content.Context
+import android.content.Intent
 import android.graphics.Rect
 import android.util.AttributeSet
 import android.view.Gravity
@@ -42,6 +43,9 @@
     companion object {
         private const val TAG = "TaskbarDividerPopupView"
         private const val DIVIDER_POPUP_CLOSING_DELAY = 500L
+        private const val SETTINGS_PACKAGE_NAME = "com.android.settings"
+        private const val CHANGE_NAVIGATION_MODE_ACTION =
+            "com.android.settings.NAVIGATION_MODE_SETTINGS"
 
         @JvmStatic
         fun createAndPopulate(
@@ -71,7 +75,7 @@
     private var didPreferenceChange = false
 
     /** Callback invoked when the pinning popup view is closing. */
-    var onCloseCallback: () -> Unit = {}
+    var onCloseCallback: (preferenceChanged: Boolean) -> Unit = {}
 
     /**
      * Callback invoked when the user preference changes in popup view. Preference change will be
@@ -98,12 +102,21 @@
         super.onFinishInflate()
         val taskbarSwitchOption = findViewById<LinearLayout>(R.id.taskbar_switch_option)
         val alwaysShowTaskbarSwitch = findViewById<Switch>(R.id.taskbar_pinning_switch)
+        val navigationModeChangeOption =
+            findViewById<LinearLayout>(R.id.navigation_mode_switch_option)
         alwaysShowTaskbarSwitch.isChecked = alwaysShowTaskbarOn
         taskbarSwitchOption.setOnClickListener {
             alwaysShowTaskbarSwitch.isClickable = true
             alwaysShowTaskbarSwitch.isChecked = !alwaysShowTaskbarOn
             onClickAlwaysShowTaskbarSwitchOption()
         }
+        navigationModeChangeOption.setOnClickListener {
+            context.startActivity(
+                Intent(CHANGE_NAVIGATION_MODE_ACTION)
+                    .setPackage(SETTINGS_PACKAGE_NAME)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            )
+        }
     }
 
     /** Orient object as usual and then center object horizontally. */
@@ -156,9 +169,7 @@
     }
 
     override fun closeComplete() {
-        if (didPreferenceChange) {
-            onCloseCallback()
-        }
+        onCloseCallback(didPreferenceChange)
         super.closeComplete()
     }
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index d64347f..b7a29e0 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -39,6 +39,7 @@
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
+import static com.android.quickstep.GestureState.GestureEndTarget.ALL_APPS;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
 import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
 import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -161,6 +162,9 @@
 
     private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
 
+    /** Shift distance to transition to All Apps if ENABLE_ALL_APPS_FROM_OVERVIEW. */
+    public static final float ALL_APPS_SHIFT_THRESHOLD = 2f;
+
     protected final BaseActivityInterface<S, T> mActivityInterface;
     protected final InputConsumerProxy mInputConsumerProxy;
     protected final ActivityInitListener mActivityInitListener;
@@ -247,6 +251,8 @@
             getNextStateFlag("STATE_CURRENT_TASK_FINISHED");
     private static final int STATE_FINISH_WITH_NO_END =
             getNextStateFlag("STATE_FINISH_WITH_NO_END");
+    private static final int STATE_SETTLED_ON_ALL_APPS =
+            getNextStateFlag("STATE_SETTLED_ON_ALL_APPS");
 
     private static final int LAUNCHER_UI_STATES =
             STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED |
@@ -299,6 +305,7 @@
     private boolean mGestureStarted;
     private boolean mLogDirectionUpOrLeft = true;
     private boolean mIsLikelyToStartNewTask;
+    private boolean mIsInAllAppsRegion;
 
     private final long mTouchTimeMs;
     private long mLauncherFrameDrawnTime;
@@ -432,6 +439,9 @@
                 this::finishCurrentTransitionToHome);
         mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
                 this::reset);
+        mStateCallback.runOnceAtState(STATE_SETTLED_ON_ALL_APPS | STATE_SCREENSHOT_CAPTURED
+                        | STATE_GESTURE_COMPLETED,
+                this::finishCurrentTransitionToAllApps);
 
         mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
                         | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS
@@ -681,7 +691,9 @@
                 maybeUpdateRecentsAttachedState(true/* animate */, true/* moveRunningTask */);
                 Optional.ofNullable(mActivityInterface.getTaskbarController())
                         .ifPresent(TaskbarUIController::startTranslationSpring);
-                performHapticFeedback();
+                if (!mIsInAllAppsRegion) {
+                    performHapticFeedback();
+                }
             }
 
             @Override
@@ -695,7 +707,7 @@
         maybeUpdateRecentsAttachedState(true /* animate */);
     }
 
-    private void maybeUpdateRecentsAttachedState(boolean animate) {
+    protected void maybeUpdateRecentsAttachedState(boolean animate) {
         maybeUpdateRecentsAttachedState(animate, false /* moveRunningTask */);
     }
 
@@ -716,7 +728,9 @@
                 ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
                 : null;
         final boolean recentsAttachedToAppWindow;
-        if (mGestureState.getEndTarget() != null) {
+        if (mIsInAllAppsRegion) {
+            recentsAttachedToAppWindow = false;
+        } else if (mGestureState.getEndTarget() != null) {
             recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow;
         } else if (mContinuingLastGesture
                 && mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
@@ -772,6 +786,26 @@
         }
     }
 
+    /**
+     * Update whether user is currently dragging in a region that will trigger all apps.
+     */
+    private void setIsInAllAppsRegion(boolean isInAllAppsRegion) {
+        if (mIsInAllAppsRegion == isInAllAppsRegion
+                || !mActivityInterface.allowAllAppsFromOverview()) {
+            return;
+        }
+        mIsInAllAppsRegion = isInAllAppsRegion;
+
+        // Newly entering or exiting the zone - do haptic and animate recent tasks.
+        VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
+        maybeUpdateRecentsAttachedState(true);
+
+        // Draw active task below Launcher so that All Apps can appear over it.
+        runActionOnRemoteHandles(remoteTargetHandle ->
+                remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(isInAllAppsRegion));
+    }
+
+
     private void buildAnimationController() {
         if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
             return;
@@ -792,10 +826,15 @@
     @Override
     public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
         WindowInsets result = view.onApplyWindowInsets(windowInsets);
+        // Don't rebuild animation when we are animating the IME, because it will cause a loop
+        // where the insets change -> animation changes (updating ime) -> insets change -> ...
+        if (windowInsets.isVisible(WindowInsets.Type.ime())) {
+            return result;
+        }
         buildAnimationController();
         // Reapply the current shift to ensure it takes new insets into account, e.g. when long
         // pressing to stash taskbar without moving the finger.
-        updateFinalShift();
+        onCurrentShiftUpdated();
         return result;
     }
 
@@ -822,7 +861,8 @@
      */
     @UiThread
     @Override
-    public void updateFinalShift() {
+    public void onCurrentShiftUpdated() {
+        setIsInAllAppsRegion(mCurrentShift.value >= ALL_APPS_SHIFT_THRESHOLD);
         updateSysUiFlags(mCurrentShift.value);
         applyScrollAndTransform();
 
@@ -1085,6 +1125,9 @@
         }
 
         switch (endTarget) {
+            case ALL_APPS:
+                mStateCallback.setState(STATE_SETTLED_ON_ALL_APPS | STATE_CAPTURE_SCREENSHOT);
+                break;
             case HOME:
                 mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
                 // Notify the SysUI to use fade-in animation when entering PiP
@@ -1173,6 +1216,9 @@
         final boolean willGoToNewTask =
                 isScrollingToNewTask() && Math.abs(velocity.x) > Math.abs(endVelocity);
         final boolean isSwipeUp = endVelocity < 0;
+        if (mIsInAllAppsRegion) {
+            return isSwipeUp ? ALL_APPS : LAST_TASK;
+        }
         if (!isSwipeUp) {
             final boolean isCenteredOnNewTask =
                     mRecentsView.getDestinationPage() != mRecentsView.getRunningTaskIndex();
@@ -1188,7 +1234,9 @@
         // Fully gestural mode.
         final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
                 .getDimension(R.dimen.quickstep_fling_threshold_speed);
-        if (isScrollingToNewTask && isFlingX) {
+        if (mIsInAllAppsRegion) {
+            return ALL_APPS;
+        } else if (isScrollingToNewTask && isFlingX) {
             // Flinging towards new task takes precedence over mIsMotionPaused (which only
             // checks y-velocity).
             return NEW_TASK;
@@ -1236,7 +1284,8 @@
         mGestureState.setEndTarget(endTarget, false /* isAtomic */);
         mAnimationFactory.setEndTarget(endTarget);
 
-        float endShift = endTarget.isLauncher ? 1 : 0;
+        float endShift = endTarget == ALL_APPS ? mDragLengthFactor
+                : endTarget.isLauncher ? 1 : 0;
         final float startShift;
         if (!isFling) {
             long expectedDuration = Math.abs(Math.round((endShift - currentShift)
@@ -1793,6 +1842,12 @@
         reset();
     }
 
+    @UiThread
+    private void finishCurrentTransitionToAllApps() {
+        finishCurrentTransitionToHome();
+        reset();
+    }
+
     private void reset() {
         mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
         if (mActivity != null) {
@@ -1926,7 +1981,8 @@
     private boolean updateThumbnail(int runningTaskId, boolean refreshView) {
         boolean finishTransitionPosted = false;
         final TaskView taskView;
-        if (mGestureState.getEndTarget() == HOME || mGestureState.getEndTarget() == NEW_TASK) {
+        if (mGestureState.getEndTarget() == HOME || mGestureState.getEndTarget() == NEW_TASK
+                || mGestureState.getEndTarget() == ALL_APPS) {
             // Capture the screenshot before finishing the transition to home or quickswitching to
             // ensure it's taken in the correct orientation, but no need to update the thumbnail.
             taskView = null;
@@ -2072,7 +2128,7 @@
 
     private void onRecentsViewScroll() {
         if (moveWindowWithRecentsScroll()) {
-            updateFinalShift();
+            onCurrentShiftUpdated();
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index fd7aa58..60083c6 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -187,6 +187,9 @@
 
     public abstract boolean allowMinimizeSplitScreen();
 
+    /** @return whether to allow going to All Apps from Overview. */
+    public abstract boolean allowAllAppsFromOverview();
+
     public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
         return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons()
                 || isTrackpadMultiFingerSwipe(ev);
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 8bb189a..5c96000 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -139,6 +139,11 @@
     }
 
     @Override
+    public boolean allowAllAppsFromOverview() {
+        return false;
+    }
+
+    @Override
     public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
         // In non-gesture mode, user might be clicking on the home button which would directly
         // start the home activity instead of going through recents. In that case, defer starting
@@ -196,6 +201,7 @@
             case LAST_TASK:
                 return BACKGROUND_APP;
             case HOME:
+            case ALL_APPS:
             default:
                 return HOME;
         }
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 02f9f57..9d7ccb4 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -18,11 +18,13 @@
 import static com.android.launcher3.MotionEventsUtils.isTrackpadFourFingerSwipe;
 import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
 import static com.android.launcher3.MotionEventsUtils.isTrackpadThreeFingerSwipe;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_ALL_APPS;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
 
@@ -68,7 +70,9 @@
                 GestureStateProto.GestureEndTarget.NEW_TASK),
 
         LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
-                GestureStateProto.GestureEndTarget.LAST_TASK);
+                GestureStateProto.GestureEndTarget.LAST_TASK),
+
+        ALL_APPS(true, LAUNCHER_STATE_ALLAPPS, false, GestureStateProto.GestureEndTarget.ALL_APPS);
 
         GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow,
                 GestureStateProto.GestureEndTarget protoEndTarget) {
@@ -385,6 +389,9 @@
             case NEW_TASK:
                 ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK);
                 break;
+            case ALL_APPS:
+                ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_ALL_APPS);
+                break;
             case LAST_TASK:
             case RECENTS:
             default:
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index ea9f032..0e0b022 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -39,6 +40,7 @@
 import com.android.launcher3.LauncherInitListener;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.StateManager;
@@ -264,6 +266,11 @@
     }
 
     @Override
+    public boolean allowAllAppsFromOverview() {
+        return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get();
+    }
+
+    @Override
     public boolean isInLiveTileMode() {
         Launcher launcher = getCreatedActivity();
         return launcher != null && launcher.getStateManager().getState() == OVERVIEW &&
@@ -347,6 +354,8 @@
             case NEW_TASK:
             case LAST_TASK:
                 return BACKGROUND_APP;
+            case ALL_APPS:
+                return ALL_APPS;
             case HOME:
             default:
                 return NORMAL;
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 66d1e1e..8626c40 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -350,7 +350,8 @@
                 enableMultipleRegions(true);
             }
             activityInterface.onExitOverview(this, mExitOverviewRunnable);
-        } else if (endTarget == GestureState.GestureEndTarget.HOME) {
+        } else if (endTarget == GestureState.GestureEndTarget.HOME
+                || endTarget == GestureState.GestureEndTarget.ALL_APPS) {
             enableMultipleRegions(false);
         } else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) {
             if (mOrientationTouchTransformer.getQuickStepStartingRotation() == -1) {
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index f913aff..1b4fdc4 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -67,7 +67,7 @@
     // 0 => preview snapShot is completely visible, and hotseat is completely translated down
     // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
     // visible.
-    protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+    protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::onCurrentShiftUpdated);
     protected float mCurrentDisplacement;
 
     // The distance needed to drag to reach the task size in recents.
@@ -148,7 +148,7 @@
      * Called when the value of {@link #mCurrentShift} changes
      */
     @UiThread
-    public abstract void updateFinalShift();
+    public abstract void onCurrentShiftUpdated();
 
     protected PagedOrientationHandler getOrientationHandler() {
         // OrientationHandler should be independent of remote target, can directly take one
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 512d47e..fdb30ce 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -967,7 +967,7 @@
             IRemoteAnimationRunner runner) {
         mBackToLauncherCallback = callback;
         mBackToLauncherRunner = runner;
-        if (mBackAnimation == null) {
+        if (mBackAnimation == null || mBackToLauncherCallback == null) {
             return;
         }
         try {
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index b3243ff..a8af05e 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -309,7 +309,7 @@
         }
 
         @Override
-        public void updateFinalShift() {
+        public void onCurrentShiftUpdated() {
             mRemoteTargetHandles[0].getPlaybackController()
                     .setProgress(mCurrentShift.value, mDragLengthFactor);
             mRemoteTargetHandles[0].getTaskViewSimulator().apply(
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 6eadd2b..8335523 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -33,10 +33,11 @@
      */
     public enum GestureEvent {
         MOTION_DOWN, MOTION_UP, MOTION_MOVE, SET_END_TARGET, SET_END_TARGET_HOME,
-        SET_END_TARGET_NEW_TASK, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION,
-        FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK,
-        CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED,
-        EXPECTING_TASK_APPEARED, FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED,
+        SET_END_TARGET_NEW_TASK, SET_END_TARGET_ALL_APPS, ON_SETTLED_ON_END_TARGET,
+        START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION,
+        SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT,
+        SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
+        FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED,
 
         /**
          * These GestureEvents are specifically associated to state flags that get set in
@@ -220,6 +221,7 @@
                 case MOTION_DOWN:
                 case SET_END_TARGET:
                 case SET_END_TARGET_HOME:
+                case SET_END_TARGET_ALL_APPS:
                 case SET_END_TARGET_NEW_TASK:
                 case START_RECENTS_ANIMATION:
                 case SET_ON_PAGE_TRANSITION_END_CALLBACK:
diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
index baca76c..a92ab2a 100644
--- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
+++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
@@ -17,9 +17,11 @@
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.AbsSwipeUpHandler.ALL_APPS_SHIFT_THRESHOLD;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
+import android.animation.AnimatorSet;
 import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.graphics.Matrix;
@@ -32,11 +34,15 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.touch.AllAppsSwipeController;
 import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -49,7 +55,9 @@
 
     private enum RecentsResistanceParams {
         FROM_APP(0.75f, 0.5f, 1f, false),
+        FROM_APP_TO_ALL_APPS(0.75f, 0.5f, 0.8f, false),
         FROM_APP_TABLET(1f, 0.7f, 1f, true),
+        FROM_APP_TO_ALL_APPS_TABLET(1f, 0.5f, 0.5f, false),
         FROM_OVERVIEW(1f, 0.75f, 0.5f, false);
 
         RecentsResistanceParams(float scaleStartResist, float scaleMaxResist,
@@ -86,6 +94,8 @@
     private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DEACCEL;
     private static final TimeInterpolator RECENTS_TRANSLATE_RESIST_INTERPOLATOR = LINEAR;
 
+    private static final Rect TEMP_RECT = new Rect();
+
     private final AnimatorPlaybackController mNormalController;
     private final AnimatorPlaybackController mResistanceController;
 
@@ -145,10 +155,42 @@
                 scaleProperty, translationTarget, translationProperty);
         PendingAnimation resistAnim = createRecentsResistanceAnim(params);
 
+        // Apply All Apps animation during the resistance animation.
+        if (recentsOrientedState.getActivityInterface().allowAllAppsFromOverview()) {
+            StatefulActivity activity =
+                    recentsOrientedState.getActivityInterface().getCreatedActivity();
+            if (activity != null) {
+                StateManager<LauncherState> stateManager = activity.getStateManager();
+                if (stateManager.isInStableState(LauncherState.BACKGROUND_APP)
+                        && stateManager.isInTransition()) {
+
+                    // Calculate the resistance progress threshold where All Apps will trigger.
+                    float threshold = getAllAppsThreshold(context, recentsOrientedState, dp);
+
+                    StateAnimationConfig config = new StateAnimationConfig();
+                    AllAppsSwipeController.applyOverviewToAllAppsAnimConfig(dp, config, threshold);
+                    AnimatorSet allAppsAnimator = stateManager.createAnimationToNewWorkspace(
+                            LauncherState.ALL_APPS, config).getTarget();
+                    resistAnim.add(allAppsAnimator);
+                }
+            }
+        }
+
         AnimatorPlaybackController resistanceController = resistAnim.createPlaybackController();
         return new AnimatorControllerWithResistance(normalController, resistanceController);
     }
 
+    private static float getAllAppsThreshold(Context context,
+            RecentsOrientedState recentsOrientedState, DeviceProfile dp) {
+        int transitionDragLength =
+                recentsOrientedState.getActivityInterface().getSwipeUpDestinationAndLength(
+                        dp, context, TEMP_RECT,
+                        recentsOrientedState.getOrientationHandler());
+        float dragLengthFactor = (float) dp.heightPx / transitionDragLength;
+        // -1s are because 0-1 is reserved for the normal transition.
+        return (ALL_APPS_SHIFT_THRESHOLD - 1) / (dragLengthFactor - 1);
+    }
+
     /**
      * Creates the resistance animation for {@link #createForRecents}, or can be used separately
      * when starting from recents, i.e. {@link #createRecentsResistanceFromOverviewAnim}.
@@ -158,8 +200,8 @@
         Rect startRect = new Rect();
         PagedOrientationHandler orientationHandler = params.recentsOrientedState
                 .getOrientationHandler();
-        LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect,
-                orientationHandler);
+        params.recentsOrientedState.getActivityInterface()
+                .calculateTaskSize(params.context, params.dp, startRect, orientationHandler);
         long distanceToCover = startRect.bottom;
         PendingAnimation resistAnim = params.resistAnim != null
                 ? params.resistAnim
@@ -257,9 +299,15 @@
             this.translationTarget = translationTarget;
             this.translationProperty = translationProperty;
             if (dp.isTablet) {
-                resistanceParams = RecentsResistanceParams.FROM_APP_TABLET;
+                resistanceParams =
+                        recentsOrientedState.getActivityInterface().allowAllAppsFromOverview()
+                                ? RecentsResistanceParams.FROM_APP_TO_ALL_APPS_TABLET
+                                : RecentsResistanceParams.FROM_APP_TABLET;
             } else {
-                resistanceParams = RecentsResistanceParams.FROM_APP;
+                resistanceParams =
+                        recentsOrientedState.getActivityInterface().allowAllAppsFromOverview()
+                                ? RecentsResistanceParams.FROM_APP_TO_ALL_APPS
+                                : RecentsResistanceParams.FROM_APP;
             }
         }
 
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index c4ba39a..f6ad692 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -116,6 +116,7 @@
                     | FLAG_SWIPE_UP_NOT_RUNNING;
 
     private final Context mContext;
+    private final BaseActivityInterface mActivityInterface;
     private final OrientationEventListener mOrientationListener;
     private final SettingsCache mSettingsCache;
     private final SettingsCache.OnChangeListener mRotationChangeListener =
@@ -135,9 +136,10 @@
      *                              is enabled
      * @see #setRotationWatcherEnabled(boolean)
      */
-    public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy,
+    public RecentsOrientedState(Context context, BaseActivityInterface activityInterface,
             IntConsumer rotationChangeListener) {
         mContext = context;
+        mActivityInterface = activityInterface;
         mOrientationListener = new OrientationEventListener(context) {
             @Override
             public void onOrientationChanged(int degrees) {
@@ -149,7 +151,7 @@
             }
         };
 
-        mFlags = sizeStrategy.rotationSupportedByActivity
+        mFlags = mActivityInterface.rotationSupportedByActivity
                 ? FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY : 0;
 
         mFlags |= FLAG_SWIPE_UP_NOT_RUNNING;
@@ -157,6 +159,10 @@
         initFlags();
     }
 
+    public BaseActivityInterface getActivityInterface() {
+        return mActivityInterface;
+    }
+
     /**
      * Sets the device profile for the current state.
      */
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index c165acc..697a1c1 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -17,6 +17,7 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 
+import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -129,7 +130,7 @@
 
     @Override
     public void onStateTransitionComplete(LauncherState finalState) {
-        if (finalState == NORMAL || finalState == SPRING_LOADED) {
+        if (finalState == NORMAL || finalState == SPRING_LOADED || finalState == ALL_APPS) {
             // Clean-up logic that occurs when recents is no longer in use/visible.
             reset();
         }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 42589ce..53660b5 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -581,6 +581,7 @@
                 mIconView, STAGE_POSITION_UNDEFINED);
         mSnapshotView.bind(task);
         setOrientationState(orientedState);
+        mDigitalWellBeingToast.initialize(mTask);
     }
 
     /**
@@ -984,10 +985,7 @@
             }
             if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
                 mIconLoadRequest = iconCache.updateIconInBackground(mTask,
-                        (task) -> {
-                            setIcon(mIconView, task.icon);
-                            mDigitalWellBeingToast.initialize(mTask);
-                        });
+                        (task) -> setIcon(mIconView, task.icon));
             }
         } else {
             if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index 6243471..4540eee 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -27,6 +27,7 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.launcher3.tapl.Overview;
 import com.android.launcher3.tapl.Taskbar;
 import com.android.launcher3.ui.TaplTestsLauncher3;
 import com.android.launcher3.util.LauncherLayoutBuilder;
@@ -63,6 +64,10 @@
                 "com.android.launcher3.testcomponent.BaseTestingActivity");
         mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, layoutBuilder);
         TaplTestsLauncher3.initialize(this);
+        Overview overview = mLauncher.getWorkspace().switchToOverview();
+        if (overview.hasTasks()) {
+            overview.dismissAllTasks();
+        }
 
         startAppFast(CALCULATOR_APP_PACKAGE);
         mLauncher.enableBlockTimeout(true);
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 039d8d3..a709fbc 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -69,15 +69,15 @@
             android:background="@android:color/transparent" />
 
         <include
+            android:id="@+id/overview_panel"
+            layout="@layout/overview_panel" />
+
+        <include
             android:id="@+id/apps_view"
             layout="@layout/all_apps"
             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/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 470a75c..01a4876 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -40,6 +40,7 @@
 import android.os.Process;
 import android.os.UserManager;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.TypedValue;
@@ -101,6 +102,20 @@
         OnDeviceProfileChangeListener, PersonalWorkSlidingTabStrip.OnActivePageChangedListener,
         ScrimView.ScrimDrawingController {
 
+
+    public static final FloatProperty<ActivityAllAppsContainerView<?>> BOTTOM_SHEET_ALPHA =
+            new FloatProperty<>("bottomSheetAlpha") {
+                @Override
+                public Float get(ActivityAllAppsContainerView<?> containerView) {
+                    return containerView.mBottomSheetAlpha;
+                }
+
+                @Override
+                public void setValue(ActivityAllAppsContainerView<?> containerView, float v) {
+                    containerView.setBottomSheetAlpha(v);
+                }
+            };
+
     public static final float PULL_MULTIPLIER = .02f;
     public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
     protected static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
@@ -159,6 +174,8 @@
     private ScrimView mScrimView;
     private int mHeaderColor;
     private int mBottomSheetBackgroundColor;
+    private float mBottomSheetAlpha = 1f;
+    private boolean mForceBottomSheetVisible;
     private int mTabsProtectionAlpha;
     @Nullable private AllAppsTransitionController mAllAppsTransitionController;
 
@@ -258,7 +275,7 @@
         final TypedValue value = new TypedValue();
         getContext().getTheme().resolveAttribute(android.R.attr.colorBackground, value, true);
         mBottomSheetBackgroundColor = value.data;
-        updateBackground(mActivityContext.getDeviceProfile());
+        updateBackgroundVisibility(mActivityContext.getDeviceProfile());
         mSearchUiManager.initializeSearch(this);
     }
 
@@ -282,6 +299,16 @@
         return mBottomSheetBackground;
     }
 
+    /**
+     * Temporarily force the bottom sheet to be visible on non-tablets.
+     *
+     * @param force {@code true} means bottom sheet will be visible on phones until {@code reset()}.
+     **/
+    public void forceBottomSheetVisible(boolean force) {
+        mForceBottomSheetVisible = force;
+        updateBackgroundVisibility(mActivityContext.getDeviceProfile());
+    }
+
     public View getSearchView() {
         return mSearchContainer;
     }
@@ -408,6 +435,7 @@
         if (mHeader != null && mHeader.getVisibility() == VISIBLE) {
             mHeader.reset(animate);
         }
+        forceBottomSheetVisible(false);
         // Reset the base recycler view after transitioning home.
         updateHeaderScroll(0);
         if (exitSearch) {
@@ -830,7 +858,7 @@
                 holder.mRecyclerView.getRecycledViewPool().clear();
             }
         }
-        updateBackground(dp);
+        updateBackgroundVisibility(dp);
 
         int navBarScrimColor = Themes.getNavBarScrimColor(mActivityContext);
         if (mNavBarScrimPaint.getColor() != navBarScrimColor) {
@@ -839,13 +867,19 @@
         }
     }
 
-    protected void updateBackground(DeviceProfile deviceProfile) {
-        mBottomSheetBackground.setVisibility(deviceProfile.isTablet ? View.VISIBLE : View.GONE);
+    protected void updateBackgroundVisibility(DeviceProfile deviceProfile) {
+        boolean visible = deviceProfile.isTablet || mForceBottomSheetVisible;
+        mBottomSheetBackground.setVisibility(visible ? View.VISIBLE : View.GONE);
         // Note: For tablets, the opaque background and header protection are added in drawOnScrim.
         // For the taskbar entrypoint, the scrim is drawn differently, so a static background is
         // added in TaskbarAllAppsContainerView and header protection is not yet supported.
     }
 
+    private void setBottomSheetAlpha(float alpha) {
+        // Bottom sheet alpha is always 1 for tablets.
+        mBottomSheetAlpha = mActivityContext.getDeviceProfile().isTablet ? 1f : alpha;
+    }
+
     private void onAppsUpdated() {
         mHasWorkApps = Stream.of(mAllAppsStore.getApps()).anyMatch(mWorkManager.getMatcher());
         if (TestProtocol.sDebugTracing) {
@@ -1148,8 +1182,8 @@
 
     @Override
     public void drawOnScrimWithScale(Canvas canvas, float scale) {
-        final boolean isTablet = mActivityContext.getDeviceProfile().isTablet;
         final View panel = mBottomSheetBackground;
+        final boolean hasBottomSheet = panel.getVisibility() == VISIBLE;
         final float translationY = ((View) panel.getParent()).getTranslationY();
 
         final float horizontalScaleOffset = (1 - scale) * panel.getWidth() / 2;
@@ -1160,8 +1194,9 @@
         final float leftWithScale = panel.getLeft() + horizontalScaleOffset;
         final float rightWithScale = panel.getRight() - horizontalScaleOffset;
         // Draw full background panel for tablets.
-        if (isTablet) {
+        if (hasBottomSheet) {
             mHeaderPaint.setColor(mBottomSheetBackgroundColor);
+            mHeaderPaint.setAlpha((int) (255 * mBottomSheetAlpha));
 
             mTmpRectF.set(
                     leftWithScale,
@@ -1192,7 +1227,7 @@
         final float headerBottomOffset = (getVisibleContainerView().getHeight() * (1 - scale) / 2);
         final float headerBottomWithScaleOnPhone = headerBottomNoScale * scale + headerBottomOffset;
         final FloatingHeaderView headerView = getFloatingHeaderView();
-        if (isTablet) {
+        if (hasBottomSheet) {
             // Start adding header protection if search bar or tabs will attach to the top.
             if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) {
                 mTmpRectF.set(
@@ -1219,12 +1254,12 @@
             }
             float left = 0f;
             float right = canvas.getWidth();
-            if (isTablet) {
+            if (hasBottomSheet) {
                 left = mBottomSheetBackground.getLeft() + horizontalScaleOffset;
                 right = mBottomSheetBackground.getRight() - horizontalScaleOffset;
             }
 
-            final float tabTopWithScale = isTablet
+            final float tabTopWithScale = hasBottomSheet
                     ? headerBottomWithScaleOnTablet
                     : headerBottomWithScaleOnPhone;
             final float tabBottomWithScale = tabTopWithScale + tabsHeight * scale;
@@ -1263,7 +1298,7 @@
      * Returns a view that denotes the visible part of all apps container view.
      */
     public View getVisibleContainerView() {
-        return mActivityContext.getDeviceProfile().isTablet ? mBottomSheetBackground : this;
+        return mBottomSheetBackground.getVisibility() == VISIBLE ? mBottomSheetBackground : this;
     }
 
     protected void onInitializeRecyclerView(RecyclerView rv) {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 4d1006a..d4f152a 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -21,8 +21,10 @@
 import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV;
@@ -410,8 +412,12 @@
         setter.setFloat(getAppsViewPullbackAlpha(), MultiPropertyFactory.MULTI_PROPERTY_VALUE,
                 hasAllAppsContent ? 1 : 0, allAppsFade);
 
-        boolean shouldProtectHeader =
-                ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS;
+        setter.setFloat(mLauncher.getAppsView(),
+                ActivityAllAppsContainerView.BOTTOM_SHEET_ALPHA, hasAllAppsContent ? 1 : 0,
+                config.getInterpolator(ANIM_ALL_APPS_BOTTOM_SHEET_FADE, INSTANT));
+
+        boolean shouldProtectHeader = !config.hasAnimationFlag(StateAnimationConfig.SKIP_SCRIM)
+                && (ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS);
         mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null);
     }
 
diff --git a/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java
index 684e98e..d7f5463 100644
--- a/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java
@@ -41,7 +41,7 @@
     }
 
     @Override
-    protected void updateBackground(DeviceProfile deviceProfile) {}
+    protected void updateBackgroundVisibility(DeviceProfile deviceProfile) {}
 
     @Override
     public boolean isInAllApps() {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 492bcf5..1aa7f05 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -118,9 +118,13 @@
 
     // TODO(Block 4): Cleanup flags
     public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR =
-            getReleaseFlag(270390286, "ENABLE_FLOATING_SEARCH_BAR", DISABLED,
+            getReleaseFlag(268388460, "ENABLE_FLOATING_SEARCH_BAR", DISABLED,
                     "Keep All Apps search bar at the bottom (but above keyboard if open)");
 
+    public static final BooleanFlag ENABLE_ALL_APPS_FROM_OVERVIEW =
+            getDebugFlag(275132633, "ENABLE_ALL_APPS_FROM_OVERVIEW", DISABLED,
+                    "Allow entering All Apps from Overview (e.g. long swipe up from app)");
+
     public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag(
             270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", ENABLED,
             "Enable option to show keyboard when going to all-apps");
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index bf839bf..70c9802 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -151,7 +151,12 @@
     }
 
     public void bindApplicationsIfNeeded() {
-        if (mAllAppsList.getAndResetChangeFlag()) {
+        boolean changeFlag = mAllAppsList.getAndResetChangeFlag();
+        if (TestProtocol.sDebugTracing) {
+            Log.d(WORK_TAB_MISSING, "bindApplicationsIfNeeded changeFlag? " +
+                    changeFlag);
+        }
+        if (changeFlag) {
             AppInfo[] apps = mAllAppsList.copyData();
             int flags = mAllAppsList.getFlags();
             scheduleCallbackTask(c -> c.bindAllApplications(apps, flags));
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 1ab64df..d4a5e1b 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -67,7 +68,7 @@
  */
 public class PackageUpdatedTask extends BaseModelUpdateTask {
 
-    private static final boolean DEBUG = false;
+    private static boolean DEBUG = false;
     private static final String TAG = "PackageUpdatedTask";
 
     public static final int OP_NONE = 0;
@@ -92,6 +93,11 @@
         mOp = op;
         mUser = user;
         mPackages = packages;
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.WORK_TAB_MISSING, "PackageUpdatedTask mOp: " + mOp +
+                    " packageCount: " + mPackages.length);
+            DEBUG = true;
+        }
     }
 
     @Override
@@ -136,6 +142,9 @@
                         // The update may have changed which shortcuts/widgets are available.
                         // Refresh the widgets for the package if we have an activity running.
                         Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
+                        if (TestProtocol.sDebugTracing) {
+                            Log.d(TestProtocol.WORK_TAB_MISSING, "launcher: " + launcher);
+                        }
                         if (launcher != null) {
                             launcher.refreshAndBindWidgetsForPackageUser(
                                     new PackageUserKey(packages[i], mUser));
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 54735f0..d1e816b 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -65,7 +65,8 @@
             ANIM_OVERVIEW_ACTIONS_FADE,
             ANIM_WORKSPACE_PAGE_TRANSLATE_X,
             ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
-            ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE
+            ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
+            ANIM_ALL_APPS_BOTTOM_SHEET_FADE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimType {}
@@ -88,8 +89,9 @@
     public static final int ANIM_WORKSPACE_PAGE_TRANSLATE_X = 15;
     public static final int ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN = 17;
     public static final int ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE = 18;
+    public static final int ANIM_ALL_APPS_BOTTOM_SHEET_FADE = 19;
 
-    private static final int ANIM_TYPES_COUNT = 19;
+    private static final int ANIM_TYPES_COUNT = 20;
 
     protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
 
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index a53751f..d028f24 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -23,6 +23,8 @@
 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.clampToProgress;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
@@ -32,11 +34,14 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.Interpolators;
@@ -269,4 +274,44 @@
                             : ALL_APPS_VERTICAL_PROGRESS_ATOMIC);
         }
     }
+
+    /**
+     * Applies Animation config values for transition from overview to all apps.
+     *
+     * @param threshold progress at which all apps will open upon release
+     */
+    public static void applyOverviewToAllAppsAnimConfig(
+            DeviceProfile deviceProfile, StateAnimationConfig config, float threshold) {
+        config.userControlled = true;
+        config.animFlags = SKIP_OVERVIEW;
+        if (deviceProfile.isTablet) {
+            config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
+            config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER);
+            // The fact that we end on Workspace is not very ideal, but since we do, fade it in at
+            // the end of the transition. Don't scale/translate it.
+            config.setInterpolator(ANIM_WORKSPACE_FADE, clampToProgress(LINEAR, 0.8f, 1));
+            config.setInterpolator(ANIM_WORKSPACE_SCALE, INSTANT);
+            config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, INSTANT);
+        } else {
+            // Remove scrim for this transition.
+            config.setInterpolator(ANIM_SCRIM_FADE, progress -> 0);
+
+            // For now, pop the background panel in at full opacity at the threshold.
+            config.setInterpolator(ANIM_ALL_APPS_BOTTOM_SHEET_FADE,
+                    thresholdInterpolator(threshold, INSTANT));
+
+            // Fade the apps in when the scrim normally does, so it's apparent sooner what is
+            // happening (in this case we are fading them on top of the background panel).
+            config.setInterpolator(ANIM_ALL_APPS_FADE,
+                    thresholdInterpolator(threshold, SCRIM_FADE_MANUAL));
+
+            config.setInterpolator(ANIM_VERTICAL_PROGRESS,
+                    thresholdInterpolator(threshold, ALL_APPS_VERTICAL_PROGRESS_MANUAL));
+        }
+    }
+
+    /** Creates an interpolator that is 0 until the threshold, then follows given interpolator. */
+    private static Interpolator thresholdInterpolator(float threshold, Interpolator interpolator) {
+        return progress -> progress <= threshold ? 0 : interpolator.getInterpolation(progress);
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 54da7de..c83820b 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -105,6 +105,8 @@
     }
 
     private void waitForWorkTabSetup() {
+        // Added for b/243688989 flake to determine if we really are in allApps or not at this point
+        mLauncher.getAllApps();
         waitForLauncherCondition("Work tab not setup", launcher -> {
             if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) {
                 Log.d(WORK_TAB_MISSING, "Deferring AppsStore updates");