Merge "Revert "Reorder widgets no longer overlaps when no space is avai..."" into tm-qpr-dev
diff --git a/quickstep/res/layout-sw600dp-land/allset_navigation_and_hint.xml b/quickstep/res/layout-sw600dp-land/allset_navigation_and_hint.xml
deleted file mode 100644
index 3bfa6da..0000000
--- a/quickstep/res/layout-sw600dp-land/allset_navigation_and_hint.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 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.
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-
-    <TextView
-        android:id="@+id/navigation_settings"
-        style="@style/TextAppearance.GestureTutorial.LinkText"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="32dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:minHeight="48dp"
-        android:text="@string/allset_navigation_settings"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/subtitle" />
-
-    <TextView
-        android:id="@+id/hint"
-        style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/allset_page_margin_bottom"
-        android:text="@string/allset_hint"
-        android:textSize="@dimen/allset_page_swipe_up_text_size"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent" />
-
-</merge>
\ No newline at end of file
diff --git a/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml b/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
index 9559072..44b3ecb 100644
--- a/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
+++ b/quickstep/res/layout-sw600dp/allset_navigation_and_hint.xml
@@ -21,12 +21,11 @@
         style="@style/TextAppearance.GestureTutorial.LinkText"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="96dp"
+        android:layout_marginTop="24dp"
         android:background="?android:attr/selectableItemBackground"
         android:minHeight="48dp"
         android:text="@string/allset_navigation_settings"
-        app:layout_constraintBottom_toTopOf="@id/hint"
-        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/subtitle"
         app:layout_constraintStart_toStartOf="parent" />
 
     <TextView
@@ -34,7 +33,7 @@
         style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/allset_page_margin_bottom"
+        android:layout_marginBottom="24dp"
         android:text="@string/allset_hint"
         android:textSize="@dimen/allset_page_swipe_up_text_size"
         app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 5507fcf..dc10c24 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -20,6 +20,5 @@
 
     <!-- All Set page -->
     <dimen name="allset_page_margin_horizontal">48dp</dimen>
-    <dimen name="allset_page_margin_bottom">24dp</dimen>
 
 </resources>
diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
index c96ad11..5899814 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -36,7 +36,6 @@
 
     <!-- All Set page -->
     <dimen name="allset_page_margin_horizontal">120dp</dimen>
-    <dimen name="allset_page_margin_bottom">24dp</dimen>
     <dimen name="allset_page_allset_text_size">38sp</dimen>
     <dimen name="allset_page_swipe_up_text_size">15sp</dimen>
 
diff --git a/quickstep/res/values-sw720dp-land/dimens.xml b/quickstep/res/values-sw720dp-land/dimens.xml
index 4bc8bf3..02d1189 100644
--- a/quickstep/res/values-sw720dp-land/dimens.xml
+++ b/quickstep/res/values-sw720dp-land/dimens.xml
@@ -17,7 +17,4 @@
 <resources>
     <!--  Overview actions  -->
     <dimen name="overview_actions_top_margin">20dp</dimen>
-
-    <!-- All Set page-->
-    <dimen name="allset_page_margin_bottom">24dp</dimen>
 </resources>
diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml
index a84b939..585f01e 100644
--- a/quickstep/res/values-sw720dp/dimens.xml
+++ b/quickstep/res/values-sw720dp/dimens.xml
@@ -35,7 +35,6 @@
     <dimen name="overview_grid_side_margin">64dp</dimen>
 
     <!-- All Set page-->
-    <dimen name="allset_page_margin_bottom">0dp</dimen>
     <dimen name="allset_page_allset_text_size">42sp</dimen>
     <dimen name="allset_page_swipe_up_text_size">16sp</dimen>
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 6e3fd32..cd60879 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -202,7 +202,6 @@
 
     <!-- All Set page -->
     <dimen name="allset_page_margin_horizontal">40dp</dimen>
-    <dimen name="allset_page_margin_bottom">0dp</dimen>
     <dimen name="allset_page_allset_text_size">36sp</dimen>
     <dimen name="allset_page_swipe_up_text_size">14sp</dimen>
 
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 868d38b..4f0fdf1 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -152,6 +152,8 @@
         <item name="android:background">@drawable/bg_overview_clear_all_button</item>
         <item name="android:minWidth">96dp</item>
         <item name="android:minHeight">48dp</item>
+        <item name="android:paddingStart">12dp</item>
+        <item name="android:paddingEnd">12dp</item>
         <item name="android:stateListAnimator">@null</item>
     </style>
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index f1e6747..df867cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -21,6 +21,7 @@
 
 import android.animation.Animator;
 
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.fallback.RecentsState;
@@ -40,8 +41,7 @@
                     animateToRecentsState(toState);
 
                     // Handle tapping on live tile.
-                    RecentsView recentsView = mRecentsActivity.getOverviewPanel();
-                    recentsView.setTaskLaunchListener(toState == RecentsState.DEFAULT
+                    getRecentsView().setTaskLaunchListener(toState == RecentsState.DEFAULT
                             ? (() -> animateToRecentsState(RecentsState.BACKGROUND_APP)) : null);
                 }
             };
@@ -70,12 +70,14 @@
      * Currently this animation just force stashes the taskbar in Overview.
      */
     public Animator createAnimToRecentsState(RecentsState toState, long duration) {
-        boolean forceStashed = toState.hasOverviewActions();
+        boolean useStashedLauncherState = toState.hasOverviewActions();
+        boolean stashedLauncherState =
+                useStashedLauncherState && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
         TaskbarStashController controller = mControllers.taskbarStashController;
         // Set both FLAG_IN_STASHED_LAUNCHER_STATE and FLAG_IN_APP to ensure the state is respected.
         // For all other states, just use the current stashed-in-app setting (e.g. if long clicked).
-        controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, forceStashed);
-        controller.updateStateForFlag(FLAG_IN_APP, !forceStashed);
+        controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, stashedLauncherState);
+        controller.updateStateForFlag(FLAG_IN_APP, !useStashedLauncherState);
         return controller.applyStateWithoutStart(duration);
     }
 
@@ -85,4 +87,9 @@
             anim.start();
         }
     }
+
+    @Override
+    public RecentsView getRecentsView() {
+        return mRecentsActivity.getOverviewPanel();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 317f6a4..beab56c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -26,7 +26,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.MotionEvent;
 import android.view.TaskTransitionSpec;
 import android.view.WindowManagerGlobal;
 
@@ -48,6 +47,7 @@
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.views.RecentsView;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -193,16 +193,15 @@
      */
     public Animator createAnimToLauncher(@NonNull LauncherState toState,
             @NonNull RecentsAnimationCallbacks callbacks, long duration) {
-        return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
-    }
+        AnimatorSet set = new AnimatorSet();
+        Animator taskbarState = mTaskbarLauncherStateController
+                .createAnimToLauncher(toState, callbacks, duration);
+        long halfDuration = Math.round(duration * 0.5f);
+        Animator translation =
+                mControllers.taskbarTranslationController.createAnimToLauncher(halfDuration);
 
-    /**
-     * @param ev MotionEvent in screen coordinates.
-     * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
-     */
-    public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
-        return mControllers.taskbarViewController.isEventOverAnyItem(ev)
-                || mControllers.navbarButtonsViewController.isEventOverAnyItem(ev);
+        set.playTogether(taskbarState, translation);
+        return set;
     }
 
     public boolean isDraggingItem() {
@@ -393,4 +392,9 @@
 
         mTaskbarLauncherStateController.dumpLogs(prefix + "\t", pw);
     }
+
+    @Override
+    public RecentsView getRecentsView() {
+        return mLauncher.getOverviewPanel();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 12dbcb3..45d5739 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -30,6 +30,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.RevealOutlineAnimation;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiValueAlpha;
@@ -66,6 +67,7 @@
 
     // Initialized in init.
     private TaskbarControllers mControllers;
+    private int mTaskbarSize;
 
     // The bounds we want to clip to in the settled state when showing the stashed handle.
     private final Rect mStashedHandleBounds = new Rect();
@@ -96,15 +98,18 @@
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
         Resources resources = mActivity.getResources();
         if (isPhoneGestureNavMode(mActivity.getDeviceProfile())) {
-            mStashedHandleView.getLayoutParams().height =
-                    resources.getDimensionPixelSize(R.dimen.taskbar_size);
+            mTaskbarSize = resources.getDimensionPixelSize(R.dimen.taskbar_size);
             mStashedHandleWidth =
                     resources.getDimensionPixelSize(R.dimen.taskbar_stashed_small_screen);
         } else {
-            mStashedHandleView.getLayoutParams().height = deviceProfile.taskbarSize;
+            mTaskbarSize = deviceProfile.taskbarSize;
             mStashedHandleWidth = resources
                     .getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
         }
+        int taskbarBottomMargin = DisplayController.isTransientTaskbar(mActivity)
+                ? resources.getDimensionPixelSize(R.dimen.transient_taskbar_margin)
+                : 0;
+        mStashedHandleView.getLayoutParams().height = mTaskbarSize + taskbarBottomMargin;
 
         mTaskbarStashedHandleAlpha.get(ALPHA_INDEX_STASHED).setValue(
                 isPhoneGestureNavMode(deviceProfile) ? 1 : 0);
@@ -181,9 +186,17 @@
      * morphs into the size of where the taskbar icons will be.
      */
     public Animator createRevealAnimToIsStashed(boolean isStashed) {
+        Rect visualBounds = new Rect(mControllers.taskbarViewController.getIconLayoutBounds());
+
+        if (DisplayController.isTransientTaskbar(mActivity)) {
+            // Account for the full visual height of the transient taskbar.
+            int heightDiff = (mTaskbarSize - visualBounds.height()) / 2;
+            visualBounds.top -= heightDiff;
+            visualBounds.bottom += heightDiff;
+        }
+
         final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
-                mStashedHandleRadius, mStashedHandleRadius,
-                mControllers.taskbarViewController.getIconLayoutBounds(), mStashedHandleBounds);
+                mStashedHandleRadius, mStashedHandleRadius, visualBounds, mStashedHandleBounds);
 
         boolean isReversed = !isStashed;
         boolean changingDirection = mWasLastRevealAnimReversed != isReversed;
@@ -220,6 +233,13 @@
     }
 
     /**
+     * Sets the translation of the stashed handle during the swipe up gesture.
+     */
+    protected void setTranslationYForSwipe(float transY) {
+        mStashedHandleView.setTranslationY(transY);
+    }
+
+    /**
      * Should be called when the home button is disabled, so we can hide this handle as well.
      */
     public void setIsHomeButtonDisabled(boolean homeDisabled) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index fad9ff4..9175226 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -78,6 +78,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.AutohideSuspendFlag;
+import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
 import com.android.launcher3.testing.TestLogging;
@@ -90,6 +91,7 @@
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.rotation.RotationButtonController;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -132,6 +134,7 @@
     private final boolean mIsUserSetupComplete;
     private final boolean mIsNavBarForceVisible;
     private final boolean mIsNavBarKidsMode;
+
     private boolean mIsDestroyed = false;
     // The flag to know if the window is excluded from magnification region computation.
     private boolean mIsExcludeFromMagnificationRegion = false;
@@ -223,6 +226,7 @@
                 new TaskbarAllAppsController(),
                 new TaskbarInsetsController(this),
                 new VoiceInteractionWindowController(this),
+                new TaskbarTranslationController(this),
                 isDesktopMode
                         ? new DesktopTaskbarRecentAppsController(this)
                         : TaskbarRecentAppsController.DEFAULT);
@@ -755,42 +759,63 @@
             if (info.isDisabled()) {
                 ItemClickHandler.handleDisabledItemClicked(info, this);
             } else {
-                Intent intent = new Intent(info.getIntent())
-                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                try {
-                    if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
-                        Toast.makeText(this, R.string.safemode_shortcut_error,
-                                Toast.LENGTH_SHORT).show();
-                    } else  if (info.isPromise()) {
-                        TestLogging.recordEvent(
-                                TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
-                        intent = new PackageManagerHelper(this)
-                                .getMarketIntent(info.getTargetPackage())
-                                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                        startActivity(intent);
+                TaskbarUIController taskbarUIController = mControllers.uiController;
+                RecentsView recents = taskbarUIController.getRecentsView();
+                if (recents != null
+                        && taskbarUIController.getRecentsView().isSplitSelectionActive()) {
+                    // If we are selecting a second app for split, launch the split tasks
+                    taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
+                } else {
+                    // Else launch the selected task
+                    Intent intent = new Intent(info.getIntent())
+                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    try {
+                        if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
+                            Toast.makeText(this, R.string.safemode_shortcut_error,
+                                    Toast.LENGTH_SHORT).show();
+                        } else if (info.isPromise()) {
+                            TestLogging.recordEvent(
+                                    TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
+                            intent = new PackageManagerHelper(this)
+                                    .getMarketIntent(info.getTargetPackage())
+                                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                            startActivity(intent);
 
-                    } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                        TestLogging.recordEvent(
-                                TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
-                        String id = info.getDeepShortcutId();
-                        String packageName = intent.getPackage();
-                        getSystemService(LauncherApps.class)
-                                .startShortcut(packageName, id, null, null, info.user);
-                    } else {
-                        startItemInfoActivity(info);
+                        } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                            TestLogging.recordEvent(
+                                    TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
+                            String id = info.getDeepShortcutId();
+                            String packageName = intent.getPackage();
+                            getSystemService(LauncherApps.class)
+                                    .startShortcut(packageName, id, null, null, info.user);
+                        } else {
+                            startItemInfoActivity(info);
+                        }
+
+                        mControllers.uiController.onTaskbarIconLaunched(info);
+                    } catch (NullPointerException
+                            | ActivityNotFoundException
+                            | SecurityException e) {
+                        Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
+                                .show();
+                        Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
                     }
-
-                    mControllers.uiController.onTaskbarIconLaunched(info);
-                    mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
-                } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
-                    Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
-                            .show();
-                    Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
                 }
+                mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
             }
         } else if (tag instanceof AppInfo) {
-            startItemInfoActivity((AppInfo) tag);
-            mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
+            AppInfo info = (AppInfo) tag;
+            TaskbarUIController taskbarUIController = mControllers.uiController;
+            RecentsView recents = taskbarUIController.getRecentsView();
+            if (recents != null
+                    && taskbarUIController.getRecentsView().isSplitSelectionActive()) {
+                // If we are selecting a second app for split, launch the split tasks
+                taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
+            } else {
+                // Else launch the selected task
+                startItemInfoActivity((AppInfo) tag);
+                mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
+            }
             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
         } else {
             Log.e(TAG, "Unknown type clicked: " + tag);
@@ -834,6 +859,20 @@
     }
 
     /**
+     * Called to start the taskbar translation spring to its settled translation (0).
+     */
+    public void startTranslationSpring() {
+        mControllers.taskbarTranslationController.startSpring();
+    }
+
+    /**
+     * Returns a callback to help monitor the swipe gesture.
+     */
+    public TransitionCallback getTranslationCallbacks() {
+        return mControllers.taskbarTranslationController.getTransitionCallback();
+    }
+
+    /**
      * Called when a transient Autohide flag suspend status changes.
      */
     public void onTransientAutohideSuspendFlagChanged(boolean isSuspended) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index abd467d..ff7e8e9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -34,6 +34,7 @@
 
     val paint: Paint = Paint()
     var backgroundHeight = context.deviceProfile.taskbarSize.toFloat()
