Merge "Fix failing DeleteDropTargetTest" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 874f862..7c6d7a8 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -32,7 +32,7 @@
     name: "enable_responsive_workspace"
     namespace: "launcher"
     description: "Enables new workspace grid calculations method."
-    bug: "241386436"
+    bug: "302189128"
 }
 
 flag {
@@ -41,3 +41,10 @@
     description: "Enable updated overview icon and menu within task."
     bug: "257950105"
 }
+
+flag {
+    name: "enable_taskbar_no_recreate"
+    namespace: "launcher"
+    description: "Enables taskbar with no recreation from lifecycle changes of TaskbarActivityContext."
+    bug: "299193589"
+}
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 6d958ed..82f8ebe 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -30,7 +30,9 @@
     <uses-permission android:name="android.permission.REMOVE_TASKS"/>
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
+    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
     <uses-permission android:name="android.permission.STATUS_BAR"/>
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
     <uses-permission android:name="android.permission.SET_ORIENTATION"/>
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index ee594c8..2b17b93 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -84,4 +84,5 @@
 
     <dimen name="keyboard_quick_switch_taskview_width">205dp</dimen>
     <dimen name="keyboard_quick_switch_taskview_height">119dp</dimen>
+
 </resources>
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 9cb3fec..5e9a177 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -27,4 +27,10 @@
     <dimen name="gesture_tutorial_menu_done_button_top_spacing">40dp</dimen>
     <dimen name="gesture_tutorial_menu_back_shape_bottom_margin">49dp</dimen>
 
+    <!-- Grid Only Overview -->
+    <!-- The top margin above the top row of tasks in grid only overview -->
+    <dimen name="overview_top_margin_grid_only">24dp</dimen>
+    <!-- The bottom margin above the bottom row of tasks in grid only overview -->
+    <dimen name="overview_bottom_margin_grid_only">40dp</dimen>
+
 </resources>
diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
index daf1f63..f9528b3 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -33,6 +33,10 @@
     <dimen name="overview_page_spacing">36dp</dimen>
     <!--  The space to the left and to the right of the "Clear all" button  -->
     <dimen name="overview_grid_side_margin">64dp</dimen>
+    <!-- The top margin above the top row of tasks in grid only overview -->
+    <dimen name="overview_top_margin_grid_only">80dp</dimen>
+    <!-- The bottom margin above the bottom row of tasks in grid only overview -->
+    <dimen name="overview_bottom_margin_grid_only">80dp</dimen>
     <!--  Overview actions  -->
     <dimen name="overview_actions_top_margin">24dp</dimen>
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index d4d6b26..6ee151b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -20,6 +20,7 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
 
@@ -29,8 +30,8 @@
 import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.Utilities.calculateTextHeight;
 import static com.android.launcher3.Utilities.isRunningInTestHarness;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NO_RECREATION;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
+import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
@@ -128,6 +129,7 @@
 import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.Optional;
+import java.util.function.Consumer;
 
 /**
  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
@@ -173,13 +175,16 @@
 
     private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
 
+    private DeviceProfile mTransientTaskbarDeviceProfile;
+
+    private DeviceProfile mPersistentTaskbarDeviceProfile;
+
     public TaskbarActivityContext(Context windowContext, DeviceProfile launcherDp,
             TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
             unfoldTransitionProgressProvider) {
         super(windowContext);
 
         applyDeviceProfile(launcherDp);
-
         final Resources resources = getResources();
 
         mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
@@ -254,9 +259,9 @@
                 new TaskbarViewController(this, taskbarView),
                 new TaskbarScrimViewController(this, taskbarScrimView),
                 new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
-                    mWindowManager,
-                    new RotationChangeProvider(c.getSystemService(DisplayManager.class), this,
-                        getMainThreadHandler())),
+                        mWindowManager,
+                        new RotationChangeProvider(c.getSystemService(DisplayManager.class), this,
+                                getMainThreadHandler())),
                 new TaskbarKeyguardController(this),
                 new StashedHandleViewController(this, stashedHandleView),
                 new TaskbarStashController(this),
@@ -274,7 +279,7 @@
                         : TaskbarRecentAppsController.DEFAULT,
                 new TaskbarEduTooltipController(this),
                 new KeyboardQuickSwitchController(),
-                new TaskbarDividerPopupController(this),
+                new TaskbarPinningController(this),
                 bubbleControllersOptional);
     }
 
@@ -295,17 +300,34 @@
      * the icon size
      */
     private void applyDeviceProfile(DeviceProfile originDeviceProfile) {
-        mDeviceProfile = originDeviceProfile.toBuilder(this)
-                .withDimensionsOverride(deviceProfile -> {
-                    // Taskbar should match the number of icons of hotseat
-                    deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons;
-                    // Same QSB width to have a smooth animation
-                    deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth;
+        Consumer<DeviceProfile> overrideProvider = deviceProfile -> {
+            // Taskbar should match the number of icons of hotseat
+            deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons;
+            // Same QSB width to have a smooth animation
+            deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth;
 
-                    // Update icon size
-                    deviceProfile.iconSizePx = deviceProfile.taskbarIconSize;
-                    deviceProfile.updateIconSize(1f, getResources());
-                }).build();
+            // Update icon size
+            deviceProfile.iconSizePx = deviceProfile.taskbarIconSize;
+            deviceProfile.updateIconSize(1f, getResources());
+        };
+        mDeviceProfile = originDeviceProfile.toBuilder(this)
+                .withDimensionsOverride(overrideProvider).build();
+
+        if (DisplayController.isTransientTaskbar(this)) {
+            mTransientTaskbarDeviceProfile = mDeviceProfile;
+            mPersistentTaskbarDeviceProfile = mDeviceProfile
+                    .toBuilder(this)
+                    .withDimensionsOverride(overrideProvider)
+                    .setIsTransientTaskbar(false)
+                    .build();
+        } else {
+            mPersistentTaskbarDeviceProfile = mDeviceProfile;
+            mTransientTaskbarDeviceProfile = mDeviceProfile
+                    .toBuilder(this)
+                    .withDimensionsOverride(overrideProvider)
+                    .setIsTransientTaskbar(true)
+                    .build();
+        }
         mNavMode = DisplayController.getNavigationMode(this);
     }
 
@@ -339,7 +361,7 @@
             mIsDestroyed = false;
         }
 
-        if (!ENABLE_TASKBAR_NO_RECREATION.get() && !mAddedWindow) {
+        if (!enableTaskbarNoRecreate() && !mAddedWindow) {
             mWindowManager.addView(mDragLayer, mWindowLayoutParams);
             mAddedWindow = true;
         } else {
@@ -391,7 +413,8 @@
 
     /**
      * Creates LayoutParams for adding a view directly to WindowManager as a new window.
-     * @param type The window type to pass to the created WindowManager.LayoutParams.
+     *
+     * @param type  The window type to pass to the created WindowManager.LayoutParams.
      * @param title The window title to pass to the created WindowManager.LayoutParams.
      */
     public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type, String title) {
@@ -430,9 +453,10 @@
      * for taskbar showing as navigation bar
      */
     private WindowManager.LayoutParams createAllWindowParams() {
+        final int windowType =
+                FLAG_HIDE_NAVBAR_WINDOW ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL;
         WindowManager.LayoutParams windowLayoutParams =
-                createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL,
-                        TaskbarActivityContext.WINDOW_TITLE);
+                createDefaultWindowLayoutParams(windowType, TaskbarActivityContext.WINDOW_TITLE);
         boolean isPhoneNavMode = TaskbarManager.isPhoneButtonNavMode(this);
         if (!isPhoneNavMode) {
             return windowLayoutParams;
@@ -445,7 +469,7 @@
         windowLayoutParams.paramsForRotation = new WindowManager.LayoutParams[4];
         for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
             WindowManager.LayoutParams lp =
-                    createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL,
+                    createDefaultWindowLayoutParams(windowType,
                             TaskbarActivityContext.WINDOW_TITLE);
             switch (rot) {
                 case Surface.ROTATION_0, Surface.ROTATION_180 -> {
@@ -695,7 +719,7 @@
         mIsDestroyed = true;
         setUIController(TaskbarUIController.DEFAULT);
         mControllers.onDestroy();
-        if (!ENABLE_TASKBAR_NO_RECREATION.get() && !FLAG_HIDE_NAVBAR_WINDOW) {
+        if (!enableTaskbarNoRecreate() && !FLAG_HIDE_NAVBAR_WINDOW) {
             mWindowManager.removeViewImmediate(mDragLayer);
             mAddedWindow = false;
         }
@@ -794,7 +818,7 @@
         // Overlay AFVs are in a separate window and do not require Taskbar to be fullscreen.
         if (!isDragInProgress
                 && !AbstractFloatingView.hasOpenView(
-                        this, TYPE_ALL & ~TYPE_TASKBAR_OVERLAY_PROXY)) {
+                this, TYPE_ALL & ~TYPE_TASKBAR_OVERLAY_PROXY)) {
             // Reverts Taskbar window to its original size
             setTaskbarWindowFullscreen(false);
         }
@@ -853,7 +877,7 @@
                 ? resources.getDimensionPixelSize(R.dimen.arrow_toast_arrow_height)
                 + (resources.getDimensionPixelSize(R.dimen.taskbar_tooltip_vertical_padding) * 2)
                 + calculateTextHeight(
-                        resources.getDimensionPixelSize(R.dimen.arrow_toast_text_size))
+                resources.getDimensionPixelSize(R.dimen.arrow_toast_text_size))
                 : 0;
 
         // Return transient taskbar window height when pinning feature is enabled, so taskbar view
@@ -865,7 +889,7 @@
             return transientTaskbarDp.taskbarHeight
                     + (2 * transientTaskbarDp.taskbarBottomMargin)
                     + Math.max(extraHeightForTaskbarTooltips, resources.getDimensionPixelSize(
-                            R.dimen.transient_taskbar_shadow_blur));
+                    R.dimen.transient_taskbar_shadow_blur));
         }
 
 
@@ -878,6 +902,14 @@
         return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame);
     }
 
