Merge "Disable 3-finger workspace scroll" into udc-dev
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 225bdcc..599095b 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -300,6 +300,8 @@
     <dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
     <dimen name="transient_taskbar_stashed_size">32dp</dimen>
     <dimen name="transient_taskbar_all_apps_button_translation_x_offset">4dp</dimen>
+    <dimen name="transient_taskbar_stash_spring_velocity_dp_per_s">400dp</dimen>
+
     <!-- An additional touch slop to prevent x-axis movement during the swipe up to show taskbar -->
     <dimen name="transient_taskbar_clamped_offset_bound">16dp</dimen>
     <!-- Taskbar swipe up thresholds -->
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index 48352a2..331184a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -65,4 +65,10 @@
 
     /** Callback invoked when a popup is shown or closed within this context. */
     public abstract void onPopupVisibilityChanged(boolean isVisible);
+
+    /**
+     * Callback invoked when user attempts to split the screen through a long-press menu in Taskbar
+     * or AllApps.
+     */
+    public abstract void onSplitScreenMenuButtonClicked();
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 089e069..a9ce61a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -82,7 +82,6 @@
             };
 
     // Initialized in init.
-    private TaskbarKeyguardController mKeyguardController;
     private final TaskbarLauncherStateController
             mTaskbarLauncherStateController = new TaskbarLauncherStateController();
 
@@ -97,11 +96,12 @@
         mTaskbarLauncherStateController.init(mControllers, mLauncher);
 
         mLauncher.setTaskbarUIController(this);
-        mKeyguardController = taskbarControllers.taskbarKeyguardController;
 
         onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */);
 
         onStashedInAppChanged(mLauncher.getDeviceProfile());
+        mTaskbarLauncherStateController.onChangeScreenState(
+                mControllers.getSharedState().sysuiStateFlags, true /* fromInit */);
         mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
     }
 