+    var translationYForSwipe = 0f
 
     private var maxBackgroundHeight = context.deviceProfile.taskbarSize.toFloat()
     private val transientBackgroundBounds = context.transientTaskbarBounds
@@ -44,8 +45,12 @@
     private var keyShadowDistance = 0f
     private var bottomMargin = 0
 
-    private val leftCornerRadius = context.leftCornerRadius.toFloat()
-    private val rightCornerRadius = context.rightCornerRadius.toFloat()
+    private val fullLeftCornerRadius = context.leftCornerRadius.toFloat()
+    private val fullRightCornerRadius = context.rightCornerRadius.toFloat()
+    private var leftCornerRadius = fullLeftCornerRadius
+    private var rightCornerRadius = fullRightCornerRadius
+    private val square: Path = Path()
+    private val circle: Path = Path()
     private val invertedLeftCornerPath: Path = Path()
     private val invertedRightCornerPath: Path = Path()
 
@@ -63,13 +68,29 @@
             keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance)
         }
 
+        setCornerRoundness(DEFAULT_ROUNDNESS)
+    }
+
+    /**
+     * Sets the roundness of the round corner above Taskbar. No effect on transient Taskkbar.
+     * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
+     */
+    fun setCornerRoundness(cornerRoundness: Float) {
+        if (isTransientTaskbar && !transientBackgroundBounds.isEmpty) {
+            return
+        }
+
+        leftCornerRadius = fullLeftCornerRadius * cornerRoundness
+        rightCornerRadius = fullRightCornerRadius * cornerRoundness
+
         // Create the paths for the inverted rounded corners above the taskbar. Start with a filled
         // square, and then subtract out a circle from the appropriate corner.
-        val square = Path()
+        square.reset()
         square.addRect(0f, 0f, leftCornerRadius, leftCornerRadius, Path.Direction.CW)
-        val circle = Path()
+        circle.reset()
         circle.addCircle(leftCornerRadius, 0f, leftCornerRadius, Path.Direction.CW)
         invertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE)
+
         square.reset()
         square.addRect(0f, 0f, rightCornerRadius, rightCornerRadius, Path.Direction.CW)
         circle.reset()
@@ -94,11 +115,13 @@
             canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
             canvas.drawPath(invertedRightCornerPath, paint)
         } else {
+            // Approximates the stash/unstash animation to transform the background.
             val scaleFactor = backgroundHeight / maxBackgroundHeight
             val width = transientBackgroundBounds.width()
             val widthScale = mapToRange(scaleFactor, 0f, 1f, 0.4f, 1f, Interpolators.LINEAR)
             val newWidth = widthScale * width
             val delta = width - newWidth
+            canvas.translate(0f, bottomMargin * ((1f - scaleFactor) / 2f))
 
             // Draw shadow.
             val shadowAlpha = mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, 25f,
@@ -112,13 +135,17 @@
 
             canvas.drawRoundRect(
                 transientBackgroundBounds.left + (delta / 2f),
-                0f,
+                translationYForSwipe,
                 transientBackgroundBounds.right - (delta / 2f),
-                backgroundHeight,
+                backgroundHeight + translationYForSwipe,
                 radius, radius, paint
             )
         }
 
         canvas.restore()
     }
+
+    companion object {
+        const val DEFAULT_ROUNDNESS = 1f
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 9c2d21e..0328df9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -23,6 +23,7 @@
 
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
+import com.android.quickstep.AnimatedFloat;
 import com.android.systemui.shared.rotation.RotationButtonController;
 
 import java.io.PrintWriter;
@@ -55,9 +56,11 @@
     public final TaskbarInsetsController taskbarInsetsController;
     public final VoiceInteractionWindowController voiceInteractionWindowController;
     public final TaskbarRecentAppsController taskbarRecentAppsController;
+    public final TaskbarTranslationController taskbarTranslationController;
     public final TaskbarOverlayController taskbarOverlayController;
 
     @Nullable private LoggableTaskbarController[] mControllersToLog = null;
+    @Nullable private BackgroundRendererController[] mBackgroundRendererControllers = null;
 
     /** Do not store this controller, as it may change at runtime. */
     @NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT;
@@ -67,6 +70,9 @@
 
     @Nullable private TaskbarSharedState mSharedState = null;
 
+    // Roundness property for round corner above taskbar .
+    private final AnimatedFloat mCornerRoundness = new AnimatedFloat(this::updateCornerRoundness);
+
     public TaskbarControllers(TaskbarActivityContext taskbarActivityContext,
             TaskbarDragController taskbarDragController,
             TaskbarNavButtonController navButtonController,
@@ -87,6 +93,7 @@
             TaskbarAllAppsController taskbarAllAppsController,
             TaskbarInsetsController taskbarInsetsController,
             VoiceInteractionWindowController voiceInteractionWindowController,
+            TaskbarTranslationController taskbarTranslationController,
             TaskbarRecentAppsController taskbarRecentAppsController) {
         this.taskbarActivityContext = taskbarActivityContext;
         this.taskbarDragController = taskbarDragController;
@@ -108,6 +115,7 @@
         this.taskbarAllAppsController = taskbarAllAppsController;
         this.taskbarInsetsController = taskbarInsetsController;
         this.voiceInteractionWindowController = voiceInteractionWindowController;
+        this.taskbarTranslationController = taskbarTranslationController;
         this.taskbarRecentAppsController = taskbarRecentAppsController;
     }
 
@@ -139,6 +147,7 @@
         taskbarInsetsController.init(this);
         voiceInteractionWindowController.init(this);
         taskbarRecentAppsController.init(this);
+        taskbarTranslationController.init(this);
 
         mControllersToLog = new LoggableTaskbarController[] {
                 taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -146,8 +155,13 @@
                 taskbarUnfoldAnimationController, taskbarKeyguardController,
                 stashedHandleViewController, taskbarStashController, taskbarEduController,
                 taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController,
+                voiceInteractionWindowController, taskbarTranslationController
+        };
+        mBackgroundRendererControllers = new BackgroundRendererController[] {
+                taskbarDragLayerController, taskbarScrimViewController,
                 voiceInteractionWindowController
         };
+        mCornerRoundness.updateValue(TaskbarBackgroundRenderer.DEFAULT_ROUNDNESS);
 
         mAreAllControllersInitialized = true;
         for (Runnable postInitCallback : mPostInitCallbacks) {
@@ -164,6 +178,7 @@
 
     public void onConfigurationChanged(@Config int configChanges) {
         navbarButtonsViewController.onConfigurationChanged(configChanges);
+        taskbarDragLayerController.onConfigurationChanged();
     }
 
     /**
@@ -191,6 +206,7 @@
         taskbarRecentAppsController.onDestroy();
 
         mControllersToLog = null;
+        mBackgroundRendererControllers = null;
     }
 
     /**
@@ -224,6 +240,23 @@
         rotationButtonController.dumpLogs(prefix + "\t", pw);
     }
 
+    /**
+     * Returns a float property that animates roundness of the round corner above Taskbar.
+     */
+    public AnimatedFloat getTaskbarCornerRoundness() {
+        return mCornerRoundness;
+    }
+
+    private void updateCornerRoundness() {
+        if (mBackgroundRendererControllers == null) {
+            return;
+        }
+
+        for (BackgroundRendererController controller : mBackgroundRendererControllers) {
+            controller.setCornerRoundness(mCornerRoundness.value);
+        }
+    }
+
     @VisibleForTesting
     TaskbarActivityContext getTaskbarActivityContext() {
         // Used to mock
@@ -233,4 +266,12 @@
     protected interface LoggableTaskbarController {
         void dumpLogs(String prefix, PrintWriter pw);
     }
+
+    protected interface BackgroundRendererController {
+        /**
+         * Sets the roundness of the round corner above Taskbar.
+         * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
+         */
+        void setCornerRoundness(float cornerRoundness);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index d7bb16e..3045eca 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.taskbar;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
@@ -301,7 +302,12 @@
     protected void callOnDragStart() {
         super.callOnDragStart();
         // Pre-drag has ended, start the global system drag.
-        AbstractFloatingView.closeAllOpenViews(mActivity);
+        if (mDisallowGlobalDrag) {
+            AbstractFloatingView.closeAllOpenViewsExcept(mActivity, TYPE_TASKBAR_ALL_APPS);
+        } else {
+            AbstractFloatingView.closeAllOpenViews(mActivity);
+        }
+
         startSystemDrag((BubbleTextView) mDragObject.originalView);
     }
 
@@ -536,10 +542,15 @@
 
     private View findTaskbarTargetForIconView(@NonNull View iconView) {
         Object tag = iconView.getTag();
+        TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
+
         if (tag instanceof ItemInfo) {
             ItemInfo item = (ItemInfo) tag;
-            TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
             if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
+                if (mDisallowGlobalDrag) {
+                    // We're dragging in taskbarAllApps, we don't have folders or shortcuts
+                    return iconView;
+                }
                 // Since all apps closes when the drag starts, target the all apps button instead.
                 return taskbarViewController.getAllAppsButtonView();
             } else if (item.container >= 0) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 7c9a13c..d0059f7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -116,9 +116,17 @@
     }
 
     @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (mControllerCallbacks != null) {
+            mControllerCallbacks.tryStashBasedOnMotionEvent(ev);
+        }
+        return super.onInterceptTouchEvent(ev);
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mControllerCallbacks != null && ev.getAction() == MotionEvent.ACTION_OUTSIDE) {
-            mControllerCallbacks.onActionOutsideEvent();
+        if (mControllerCallbacks != null) {
+            mControllerCallbacks.tryStashBasedOnMotionEvent(ev);
         }
         return super.onTouchEvent(ev);
     }
@@ -158,6 +166,23 @@
         invalidate();
     }
 
+    /**
+     * Sets the roundness of the round corner above Taskbar.
+     * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
+     */
+    protected void setCornerRoundness(float cornerRoundness) {
+        mBackgroundRenderer.setCornerRoundness(cornerRoundness);
+        invalidate();
+    }
+
+    /*
+     * Sets the translation of the background during the swipe up gesture.
+     */
+    protected void setBackgroundTranslationYForSwipe(float translationY) {
+        mBackgroundRenderer.setTranslationYForSwipe(translationY);
+        invalidate();
+    }
+
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 13ecf81..e54fc00 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -18,10 +18,12 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.view.MotionEvent;
 import android.view.ViewTreeObserver;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.DimensionUtils;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.TouchController;
@@ -32,11 +34,13 @@
 /**
  * Handles properties/data collection, then passes the results to TaskbarDragLayer to render.
  */
-public class TaskbarDragLayerController implements TaskbarControllers.LoggableTaskbarController {
+public class TaskbarDragLayerController implements TaskbarControllers.LoggableTaskbarController,
+        TaskbarControllers.BackgroundRendererController {
 
     private final TaskbarActivityContext mActivity;
     private final TaskbarDragLayer mTaskbarDragLayer;
     private final int mFolderMargin;
+    private float mGestureHeightYThreshold;
 
     // Alpha properties for taskbar background.
     private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
@@ -63,6 +67,7 @@
         mTaskbarDragLayer = taskbarDragLayer;
         final Resources resources = mTaskbarDragLayer.getResources();
         mFolderMargin = resources.getDimensionPixelSize(R.dimen.taskbar_folder_margin);
+        updateGestureHeight();
     }
 
     public void init(TaskbarControllers controllers) {
@@ -122,6 +127,19 @@
         return mBgOffset;
     }
 
+    private void updateGestureHeight() {
+        int gestureHeight = ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
+                mActivity.getResources());
+        mGestureHeightYThreshold = mActivity.getDeviceProfile().heightPx - gestureHeight;
+    }
+
+    /**
+     * Make updates when configuration changes.
+     */
+    public void onConfigurationChanged() {
+        updateGestureHeight();
+    }
+
     private void updateBackgroundAlpha() {
         final float bgNavbar = mBgNavbar.value;
         final float bgTaskbar = mBgTaskbar.value * mKeyguardBgTaskbar.value
@@ -132,12 +150,24 @@
         updateNavBarDarkIntensityMultiplier();
     }
 
+    /**
+     * Sets the translation of the background during the swipe up gesture.
+     */
+    public void setTranslationYForSwipe(float transY) {
+        mTaskbarDragLayer.setBackgroundTranslationYForSwipe(transY);
+    }
+
     private void updateBackgroundOffset() {
         mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
 
         updateNavBarDarkIntensityMultiplier();
     }
 
+    @Override
+    public void setCornerRoundness(float cornerRoundness) {
+        mTaskbarDragLayer.setCornerRoundness(cornerRoundness);
+    }
+
     private void updateNavBarDarkIntensityMultiplier() {
         // Zero out the app-requested dark intensity when we're drawing our own background.
         float effectiveBgAlpha = mLastSetBackgroundAlpha * (1 - mBgOffset.value);
@@ -158,6 +188,8 @@
      */
     public class TaskbarDragLayerCallbacks {
 
+        private final int[] mTempOutLocation = new int[2];
+
         /**
          * Called to update the touchable insets.
          * @see ViewTreeObserver.InternalInsetsInfo#setTouchableInsets(int)
@@ -167,9 +199,9 @@
         }
 
         /**
-         * Called whenever TaskbarDragLayer receives an ACTION_OUTSIDE event.
+         * Listens to TaskbarDragLayer touch events and responds accordingly.
          */
-        public void onActionOutsideEvent() {
+        public void tryStashBasedOnMotionEvent(MotionEvent ev) {
             if (!DisplayController.isTransientTaskbar(mActivity)) {
                 return;
             }
@@ -177,7 +209,24 @@
                 return;
             }
 
-            mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+            boolean stashTaskbar = false;
+
+            MotionEvent screenCoordinates = MotionEvent.obtain(ev);
+            if (ev.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                stashTaskbar = true;
+            } else if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+                mTaskbarDragLayer.getLocationOnScreen(mTempOutLocation);
+                screenCoordinates.offsetLocation(mTempOutLocation[0], mTempOutLocation[1]);
+
+                if (!mControllers.taskbarViewController.isEventOverAnyItem(screenCoordinates)
+                        && screenCoordinates.getY() < mGestureHeightYThreshold) {
+                    stashTaskbar = true;
+                }
+            }
+
+            if (stashTaskbar) {
+                mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+            }
         }
 
         /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 723e214..ea5df87 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -73,6 +73,7 @@
 
     private TaskbarControllers mControllers;
     private AnimatedFloat mTaskbarBackgroundAlpha;
+    private AnimatedFloat mTaskbarCornerRoundness;
     private MultiProperty mIconAlphaForHome;
     private QuickstepLauncher mLauncher;
 
@@ -118,10 +119,13 @@
                     mLauncherState = finalState;
                     updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false);
                     applyState();
-                    mControllers.taskbarDragController.setDisallowGlobalDrag(
-                            (finalState instanceof OverviewState));
-                    mControllers.taskbarDragController.setDisallowLongClick(
-                            finalState == LauncherState.OVERVIEW_SPLIT_SELECT);
+                    boolean disallowGlobalDrag = finalState instanceof OverviewState;
+                    boolean disallowLongClick = finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
+                    mControllers.taskbarDragController.setDisallowGlobalDrag(disallowGlobalDrag);
+                    mControllers.taskbarDragController.setDisallowLongClick(disallowLongClick);
+                    mControllers.taskbarAllAppsController.setDisallowGlobalDrag(disallowGlobalDrag);
+                    mControllers.taskbarAllAppsController.setDisallowLongClick(disallowLongClick);
+                    mControllers.taskbarPopupController.setHideSplitOptions(disallowGlobalDrag);
                 }
             };
 