+    public DeviceProfile getTransientTaskbarDeviceProfile() {
+        return mTransientTaskbarDeviceProfile;
+    }
+
+    public DeviceProfile getPersistentTaskbarDeviceProfile() {
+        return mPersistentTaskbarDeviceProfile;
+    }
+
     /**
      * Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar
      * window.
@@ -1290,7 +1322,7 @@
 
     void notifyUpdateLayoutParams() {
         if (mDragLayer.isAttachedToWindow()) {
-            if (ENABLE_TASKBAR_NO_RECREATION.get()) {
+            if (enableTaskbarNoRecreate()) {
                 mWindowManager.updateViewLayout(mDragLayer.getRootView(), mWindowLayoutParams);
             } else {
                 mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index d237c1f..e4ebf65 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -29,26 +29,38 @@
 import com.android.launcher3.Utilities.mapRange
 import com.android.launcher3.Utilities.mapToRange
 import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
+import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_PERSISTENT
+import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_TRANSIENT
 import com.android.launcher3.util.DisplayController
 
 /** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
-class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
+class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) {
 
     private val isInSetup: Boolean = !context.isUserSetupComplete
     private val DARK_THEME_SHADOW_ALPHA = 51f
     private val LIGHT_THEME_SHADOW_ALPHA = 25f
 
+    private val maxTransientTaskbarHeight =
+        context.transientTaskbarDeviceProfile.taskbarHeight.toFloat()
+    private val maxPersistentTaskbarHeight =
+        context.persistentTaskbarDeviceProfile.taskbarHeight.toFloat()
+    var backgroundProgress =
+        if (DisplayController.isTransientTaskbar(context)) {
+            PINNING_TRANSIENT
+        } else {
+            PINNING_PERSISTENT
+        }
+
+    var isAnimatingPinning = false
+
     val paint = Paint()
     val lastDrawnTransientRect = RectF()
     var backgroundHeight = context.deviceProfile.taskbarHeight.toFloat()
     var translationYForSwipe = 0f
     var translationYForStash = 0f
 
-    private var maxBackgroundHeight = context.deviceProfile.taskbarHeight.toFloat()
     private val transientBackgroundBounds = context.transientTaskbarBounds
 
-    private val isTransientTaskbar = DisplayController.isTransientTaskbar(context)
-
     private val shadowAlpha: Float
     private var shadowBlur = 0f
     private var keyShadowDistance = 0f
@@ -75,13 +87,6 @@
         paint.flags = Paint.ANTI_ALIAS_FLAG
         paint.style = Paint.Style.FILL
 
-        if (isTransientTaskbar) {
-            val res = context.resources
-            bottomMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin)
-            shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur)
-            keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance)
-        }
-
         shadowAlpha =
             if (Utilities.isDarkTheme(context)) DARK_THEME_SHADOW_ALPHA
             else LIGHT_THEME_SHADOW_ALPHA
@@ -90,10 +95,11 @@
     }
 
     fun updateStashedHandleWidth(dp: DeviceProfile, res: Resources) {
-        stashedHandleWidth = res.getDimensionPixelSize(
+        stashedHandleWidth =
+            res.getDimensionPixelSize(
                 if (TaskbarManager.isPhoneMode(dp)) R.dimen.taskbar_stashed_small_screen
                 else R.dimen.taskbar_stashed_handle_width
-        )
+            )
     }
 
     /**
@@ -102,7 +108,7 @@
      * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
      */
     fun setCornerRoundness(cornerRoundness: Float) {
-        if (isTransientTaskbar && !transientBackgroundBounds.isEmpty) {
+        if (DisplayController.isTransientTaskbar(context) && !transientBackgroundBounds.isEmpty) {
             return
         }
 
@@ -126,63 +132,122 @@
 
     /** Draws the background with the given paint and height, on the provided canvas. */
     fun draw(canvas: Canvas) {
+        if (isInSetup) return
+        val isTransientTaskbar = backgroundProgress == 0f
         canvas.save()
-        if (!isTransientTaskbar || transientBackgroundBounds.isEmpty) {
-            canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin)
-            // Draw the background behind taskbar content.
-            canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint)
-
-            // Draw the inverted rounded corners above the taskbar.
-            canvas.translate(0f, -leftCornerRadius)
-            canvas.drawPath(invertedLeftCornerPath, paint)
-            canvas.translate(0f, leftCornerRadius)
-            canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
-            canvas.drawPath(invertedRightCornerPath, paint)
-        } else if (!isInSetup) {
-            // backgroundHeight is a value from [0...maxBackgroundHeight], so we can use it as a
-            // proxy to figure out the animation progress of the stash/unstash animation.
-            val progress = backgroundHeight / maxBackgroundHeight
-
-            // At progress 0, we draw the background as the stashed handle.
-            // At progress 1, we draw the background as the full taskbar.
-            val newBackgroundHeight =
-                mapRange(progress, stashedHandleHeight.toFloat(), maxBackgroundHeight)
-            val fullWidth = transientBackgroundBounds.width()
-            val newWidth = mapRange(progress, stashedHandleWidth.toFloat(), fullWidth.toFloat())
-            val halfWidthDelta = (fullWidth - newWidth) / 2f
-            val radius = newBackgroundHeight / 2f
-            val bottomMarginProgress = bottomMargin * ((1f - progress) / 2f)
-
-            // Aligns the bottom with the bottom of the stashed handle.
-            val bottom =
-                canvas.height - bottomMargin +
-                    bottomMarginProgress +
-                    translationYForSwipe +
-                    translationYForStash +
-                    -mapRange(1f - progress, 0f, stashedHandleHeight / 2f)
-
-            // Draw shadow.
-            val newShadowAlpha =
-                mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR)
-            paint.setShadowLayer(
-                shadowBlur,
-                0f,
-                keyShadowDistance,
-                setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha))
-            )
-
-            lastDrawnTransientRect.set(
-                transientBackgroundBounds.left + halfWidthDelta,
-                bottom - newBackgroundHeight,
-                transientBackgroundBounds.right - halfWidthDelta,
-                bottom
-            )
-            val horizontalInset = fullWidth * widthInsetPercentage
-            lastDrawnTransientRect.inset(horizontalInset, 0f)
-
-            canvas.drawRoundRect(lastDrawnTransientRect, radius, radius, paint)
+        if (!isTransientTaskbar || transientBackgroundBounds.isEmpty || isAnimatingPinning) {
+            drawPersistentBackground(canvas)
         }
         canvas.restore()
+        canvas.save()
+        if (isAnimatingPinning || isTransientTaskbar) {
+            drawTransientBackground(canvas)
+        }
+        canvas.restore()
+    }
+
+    private fun drawPersistentBackground(canvas: Canvas) {
+        if (isAnimatingPinning) {
+            val persistentTaskbarHeight = maxPersistentTaskbarHeight * backgroundProgress
+            canvas.translate(0f, canvas.height - persistentTaskbarHeight)
+            // Draw the background behind taskbar content.
+            canvas.drawRect(0f, 0f, canvas.width.toFloat(), persistentTaskbarHeight, paint)
+        } else {
+            canvas.translate(0f, canvas.height - maxPersistentTaskbarHeight)
+            // Draw the background behind taskbar content.
+            canvas.drawRect(0f, 0f, canvas.width.toFloat(), maxPersistentTaskbarHeight, paint)
+        }
+
+        // Draw the inverted rounded corners above the taskbar.
+        canvas.translate(0f, -leftCornerRadius)
+        canvas.drawPath(invertedLeftCornerPath, paint)
+        canvas.translate(0f, leftCornerRadius)
+        canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
+        canvas.drawPath(invertedRightCornerPath, paint)
+    }
+
+    private fun drawTransientBackground(canvas: Canvas) {
+        val res = context.resources
+        val transientTaskbarHeight = maxTransientTaskbarHeight * (1f - backgroundProgress)
+        val heightProgressWhileAnimating =
+            if (isAnimatingPinning) transientTaskbarHeight else backgroundHeight
+
+        var progress = heightProgressWhileAnimating / maxTransientTaskbarHeight
+        progress = Math.round(progress * 100f) / 100f
+        if (isAnimatingPinning) {
+            var scale = transientTaskbarHeight / maxTransientTaskbarHeight
+            scale = Math.round(scale * 100f) / 100f
+            bottomMargin =
+                mapRange(
+                        scale,
+                        0f,
+                        res.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin).toFloat()
+                    )
+                    .toInt()
+            shadowBlur =
+                mapRange(scale, 0f, res.getDimension(R.dimen.transient_taskbar_shadow_blur))
+            keyShadowDistance =
+                mapRange(scale, 0f, res.getDimension(R.dimen.transient_taskbar_key_shadow_distance))
+        } else {
+            bottomMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin)
+            shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur)
+            keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance)
+        }
+
+        // At progress 0, we draw the background as the stashed handle.
+        // At progress 1, we draw the background as the full taskbar.
+        // Min height capped to max persistent taskbar height for animation
+        val backgroundHeightWhileAnimating =
+            if (isAnimatingPinning) maxPersistentTaskbarHeight else stashedHandleHeight.toFloat()
+        val newBackgroundHeight =
+            mapRange(progress, backgroundHeightWhileAnimating, maxTransientTaskbarHeight)
+        val fullWidth = transientBackgroundBounds.width()
+
+        // .9f is here to restrict min width of the background while animating, so transient
+        // background keeps it pill shape until animation end.
+        val animationWidth =
+            if (DisplayController.isTransientTaskbar(context)) fullWidth.toFloat() * .9f
+            else fullWidth.toFloat()
+        val backgroundWidthWhileAnimating =
+            if (isAnimatingPinning) animationWidth else stashedHandleWidth.toFloat()
+
+        val newWidth = mapRange(progress, backgroundWidthWhileAnimating, fullWidth.toFloat())
+        val halfWidthDelta = (fullWidth - newWidth) / 2f
+        val radius = newBackgroundHeight / 2f
+        val bottomMarginProgress = bottomMargin * ((1f - progress) / 2f)
+
+        // Aligns the bottom with the bottom of the stashed handle.
+        val bottom =
+            canvas.height - bottomMargin +
+                bottomMarginProgress +
+                translationYForSwipe +
+                translationYForStash +
+                -mapRange(
+                    1f - progress,
+                    0f,
+                    if (isAnimatingPinning) 0f else stashedHandleHeight / 2f
+                )
+
+        // Draw shadow.
+        val newShadowAlpha =
+            mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR)
+        paint.setShadowLayer(
+            shadowBlur,
+            0f,
+            keyShadowDistance,
+            setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha))
+        )
+
+        lastDrawnTransientRect.set(
+            transientBackgroundBounds.left + halfWidthDelta,
+            bottom - newBackgroundHeight,
+            transientBackgroundBounds.right - halfWidthDelta,
+            bottom
+        )
+        val horizontalInset = fullWidth * widthInsetPercentage
+        lastDrawnTransientRect.inset(horizontalInset, 0f)
+
+        canvas.drawRoundRect(lastDrawnTransientRect, radius, radius, paint)
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index d82f501..f9ddc3d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -62,7 +62,7 @@
     public final TaskbarOverlayController taskbarOverlayController;
     public final TaskbarEduTooltipController taskbarEduTooltipController;
     public final KeyboardQuickSwitchController keyboardQuickSwitchController;
-    public final TaskbarDividerPopupController taskbarPinningController;
+    public final TaskbarPinningController taskbarPinningController;
     public final Optional<BubbleControllers> bubbleControllers;
 
     @Nullable private LoggableTaskbarController[] mControllersToLog = null;
@@ -110,7 +110,7 @@
             TaskbarRecentAppsController taskbarRecentAppsController,
             TaskbarEduTooltipController taskbarEduTooltipController,
             KeyboardQuickSwitchController keyboardQuickSwitchController,
