Merge "Fix Launcher3 issues due to hardcoded default user" into udc-qpr-dev
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index b28341a..1b5b0ee 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -32,6 +32,10 @@
     <color name="taskbar_stashed_handle_light_color">#EBffffff</color>
     <color name="taskbar_stashed_handle_dark_color">#99000000</color>
 
+    <!-- Floating rotation button -->
+    <color name="floating_rotation_button_light_color">#ffffff</color>
+    <color name="floating_rotation_button_dark_color">#99000000</color>
+
     <!-- Gesture navigation tutorial -->
     <color name="gesture_tutorial_back_arrow_color">#FFFFFFFF</color>
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 4d7eb63..31af1ce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -226,8 +226,8 @@
                         ? new DesktopNavbarButtonsViewController(this, navButtonsView)
                         : new NavbarButtonsViewController(this, navButtonsView),
                 new RotationButtonController(this,
-                        c.getColor(R.color.taskbar_nav_icon_light_color),
-                        c.getColor(R.color.taskbar_nav_icon_dark_color),
+                        c.getColor(R.color.floating_rotation_button_light_color),
+                        c.getColor(R.color.floating_rotation_button_dark_color),
                         R.drawable.ic_sysbar_rotate_button_ccw_start_0,
                         R.drawable.ic_sysbar_rotate_button_ccw_start_90,
                         R.drawable.ic_sysbar_rotate_button_cw_start_0,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
index 2517ff6..70999e7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
@@ -41,8 +41,10 @@
     public static final int FLAG_AUTOHIDE_SUSPEND_TOUCHING = 1 << 2;
     // Taskbar EDU overlay is open above the Taskbar. */
     public static final int FLAG_AUTOHIDE_SUSPEND_EDU_OPEN = 1 << 3;
-    // Taskbar in immersive mode in overview
+    // Taskbar is in immersive mode in overview
     public static final int FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER = 1 << 4;
+    // Transient Taskbar is temporarily unstashed (pending a timeout).
+    public static final int FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR = 1 << 5;
 
     @IntDef(flag = true, value = {
             FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
@@ -50,6 +52,7 @@
             FLAG_AUTOHIDE_SUSPEND_TOUCHING,
             FLAG_AUTOHIDE_SUSPEND_EDU_OPEN,
             FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER,
+            FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AutohideSuspendFlag {}
@@ -85,20 +88,30 @@
 
         boolean isSuspended = isSuspended();
         mSystemUiProxy.notifyTaskbarAutohideSuspend(isSuspended);
-        mActivity.onTransientAutohideSuspendFlagChanged(isSuspended);
+        mActivity.onTransientAutohideSuspendFlagChanged(isTransientTaskbarStashingSuspended());
     }
 
     /**
      * Returns true iff taskbar autohide is currently suspended.
      */
-    public boolean isSuspended() {
+    private boolean isSuspended() {
         return mAutohideSuspendFlags != 0;
     }
 
-    public boolean isSuspendedForTransientTaskbarInOverview() {
+    /**
+     * Returns whether Transient Taskbar should avoid auto-stashing in Launcher(Overview).
+     */
+    public boolean isSuspendedForTransientTaskbarInLauncher() {
         return (mAutohideSuspendFlags & FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER) != 0;
     }
 
+    /**
+     * Returns whether Transient Taskbar should avoid auto-stashing.
+     */
+    public boolean isTransientTaskbarStashingSuspended() {
+        return (mAutohideSuspendFlags & ~FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR) != 0;
+    }
+
     @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "TaskbarAutohideSuspendController:");
@@ -115,6 +128,8 @@
         appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_EDU_OPEN, "FLAG_AUTOHIDE_SUSPEND_EDU_OPEN");
         appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER,
                 "FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER");
+        appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
+                "FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR");
         return str.toString();
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index fab70d3..5e37cf4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -522,9 +522,12 @@
             return;
         }
 
-        if (stash && mControllers.taskbarAutohideSuspendController.isSuspended()
+        if (
+                stash
                 && !mControllers.taskbarAutohideSuspendController
-                .isSuspendedForTransientTaskbarInOverview()) {
+                .isSuspendedForTransientTaskbarInLauncher()
+                && mControllers.taskbarAutohideSuspendController
+                .isTransientTaskbarStashingSuspended()) {
             // Avoid stashing if autohide is currently suspended.
             return;
         }
@@ -1088,6 +1091,9 @@
             mActivity.getStatsLogManager().logger().log(hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)
                     ? LAUNCHER_TRANSIENT_TASKBAR_HIDE
                     : LAUNCHER_TRANSIENT_TASKBAR_SHOW);
+            mControllers.taskbarAutohideSuspendController.updateFlag(
+                    TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
+                    !hasAnyFlag(FLAG_STASHED_IN_APP_AUTO));
         }
     }
 
@@ -1180,7 +1186,7 @@
     }
 
     private void onTaskbarTimeout(Alarm alarm) {
-        if (mControllers.taskbarAutohideSuspendController.isSuspended()) {
+        if (mControllers.taskbarAutohideSuspendController.isTransientTaskbarStashingSuspended()) {
             return;
         }
         updateAndAnimateTransientTaskbarForTimeout();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 2bd6fcb..6b5c962 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -185,16 +185,26 @@
             mBubbleBarViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED);
             mBubbleStashedHandleViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED);
         });
+    }
 