@@ -133,6 +137,7 @@
 
         mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
                 .getTaskbarBackgroundAlpha();
+        mTaskbarCornerRoundness = mControllers.getTaskbarCornerRoundness();
         mIconAlphaForHome = mControllers.taskbarViewController
                 .getTaskbarIconAlpha().get(ALPHA_INDEX_HOME);
 
@@ -316,6 +321,19 @@
                     .setDuration(duration));
         }
 
+        float cornerRoundness = goingToLauncher ? 0 : 1;
+        // Don't animate if corner roundness has reached desired value.
+        if (mTaskbarCornerRoundness.isAnimating()
+                || mTaskbarCornerRoundness.value != cornerRoundness) {
+            mTaskbarCornerRoundness.cancelAnimation();
+            if (DEBUG) {
+                Log.d(TAG, "onStateChangeApplied - taskbarCornerRoundness - "
+                        + mTaskbarCornerRoundness.value
+                        + " -> " + cornerRoundness + ": " + duration);
+            }
+            animatorSet.play(mTaskbarCornerRoundness.animateToValue(cornerRoundness));
+        }
+
         if (mIconAlignment.isAnimatingToValue(toAlignment)
                 || mIconAlignment.isSettledOnValue(toAlignment)) {
             // Already at desired value, but make sure we run the callback at the end.
@@ -333,6 +351,7 @@
             }
             animatorSet.play(iconAlignAnim);
         }
+
         animatorSet.setInterpolator(EMPHASIZED);
 
         if (start) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index da6dab1..9b27c9d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -75,6 +75,7 @@
 
     // Initialized in init.
     private TaskbarControllers mControllers;
+    private boolean mHideSplitOptions;
 
     public TaskbarPopupController(TaskbarActivityContext context) {
         mContext = context;
@@ -100,6 +101,10 @@
         mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
     }
 
+    public void setHideSplitOptions(boolean hideSplitOptions) {
+        mHideSplitOptions = hideSplitOptions;
+    }
+
     private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
         final PackageUserKey packageUserKey = new PackageUserKey(null, null);
         Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
@@ -186,11 +191,16 @@
     // TODO(b/227800345): Add "Split bottom" option when tablet is in portrait mode.
     private Stream<SystemShortcut.Factory> getSystemShortcuts() {
         // concat a Stream of split options with a Stream of APP_INFO
+        Stream<SystemShortcut.Factory> appInfo = Stream.of(APP_INFO);
+        if (mHideSplitOptions) {
+            return appInfo;
+        }
+
         return Stream.concat(
                 Utilities.getSplitPositionOptions(mContext.getDeviceProfile())
                         .stream()
                         .map(this::createSplitShortcutFactory),
-                Stream.of(APP_INFO)
+                appInfo
         );
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
index 1d3757f..cdc6d59 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
@@ -69,4 +69,13 @@
         mRenderer.getPaint().setAlpha((int) (alpha * 255));
         invalidate();
     }
+
+    /**
+     * Sets the roundness of the round corner above Taskbar.
+     * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
+     */
+    protected void setCornerRoundness(float cornerRoundness) {
+        mRenderer.setCornerRoundness(cornerRoundness);
+        invalidate();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index c3b0f57..ce191b7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -30,7 +30,8 @@
 /**
  * Handles properties/data collection, and passes the results to {@link TaskbarScrimView} to render.
  */
-public class TaskbarScrimViewController implements TaskbarControllers.LoggableTaskbarController {
+public class TaskbarScrimViewController implements TaskbarControllers.LoggableTaskbarController,
+        TaskbarControllers.BackgroundRendererController {
 
     private static final float SCRIM_ALPHA = 0.6f;
 
@@ -95,6 +96,11 @@
     }
 
     @Override
+    public void setCornerRoundness(float cornerRoundness) {
+        mScrimView.setCornerRoundness(cornerRoundness);
+    }
+
+    @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "TaskbarScrimViewController:");
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
new file mode 100644
index 0000000..1a7ec13
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.quickstep.AnimatedFloat.VALUE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+
+import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.AnimatedFloat;
+
+import java.io.PrintWriter;
+
+/**
+ * Class responsible for translating the transient taskbar UI during a swipe gesture.
+ *
+ * The translation is controlled, in priority order:
+ * - animation to home
+ * - a spring animation
+ * - controlled by user
+ *
+ * The spring animation will play start once the user lets go or when user pauses to go to overview.
+ * When the user goes home, the stash animation will play.
+ */
+public class TaskbarTranslationController implements TaskbarControllers.LoggableTaskbarController {
+
+    private final TaskbarActivityContext mContext;
+    private TaskbarControllers mControllers;
+    private final AnimatedFloat mTranslationYForSwipe = new AnimatedFloat(
+            this::updateTranslationYForSwipe);
+
+    private boolean mHasSprungOnceThisGesture;
+    private @Nullable ValueAnimator mSpringBounce;
+    private boolean mGestureEnded;
+    private boolean mAnimationToHomeRunning;
+
+    private final boolean mIsTransientTaskbar;
+
+    private final TransitionCallback mCallback;
+
+    public TaskbarTranslationController(TaskbarActivityContext context) {
+        mContext = context;
+        mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+        mCallback = new TransitionCallback();
+    }
+
+    /**
+     * Initialization method.
+     */
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
+    /**
+     * Called to cancel any existing animations.
+     */
+    public void cancelAnimationIfExists() {
+        if (mSpringBounce != null) {
+            mSpringBounce.cancel();
+            mSpringBounce = null;
+        }
+        reset();
+    }
+
+    private void updateTranslationYForSwipe() {
+        if (!mIsTransientTaskbar) {
+            return;
+        }
+
+        float transY = mTranslationYForSwipe.value;
+        mControllers.stashedHandleViewController.setTranslationYForSwipe(transY);
+        mControllers.taskbarViewController.setTranslationYForSwipe(transY);
+        mControllers.taskbarDragLayerController.setTranslationYForSwipe(transY);
+    }
+
+    /**
+     * Starts a spring aniamtion to set the views back to the resting state.
+     */
+    public void startSpring() {
+        if (mHasSprungOnceThisGesture || mAnimationToHomeRunning) {
+            return;
+        }
+        mSpringBounce = new SpringAnimationBuilder(mContext)
+                .setStartValue(mTranslationYForSwipe.value)
+                .setEndValue(0)
+                .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
+                .setStiffness(SpringForce.STIFFNESS_LOW)
+                .build(mTranslationYForSwipe, VALUE);
+        mSpringBounce.addListener(forEndCallback(() -> {
+            if (mGestureEnded) {
+                reset();
+            }
+        }));
+        mSpringBounce.start();
+        mHasSprungOnceThisGesture = true;
+    }
+
+    private void reset() {
+        mGestureEnded = false;
+        mHasSprungOnceThisGesture = false;
+    }
+
+    /**
+     * Returns a callback to help monitor the swipe gesture.
+     */
+    public TransitionCallback getTransitionCallback() {
+        return mCallback;
+    }
+
+    /**
+     * Returns an animation to reset the taskbar translation for animation back to launcher.
+     */
+    public ObjectAnimator createAnimToLauncher(long duration) {
+        ObjectAnimator animator = ObjectAnimator.ofFloat(mTranslationYForSwipe, VALUE, 0);
+        animator.setInterpolator(Interpolators.LINEAR);
+        animator.setDuration(duration);
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                cancelAnimationIfExists();
+                mAnimationToHomeRunning = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimationToHomeRunning = false;
+                reset();
+            }
+        });
+        return animator;
+    }
+
+    /**
+     * Helper class to communicate to/from  the input consumer.
+     */
+    public class TransitionCallback {
+
+        /**
+         * Called when there is movement to move the taskbar.
+         */
+        public void onActionMove(float dY) {
+            if (mAnimationToHomeRunning
+                    || (mHasSprungOnceThisGesture && !mGestureEnded)) {
+                return;
+            }
+
+            mTranslationYForSwipe.updateValue(dY);
+        }
+
+        /**
+         * Called when swipe gesture has ended.
+         */
+        public void onActionEnd() {
+            if (mHasSprungOnceThisGesture) {
+                reset();
+            } else {
+                mGestureEnded = true;
+                startSpring();
+            }
+        }
+    }
+
+    @Override
+    public void dumpLogs(String prefix, PrintWriter pw) {
+        pw.println(prefix + "TaskbarTranslationController:");
+
+        pw.println(prefix + "\tmTranslationYForSwipe=" + mTranslationYForSwipe.value);
+        pw.println(prefix + "\tmHasSprungOnceThisGesture=" + mHasSprungOnceThisGesture);
+        pw.println(prefix + "\tmAnimationToHomeRunning=" + mAnimationToHomeRunning);
+        pw.println(prefix + "\tmGestureEnded=" + mGestureEnded);
+        pw.println(prefix + "\tmSpringBounce is running=" + (mSpringBounce != null
+                && mSpringBounce.isRunning()));
+    }
+}
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 1152126..4ec9b41 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,13 +15,18 @@
  */
 package com.android.launcher3.taskbar;
 
+import android.content.Intent;
+import android.graphics.drawable.BitmapDrawable;
 import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
 
 import java.io.PrintWriter;
 import java.util.stream.Stream;
@@ -105,6 +110,13 @@
         return mControllers.taskbarStashController.isStashed();
     }
 
+    /**
+     * Called at the end of the swipe gesture on Transient taskbar.
+     */
+    public void startTranslationSpring() {
+        mControllers.taskbarActivityContext.startTranslationSpring();
+    }
+
     /*
      * @param ev MotionEvent in screen coordinates.
      * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
@@ -128,4 +140,38 @@
                 prefix,
                 getClass().getSimpleName()));
     }
+
+    /**
+     * Returns RecentsView. Overwritten in LauncherTaskbarUIController and
+     * FallbackTaskbarUIController with Launcher-specific implementations. Returns null for other
+     * UI controllers (like DesktopTaskbarUIController) that don't have a RecentsView.
+     */
+    public @Nullable RecentsView getRecentsView() {
+        return null;
+    }
+
+    /**
+     * Uses the clicked Taskbar icon to launch a second app for splitscreen.
+     */
+    public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
+        RecentsView recents = getRecentsView();
+        TaskView foundTaskView = recents.getTaskViewByComponentName(info.getTargetComponent());
+        if (foundTaskView != null) {
+            recents.confirmSplitSelect(
+                    foundTaskView,
+                    foundTaskView.getTask(),
+                    foundTaskView.getIconView().getDrawable(),
+                    foundTaskView.getThumbnail(),
+                    foundTaskView.getThumbnail().getThumbnail(),
+                    /* intent */ null);
+        } else {
+            recents.confirmSplitSelect(
+                    /* containerTaskView */ null,
+                    /* task */ null,
+                    new BitmapDrawable(info.bitmap.icon),
+                    startingView,
+                    /* thumbnail */ null,
+                    intent);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index a73528b..80a31b4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -91,6 +91,7 @@
             this::updateTranslationY);
     private AnimatedFloat mTaskbarNavButtonTranslationY;
     private AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay;
+    private float mTaskbarIconTranslationYForSwipe;
 
     private final int mTaskbarBottomMargin;
 
@@ -260,9 +261,18 @@
         mTaskbarView.setScaleY(scale);
     }
 
+    /**
+     * Sets the translation of the TaskbarView during the swipe up gesture.
+     */
+    public void setTranslationYForSwipe(float transY) {
+        mTaskbarIconTranslationYForSwipe = transY;
+        updateTranslationY();
+    }
+
     private void updateTranslationY() {
         mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value
-                + mTaskbarIconTranslationYForStash.value);
+                + mTaskbarIconTranslationYForStash.value
+                + mTaskbarIconTranslationYForSwipe);
     }
 
     private void updateIconsBackground() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index 837af58..a033507 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -14,7 +14,8 @@
  * Controls Taskbar behavior while Voice Interaction Window (assistant) is showing.
  */
 class VoiceInteractionWindowController(val context: TaskbarActivityContext)
-    : TaskbarControllers.LoggableTaskbarController {
+    : TaskbarControllers.LoggableTaskbarController,
+        TaskbarControllers.BackgroundRendererController {
 
     private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
 
@@ -111,6 +112,11 @@
         }
     }
 
+    override fun setCornerRoundness(cornerRoundness: Float) {
+        taskbarBackgroundRenderer.setCornerRoundness(cornerRoundness)
+        separateWindowForTaskbarBackground.invalidate()
+    }
+
     override fun dumpLogs(prefix: String, pw: PrintWriter) {
         pw.println(prefix + "VoiceInteractionWindowController:")
         pw.println("$prefix\tisVoiceInteractionWindowVisible=$isVoiceInteractionWindowVisible")
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 85c6318..4dc8d47 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -48,6 +48,8 @@
     private AppInfo[] mApps;
     private int mAppsModelFlags;
     private List<ItemInfo> mPredictedApps;
+    private boolean mDisallowGlobalDrag;
+    private boolean mDisallowLongClick;
 
     /** Initialize the controller. */
     public void init(TaskbarControllers controllers, boolean allAppsVisible) {
@@ -78,6 +80,14 @@
         }
     }
 
+    public void setDisallowGlobalDrag(boolean disableDragForOverviewState) {
+        mDisallowGlobalDrag = disableDragForOverviewState;
+    }
+
+    public void setDisallowLongClick(boolean disallowLongClick) {
+        mDisallowLongClick = disallowLongClick;
+    }
+
     /** Updates the current predictions. */
     public void setPredictedApps(List<ItemInfo> predictedApps) {
         if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
@@ -123,6 +133,12 @@
         mAppsView.getFloatingHeaderView()
                 .findFixedRowByType(PredictionRowView.class)
                 .setPredictedApps(mPredictedApps);
+        // 1 alternative that would be more work:
+        // Create a shared drag layer between taskbar and taskbarAllApps so that when dragging
+        // starts and taskbarAllApps can close, but the drag layer that the view is being dragged in
+        // doesn't also close
+        overlayContext.getDragController().setDisallowGlobalDrag(mDisallowGlobalDrag);
+        overlayContext.getDragController().setDisallowLongClick(mDisallowLongClick);
     }
 
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 7e4c2f6..b09e531 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -99,6 +99,7 @@
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.tracing.InputConsumerProto;
 import com.android.launcher3.tracing.SwipeHandlerProto;
 import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
@@ -662,6 +663,8 @@
                         true/* moveFocusedTask */,
                         new ActiveGestureLog.CompoundString(
                                 "motion pause detected (animate=true)"));
+                Optional.ofNullable(mActivityInterface.getTaskbarController())
+                        .ifPresent(TaskbarUIController::startTranslationSpring);
                 performHapticFeedback();
             }
 