-            TaskbarDividerPopupController taskbarPinningController,
+            TaskbarPinningController taskbarPinningController,
             Optional<BubbleControllers> bubbleControllers) {
         this.taskbarActivityContext = taskbarActivityContext;
         this.taskbarDragController = taskbarDragController;
@@ -171,7 +171,7 @@
         taskbarTranslationController.init(this);
         taskbarEduTooltipController.init(this);
         keyboardQuickSwitchController.init(this);
-        taskbarPinningController.init(this);
+        taskbarPinningController.init(this, mSharedState);
         bubbleControllers.ifPresent(controllers -> controllers.init(this));
 
         mControllersToLog = new LoggableTaskbarController[] {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
deleted file mode 100644
index a2c61ce..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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 android.view.View
-import com.android.launcher3.LauncherPrefs
-import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
-import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE
-import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN
-import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_PINNED
-import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_UNPINNED
-import com.android.launcher3.taskbar.TaskbarDividerPopupView.Companion.createAndPopulate
-import java.io.PrintWriter
-
-/** Controls taskbar pinning through a popup view. */
-class TaskbarDividerPopupController(private val context: TaskbarActivityContext) :
-    TaskbarControllers.LoggableTaskbarController {
-
-    private lateinit var controllers: TaskbarControllers
-    private val launcherPrefs = LauncherPrefs.get(context)
-    private val statsLogManager = context.statsLogManager
-
-    fun init(taskbarControllers: TaskbarControllers) {
-        controllers = taskbarControllers
-    }
-
-    fun showPinningView(view: View) {
-        context.isTaskbarWindowFullscreen = true
-
-        view.post {
-            val popupView = createAndPopulate(view, context)
-            popupView.requestFocus()
-
-            popupView.onCloseCallback =
-                callback@{ didPreferenceChange ->
-                    statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE)
-                    context.dragLayer.post { context.onPopupVisibilityChanged(false) }
-
-                    if (!didPreferenceChange) {
-                        return@callback
-                    }
-
-                    if (launcherPrefs.get(TASKBAR_PINNING)) {
-                        animateTransientToPersistentTaskbar()
-                        statsLogManager.logger().log(LAUNCHER_TASKBAR_PINNED)
-                    } else {
-                        animatePersistentToTransientTaskbar()
-                        statsLogManager.logger().log(LAUNCHER_TASKBAR_UNPINNED)
-                    }
-                }
-            popupView.changePreference = {
-                launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING))
-            }
-            context.onPopupVisibilityChanged(true)
-            popupView.show()
-            statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN)
-        }
-    }
-
-    // TODO(b/265436799): provide animation/transition from transient taskbar to persistent one
-    private fun animateTransientToPersistentTaskbar() {}
-
-    // TODO(b/265436799): provide animation/transition from persistent taskbar to transient one
-    private fun animatePersistentToTransientTaskbar() {}
-
-    override fun dumpLogs(prefix: String, pw: PrintWriter) {
-        pw.println(prefix + "TaskbarPinningController:")
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index b200858..d13b53f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -75,12 +75,6 @@
     /** Callback invoked when the pinning popup view is closing. */
     var onCloseCallback: (preferenceChanged: Boolean) -> Unit = {}
 
-    /**
-     * Callback invoked when the user preference changes in popup view. Preference change will be
-     * based upon current value stored in [LauncherPrefs] for `TASKBAR_PINNING`
-     */
-    var changePreference: () -> Unit = {}
-
     init {
         // This synchronizes the arrow and menu to open at the same time
         mOpenChildFadeStartDelay = mOpenFadeStartDelay
@@ -185,8 +179,6 @@
 
     private fun onClickAlwaysShowTaskbarSwitchOption() {
         didPreferenceChange = true
-        changePreference()
-        changePreference = {}
         // Allow switch animation to finish and then close the popup.
         postDelayed(DIVIDER_POPUP_CLOSING_DELAY) {
             if (isOpen) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index e521154..1eb8d53 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -73,6 +73,8 @@
     private SafeCloseable mViewCaptureCloseable;
 
     private float mTaskbarBackgroundOffset;
+    private float mTaskbarBackgroundProgress;
+    private boolean mIsAnimatingTaskbarPinning = false;
 
     private final MultiPropertyFactory<TaskbarDragLayer> mTaskbarBackgroundAlpha;
 
@@ -162,10 +164,19 @@
         float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight()
                 * (1f - mTaskbarBackgroundOffset);
         mBackgroundRenderer.setBackgroundHeight(backgroundHeight);
+        mBackgroundRenderer.setBackgroundProgress(mTaskbarBackgroundProgress);
         mBackgroundRenderer.draw(canvas);
         super.dispatchDraw(canvas);
     }
 
+    /**
+     * Sets animation boolean when taskbar pinning animation starts or stops.
+     */
+    public void setAnimatingTaskbarPinning(boolean animatingTaskbarPinning) {
+        mIsAnimatingTaskbarPinning = animatingTaskbarPinning;
+        mBackgroundRenderer.setAnimatingPinning(mIsAnimatingTaskbarPinning);
+    }
+
     protected MultiProperty getBackgroundRendererAlpha() {
         return mTaskbarBackgroundAlpha.get(INDEX_ALL_OTHER_STATES);
     }
@@ -175,6 +186,15 @@
     }
 
     /**
+     * Sets the value for taskbar background switching between persistent and transient backgrounds.
+     * @param progress 0 is transient background, 1 is persistent background.
+     */
+    protected void setTaskbarBackgroundProgress(float progress) {
+        mTaskbarBackgroundProgress = progress;
+        invalidate();
+    }
+
+    /**
      * Sets the translation of the background color behind all the Taskbar contents.
      * @param offset 0 is fully onscreen, 1 is fully offscreen.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 867b062..73e32ab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.taskbar;
 
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_PERSISTENT;
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_TRANSIENT;
+
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -24,6 +27,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.util.DimensionUtils;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
 import com.android.launcher3.util.TouchController;
 
@@ -58,6 +62,9 @@
     // changes the inset visibility.
     private final AnimatedFloat mTaskbarAlpha = new AnimatedFloat(this::updateTaskbarAlpha);
 
+    private final AnimatedFloat mTaskbarBackgroundProgress = new AnimatedFloat(
+            this::updateTaskbarBackgroundProgress);
+
     // Initialized in init.
     private TaskbarControllers mControllers;
     private TaskbarStashViaTouchController mTaskbarStashViaTouchController;
@@ -83,6 +90,10 @@
         mOnBackgroundNavButtonColorIntensity = mControllers.navbarButtonsViewController
                 .getOnTaskbarBackgroundNavButtonColorOverride();
 
+        mTaskbarBackgroundProgress.updateValue(DisplayController.isTransientTaskbar(mActivity)
+                ? PINNING_TRANSIENT
+                : PINNING_PERSISTENT);
+
         mBgTaskbar.value = 1;
         mKeyguardBgTaskbar.value = 1;
         mNotificationShadeBgTaskbar.value = 1;
@@ -138,6 +149,11 @@
         return mBgOffset;
     }
 
+    // AnimatedFloat is for animating between pinned and transient taskbar
+    public AnimatedFloat getTaskbarBackgroundProgress() {
+        return mTaskbarBackgroundProgress;
+    }
+
     public AnimatedFloat getTaskbarAlpha() {
         return mTaskbarAlpha;
     }
@@ -180,10 +196,13 @@
 
     private void updateBackgroundOffset() {
         mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
-
         updateOnBackgroundNavButtonColorIntensity();
     }
 
+    private void updateTaskbarBackgroundProgress() {
+        mTaskbarDragLayer.setTaskbarBackgroundProgress(mTaskbarBackgroundProgress.value);
+    }
+
     private void updateTaskbarAlpha() {
         mTaskbarDragLayer.setAlpha(mTaskbarAlpha.value);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 56ba460..d4f42d8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -211,7 +211,7 @@
                     }
                     applyState();
                     boolean disallowLongClick =
-                            FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
+                            FeatureFlags.enableSplitContextually()
                                     ? mLauncher.isSplitSelectionEnabled()
                                     : finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
                     com.android.launcher3.taskbar.Utilities.setOverviewDragState(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 6dfd243..b500c3e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -18,12 +18,11 @@
 import static android.content.Context.RECEIVER_NOT_EXPORTED;
 import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
-import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
-import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NO_RECREATION;
+import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
 import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
@@ -37,7 +36,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.hardware.display.DisplayManager;
@@ -59,12 +57,12 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.quickstep.RecentsActivity;
@@ -147,13 +145,6 @@
     private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver =
             new SimpleBroadcastReceiver(this::showTaskbarFromBroadcast);
 
-    private final SharedPreferences.OnSharedPreferenceChangeListener
-            mTaskbarPinningPreferenceChangeListener = (sharedPreferences, key) -> {
-                if (TASKBAR_PINNING_KEY.equals(key)) {
-                    recreateTaskbar();
-                }
-            };
-
     private final ActivityLifecycleCallbacksAdapter mLifecycleCallbacks =
             new ActivityLifecycleCallbacksAdapter() {
                 @Override
@@ -209,8 +200,9 @@
     public TaskbarManager(TouchInteractionService service) {
         Display display =
                 service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
-        mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
-        if (ENABLE_TASKBAR_NO_RECREATION.get()) {
+        mContext = service.createWindowContext(display,
+                FLAG_HIDE_NAVBAR_WINDOW ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL, null);
+        if (enableTaskbarNoRecreate()) {
             mWindowManager = mContext.getSystemService(WindowManager.class);
             mTaskbarRootLayout = new FrameLayout(mContext) {
                 @Override
@@ -305,10 +297,8 @@
     private void destroyExistingTaskbar() {
         debugWhyTaskbarNotDestroyed("destroyExistingTaskbar: " + mTaskbarActivityContext);
         if (mTaskbarActivityContext != null) {
-            LauncherPrefs.get(mContext).removeListener(mTaskbarPinningPreferenceChangeListener,
-                    TASKBAR_PINNING);
             mTaskbarActivityContext.onDestroy();
-            if (!FLAG_HIDE_NAVBAR_WINDOW || ENABLE_TASKBAR_NO_RECREATION.get()) {
+            if (!FLAG_HIDE_NAVBAR_WINDOW || enableTaskbarNoRecreate()) {
                 mTaskbarActivityContext = null;
             }
         }
@@ -448,12 +438,14 @@
                 return;
             }
 
-            if (ENABLE_TASKBAR_NO_RECREATION.get() || mTaskbarActivityContext == null) {
+            if (enableTaskbarNoRecreate() || mTaskbarActivityContext == null) {
                 mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp,
                         mNavButtonController, mUnfoldProgressProvider);
             } else {
                 mTaskbarActivityContext.updateDeviceProfile(dp);
             }
+            mSharedState.startTaskbarVariantIsTransient =
+                    DisplayController.isTransientTaskbar(mTaskbarActivityContext);
             mTaskbarActivityContext.init(mSharedState);
 
             if (mActivity != null) {
@@ -461,16 +453,12 @@
                     createTaskbarUIControllerForActivity(mActivity));
             }
 
-            if (ENABLE_TASKBAR_NO_RECREATION.get()) {
+            if (enableTaskbarNoRecreate()) {
                 addTaskbarRootViewToWindow();
                 mTaskbarRootLayout.removeAllViews();
                 mTaskbarRootLayout.addView(mTaskbarActivityContext.getDragLayer());
                 mTaskbarActivityContext.notifyUpdateLayoutParams();
             }
-
-            // We to wait until user unlocks the device to attach listener.
-            LauncherPrefs.get(mContext).addListener(mTaskbarPinningPreferenceChangeListener,
-                TASKBAR_PINNING);
         } finally {
             Trace.endSection();
         }
@@ -603,8 +591,7 @@
     }
 
     private void addTaskbarRootViewToWindow() {
-        if (ENABLE_TASKBAR_NO_RECREATION.get() && !mAddedWindow
-                && mTaskbarActivityContext != null) {
+        if (enableTaskbarNoRecreate() && !mAddedWindow && mTaskbarActivityContext != null) {
             mWindowManager.addView(mTaskbarRootLayout,
                     mTaskbarActivityContext.getWindowLayoutParams());
             mAddedWindow = true;
@@ -612,7 +599,7 @@
     }
 
     private void removeTaskbarRootViewFromWindow() {
-        if (ENABLE_TASKBAR_NO_RECREATION.get() && mAddedWindow) {
+        if (enableTaskbarNoRecreate() && mAddedWindow) {
             mWindowManager.removeViewImmediate(mTaskbarRootLayout);
             mAddedWindow = false;
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
new file mode 100644
index 0000000..d1bed3e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
@@ -0,0 +1,112 @@
+/*
+ * 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 android.animation.AnimatorSet
+import android.view.View
+import androidx.core.animation.doOnEnd
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN
+import com.android.launcher3.taskbar.TaskbarDividerPopupView.Companion.createAndPopulate
+import java.io.PrintWriter
+
+/** Controls taskbar pinning through a popup view. */
+class TaskbarPinningController(private val context: TaskbarActivityContext) :
+    TaskbarControllers.LoggableTaskbarController {
+
+    private lateinit var controllers: TaskbarControllers
+    private lateinit var taskbarSharedState: TaskbarSharedState
+    private val launcherPrefs = LauncherPrefs.get(context)
+    private val statsLogManager = context.statsLogManager
+    private var isAnimatingTaskbarPinning = false
+
+    fun init(taskbarControllers: TaskbarControllers, sharedState: TaskbarSharedState) {
+        controllers = taskbarControllers
+        taskbarSharedState = sharedState
+    }
+
+    fun showPinningView(view: View) {
+        context.isTaskbarWindowFullscreen = true
+
+        view.post {
+            val popupView = createAndPopulate(view, context)
+            popupView.requestFocus()
+
+            popupView.onCloseCallback =
+                callback@{ didPreferenceChange ->
+                    statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE)
+                    context.dragLayer.post { context.onPopupVisibilityChanged(false) }
+
+                    if (!didPreferenceChange) {
+                        return@callback
+                    }
+                    val animateToValue =
+                        if (!launcherPrefs.get(TASKBAR_PINNING)) {
+                            PINNING_PERSISTENT
+                        } else {
+                            PINNING_TRANSIENT
+                        }
+                    taskbarSharedState.taskbarWasPinned = animateToValue == PINNING_TRANSIENT
+                    animateTaskbarPinning(animateToValue)
+                }
+            context.onPopupVisibilityChanged(true)
+            popupView.show()
+            statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN)
+        }
+    }
+
+    private fun animateTaskbarPinning(animateToValue: Float) {
+        val animatorSet = AnimatorSet()
+        val taskbarViewController = controllers.taskbarViewController
+        val dragLayerController = controllers.taskbarDragLayerController
+
+        animatorSet.playTogether(
+            dragLayerController.taskbarBackgroundProgress.animateToValue(animateToValue),
+            taskbarViewController.taskbarIconTranslationYForPinning.animateToValue(animateToValue),
+            taskbarViewController.taskbarIconScaleForPinning.animateToValue(animateToValue),
+            taskbarViewController.taskbarIconTranslationXForPinning.animateToValue(animateToValue)
+        )
+
+        animatorSet.doOnEnd { recreateTaskbarAndUpdatePinningValue() }
+        animatorSet.duration = PINNING_ANIMATION_DURATION
+        updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(true)
+        animatorSet.start()
+    }
+
+    private fun updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(isAnimating: Boolean) {
+        isAnimatingTaskbarPinning = isAnimating
+        context.dragLayer.setAnimatingTaskbarPinning(isAnimating)
+    }
+
+    private fun recreateTaskbarAndUpdatePinningValue() {
+        updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(false)
+        launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING))
+    }
+
+    override fun dumpLogs(prefix: String, pw: PrintWriter) {
+        pw.println(prefix + "TaskbarPinningController:")
+        pw.println("$prefix\tisAnimatingTaskbarPinning=$isAnimatingTaskbarPinning")
+        pw.println("$prefix\tTASKBAR_PINNING shared pref =" + launcherPrefs.get(TASKBAR_PINNING))
+    }
+
+    companion object {
+        const val PINNING_PERSISTENT = 1f
+        const val PINNING_TRANSIENT = 0f
+        const val PINNING_ANIMATION_DURATION = 500L
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index abbd18b..176a8c5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -71,4 +71,11 @@
             new InsetsFrameProvider(mInsetsOwner, INDEX_RIGHT, systemGestures())
                     .setSource(SOURCE_DISPLAY)
     };