-        BUBBLE_STATE_EXECUTOR.execute(() -> {
-            if (mOverflowBubble == null) {
-                BubbleBarOverflow overflow = createOverflow(mContext);
-                mMainExecutor.execute(() -> {
+    /**
+     * Creates and adds the overflow bubble to the bubble bar if it hasn't been created yet.
+     *
+     * <p>This should be called on the {@link #BUBBLE_STATE_EXECUTOR} executor to avoid inflating
+     * the overflow multiple times.
+     */
+    private void createAndAddOverflowIfNeeded() {
+        if (mOverflowBubble == null) {
+            BubbleBarOverflow overflow = createOverflow(mContext);
+            mMainExecutor.execute(() -> {
+                // we're on the main executor now, so check that the overflow hasn't been created
+                // again to avoid races.
+                if (mOverflowBubble == null) {
                     mBubbleBarViewController.addBubble(overflow);
                     mOverflowBubble = overflow;
-                });
-            }
-        });
+                }
+            });
+        }
     }
 
     /**
@@ -226,6 +236,7 @@
                 || !update.currentBubbleList.isEmpty()) {
             // We have bubbles to load
             BUBBLE_STATE_EXECUTOR.execute(() -> {
+                createAndAddOverflowIfNeeded();
                 if (update.addedBubble != null) {
                     viewUpdate.addedBubble = populateBubble(update.addedBubble, mContext, mBarView);
                 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 8c8e267..d241260 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -46,6 +46,7 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
@@ -113,7 +114,9 @@
         setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
 
-        if (mRecentsView.isSplitSelectionActive()) {
+        boolean exitingOverview = !FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
+                || !toState.overviewUi;
+        if (mRecentsView.isSplitSelectionActive() && exitingOverview) {
             // TODO (b/238651489): Refactor state management to avoid need for double check
             FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView();
             if (floatingTask != null) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index e61599f..2064fe2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -189,7 +189,7 @@
         } else {
             float[] hctPlateColor = new float[3];
             ColorUtils.colorToM3HCT(mDotParams.appColor, hctPlateColor);
-            newPlateColor = ColorUtils.M3HCTtoColor(hctPlateColor[0], 36, 85);
+            newPlateColor = ColorUtils.M3HCTToColor(hctPlateColor[0], 36, 85);
         }
 
         if (!animate) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index ecc8e19..512d5f4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -615,9 +615,10 @@
         mSplitSelectStateController.findLastActiveTaskAndRunCallback(
                 splitSelectSource.itemInfo.getComponentKey(),
                 foundTask -> {
-                    splitSelectSource.alreadyRunningTaskId = foundTask == null
-                            ? INVALID_TASK_ID
-                            : foundTask.key.id;
+                    boolean taskWasFound = foundTask != null;
+                    splitSelectSource.alreadyRunningTaskId = taskWasFound
+                            ? foundTask.key.id
+                            : INVALID_TASK_ID;
                     if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
                         startSplitToHome(splitSelectSource);
                     } else {
@@ -1295,7 +1296,7 @@
                 groupTask.task1.key.id,
                 groupTask.task2.key.id,
                 SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
-                /* callback= */ success -> {},
+                /* callback= */ success -> mSplitSelectStateController.resetState(),
                 /* freezeTaskList= */ true,
                 groupTask.mSplitBounds == null
                         ? DEFAULT_SPLIT_RATIO
@@ -1304,6 +1305,13 @@
                                 : groupTask.mSplitBounds.leftTaskPercent);
     }
 
+    @Override
+    public boolean isCommandQueueEmpty() {
+        OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper();
+        return super.isCommandQueueEmpty()
+                && (overviewCommandHelper == null || overviewCommandHelper.isCommandQueueEmpty());
+    }
+
     private static final class LauncherTaskViewController extends
             TaskViewTouchController<Launcher> {
 
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index a0d49a4..4a60566 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -140,6 +140,11 @@
         mPendingCommands.clear();
     }
 
+    @UiThread
+    public boolean isCommandQueueEmpty() {
+        return mPendingCommands.isEmpty();
+    }
+
     @Nullable
     private TaskView getNextTask(RecentsView view) {
         final TaskView runningTaskView = view.getRunningTaskView();
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index bf96690..e282d1f 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -466,4 +466,11 @@
             }
         };
     }