@@ -951,6 +954,8 @@
                             InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
                     InteractionJankMonitorWrapper.begin(mRecentsView,
                             InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+                    InteractionJankMonitorWrapper.begin(mRecentsView,
+                            InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
 
                     rv.post(() -> rv.getViewTreeObserver().removeOnDrawListener(this));
                 }
@@ -1055,6 +1060,10 @@
             InteractionJankMonitorWrapper.cancel(
                     InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
         }
+        if (endTarget != RECENTS) {
+            InteractionJankMonitorWrapper.cancel(
+                    InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
+        }
 
         switch (endTarget) {
             case HOME:
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index de150e1..a622326 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -30,6 +30,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -501,45 +502,43 @@
                         "setRecentsAttachedToAppWindow: exiting early"));
                 return;
             }
-            mIsAttachedToWindow = attached;
-            RecentsView recentsView = mActivity.getOverviewPanel();
-            if (attached) {
-                mHasEverAttachedToWindow = true;
-            }
-            Animator fadeAnim = mActivity.getStateManager()
-                    .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
-
-            float fromTranslation = attached ? 1 : 0;
-            float toTranslation = attached ? 0 : 1;
+            mActivity.getStateManager()
+                    .cancelStateElementAnimation(INDEX_RECENTS_FADE_ANIM);
             mActivity.getStateManager()
                     .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
-            if (!recentsView.isShown() && animate) {
-                ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, fromTranslation);
-                ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
-                        "setRecentsAttachedToAppWindow: recents view not shown, setting ")
-                        .append("ADJACENT_PAGE_HORIZONTAL_OFFSET to ")
-                        .append(Float.toString(fromTranslation)));
-            } else {
-                fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(recentsView);
-                ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
-                        "setRecentsAttachedToAppWindow: updating fromTranslation to ")
-                        .append(Float.toString(fromTranslation)));
-            }
+
+            AnimatorSet animatorSet = new AnimatorSet();
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    super.onAnimationStart(animation);
+                    mIsAttachedToWindow = attached;
+                    if (attached) {
+                        mHasEverAttachedToWindow = true;
+                    }
+                }});
+
+            long animationDuration = animate ? RECENTS_ATTACH_DURATION : 0;
+            Animator fadeAnim = mActivity.getStateManager()
+                    .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
+            fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2);
+            fadeAnim.setDuration(animationDuration);
+            animatorSet.play(fadeAnim);
+
+            float fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(
+                    mActivity.getOverviewPanel());
+            float toTranslation = attached ? 0 : 1;
             ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
                     "setRecentsAttachedToAppWindow: fromTranslation=")
                     .append(Float.toString(fromTranslation))
                     .append(", toTranslation=")
                     .append(Float.toString(toTranslation)));
-            if (!animate) {
-                ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, toTranslation);
-            } else {
-                mActivity.getStateManager().createStateElementAnimation(
-                        INDEX_RECENTS_TRANSLATE_X_ANIM,
-                        fromTranslation, toTranslation).start();
-            }
 
-            fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2);
-            fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
+            Animator translationAnimator = mActivity.getStateManager().createStateElementAnimation(
+                    INDEX_RECENTS_TRANSLATE_X_ANIM, fromTranslation, toTranslation);
+            translationAnimator.setDuration(animationDuration);
+            animatorSet.play(translationAnimator);
+            animatorSet.start();
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 875b72c..5a09e02 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -116,7 +116,7 @@
      */
     @BinderThread
     public void addCommand(int type) {
-        if (mPendingCommands.size() > MAX_QUEUE_SIZE) {
+        if (mPendingCommands.size() >= MAX_QUEUE_SIZE) {
             return;
         }
         CommandInfo cmd = new CommandInfo(type);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index cf1c137..4adfae5 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -170,6 +170,8 @@
             mController.finish(toRecents, sendUserLeaveHint);
             InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
             InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+            InteractionJankMonitorWrapper.end(
+                    InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
             MAIN_EXECUTOR.execute(mPendingFinishCallbacks::executeAllAndDestroy);
         });
     }
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index acf597b..ef7c6dc 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -548,15 +548,13 @@
         }
     }
 
-    public void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent,
-            Bundle options1, int taskId, Bundle options2,
-            @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
-            RemoteTransition remoteTransition, InstanceId instanceId) {
+    public void startIntentAndTask(PendingIntent pendingIntent, Bundle options1, int taskId,
+            Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
+            float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
-                mSplitScreen.startIntentAndTask(pendingIntent, fillInIntent, options1,
-                        taskId, options2, splitPosition, splitRatio,
-                        remoteTransition, instanceId);
+                mSplitScreen.startIntentAndTask(pendingIntent, options1, taskId, options2,
+                        splitPosition, splitRatio, remoteTransition, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startIntentAndTask");
             }
@@ -593,13 +591,13 @@
     }
 
     public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
-            Intent fillInIntent, Bundle options1, int taskId, Bundle options2,
+            Bundle options1, int taskId, Bundle options2,
             @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
             RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
-                mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
-                        options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
+                mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, options1, taskId,
+                        options2, splitPosition, splitRatio, adapter, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition");
             }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
index 1430492..b7f2022 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -27,9 +27,13 @@
 import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.MotionEvent;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
+import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.InputConsumer;
 import com.android.systemui.shared.system.InputMonitorCompat;
@@ -48,7 +52,7 @@
     private final float mUnstashArea;
     private final float mScreenWidth;
 
-    private final int mTaskbarThreshold;
+    private final int mTaskbarThresholdY;
     private boolean mHasPassedTaskbarThreshold;
 
     private final PointF mDownPos = new PointF();
@@ -57,6 +61,8 @@
 
     private final boolean mIsTransientTaskbar;
 
+    private final @Nullable TransitionCallback mTransitionCallback;
+
     public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
             InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) {
         super(delegate, inputMonitor);
@@ -66,7 +72,9 @@
 
         Resources res = context.getResources();
         mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
-        mTaskbarThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold);
+        int taskbarThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold);
+        int screenHeight = taskbarActivityContext.getDeviceProfile().heightPx;
+        mTaskbarThresholdY = screenHeight - taskbarThreshold;
 
         mIsTransientTaskbar = DisplayController.isTransientTaskbar(context);
 
@@ -76,6 +84,10 @@
                 onLongPressDetected(motionEvent);
             }
         });
+
+        mTransitionCallback = mIsTransientTaskbar
+                ? taskbarActivityContext.getTranslationCallbacks()
+                : null;
     }
 
     @Override
@@ -138,16 +150,25 @@
                             break;
                         }
                         mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-                        float displacementY = mLastPos.y - mDownPos.y;
-                        float verticalDist = Math.abs(displacementY);
-                        boolean passedTaskbarThreshold = verticalDist >= mTaskbarThreshold;
 
-                        if (!mHasPassedTaskbarThreshold
-                                && passedTaskbarThreshold
-                                && mIsTransientTaskbar) {
-                            mHasPassedTaskbarThreshold = true;
+                        if (mIsTransientTaskbar) {
+                            float dY = mLastPos.y - mDownPos.y;
+                            boolean passedTaskbarThreshold = dY < 0
+                                    && mLastPos.y < mTaskbarThresholdY;
 
-                            mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+                            if (!mHasPassedTaskbarThreshold
+                                    && passedTaskbarThreshold) {
+                                mHasPassedTaskbarThreshold = true;
+
+                                mTaskbarActivityContext.onSwipeToUnstashTaskbar();
+                            }
+
+                            if (dY < 0) {
+                                dY = -OverScroll.dampedScroll(-dY, mTaskbarThresholdY);
+                                if (mTransitionCallback != null) {
+                                    mTransitionCallback.onActionMove(dY);
+                                }
+                            }
                         }
                         break;
                     case MotionEvent.ACTION_UP:
@@ -158,6 +179,9 @@
                         }
                         mTaskbarActivityContext.setAutohideSuspendFlag(
                                 FLAG_AUTOHIDE_SUSPEND_TOUCHING, false);
+                        if (mTransitionCallback != null) {
+                            mTransitionCallback.onActionEnd();
+                        }
                         mHasPassedTaskbarThreshold = false;
                         break;
                 }
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 08f9fa6..4a74ac6 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -37,7 +37,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.view.RemoteAnimationAdapter;
@@ -88,10 +87,8 @@
     private ItemInfo mItemInfo;
     private Intent mInitialTaskIntent;
     private int mInitialTaskId = INVALID_TASK_ID;
-    private String mInitialTaskPackageName;
     private Intent mSecondTaskIntent;
     private int mSecondTaskId = INVALID_TASK_ID;
-    private String mSecondTaskPackageName;
     private boolean mRecentsAnimationRunning;
     @Nullable
     private UserHandle mUser;
@@ -119,7 +116,6 @@
     public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
             StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
         mInitialTaskId = task.key.id;
-        mInitialTaskPackageName = task.getTopComponent().getPackageName();
         setInitialData(stagePosition, splitEvent, itemInfo);
     }
 
@@ -131,7 +127,6 @@
         mInitialTaskIntent = intent;
         mUser = itemInfo.user;
         mItemInfo = itemInfo;
-        mInitialTaskPackageName = intent.getComponent().getPackageName();
         setInitialData(stagePosition, splitEvent, itemInfo);
     }
 
@@ -143,7 +138,6 @@
             @StagePosition int stagePosition, @NonNull ItemInfo itemInfo,
             StatsLogManager.EventEnum splitEvent) {
         mInitialTaskId = info.taskId;
-        mInitialTaskPackageName = info.topActivity.getPackageName();
         setInitialData(stagePosition, splitEvent, itemInfo);
     }
 
@@ -161,9 +155,9 @@
     public void launchSplitTasks(Consumer<Boolean> callback) {
         Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
                 LogUtils.getShellShareableInstanceId();
-        launchTasks(mInitialTaskId, mInitialTaskIntent, mInitialTaskPackageName, mSecondTaskId,
-                mSecondTaskIntent, mSecondTaskPackageName, mStagePosition, callback,
-                false /* freezeTaskList */, DEFAULT_SPLIT_RATIO, instanceIds.first);
+        launchTasks(mInitialTaskId, mInitialTaskIntent, mSecondTaskId, mSecondTaskIntent,
+                mStagePosition, callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
+                instanceIds.first);
 
         mStatsLogManager.logger()
                 .withItemInfo(mItemInfo)
@@ -177,12 +171,10 @@
      */
     public void setSecondTask(Task task) {
         mSecondTaskId = task.key.id;
-        mSecondTaskPackageName = task.getTopComponent().getPackageName();
     }
 
     public void setSecondTask(Intent intent) {
         mSecondTaskIntent = intent;
-        mSecondTaskPackageName = intent.getComponent().getPackageName();
     }
 
     /**
@@ -205,9 +197,8 @@
      */
     public void launchTasks(int taskId1, int taskId2, @StagePosition int stagePosition,
             Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
-        launchTasks(taskId1, null /* intent1 */, null /* packageName1 */, taskId2,
-                null /* intent2 */, null /* packageName2 */, stagePosition, callback,
-                freezeTaskList, splitRatio, null);
+        launchTasks(taskId1, null /* intent1 */, taskId2, null /* intent2 */, stagePosition,
+                callback, freezeTaskList, splitRatio, null);
     }
 
     /**
@@ -220,8 +211,8 @@
      *                   a split instance, null for cases that bring existing instaces to the
      *                   foreground (quickswitch, launching previous pairs from overview)
      */
-    public void launchTasks(int taskId1, @Nullable Intent intent1, String packageName1, int taskId2,
-            @Nullable Intent intent2, String packageName2, @StagePosition int stagePosition,
+    public void launchTasks(int taskId1, @Nullable Intent intent1, int taskId2,
+            @Nullable Intent intent2, @StagePosition int stagePosition,
             Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio,
             @Nullable InstanceId shellInstanceId) {
         TestLogging.recordEvent(
@@ -240,10 +231,10 @@
                         null /* options2 */, stagePosition, splitRatio, remoteTransition,
                         shellInstanceId);
             } else if (intent2 == null) {
-                launchIntentOrShortcut(intent1, packageName2, options1, taskId2, stagePosition,
-                        splitRatio, remoteTransition, shellInstanceId);
+                launchIntentOrShortcut(intent1, options1, taskId2, stagePosition, splitRatio,
+                        remoteTransition, shellInstanceId);
             } else if (intent1 == null) {
-                launchIntentOrShortcut(intent2, packageName1, options1, taskId1,
+                launchIntentOrShortcut(intent2, options1, taskId1,
                         getOppositeStagePosition(stagePosition), splitRatio, remoteTransition,
                         shellInstanceId);
             } else {
@@ -261,10 +252,10 @@
                         taskId2, null /* options2 */, stagePosition, splitRatio, adapter,
                         shellInstanceId);
             } else if (intent2 == null) {
-                launchIntentOrShortcutLegacy(intent1, packageName2, options1, taskId2,
-                        stagePosition, splitRatio, adapter, shellInstanceId);
+                launchIntentOrShortcutLegacy(intent1, options1, taskId2, stagePosition, splitRatio,
+                        adapter, shellInstanceId);
             } else if (intent1 == null) {
-                launchIntentOrShortcutLegacy(intent2, packageName1, options1, taskId1,
+                launchIntentOrShortcutLegacy(intent2, options1, taskId1,
                         getOppositeStagePosition(stagePosition), splitRatio, adapter,
                         shellInstanceId);
             } else {
@@ -273,9 +264,8 @@
         }
     }
 
-    private void launchIntentOrShortcut(Intent intent, String otherTaskPackageName,
-            ActivityOptions options1, int taskId, @StagePosition int stagePosition,
-            float splitRatio, RemoteTransition remoteTransition,
+    private void launchIntentOrShortcut(Intent intent, ActivityOptions options1, int taskId,
+            @StagePosition int stagePosition, float splitRatio, RemoteTransition remoteTransition,
             @Nullable InstanceId shellInstanceId) {
         PendingIntent pendingIntent = getPendingIntent(intent);
         final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
@@ -285,16 +275,14 @@
                     options1.toBundle(), taskId, null /* options2 */, stagePosition,
                     splitRatio, remoteTransition, shellInstanceId);
         } else {
-            mSystemUiProxy.startIntentAndTask(pendingIntent,
-                    getFillInIntent(intent, otherTaskPackageName), options1.toBundle(), taskId,
+            mSystemUiProxy.startIntentAndTask(pendingIntent, options1.toBundle(), taskId,
                     null /* options2 */, stagePosition, splitRatio, remoteTransition,
                     shellInstanceId);
         }
     }
 
-    private void launchIntentOrShortcutLegacy(Intent intent, String otherTaskPackageName,
-            ActivityOptions options1, int taskId, @StagePosition int stagePosition,
-            float splitRatio, RemoteAnimationAdapter adapter,
+    private void launchIntentOrShortcutLegacy(Intent intent, ActivityOptions options1, int taskId,
+            @StagePosition int stagePosition, float splitRatio, RemoteAnimationAdapter adapter,
             @Nullable InstanceId shellInstanceId) {
         PendingIntent pendingIntent = getPendingIntent(intent);
         final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
@@ -305,9 +293,8 @@
                     splitRatio, adapter, shellInstanceId);
         } else {
             mSystemUiProxy.startIntentAndTaskWithLegacyTransition(pendingIntent,
-                    getFillInIntent(intent, otherTaskPackageName), options1.toBundle(), taskId,
-                    null /* options2 */, stagePosition, splitRatio, adapter,
-                    shellInstanceId);
+                    options1.toBundle(), taskId, null /* options2 */, stagePosition, splitRatio,
+                    adapter, shellInstanceId);
         }
     }
 