+
+    // Allows us to shift translation logic when doing taskbar pinning animation.
+    public Boolean startTaskbarVariantIsTransient = true;
+
+    // To track if taskbar was pinned using taskbar pinning feature at the time of recreate,
+    // so we can unstash transient taskbar when we un-pinning taskbar.
+    public Boolean taskbarWasPinned = false;
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index e67a6d5..5be74be 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -22,7 +22,7 @@
 import static com.android.app.animation.Interpolators.FINAL_FRAME;
 import static com.android.app.animation.Interpolators.INSTANT;
 import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
+import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
@@ -337,7 +337,8 @@
                 && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
         boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
         updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
-        updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isTransientTaskbar);
+        updateStateForFlag(FLAG_STASHED_IN_APP_AUTO,
+                isTransientTaskbar && !mTaskbarSharedState.taskbarWasPinned);
         updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
         updateStateForFlag(FLAG_IN_SETUP, isInSetup);
         updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode()
@@ -346,6 +347,9 @@
         // us that we're paused until a bit later. This avoids flickering upon recreating taskbar.
         updateStateForFlag(FLAG_IN_APP, true);
         applyState(/* duration = */ 0);
+        if (mTaskbarSharedState.taskbarWasPinned) {
+            tryStartTaskbarTimeout();
+        }
         notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp());
     }
 
@@ -361,7 +365,7 @@
      * Returns whether the user can manually stash the taskbar based on the current device state.
      */
     protected boolean supportsManualStashing() {
-        if (ENABLE_TASKBAR_PINNING.get() && mPrefs.getBoolean(TASKBAR_PINNING_KEY, false)) {
+        if (ENABLE_TASKBAR_PINNING.get() && LauncherPrefs.get(mActivity).get(TASKBAR_PINNING)) {
             return false;
         }
         return supportsVisualStashing()
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 5f58de5..a7461b7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -20,7 +20,6 @@
 
 import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
 import android.content.Context;
@@ -98,8 +97,6 @@
 
     private final float mTransientTaskbarMinWidth;
 
-    private final float mTaskbarAllAppsButtonTranslationXOffset;
-
     private boolean mShouldTryStartAlign;
 
     public TaskbarView(@NonNull Context context) {
@@ -125,13 +122,16 @@
                 && !TaskbarManager.isPhoneMode(mActivityContext.getDeviceProfile());
         mIsRtl = Utilities.isRtl(resources);
         mTransientTaskbarMinWidth = resources.getDimension(R.dimen.transient_taskbar_min_width);
-        mTaskbarAllAppsButtonTranslationXOffset =
-                resources.getDimension(getAllAppsButtonTranslationXOffset(isTransientTaskbar));
+
 
         onDeviceProfileChanged(mActivityContext.getDeviceProfile());
 
         int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
         int actualIconSize = mActivityContext.getDeviceProfile().taskbarIconSize;
+        if (FeatureFlags.ENABLE_TASKBAR_PINNING.get()) {
+            DeviceProfile deviceProfile = mActivityContext.getTransientTaskbarDeviceProfile();
+            actualIconSize = deviceProfile.taskbarIconSize;
+        }
         int visualIconSize = (int) (actualIconSize * ICON_VISIBLE_AREA_FACTOR);
 
         mIconTouchSize = Math.max(actualIconSize,
@@ -139,8 +139,11 @@
 
         // We layout the icons to be of mIconTouchSize in width and height
         mItemMarginLeftRight = actualMargin - (mIconTouchSize - visualIconSize) / 2;
-        mItemPadding = (mIconTouchSize - actualIconSize) / 2;
 
+        // We always layout taskbar as a transient taskbar when we have taskbar pinning feature on,
+        // then we scale and translate the icons to match persistent taskbar designs, so we use
+        // taskbar icon size from current device profile to calculate correct item padding.
+        mItemPadding = (mIconTouchSize - mActivityContext.getDeviceProfile().taskbarIconSize) / 2;
         mFolderLeaveBehindColor = Themes.getAttrColor(mActivityContext,
                 android.R.attr.textColorTertiary);
 
@@ -173,7 +176,8 @@
 
     @DrawableRes
     private int getAllAppsButton(boolean isTransientTaskbar) {
-        boolean shouldSelectTransientIcon = (isTransientTaskbar || ENABLE_TASKBAR_PINNING.get())
+        boolean shouldSelectTransientIcon =
+                (isTransientTaskbar || FeatureFlags.ENABLE_TASKBAR_PINNING.get())
                 && !mActivityContext.isThreeButtonNav();
         if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
             return shouldSelectTransientIcon
@@ -187,7 +191,7 @@
     }
 
     @DimenRes
-    private int getAllAppsButtonTranslationXOffset(boolean isTransientTaskbar) {
+    public int getAllAppsButtonTranslationXOffset(boolean isTransientTaskbar) {
         if (isTransientTaskbar) {
             return R.dimen.transient_taskbar_all_apps_button_translation_x_offset;
         } else {
@@ -370,8 +374,6 @@
         }
 
         if (mAllAppsButton != null) {
-            mAllAppsButton.setTranslationXForTaskbarAllAppsIcon(getChildCount() > 0
-                    ? mTaskbarAllAppsButtonTranslationXOffset : 0f);
             addView(mAllAppsButton, mIsRtl ? getChildCount() : 0);
 
             // if only all apps button present, don't include divider view.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 8a8c3bc..0d06088 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -21,14 +21,19 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.Utilities.mapRange;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.anim.AnimatedFloat.VALUE;
 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_PERSISTENT;
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_TRANSIENT;
 import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode;
 import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_PINNING_ANIM;
 import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_REVEAL_ANIM;
 
 import android.animation.Animator;
@@ -61,11 +66,13 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.ThemedIconDrawable;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiTranslateDelegate;
 import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.views.IconButtonView;
 
 import java.io.PrintWriter;
 import java.util.function.Predicate;
@@ -98,12 +105,27 @@
             this::updateTranslationY);
     private final AnimatedFloat mTaskbarIconTranslationYForStash = new AnimatedFloat(
             this::updateTranslationY);
+
+    private final AnimatedFloat mTaskbarIconScaleForPinning = new AnimatedFloat(
+            this::updateTaskbarIconsScale);
+
+    private final AnimatedFloat mTaskbarIconTranslationXForPinning = new AnimatedFloat(
+            this::updateTaskbarIconTranslationXForPinning);
+
+    private final AnimatedFloat mTaskbarIconTranslationYForPinning = new AnimatedFloat(
+            this::updateTranslationY);
+
+    private final View.OnLayoutChangeListener mTaskbarViewLayoutChangeListener =
+            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
+                    -> updateTaskbarIconTranslationXForPinning();
+
+
     private AnimatedFloat mTaskbarNavButtonTranslationY;
     private AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay;
     private float mTaskbarIconTranslationYForSwipe;
     private float mTaskbarIconTranslationYForSpringOnStash;
 
-    private final int mTaskbarBottomMargin;
+    private int mTaskbarBottomMargin;
     private final int mStashedHandleHeight;
     private final int mLauncherThemedIconsBackgroundColor;
     private final int mTaskbarThemedIconsBackgroundColor;
@@ -131,8 +153,18 @@
 
     private final boolean mIsRtl;
 
+    private final DeviceProfile mTransientTaskbarDp;
+    private final DeviceProfile mPersistentTaskbarDp;
+
+    private final int mTransientIconSize;
+    private final int mPersistentIconSize;
+
     public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
         mActivity = activity;
+        mTransientTaskbarDp = mActivity.getTransientTaskbarDeviceProfile();
+        mPersistentTaskbarDp = mActivity.getPersistentTaskbarDeviceProfile();
+        mTransientIconSize = mTransientTaskbarDp.taskbarIconSize;
+        mPersistentIconSize = mPersistentTaskbarDp.taskbarIconSize;
         mTaskbarView = taskbarView;
         mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, NUM_ALPHA_CHANNELS);
         mTaskbarIconAlpha.setUpdateVisibility(true);
@@ -162,6 +194,12 @@
                 : mActivity.getDeviceProfile().taskbarHeight;
 
         mTaskbarIconScaleForStash.updateValue(1f);
+        float pinningValue = DisplayController.isTransientTaskbar(mActivity)
+                ? PINNING_TRANSIENT
+                : PINNING_PERSISTENT;
+        mTaskbarIconScaleForPinning.updateValue(pinningValue);
+        mTaskbarIconTranslationYForPinning.updateValue(pinningValue);
+        mTaskbarIconTranslationXForPinning.updateValue(pinningValue);
 
         mModelCallbacks.init(controllers);
         if (mActivity.isUserSetupComplete()) {
@@ -181,6 +219,9 @@
             mTaskbarIconAlpha.get(ALPHA_INDEX_SMALL_SCREEN)
                     .animateToValue(isPhoneButtonNavMode(mActivity) ? 0 : 1).start();
         }
+        if (ENABLE_TASKBAR_PINNING.get()) {
+            mTaskbarView.addOnLayoutChangeListener(mTaskbarViewLayoutChangeListener);
+        }
     }
 
     /**
@@ -191,6 +232,9 @@
     }
 
     public void onDestroy() {
+        if (ENABLE_TASKBAR_PINNING.get()) {
+            mTaskbarView.removeOnLayoutChangeListener(mTaskbarViewLayoutChangeListener);
+        }
         LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
         mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
         mModelCallbacks.unregisterListeners();
@@ -253,6 +297,18 @@
         return mTaskbarIconTranslationYForStash;
     }
 
+    public AnimatedFloat getTaskbarIconScaleForPinning() {
+        return mTaskbarIconScaleForPinning;
+    }
+
+    public AnimatedFloat getTaskbarIconTranslationXForPinning() {
+        return mTaskbarIconTranslationXForPinning;
+    }
+
+    public AnimatedFloat getTaskbarIconTranslationYForPinning() {
+        return mTaskbarIconTranslationYForPinning;
+    }
+
     /**
      * Applies scale properties for the entire TaskbarView (rather than individual icons).
      */
@@ -263,6 +319,65 @@
     }
 
     /**
+     * Applies scale properties for the taskbar icons
+     */
+    private void updateTaskbarIconsScale() {
+        float scale = mTaskbarIconScaleForPinning.value;
+        View[] iconViews = mTaskbarView.getIconViews();
+
+        float finalScale;
+        if (mControllers.getSharedState().startTaskbarVariantIsTransient) {
+            finalScale = mapRange(scale, 1f, ((float) mPersistentIconSize / mTransientIconSize));
+        } else {
+            finalScale = mapRange(scale, ((float) mTransientIconSize / mPersistentIconSize), 1f);
+        }
+
+        for (int iconIndex = 0; iconIndex < iconViews.length; iconIndex++) {
+            iconViews[iconIndex].setScaleX(finalScale);
+            iconViews[iconIndex].setScaleY(finalScale);
+        }
+    }
+
+    private void updateTaskbarIconTranslationXForPinning() {
+        View[] iconViews = mTaskbarView.getIconViews();
+        float scale = mTaskbarIconTranslationXForPinning.value;
+        float taskbarCenterX =
+                mTaskbarView.getLeft() + (mTaskbarView.getRight() - mTaskbarView.getLeft()) / 2.0f;
+
+        float finalMarginScale = mapRange(scale, 0f, mTransientIconSize - mPersistentIconSize);
+
+        float transientTaskbarAllAppsOffset = mActivity.getResources().getDimension(
+                mTaskbarView.getAllAppsButtonTranslationXOffset(true));
+        float persistentTaskbarAllAppsOffset = mActivity.getResources().getDimension(
+                mTaskbarView.getAllAppsButtonTranslationXOffset(false));
+
+        float allAppIconTranslateRange = mapRange(scale, transientTaskbarAllAppsOffset,
+                persistentTaskbarAllAppsOffset);
+
+        float halfIconCount = iconViews.length / 2.0f;
+        for (int iconIndex = 0; iconIndex < iconViews.length; iconIndex++) {
+            View iconView = iconViews[iconIndex];
+            MultiTranslateDelegate translateDelegate =
+                    ((Reorderable) iconView).getTranslateDelegate();
+            float iconCenterX =
+                    iconView.getLeft() + (iconView.getRight() - iconView.getLeft()) / 2.0f;
+            if (iconCenterX <= taskbarCenterX) {
+                translateDelegate.getTranslationX(INDEX_TASKBAR_PINNING_ANIM).setValue(
+                        finalMarginScale * (halfIconCount - iconIndex));
+            } else {
+                translateDelegate.getTranslationX(INDEX_TASKBAR_PINNING_ANIM).setValue(
+                        -finalMarginScale * (iconIndex - halfIconCount));
+            }
+
+            if (iconView.equals(mTaskbarView.getAllAppsButtonView()) && iconViews.length > 1) {
+                ((IconButtonView) iconView).setTranslationXForTaskbarAllAppsIcon(
+                        allAppIconTranslateRange);
+            }
+        }
+    }
+
+
+    /**
      * Sets the translation of the TaskbarView during the swipe up gesture.
      */
     public void setTranslationYForSwipe(float transY) {
@@ -282,10 +397,41 @@
         mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value
                 + mTaskbarIconTranslationYForStash.value
                 + mTaskbarIconTranslationYForSwipe
+                + getTaskbarIconTranslationYForPinningValue()
                 + mTaskbarIconTranslationYForSpringOnStash);
     }
 
     /**
+     * Computes translation y for taskbar pinning.
+     */
+    private float getTaskbarIconTranslationYForPinningValue() {
+        if (mControllers.getSharedState() == null) return 0f;
+
+        float scale = mTaskbarIconTranslationYForPinning.value;
+        float taskbarIconTranslationYForPinningValue;
+
+        // transY is calculated here by adding/subtracting the taskbar bottom margin
+        // aligning the icon bound to be at bottom of current taskbar view and then
+        // finally placing the icon in the middle of new taskbar background height.
+        if (mControllers.getSharedState().startTaskbarVariantIsTransient) {
+            float transY =
+                    mTransientTaskbarDp.taskbarBottomMargin + (mTransientTaskbarDp.taskbarHeight
+                            - mTaskbarView.getIconLayoutBounds().bottom)
+                            - (mPersistentTaskbarDp.taskbarHeight
+                                    - mTransientTaskbarDp.taskbarIconSize) / 2f;
+            taskbarIconTranslationYForPinningValue = mapRange(scale, 0f, transY);
+        } else {
+            float transY =
+                    -mTransientTaskbarDp.taskbarBottomMargin + (mPersistentTaskbarDp.taskbarHeight
+                            - mTaskbarView.getIconLayoutBounds().bottom)
+                            - (mTransientTaskbarDp.taskbarHeight
+                                    - mTransientTaskbarDp.taskbarIconSize) / 2f;
+            taskbarIconTranslationYForPinningValue = mapRange(scale, transY, 0f);
+        }
+        return taskbarIconTranslationYForPinningValue;
+    }
+
+    /**
      * Updates the Taskbar's themed icons background according to the progress between in-app/home.
      */
     protected void updateIconsBackground() {
@@ -471,6 +617,8 @@
         mOnControllerPreCreateCallback.run();
         DeviceProfile taskbarDp = mActivity.getDeviceProfile();
         Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
+        boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+
         float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.taskbarIconSize;
         int borderSpacing = launcherDp.hotseatBorderSpace;
         int hotseatCellSize = DeviceProfile.calculateCellWidth(
@@ -497,6 +645,10 @@
         setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
                 anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
 
+        mTaskbarBottomMargin = isTransientTaskbar
+                ? mTransientTaskbarDp.taskbarBottomMargin
+                : mPersistentTaskbarDp.taskbarBottomMargin;
+
         for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
             View child = mTaskbarView.getChildAt(i);
             boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonView();
@@ -507,7 +659,7 @@
                 // to avoid icons disappearing rather than fading out visually.
                 setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0.8f, 1f));
             } else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())