+
+    @Override
+    public boolean isCommandQueueEmpty() {
+        OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper();
+        return super.isCommandQueueEmpty()
+                && (overviewCommandHelper == null || overviewCommandHelper.isCommandQueueEmpty());
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 27fb476..84246e9 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -19,6 +19,7 @@
 import android.app.WindowConfiguration;
 import android.content.Context;
 import android.graphics.Rect;
+import android.util.Log;
 import android.view.RemoteAnimationTarget;
 
 import androidx.annotation.Nullable;
@@ -37,6 +38,8 @@
  * {@link TaskViewSimulator}
  */
 public class RemoteTargetGluer {
+    private static final String TAG = "RemoteTargetGluer";
+
     private static final int DEFAULT_NUM_HANDLES = 2;
 
     private RemoteTargetHandle[] mRemoteTargetHandles;
@@ -118,7 +121,9 @@
         long appCount = Arrays.stream(targets.apps)
                 .filter(app -> app.mode == targets.targetMode)
                 .count();
+        Log.d(TAG, "appCount: " + appCount + " handleLength: " + mRemoteTargetHandles.length);
         if (appCount < mRemoteTargetHandles.length) {
+            Log.d(TAG, "resizing handles");
             RemoteTargetHandle[] newHandles = new RemoteTargetHandle[(int) appCount];
             System.arraycopy(mRemoteTargetHandles, 0/*src*/, newHandles, 0/*dst*/, (int) appCount);
             mRemoteTargetHandles = newHandles;
@@ -128,6 +133,8 @@
                 .anyMatch(remoteAnimationTarget ->
                         remoteAnimationTarget.windowConfiguration.getWindowingMode()
                                 == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+        Log.d(TAG, "containsSplitTargets? " + containsSplitTargets + " handleLength: " +
+                mRemoteTargetHandles.length + " appsLength: " + targets.apps.length);
 
         if (mRemoteTargetHandles.length == 1) {
             // Single fullscreen app
diff --git a/quickstep/src/com/android/quickstep/SplitSelectionListener.kt b/quickstep/src/com/android/quickstep/SplitSelectionListener.kt
new file mode 100644
index 0000000..5025c1c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SplitSelectionListener.kt
@@ -0,0 +1,17 @@
+package com.android.quickstep
+
+interface SplitSelectionListener {
+    /** Called when the first app has been selected with the intention to launch split screen */
+    fun onSplitSelectionActive()
+
+    /** Called when the second app has been selected with the intention to launch split screen */
+    fun onSplitSelectionConfirmed()
+
+    /**
+     * Called when the user no longer is in the process of selecting apps for split screen.
+     * [launchedSplit] will be true if selected apps have launched successfully (either in
+     * split screen or fullscreen), false if the user canceled/exited the selection process
+     */
+    fun onSplitSelectionExit(launchedSplit: Boolean) {
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 074aedd..a9a57c6 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.SplitConfigurationOptions;
@@ -79,11 +80,16 @@
     }
 
     @Override
-    public void startHome(boolean animated) {
+    protected void handleStartHome(boolean animated) {
         mActivity.startHome();
         AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
     }
 
+    @Override
+    public boolean isCommandQueueEmpty() {
+        return mActivity.isCommandQueueEmpty();
+    }
+
     /**
      * When starting gesture interaction from home, we add a temporary invisible tile corresponding
      * to the home task. This allows us to handle quick-switch similarly to a quick-switching
@@ -246,7 +252,11 @@
             setOverviewSelectEnabled(false);
         }
         if (finalState != OVERVIEW_SPLIT_SELECT) {
-            resetFromSplitSelectionState();
+            if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                mSplitSelectStateController.resetState();
+            } else {
+                resetFromSplitSelectionState();
+            }
         }
 
         if (isOverlayEnabled) {
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 2123253..e063b44 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -73,16 +73,18 @@
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SplitSelectionListener;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.views.FloatingTaskView;
 import com.android.quickstep.views.GroupedTaskView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -138,6 +140,8 @@
 
     private FloatingTaskView mFirstFloatingTaskView;
 
+    private final List<SplitSelectionListener> mSplitSelectionListeners = new ArrayList<>();
+
     public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
             DepthController depthController, StatsLogManager statsLogManager,
             SystemUiProxy systemUiProxy, RecentsModel recentsModel) {
@@ -248,6 +252,27 @@
     }
 
     /**
+     * Listener will only get callbacks going forward from the point of registration. No
+     * methods will be fired upon registering.
+     */
+    public void registerSplitListener(@NonNull SplitSelectionListener listener) {
+        if (mSplitSelectionListeners.contains(listener)) {
+            return;
+        }
+        mSplitSelectionListeners.add(listener);
+    }
+
+    public void unregisterSplitListener(@NonNull SplitSelectionListener listener) {
+        mSplitSelectionListeners.remove(listener);
+    }
+
+    private void dispatchOnSplitSelectionExit() {
+        for (SplitSelectionListener listener : mSplitSelectionListeners) {
+            listener.onSplitSelectionExit(false);
+        }
+    }
+
+    /**
      * To be called when the actual tasks ({@link #mInitialTaskId}, {@link #mSecondTaskId}) are
      * to be launched. Call after launcher side animations are complete.
      */
@@ -790,12 +815,16 @@
     }
 
     /**
-     * To be called if split select was cancelled
+     * To be called whenever we exit split selection state. If
+     * {@link FeatureFlags#ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE} is set, this should be the
+     * central way split is getting reset, which should then go through the callbacks to reset
+     * other state.
      */
     public void resetState() {
         if (FeatureFlags.ENABLE_SPLIT_LAUNCH_DATA_REFACTOR.get()) {
             mSplitSelectDataHolder.resetState();
         }
+        dispatchOnSplitSelectionExit();
         mInitialTaskId = INVALID_TASK_ID;
         mInitialTaskIntent = null;
         mSecondTaskId = INVALID_TASK_ID;
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 6813857..19ac1f8 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep.views;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW;
+
 import android.content.Context;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -248,8 +250,15 @@
      */
     private float getOriginalTranslationY() {
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-        return deviceProfile.isTablet
-                ? deviceProfile.overviewRowSpacing
-                : deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f;
+        if (deviceProfile.isTablet) {
+            if (ENABLE_GRID_ONLY_OVERVIEW.get()) {
+                return (getRecentsView().getLastComputedTaskSize().height()
+                        + deviceProfile.overviewTaskThumbnailTopMarginPx) / 2.0f
+                        + deviceProfile.overviewRowSpacing;
+            } else {
+                return deviceProfile.overviewRowSpacing;
+            }
+        }
+        return deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 4dbf4e3..9e9b22f 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -37,6 +37,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DesktopVisibilityController;
@@ -81,7 +82,7 @@
     }
 
     @Override
-    public void startHome(boolean animated) {
+    protected void handleStartHome(boolean animated) {
         StateManager stateManager = mActivity.getStateManager();
         animated &= stateManager.shouldAnimateStateChange();
         stateManager.goToState(NORMAL, animated);
@@ -89,6 +90,11 @@
     }
 
     @Override
+    public boolean isCommandQueueEmpty() {
+        return mActivity.isCommandQueueEmpty();
+    }
+
+    @Override
     protected void onTaskLaunchAnimationEnd(boolean success) {
         if (success) {
             mActivity.getStateManager().moveToRestState();
@@ -218,7 +224,11 @@
 
     @Override
     protected boolean canLaunchFullscreenTask() {
-        return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
+        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+            return !mSplitSelectStateController.isSplitSelectActive();
+        } else {
+            return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 4d88a04..f0daf8d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -173,6 +173,7 @@
 import com.android.quickstep.RemoteTargetGluer;
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.RotationTouchHelper;
+import com.android.quickstep.SplitSelectionListener;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskThumbnailCache;
@@ -662,7 +663,8 @@
     /**
      * Placeholder view indicating where the first split screen selected app will be placed
      */
-    private SplitSelectStateController mSplitSelectStateController;
+    protected SplitSelectStateController mSplitSelectStateController;
+
     /**
      * The first task that split screen selection was initiated with. When split select state is
      * initialized, we create a
@@ -685,6 +687,19 @@
     @Nullable
     private SplitSelectSource mSplitSelectSource;
 
+    private final SplitSelectionListener mSplitSelectionListener = new SplitSelectionListener() {
+        @Override
+        public void onSplitSelectionConfirmed() { }
+
+        @Override
+        public void onSplitSelectionActive() { }
+
+        @Override
+        public void onSplitSelectionExit(boolean launchedSplit) {
+            resetFromSplitSelectionState();
+        }
+    };
+
     /**
      * Keeps track of the index of the TaskView that split screen was initialized with so we know
      * where to insert it back into list of taskViews in case user backs out of entering split
@@ -1065,6 +1080,9 @@
                 mIPipAnimationListener);
         mOrientationState.initListeners();
         mTaskOverlayFactory.initListeners();
+        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+            mSplitSelectStateController.registerSplitListener(mSplitSelectionListener);
+        }
     }
 
     @Override
@@ -1083,6 +1101,9 @@
         mIPipAnimationListener.setActivityAndRecentsView(null, null);
         mOrientationState.destroyListeners();
         mTaskOverlayFactory.removeListeners();
+        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+            mSplitSelectStateController.unregisterSplitListener(mSplitSelectionListener);
+        }
     }
 
     @Override
@@ -1660,8 +1681,8 @@
         // If we are entering Overview as a result of initiating a split from somewhere else
         // (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
         int stagedTaskIdToBeRemovedFromGrid;
-        if (mSplitSelectSource != null) {
-            stagedTaskIdToBeRemovedFromGrid = mSplitSelectSource.alreadyRunningTaskId;
+        if (isSplitSelectionActive()) {
+            stagedTaskIdToBeRemovedFromGrid = mSplitSelectStateController.getInitialTaskId();
             updateCurrentTaskActionsVisibility();
         } else {
             stagedTaskIdToBeRemovedFromGrid = INVALID_TASK_ID;
@@ -2345,7 +2366,15 @@
         startHome(mActivity.isStarted());
     }
 
-    public abstract void startHome(boolean animated);
+    public void startHome(boolean animated) {
+        if (!isCommandQueueEmpty()) return;
+        handleStartHome(animated);
+    }
+
+    protected abstract void handleStartHome(boolean animated);
+
+    /** Returns whether the overview command helper queue is empty. */
+    public abstract boolean isCommandQueueEmpty();
 
     public void reset() {
         setCurrentTask(-1);
@@ -2367,7 +2396,9 @@
             remoteTargetHandle.getTransformParams().setTargetSet(null);
             remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
         });
-        resetFromSplitSelectionState();
+        if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+            resetFromSplitSelectionState();
+        }
 
         // These are relatively expensive and don't need to be done this frame (RecentsView isn't
         // visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
@@ -3251,7 +3282,11 @@
                         InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
             } else {
                 // If transition to split select was interrupted, clean up to prevent glitches
-                resetFromSplitSelectionState();
+                if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                    mSplitSelectStateController.resetState();
+                } else {
+                    resetFromSplitSelectionState();
+                }
                 InteractionJankMonitorWrapper.cancel(
                         InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
             }
@@ -3284,8 +3319,13 @@
                 true /* isStagedTask */);
 
         pendingAnimation.addEndListener(animationSuccess ->
-                mSplitSelectStateController.launchInitialAppFullscreen(launchSuccess ->
-                        resetFromSplitSelectionState()));
+                mSplitSelectStateController.launchInitialAppFullscreen(launchSuccess -> {
+                    if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                        mSplitSelectStateController.resetState();
+                    } else {
+                        resetFromSplitSelectionState();
+                    }
+                }));
 
         pendingAnimation.buildAnim().start();
     }
@@ -3766,19 +3806,33 @@
                                         taskViewIdArray.removeValue(
                                                 finalNextFocusedTaskView.getTaskViewId());
                                     }
-                                    if (snappedIndex < taskViewIdArray.size()) {
-                                        taskViewIdToSnapTo = taskViewIdArray.get(snappedIndex);
-                                    } else if (snappedIndex == taskViewIdArray.size()) {
-                                        // If the snapped task is the last item from the
-                                        // dismissed row,
-                                        // snap to the same column in the other grid row
-                                        IntArray inverseRowTaskViewIdArray =
-                                                isSnappedTaskInTopRow ? getBottomRowIdArray()
-                                                        : getTopRowIdArray();
-                                        if (snappedIndex < inverseRowTaskViewIdArray.size()) {
-                                            taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
-                                                    snappedIndex);
+                                    try {
+                                        if (snappedIndex < taskViewIdArray.size()) {
+                                            taskViewIdToSnapTo = taskViewIdArray.get(snappedIndex);
+                                        } else if (snappedIndex == taskViewIdArray.size()) {
+                                            // If the snapped task is the last item from the
+                                            // dismissed row,
+                                            // snap to the same column in the other grid row
+                                            IntArray inverseRowTaskViewIdArray =
+                                                    isSnappedTaskInTopRow ? getBottomRowIdArray()
+                                                            : getTopRowIdArray();
+                                            if (snappedIndex < inverseRowTaskViewIdArray.size()) {
+                                                taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
+                                                        snappedIndex);
+                                            }
                                         }
+                                    } catch (ArrayIndexOutOfBoundsException e) {
+                                        throw new IllegalStateException(
+                                                "b/269956477 invalid snappedIndex"
+                                                        + "\nsnappedTaskViewId: "
+                                                        + snappedTaskViewId
+                                                        + "\nfocusedTaskViewId: "
+                                                        + mFocusedTaskViewId
+                                                        + "\ntopRowIdArray: "
+                                                        + getTopRowIdArray().toConcatString()
+                                                        + "\nbottomRowIdArray: "
+                                                        + getBottomRowIdArray().toConcatString(),
+                                                e);
                                     }
                                 }
                             }
@@ -4723,7 +4777,13 @@
 
         pendingAnimation.addEndListener(aBoolean -> {
             mSplitSelectStateController.launchSplitTasks(
-                    aBoolean1 -> RecentsView.this.resetFromSplitSelectionState());
+                    aBoolean1 -> {
+                        if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+                            mSplitSelectStateController.resetState();
+                        } else {
+                            resetFromSplitSelectionState();
+                        }
+                    });
             InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
         });
 