@@ -119,7 +119,7 @@
     @Override
     protected boolean isTaskbarTouchable() {
         return !(mTaskbarLauncherStateController.isAnimatingToLauncher()
-                && mTaskbarLauncherStateController.goingToAlignedLauncherState());
+                && mTaskbarLauncherStateController.isTaskbarAlignedWithHotseat());
     }
 
     public void setShouldDelayLauncherStateAnim(boolean shouldDelayLauncherStateAnim) {
@@ -167,15 +167,6 @@
     @Nullable
     private Animator onLauncherResumedOrPaused(
             boolean isResumed, boolean fromInit, boolean startAnimation, int duration) {
-        if (mKeyguardController.isScreenOff()) {
-            if (!isResumed) {
-                return null;
-            } else {
-                // Resuming implicitly means device unlocked
-                mKeyguardController.setScreenOn();
-            }
-        }
-
         // Launcher is resumed during the swipe-to-overview gesture under shell-transitions, so
         // avoid updating taskbar state in that situation (when it's non-interactive -- or
         // "background") to avoid premature animations.
@@ -328,6 +319,11 @@
     }
 
     @Override
+    public void onChangeScreenState(int screenState) {
+        mTaskbarLauncherStateController.onChangeScreenState(screenState, false /* fromInit */);
+    }
+
+    @Override
     public boolean isIconAlignedWithHotseat() {
         return mTaskbarLauncherStateController.isIconAlignedWithHotseat();
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index f082fc6..48b3f72 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -84,6 +84,9 @@
     private boolean mIsStashed;
     private boolean mTaskbarHidden;
 
+    private float mTranslationYForSwipe;
+    private float mTranslationYForStash;
+
     public StashedHandleViewController(TaskbarActivityContext activity,
             StashedHandleView stashedHandleView) {
         mActivity = activity;
@@ -254,7 +257,20 @@
      * Sets the translation of the stashed handle during the swipe up gesture.
      */
     protected void setTranslationYForSwipe(float transY) {
-        mStashedHandleView.setTranslationY(transY);
+        mTranslationYForSwipe = transY;
+        updateTranslationY();
+    }
+
+    /**
+     * Sets the translation of the stashed handle during the spring on stash animation.
+     */
+    protected void setTranslationYForStash(float transY) {
+        mTranslationYForStash = transY;
+        updateTranslationY();
+    }
+
+    private void updateTranslationY() {
+        mStashedHandleView.setTranslationY(mTranslationYForSwipe + mTranslationYForStash);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 39a5ad4..6041192 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -32,6 +32,7 @@
 import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_STATE_MASK;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
 
 import android.animation.AnimatorSet;
@@ -56,7 +57,6 @@
 import android.view.RoundedCorner;
 import android.view.View;
 import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
 import android.widget.FrameLayout;
 import android.widget.Toast;
 
@@ -80,6 +80,7 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.AutohideSuspendFlag;
 import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
@@ -234,6 +235,7 @@
                 new TaskbarInsetsController(this),
                 new VoiceInteractionWindowController(this),
                 new TaskbarTranslationController(this),
+                new TaskbarSpringOnStashController(this),
                 isDesktopMode
                         ? new DesktopTaskbarRecentAppsController(this)
                         : TaskbarRecentAppsController.DEFAULT,
@@ -525,6 +527,16 @@
         setTaskbarWindowFocusable(isVisible);
     }
 
+    @Override
+    public void onSplitScreenMenuButtonClicked() {
+        PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(this);
+        if (popup != null) {
+            popup.addOnCloseCallback(() -> {
+                mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+            });
+        }
+    }
+
     /**
      * Sets a new data-source for this taskbar instance
      */
@@ -579,6 +591,9 @@
         mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags);
         mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible(
                 (systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit);
+
+        mControllers.uiController.onChangeScreenState(
+                systemUiStateFlags & SYSUI_STATE_SCREEN_STATE_MASK);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 3375877..82b455d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -35,6 +35,7 @@
     val lastDrawnTransientRect = RectF()
     var backgroundHeight = context.deviceProfile.taskbarSize.toFloat()
     var translationYForSwipe = 0f
+    var translationYForStash = 0f
 
     private var maxBackgroundHeight = context.deviceProfile.taskbarSize.toFloat()
     private val transientBackgroundBounds = context.transientTaskbarBounds
@@ -136,7 +137,9 @@
             val bottom =
                 canvas.height - bottomMargin +
                     bottomMarginProgress +
-                    (-mapRange(1f - progress, 0f, stashedHandleHeight / 2f) + translationYForSwipe)
+                    translationYForSwipe +
+                    translationYForStash +
+                    -mapRange(1f - progress, 0f, stashedHandleHeight / 2f)
 
             // Draw shadow.
             val shadowAlpha =
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 931d79f..a6da56d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -57,6 +57,7 @@
     public final VoiceInteractionWindowController voiceInteractionWindowController;
     public final TaskbarRecentAppsController taskbarRecentAppsController;
     public final TaskbarTranslationController taskbarTranslationController;
+    public final TaskbarSpringOnStashController taskbarSpringOnStashController;
     public final TaskbarOverlayController taskbarOverlayController;
     public final TaskbarEduTooltipController taskbarEduTooltipController;
     public final KeyboardQuickSwitchController keyboardQuickSwitchController;
@@ -103,6 +104,7 @@
             TaskbarInsetsController taskbarInsetsController,
             VoiceInteractionWindowController voiceInteractionWindowController,
             TaskbarTranslationController taskbarTranslationController,
+            TaskbarSpringOnStashController taskbarSpringOnStashController,
             TaskbarRecentAppsController taskbarRecentAppsController,
             TaskbarEduTooltipController taskbarEduTooltipController,
             KeyboardQuickSwitchController keyboardQuickSwitchController) {
@@ -127,6 +129,7 @@
         this.taskbarInsetsController = taskbarInsetsController;
         this.voiceInteractionWindowController = voiceInteractionWindowController;
         this.taskbarTranslationController = taskbarTranslationController;
+        this.taskbarSpringOnStashController = taskbarSpringOnStashController;
         this.taskbarRecentAppsController = taskbarRecentAppsController;
         this.taskbarEduTooltipController = taskbarEduTooltipController;
         this.keyboardQuickSwitchController = keyboardQuickSwitchController;
@@ -149,6 +152,7 @@
         taskbarScrimViewController.init(this);
         taskbarUnfoldAnimationController.init(this);
         taskbarKeyguardController.init(navbarButtonsViewController);
+        taskbarSpringOnStashController.init(this);
         stashedHandleViewController.init(this);
         taskbarStashController.init(this, sharedState.setupUIVisible);
         taskbarEduController.init(this);
@@ -209,7 +213,6 @@
         uiController.onDestroy();
         rotationButtonController.onDestroy();
         taskbarDragLayerController.onDestroy();
-        taskbarKeyguardController.onDestroy();
         taskbarUnfoldAnimationController.onDestroy();
         taskbarViewController.onDestroy();
         stashedHandleViewController.onDestroy();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 58d6244..6b9297d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -184,6 +184,14 @@
         invalidate();
     }
 
+    /*
+     * Sets the translation of the background during the spring on stash animation.
+     */
+    protected void setBackgroundTranslationYForStash(float translationY) {
+        mBackgroundRenderer.setTranslationYForStash(translationY);
+        invalidate();
+    }
+
     /** Returns the bounds in DragLayer coordinates of where the transient background was drawn. */
     protected RectF getLastDrawnTransientRect() {
         return mBackgroundRenderer.getLastDrawnTransientRect();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 7c3d14d..2badc24 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -161,6 +161,13 @@
         mTaskbarDragLayer.setBackgroundTranslationYForSwipe(transY);
     }
 
+    /**
+     * Sets the translation of the background during the spring on stash animation.
+     */
+    public void setTranslationYForStash(float transY) {
+        mTaskbarDragLayer.setBackgroundTranslationYForStash(transY);
+    }
+
     private void updateBackgroundOffset() {
         mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
index 93ba4eb..4e390f1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
@@ -1,19 +1,20 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_OFF;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_ON;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_STATE_MASK;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 
 import android.app.KeyguardManager;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.util.ScreenOnTracker;
-import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
 import com.android.systemui.shared.system.QuickStepContract;
 
 import java.io.PrintWriter;
@@ -23,36 +24,35 @@
  */
 public class TaskbarKeyguardController implements TaskbarControllers.LoggableTaskbarController {
 
-    private static final int KEYGUARD_SYSUI_FLAGS = SYSUI_STATE_BOUNCER_SHOWING |
-            SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING | SYSUI_STATE_DEVICE_DOZING |
-            SYSUI_STATE_OVERVIEW_DISABLED | SYSUI_STATE_HOME_DISABLED |
-            SYSUI_STATE_BACK_DISABLED | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+    private static final int KEYGUARD_SYSUI_FLAGS = SYSUI_STATE_BOUNCER_SHOWING
+            | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING | SYSUI_STATE_DEVICE_DOZING
+            | SYSUI_STATE_OVERVIEW_DISABLED | SYSUI_STATE_HOME_DISABLED
+            | SYSUI_STATE_BACK_DISABLED | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
+            | SYSUI_STATE_SCREEN_STATE_MASK;
 
-    private final ScreenOnListener mScreenOnListener;
     private final TaskbarActivityContext mContext;
     private int mKeyguardSysuiFlags;
     private boolean mBouncerShowing;
     private NavbarButtonsViewController mNavbarButtonsViewController;
     private final KeyguardManager mKeyguardManager;
-    private boolean mIsScreenOff;
 
     public TaskbarKeyguardController(TaskbarActivityContext context) {
         mContext = context;
-        mScreenOnListener = isOn -> {
-            if (!isOn) {
-                mIsScreenOff = true;
-                AbstractFloatingView.closeOpenViews(mContext, false, TYPE_ALL);
-            }
-        };
         mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
     }
 
     public void init(NavbarButtonsViewController navbarButtonUIController) {
         mNavbarButtonsViewController = navbarButtonUIController;
-        ScreenOnTracker.INSTANCE.get(mContext).addListener(mScreenOnListener);
     }
 
     public void updateStateForSysuiFlags(int systemUiStateFlags) {
+        int interestingKeyguardFlags = systemUiStateFlags & KEYGUARD_SYSUI_FLAGS;
+        if (interestingKeyguardFlags == mKeyguardSysuiFlags) {
+            // No change in keyguard relevant flags
+            return;
+        }
+        mKeyguardSysuiFlags = interestingKeyguardFlags;
+
         boolean bouncerShowing = (systemUiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0;
         boolean keyguardShowing = (systemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING)
                 != 0;
@@ -60,11 +60,6 @@
                 (systemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0;
         boolean dozing = (systemUiStateFlags & SYSUI_STATE_DEVICE_DOZING) != 0;
 
-        int interestingKeyguardFlags = systemUiStateFlags & KEYGUARD_SYSUI_FLAGS;
-        if (interestingKeyguardFlags == mKeyguardSysuiFlags) {
-            return;
-        }
-        mKeyguardSysuiFlags = interestingKeyguardFlags;
 
         mBouncerShowing = bouncerShowing;
 
@@ -72,19 +67,17 @@
                 keyguardOccluded);
         updateIconsForBouncer();
 
-        if (keyguardShowing) {
-            AbstractFloatingView.closeOpenViews(mContext, true, TYPE_ALL);
+        boolean screenOffOrTransitioningOff = (systemUiStateFlags & SYSUI_STATE_SCREEN_ON) == 0;
+        boolean closeFloatingViews = keyguardShowing || screenOffOrTransitioningOff;
+
+        if (closeFloatingViews) {
+            // animate the closing of the views, unless the screen is already fully turned off.
+            boolean animateViewClosing =
+                    (systemUiStateFlags & SYSUI_STATE_SCREEN_STATE_MASK) != SCREEN_STATE_OFF;
+            AbstractFloatingView.closeOpenViews(mContext, animateViewClosing, TYPE_ALL);
         }
     }
 
-    public boolean isScreenOff() {
-        return mIsScreenOff;
-    }
-
-    public void setScreenOn() {
-        mIsScreenOff = false;
-    }
-
     /**
      * Hides/shows taskbar when keyguard is up
      */
@@ -95,9 +88,6 @@
         mNavbarButtonsViewController.setBackForBouncer(showBackForBouncer);
     }
 
-    public void onDestroy() {
-        ScreenOnTracker.INSTANCE.get(mContext).removeListener(mScreenOnListener);
-    }
 
     @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
@@ -106,6 +96,5 @@
         pw.println(prefix + "\tmKeyguardSysuiFlags=" + QuickStepContract.getSystemUiStateString(
                 mKeyguardSysuiFlags));
         pw.println(prefix + "\tmBouncerShowing=" + mBouncerShowing);
-        pw.println(prefix + "\tmIsScreenOff=" + mIsScreenOff);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 7d5a400..93a0c11 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -18,7 +18,10 @@
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
+import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
+import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
 import static com.android.systemui.animation.Interpolators.EMPHASIZED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_ON;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -53,16 +56,43 @@
  * Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
  * the task bar accordingly.
  */
- public class TaskbarLauncherStateController {
+public class TaskbarLauncherStateController {
 
     private static final String TAG = TaskbarLauncherStateController.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    /** Launcher activity is resumed and focused. */
     public static final int FLAG_RESUMED = 1 << 0;
-    public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1;
-    public static final int FLAG_TRANSITION_STATE_RUNNING = 1 << 2;
 
-    private static final int FLAGS_LAUNCHER = FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING;
+    /**
+     * A external transition / animation is running that will result in FLAG_RESUMED being set.
+     **/
+    public static final int FLAG_TRANSITION_TO_RESUMED = 1 << 1;
+
+    /**
+     * Set while the launcher state machine is performing a state transition, see {@link
+     * StateManager.StateListener}.
+     */
+    public static final int FLAG_LAUNCHER_IN_STATE_TRANSITION = 1 << 2;
+
+    /**
+     * Whether the screen is currently on, or is transitioning to be on.
+     *
+     * This is cleared as soon as the screen begins to transition off.
+     */
+    private static final int FLAG_SCREEN_ON = 1 << 3;
+
+    /**
+     * Captures whether the launcher was active at the time the FLAG_SCREEN_ON was cleared.
+     * Always cleared when FLAG_SCREEN_ON is set.
+     * <p>
+     * FLAG_RESUMED will be cleared when the screen is off, since all apps get paused at this point.
+     * Thus, this flag indicates whether the launcher will be shown when the screen gets turned on
+     * again.
+     */
+    private static final int FLAG_LAUNCHER_ACTIVE_AT_SCREEN_OFF = 1 << 4;
+
+    private static final int FLAGS_LAUNCHER_ACTIVE = FLAG_RESUMED | FLAG_TRANSITION_TO_RESUMED;
     /** Equivalent to an int with all 1s for binary operation purposes */
     private static final int FLAGS_ALL = ~0;
 
@@ -115,12 +145,13 @@
                 @Override
                 public void onStateTransitionStart(LauncherState toState) {
                     if (toState != mLauncherState) {
-                        // Treat FLAG_TRANSITION_STATE_RUNNING as a changed flag even if a previous
-                        // state transition was already running, so we update the new target.
-                        mPrevState &= ~FLAG_TRANSITION_STATE_RUNNING;
+                        // Treat FLAG_LAUNCHER_IN_STATE_TRANSITION as a changed flag even if a
+                        // previous state transition was already running, so we update the new
+                        // target.
+                        mPrevState &= ~FLAG_LAUNCHER_IN_STATE_TRANSITION;
                         mLauncherState = toState;
                     }
-                    updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, true);
+                    updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, true);
                     if (!mShouldDelayLauncherStateAnim) {
                         if (toState == LauncherState.NORMAL) {
                             applyState(QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION);
@@ -133,7 +164,7 @@
                 @Override
                 public void onStateTransitionComplete(LauncherState finalState) {
                     mLauncherState = finalState;
-                    updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false);
+                    updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, false);
                     applyState();
                     boolean disallowLongClick = finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
                     com.android.launcher3.taskbar.Utilities.setOverviewDragState(
@@ -159,9 +190,6 @@
         resetIconAlignment();
 
         mLauncher.getStateManager().addStateListener(mStateListener);
-
-        // Initialize to the current launcher state
-        updateStateForFlag(FLAG_RESUMED, launcher.hasBeenResumed());
         mLauncherState = launcher.getStateManager().getState();
         applyState(0);
 
@@ -182,6 +210,12 @@
         mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
     }
 
+    /**
+     * Creates a transition animation to the launcher activity.
+     *
+     * Warning: the resulting animation must be played, since this method has side effects on this
+     * controller's state.
+     */
     public Animator createAnimToLauncher(@NonNull LauncherState toState,
             @NonNull RecentsAnimationCallbacks callbacks, long duration) {
         // If going to overview, stash the task bar
@@ -197,7 +231,7 @@
         }
         stashController.updateStateForFlag(FLAG_IN_APP, false);
 
-        updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true);
+        updateStateForFlag(FLAG_TRANSITION_TO_RESUMED, true);
         animatorSet.play(stashController.createApplyStateAnimator(duration));
         animatorSet.play(applyState(duration, false));
 
@@ -225,12 +259,30 @@
         mShouldDelayLauncherStateAnim = shouldDelayLauncherStateAnim;
     }
 
+    /** Screen state changed, see QuickStepContract.SCREEN_STATE_* values. */
+    public void onChangeScreenState(int screenState, boolean fromInit) {
+        final boolean prevScreenIsOn = hasAnyFlag(FLAG_SCREEN_ON);
+        final boolean currScreenIsOn = hasAnyFlag(screenState, SYSUI_STATE_SCREEN_ON);
+
+        if (prevScreenIsOn == currScreenIsOn) return;
+
+        updateStateForFlag(FLAG_SCREEN_ON, currScreenIsOn);
+        updateStateForFlag(FLAG_LAUNCHER_ACTIVE_AT_SCREEN_OFF,
+                prevScreenIsOn && hasAnyFlag(FLAGS_LAUNCHER_ACTIVE));
+
+        if (fromInit) {
+            applyState(0);
+        } else {
+            applyState();
+        }
+    }
+
     /**
      * Updates the proper flag to change the state of the task bar.
      *
      * Note that this only updates the flag. {@link #applyState()} needs to be called separately.
      *
-     * @param flag The flag to update.
+     * @param flag    The flag to update.
      * @param enabled Whether to enable the flag
      */
     public void updateStateForFlag(int flag, boolean enabled) {
@@ -269,6 +321,19 @@
         if (mPrevState == null || mPrevState != mState) {
             // If this is our initial state, treat all flags as changed.
             int changedFlags = mPrevState == null ? FLAGS_ALL : mPrevState ^ mState;
+
+            if (DEBUG) {
+                String stateString;
+                if (mPrevState == null) {
+                    stateString = getStateString(mState) + "(initial update)";
+                } else {
+                    stateString = formatFlagChange(mState, mPrevState,
+                            TaskbarLauncherStateController::getStateString);
+                }
+                Log.d(TAG, "applyState: " + stateString
+                        + ", duration: " + duration
+                        + ", start: " + start);
+            }
             mPrevState = mState;
             animator = onStateChangeApplied(changedFlags, duration, start);
         }
@@ -276,26 +341,23 @@
     }
 
     private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
-        final boolean goingToLauncher = isInLauncher();
+        final boolean isInLauncher = isInLauncher();
         final boolean isIconAlignedWithHotseat = isIconAlignedWithHotseat();
         final float toAlignment = isIconAlignedWithHotseat ? 1 : 0;
         boolean handleOpenFloatingViews = false;
         if (DEBUG) {
-            Log.d(TAG, "onStateChangeApplied - mState: " + getStateString(mState)
-                    + ", changedFlags: " + getStateString(changedFlags)
-                    + ", goingToLauncher: " + goingToLauncher
+            Log.d(TAG, "onStateChangeApplied - isInLauncher: " + isInLauncher
                     + ", mLauncherState: " + mLauncherState
                     + ", toAlignment: " + toAlignment);
         }
         AnimatorSet animatorSet = new AnimatorSet();
 
-        // Add the state animation first to ensure FLAG_IN_STASHED_LAUNCHER_STATE is set and we can
-        // determine whether goingToUnstashedLauncherStateChanged.
-        if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_RUNNING)) {
-            boolean committed = !hasAnyFlag(FLAG_TRANSITION_STATE_RUNNING);
-            playStateTransitionAnim(animatorSet, duration, committed);
+        if (hasAnyFlag(changedFlags, FLAG_LAUNCHER_IN_STATE_TRANSITION)) {
+            boolean launcherTransitionCompleted = !hasAnyFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION);
+            playStateTransitionAnim(animatorSet, duration, launcherTransitionCompleted);
 
-            if (committed && mLauncherState == LauncherState.QUICK_SWITCH_FROM_HOME) {
+            if (launcherTransitionCompleted
+                    && mLauncherState == LauncherState.QUICK_SWITCH_FROM_HOME) {
                 // We're about to be paused, set immediately to ensure seamless handoff.
                 updateStateForFlag(FLAG_RESUMED, false);
                 applyState(0 /* duration */);
@@ -306,37 +368,37 @@
             }
         }
 
-        if (hasAnyFlag(changedFlags, FLAGS_LAUNCHER)) {
+        if (hasAnyFlag(changedFlags, FLAGS_LAUNCHER_ACTIVE)) {
             animatorSet.addListener(new AnimatorListenerAdapter() {
                 @Override
-                public void onAnimationEnd(Animator animation) {
-                    mIsAnimatingToLauncher = false;
-                }
-
-                @Override
                 public void onAnimationStart(Animator animation) {
-                    mIsAnimatingToLauncher = goingToLauncher;
+                    mIsAnimatingToLauncher = isInLauncher;
 
                     TaskbarStashController stashController =
                             mControllers.taskbarStashController;
                     if (DEBUG) {
-                        Log.d(TAG, "onAnimationStart - FLAG_IN_APP: " + !goingToLauncher);
+                        Log.d(TAG, "onAnimationStart - FLAG_IN_APP: " + !isInLauncher);
                     }
-                    stashController.updateStateForFlag(FLAG_IN_APP, !goingToLauncher);
+                    stashController.updateStateForFlag(FLAG_IN_APP, !isInLauncher);
                     stashController.applyState(duration);
                 }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mIsAnimatingToLauncher = false;
+                }
             });
 
             // Handle closing open popups when going home/overview
             handleOpenFloatingViews = true;
         }
-        if (handleOpenFloatingViews && goingToLauncher) {
+
+        if (handleOpenFloatingViews && isInLauncher) {
             AbstractFloatingView.closeAllOpenViews(mControllers.taskbarActivityContext);
         }
 
-        float backgroundAlpha =
-                goingToLauncher && mLauncherState.isTaskbarAlignedWithHotseat(mLauncher)
-                        ? 0 : 1;
+        float backgroundAlpha = isInLauncher && isTaskbarAlignedWithHotseat() ? 0 : 1;
+
         // Don't animate if background has reached desired value.
         if (mTaskbarBackgroundAlpha.isAnimating()
                 || mTaskbarBackgroundAlpha.value != backgroundAlpha) {
@@ -347,21 +409,20 @@
                         + " -> " + backgroundAlpha + ": " + duration);
             }
 
-            boolean goingToLauncherIconNotAligned = goingToLauncher && !isIconAlignedWithHotseat;
-            boolean notGoingToLauncherIconNotAligned = !goingToLauncher
-                    && !isIconAlignedWithHotseat;
-            boolean goingToLauncherIconIsAligned = goingToLauncher && isIconAlignedWithHotseat;
+            boolean isInLauncherIconNotAligned = isInLauncher && !isIconAlignedWithHotseat;
+            boolean notInLauncherIconNotAligned = !isInLauncher && !isIconAlignedWithHotseat;
+            boolean isInLauncherIconIsAligned = isInLauncher && isIconAlignedWithHotseat;
 
             float startDelay = 0;
             // We want to delay the background from fading in so that the icons have time to move
             // into the bounds of the background before it appears.
-            if (goingToLauncherIconNotAligned) {
+            if (isInLauncherIconNotAligned) {
                 startDelay = duration * TASKBAR_BG_ALPHA_LAUNCHER_NOT_ALIGNED_DELAY_MULT;
-            } else if (notGoingToLauncherIconNotAligned) {
+            } else if (notInLauncherIconNotAligned) {
                 startDelay = duration * TASKBAR_BG_ALPHA_NOT_LAUNCHER_NOT_ALIGNED_DELAY_MULT;
             }
             float newDuration = duration - startDelay;
-            if (goingToLauncherIconIsAligned) {
+            if (isInLauncherIconIsAligned) {
                 // Make the background fade out faster so that it is gone by the time the
                 // icons move outside of the bounds of the background.
                 newDuration = duration * TASKBAR_BG_ALPHA_LAUNCHER_IS_ALIGNED_DURATION_MULT;
@@ -373,7 +434,8 @@
             animatorSet.play(taskbarBackgroundAlpha);
         }
 
-        float cornerRoundness = goingToLauncher ? 0 : 1;
+        float cornerRoundness = isInLauncher ? 0 : 1;
+
         // Don't animate if corner roundness has reached desired value.
         if (mTaskbarCornerRoundness.isAnimating()
                 || mTaskbarCornerRoundness.value != cornerRoundness) {
@@ -412,8 +474,12 @@
         return animatorSet;
     }
 
-    /** Returns whether we're going to a state where taskbar icons should align with launcher. */
-    public boolean goingToAlignedLauncherState() {
+    /**
+     * Whether the taskbar is aligned with the hotseat in the current/target launcher state.
+     *
+     * This refers to the intended state - a transition to this state might be in progress.
+     */
+    public boolean isTaskbarAlignedWithHotseat() {
         return mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
     }
 
@@ -481,8 +547,13 @@
         }
     }
 
+    /** Whether the launcher is considered active. */
     private boolean isInLauncher() {
-        return (mState & FLAGS_LAUNCHER) != 0;
+        if (hasAnyFlag(FLAG_SCREEN_ON)) {
+            return hasAnyFlag(FLAGS_LAUNCHER_ACTIVE);
+        } else {
+            return hasAnyFlag(FLAG_LAUNCHER_ACTIVE_AT_SCREEN_OFF);
+        }
     }
 
     /**
@@ -509,7 +580,8 @@
         if (firstFrameVisChanged && mCanSyncViews && !Utilities.isRunningInTestHarness()) {
             ViewRootSync.synchronizeNextDraw(mLauncher.getHotseat(),
                     mControllers.taskbarActivityContext.getDragLayer(),
-                    () -> {});
+                    () -> {
+                    });
         }
     }
 
@@ -562,7 +634,7 @@
 
             // Update the resumed state immediately to ensure a seamless handoff
             boolean launcherResumed = !finishedToApp;
-            updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, false);
+            updateStateForFlag(FLAG_TRANSITION_TO_RESUMED, false);
             updateStateForFlag(FLAG_RESUMED, launcherResumed);
             applyState();
 
@@ -576,17 +648,15 @@
     }
 
     private static String getStateString(int flags) {
-        StringJoiner str = new StringJoiner("|");
-        if ((flags & FLAG_RESUMED) != 0) {
-            str.add("FLAG_RESUMED");
-        }
-        if ((flags & FLAG_RECENTS_ANIMATION_RUNNING) != 0) {
-            str.add("FLAG_RECENTS_ANIMATION_RUNNING");
-        }
-        if ((flags & FLAG_TRANSITION_STATE_RUNNING) != 0) {
-            str.add("FLAG_TRANSITION_STATE_RUNNING");
-        }
-        return str.toString();
+        StringJoiner result = new StringJoiner("|");
+        appendFlag(result, flags, FLAG_RESUMED, "resumed");
+        appendFlag(result, flags, FLAG_TRANSITION_TO_RESUMED, "transition_to_resumed");
+        appendFlag(result, flags, FLAG_LAUNCHER_IN_STATE_TRANSITION,
+                "launcher_in_state_transition");
+        appendFlag(result, flags, FLAG_SCREEN_ON, "screen_on");
+        appendFlag(result, flags, FLAG_LAUNCHER_ACTIVE_AT_SCREEN_OFF,
+                "launcher_active_at_screen_off");
+        return result.toString();
     }
 
     protected void dumpLogs(String prefix, PrintWriter pw) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 115b99e..0ed4538 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -65,6 +65,7 @@
 
 /**
  * Implements interfaces required to show and allow interacting with a PopupContainerWithArrow.
+ * Controls the long-press menu on Taskbar and AllApps icons.
  */
 public class TaskbarPopupController implements TaskbarControllers.LoggableTaskbarController {
 
@@ -174,6 +175,7 @@
                     deepShortcutCount,
                     mPopupDataProvider.getNotificationKeysForItem(item),
                     systemShortcuts);
+            icon.clearAccessibilityFocus();
         }
 
         container.addOnAttachStateChangeListener(
@@ -190,9 +192,8 @@
 
         // Make focusable to receive back events
         context.onPopupVisibilityChanged(true);
-        container.setOnCloseCallback(() -> {
+        container.addOnCloseCallback(() -> {
             context.getDragLayer().post(() -> context.onPopupVisibilityChanged(false));
-            container.setOnCloseCallback(null);
         });
 
         return container;
@@ -293,13 +294,19 @@
 
         @Override
         public void onClick(View view) {
+            // Add callbacks depending on what type of Taskbar context we're in (Taskbar or AllApps)
+            mTarget.onSplitScreenMenuButtonClicked();
             AbstractFloatingView.closeAllOpenViews(mTarget);
+
+            // Depending on what app state we're in, we either want to initiate the split screen
+            // staging process or immediately launch a split with an existing app.
+            // - Initiate the split screen staging process
              if (mAllowInitialSplitSelection) {
                  super.onClick(view);
                  return;
              }
 
-            // Initiate splitscreen from the in-app Taskbar or Taskbar All Apps
+            // - Immediately launch split with the running app
             Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
                     LogUtils.getShellShareableInstanceId();
             mTarget.getStatsLogManager().logger()
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
new file mode 100644
index 0000000..d65b5c0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 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.taskbar;
+
+import static com.android.launcher3.anim.AnimatedFloat.VALUE;
+
+import android.animation.ValueAnimator;
+
+import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController;
+import com.android.launcher3.util.DisplayController;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages the spring animation when stashing the transient taskbar.
+ */
+public class TaskbarSpringOnStashController implements LoggableTaskbarController {
+
+    private final TaskbarActivityContext mContext;
+    private TaskbarControllers mControllers;
+    private final AnimatedFloat mTranslationForStash = new AnimatedFloat(
+            this::updateTranslationYForStash);
+
+    private final boolean mIsTransientTaskbar;
+
+    private final float mStartVelocityPxPerS;
+
+    public TaskbarSpringOnStashController(TaskbarActivityContext context) {
+        mContext = context;
+        mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+        mStartVelocityPxPerS = context.getResources()
+                .getDimension(R.dimen.transient_taskbar_stash_spring_velocity_dp_per_s);
+    }
+
+    /**
+     * Initialization method.
+     */
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
+    private void updateTranslationYForStash() {
+        if (!mIsTransientTaskbar) {
+            return;
+        }
+
+        float transY = mTranslationForStash.value;
+        mControllers.stashedHandleViewController.setTranslationYForStash(transY);
+        mControllers.taskbarViewController.setTranslationYForStash(transY);
+        mControllers.taskbarDragLayerController.setTranslationYForStash(transY);
+    }
+
+    /**
+     * Returns a spring animation to be used when stashing the transient taskbar.
+     */
+    public @Nullable ValueAnimator createSpringToStash() {
+        if (!mIsTransientTaskbar) {
+            return null;
+        }
+        return new SpringAnimationBuilder(mContext)
+                .setStartValue(mTranslationForStash.value)
+                .setEndValue(0)
+                .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
+                .setStiffness(SpringForce.STIFFNESS_LOW)
+                .setStartVelocity(mStartVelocityPxPerS)
+                .build(mTranslationForStash, VALUE);
+    }
+
+
+    @Override
+    public void dumpLogs(String prefix, PrintWriter pw) {
+        pw.println(prefix + "TaskbarSpringOnStashController:");
+
+        pw.println(prefix + "\tmTranslationForStash=" + mTranslationForStash.value);
+        pw.println(prefix + "\tmStartVelocityPxPerS=" + mStartVelocityPxPerS);
+    }
+}
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index cbc1672..b613763 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -81,7 +81,7 @@
 
     public static final int FLAG_IN_APP = 1 << 0;
     public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
-    public static final int FLAG_STASHED_IN_SYSUI_STATE = 1 << 2; // app pinning, keyguard, etc.
+    public static final int FLAG_STASHED_IN_APP_SYSUI = 1 << 2; // shade open, ...
     public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 3; // setup wizard and AllSetActivity
     public static final int FLAG_STASHED_IN_APP_IME = 1 << 4; // IME is visible
     public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 5;
@@ -89,13 +89,14 @@
     public static final int FLAG_IN_SETUP = 1 << 7; // In the Setup Wizard
     public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 8; // phone screen gesture nav, stashed
     public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 9; // Autohide (transient taskbar).
+    public static final int FLAG_STASHED_SYSUI = 1 << 10; //  app pinning, keyguard, etc.
 
     // If any of these flags are enabled, isInApp should return true.
     private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
 
     // If we're in an app and any of these flags are enabled, taskbar should be stashed.
     private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
-            | FLAG_STASHED_IN_SYSUI_STATE | FLAG_STASHED_IN_APP_SETUP
+            | FLAG_STASHED_IN_APP_SYSUI | FLAG_STASHED_IN_APP_SETUP
             | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS
             | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO;
 
@@ -218,11 +219,11 @@
                 boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
                 boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
                 boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
-                boolean stashedInTaskbarAllApps =
-                        hasAnyFlag(flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS);
-                boolean stashedForSmallScreen = hasAnyFlag(flags, FLAG_STASHED_SMALL_SCREEN);
-                return (inApp && stashedInApp) || (!inApp && stashedLauncherState)
-                        || stashedInTaskbarAllApps || stashedForSmallScreen;
+                boolean forceStashed = hasAnyFlag(flags,
+                        FLAG_STASHED_SYSUI
+                                | FLAG_STASHED_IN_TASKBAR_ALL_APPS
+                                | FLAG_STASHED_SMALL_SCREEN);
+                return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed;
             });
 
     public TaskbarStashController(TaskbarActivityContext activity) {
@@ -712,6 +713,9 @@
             play(as, mTaskbarStashedHandleAlpha.animateToValue(1), alphaStartDelay,
                     Math.max(0, duration - alphaStartDelay), LINEAR);
 
+            play(as, mControllers.taskbarSpringOnStashController.createSpringToStash(), 0, duration,
+                    LINEAR);
+
             if (skipStashAnimation) {
                 skipInterpolator = INSTANT;
             }
@@ -898,13 +902,14 @@
         long animDuration = TASKBAR_STASH_DURATION;
         long startDelay = 0;
 
-        updateStateForFlag(FLAG_STASHED_IN_SYSUI_STATE, hasAnyFlag(systemUiStateFlags,
+        updateStateForFlag(FLAG_STASHED_IN_APP_SYSUI, hasAnyFlag(systemUiStateFlags,
+                SYSUI_STATE_QUICK_SETTINGS_EXPANDED
+                        | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED));
+        updateStateForFlag(FLAG_STASHED_SYSUI, hasAnyFlag(systemUiStateFlags,
                 SYSUI_STATE_SCREEN_PINNING
                         | SYSUI_STATE_BOUNCER_SHOWING
                         | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
-                        | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
-                        | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
-                        | SYSUI_STATE_QUICK_SETTINGS_EXPANDED));
+                        | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED));
 
         // Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
         mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING);
@@ -1068,13 +1073,14 @@
         StringJoiner sj = new StringJoiner("|");
         appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP");
         appendFlag(sj, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL");
-        appendFlag(sj, flags, FLAG_STASHED_IN_SYSUI_STATE, "FLAG_STASHED_IN_SYSUI_STATE");
+        appendFlag(sj, flags, FLAG_STASHED_IN_APP_SYSUI, "FLAG_STASHED_IN_APP_SYSUI");
         appendFlag(sj, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP");
         appendFlag(sj, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME");
         appendFlag(sj, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE");
         appendFlag(sj, flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS, "FLAG_STASHED_IN_TASKBAR_ALL_APPS");
         appendFlag(sj, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP");
         appendFlag(sj, flags, FLAG_STASHED_IN_APP_AUTO, "FLAG_STASHED_IN_APP_AUTO");
+        appendFlag(sj, flags, FLAG_STASHED_SYSUI, "FLAG_STASHED_SYSUI");
         return sj.toString();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 062b4ce..dd88a37 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -138,7 +138,7 @@
      * Returns an animation to reset the taskbar translation to {@code 0}.
      */
     public ObjectAnimator createAnimToResetTranslation(long duration) {
-        ObjectAnimator animator = ObjectAnimator.ofFloat(mTranslationYForSwipe, VALUE, 0);
+        ObjectAnimator animator = mTranslationYForSwipe.animateToValue(0);
         animator.setInterpolator(Interpolators.LINEAR);
         animator.setDuration(duration);
         animator.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 4c6d3fa..8046cd6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -125,6 +125,12 @@
     }
 
     /**
+     * Screen state changed, see QuickStepContract.SCREEN_STATE_* values.
+     */
+    public void onChangeScreenState(int screenState){
+    }
+
+    /**
      * Returns {@code true} iff taskbar is stashed.
      */
     public boolean isTaskbarStashed() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 0116e16..c5b6cdf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -47,6 +47,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.DoubleShadowBubbleTextView;
 import com.android.launcher3.views.IconButtonView;
@@ -66,6 +67,7 @@
     private final int mIconTouchSize;
     private final int mItemMarginLeftRight;
     private final int mItemPadding;
+    private final int mFolderLeaveBehindColor;
     private final boolean mIsRtl;
 
     private final TaskbarActivityContext mActivityContext;
@@ -132,6 +134,9 @@
         mItemMarginLeftRight = actualMargin - (mIconTouchSize - actualIconSize) / 2;
         mItemPadding = (mIconTouchSize - actualIconSize) / 2;
 
+        mFolderLeaveBehindColor = Themes.getAttrColor(mActivityContext,
+                android.R.attr.textColorTertiary);
+
         // Needed to draw folder leave-behind when opening one.
         setWillNotDraw(false);
 
@@ -523,7 +528,8 @@
         if (mLeaveBehindFolderIcon != null) {
             canvas.save();
             canvas.translate(mLeaveBehindFolderIcon.getLeft(), mLeaveBehindFolderIcon.getTop());
-            mLeaveBehindFolderIcon.getFolderBackground().drawLeaveBehind(canvas);
+            mLeaveBehindFolderIcon.getFolderBackground().drawLeaveBehind(canvas,
+                    mFolderLeaveBehindColor);
             canvas.restore();
         }
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 4d92a9e..ad16eed 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -103,6 +103,7 @@
     private AnimatedFloat mTaskbarNavButtonTranslationY;
     private AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay;
     private float mTaskbarIconTranslationYForSwipe;
+    private float mTaskbarIconTranslationYForSpringOnStash;
 
     private final int mTaskbarBottomMargin;
     private final int mStashedHandleHeight;
@@ -291,10 +292,19 @@
         updateTranslationY();
     }
 
+    /**
+     * Sets the translation of the TaskbarView during the spring on stash animation.
+     */
+    public void setTranslationYForStash(float transY) {
+        mTaskbarIconTranslationYForSpringOnStash = transY;
+        updateTranslationY();
+    }
+
     private void updateTranslationY() {
         mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value
                 + mTaskbarIconTranslationYForStash.value
-                + mTaskbarIconTranslationYForSwipe);
+                + mTaskbarIconTranslationYForSwipe
+                + mTaskbarIconTranslationYForSpringOnStash);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 3edb375..66d5918 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.taskbar.overlay;
 
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-
 import android.content.Context;
 import android.view.View;
 
@@ -33,10 +31,6 @@
 import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.function.Consumer;
 
 /**
  * Window context for the taskbar overlays such as All Apps and EDU.
@@ -141,4 +135,8 @@
 
     @Override
     public void onPopupVisibilityChanged(boolean isVisible) {}
+
+    @Override
+    public void onSplitScreenMenuButtonClicked() {
+    }
 }
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
index 4cca24e..172cb46 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
@@ -50,6 +50,7 @@
     @Mock lateinit var voiceInteractionWindowController: VoiceInteractionWindowController
     @Mock lateinit var taskbarRecentAppsController: TaskbarRecentAppsController
     @Mock lateinit var taskbarTranslationController: TaskbarTranslationController
+    @Mock lateinit var taskbarSpringOnStashController: TaskbarSpringOnStashController
     @Mock lateinit var taskbarOverlayController: TaskbarOverlayController
     @Mock lateinit var taskbarEduTooltipController: TaskbarEduTooltipController
     @Mock lateinit var keyboardQuickSwitchController: KeyboardQuickSwitchController
@@ -89,6 +90,7 @@
                 taskbarInsetsController,
                 voiceInteractionWindowController,
                 taskbarTranslationController,
+                taskbarSpringOnStashController,
                 taskbarRecentAppsController,
                 taskbarEduTooltipController,
                 keyboardQuickSwitchController
diff --git a/res/layout/widgets_full_sheet_large_screen.xml b/res/layout/widgets_full_sheet_large_screen.xml
index b99ac5c..808d3eb 100644
--- a/res/layout/widgets_full_sheet_large_screen.xml
+++ b/res/layout/widgets_full_sheet_large_screen.xml
@@ -20,68 +20,89 @@
     android:orientation="vertical"
     android:theme="?attr/widgetsTheme">
 
-    <androidx.constraintlayout.widget.ConstraintLayout
+    <com.android.launcher3.views.SpringRelativeLayout
         android:id="@+id/container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:focusable="true"
         android:importantForAccessibility="no">
 
-        <FrameLayout
-            android:id="@+id/recycler_view_container"
-            android:layout_width="0dp"
-            android:layout_height="0dp"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/title"
-            app:layout_constraintWidth_percent="0.33">
+        <View
+            android:id="@+id/collapse_handle"
+            android:layout_width="@dimen/bottom_sheet_handle_width"
+            android:layout_height="@dimen/bottom_sheet_handle_height"
+            android:layout_marginTop="@dimen/bottom_sheet_handle_margin"
+            android:layout_centerHorizontal="true"
+            android:background="@drawable/bg_rounded_corner_bottom_sheet_handle"/>
 
-            <TextView
-                android:id="@+id/fast_scroller_popup"
-                style="@style/FastScrollerPopup"
-                android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="24dp"
+            android:gravity="center_horizontal"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+            android:text="@string/widget_button_text"
+            android:textColor="?android:attr/textColorSecondary"
+            android:textSize="24sp" />
 
-            <!-- Fast scroller popup -->
-            <com.android.launcher3.views.RecyclerViewFastScroller
-                android:id="@+id/fast_scroller"
-                android:layout_width="@dimen/fastscroll_width"
+        <LinearLayout
+            android:id="@+id/linear_layout_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_below="@id/title">
+
+            <FrameLayout
+                android:id="@+id/recycler_view_container"
+                android:layout_width="0dp"
                 android:layout_height="match_parent"
-                android:layout_marginEnd="@dimen/fastscroll_end_margin" />
+                android:layout_gravity="start"
+                android:layout_weight="0.33">
+                <TextView
+                    android:id="@+id/fast_scroller_popup"
+                    style="@style/FastScrollerPopup"
+                    android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
 
-            <com.android.launcher3.widget.picker.WidgetsRecyclerView
-                android:id="@+id/search_widgets_list_view"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:clipToPadding="false"
-                android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
-                android:visibility="gone" />
-        </FrameLayout>
+                <!-- Fast scroller popup -->
+                <com.android.launcher3.views.RecyclerViewFastScroller
+                    android:id="@+id/fast_scroller"
+                    android:layout_width="@dimen/fastscroll_width"
+                    android:layout_height="match_parent"
+                    android:layout_marginEnd="@dimen/fastscroll_end_margin" />
 
-        <FrameLayout
-            android:layout_width="0dp"
-            android:layout_height="0dp"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toEndOf="@id/recycler_view_container"
-            app:layout_constraintTop_toBottomOf="@id/title"
-            android:paddingEnd="16dp"
-            android:paddingStart="8dp"
-            android:layout_marginTop="26dp"
-            app:layout_constraintWidth_percent="0.67"
-            app:layout_constraintBottom_toBottomOf="parent"
-            android:orientation="horizontal">
-            <TextView
-                android:id="@+id/no_widgets_text"
-                style="@style/PrimaryHeadline"
-                android:layout_width="match_parent"
+                <com.android.launcher3.widget.picker.WidgetsRecyclerView
+                    android:id="@+id/search_widgets_list_view"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:clipToPadding="false"
+                    android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+                    android:visibility="gone" />
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/right_pane_container"
+                android:layout_width="0dp"
                 android:layout_height="match_parent"
-                android:gravity="center"
-                android:textSize="18sp"
-                android:visibility="gone"
-                tools:text="No widgets available" />
-            <ScrollView
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:id="@+id/right_pane">
+                android:layout_weight="0.67"
+                android:paddingEnd="16dp"
+                android:paddingTop="24dp"
+                android:gravity="end"
+                android:layout_gravity="end"
+                android:paddingStart="8dp"
+                android:orientation="horizontal">
+                <TextView
+                    android:id="@+id/no_widgets_text"
+                    style="@style/PrimaryHeadline"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:gravity="center"
+                    android:textSize="18sp"
+                    android:visibility="gone"
+                    tools:text="No widgets available" />
+                <ScrollView
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:id="@+id/right_pane">
                     <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
                         android:id="@+id/recommended_widget_table"
                         android:background="@drawable/widgets_surface_background"
@@ -91,32 +112,8 @@
                             "@dimen/widget_list_horizontal_margin_large_screen"
                         android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
                         android:visibility="gone" />
-            </ScrollView>
-        </FrameLayout>
-
-        <View
-            android:id="@+id/collapse_handle"
-            android:layout_width="@dimen/bottom_sheet_handle_width"
-            android:layout_height="@dimen/bottom_sheet_handle_height"
-            android:layout_marginTop="@dimen/bottom_sheet_handle_margin"
-            android:background="@drawable/bg_rounded_corner_bottom_sheet_handle"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent" />
-
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/collapse_handle"
-            android:layout_marginTop="24dp"
-            android:gravity="center_horizontal"
-            android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
-            android:text="@string/widget_button_text"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textSize="24sp" />
-
-    </androidx.constraintlayout.widget.ConstraintLayout>
+                </ScrollView>
+            </FrameLayout>
+        </LinearLayout>
+    </com.android.launcher3.views.SpringRelativeLayout>
 </com.android.launcher3.widget.picker.WidgetsFullSheet>
diff --git a/res/layout/widgets_full_sheet_paged_view_large_screen.xml b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
index edee352..f729981 100644
--- a/res/layout/widgets_full_sheet_paged_view_large_screen.xml
+++ b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
@@ -18,13 +18,11 @@
 
     <FrameLayout
         android:id="@+id/widgets_full_sheet_paged_view_large_screen"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        app:layout_constraintEnd_toStartOf="@id/scrollView"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/title"
-        app:layout_constraintWidth_percent="0.33">
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="start"
+        android:layout_gravity="start"
+        android:layout_alignParentStart="true">
         <com.android.launcher3.widget.picker.WidgetPagedView
             android:id="@+id/widgets_view_pager"
             android:layout_width="match_parent"
diff --git a/res/layout/widgets_full_sheet_recyclerview_large_screen.xml b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
index c6a4f62..8fef303 100644
--- a/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
+++ b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
@@ -18,13 +18,11 @@
 
     <FrameLayout
         android:id="@+id/widgets_full_sheet_recyclerview_large_screen"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        app:layout_constraintEnd_toStartOf="@id/scrollView"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/title"
-        app:layout_constraintWidth_percent="0.33">
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="start"
+        android:layout_gravity="start"
+        android:layout_alignParentStart="true">
 
         <com.android.launcher3.widget.picker.WidgetsRecyclerView
             android:id="@+id/primary_widgets_list_view"
@@ -65,4 +63,4 @@
             </LinearLayout>
         </com.android.launcher3.views.StickyHeaderLayout>
     </FrameLayout>
-</merge>
+</merge>
\ No newline at end of file
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 38b0e08..3bda94b 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -92,6 +92,9 @@
     private static final String TAG = "CellLayout";
     private static final boolean LOGD = false;
 
+    /** The color of the "leave-behind" shape when a folder is opened from Hotseat. */
+    private static final int FOLDER_LEAVE_BEHIND_COLOR = Color.argb(160, 245, 245, 245);
+
     protected final ActivityContext mActivity;
     @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk int mCellWidth;
@@ -100,7 +103,7 @@
     private int mFixedCellWidth;
     private int mFixedCellHeight;
     @ViewDebug.ExportedProperty(category = "launcher")
-    private Point mBorderSpace;
+    protected Point mBorderSpace;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mCountX;
@@ -528,7 +531,7 @@
                     mFolderLeaveBehind.mDelegateCellY, mTempLocation);
             canvas.save();
             canvas.translate(mTempLocation[0], mTempLocation[1]);
-            mFolderLeaveBehind.drawLeaveBehind(canvas);
+            mFolderLeaveBehind.drawLeaveBehind(canvas, FOLDER_LEAVE_BEHIND_COLOR);
             canvas.restore();
         }
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index c81ad01..abf5866 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -31,7 +31,11 @@
 import android.content.pm.LauncherApps;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.SparseArray;
+import android.widget.RemoteViews;
 
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.config.FeatureFlags;
@@ -70,6 +74,12 @@
     private final InvariantDeviceProfile mInvariantDeviceProfile;
     private final RunnableList mOnTerminateCallback = new RunnableList();
 
+    // WORKAROUND: b/269335387 remove this after widget background listener is enabled
+    /* Array of RemoteViews cached by Launcher process */
+    @GuardedBy("itself")
+    @NonNull
+    public final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();
+
     public static LauncherAppState getInstance(final Context context) {
         return INSTANCE.get(context);
     }
diff --git a/src/com/android/launcher3/MultipageCellLayout.java b/src/com/android/launcher3/MultipageCellLayout.java
index 12cb35d..20e9f60 100644
--- a/src/com/android/launcher3/MultipageCellLayout.java
+++ b/src/com/android/launcher3/MultipageCellLayout.java
@@ -95,6 +95,13 @@
     }
 
     @Override
+    public int getUnusedHorizontalSpace() {
+        return (int) Math.ceil(
+                (getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth)
+                        - ((mCountX - 1) * mBorderSpace.x)) / 2f);
+    }
+
+    @Override
     protected void onDraw(Canvas canvas) {
         if (mLeftBackground.getAlpha() > 0) {
             mLeftBackground.setState(mBackground.getState());
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 3c316b8..27119ae 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -174,7 +174,7 @@
             mContext.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
             ArrowPopup popup = OptionsPopupView.show(mContext, new RectF(pos), actions, false);
             popup.requestFocus();
-            popup.setOnCloseCallback(() -> {
+            popup.addOnCloseCallback(() -> {
                 host.requestFocus();
                 host.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
                 host.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 38f33de..43f2329 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -351,7 +351,7 @@
             "SHOW_DOT_PAGINATION", true, "Enable showing dot pagination in workspace");
 
     public static final BooleanFlag LARGE_SCREEN_WIDGET_PICKER = getDebugFlag(270395809,
-            "LARGE_SCREEN_WIDGET_PICKER", false, "Enable new widget picker that takes "
+            "LARGE_SCREEN_WIDGET_PICKER", true, "Enable new widget picker that takes "
                     + "advantage of large screen format");
 
     public static final BooleanFlag ENABLE_NEW_GESTURE_NAV_TUTORIAL = getDebugFlag(270396257,
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 8f9fa8a..2465745 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -333,12 +333,15 @@
                 getOffsetX() + inset, getOffsetY() + inset, getScaledRadius() - inset, mPaint);
     }
 
-    public void drawLeaveBehind(Canvas canvas) {
+    /**
+     * Draws the leave-behind circle on the given canvas and in the given color.
+     */
+    public void drawLeaveBehind(Canvas canvas, int color) {
         float originalScale = mScale;
         mScale = 0.5f;
 
         mPaint.setStyle(Paint.Style.FILL);
-        mPaint.setColor(Color.argb(160, 245, 245, 245));
+        mPaint.setColor(color);
         getShape().drawShape(canvas, getOffsetX(), getOffsetY(), getScaledRadius(), mPaint);
 
         mScale = originalScale;
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index be3a09b..9d06d4a 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -48,14 +48,13 @@
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
@@ -124,7 +123,7 @@
     private final GradientDrawable mRoundedTop;
     private final GradientDrawable mRoundedBottom;
 
-    @Nullable private Runnable mOnCloseCallback = null;
+    private RunnableList mOnCloseCallbacks = new RunnableList();
 
     // The rect string of the view that the arrow is attached to, in screen reference frame.
     protected int mArrowColor;
@@ -785,16 +784,14 @@
         mDeferContainerRemoval = false;
         getPopupContainer().removeView(this);
         getPopupContainer().removeView(mArrow);
-        if (mOnCloseCallback != null) {
-            mOnCloseCallback.run();
-        }
+        mOnCloseCallbacks.executeAllAndClear();
     }
 
     /**
-     * Callback to be called when the popup is closed
+     * Callbacks to be called when the popup is closed
      */
-    public void setOnCloseCallback(@Nullable Runnable callback) {
-        mOnCloseCallback = callback;
+    public void addOnCloseCallback(Runnable callback) {
+        mOnCloseCallbacks.add(callback);
     }
 
     protected BaseDragLayer getPopupContainer() {
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 0b756b6..e62ccbc 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.testing;
 
 import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
+import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.annotation.TargetApi;
@@ -156,7 +157,7 @@
 
             case TestProtocol.REQUEST_IS_TWO_PANELS:
                 response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
-                        mDeviceProfile.isTwoPanels);
+                        FOLDABLE_SINGLE_PAGE.get() ? false : mDeviceProfile.isTwoPanels);
                 return response;
 
             case TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS:
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index 8e67eb1..2ca825c 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -36,6 +36,7 @@
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
@@ -74,8 +75,6 @@
     private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
     @NonNull
     private final SparseArray<LauncherAppWidgetHostView> mDeferredViews = new SparseArray<>();
-    @NonNull
-    private final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();
 
     protected int mFlags = FLAG_STATE_IS_NORMAL;
 
@@ -174,6 +173,12 @@
     public void deleteAppWidgetId(int appWidgetId) {
         mWidgetHost.deleteAppWidgetId(appWidgetId);
         mViews.remove(appWidgetId);
+        if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+            final LauncherAppState state = LauncherAppState.getInstance(mContext);
+            synchronized (state.mCachedRemoteViews) {
+                state.mCachedRemoteViews.delete(appWidgetId);
+            }
+        }
     }
 
     /**
@@ -308,7 +313,17 @@
         if (WidgetsModel.GO_DISABLE_WIDGETS) {
             return;
         }
-
+        if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+            // Cache the content from the widgets when Launcher stops listening to widget updates
+            final LauncherAppState state = LauncherAppState.getInstance(mContext);
+            synchronized (state.mCachedRemoteViews) {
+                for (int i = 0; i < mViews.size(); i++) {
+                    final int appWidgetId = mViews.keyAt(i);
+                    final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
+                    state.mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
+                }
+            }
+        }
         mWidgetHost.stopListening();
         setListeningFlag(false);
     }
@@ -350,23 +365,24 @@
             // RemoteViews from system process.
             // TODO: have launcher always listens to widget updates in background so that this
             //  check can be removed altogether.
-            if (FeatureFlags.ENABLE_CACHED_WIDGET.get()
-                    && mCachedRemoteViews.get(appWidgetId) != null) {
-                // We've found RemoteViews from cache for this widget, so we will instantiate a
-                // widget host view and populate it with the cached RemoteViews.
-                final LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
-                view.setAppWidget(appWidgetId, appWidget);
-                view.updateAppWidget(mCachedRemoteViews.get(appWidgetId));
-                mDeferredViews.put(appWidgetId, view);
-                mViews.put(appWidgetId, view);
-                return view;
-            } else {
-                // When cache misses, a placeholder for the widget will be returned instead.
-                DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
-                view.setAppWidget(appWidgetId, appWidget);
-                mViews.put(appWidgetId, view);
-                return view;
+            if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+                final RemoteViews cachedRemoteViews = getCachedRemoteViews(appWidgetId);
+                if (cachedRemoteViews != null) {
+                    // We've found RemoteViews from cache for this widget, so we will instantiate a
+                    // widget host view and populate it with the cached RemoteViews.
+                    final LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+                    view.setAppWidget(appWidgetId, appWidget);
+                    view.updateAppWidget(cachedRemoteViews);
+                    mDeferredViews.put(appWidgetId, view);
+                    mViews.put(appWidgetId, view);
+                    return view;
+                }
             }
+            // If cache misses or not enabled, a placeholder for the widget will be returned.
+            DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
+            view.setAppWidget(appWidgetId, appWidget);
+            mViews.put(appWidgetId, view);
+            return view;
         } else {
             try {
                 return mWidgetHost.createView(context, appWidgetId, appWidget);
@@ -432,15 +448,8 @@
         LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost;
         tempHost.clearViews();
         if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
-            // First, we clear any previously cached content from existing widgets
-            mCachedRemoteViews.clear();
+            // Clear previously cached content from existing widgets
             mDeferredViews.clear();
-            // Then we proceed to cache the content from the widgets
-            for (int i = 0; i < mViews.size(); i++) {
-                final int appWidgetId = mViews.keyAt(i);
-                final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
-                mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
-            }
         }
         mViews.clear();
     }
@@ -481,6 +490,14 @@
         return (flags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN;
     }
 
+    @Nullable
+    private RemoteViews getCachedRemoteViews(int appWidgetId) {
+        final LauncherAppState state = LauncherAppState.getInstance(mContext);
+        synchronized (state.mCachedRemoteViews) {
+            return state.mCachedRemoteViews.get(appWidgetId);
+        }
+    }
+
     /**
      * Returns the new LauncherWidgetHolder instance
      */
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 0b2f5a5..d3be00c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -204,6 +204,7 @@
                 && mDeviceProfile.isLandscape
                 && LARGE_SCREEN_WIDGET_PICKER.get();
         mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
+        mOrientation = Launcher.getLauncher(context).getOrientation();
         mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
         mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
         mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
@@ -232,13 +233,22 @@
         mContent.setClipToOutline(true);
 
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
-        int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
-                : R.layout.widgets_full_sheet_recyclerview;
+
         if (mIsTwoPane) {
-            contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view_large_screen
-                    : R.layout.widgets_full_sheet_recyclerview_large_screen;
+            layoutInflater.inflate(
+                    mHasWorkProfile
+                           ? R.layout.widgets_full_sheet_paged_view_large_screen
+                           : R.layout.widgets_full_sheet_recyclerview_large_screen,
+                    findViewById(R.id.recycler_view_container),
+                    true);
+        } else {
+            layoutInflater.inflate(
+                    mHasWorkProfile
+                            ? R.layout.widgets_full_sheet_paged_view
+                            : R.layout.widgets_full_sheet_recyclerview,
+                    mContent,
+                    true);
         }
-        layoutInflater.inflate(contentLayoutRes, mContent, true);
 
         mFastScroller = findViewById(R.id.fast_scroller);
         if (mIsTwoPane) {
@@ -349,7 +359,8 @@
 
         // if the current active page changes to personal or work we set suggestions
         // to be the selected widget
-        if (mIsTwoPane && (currentActivePage == PERSONAL_TAB || currentActivePage == WORK_TAB)) {
+        if (mIsTwoPane && mSuggestedWidgetsHeader != null
+                && (currentActivePage == PERSONAL_TAB || currentActivePage == WORK_TAB)) {
             mSuggestedWidgetsHeader.callOnClick();
         }
 
@@ -433,7 +444,9 @@
         super.onAttachedToWindow();
         mActivityContext.getAppWidgetHolder().addProviderChangeListener(this);
         notifyWidgetProvidersChanged();
-        onRecommendedWidgetsBound();
+        if (!mIsTwoPane) {
+            onRecommendedWidgetsBound();
+        }
     }
 
     @Override
@@ -696,6 +709,9 @@
                     recommendedWidgetsInTable, maxTableHeight);
         } else {
             mRecommendedWidgetsTable.setVisibility(GONE);
+            if (mSuggestedWidgetsContainer != null) {
+                mSuggestedWidgetsContainer.setVisibility(GONE);
+            }
         }
     }
 
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
index 1e6737d..2960807 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.celllayout;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+
 import android.view.View;
 
 import com.android.launcher3.CellLayout;
@@ -29,23 +31,31 @@
         ArrayList<CellLayoutBoard> boards = new ArrayList<>();
         int widgetCount = 0;
         for (CellLayout cellLayout : launcher.getWorkspace().mWorkspaceScreens) {
-            CellLayoutBoard board = new CellLayoutBoard();
+
             int count = cellLayout.getShortcutsAndWidgets().getChildCount();
             for (int i = 0; i < count; i++) {
                 View callView = cellLayout.getShortcutsAndWidgets().getChildAt(i);
                 CellLayoutLayoutParams params =
                         (CellLayoutLayoutParams) callView.getLayoutParams();
+
+                CellPosMapper.CellPos pos = launcher.getCellPosMapper().mapPresenterToModel(
+                        params.getCellX(), params.getCellY(),
+                        launcher.getWorkspace().getIdForScreen(cellLayout), CONTAINER_DESKTOP);
+                int screenId = pos.screenId;
+                if (screenId >= boards.size() - 1) {
+                    boards.add(new CellLayoutBoard());
+                }
+                CellLayoutBoard board = boards.get(screenId);
                 // is icon
                 if (callView instanceof DoubleShadowBubbleTextView) {
-                    board.addIcon(params.getCellX(), params.getCellY());
+                    board.addIcon(pos.cellX, pos.cellY);
                 } else {
                     // is widget
-                    board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan,
-                            params.cellVSpan, (char) ('A' + widgetCount));
+                    board.addWidget(pos.cellX, pos.cellY, params.cellHSpan, params.cellVSpan,
+                            (char) ('A' + widgetCount));
                     widgetCount++;
                 }
             }
-            boards.add(board);
         }
         return boards;
     }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 0fccf79..78a006e 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -60,11 +60,11 @@
         final LauncherAppWidgetProviderInfo widgetInfo =
                 TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
 
-        WidgetResizeFrame resizeFrame = mLauncher.
-                getWorkspace().
-                openAllWidgets().
-                getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())).
-                dragWidgetToWorkspace();
+        WidgetResizeFrame resizeFrame = mLauncher
+                .getWorkspace()
+                .openAllWidgets()
+                .getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager()))
+                .dragWidgetToWorkspace();
 
         assertTrue(mActivityMonitor.itemExists(
                 (info, view) -> info instanceof LauncherAppWidgetInfo &&
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 96e2e3f..c1c26ec 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -77,8 +77,8 @@
             mLauncher.scroll(
                     widgetsContainer,
                     Direction.UP,
-                    new Rect(0, 0, mLauncher.getRightGestureMarginInContainer(widgetsContainer) + 1,
-                            0),
+                    new Rect(0, 0, 0,
+                            mLauncher.getBottomGestureMarginInContainer(widgetsContainer) + 1),
                     FLING_STEPS, false);
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
                 verifyActiveContainer();
@@ -168,6 +168,8 @@
     private UiObject2 findTestAppWidgetsTableContainer() {
         final BySelector headerSelector = By.res(mLauncher.getLauncherPackageName(),
                 "widgets_list_header");
+        final BySelector widgetPickerSelector = By.res(mLauncher.getLauncherPackageName(),
+                "container");
         final BySelector targetAppSelector = By.clazz("android.widget.TextView").text(
                 mLauncher.getContext().getPackageName());
         final BySelector widgetsContainerSelector = By.res(mLauncher.getLauncherPackageName(),
@@ -176,17 +178,23 @@
         boolean hasHeaderExpanded = false;
         int scrollDistance = 0;
         for (int i = 0; i < SCROLL_ATTEMPTS; i++) {
-            UiObject2 fullWidgetsPicker = verifyActiveContainer();
-
-            UiObject2 header = mLauncher.waitForObjectInContainer(fullWidgetsPicker,
+            UiObject2 widgetPicker = mLauncher.waitForLauncherObject(widgetPickerSelector);
+            UiObject2 widgetListView = verifyActiveContainer();
+            UiObject2 header = mLauncher.waitForObjectInContainer(widgetListView,
                     headerSelector);
+            // If we are in a tablet in landscape mode then we will have a two pane view and we use
+            // the right pane to display the widgets table.
+            UiObject2 rightPane = mLauncher.findObjectInContainer(
+                    widgetPicker,
+                    widgetsContainerSelector);
+
             // If a header is barely visible in the bottom edge of the screen, its height could be
             // too small for a scroll gesture. Since all header should have roughly the same height,
             // let's pick the max height we have seen so far.
             scrollDistance = Math.max(scrollDistance, header.getVisibleBounds().height());
 
             // Look for a header that has the test app name.
-            UiObject2 headerTitle = mLauncher.findObjectInContainer(fullWidgetsPicker,
+            UiObject2 headerTitle = mLauncher.findObjectInContainer(widgetListView,
                     targetAppSelector);
             if (headerTitle != null) {
                 // If we find the header and it has not been expanded, let's click it to see the
@@ -202,7 +210,8 @@
                 }
 
                 // Look for a widgets list.
-                UiObject2 widgetsContainer = mLauncher.findObjectInContainer(fullWidgetsPicker,
+                UiObject2 widgetsContainer = mLauncher.findObjectInContainer(
+                        rightPane != null ? rightPane : widgetListView,
                         widgetsContainerSelector);
                 if (widgetsContainer != null) {
                     log("Widgets container found.");
@@ -210,7 +219,9 @@
                 }
             }
             log("Finding test widget package - scroll with distance: " + scrollDistance);
-            mLauncher.scrollDownByDistance(fullWidgetsPicker, scrollDistance);
+            mLauncher.scrollDownByDistance(hasHeaderExpanded && rightPane != null
+                    ? rightPane
+                    : widgetListView, scrollDistance);
         }
 
         return null;