-                    || (isTaskbarDividerView && FeatureFlags.ENABLE_TASKBAR_PINNING.get())) {
+                    || (isTaskbarDividerView && ENABLE_TASKBAR_PINNING.get())) {
                 if (!isToHome
                         && mIsHotseatIconOnTopWhenAligned
                         && mIsStashed) {
@@ -528,6 +680,8 @@
                         + launcherDp.hotseatQsbWidth / 2f
                         : hotseatPadding.left - borderSpacing - launcherDp.hotseatQsbWidth / 2f;
                 float childCenter = (child.getLeft() + child.getRight()) / 2f;
+                childCenter += ((Reorderable) child).getTranslateDelegate().getTranslationX(
+                        INDEX_TASKBAR_PINNING_ANIM).getValue();
                 float halfQsbIconWidthDiff =
                         (launcherDp.hotseatQsbWidth - taskbarDp.taskbarIconSize) / 2f;
                 float scale = ((float) taskbarDp.taskbarIconSize)
@@ -561,9 +715,9 @@
             }
 
             int positionInHotseat;
-            if (isAllAppsButton) {
-                // Note that there is no All Apps button in the hotseat, this position is only used
-                // as its convenient for animation purposes.
+            if (isAllAppsButton || isTaskbarDividerView) {
+                // Note that there is no All Apps button or taskbar divider view in the hotseat,
+                // this position is only used as its convenient for animation purposes.
                 positionInHotseat = Utilities.isRtl(child.getResources())
                         ? taskbarDp.numShownHotseatIcons
                         : -1;
@@ -587,10 +741,11 @@
                         + hotseatCellSize / 2f;
             }
             float childCenter = (child.getLeft() + child.getRight()) / 2f;
+            childCenter += ((Reorderable) child).getTranslateDelegate().getTranslationX(
+                    INDEX_TASKBAR_PINNING_ANIM).getValue();
             float toX = hotseatIconCenter - childCenter;
             if (child instanceof Reorderable) {
                 MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate();
-
                 setter.setFloat(mtd.getTranslationX(INDEX_TASKBAR_ALIGNMENT_ANIM),
                         MULTI_PROPERTY_VALUE, toX, interpolator);
                 setter.setFloat(mtd.getTranslationY(INDEX_TASKBAR_ALIGNMENT_ANIM),
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index d78ca88..e2f4f32 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -107,8 +107,7 @@
         setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
 
-        boolean exitingOverview = !FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
-                && !toState.overviewUi;
+        boolean exitingOverview = !FeatureFlags.enableSplitContextually() && !toState.overviewUi;
         if (mRecentsView.isSplitSelectionActive() && exitingOverview) {
             setter.add(mRecentsView.getSplitSelectController().getSplitAnimationController()
                     .createPlaceholderDismissAnim(mLauncher));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 4c739fa..009b1bd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -34,7 +34,6 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_HOME_TRANSITION_LISTENER;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
@@ -644,7 +643,7 @@
                     splitSelectSource.alreadyRunningTaskId = taskWasFound
                             ? foundTask.key.id
                             : INVALID_TASK_ID;
-                    if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                    if (FeatureFlags.enableSplitContextually()) {
                         startSplitToHome(splitSelectSource);
                     } else {
                         recentsView.initiateSplitSelect(splitSelectSource);
@@ -718,7 +717,7 @@
 
         super.onPause();
 
-        if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (FeatureFlags.enableSplitContextually()) {
             // If Launcher pauses before both split apps are selected, exit split screen.
             if (!mSplitSelectStateController.isBothSplitAppsConfirmed() &&
                     !mSplitSelectStateController.isLaunchingFirstAppFullscreen()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index b266bcd..f6cd30a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -195,7 +195,7 @@
                     () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
             if (mStartState.overviewUi) {
                 new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState),
-                        FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
+                        FeatureFlags.enableSplitContextually()
                                 ? mCancelSplitRunnable
                                 : null)
                         .animateWithVelocity(velocity);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 25389c5..88933f4 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -267,7 +267,7 @@
         Resources res = context.getResources();
         float maxScale = res.getFloat(R.dimen.overview_max_scale);
         Rect gridRect = new Rect();
-        calculateGridSize(dp, gridRect);
+        calculateGridSize(dp, context, gridRect);
         calculateTaskSizeInternal(context, dp, gridRect, maxScale, Gravity.CENTER, outRect);
     }
 
@@ -321,10 +321,16 @@
     /**
      * Calculates the overview grid size for the provided device configuration.
      */
-    public final void calculateGridSize(DeviceProfile dp, Rect outRect) {
+    public final void calculateGridSize(DeviceProfile dp, Context context, Rect outRect) {
         Rect insets = dp.getInsets();
         int topMargin = dp.overviewTaskThumbnailTopMarginPx;
         int bottomMargin = dp.getOverviewActionsClaimedSpace();
+        if (dp.isTaskbarPresent && Flags.enableGridOnlyOverview()) {
+            topMargin += context.getResources().getDimensionPixelSize(
+                    R.dimen.overview_top_margin_grid_only);
+            bottomMargin += context.getResources().getDimensionPixelSize(
+                    R.dimen.overview_bottom_margin_grid_only);
+        }
         int sideMargin = dp.overviewGridSideMargin;
 
         outRect.set(0, 0, dp.widthPx, dp.heightPx);
@@ -340,7 +346,7 @@
         Resources res = context.getResources();
         Rect potentialTaskRect = new Rect();
         if (Flags.enableGridOnlyOverview()) {
-            calculateGridSize(dp, potentialTaskRect);
+            calculateGridSize(dp, context, potentialTaskRect);
         } else {
             calculateFocusTaskSize(context, dp, potentialTaskRect);
         }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 3fdc7ba..8d26fd4 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE;
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
@@ -60,6 +61,8 @@
 import androidx.annotation.BinderThread;
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
@@ -86,8 +89,8 @@
     static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
 
     // TODO: Move to quickstep contract
-    private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 9;
-    private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 2;
+    private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
+    private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 1.414f;
 
     private final Context mContext;
     private final DisplayController mDisplayController;
@@ -546,15 +549,31 @@
 
     /**
      * Returns the touch slop for {@link InputConsumer}s to compare against before pilfering
-     * pointers. Note that this is squared because it expects to be compared against
-     * {@link com.android.launcher3.Utilities#squaredHypot} (to avoid square root on each event).
+     * pointers.
      */
-    public float getSquaredTouchSlop() {
+    public float getTouchSlop() {
         float slopMultiplier = isFullyGesturalNavMode()
                 ? QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL
                 : QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
         float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-        return slopMultiplier * touchSlop * touchSlop;
+
+        if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
+            float customSlopMultiplier =
+                    LauncherPrefs.get(mContext).get(LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE) / 100f;
+            return customSlopMultiplier * slopMultiplier * touchSlop;
+        } else {
+            return slopMultiplier * touchSlop;
+        }
+    }
+
+    /**
+     * Returns the squared touch slop for {@link InputConsumer}s to compare against before pilfering
+     * pointers. Note that this is squared because it expects to be compared against
+     * {@link com.android.launcher3.Utilities#squaredHypot} (to avoid square root on each event).
+     */
+    public float getSquaredTouchSlop() {
+        float touchSlop = getTouchSlop();
+        return touchSlop * touchSlop;
     }
 
     public String getSystemUiStateString() {
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index cf74b7d..312cdc9 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -190,11 +190,11 @@
          */
         public void endLiveTileMode(@NonNull Runnable callback) {
             RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
-            if (recentsView != null) {
-                recentsView.switchToScreenshot(
-                        () -> recentsView.finishRecentsAnimation(true /* toRecents */,
-                                false /* shouldPip */, callback));
-            }
+            // Task has already been dismissed
+            if (recentsView == null) return;
+            recentsView.switchToScreenshot(
+                    () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+                            false /* shouldPip */, callback));
         }
 
         /**
@@ -212,6 +212,8 @@
 
         private void enterSplitSelect() {
             RecentsView overviewPanel = mThumbnailView.getTaskView().getRecentsView();
+            // Task has already been dismissed
+            if (overviewPanel == null) return;
             overviewPanel.initiateSplitSelect(mThumbnailView.getTaskView());
         }
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index b2f04b8..98a9938 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -963,7 +963,8 @@
                         .append(SUBSTRING_PREFIX)
                         .append("Long press nav handle enabled, ")
                         .append("using NavHandleLongPressInputConsumer");
-                base = new NavHandleLongPressInputConsumer(this, base, mInputMonitorCompat);
+                base = new NavHandleLongPressInputConsumer(this, base, mInputMonitorCompat,
+                        mDeviceState);
             }
 
             if (mDeviceState.isBubblesExpanded()) {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 059b0ce..95ce406 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -254,7 +254,7 @@
             setOverviewSelectEnabled(false);
         }
         if (finalState != OVERVIEW_SPLIT_SELECT) {
-            if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+            if (FeatureFlags.enableSplitContextually()) {
                 mSplitSelectStateController.resetState();
             } else {
                 resetFromSplitSelectionState();
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index f11e537..32f7bd5 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -15,7 +15,6 @@
  */
 package com.android.quickstep.inputconsumers;
 
-import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE;
 import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
@@ -28,6 +27,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
 /**
@@ -47,23 +47,18 @@
     private MotionEvent mCurrentDownEvent;
 
     public NavHandleLongPressInputConsumer(Context context, InputConsumer delegate,
-            InputMonitorCompat inputMonitor) {
+            InputMonitorCompat inputMonitor, RecentsAnimationDeviceState deviceState) {
         super(delegate, inputMonitor);
         mViewConfiguration = ViewConfiguration.get(context);
         mNavHandleWidth = context.getResources().getDimensionPixelSize(
                 R.dimen.navigation_home_handle_width);
         mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
-        float touchSlop;
         if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
-            float customSlopMultiplier =
-                    LauncherPrefs.get(context).get(LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE) / 100f;
-            touchSlop = mViewConfiguration.getScaledEdgeSlop() * customSlopMultiplier;
             mLongPressTimeout = LauncherPrefs.get(context).get(LONG_PRESS_NAV_HANDLE_TIMEOUT_MS);
         } else {
-            touchSlop = mViewConfiguration.getScaledTouchSlop();
             mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
         }
-        mTouchSlopSquared = touchSlop * touchSlop;
+        mTouchSlopSquared = deviceState.getSquaredTouchSlop();
         mNavHandleLongPressHandler = NavHandleLongPressHandler.newInstance(context);
     }
 
@@ -87,17 +82,14 @@
                 }
             }
             case MotionEvent.ACTION_MOVE -> {
+                if (!MAIN_EXECUTOR.getHandler().hasCallbacks(mTriggerLongPress)) {
+                    break;
+                }
+
                 float touchSlopSquared = mTouchSlopSquared;
                 float dx = ev.getX() - mCurrentDownEvent.getX();
                 float dy = ev.getY() - mCurrentDownEvent.getY();
                 double distanceSquared = (dx * dx) + (dy * dy);
-                // If the gesture is ambiguous then require more movement before classifying this
-                // as a NON long press gesture.
-                if (ev.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE) {
-                    float ambiguousGestureMultiplier =
-                            mViewConfiguration.getScaledAmbiguousGestureMultiplier();
-                    touchSlopSquared *= ambiguousGestureMultiplier * ambiguousGestureMultiplier;
-                }
                 if (distanceSquared > touchSlopSquared) {
                     cancelLongPress();
                 }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 28c00eb..e724547 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -40,7 +40,6 @@
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
 
 import androidx.annotation.UiThread;
 
@@ -154,7 +153,7 @@
         boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
         mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
 
-        mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
+        mTouchSlop = mDeviceState.getTouchSlop();
         mSquaredTouchSlop = mDeviceState.getSquaredTouchSlop();
 
         mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index ae497f0..6ee65d4 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -300,7 +300,7 @@
 
         pendingAnimation.addEndListener {
             splitSelectStateController.launchInitialAppFullscreen {
-                if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                if (FeatureFlags.enableSplitContextually()) {
                     splitSelectStateController.resetState()
                 } else if (resetCallback.isPresent) {
                     resetCallback.get().run()
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 8e3b5ec..c873152 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -152,8 +152,7 @@
     private final BackPressHandler mSplitBackHandler = new BackPressHandler() {
         @Override
         public boolean canHandleBack() {
-            return FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() &&
-                    isSplitSelectActive();
+            return FeatureFlags.enableSplitContextually() && isSplitSelectActive();
         }
 
         @Override
@@ -711,7 +710,7 @@
 
     /**
      * To be called whenever we exit split selection state. If
-     * {@link FeatureFlags#ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE} is set, this should be the
+     * {@link FeatureFlags#enableSplitContextually()} is set, this should be the
      * central way split is getting reset, which should then go through the callbacks to reset
      * other state.
      */
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index 00b5621..1b80388 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -17,7 +17,6 @@
 package com.android.quickstep.util;
 
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.animation.Animator;
@@ -40,6 +39,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.data.PackageItemInfo;
@@ -192,7 +192,7 @@
 
     private boolean shouldIgnoreSecondSplitLaunch() {
         return (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()
-                && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
+                && !FeatureFlags.enableSplitContextually()
                 && !DesktopTaskView.DESKTOP_MODE_SUPPORTED)
                 || !mController.isSplitSelectActive();
     }
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index fb9e640..7ff2de9 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -87,7 +87,7 @@
         StateManager stateManager = mActivity.getStateManager();
         animated &= stateManager.shouldAnimateStateChange();
         stateManager.goToState(NORMAL, animated);
-        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (FeatureFlags.enableSplitContextually()) {
             mSplitSelectStateController.getSplitAnimationController()
                     .playPlaceholderDismissAnim(mActivity);
         }
@@ -232,7 +232,7 @@
 
     @Override
     protected boolean canLaunchFullscreenTask() {
-        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (FeatureFlags.enableSplitContextually()) {
             return !mSplitSelectStateController.isSplitSelectActive();
         } else {
             return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 0a195c3..b3d8da7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1087,7 +1087,7 @@
                 mIPipAnimationListener);
         mOrientationState.initListeners();
         mTaskOverlayFactory.initListeners();
-        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (FeatureFlags.enableSplitContextually()) {
             mSplitSelectStateController.registerSplitListener(mSplitSelectionListener);
         }
     }
@@ -1108,7 +1108,7 @@
         mIPipAnimationListener.setActivityAndRecentsView(null, null);
         mOrientationState.destroyListeners();
         mTaskOverlayFactory.removeListeners();
-        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (FeatureFlags.enableSplitContextually()) {
             mSplitSelectStateController.unregisterSplitListener(mSplitSelectionListener);
         }
     }
@@ -2064,7 +2064,7 @@
                 dp.widthPx - mInsets.right - mTempRect.right,
                 dp.heightPx - mInsets.bottom - mTempRect.bottom);
 
-        mSizeStrategy.calculateGridSize(mActivity.getDeviceProfile(),
+        mSizeStrategy.calculateGridSize(mActivity.getDeviceProfile(), mActivity,
                 mLastComputedGridSize);
         mSizeStrategy.calculateGridTaskSize(mActivity, mActivity.getDeviceProfile(),
                 mLastComputedGridTaskSize, mOrientationHandler);
@@ -2418,7 +2418,7 @@
             remoteTargetHandle.getTransformParams().setTargetSet(null);
             remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
         });
-        if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (!FeatureFlags.enableSplitContextually()) {
             resetFromSplitSelectionState();
         }
 
@@ -3307,7 +3307,7 @@
                         InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
             } else {
                 // If transition to split select was interrupted, clean up to prevent glitches
-                if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                if (FeatureFlags.enableSplitContextually()) {
                     mSplitSelectStateController.resetState();
                 } else {
                     resetFromSplitSelectionState();
@@ -4760,7 +4760,7 @@
         pendingAnimation.addEndListener(aBoolean -> {
             mSplitSelectStateController.launchSplitTasks(
                     aBoolean1 -> {
-                        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                        if (FeatureFlags.enableSplitContextually()) {
                             mSplitSelectStateController.resetState();
                         } else {
                             resetFromSplitSelectionState();
@@ -4789,7 +4789,7 @@
     @SuppressLint("WrongCall")
     protected void resetFromSplitSelectionState() {
         if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1 ||
-                FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                FeatureFlags.enableSplitContextually()) {
             safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView());
             safeRemoveDragLayerView(mSecondFloatingTaskView);
             safeRemoveDragLayerView(mSplitSelectStateController.getSplitInstructionsView());
@@ -4809,7 +4809,7 @@
         setTaskViewsPrimarySplitTranslation(0);
         setTaskViewsSecondarySplitTranslation(0);
 
-        if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (!FeatureFlags.enableSplitContextually()) {
             // When flag is on, this method gets called from resetState() call below, let's avoid
             // infinite recursion today
             mSplitSelectStateController.resetState();
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 4ca02e0..e8f06ee 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -99,7 +99,7 @@
     private void init() {
         mTextView = findViewById(R.id.split_instructions_text);
 
-        if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (!FeatureFlags.enableSplitContextually()) {
             mTextView.setCompoundDrawables(null, null, null, null);
             return;
         }
@@ -138,7 +138,7 @@
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-        if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (!FeatureFlags.enableSplitContextually()) {
             return;
         }
 
@@ -150,7 +150,7 @@
 
     @Override
     public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+        if (!FeatureFlags.enableSplitContextually()) {
             return super.performAccessibilityAction(action, arguments);
         }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index beb10ef..fec17b4 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1677,6 +1677,7 @@
         return super.performAccessibilityAction(action, arguments);
     }
 
+    @Nullable
     public RecentsView getRecentsView() {
         return (RecentsView) getParent();
     }
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
index 2c16c15..15b1e53 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
@@ -55,7 +55,7 @@
     @Mock lateinit var taskbarOverlayController: TaskbarOverlayController
     @Mock lateinit var taskbarEduTooltipController: TaskbarEduTooltipController
     @Mock lateinit var keyboardQuickSwitchController: KeyboardQuickSwitchController
-    @Mock lateinit var taskbarPinningController: TaskbarDividerPopupController
+    @Mock lateinit var taskbarPinningController: TaskbarPinningController
     @Mock lateinit var optionalBubbleControllers: Optional<BubbleControllers>
 
     lateinit var taskbarControllers: TaskbarControllers
diff --git a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconAppChipMenuTest.java b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconAppChipMenuTest.java
index 15952c1..969da68 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconAppChipMenuTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconAppChipMenuTest.java
@@ -18,6 +18,7 @@
 import com.android.launcher3.Flags;
 import com.android.launcher3.InvariantDeviceProfile;
 
+import org.junit.After;
 import org.junit.Before;
 
 /**
@@ -35,4 +36,11 @@
         executeOnLauncher(launcher -> InvariantDeviceProfile.INSTANCE.get(launcher).onConfigChanged(
                 launcher));
     }
+
+    @After
+    public void tearDown() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU);
+        executeOnLauncher(launcher -> InvariantDeviceProfile.INSTANCE.get(launcher).onConfigChanged(
+                launcher));
+    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
index eba3252..f6368b0 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
@@ -82,7 +82,7 @@
         taskMenu.touchOutsideTaskMenuToDismiss();
 
         OverviewTaskMenu splitMenu =
-                mLauncher.getOverview().getCurrentTask().tapSplitTaskMenu();
+                mLauncher.goHome().switchToOverview().getCurrentTask().tapSplitTaskMenu();
         assertTrue("App info item not appearing in expanded split task's menu.",
                 splitMenu.hasMenuItem("App info"));
         splitMenu.touchOutsideTaskMenuToDismiss();
diff --git a/res/values/config.xml b/res/values/config.xml
index cd9c9de..4b15a6b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -242,6 +242,8 @@
         <item>@dimen/iconSize72dp</item>
     </integer-array>
 
+    <dimen name="minimum_icon_label_size">8sp</dimen>
+
     <!--  Used for custom widgets  -->
     <array name="custom_widget_providers"/>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index e532dad..81c2337 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -412,6 +412,9 @@
     <dimen name="overview_grid_side_margin">0dp</dimen>
     <dimen name="overview_grid_row_spacing">0dp</dimen>
     <dimen name="overview_page_spacing">0dp</dimen>
+    <dimen name="overview_top_margin_grid_only">0dp</dimen>
+    <dimen name="overview_bottom_margin_grid_only">0dp</dimen>
+
     <dimen name="split_placeholder_size">72dp</dimen>
     <dimen name="split_placeholder_inset">16dp</dimen>
     <dimen name="split_placeholder_icon_size">44dp</dimen>
@@ -423,7 +426,7 @@
     <dimen name="split_instructions_drawable_padding">10dp</dimen>
     <dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen>
     <dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen>
-    
+
     <!-- Workspace grid visualization parameters -->
     <dimen name="grid_visualization_rounding_radius">28dp</dimen>
     <dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 2ac6098..bdffe46 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -63,6 +63,7 @@
 import com.android.launcher3.responsive.HotseatSpecs;
 import com.android.launcher3.responsive.WorkspaceSpecs;
 import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.CellContentDimensions;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.IconSizeSteps;
@@ -299,6 +300,7 @@
     public final int stashedTaskbarHeight;
     public final int taskbarBottomMargin;
     public final int taskbarIconSize;
+    private final int mTransientTaskbarClaimedSpace;
     // If true, used to layout taskbar in 3 button navigation mode.
     public final boolean startAlignTaskbar;
     public final boolean isTransientTaskbar;
@@ -370,18 +372,23 @@
         }
 
         this.isTransientTaskbar = isTransientTaskbar;
+        int transientTaskbarIconSize = pxFromDp(inv.transientTaskbarIconSize[mTypeIndex], mMetrics);
+        int transientTaskbarBottomMargin =
+                res.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin);
+        int transientTaskbarHeight =
+                Math.round((transientTaskbarIconSize * ICON_VISIBLE_AREA_FACTOR)
+                    + (2 * res.getDimensionPixelSize(R.dimen.transient_taskbar_padding)));
+        mTransientTaskbarClaimedSpace = transientTaskbarHeight + 2 * transientTaskbarBottomMargin;
+
         if (!isTaskbarPresent) {
             taskbarIconSize = taskbarHeight = stashedTaskbarHeight = taskbarBottomMargin = 0;
             startAlignTaskbar = false;
         } else if (isTransientTaskbar) {
-            float invTransientIconSizeDp = inv.transientTaskbarIconSize[mTypeIndex];
-            taskbarIconSize = pxFromDp(invTransientIconSizeDp, mMetrics);
-            taskbarHeight = Math.round((taskbarIconSize * ICON_VISIBLE_AREA_FACTOR)
-                    + (2 * res.getDimensionPixelSize(R.dimen.transient_taskbar_padding)));
+            taskbarIconSize = transientTaskbarIconSize;
+            taskbarHeight = transientTaskbarHeight;
             stashedTaskbarHeight =
                     res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_height);
-            taskbarBottomMargin =
-                    res.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin);
+            taskbarBottomMargin = transientTaskbarBottomMargin;
             startAlignTaskbar = false;
         } else {
             taskbarIconSize = pxFromDp(ResourcesCompat.getFloat(res, R.dimen.taskbar_icon_size),
@@ -924,14 +931,11 @@
                 - iconTextHeight;
 
         if (mIsResponsiveGrid) {
-            // Hide text only if doesn't fit inside the cell for responsive grid
-            if (workspaceCellPaddingY < 0) {
-                iconTextSizePx = 0;
-                iconDrawablePaddingPx = 0;
-                int iconSizeWithOverlap = getIconSizeWithOverlap(iconSizePx);
-                cellYPaddingPx = Math.max(0, getCellSize().y - iconSizeWithOverlap) / 2;
-                autoResizeAllAppsCells();
-            }
+            iconTextSizePx = 0;
+            iconDrawablePaddingPx = 0;
+            int iconSizeWithOverlap = getIconSizeWithOverlap(iconSizePx);
+            cellYPaddingPx = Math.max(0, getCellSize().y - iconSizeWithOverlap) / 2;
+            autoResizeAllAppsCells();
 
             return;
         }
@@ -1047,22 +1051,23 @@
                 iconSizePx = mIconSizeSteps.getIconSmallerThan(cellWidthPx);
             }
 
-            // TODO(b/296400197): isVerticalBar shouldn't show labels anymore
             iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
-            int iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx);
-            int cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
 
-            while (iconSizePx > mIconSizeSteps.minimumIconSize()
-                    && cellContentHeight > cellHeightPx) {
-                iconDrawablePaddingPx -= cellContentHeight - cellHeightPx;
-                if (iconDrawablePaddingPx < 0) {
-                    // get a smaller icon size
-                    iconSizePx = mIconSizeSteps.getNextLowerIconSize(iconSizePx);
-                    iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
+            CellContentDimensions cellContentDimensions = new CellContentDimensions(iconSizePx,
+                    iconDrawablePaddingPx,
+                    iconTextSizePx);
+            if (isVerticalLayout) {
+                if (cellHeightPx < iconSizePx) {
+                    cellContentDimensions.setIconSizePx(
+                            mIconSizeSteps.getIconSmallerThan(cellHeightPx));
                 }
-                // calculate new cellContentHeight
-                cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
+            } else {
+                cellContentDimensions.resizeToFitCellHeight(cellHeightPx, mIconSizeSteps);
             }
+            iconSizePx = cellContentDimensions.getIconSizePx();
+            iconDrawablePaddingPx = cellContentDimensions.getIconDrawablePaddingPx();
+            iconTextSizePx = cellContentDimensions.getIconTextSizePx();
+            int cellContentHeight = cellContentDimensions.getCellContentHeight();
 
             cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
         } else if (mIsScalableGrid) {
@@ -1760,14 +1765,9 @@
         return getHotseatBarBottomPadding() + launcherIconBottomSpace - taskbarIconBottomSpace;
     }
 
-    /**
-     * Returns the number of pixels required below OverviewActions excluding insets.
-     */
+    /** Returns the number of pixels required below OverviewActions. */
     public int getOverviewActionsClaimedSpaceBelow() {
-        if (isTaskbarPresent) {
-            return taskbarHeight + taskbarBottomMargin * 2;
-        }
-        return mInsets.bottom;
+        return isTaskbarPresent ? mTransientTaskbarClaimedSpace : mInsets.bottom;
     }
 
     /** Gets the space that the overview actions will take, including bottom margin. */
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c0520c1..9255ff4 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1578,7 +1578,7 @@
                 }
             }
 
-            if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+            if (FeatureFlags.enableSplitContextually()) {
                 handleSplitAnimationGoingToHome();
             }
             mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index c6a9283..4f8caab 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -20,11 +20,12 @@
 import android.content.SharedPreferences
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener
 import android.util.Log
-import android.view.ViewConfiguration
 import androidx.annotation.VisibleForTesting
 import com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN
 import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
 import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
+import com.android.launcher3.config.FeatureFlags.LPNH_SLOP_PERCENTAGE
+import com.android.launcher3.config.FeatureFlags.LPNH_TIMEOUT_MS
 import com.android.launcher3.model.DeviceGridState
 import com.android.launcher3.pm.InstallSessionHelper
 import com.android.launcher3.provider.RestoreDbTask
@@ -310,16 +311,16 @@
             )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
-                nonRestorableItem(
-                        "pref_long_press_nav_handle_slop_multiplier",
-                        100,
+            nonRestorableItem(
+                "pref_long_press_nav_handle_slop_percentage",
+                        LPNH_SLOP_PERCENTAGE.get(),
                         EncryptionType.MOVE_TO_DEVICE_PROTECTED
-                )
+            )
         @JvmField
         val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
                 nonRestorableItem(
                         "pref_long_press_nav_handle_timeout_ms",
-                        ViewConfiguration.getLongPressTimeout(),
+                        LPNH_TIMEOUT_MS.get(),
                         EncryptionType.MOVE_TO_DEVICE_PROTECTED
                 )
         @JvmField
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 9c0a508..c0d0662 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -21,8 +21,11 @@
 import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED;
 import static com.android.launcher3.config.FeatureFlags.FlagState.TEAMFOOD;
 import static com.android.launcher3.uioverrides.flags.FlagsFactory.getDebugFlag;
+import static com.android.launcher3.uioverrides.flags.FlagsFactory.getIntFlag;
 import static com.android.launcher3.uioverrides.flags.FlagsFactory.getReleaseFlag;
 
+import android.view.ViewConfiguration;
+
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.BuildConfig;
@@ -113,9 +116,17 @@
                     "Allow entering All Apps from Overview (e.g. long swipe up from app)");
 
     public static final BooleanFlag CUSTOM_LPNH_THRESHOLDS =
-            getDebugFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", DISABLED,
+            getReleaseFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", DISABLED,
                     "Add dev options to customize the LPNH trigger slop and milliseconds");
 
+    public static final IntFlag LPNH_SLOP_PERCENTAGE =
+            getIntFlag(301680992, "LPNH_SLOP_PERCENTAGE", 100,
+                    "Controls touch slop percentage for lpnh");
+
+    public static final IntFlag LPNH_TIMEOUT_MS =
+            getIntFlag(301680992, "LPNH_TIMEOUT_MS", ViewConfiguration.getLongPressTimeout(),
+                    "Controls lpnh timeout in milliseconds");
+
     public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag(
             270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", ENABLED,
             "Enable option to show keyboard when going to all-apps");
@@ -202,9 +213,13 @@
     public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(270395798,
             "ENABLE_TRANSIENT_TASKBAR", ENABLED, "Enables transient taskbar.");
 
+    // Aconfig migration complete for ENABLE_TASKBAR_NO_RECREATION.
     public static final BooleanFlag ENABLE_TASKBAR_NO_RECREATION = getDebugFlag(299193589,
             "ENABLE_TASKBAR_NO_RECREATION", DISABLED,
             "Enables taskbar with no recreation from lifecycle changes of TaskbarActivityContext.");
+    public static boolean enableTaskbarNoRecreate() {
+        return ENABLE_TASKBAR_NO_RECREATION.get() || Flags.enableTaskbarNoRecreate();
+    }
 
     // TODO(Block 16): Clean up flags
     // When enabled the promise icon is visible in all apps while installation an app.
@@ -379,6 +394,10 @@
     public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE = getDebugFlag(
             270393453, "ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", DISABLED,
             "Enable initiating split screen from workspace to workspace.");
+    public static boolean enableSplitContextually() {
+        return ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() ||
+                com.android.wm.shell.Flags.enableSplitContextual();
+    }
 
     public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401,
             "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture.");
diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java
index 8167b97..1360510 100644
--- a/src/com/android/launcher3/model/DatabaseHelper.java
+++ b/src/com/android/launcher3/model/DatabaseHelper.java
@@ -269,7 +269,6 @@
                                     Favorites.CONTAINER, Favorites.CONTAINER_DESKTOP,
                                     Favorites.CELLY, 0), null);
                 }
-                return;
             }
             case 31: {
                 LauncherDbUtils.migrateLegacyShortcuts(mContext, db);
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index a09e5a4..0c322cc 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -138,8 +138,9 @@
         // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
         if (launcher.getDragController().isDragging()) return false;
         // Return early if user is in the middle of selecting split-screen apps
-        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() &&
-                launcher.isSplitSelectionEnabled()) return false;
+        if (FeatureFlags.enableSplitContextually() && launcher.isSplitSelectionEnabled()) {
+            return false;
+        }
 
         return true;
     }
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 1232069..5b6c9e0 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -206,8 +206,7 @@
                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
                 mLauncher.getStatsLogManager().logger().log(LAUNCHER_WORKSPACE_LONGPRESS);
                 mLauncher.showDefaultOptions(mTouchDownPoint.x, mTouchDownPoint.y);
-                if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() &&
-                        mLauncher.isSplitSelectionEnabled()) {
+                if (FeatureFlags.enableSplitContextually() && mLauncher.isSplitSelectionEnabled()) {
                     mLauncher.dismissSplitSelection();
                 }
             } else {
diff --git a/src/com/android/launcher3/util/CellContentDimensions.kt b/src/com/android/launcher3/util/CellContentDimensions.kt
new file mode 100644
index 0000000..3c8e0c4
--- /dev/null
+++ b/src/com/android/launcher3/util/CellContentDimensions.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.util
+
+import com.android.launcher3.Utilities
+import kotlin.math.max
+
+class CellContentDimensions(
+    var iconSizePx: Int,
+    var iconDrawablePaddingPx: Int,
+    var iconTextSizePx: Int
+) {
+    /**
+     * This method goes through some steps to reduce the padding between icon and label, icon size
+     * and then label size, until it can fit in the [cellHeightPx].
+     *
+     * @return the height of the content after being sized down.
+     */
+    fun resizeToFitCellHeight(cellHeightPx: Int, iconSizeSteps: IconSizeSteps): Int {
+        var cellContentHeight = getCellContentHeight()
+
+        // Step 1. Decrease drawable padding
+        if (cellContentHeight > cellHeightPx) {
+            val diff = cellContentHeight - cellHeightPx
+            iconDrawablePaddingPx = max(0, iconDrawablePaddingPx - diff)
+            cellContentHeight = getCellContentHeight()
+        }
+
+        while (
+            (iconTextSizePx > iconSizeSteps.minimumIconLabelSize ||
+                iconSizePx > iconSizeSteps.minimumIconSize()) && cellContentHeight > cellHeightPx
+        ) {
+            // Step 2. Decrease icon size
+            iconSizePx = iconSizeSteps.getNextLowerIconSize(iconSizePx)
+            cellContentHeight = getCellContentHeight()
+
+            // Step 3. Decrease label size
+            if (cellContentHeight > cellHeightPx) {
+                iconTextSizePx =
+                    max(
+                        iconSizeSteps.minimumIconLabelSize,
+                        iconTextSizePx - IconSizeSteps.TEXT_STEP
+                    )
+                cellContentHeight = getCellContentHeight()
+            }
+        }
+
+        return cellContentHeight
+    }
+
+    /** Calculate new cellContentHeight */
+    fun getCellContentHeight(): Int {
+        val iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx.toFloat())
+        return iconSizePx + iconDrawablePaddingPx + iconTextHeight
+    }
+}
diff --git a/src/com/android/launcher3/util/IconSizeSteps.kt b/src/com/android/launcher3/util/IconSizeSteps.kt
index 2a5afe0..aa644b0 100644
--- a/src/com/android/launcher3/util/IconSizeSteps.kt
+++ b/src/com/android/launcher3/util/IconSizeSteps.kt
@@ -23,12 +23,14 @@
 
 class IconSizeSteps(res: Resources) {
     private val steps: List<Int>
+    val minimumIconLabelSize: Int
 
     init {
         steps =
             res.obtainTypedArray(R.array.icon_size_steps).use {
                 (0 until it.length()).map { step -> it.getDimensionOrThrow(step).toInt() }.sorted()
             }
+        minimumIconLabelSize = res.getDimensionPixelSize(R.dimen.minimum_icon_label_size)
     }
 
     fun minimumIconSize(): Int = steps[0]
@@ -44,4 +46,8 @@
     private fun getIndexForIconSize(iconSizePx: Int): Int {
         return max(0, steps.indexOfFirst { iconSizePx <= it })
     }
+
+    companion object {
+        internal const val TEXT_STEP = 1
+    }
 }