@@ -318,18 +305,6 @@
                 : PendingIntent.getActivity(mContext, 0, intent, FLAG_MUTABLE));
     }
 
-    private Intent getFillInIntent(Intent intent, String otherTaskPackageName) {
-        if (intent == null) {
-            return null;
-        }
-
-        Intent fillInIntent = new Intent();
-        if (TextUtils.equals(intent.getComponent().getPackageName(), otherTaskPackageName)) {
-            fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-        }
-        return fillInIntent;
-    }
-
 
     public @StagePosition int getActiveSplitStagePosition() {
         return mStagePosition;
@@ -464,10 +439,8 @@
     public void resetState() {
         mInitialTaskId = INVALID_TASK_ID;
         mInitialTaskIntent = null;
-        mInitialTaskPackageName = null;
         mSecondTaskId = INVALID_TASK_ID;
         mSecondTaskIntent = null;
-        mSecondTaskPackageName = null;
         mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
         mRecentsAnimationRunning = false;
         mLaunchingTaskView = null;
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 5c37da1..b476c12 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -322,7 +322,7 @@
             boolean isRtlEnabled = !mIsRecentsRtl;
             mPositionHelper.updateThumbnailMatrix(
                     mThumbnailPosition, mThumbnailData, mTaskRect.width(), mTaskRect.height(),
-                    mDp.widthPx, mDp.taskbarSize, mDp.isTablet,
+                    mDp.widthPx, mDp.heightPx, mDp.taskbarSize, mDp.isTablet,
                     mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
             mPositionHelper.getMatrix().invert(mInversePositionMatrix);
             if (DEBUG) {
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 2abd715..8c43fd1 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -288,7 +288,7 @@
     public RunnableList launchTasks() {
         SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
         getRecentsView().startHome();
-        return new RunnableList();
+        return null;
     }
 
     @Nullable
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 35414a6..ff0c984 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -74,9 +74,12 @@
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.WindowConfiguration;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.LocusId;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -122,6 +125,7 @@
 import androidx.annotation.UiThread;
 import androidx.core.graphics.ColorUtils;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
 import com.android.launcher3.DeviceProfile;
@@ -661,8 +665,6 @@
     private TaskView mSecondSplitHiddenView;
     @Nullable
     private SplitBounds mSplitBoundsConfig;
-    private final Toast mSplitToast = Toast.makeText(getContext(),
-            R.string.toast_split_select_app, Toast.LENGTH_SHORT);
     private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
             R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);
 
@@ -1212,6 +1214,21 @@
         return null;
     }
 
+    /**
+     * Returns a {@link TaskView} that has ComponentName matching {@code componentName} or null if
+     * no match.
+     */
+    @Nullable
+    public TaskView getTaskViewByComponentName(ComponentName componentName) {
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView taskView = requireTaskViewAt(i);
+            if (taskView.getTask().key.sourceComponent.equals(componentName)) {
+                return taskView;
+            }
+        }
+        return null;
+    }
+
     public void setOverviewStateEnabled(boolean enabled) {
         mOverviewStateEnabled = enabled;
         updateTaskStackListenerState();
@@ -2241,8 +2258,14 @@
         for (int i = 0; i < getTaskViewCount(); i++) {
             requireTaskViewAt(i).setOrientationState(mOrientationState);
         }
+        boolean shouldRotateMenuForFakeRotation =
+                !mOrientationState.isRecentsActivityRotationAllowed();
+        if (!shouldRotateMenuForFakeRotation) {
+            return;
+        }
         TaskMenuView tv = (TaskMenuView) getTopOpenViewWithType(mActivity, TYPE_TASK_MENU);
         if (tv != null) {
+            // Rotation is supported on phone (details at b/254198019#comment4)
             tv.onRotationChanged();
         }
     }
@@ -3521,7 +3544,8 @@
         mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SELECT_ACTIVE, isSplitSelectionActive());
         mActionsView.updateSplitButtonHiddenFlags(FLAG_IS_NOT_TABLET,
                 !mActivity.getDeviceProfile().isTablet);
-        mActionsView.updateSplitButtonDisabledFlags(FLAG_SINGLE_TASK, getTaskViewCount() <= 1);
+        mActionsView.updateSplitButtonDisabledFlags(FLAG_SINGLE_TASK,
+                !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get() && getTaskViewCount() <= 1);
     }
 
     /**
@@ -4230,24 +4254,39 @@
      * Confirms the selection of the next split task. The extra data is passed through because the
      * user may be selecting a subtask in a group.
      *
+     * @param containerTaskView If our second selected app is currently running in Recents, this is
+     *                          the "container" TaskView from Recents. If we are starting a fresh
+     *                          instance of the app from an Intent, this will be null.
+     * @param task The Task corresponding to our second selected app. If we are starting a fresh
+     *             instance of the app from an Intent, this will be null.
+     * @param drawable The Drawable corresponding to our second selected app's icon.
+     * @param secondView The View representing the current space on the screen where the second app
+     *                   is (either the ThumbnailView or the tapped icon).
+     * @param intent If we are launching a fresh instance of the app, this is the Intent for it. If
+     *               the second app is already running in Recents, this will be null.
      * @return true if waiting for confirmation of second app or if split animations are running,
      *          false otherwise
      */
-    public boolean confirmSplitSelect(TaskView containerTaskView, Task task, IconView iconView,
-            TaskThumbnailView thumbnailView) {
+    public boolean confirmSplitSelect(TaskView containerTaskView, Task task, Drawable drawable,
+            View secondView, @Nullable Bitmap thumbnail, Intent intent) {
         if (canLaunchFullscreenTask()) {
             return false;
         }
         if (mSplitSelectStateController.isBothSplitAppsConfirmed()) {
             return true;
         }
-        mSplitToast.cancel();
-        if (!task.isDockable) {
-            // Task not split screen supported
-            mSplitUnsupportedToast.show();
-            return true;
+        // Second task is selected either as an already-running Task or an Intent
+        if (task != null) {
+            if (!task.isDockable) {
+                // Task does not support split screen
+                mSplitUnsupportedToast.show();
+                return true;
+            }
+            mSplitSelectStateController.setSecondTask(task);
+        } else {
+            mSplitSelectStateController.setSecondTask(intent);
         }
-        mSplitSelectStateController.setSecondTask(task);
+
         RectF secondTaskStartingBounds = new RectF();
         Rect secondTaskEndingBounds = new Rect();
         // TODO(194414938) starting bounds seem slightly off, investigate
@@ -4274,9 +4313,9 @@
                 false /* fadeWithThumbnail */, true /* isStagedTask */);
 
         safeRemoveDragLayerView(mSecondFloatingTaskView);
-        mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
-                thumbnailView, thumbnailView.getThumbnail(),
-                iconView.getDrawable(), secondTaskStartingBounds);
+
+        mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity, secondView,
+                thumbnail, drawable, secondTaskStartingBounds);
         mSecondFloatingTaskView.setAlpha(1);
         mSecondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
                 secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
@@ -4292,7 +4331,9 @@
         });
 
         mSecondSplitHiddenView = containerTaskView;
-        mSecondSplitHiddenView.setThumbnailVisibility(INVISIBLE);
+        if (mSecondSplitHiddenView != null) {
+            mSecondSplitHiddenView.setThumbnailVisibility(INVISIBLE);
+        }
 
         InteractionJankMonitorWrapper.begin(this,
                 InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 6792dc5..904c944 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -473,8 +473,8 @@
             boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
             DeviceProfile dp = mActivity.getDeviceProfile();
             mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
-                    getMeasuredWidth(), getMeasuredHeight(), dp.widthPx, dp.taskbarSize,
-                    dp.isTablet, currentRotation, isRtl);
+                    getMeasuredWidth(), getMeasuredHeight(), dp.widthPx, dp.heightPx,
+                    dp.taskbarSize, dp.isTablet, currentRotation, isRtl);
 
             mBitmapShader.setLocalMatrix(mPreviewPositionHelper.getMatrix());
             mPaint.setShader(mBitmapShader);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 527a0d1..c428c64 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -621,7 +621,8 @@
         TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
         if (container != null) {
             return getRecentsView().confirmSplitSelect(this, container.getTask(),
-                    container.getIconView(), container.getThumbnailView());
+                    container.getIconView().getDrawable(), container.getThumbnailView(),
+                    container.getThumbnailView().getThumbnail(), /* intent */ null);
         }
         return false;
     }
@@ -725,13 +726,14 @@
     /**
      * Launch of the current task (both live and inactive tasks) with an animation.
      */
+    @Nullable
     public RunnableList launchTasks() {
         RecentsView recentsView = getRecentsView();
         RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
-        RunnableList runnableList = new RunnableList();
         if (isRunningTask() && remoteTargetHandles != null) {
             if (!mIsClickableAsLiveTile) {
-                return runnableList;
+                Log.e(TAG, "TaskView is not clickable as a live tile; returning to home.");
+                return null;
             }
 
             mIsClickableAsLiveTile = false;
@@ -756,11 +758,16 @@
             if (targets == null) {
                 // If the recents animation is cancelled somehow between the parent if block and
                 // here, try to launch the task as a non live tile task.
-                launchTaskAnimated();
+                RunnableList runnableList = launchTaskAnimated();
+                if (runnableList == null) {
+                    Log.e(TAG, "Recents animation cancelled and cannot launch task as non-live tile"
+                            + "; returning to home");
+                }
                 mIsClickableAsLiveTile = true;
                 return runnableList;
             }
 
+            RunnableList runnableList = new RunnableList();
             AnimatorSet anim = new AnimatorSet();
             TaskViewUtils.composeRecentsLaunchAnimator(
                     anim, this, targets.apps,
@@ -797,10 +804,10 @@
             });
             anim.start();
             recentsView.onTaskLaunchedInLiveTileMode();
+            return runnableList;
         } else {
             return launchTaskAnimated();
         }
-        return runnableList;
     }
 
     /**
diff --git a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
index 4785350..963da27 100644
--- a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
@@ -57,7 +57,7 @@
         val isRtl = false
 
         mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
-                canvasHeight, dp.widthPx, dp.taskbarSize, dp.isTablet, currentRotation,
+                canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
                 isRtl)
         params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
                 /* taskViewScale= */ 1.0f,  /* previewWidth= */ 0, dp, mPreviewPositionHelper)
@@ -78,7 +78,7 @@
         val isRtl = false
 
         mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
-                canvasHeight, dp.widthPx, dp.taskbarSize, dp.isTablet, currentRotation,
+                canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation,
                 isRtl)
         params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
                 /* taskViewScale= */ 1.0f,  /* previewWidth= */ 0, dp, mPreviewPositionHelper)
diff --git a/res/color-night-v31/transient_taskbar_background.xml b/res/color-night-v31/transient_taskbar_background.xml
new file mode 100644
index 0000000..40f6494
--- /dev/null
+++ b/res/color-night-v31/transient_taskbar_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:color="@android:color/system_neutral1_500" android:lStar="15" />
+</selector>
+
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 39f93d9..5518dc8 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -30,11 +30,11 @@
     <LinearLayout
         android:id="@+id/folder_footer"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/folder_label_height"
+        android:layout_height="@dimen/folder_footer_height_default"
         android:clipChildren="false"
         android:orientation="horizontal"
-        android:paddingLeft="12dp"
-        android:paddingRight="12dp">
+        android:paddingLeft="@dimen/folder_footer_horiz_padding"
+        android:paddingRight="@dimen/folder_footer_horiz_padding">
 
         <com.android.launcher3.folder.FolderNameEditText
             android:id="@+id/folder_name"
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 9e2d9a4..8b944fe 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -124,7 +124,7 @@
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> instalatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> deskargatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> instalatzeko zain"</string>
-    <string name="dialog_update_title" msgid="114234265740994042">"Aplikazioa eguneratu behar da"</string>
+    <string name="dialog_update_title" msgid="114234265740994042">"Aplikazioa eguneratu egin behar da"</string>
     <string name="dialog_update_message" msgid="4176784553982226114">"Ikonoaren aplikazioa ez dago eguneratuta. Lasterbidea berriro gaitzeko, eskuz egunera dezakezu aplikazioa. Bestela, kendu ikonoa."</string>
     <string name="dialog_update" msgid="2178028071796141234">"Eguneratu"</string>
     <string name="dialog_remove" msgid="6510806469849709407">"Kendu"</string>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 7b2ed8b..09b2d6f 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -43,4 +43,7 @@
 
 <!-- Bottom sheet-->
     <dimen name="bottom_sheet_extra_top_padding">300dp</dimen>
+
+    <!--  Folder spaces  -->
+    <dimen name="folder_footer_horiz_padding">24dp</dimen>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 283c793..f270b10 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -141,9 +141,12 @@
         <attr name="numColumns" format="integer" />
         <!--  numSearchContainerColumns defaults to numColumns, if not specified -->
         <attr name="numSearchContainerColumns" format="integer" />
+
         <!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
         <attr name="numFolderRows" format="integer" />
         <attr name="numFolderColumns" format="integer" />
+        <attr name="folderStyle" format="reference" />
+
         <!-- numAllAppsColumns defaults to numColumns, if not specified -->
         <attr name="numAllAppsColumns" format="integer" />
         <!-- Number of columns to use when extending the all-apps size,
@@ -333,19 +336,6 @@
         if not specified -->
         <attr name="allAppsBorderSpaceTwoPanelLandscapeVertical" format="float" />
 
-        <!-- defaults to minCellHeight if not specified
-        when GridDisplayOption#isScalable is true. -->
-        <attr name="folderCellHeight" format="float" />
-        <!-- defaults to minCellWidth, if not specified -->
-        <attr name="folderCellWidth" format="float" />
-
-        <!-- defaults to borderSpace, if not specified -->
-        <!-- space to be used horizontally and vertically -->
-        <attr name="folderBorderSpace" format="float" />
-
-        <!-- defaults to folderBorderSpace vertical, if not specified -->
-        <attr name="folderTopPadding" format="float" />
-
         <!-- defaults to res.hotseat_bar_bottom_space_default, if not specified -->
         <attr name="hotseatBarBottomSpace" format="float" />
         <!-- defaults to hotseatBarBottomSpace, if not specified -->
@@ -394,6 +384,22 @@
 
     </declare-styleable>
 
+    <declare-styleable name="FolderDisplayStyle">
+        <!-- defaults to minCellHeight if not specified
+        when GridDisplayOption#isScalable is true. -->
+        <attr name="folderCellHeight" format="dimension" />
+        <!-- defaults to minCellWidth, if not specified -->
+        <attr name="folderCellWidth" format="dimension" />
+        <!-- space to be used horizontally and vertically between icons,
+        and to the left and right of folder -->
+        <attr name="folderBorderSpace" format="dimension" />
+        <!-- height of the footer of the folder -->
+        <attr name="folderFooterHeight" format="dimension" />
+        <!-- padding on top of the folder -->
+        <attr name="folderTopPadding" format="dimension" />
+    </declare-styleable>
+
+
     <declare-styleable name="CellLayout">
         <attr name="containerType" format="integer">
             <enum name="workspace" value="0" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8f48ec8..3b92ac4 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -255,7 +255,7 @@
     <dimen name="folder_cell_y_padding">6dp</dimen>
     <!-- label text size = workspace text size multiplied by this scale -->
     <dimen name="folder_label_text_scale">1.14</dimen>