@@ -4745,7 +4805,8 @@
 
     @SuppressLint("WrongCall")
     protected void resetFromSplitSelectionState() {
-        if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) {
+        if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1 ||
+                FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
             safeRemoveDragLayerView(mSplitSelectStateController.getFirstFloatingTaskView());
             safeRemoveDragLayerView(mSecondFloatingTaskView);
             safeRemoveDragLayerView(mSplitInstructionsView);
@@ -4764,7 +4825,11 @@
         setTaskViewsPrimarySplitTranslation(0);
         setTaskViewsSecondarySplitTranslation(0);
 
-        mSplitSelectStateController.resetState();
+        if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+            // When flag is on, this method gets called from resetState() call below, let's avoid
+            // infinite recursion today
+            mSplitSelectStateController.resetState();
+        }
         if (mSplitHiddenTaskViewIndex == -1) {
             return;
         }
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index 9868e20..55dd1de 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -19,7 +19,6 @@
     android:layout_height="wrap_content"
     android:paddingHorizontal="@dimen/widget_cell_horizontal_padding"
     android:paddingVertical="@dimen/widget_cell_vertical_padding"
-    android:layout_marginHorizontal="@dimen/widget_cell_horizontal_padding"
     android:layout_weight="1"
     android:orientation="vertical"
     android:focusable="true"
diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml
index 4a32672..c41d0bb 100644
--- a/res/layout/widgets_table_container.xml
+++ b/res/layout/widgets_table_container.xml
@@ -16,6 +16,7 @@
 <com.android.launcher3.widget.picker.WidgetsListTableView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/widgets_table"
+    android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
     android:background="@drawable/bg_widgets_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content" />
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 1bb4025..76a1239 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -208,6 +208,10 @@
         Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
         <attr name="allAppsSpecsId" format="reference" />
 
+        <!-- File that contains the specs for the workspace.
+        Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
+        <attr name="folderSpecsId" format="reference" />
+
         <!-- By default all categories are enabled -->
         <attr name="deviceCategory" format="integer">
             <!-- Enable on phone only -->
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 32421a4..64ac841 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -382,8 +382,7 @@
     private void resetCellSizeInternal(DeviceProfile deviceProfile) {
         switch (mContainerType) {
             case FOLDER:
-                mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx,
-                        deviceProfile.folderCellLayoutBorderSpacePx);
+                mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx);
                 break;
             case HOTSEAT:
                 mBorderSpace = new Point(deviceProfile.hotseatBorderSpace,
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index a166271..bd47fca 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -55,6 +55,8 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.responsive.AllAppsSpecs;
 import com.android.launcher3.responsive.CalculatedAllAppsSpec;
+import com.android.launcher3.responsive.CalculatedFolderSpec;
+import com.android.launcher3.responsive.FolderSpecs;
 import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
@@ -78,8 +80,7 @@
 
     public static final PointF DEFAULT_SCALE = new PointF(1.0f, 1.0f);
     public static final ViewScaleProvider DEFAULT_PROVIDER = itemInfo -> DEFAULT_SCALE;
-    public static final Consumer<DeviceProfile> DEFAULT_DIMENSION_PROVIDER = dp -> {
-    };
+    public static final Consumer<DeviceProfile> DEFAULT_DIMENSION_PROVIDER = dp -> {};
 
     public final InvariantDeviceProfile inv;
     private final Info mInfo;
@@ -120,6 +121,9 @@
     private AllAppsSpecs mAllAppsSpecs;
     private CalculatedAllAppsSpec mAllAppsResponsiveWidthSpec;
     private CalculatedAllAppsSpec mAllAppsResponsiveHeightSpec;
+    private FolderSpecs mFolderSpecs;
+    private CalculatedFolderSpec mResponsiveFolderWidthSpec;
+    private CalculatedFolderSpec mResponsiveFolderHeightSpec;
 
     /**
      * The maximum amount of left/right workspace padding as a percentage of the screen width.
@@ -178,7 +182,7 @@
     public int folderIconOffsetYPx;
 
     // Folder content
-    public int folderCellLayoutBorderSpacePx;
+    public Point folderCellLayoutBorderSpacePx;
     public int folderContentPaddingLeftRight;
     public int folderContentPaddingTop;
 
@@ -314,7 +318,8 @@
 
         // TODO(b/241386436): shouldn't change any launcher behaviour
         mIsResponsiveGrid = inv.workspaceSpecsId != INVALID_RESOURCE_HANDLE
-                && inv.allAppsSpecsId != INVALID_RESOURCE_HANDLE;
+                && inv.allAppsSpecsId != INVALID_RESOURCE_HANDLE
+                && inv.folderSpecsId != INVALID_RESOURCE_HANDLE;
 
         mIsScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
         // Determine device posture.
@@ -420,13 +425,15 @@
 
             folderContentPaddingTop = folderStyle.getDimensionPixelSize(
                     R.styleable.FolderStyle_folderTopPadding, 0);
-            folderCellLayoutBorderSpacePx = folderStyle.getDimensionPixelSize(
+
+            int gutter = folderStyle.getDimensionPixelSize(
                     R.styleable.FolderStyle_folderBorderSpace, 0);
+            folderCellLayoutBorderSpacePx = new Point(gutter, gutter);
             folderFooterHeightPx = folderStyle.getDimensionPixelSize(
                     R.styleable.FolderStyle_folderFooterHeight, 0);
             folderStyle.recycle();
-        } else {
-            folderCellLayoutBorderSpacePx = 0;
+        } else if (!mIsResponsiveGrid) {
+            folderCellLayoutBorderSpacePx = new Point(0, 0);
             folderFooterHeightPx = res.getDimensionPixelSize(R.dimen.folder_footer_height_default);
             folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_top_padding_default);
         }
@@ -556,6 +563,12 @@
             mAllAppsResponsiveHeightSpec = mAllAppsSpecs.getCalculatedHeightSpec(inv.numRows,
                     mResponsiveHeightSpec.getAvailableSpace(),
                     mResponsiveHeightSpec);
+
+            mFolderSpecs = new FolderSpecs(new ResourceHelper(context, inv.folderSpecsId));
+            mResponsiveFolderWidthSpec = mFolderSpecs.getWidthSpec(inv.numFolderColumns,
+                    mResponsiveWidthSpec.getAvailableSpace(), mResponsiveWidthSpec);
+            mResponsiveFolderHeightSpec = mFolderSpecs.getHeightSpec(inv.numFolderRows,
+                    mResponsiveHeightSpec.getAvailableSpace(), mResponsiveHeightSpec);
         }
 
         desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res);
@@ -1179,15 +1192,19 @@
         allAppsStyle.recycle();
     }
 
+    // TODO(b/288075868): Resize the icon size to make sure it will fit inside the cell size
     private void updateAvailableFolderCellDimensions(Resources res) {
         updateFolderCellSize(1f, res);
 
+        // Responsive grid doesn't need to scale the folder
+        if (mIsResponsiveGrid) return;
+
         // For usability we can't have the folder use the whole width of the screen
         Point totalWorkspacePadding = getTotalWorkspacePadding();
 
         // Check if the folder fit within the available height.
         float contentUsedHeight = folderCellHeightPx * inv.numFolderRows
-                + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx)
+                + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx.y)
                 + folderFooterHeightPx
                 + folderContentPaddingTop;
         int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y;
@@ -1195,7 +1212,7 @@
 
         // Check if the folder fit within the available width.
         float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns
-                + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx)
+                + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x)
                 + folderContentPaddingLeftRight * 2;
         int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x;
         float scaleX = contentMaxWidth / contentUsedWidth;
@@ -1215,7 +1232,19 @@
 
         int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
 
-        if (mIsScalableGrid) {
+        if (mIsResponsiveGrid) {
+            folderCellWidthPx = mResponsiveFolderWidthSpec.getCellSizePx();
+
+            // Height
+            folderCellHeightPx = mResponsiveFolderHeightSpec.getCellSizePx();
+            folderContentPaddingTop = mResponsiveFolderHeightSpec.getStartPaddingPx();
+            folderFooterHeightPx = mResponsiveFolderHeightSpec.getEndPaddingPx();
+
+            folderCellLayoutBorderSpacePx = new Point(mResponsiveFolderWidthSpec.getGutterPx(),
+                    mResponsiveHeightSpec.getGutterPx());
+
+            folderContentPaddingLeftRight = mResponsiveFolderWidthSpec.getStartPaddingPx();
+        } else if (mIsScalableGrid) {
             if (inv.folderStyle == INVALID_RESOURCE_HANDLE) {
                 folderCellWidthPx = roundPxValueFromFloat(getCellSize().x * scale);
                 folderCellHeightPx = roundPxValueFromFloat(getCellSize().y * scale);
@@ -1225,11 +1254,13 @@
             }
 
             folderContentPaddingTop = roundPxValueFromFloat(folderContentPaddingTop * scale);
-            folderCellLayoutBorderSpacePx = roundPxValueFromFloat(
-                    folderCellLayoutBorderSpacePx * scale);
+            folderCellLayoutBorderSpacePx = new Point(
+                    roundPxValueFromFloat(folderCellLayoutBorderSpacePx.x * scale),
+                    roundPxValueFromFloat(folderCellLayoutBorderSpacePx.y * scale)
+            );
             folderFooterHeightPx = roundPxValueFromFloat(folderFooterHeightPx * scale);
 
-            folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx;
+            folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx.x;
         } else {
             int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding)
                     * scale);
@@ -1750,8 +1781,10 @@
         writer.println(prefix + pxToDpStr("folderChildTextSizePx", folderChildTextSizePx));
         writer.println(prefix + pxToDpStr("folderChildDrawablePaddingPx",
                 folderChildDrawablePaddingPx));
-        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx",
-                folderCellLayoutBorderSpacePx));
+        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx.x",
+                folderCellLayoutBorderSpacePx.x));
+        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx.y",
+                folderCellLayoutBorderSpacePx.y));
         writer.println(prefix + pxToDpStr("folderContentPaddingLeftRight",
                 folderContentPaddingLeftRight));
         writer.println(prefix + pxToDpStr("folderTopPadding", folderContentPaddingTop));
@@ -1871,6 +1904,8 @@
                     + mAllAppsResponsiveHeightSpec.toString());
             writer.println(prefix + "\tmAllAppsResponsiveWidthSpec:"
                     + mAllAppsResponsiveWidthSpec.toString());
+            writer.println(prefix + "\tmResponsiveFolderHeightSpec:" + mResponsiveFolderHeightSpec);
+            writer.println(prefix + "\tmResponsiveFolderWidthSpec:" + mResponsiveFolderWidthSpec);
         }
     }
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 81aa2b2..4eaacdc 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -181,6 +181,8 @@
     public int workspaceSpecsId = INVALID_RESOURCE_HANDLE;
     @XmlRes
     public int allAppsSpecsId = INVALID_RESOURCE_HANDLE;
+    @XmlRes
+    public int folderSpecsId = INVALID_RESOURCE_HANDLE;
 
     public String dbFile;
     public int defaultLayoutId;
@@ -356,6 +358,7 @@
         devicePaddingId = closestProfile.devicePaddingId;
         workspaceSpecsId = closestProfile.mWorkspaceSpecsId;
         allAppsSpecsId = closestProfile.mAllAppsSpecsId;
+        folderSpecsId = closestProfile.mFolderSpecsId;
         this.deviceType = deviceType;
 
         inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing;
@@ -803,6 +806,7 @@
         private final int devicePaddingId;
         private final int mWorkspaceSpecsId;
         private final int mAllAppsSpecsId;
+        private final int mFolderSpecsId;
 
         public GridOption(Context context, AttributeSet attrs) {
             TypedArray a = context.obtainStyledAttributes(
@@ -869,9 +873,12 @@
                         R.styleable.GridDisplayOption_workspaceSpecsId, INVALID_RESOURCE_HANDLE);
                 mAllAppsSpecsId = a.getResourceId(
                         R.styleable.GridDisplayOption_allAppsSpecsId, INVALID_RESOURCE_HANDLE);
+                mFolderSpecsId = a.getResourceId(
+                        R.styleable.GridDisplayOption_folderSpecsId, INVALID_RESOURCE_HANDLE);
             } else {
                 mWorkspaceSpecsId = INVALID_RESOURCE_HANDLE;
                 mAllAppsSpecsId = INVALID_RESOURCE_HANDLE;
+                mFolderSpecsId = INVALID_RESOURCE_HANDLE;
             }
 
             int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index ba6dc26..f921d1d 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -162,7 +162,7 @@
             // No need to add padding when cell layout border spacing is present.
             boolean noPaddingX =
                     (dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE)
-                            || (dp.folderCellLayoutBorderSpacePx > 0 && mContainerType == FOLDER)
+                            || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER)
                             || (dp.hotseatBorderSpace > 0 && mContainerType == HOTSEAT);
             int cellPaddingX = noPaddingX
                     ? 0
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index dd82ecf..b09985c 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -236,9 +236,9 @@
                 mFolder, startRect, endRect, finalRadius, !mIsOpening));
 
         // Create reveal animator for the folder content (capture the top 4 icons 2x2)
-        int width = mDeviceProfile.folderCellLayoutBorderSpacePx
+        int width = mDeviceProfile.folderCellLayoutBorderSpacePx.x
                 + mDeviceProfile.folderCellWidthPx * 2;
-        int height = mDeviceProfile.folderCellLayoutBorderSpacePx
+        int height = mDeviceProfile.folderCellLayoutBorderSpacePx.y
                 + mDeviceProfile.folderCellHeightPx * 2;
         int page = mIsOpening ? mContent.getCurrentPage() : mContent.getDestinationPage();
         int left = mContent.getPaddingLeft() + page * lp.width;
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index c5c74ac..307052a 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -155,19 +155,19 @@
         // Progress color
         float[] m3HCT = new float[3];
         ColorUtils.colorToM3HCT(primaryIconColor, m3HCT);
-        mProgressColor = ColorUtils.M3HCTtoColor(
+        mProgressColor = ColorUtils.M3HCTToColor(
                 m3HCT[0],
                 m3HCT[1],
                 isDarkMode ? Math.max(m3HCT[2], 55) : Math.min(m3HCT[2], 40));
 
         // Track color
-        mTrackColor = ColorUtils.M3HCTtoColor(
+        mTrackColor = ColorUtils.M3HCTToColor(
                 m3HCT[0],
                 16,
                 isDarkMode ? 30 : 90
         );
         // Plate color
-        mPlateColor = ColorUtils.M3HCTtoColor(
+        mPlateColor = ColorUtils.M3HCTToColor(
                 m3HCT[0],
                 isDarkMode ? 36 : 24,
                 isDarkMode ? (isThemed() ? 10 : 20) : 80
diff --git a/src/com/android/launcher3/responsive/FolderSpecs.kt b/src/com/android/launcher3/responsive/FolderSpecs.kt
index be97cf8..f4446bc 100644
--- a/src/com/android/launcher3/responsive/FolderSpecs.kt
+++ b/src/com/android/launcher3/responsive/FolderSpecs.kt
@@ -188,12 +188,12 @@
 }
 
 data class CalculatedFolderSpec(
+    val availableSpace: Int,
+    val cells: Int,
     val startPaddingPx: Int,
     val endPaddingPx: Int,
     val gutterPx: Int,
-    val cellSizePx: Int,
-    val availableSpace: Int,
-    val cells: Int
+    val cellSizePx: Int
 )
 
 /**
@@ -270,11 +270,11 @@
     cellSizePx = folderSpec.cellSize.getRemainderSpaceValue(remainderSpace, cellSizePx)
 
     return CalculatedFolderSpec(
+        availableSpace = availableSpace,
+        cells = cells,
         startPaddingPx = startPaddingPx,
         endPaddingPx = endPaddingPx,
         gutterPx = gutterPx,
-        cellSizePx = cellSizePx,
-        availableSpace = availableSpace,
-        cells = cells
+        cellSizePx = cellSizePx
     )
 }
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 520f33c..de5887f 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -237,4 +237,10 @@
      * @param leftOrTop if the staged split will be positioned left or top.
      */
     public void enterStageSplitFromRunningApp(boolean leftOrTop) { }
+
+
+    /** Returns whether the overview command helper queue is empty. */
+    public boolean isCommandQueueEmpty() {
+        return true;
+    }
 }
diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
index c91a4a9..0a95771 100644
--- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
+++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
@@ -84,7 +84,8 @@
                     "\tfolderChildIconSizePx: 147.0px (56.0dp)\n" +
                     "\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
                     "\tfolderTopPadding: 63.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -220,7 +221,8 @@
                     "\tfolderChildIconSizePx: 147.0px (56.0dp)\n" +
                     "\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
                     "\tfolderTopPadding: 63.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -356,7 +358,8 @@
                     "\tfolderChildIconSizePx: 131.0px (49.904762dp)\n" +
                     "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 9.0px (3.4285715dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
                     "\tfolderTopPadding: 56.0px (21.333334dp)\n" +
                     "\tfolderFooterHeight: 131.0px (49.904762dp)\n" +
@@ -492,7 +495,8 @@
                     "\tfolderChildIconSizePx: 123.0px (46.857143dp)\n" +
                     "\tfolderChildTextSizePx: 32.0px (12.190476dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 8.0px (3.047619dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
                     "\tfolderTopPadding: 53.0px (20.190475dp)\n" +
                     "\tfolderFooterHeight: 123.0px (46.857143dp)\n" +
@@ -629,7 +633,8 @@
                     "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
                     "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
                     "\tfolderTopPadding: 48.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 112.0px (56.0dp)\n" +
@@ -766,7 +771,8 @@
                     "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
                     "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
                     "\tfolderTopPadding: 48.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 112.0px (56.0dp)\n" +
@@ -903,7 +909,8 @@
                     "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
                     "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
                     "\tfolderTopPadding: 48.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 112.0px (56.0dp)\n" +
@@ -1040,7 +1047,8 @@
                     "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
                     "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
                     "\tfolderTopPadding: 48.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 112.0px (56.0dp)\n" +
@@ -1182,7 +1190,8 @@
                     "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
                     "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
                     "\tfolderTopPadding: 63.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -1323,7 +1332,8 @@
                     "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
                     "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
                     "\tfolderTopPadding: 63.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -1464,7 +1474,8 @@
                     "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
                     "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
                     "\tfolderTopPadding: 63.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
@@ -1601,7 +1612,8 @@
                     "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
                     "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
                     "\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
+                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
                     "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
                     "\tfolderTopPadding: 63.0px (24.0dp)\n" +
                     "\tfolderFooterHeight: 147.0px (56.0dp)\n" +