diff --git a/src/com/android/launcher3/util/MultiTranslateDelegate.java b/src/com/android/launcher3/util/MultiTranslateDelegate.java
index 6f6392f..84ef445 100644
--- a/src/com/android/launcher3/util/MultiTranslateDelegate.java
+++ b/src/com/android/launcher3/util/MultiTranslateDelegate.java
@@ -36,6 +36,7 @@
     // Specific for items in taskbar (icons, folders, qsb)
     public static final int INDEX_TASKBAR_ALIGNMENT_ANIM = 3;
     public static final int INDEX_TASKBAR_REVEAL_ANIM = 4;
+    public static final int INDEX_TASKBAR_PINNING_ANIM = 5;
 
     // Affect all items inside of a MultipageCellLayout
     public static final int INDEX_CELLAYOUT_MULTIPAGE_SPACING = 3;
@@ -46,7 +47,7 @@
     // Specific for hotseat items when adjusting for bubbles
     public static final int INDEX_BUBBLE_ADJUSTMENT_ANIM = 3;
 
-    public static final int COUNT = 5;
+    public static final int COUNT = 6;
 
     private final MultiPropertyFactory<View> mTranslationX;
     private final MultiPropertyFactory<View> mTranslationY;
diff --git a/tests/Android.bp b/tests/Android.bp
index a3d05bb..8725b46 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -40,19 +40,14 @@
       "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
       "src/com/android/launcher3/ui/TestViewHelpers.java",
       "src/com/android/launcher3/util/LauncherLayoutBuilder.java",