-    <dimen name="folder_label_height">56dp</dimen>
+    <dimen name="folder_footer_height_default">56dp</dimen>
 
     <dimen name="folder_content_padding_left_right">8dp</dimen>
     <dimen name="folder_content_padding_top">16dp</dimen>
@@ -333,7 +333,7 @@
 
     <!-- Snackbar -->
     <dimen name="snackbar_height">48dp</dimen>
-    <dimen name="snackbar_content_height">32dp</dimen>
+    <dimen name="snackbar_content_height">48dp</dimen>
     <dimen name="snackbar_padding">8dp</dimen>
     <dimen name="snackbar_min_margin_left_right">6dp</dimen>
     <dimen name="snackbar_max_margin_left_right">72dp</dimen>
@@ -441,4 +441,8 @@
 
     <!-- State transition -->
     <item name="workspace_content_scale" format="float" type="dimen">0.97</item>
+
+    <!--  Folder spaces  -->
+    <dimen name="folder_top_padding_default">24dp</dimen>
+    <dimen name="folder_footer_horiz_padding">20dp</dimen>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d0be420..9e75a31 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -309,4 +309,13 @@
         <item name="android:windowLightStatusBar">true</item>
         <item name="android:windowTranslucentStatus">true</item>
     </style>
+
+    <style name="FolderDefaultStyle">
+        <item name="folderTopPadding">24dp</item>
+        <item name="folderCellHeight">94dp</item>
+        <item name="folderCellWidth">80dp</item>
+        <item name="folderBorderSpace">16dp</item>
+        <item name="folderFooterHeight">56dp</item>
+    </style>
+
 </resources>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index de2a1f9..2103593 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -373,7 +373,8 @@
     private void resetCellSizeInternal(DeviceProfile deviceProfile) {
         switch (mContainerType) {
             case FOLDER:
-                mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx);
+                mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx,
+                        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 2b9eb29..c91e3eb 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -25,12 +25,14 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
+import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -61,6 +63,7 @@
 
     private static final int DEFAULT_DOT_SIZE = 100;
     private static final float ALL_APPS_TABLET_MAX_ROWS = 5.5f;
+    private static final float MIN_FOLDER_TEXT_SIZE_SP = 16f;
 
     public static final PointF DEFAULT_SCALE = new PointF(1.0f, 1.0f);
     public static final ViewScaleProvider DEFAULT_PROVIDER = itemInfo -> DEFAULT_SCALE;
@@ -147,11 +150,12 @@
     // Folder
     public float folderLabelTextScale;
     public int folderLabelTextSizePx;
+    public int folderFooterHeightPx;
     public int folderIconSizePx;
     public int folderIconOffsetYPx;
 
     // Folder content
-    public Point folderCellLayoutBorderSpacePx;
+    public int folderCellLayoutBorderSpacePx;
     public int folderContentPaddingLeftRight;
     public int folderContentPaddingTop;
 
@@ -352,17 +356,34 @@
         }
 
         folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale);
-        folderContentPaddingLeftRight =
-                res.getDimensionPixelSize(R.dimen.folder_content_padding_left_right);
-        folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_content_padding_top);
+
+        if (inv.folderStyle != INVALID_RESOURCE_HANDLE) {
+            TypedArray folderStyle = context.obtainStyledAttributes(inv.folderStyle,
+                    R.styleable.FolderDisplayStyle);
+            // These are re-set in #updateFolderCellSize if the grid is not scalable
+            folderCellHeightPx = folderStyle.getDimensionPixelSize(
+                    R.styleable.FolderDisplayStyle_folderCellHeight, 0);
+            folderCellWidthPx = folderStyle.getDimensionPixelSize(
+                    R.styleable.FolderDisplayStyle_folderCellWidth, 0);
+
+            folderContentPaddingTop = folderStyle.getDimensionPixelSize(
+                    R.styleable.FolderDisplayStyle_folderTopPadding, 0);
+            folderCellLayoutBorderSpacePx = folderStyle.getDimensionPixelSize(
+                    R.styleable.FolderDisplayStyle_folderBorderSpace, 0);
+            folderFooterHeightPx = folderStyle.getDimensionPixelSize(
+                    R.styleable.FolderDisplayStyle_folderFooterHeight, 0);
+            folderStyle.recycle();
+        } else {
+            folderCellLayoutBorderSpacePx = 0;
+            folderFooterHeightPx = 0;
+            folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_top_padding_default);
+        }
 
         cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv);
+        cellLayoutBorderSpaceOriginalPx = new Point(cellLayoutBorderSpacePx);
         allAppsBorderSpacePx = new Point(
                 pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].x, mMetrics),
                 pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].y, mMetrics));
-        cellLayoutBorderSpaceOriginalPx = new Point(cellLayoutBorderSpacePx);
-        folderCellLayoutBorderSpacePx = new Point(pxFromDp(inv.folderBorderSpaces.x, mMetrics),
-                pxFromDp(inv.folderBorderSpaces.y, mMetrics));
 
         workspacePageIndicatorHeight = res.getDimensionPixelSize(
                 R.dimen.workspace_page_indicator_height);
@@ -931,22 +952,20 @@
     private void updateAvailableFolderCellDimensions(Resources res) {
         updateFolderCellSize(1f, res);
 
-        final int folderBottomPanelSize = res.getDimensionPixelSize(R.dimen.folder_label_height);
-
         // Don't let the folder get too close to the edges of the screen.
         int folderMargin = edgeMarginPx * 2;
         Point totalWorkspacePadding = getTotalWorkspacePadding();
 
         // Check if the icons fit within the available height.
         float contentUsedHeight = folderCellHeightPx * inv.numFolderRows
-                + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx.y);
-        int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y - folderBottomPanelSize
+                + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx);
+        int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y - folderFooterHeightPx
                 - folderMargin - folderContentPaddingTop;
         float scaleY = contentMaxHeight / contentUsedHeight;
 
         // Check if the icons fit within the available width.
         float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns
-                + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x);
+                + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx);
         int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin
                 - folderContentPaddingLeftRight * 2;
         float scaleX = contentMaxWidth / contentUsedWidth;
@@ -964,19 +983,18 @@
         folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
         folderChildTextSizePx =
                 pxFromSp(inv.iconTextSize[INDEX_DEFAULT], mMetrics, scale);
-        folderLabelTextSizePx = (int) (folderChildTextSizePx * folderLabelTextScale);
+        folderLabelTextSizePx = Math.max(pxFromSp(MIN_FOLDER_TEXT_SIZE_SP, mMetrics),
+                (int) (folderChildTextSizePx * folderLabelTextScale));
 
         int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
 
         if (isScalableGrid) {
-            folderCellWidthPx = pxFromDp(inv.folderCellSize.x, mMetrics, scale);
-            folderCellHeightPx = pxFromDp(inv.folderCellSize.y, mMetrics, scale);
+            if (inv.folderStyle == INVALID_RESOURCE_HANDLE) {
+                folderCellWidthPx = pxFromDp(getCellSize().x, mMetrics, scale);
+                folderCellHeightPx = pxFromDp(getCellSize().y, mMetrics, scale);
+            }
 
-            folderCellLayoutBorderSpacePx = new Point(
-                    pxFromDp(inv.folderBorderSpaces.x, mMetrics, scale),
-                    pxFromDp(inv.folderBorderSpaces.y, mMetrics, scale));
-            folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx.x;
-            folderContentPaddingTop = pxFromDp(inv.folderTopPadding, mMetrics, scale);
+            folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx;
         } else {
             int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding)
                     * scale);
@@ -985,6 +1003,10 @@
 
             folderCellWidthPx = folderChildIconSizePx + 2 * cellPaddingX;
             folderCellHeightPx = folderChildIconSizePx + 2 * cellPaddingY + textHeight;
+            folderContentPaddingLeftRight =
+                    res.getDimensionPixelSize(R.dimen.folder_content_padding_left_right);
+            folderFooterHeightPx =
+                    res.getDimensionPixelSize(R.dimen.folder_footer_height_default);
         }
 
         folderChildDrawablePaddingPx = Math.max(0,
@@ -1468,13 +1490,12 @@
         writer.println(prefix + pxToDpStr("folderChildTextSizePx", folderChildTextSizePx));
         writer.println(prefix + pxToDpStr("folderChildDrawablePaddingPx",
                 folderChildDrawablePaddingPx));
-        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx Horizontal",
-                folderCellLayoutBorderSpacePx.x));
-        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx Vertical",
-                folderCellLayoutBorderSpacePx.y));
+        writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx",
+                folderCellLayoutBorderSpacePx));
         writer.println(prefix + pxToDpStr("folderContentPaddingLeftRight",
                 folderContentPaddingLeftRight));
         writer.println(prefix + pxToDpStr("folderTopPadding", folderContentPaddingTop));
+        writer.println(prefix + pxToDpStr("folderFooterHeight", folderFooterHeightPx));
 
         writer.println(prefix + pxToDpStr("bottomSheetTopPadding", bottomSheetTopPadding));
         writer.println(prefix + "\tbottomSheetOpenDuration: " + bottomSheetOpenDuration);
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 635f400..ca92aa4 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
+import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
@@ -43,8 +44,10 @@
 import android.util.Xml;
 import android.view.Display;
 
+import androidx.annotation.DimenRes;
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.content.res.ResourcesCompat;
 
@@ -129,11 +132,9 @@
     public PointF[] minCellSize;
 
     public PointF[] borderSpaces;
-    public int inlineNavButtonsEndSpacing;
+    public @DimenRes int inlineNavButtonsEndSpacing;
 
-    public PointF folderBorderSpaces;
-    public PointF folderCellSize;
-    public float folderTopPadding;
+    public @StyleRes int folderStyle;
 
     public float[] horizontalMargin;
 
@@ -323,6 +324,11 @@
         return displayOption.grid.name;
     }
 
+    @VisibleForTesting
+    public static String getDefaultGridName(Context context) {
+        return new InvariantDeviceProfile().initGrid(context, null);
+    }
+
     private void initGrid(Context context, Info displayInfo, DisplayOption displayOption,
             @DeviceType int deviceType) {
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
@@ -333,8 +339,11 @@
         dbFile = closestProfile.dbFile;
         defaultLayoutId = closestProfile.defaultLayoutId;
         demoModeLayoutId = closestProfile.demoModeLayoutId;
+
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
+        folderStyle = closestProfile.folderStyle;
+
         isScalable = closestProfile.isScalable;
         devicePaddingId = closestProfile.devicePaddingId;
         this.deviceType = deviceType;
@@ -357,10 +366,6 @@
 
         borderSpaces = displayOption.borderSpaces;
 
-        folderBorderSpaces = displayOption.folderBorderSpaces;
-        folderCellSize = displayOption.folderCellSize;
-        folderTopPadding = displayOption.folderTopPadding;
-
         horizontalMargin = displayOption.horizontalMargin;
 
         numShownHotseatIcons = closestProfile.numHotseatIcons;
@@ -749,6 +754,7 @@
 
         private final int numFolderRows;
         private final int numFolderColumns;
+        private final @StyleRes int folderStyle;
 
         private final int numAllAppsColumns;
         private final int numDatabaseAllAppsColumns;
@@ -759,7 +765,7 @@
 
         private final boolean[] inlineQsb = new boolean[COUNT_SIZES];
 
-        private int inlineNavButtonsEndSpacing;
+        private @DimenRes int inlineNavButtonsEndSpacing;
         private final String dbFile;
 
         private final int defaultLayoutId;
@@ -811,11 +817,15 @@
             inlineNavButtonsEndSpacing =
                     a.getResourceId(R.styleable.GridDisplayOption_inlineNavButtonsEndSpacing,
                     R.dimen.taskbar_button_margin_default);
+
             numFolderRows = a.getInt(
                     R.styleable.GridDisplayOption_numFolderRows, numRows);
             numFolderColumns = a.getInt(
                     R.styleable.GridDisplayOption_numFolderColumns, numColumns);
 
+            folderStyle = a.getResourceId(R.styleable.GridDisplayOption_folderStyle,
+                    INVALID_RESOURCE_HANDLE);
+
             isScalable = a.getBoolean(
                     R.styleable.GridDisplayOption_isScalable, false);
             devicePaddingId = a.getResourceId(
@@ -860,10 +870,6 @@
 
         private final PointF[] minCellSize = new PointF[COUNT_SIZES];
 
-        private final PointF folderCellSize;
-        private final PointF folderBorderSpaces;
-        private float folderTopPadding;
-
         private final PointF[] borderSpaces = new PointF[COUNT_SIZES];
         private final float[] horizontalMargin = new float[COUNT_SIZES];
         private final float[] hotseatBarBottomSpace = new float[COUNT_SIZES];
@@ -946,21 +952,6 @@
                     borderSpaceTwoPanelLandscape);
             borderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
 
-            x = a.getFloat(R.styleable.ProfileDisplayOption_folderCellWidth,
-                    minCellSize[INDEX_DEFAULT].x);
-            y = a.getFloat(R.styleable.ProfileDisplayOption_folderCellHeight,
-                    minCellSize[INDEX_DEFAULT].y);
-            folderCellSize = new PointF(x, y);
-
-            float folderBorderSpace = a.getFloat(R.styleable.ProfileDisplayOption_folderBorderSpace,
-                    borderSpace);
-
-            x = y = folderBorderSpace;
-            folderBorderSpaces = new PointF(x, y);
-
-            folderTopPadding = a.getFloat(R.styleable.ProfileDisplayOption_folderTopPadding,
-                    folderBorderSpaces.y);
-
             x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidth,
                     minCellSize[INDEX_DEFAULT].x);
             y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeight,
@@ -1133,9 +1124,6 @@
                 allAppsIconTextSizes[i] = 0;
                 allAppsBorderSpaces[i] = new PointF();
             }
-            folderBorderSpaces = new PointF();
-            folderCellSize = new PointF();
-            folderTopPadding = 0f;
         }
 
         private DisplayOption multiply(float w) {
@@ -1156,11 +1144,6 @@
                 allAppsBorderSpaces[i].x *= w;
                 allAppsBorderSpaces[i].y *= w;
             }
-            folderBorderSpaces.x *= w;
-            folderBorderSpaces.y *= w;
-            folderCellSize.x *= w;
-            folderCellSize.y *= w;
-            folderTopPadding *= w;
 
             return this;
         }
@@ -1183,11 +1166,6 @@
                 allAppsBorderSpaces[i].x += p.allAppsBorderSpaces[i].x;
                 allAppsBorderSpaces[i].y += p.allAppsBorderSpaces[i].y;
             }
-            folderBorderSpaces.x += p.folderBorderSpaces.x;
-            folderBorderSpaces.y += p.folderBorderSpaces.y;
-            folderCellSize.x += p.folderCellSize.x;
-            folderCellSize.y += p.folderCellSize.y;
-            folderTopPadding += p.folderTopPadding;
 
             return this;
         }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c73e077..ab7f622 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -482,6 +482,9 @@
 
         mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
 
+        // TODO: move the SearchConfig to SearchState when new LauncherState is created.
+        mBaseSearchConfig = new BaseSearchConfig();
+
         mAppWidgetManager = new WidgetManagerHelper(this);
         mAppWidgetHolder = createAppWidgetHolder();
         mAppWidgetHolder.startListening();
@@ -548,9 +551,6 @@
             getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
         }
         setTitle(R.string.home_screen);