+      "src/com/android/launcher3/util/ModelTestExtensions.kt",
       "src/com/android/launcher3/util/TestConstants.java",
       "src/com/android/launcher3/util/TestUtil.java",
       "src/com/android/launcher3/util/Wait.java",
       "src/com/android/launcher3/util/WidgetUtils.java",
-      "src/com/android/launcher3/util/rule/FailureWatcher.java",
-      "src/com/android/launcher3/util/rule/ViewCaptureRule.kt",
+      "src/com/android/launcher3/util/rule/*.java",
+      "src/com/android/launcher3/util/rule/*.kt",
       "src/com/android/launcher3/util/viewcapture_analysis/*.java",
-      "src/com/android/launcher3/util/rule/SamplerRule.java",
-      "src/com/android/launcher3/util/rule/ScreenRecordRule.java",
-      "src/com/android/launcher3/util/rule/ShellCommandRule.java",
-      "src/com/android/launcher3/util/rule/TestIsolationRule.java",
-      "src/com/android/launcher3/util/rule/TestStabilityRule.java",
-      "src/com/android/launcher3/util/ModelTestExtensions.kt",
       "src/com/android/launcher3/testcomponent/*.java",
       "src/com/android/launcher3/testcomponent/*.kt",
     ],
diff --git a/tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt b/tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt
new file mode 100644
index 0000000..4770546
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt
@@ -0,0 +1,148 @@
+/*
+ * 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.util
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.DisplayMetrics
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CellContentDimensionsTest {
+    private var context: Context? = null
+    private val runningContext: Context = ApplicationProvider.getApplicationContext()
+    private lateinit var iconSizeSteps: IconSizeSteps
+
+    @Before
+    fun setup() {
+        // 160dp makes 1px = 1dp
+        val config =
+            Configuration(runningContext.resources.configuration).apply {
+                this.densityDpi = DisplayMetrics.DENSITY_DEFAULT
+            }
+        context = runningContext.createConfigurationContext(config)
+        iconSizeSteps = IconSizeSteps(context!!.resources)
+    }
+
+    @Test
+    fun dimensionsFitTheCell() {
+        val cellSize = Pair(80, 104)
+        val cellContentDimensions =
+            CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+        val contentHeight =
+            cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+        assertThat(contentHeight).isEqualTo(93)
+        cellContentDimensions.run {
+            assertThat(iconSizePx).isEqualTo(66)
+            assertThat(iconDrawablePaddingPx).isEqualTo(8)
+            assertThat(iconTextSizePx).isEqualTo(14)
+        }
+    }
+
+    @Test
+    fun decreasePadding() {
+        val cellSize = Pair(67, 87)
+        val cellContentDimensions =
+            CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+        val contentHeight =
+            cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+        assertThat(contentHeight).isEqualTo(87)
+        cellContentDimensions.run {
+            assertThat(iconSizePx).isEqualTo(66)
+            assertThat(iconDrawablePaddingPx).isEqualTo(2)
+            assertThat(iconTextSizePx).isEqualTo(14)
+        }
+    }
+
+    @Test
+    fun decreaseIcon() {
+        val cellSize = Pair(65, 84)
+        val cellContentDimensions =
+            CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+        val contentHeight =
+            cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+        assertThat(contentHeight).isEqualTo(82)
+        cellContentDimensions.run {
+            assertThat(iconSizePx).isEqualTo(63)
+            assertThat(iconDrawablePaddingPx).isEqualTo(0)
+            assertThat(iconTextSizePx).isEqualTo(14)
+        }
+    }
+
+    @Test
+    fun decreaseText() {
+        val cellSize = Pair(63, 81)
+        val cellContentDimensions =
+            CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+        val contentHeight =
+            cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+        assertThat(contentHeight).isEqualTo(81)
+        cellContentDimensions.run {
+            assertThat(iconSizePx).isEqualTo(63)
+            assertThat(iconDrawablePaddingPx).isEqualTo(0)
+            assertThat(iconTextSizePx).isEqualTo(13)
+        }
+    }
+
+    @Test
+    fun decreaseIconAndTextTwoSteps() {
+        val cellSize = Pair(60, 78)
+        val cellContentDimensions =
+            CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+        val contentHeight =
+            cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+        assertThat(contentHeight).isEqualTo(77)
+        cellContentDimensions.run {
+            assertThat(iconSizePx).isEqualTo(61)
+            assertThat(iconDrawablePaddingPx).isEqualTo(0)
+            assertThat(iconTextSizePx).isEqualTo(12)
+        }
+    }
+
+    @Test
+    fun decreaseIconAndTextToMinimum() {
+        val cellSize = Pair(52, 63)
+        val cellContentDimensions =
+            CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+        val contentHeight =
+            cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+        assertThat(contentHeight).isEqualTo(63)
+        cellContentDimensions.run {
+            assertThat(iconSizePx).isEqualTo(52)
+            assertThat(iconDrawablePaddingPx).isEqualTo(0)
+            assertThat(iconTextSizePx).isEqualTo(8)
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/SetFlagsRuleExt.kt b/tests/src/com/android/launcher3/util/rule/SetFlagsRuleExt.kt
new file mode 100644
index 0000000..d682456
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/SetFlagsRuleExt.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util.rule
+
+import android.platform.test.flag.junit.SetFlagsRule
+
+fun SetFlagsRule.setFlags(enabled: Boolean, vararg flagName: String) {
+    if (enabled) enableFlags(*flagName) else disableFlags(*flagName)
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
index 25c73de..38cc321 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.tapl;
 
-import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiObject2;
@@ -61,14 +59,8 @@
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "before tapping the app info menu item")) {
-
-            mLauncher.executeAndWaitForLauncherEvent(
-                    () -> mLauncher.clickLauncherObject(
-                            mLauncher.findObjectInContainer(mMenu, By.text("App info"))),
-                    accessibilityEvent ->
-                            accessibilityEvent.getEventType() == TYPE_WINDOW_STATE_CHANGED,
-                    () -> "Unable to start Settings by clicking App Info",
-                    "Tap the app info menu item");
+            mLauncher.clickLauncherObject(
+                    mLauncher.findObjectInContainer(mMenu, By.text("App info")));
 
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                     "tapped app info menu item")) {