-
-        // TODO: move the SearchConfig to SearchState when new LauncherState is created.
-        mBaseSearchConfig = new BaseSearchConfig();
     }
 
     protected LauncherOverlayManager getDefaultOverlay() {
@@ -1598,7 +1598,6 @@
                 && AbstractFloatingView.getTopOpenView(this) == null;
         boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
         boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this);
-        hideKeyboard();
 
         if (isActionMain) {
             if (!internalStateHandled) {
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 8b342ea..7a74d7e 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -152,7 +152,7 @@
             // No need to add padding when cell layout border spacing is present.
             boolean noPaddingX =
                     (dp.cellLayoutBorderSpacePx.x > 0 && mContainerType == WORKSPACE)
-                            || (dp.folderCellLayoutBorderSpacePx.x > 0 && mContainerType == FOLDER)
+                            || (dp.folderCellLayoutBorderSpacePx > 0 && mContainerType == FOLDER)
                             || (dp.hotseatBorderSpace > 0 && mContainerType == HOTSEAT);
             int cellPaddingX = noPaddingX
                     ? 0
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index f70511a..a507532 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -521,6 +521,11 @@
         return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
     }
 
+    /** Converts a dp value to pixels for a certain density. */
+    public static int dpToPx(float dp, int densityDpi) {
+        float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+        return (int) (dp * densityRatio);
+    }
 
     public static int pxFromSp(float size, DisplayMetrics metrics) {
         return pxFromSp(size, metrics, 1f);
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 368a373..e34d4c8 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -31,6 +31,7 @@
 import com.android.launcher3.util.ScrollableLayoutManager;
 import com.android.launcher3.views.ActivityContext;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -43,6 +44,31 @@
 
     public static final String TAG = "AppsGridAdapter";
     private final AppsGridLayoutManager mGridLayoutMgr;
+    private final List<OnLayoutCompletedListener> mOnLayoutCompletedListeners = new ArrayList<>();
+
+    /**
+     * Listener for {@link RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)}
+     */
+    public interface OnLayoutCompletedListener {
+        void onLayoutCompleted();
+    }
+
+    /**
+     * Adds a {@link OnLayoutCompletedListener} to receive a callback when {@link
+     * RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)} is called
+     */
+    public void addOnLayoutCompletedListener(OnLayoutCompletedListener listener) {
+        mOnLayoutCompletedListeners.add(listener);
+    }
+
+    /**
+     * Removes a {@link OnLayoutCompletedListener} to not receive a callback when {@link
+     * RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)} is called
+     */
+    public void removeOnLayoutCompletedListener(OnLayoutCompletedListener listener) {
+        mOnLayoutCompletedListeners.remove(listener);
+    }
+
 
     public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
             AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) {
@@ -133,6 +159,14 @@
         }
 
         @Override
+        public void onLayoutCompleted(RecyclerView.State state) {
+            super.onLayoutCompleted(state);
+            for (OnLayoutCompletedListener listener : mOnLayoutCompletedListeners) {
+                listener.onLayoutCompleted();
+            }
+        }
+
+        @Override
         protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
             AllAppsGridAdapter.AdapterItem item = mApps.getAdapterItems().get(position);
             // only account for the first icon in the row since they are the same size within a row
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index fa2c6e9..9930abe 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -141,6 +141,9 @@
     private final Launcher mLauncher;
     private boolean mIsVerticalLayout;
 
+    // Whether this class should take care of closing the keyboard.
+    private boolean mShouldControlKeyboard;
+
     // Animation in this class is controlled by a single variable {@link mProgress}.
     // Visually, it represents top y coordinate of the all apps container if multiplied with
     // {@link mShiftRange}.
@@ -238,7 +241,7 @@
             StateAnimationConfig config, PendingAnimation builder) {
         if (mLauncher.isInState(ALL_APPS) && !ALL_APPS.equals(toState)) {
             // For atomic animations, we close the keyboard immediately.
-            if (!config.userControlled && !mLauncher.getSearchConfig().isKeyboardSyncEnabled()) {
+            if (!config.userControlled && mShouldControlKeyboard) {
                 mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
             }
 
@@ -252,7 +255,7 @@
                 // the keyboard open and then changes their mind and swipes back up, we want the
                 // keyboard to remain open. However an onCancel signal is sent to the listeners
                 // (success = false), so we need to check for that.
-                if (config.userControlled && success) {
+                if (config.userControlled && success && mShouldControlKeyboard) {
                     mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
                 }
             });
@@ -317,6 +320,8 @@
         mAppsViewAlpha.setUpdateVisibility(true);
         mAppsViewTranslationY = new MultiPropertyFactory<>(
                 mAppsView, VIEW_TRANSLATE_Y, APPS_VIEW_INDEX_COUNT, Float::sum);
+
+        mShouldControlKeyboard = !mLauncher.getSearchConfig().isKeyboardSyncEnabled();
     }
 
     /**
@@ -333,7 +338,9 @@
     private void onProgressAnimationEnd() {
         if (Float.compare(mProgress, 1f) == 0) {
             mAppsView.reset(false /* animate */);
-            mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
+            if (mShouldControlKeyboard) {
+                mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
+            }
         }
     }
 }
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
index 9c3dab4..495f5c3 100644
--- a/src/com/android/launcher3/allapps/SearchTransitionController.java
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -32,6 +32,7 @@
 import android.util.FloatProperty;
 import android.util.Log;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.BubbleTextView;
@@ -39,6 +40,7 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.ItemInfo;
 
 /** Coordinates the transition between Search and A-Z in All Apps. */
@@ -225,16 +227,35 @@
 
                 numSearchResultsAnimated++;
             }
-            searchResultView.setAlpha(contentAlpha);
-            // Apply background alpha to decorator if possible.
-            if (adapterPosition != NO_POSITION) {
-                searchRecyclerView.getApps().getAdapterItems()
-                        .get(adapterPosition).setDecorationFillAlpha((int) (255 * backgroundAlpha));
-            }
-            // Apply background alpha to view's background (e.g. for Search Edu card).
+
             Drawable background = searchResultView.getBackground();
-            if (background != null) {
+            if (background != null
+                    && searchResultView instanceof ViewGroup
+                    && FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get()) {
+                searchResultView.setAlpha(1f);
+
+                // Apply content alpha to each child, since the view needs to be fully opaque for
+                // the background to show properly.
+                ViewGroup searchResultViewGroup = (ViewGroup) searchResultView;
+                for (int j = 0; j < searchResultViewGroup.getChildCount(); j++) {
+                    searchResultViewGroup.getChildAt(j).setAlpha(contentAlpha);
+                }
+
+                // Apply background alpha to the background drawable directly.
                 background.setAlpha((int) (255 * backgroundAlpha));
+            } else {
+                searchResultView.setAlpha(contentAlpha);
+
+                // Apply background alpha to decorator if possible.
+                if (adapterPosition != NO_POSITION) {
+                    searchRecyclerView.getApps().getAdapterItems().get(adapterPosition)
+                            .setDecorationFillAlpha((int) (255 * backgroundAlpha));
+                }
+
+                // Apply background alpha to view's background (e.g. for Search Edu card).
+                if (background != null) {
+                    background.setAlpha((int) (255 * backgroundAlpha));
+                }
             }
 
             float scaleY = 1;
@@ -304,6 +325,13 @@
                 getSearchRecyclerView().getApps().getAdapterItems().get(adapterPosition)
                         .setDecorationFillAlpha(255);
             }
+            if (child instanceof ViewGroup
+                    && FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get()) {
+                ViewGroup childGroup = (ViewGroup) child;
+                for (int i = 0; i < childGroup.getChildCount(); i++) {
+                    childGroup.getChildAt(i).setAlpha(1f);
+                }
+            }
             if (child.getBackground() != null) {
                 child.getBackground().setAlpha(255);
             }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 4287779..9484fff 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -264,6 +264,10 @@
     public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = new DeviceFlag(
             "ENABLE_ONE_SEARCH_MOTION", true, "Enables animations in OneSearch.");
 
+    public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = new DeviceFlag(
+            "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", false,
+            "Enable option to replace decorator-based search result backgrounds with drawables");
+
     public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = new DeviceFlag(
             "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", true,
             "Enable option to show keyboard when going to all-apps");
@@ -323,6 +327,17 @@
     public static final BooleanFlag SHOW_DOT_PAGINATION = getDebugFlag(
             "SHOW_DOT_PAGINATION", false, "Enable showing dot pagination in workspace");
 
+    public static final BooleanFlag LARGE_SCREEN_WIDGET_PICKER = getDebugFlag(
+            "LARGE_SCREEN_WIDGET_PICKER", false, "Enable new widget picker that takes "
+                    + "advantage of large screen format");
+
+    public static final BooleanFlag ENABLE_NEW_GESTURE_NAV_TUTORIAL = getDebugFlag(
+            "ENABLE_NEW_GESTURE_NAV_TUTORIAL", false,
+            "Enable the redesigned gesture navigation tutorial");
+
+    public static final BooleanFlag ENABLE_TOAST_IMPRESSION_LOGGING = getDebugFlag(
+            "ENABLE_TOAST_IMPRESSION_LOGGING", false, "Enable toast impression logging");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 7e14912..99822da 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -292,7 +292,7 @@
         mFolderName.forceDisableSuggestions(true);
 
         mFooter = findViewById(R.id.folder_footer);
-        mFooterHeight = getResources().getDimensionPixelSize(R.dimen.folder_label_height);
+        mFooterHeight = dp.folderFooterHeightPx;
 
         if (Utilities.ATLEAST_R) {
             mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this);
@@ -1167,14 +1167,6 @@
         mContent.setFixedSize(contentWidth, contentHeight);
         mContent.measure(contentAreaWidthSpec, contentAreaHeightSpec);
 
-        if (mContent.getChildCount() > 0) {
-            int cellIconGap = (mContent.getPageAt(0).getCellWidth()
-                    - mActivityContext.getDeviceProfile().iconSizePx) / 2;
-            mFooter.setPadding(mContent.getPaddingLeft() + cellIconGap,
-                    mFooter.getPaddingTop(),
-                    mContent.getPaddingRight() + cellIconGap,
-                    mFooter.getPaddingBottom());
-        }
         mFooter.measure(contentAreaWidthSpec,
                 MeasureSpec.makeMeasureSpec(mFooterHeight, MeasureSpec.EXACTLY));
 
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index d5ef9df..4829c42 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -234,9 +234,9 @@
                 mFolder, startRect, endRect, finalRadius, !mIsOpening));
 
         // Create reveal animator for the folder content (capture the top 4 icons 2x2)
-        int width = mDeviceProfile.folderCellLayoutBorderSpacePx.x
+        int width = mDeviceProfile.folderCellLayoutBorderSpacePx
                 + mDeviceProfile.folderCellWidthPx * 2;
-        int height = mDeviceProfile.folderCellLayoutBorderSpacePx.y
+        int height = mDeviceProfile.folderCellLayoutBorderSpacePx
                 + mDeviceProfile.folderCellHeightPx * 2;
         int page = mIsOpening ? mContent.getCurrentPage() : mContent.getDestinationPage();
         int left = mContent.getPaddingLeft() + page * lp.width;
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 49d27b7..70956a3 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -18,6 +18,7 @@
 
 import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
 
+import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
 
 import android.content.Intent;
@@ -207,7 +208,11 @@
             PreferenceScreen screen = getPreferenceScreen();
             for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
                 Preference preference = screen.getPreference(i);
-                if (!initPreference(preference)) {
+                if (initPreference(preference)) {
+                    if (IS_STUDIO_BUILD && preference == mDeveloperOptionPref) {
+                        preference.setOrder(0);
+                    }
+                } else {
                     screen.removePreference(preference);
                 }
             }
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index af689dc..78e17d8 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -585,7 +585,6 @@
     @Override
     public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
             SplitBounds splitInfo, int desiredStagePosition) {
-        boolean isLandscape = dp.isLandscape;
         float topLeftTaskPercent = splitInfo.appsStackedVertically
                 ? splitInfo.topTaskPercent
                 : splitInfo.leftTaskPercent;
@@ -593,18 +592,24 @@
                 ? splitInfo.dividerHeightPercent
                 : splitInfo.dividerWidthPercent;
 
+        int deviceHeightWithoutTaskbar = dp.availableHeightPx - dp.taskbarSize;
+        float scale = (float) outRect.height() / deviceHeightWithoutTaskbar;
+        float topTaskHeight = dp.availableHeightPx * topLeftTaskPercent;
+        float scaledTopTaskHeight = topTaskHeight * scale;
+        float dividerHeight = dp.availableHeightPx * dividerBarPercent;
+        float scaledDividerHeight = dividerHeight * scale;
+
         if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
-            if (isLandscape) {
-                outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent);
+            if (splitInfo.appsStackedVertically) {
+                outRect.bottom = Math.round(outRect.top + scaledTopTaskHeight);
             } else {
-                outRect.bottom = outRect.top + Math.round(outRect.height() * topLeftTaskPercent);
+                outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent);
             }
         } else {
-            if (isLandscape) {
-                outRect.left += Math.round(outRect.width()
-                        * (topLeftTaskPercent + dividerBarPercent));
+            if (splitInfo.appsStackedVertically) {
+                outRect.top += Math.round(scaledTopTaskHeight + scaledDividerHeight);
             } else {
-                outRect.top += Math.round(outRect.height()
+                outRect.left += Math.round(outRect.width()
                         * (topLeftTaskPercent + dividerBarPercent));
             }
         }
@@ -617,7 +622,7 @@
         int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
         int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
         int dividerBar = Math.round(splitBoundsConfig.appsStackedVertically
-                ? splitBoundsConfig.dividerHeightPercent * totalThumbnailHeight
+                ? splitBoundsConfig.dividerHeightPercent * dp.availableHeightPx
                 : splitBoundsConfig.dividerWidthPercent * parentWidth);
         int primarySnapshotHeight;
         int primarySnapshotWidth;
@@ -641,12 +646,18 @@
             }
             secondarySnapshot.setTranslationY(spaceAboveSnapshot);
         } else {
+            int deviceHeightWithoutTaskbar = dp.availableHeightPx - dp.taskbarSize;
+            float scale = (float) totalThumbnailHeight / deviceHeightWithoutTaskbar;
+            float topTaskHeight = dp.availableHeightPx * taskPercent;
+            float finalDividerHeight = dividerBar * scale;
+            float scaledTopTaskHeight = topTaskHeight * scale;
             primarySnapshotWidth = parentWidth;
-            primarySnapshotHeight = Math.round(totalThumbnailHeight * taskPercent);
+            primarySnapshotHeight = Math.round(scaledTopTaskHeight);
 
             secondarySnapshotWidth = parentWidth;
-            secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar;
-            int translationY = primarySnapshotHeight + spaceAboveSnapshot + dividerBar;
+            secondarySnapshotHeight = Math.round(totalThumbnailHeight - primarySnapshotHeight
+                    - finalDividerHeight);
+            float translationY = primarySnapshotHeight + spaceAboveSnapshot + finalDividerHeight;
             secondarySnapshot.setTranslationY(translationY);
 
             FrameLayout.LayoutParams primaryParams =
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
index 2c1cbdf..fe30fdc 100644
--- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -16,11 +16,12 @@
 package com.android.launcher3
 
 import android.content.Context
+import android.graphics.Point
 import android.graphics.PointF
 import android.graphics.Rect
 import android.util.SparseArray
 import androidx.test.core.app.ApplicationProvider
-import com.android.launcher3.DeviceProfile.DEFAULT_PROVIDER;
+import com.android.launcher3.DeviceProfile.DEFAULT_PROVIDER
 import com.android.launcher3.util.DisplayController.Info
 import com.android.launcher3.util.WindowBounds
 import org.junit.Before
@@ -116,10 +117,7 @@
 
             numFolderRows = 3
             numFolderColumns = 3
-            folderBorderSpaces = PointF(16f, 16f)
-            folderTopPadding = 24f
-            folderCellSize = PointF(80f, 94f)
-
+            folderStyle = R.style.FolderDefaultStyle
 
             inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_4_5
 
@@ -207,9 +205,7 @@
 
             numFolderRows = 3
             numFolderColumns = 3
-            folderBorderSpaces = PointF(16f, 16f)
-            folderTopPadding = 24f
-            folderCellSize = PointF(120f, 104f)
+            folderStyle = R.style.FolderDefaultStyle
 
             inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5
 
@@ -241,7 +237,7 @@
             numAllAppsColumns = 6
 
             isScalable = true
-            devicePaddingId = 2132148242 // "@xml/paddings_6x5"
+            devicePaddingId = R.xml.paddings_6x5
 
             inlineQsb = booleanArrayOf(
                     false,
@@ -305,9 +301,7 @@
 
             numFolderRows = 3
             numFolderColumns = 3
-            folderBorderSpaces = PointF(16f, 16f)
-            folderTopPadding = 24f
-            folderCellSize = PointF(80f, 94f)
+            folderStyle = R.style.FolderDefaultStyle
 
             inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_4_4
 
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 978e84c..50e0990 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -335,6 +335,7 @@
     @IwTest(focusArea="launcher")
     @Test
     @PortraitLandscape
+    @ScreenRecord // b/256898879
     public void testDragAppIcon() throws Throwable {
         // 1. Open all apps and wait for load complete.
         // 2. Drag icon to homescreen.
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 6bbdf48..b0cf20f 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.tapl.LauncherInstrumentation.DEFAULT_POLL_INTERVAL;
+import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
+
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -37,6 +40,9 @@
  * Operations on AllApps opened from Home. Also a parent for All Apps opened from Overview.
  */
 public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
+    // Defer updates flag used to defer all apps updates by a test's request.
+    private static final int DEFER_UPDATES_TEST = 1 << 1;
+
     private static final int MAX_SCROLL_ATTEMPTS = 40;
 
     private final int mHeight;
@@ -292,12 +298,16 @@
      */
     public void unfreeze() {
         mLauncher.getTestInfo(TestProtocol.REQUEST_UNFREEZE_APP_LIST);
-        verifyNotFrozen("All apps freeze flags upon unfreezing");
     }
 
     private void verifyNotFrozen(String message) {
+        mLauncher.assertEquals(message, 0, getFreezeFlags() & DEFER_UPDATES_TEST);
+        mLauncher.assertTrue(message, mLauncher.waitAndGet(() -> getFreezeFlags() == 0,
+                WAIT_TIME_MS, DEFAULT_POLL_INTERVAL));
+    }
+
+    private int getFreezeFlags() {
         final Bundle testInfo = mLauncher.getTestInfo(TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS);
-        if (testInfo == null) return;
-        mLauncher.assertEquals(message, 0, testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD));
+        return testInfo == null ? 0 : testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 15705e7..5a96d95 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -58,7 +58,7 @@
                      "want to switch from background to overview")) {
             verifyActiveContainer();
             goToOverviewUnchecked();
-            return mLauncher.isFallbackOverview()
+            return mLauncher.is3PLauncher()
                     ? new BaseOverview(mLauncher) : new Overview(mLauncher);
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 4b02ecc..f23a38c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -55,7 +55,7 @@
     public Taskbar getTaskbar() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to get the taskbar")) {
-            mLauncher.waitForLauncherObject(TASKBAR_RES_ID);
+            mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
 
             return new Taskbar(mLauncher);
         }
@@ -67,7 +67,7 @@
     public void assertTaskbarHidden() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "waiting for taskbar to be hidden")) {
-            mLauncher.waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+            mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
         }
     }
 
@@ -77,7 +77,7 @@
     public void assertTaskbarVisible() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "waiting for taskbar to be visible")) {
-            mLauncher.waitForLauncherObject(TASKBAR_RES_ID);
+            mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
         }
     }
 
@@ -92,7 +92,7 @@
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                      "want to show the taskbar")) {
-            mLauncher.waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+            mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
 
             final long downTime = SystemClock.uptimeMillis();
             final int unstashTargetY = mLauncher.getRealDisplaySize().y
@@ -106,7 +106,7 @@
             LauncherInstrumentation.log("showTaskbar: sent down");
 
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
-                mLauncher.waitForLauncherObject(TASKBAR_RES_ID);
+                mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
                 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
                         LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 1c5c5fa..1212c3d 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -83,6 +83,7 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.TimeoutException;
+import java.util.function.BooleanSupplier;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -181,6 +182,7 @@
     static final String TASKBAR_RES_ID = "taskbar_view";
     private static final String SPLIT_PLACEHOLDER_RES_ID = "split_placeholder";
     public static final int WAIT_TIME_MS = 30000;
+    static final long DEFAULT_POLL_INTERVAL = 1000;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
     private static final String ANDROID_PACKAGE = "android";
 
@@ -559,7 +561,7 @@
     private String getVisibleStateMessage() {
         if (hasLauncherObject(CONTEXT_MENU_RES_ID)) return "Context Menu";
         if (hasLauncherObject(WIDGETS_RES_ID)) return "Widgets";
-        if (hasLauncherObject(OVERVIEW_RES_ID)) return "Overview";
+        if (hasSystemLauncherObject(OVERVIEW_RES_ID)) return "Overview";
         if (hasLauncherObject(WORKSPACE_RES_ID)) return "Workspace";
         if (hasLauncherObject(APPS_RES_ID)) return "AllApps";
         return "LaunchedApp (" + getVisiblePackages() + ")";
@@ -771,73 +773,89 @@
             switch (containerType) {
                 case WORKSPACE: {
                     waitUntilLauncherObjectGone(APPS_RES_ID);
-                    waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
-                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
-                    waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+                    waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+                    waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+
+                    if (is3PLauncher() && isTablet()) {
+                        waitForSystemLauncherObject(TASKBAR_RES_ID);
+                    } else {
+                        waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+                    }
 
                     return waitForLauncherObject(WORKSPACE_RES_ID);
                 }
                 case WIDGETS: {
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(APPS_RES_ID);
-                    waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
-                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
-                    waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+                    waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+                    waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+
+                    if (is3PLauncher() && isTablet()) {
+                        waitForSystemLauncherObject(TASKBAR_RES_ID);
+                    } else {
+                        waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+                    }
 
                     return waitForLauncherObject(WIDGETS_RES_ID);
                 }
-                case TASKBAR_ALL_APPS:
-                case HOME_ALL_APPS: {
+                case TASKBAR_ALL_APPS: {
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
-                    waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
-                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
-                    waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+                    waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+                    waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+                    waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
 
                     return waitForLauncherObject(APPS_RES_ID);
                 }
-                case OVERVIEW: {
+                case HOME_ALL_APPS: {
+                    waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+                    waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+                    waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+                    waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+
+                    if (is3PLauncher() && isTablet()) {
+                        waitForSystemLauncherObject(TASKBAR_RES_ID);
+                    } else {
+                        waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+                    }
+
+                    return waitForLauncherObject(APPS_RES_ID);
+                }
+                case OVERVIEW:
+                case FALLBACK_OVERVIEW: {
                     waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
-                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
-                    waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+                    waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
+                    waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
 
-                    return waitForLauncherObject(OVERVIEW_RES_ID);
+                    return waitForSystemLauncherObject(OVERVIEW_RES_ID);
                 }
                 case SPLIT_SCREEN_SELECT: {
                     waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
-                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+                    waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
 
-                    waitForLauncherObject(SPLIT_PLACEHOLDER_RES_ID);
-                    return waitForLauncherObject(OVERVIEW_RES_ID);
-                }
-                case FALLBACK_OVERVIEW: {
-                    waitUntilLauncherObjectGone(APPS_RES_ID);
-                    waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
-                    waitUntilLauncherObjectGone(WIDGETS_RES_ID);
-                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
-                    waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
-
-                    return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
+                    waitForSystemLauncherObject(SPLIT_PLACEHOLDER_RES_ID);
+                    return waitForSystemLauncherObject(OVERVIEW_RES_ID);
                 }
                 case LAUNCHED_APP: {
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(APPS_RES_ID);
-                    waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
-                    waitUntilLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
+                    waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
+                    waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
 
                     if (mIgnoreTaskbarVisibility) {
                         return null;
                     }
-                    if (isTablet() && !isFallbackOverview()) {
-                        waitForLauncherObject(TASKBAR_RES_ID);
+
+                    if (isTablet()) {
+                        waitForSystemLauncherObject(TASKBAR_RES_ID);
                     } else {
-                        waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+                        waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
                     }
                     return null;
                 }
@@ -972,14 +990,9 @@
                 checkForAnomaly(false, true);
 
                 final Point displaySize = getRealDisplaySize();
-                // The swipe up to home gesture starts from inside the launcher when the user is
-                // already home. Otherwise, the gesture can start inside the launcher process if the
-                // taskbar is visible.
-                boolean gestureStartFromLauncher = isTablet()
-                        ? !isLauncher3()
-                        || hasLauncherObject(WORKSPACE_RES_ID)
-                        || hasLauncherObject(TASKBAR_RES_ID)
-                        : isLauncherVisible();
+
+                boolean gestureStartFromLauncher =
+                        isTablet() ? !isLauncher3() : isLauncherVisible();
 
                 // CLose floating views before going back to home.
                 swipeUpToCloseFloatingView(gestureStartFromLauncher);
@@ -1012,7 +1025,7 @@
                         NORMAL_STATE_ORDINAL,
                         !hasLauncherObject(WORKSPACE_RES_ID)
                                 && (hasLauncherObject(APPS_RES_ID)
-                                || hasLauncherObject(OVERVIEW_RES_ID)),
+                                || hasSystemLauncherObject(OVERVIEW_RES_ID)),
                         action);
             }
             try (LauncherInstrumentation.Closable c1 = addContextLayer(
@@ -1068,7 +1081,8 @@
 
     boolean isLauncherContainerVisible() {
         final String[] containerResources = {WORKSPACE_RES_ID, OVERVIEW_RES_ID, APPS_RES_ID};
-        return Arrays.stream(containerResources).anyMatch(r -> hasLauncherObject(r));
+        return Arrays.stream(containerResources).anyMatch(
+                r -> r.equals(OVERVIEW_RES_ID) ? hasSystemLauncherObject(r) : hasLauncherObject(r));
     }
 
     /**
@@ -1151,6 +1165,14 @@
         waitUntilGoneBySelector(getOverviewObjectSelector(resId));
     }
 
+    void waitUntilSystemLauncherObjectGone(String resId) {
+        if (is3PLauncher()) {
+            waitUntilOverviewObjectGone(resId);
+        } else {
+            waitUntilLauncherObjectGone(resId);
+        }
+    }
+
     void waitUntilLauncherObjectGone(BySelector selector) {
         waitUntilGoneBySelector(makeLauncherSelector(selector));
     }
@@ -1281,6 +1303,11 @@
         return mDevice.hasObject(getLauncherObjectSelector(resId));
     }
 
+    private boolean hasSystemLauncherObject(String resId) {
+        return mDevice.hasObject(is3PLauncher() ? getOverviewObjectSelector(resId)
+                : getLauncherObjectSelector(resId));
+    }
+
     boolean hasLauncherObject(BySelector selector) {
         return mDevice.hasObject(makeLauncherSelector(selector));
     }
@@ -1300,6 +1327,12 @@
     }
 
     @NonNull
+    UiObject2 waitForSystemLauncherObject(String resName) {
+        return is3PLauncher() ? waitForOverviewObject(resName)
+                : waitForLauncherObject(resName);
+    }
+
+    @NonNull
     UiObject2 waitForLauncherObject(BySelector selector) {
         return waitForObjectBySelector(makeLauncherSelector(selector));
     }
@@ -1310,11 +1343,6 @@
     }
 
     @NonNull
-    UiObject2 waitForFallbackLauncherObject(String resName) {
-        return waitForObjectBySelector(getOverviewObjectSelector(resName));
-    }
-
-    @NonNull
     UiObject2 waitForAndroidObject(String resId) {
         final UiObject2 object = TestHelpers.wait(
                 Until.findObject(By.res(ANDROID_PACKAGE, resId)), WAIT_TIME_MS);
@@ -1351,7 +1379,7 @@
         return mDevice.getLauncherPackageName();
     }
 
-    boolean isFallbackOverview() {
+    boolean is3PLauncher() {
         return !getOverviewPackageName().equals(getLauncherPackageName());
     }
 
@@ -1991,4 +2019,21 @@
             mCallbackAtRunPoint.accept(runPoint);
         }
     }
+
+    /**
+     * Waits until a particular condition is true. Based on WaitMixin.
+     */
+    boolean waitAndGet(BooleanSupplier condition, long timeout, long interval) {
+        long startTime = SystemClock.uptimeMillis();
+
+        boolean result = condition.getAsBoolean();
+        for (long elapsedTime = 0; !result; elapsedTime = SystemClock.uptimeMillis() - startTime) {
+            if (elapsedTime >= timeout) {
+                break;
+            }
+            SystemClock.sleep(interval);
+            result = condition.getAsBoolean();
+        }
+        return result;
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index 0f9d5f5..6ca7f4b 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -52,7 +52,7 @@
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to get a taskbar icon")) {
             return new TaskbarAppIcon(mLauncher, mLauncher.waitForObjectInContainer(
-                    mLauncher.waitForLauncherObject(TASKBAR_RES_ID),
+                    mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
                     AppIcon.getAppIconSelector(appName, mLauncher)));
         }
     }
@@ -68,7 +68,7 @@
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to hide the taskbar");
              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-            mLauncher.waitForLauncherObject(TASKBAR_RES_ID);
+            mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
 
             final long downTime = SystemClock.uptimeMillis();
             Point stashTarget = new Point(
@@ -79,7 +79,7 @@
             LauncherInstrumentation.log("hideTaskbar: sent down");
 
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
-                mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+                mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
                 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget,
                         LauncherInstrumentation.GestureScope.INSIDE);
             }
@@ -97,7 +97,8 @@
              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
 
             mLauncher.clickLauncherObject(mLauncher.waitForObjectInContainer(
-                    mLauncher.waitForLauncherObject(TASKBAR_RES_ID), getAllAppsButtonSelector()));
+                    mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
+                    getAllAppsButtonSelector()));
 
             return new AllAppsFromTaskbar(mLauncher);
         }
@@ -108,7 +109,7 @@
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to get all taskbar icons")) {
             return mLauncher.waitForObjectsInContainer(
-                    mLauncher.waitForLauncherObject(TASKBAR_RES_ID),
+                    mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
                     AppIcon.getAnyAppIconSelector())
                     .stream()
                     .map(UiObject2::getText)