Merge "Revert "Add unit tests for LoaderTask"" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index aca1b3b..b4132e4 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -13,3 +13,17 @@
     description: "Enables two line label inside all apps."
     bug: "270390937"
 }
+
+flag {
+    name: "enable_grid_only_overview"
+    namespace: "launcher"
+    description: "Enable a grid-only overview without a focused task."
+    bug: "257950105"
+}
+
+flag {
+    name: "enable_cursor_hover_states"
+    namespace: "launcher"
+    description: "Enables cursor hover states for certain elements."
+    bug: "243191650"
+}
diff --git a/quickstep/res/layout/keyboard_quick_switch_view.xml b/quickstep/res/layout/keyboard_quick_switch_view.xml
index 16abdee..5af8d51 100644
--- a/quickstep/res/layout/keyboard_quick_switch_view.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_view.xml
@@ -17,6 +17,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyboard_quick_switch_view"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginTop="@dimen/keyboard_quick_switch_margin_top"
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 6c84e6f..b7cdd15 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -116,7 +116,7 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"ಹಿಂದೆ"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME ಪರಿವರ್ತಕ"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"ಇತ್ತೀಚಿನವು"</string>
-    <string name="taskbar_button_notifications" msgid="7471740351507357318">"ಅಧಿಸೂಚನೆಗಳು"</string>
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"ನೋಟಿಫಿಕೇಶನ್‌ಗಳು"</string>
     <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳು"</string>
     <string name="taskbar_a11y_title" msgid="6432169809852243110">"ಟಾಸ್ಕ್‌ಬಾರ್"</string>
     <string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ಟಾಸ್ಕ್‌ಬಾರ್ ತೋರಿಸಲಾಗಿದೆ"</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index b024418..aeb453c 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -306,7 +306,7 @@
     <dimen name="taskbar_home_button_left_margin_kids">48dp</dimen>
     <dimen name="taskbar_icon_size_kids">32dp</dimen>
     <dimen name="taskbar_all_apps_button_translation_x_offset">6dp</dimen>
-
+    <dimen name="taskbar_all_apps_search_button_translation_x_offset">4.5dp</dimen>
 
     <!-- Transient taskbar -->
     <dimen name="transient_taskbar_padding">12dp</dimen>
@@ -317,6 +317,8 @@
     <dimen name="transient_taskbar_stashed_height">32dp</dimen>
     <dimen name="transient_taskbar_all_apps_button_translation_x_offset">4dp</dimen>
     <dimen name="transient_taskbar_stash_spring_velocity_dp_per_s">400dp</dimen>
+    <dimen name="taskbar_tooltip_vertical_padding">8dp</dimen>
+    <dimen name="taskbar_tooltip_horizontal_padding">16dp</dimen>
 
     <!-- An additional touch slop to prevent x-axis movement during the swipe up to show taskbar -->
     <dimen name="transient_taskbar_clamped_offset_bound">16dp</dimen>
@@ -368,6 +370,7 @@
     <dimen name="bubblebar_stashed_handle_height">@dimen/taskbar_stashed_handle_height</dimen>
     <dimen name="bubblebar_pointer_size">8dp</dimen>
     <dimen name="bubblebar_elevation">1dp</dimen>
+    <dimen name="bubblebar_hotseat_adjustment_threshold">90dp</dimen>
 
     <dimen name="bubblebar_icon_size">50dp</dimen>
     <dimen name="bubblebar_badge_size">24dp</dimen>
diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml
index 860abc1..df32626 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -33,4 +33,6 @@
 
   <string name="taskbar_model_callbacks_factory_class" translatable="false">com.android.launcher3.taskbar.TaskbarModelCallbacksFactory</string>
 
+  <string name="assist_state_manager_class" translatable="false"></string>
+
 </resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index f8ea932..841fc8f 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -22,6 +22,8 @@
 import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OPEN;
@@ -151,6 +153,7 @@
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DelegateLaunchAnimatorController;
+import com.android.systemui.animation.LaunchableView;
 import com.android.systemui.animation.RemoteAnimationDelegate;
 import com.android.systemui.shared.system.BlurUtils;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
@@ -239,6 +242,8 @@
     private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
     private RemoteTransition mLauncherOpenTransition;
 
+    private final RemoteAnimationCoordinateTransfer mCoordinateTransfer;
+
     private LauncherBackAnimationController mBackAnimationController;
     private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
         @Override
@@ -287,6 +292,7 @@
         mOpeningXInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.app_open_x);
         mOpeningInterpolator = AnimationUtils.loadInterpolator(context,
                 R.interpolator.emphasized_interpolator);
+        mCoordinateTransfer = new RemoteAnimationCoordinateTransfer(mLauncher);
     }
 
     @Override
@@ -1388,8 +1394,9 @@
                         targetRect));
 
         // Hook up floating views to the closing window animators.
-        final int rotationChange = getRotationChange(targets);
-        Rect windowTargetBounds = getWindowTargetBounds(targets, rotationChange);
+        // note the coordinate of closingWindowStartRect is based on launcher
+        Rect windowTargetBounds = new Rect();
+        closingWindowStartRect.round(windowTargetBounds);
         if (floatingIconView != null) {
             anim.addAnimatorListener(floatingIconView);
             floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
@@ -1693,8 +1700,18 @@
 
             RectF windowTargetBounds =
                     new RectF(getWindowTargetBounds(appTargets, getRotationChange(appTargets)));
+
+            final RectF resolveRectF = new RectF(windowTargetBounds);
+            for (RemoteAnimationTarget t : appTargets) {
+                if (t.mode == MODE_CLOSING) {
+                    transferRectToTargetCoordinate(
+                            t, windowTargetBounds, true, resolveRectF);
+                    break;
+                }
+            }
+
             Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
-                    appTargets, wallpaperTargets, mFromUnlock, windowTargetBounds,
+                    appTargets, wallpaperTargets, mFromUnlock, resolveRectF,
                     QuickStepContract.getWindowCornerRadius(mLauncher),
                     false /* fromPredictiveBack */);
 
@@ -1777,7 +1794,7 @@
         @Nullable
         private static ContainerAnimationRunner from(View v, Launcher launcher,
                 StartingWindowListener startingWindowListener, RunnableList onEndCallback) {
-            View viewToUse = findViewWithBackground(v);
+            View viewToUse = findLaunchableViewWithBackground(v);
             if (viewToUse == null) {
                 viewToUse = v;
             }
@@ -1820,11 +1837,15 @@
                     new ActivityLaunchAnimator.AnimationDelegate(controller, callback, listener));
         }
 
-        /** Finds the closest parent of [view] (inclusive) with a background drawable. */
+        /**
+         * Finds the closest parent of [view] (inclusive) that implements {@link LaunchableView} and
+         * has a background drawable.
+         */
         @Nullable
-        private static View findViewWithBackground(View view) {
+        private static <T extends View & LaunchableView> T findLaunchableViewWithBackground(
+                View view) {
             View current = view;
-            while (current.getBackground() == null) {
+            while (current.getBackground() == null || !(current instanceof LaunchableView)) {
                 if (!(current.getParent() instanceof View)) {
                     return null;
                 }
@@ -1832,7 +1853,7 @@
                 current = (View) view.getParent();
             }
 
-            return current;
+            return (T) current;
         }
 
         @Override
@@ -1936,6 +1957,52 @@
     }
 
     /**
+     * Transfer the rectangle to another coordinate if needed.
+     * @param toLauncher which one is the anchor of this transfer, if true then transfer from
+     *                   animation target to launcher, false transfer from launcher to animation
+     *                   target.
+     */
+    public void transferRectToTargetCoordinate(RemoteAnimationTarget target, RectF currentRect,
+            boolean toLauncher, RectF resultRect) {
+        mCoordinateTransfer.transferRectToTargetCoordinate(
+                target, currentRect, toLauncher, resultRect);
+    }
+
+    private static class RemoteAnimationCoordinateTransfer {
+        private final QuickstepLauncher mLauncher;
+        private final Rect mDisplayRect = new Rect();
+        private final Rect mTmpResult = new Rect();
+
+        RemoteAnimationCoordinateTransfer(QuickstepLauncher launcher) {
+            mLauncher = launcher;
+        }
+
+        void transferRectToTargetCoordinate(RemoteAnimationTarget target, RectF currentRect,
+                boolean toLauncher, RectF resultRect) {
+            final int taskRotation = target.windowConfiguration.getRotation();
+            final DeviceProfile profile = mLauncher.getDeviceProfile();
+
+            final int rotationDelta = toLauncher
+                    ? android.util.RotationUtils.deltaRotation(taskRotation, profile.rotationHint)
+                    : android.util.RotationUtils.deltaRotation(profile.rotationHint, taskRotation);
+            if (rotationDelta != ROTATION_0) {
+                // Get original display size when task is on top but with different rotation
+                if (rotationDelta % 2 != 0 && toLauncher && (profile.rotationHint == ROTATION_0
+                        || profile.rotationHint == ROTATION_180)) {
+                    mDisplayRect.set(0, 0, profile.heightPx, profile.widthPx);
+                } else {
+                    mDisplayRect.set(0, 0, profile.widthPx, profile.heightPx);
+                }
+                currentRect.round(mTmpResult);
+                android.util.RotationUtils.rotateBounds(mTmpResult, mDisplayRect, rotationDelta);
+                resultRect.set(mTmpResult);
+            } else {
+                resultRect.set(currentRect);
+            }
+        }
+    }
+
+    /**
      * RectFSpringAnim update listener to be used for app to home animation.
      */
     private class SpringAnimRunner implements RectFSpringAnim.OnUpdateListener {
@@ -1957,6 +2024,20 @@
             mEndRadius = Math.max(1, targetRect.width()) / 2f;
             mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer);
             mWindowTargetBounds.set(windowTargetBounds);
+
+            // transfer the coordinate based on animation target.
+            if (mAppTargets != null) {
+                for (RemoteAnimationTarget t : mAppTargets) {
+                    if (t.mode == MODE_CLOSING) {
+                        final RectF targetBounds = new RectF(mWindowTargetBounds);
+                        final RectF result = new RectF();
+                        transferRectToTargetCoordinate(
+                                t, targetBounds, false, result);
+                        result.round(mWindowTargetBounds);
+                        break;
+                    }
+                }
+            }
         }
 
         public float getCornerRadius(float progress) {
@@ -1977,6 +2058,8 @@
                 }
 
                 if (target.mode == MODE_CLOSING) {
+                    final RectF before = new RectF(currentRectF);
+                    transferRectToTargetCoordinate(target, currentRectF, false, currentRectF);
                     currentRectF.round(mCurrentRect);
 
                     // Scale the target window to match the currentRectF.
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 4f889c0..f82474a 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.appprediction;
 
-import static com.android.launcher3.BubbleTextView.DISPLAY_PREDICTION_ROW;
-
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -189,7 +187,7 @@
             LayoutInflater inflater = mActivityContext.getAppsView().getLayoutInflater();
             while (getChildCount() < mNumPredictedAppsPerRow) {
                 BubbleTextView icon = (BubbleTextView) inflater.inflate(
-                        R.layout.all_apps_icon, this, false);
+                        R.layout.all_apps_prediction_row_icon, this, false);
                 icon.setOnClickListener(mActivityContext.getItemOnClickListener());
                 icon.setOnLongClickListener(mActivityContext.getAllAppsItemLongClickListener());
                 icon.setLongPressTimeoutFactor(1f);
@@ -211,7 +209,6 @@
             icon.reset();
             if (predictionCount > i) {
                 icon.setVisibility(View.VISIBLE);
-                icon.setDisplay(DISPLAY_PREDICTION_ROW);
                 icon.applyFromWorkspaceItem(mPredictedApps.get(i));
             } else {
                 icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 87a9ecb..a63f9e8 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -61,9 +62,11 @@
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.views.Snackbar;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.StringJoiner;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -185,7 +188,7 @@
     }
 
     private void fillGapsWithPrediction(boolean animate) {
-        Log.d(TAG, "fillGapsWithPrediction");
+        Log.d(TAG, "fillGapsWithPrediction flags: " + getStateString(mPauseFlags));
         if (mPauseFlags != 0) {
             return;
         }
@@ -291,6 +294,7 @@
      * start and pauses predicted apps update on the hotseat
      */
     public void setPauseUIUpdate(boolean paused) {
+        Log.d(TAG, "setPauseUIUpdate parameter `paused` is " + paused);
         mPauseFlags = paused
                 ? (mPauseFlags | FLAG_UPDATE_PAUSED)
                 : (mPauseFlags & ~FLAG_UPDATE_PAUSED);
@@ -515,4 +519,21 @@
                 && ((WorkspaceItemInfo) view.getTag()).container
                 == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
     }
+
+    private static String getStateString(int flags) {
+        StringJoiner str = new StringJoiner("|");
+        appendFlag(str, flags, FLAG_UPDATE_PAUSED, "FLAG_UPDATE_PAUSED");
+        appendFlag(str, flags, FLAG_DRAG_IN_PROGRESS, "FLAG_DRAG_IN_PROGRESS");
+        appendFlag(str, flags, FLAG_FILL_IN_PROGRESS, "FLAG_FILL_IN_PROGRESS");
+        appendFlag(str, flags, FLAG_REMOVING_PREDICTED_ICON,
+                "FLAG_REMOVING_PREDICTED_ICON");
+        return str.toString();
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + this.getClass().getSimpleName());
+        writer.println(prefix + "\tFlags: " + getStateString(mPauseFlags));
+        writer.println(prefix + "\tmHotSeatItemsCount: " + mHotSeatItemsCount);
+        writer.println(prefix + "\tmPredictedItems: " + mPredictedItems);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index d7a4f76..42e6809 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -160,7 +160,9 @@
             if (mInOverviewState) {
                 setLauncherViewsVisibility(View.VISIBLE);
                 markLauncherResumed();
-            } else if (mFreeformTasksVisible) {
+            } else if (mFreeformTasksVisible && !mGestureInProgress) {
+                // Switching out of overview state and gesture finished.
+                // If freeform tasks are still visible, hide launcher again.
                 setLauncherViewsVisibility(View.INVISIBLE);
                 markLauncherPaused();
             }
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 4e9e301..42c423c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -46,6 +46,8 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.quickstep.util.GroupTask;
 
 import java.util.HashMap;
@@ -360,11 +362,8 @@
                                         OPEN_OUTLINE_INTERPOLATOR));
                     }
                 });
-                if (currentFocusIndexOverride == -1) {
-                    initializeScroll(/* index= */ 0, /* shouldTruncateTarget= */ false);
-                } else {
-                    animateFocusMove(-1, currentFocusIndexOverride);
-                }
+                animateFocusMove(-1, currentFocusIndexOverride == -1
+                        ? Math.min(mContent.getChildCount(), 1) : currentFocusIndexOverride);
                 displayedContent.setVisibility(VISIBLE);
                 setVisibility(VISIBLE);
                 requestFocus();
@@ -413,7 +412,8 @@
                     // there are more tasks
                     initializeScroll(
                             firstVisibleTaskIndex,
-                            /* shouldTruncateTarget= */ firstVisibleTaskIndex != toIndex);
+                            /* shouldTruncateTarget= */ firstVisibleTaskIndex != 0
+                                    && firstVisibleTaskIndex != toIndex);
                 } else if (toIndex > fromIndex || toIndex == 0) {
                     // Scrolling to next task view
                     if (mIsRtl) {
@@ -439,6 +439,13 @@
     }
 
     @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        TestLogging.recordKeyEvent(
+                TestProtocol.SEQUENCE_MAIN, "KeyboardQuickSwitchView key event", event);
+        return super.dispatchKeyEvent(event);
+    }
+
+    @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         return (mViewCallbacks != null
                 && mViewCallbacks.onKeyUp(keyCode, event, mIsRtl, mDisplayingRecentTasks))
@@ -454,56 +461,80 @@
             return;
         }
         if (mIsRtl) {
-            scrollRightTo(
-                    task, shouldTruncateTarget, /* smoothScroll= */ false);
-        } else {
             scrollLeftTo(
-                    task, shouldTruncateTarget, /* smoothScroll= */ false);
+                    task,
+                    shouldTruncateTarget,
+                    /* smoothScroll= */ false,
+                    /* waitForLayout= */ true);
+        } else {
+            scrollRightTo(
+                    task,
+                    shouldTruncateTarget,
+                    /* smoothScroll= */ false,
+                    /* waitForLayout= */ true);
         }
     }
 
     private void scrollRightTo(@NonNull View targetTask) {
-        scrollRightTo(targetTask, /* shouldTruncateTarget= */ false, /* smoothScroll= */ true);
+        scrollRightTo(
+                targetTask,
+                /* shouldTruncateTarget= */ false,
+                /* smoothScroll= */ true,
+                /* waitForLayout= */ false);
     }
 
     private void scrollRightTo(
-            @NonNull View targetTask, boolean shouldTruncateTarget, boolean smoothScroll) {
+            @NonNull View targetTask,
+            boolean shouldTruncateTarget,
+            boolean smoothScroll,
+            boolean waitForLayout) {
         if (!mDisplayingRecentTasks) {
             return;
         }
         if (smoothScroll && !shouldScroll(targetTask, shouldTruncateTarget)) {
             return;
         }
-        int scrollTo = targetTask.getLeft() - mSpacing
-                + (shouldTruncateTarget ? targetTask.getWidth() / 2 : 0);
-        // Scroll so that the focused task is to the left of the list
-        if (smoothScroll) {
-            mScrollView.smoothScrollTo(scrollTo, 0);
-        } else {
-            mScrollView.scrollTo(scrollTo, 0);
-        }
+        runScrollCommand(waitForLayout, () -> {
+            int scrollTo = targetTask.getLeft() - mSpacing
+                    + (shouldTruncateTarget ? targetTask.getWidth() / 2 : 0);
+            // Scroll so that the focused task is to the left of the list
+            if (smoothScroll) {
+                mScrollView.smoothScrollTo(scrollTo, 0);
+            } else {
+                mScrollView.scrollTo(scrollTo, 0);
+            }
+        });
     }
 
     private void scrollLeftTo(@NonNull View targetTask) {
-        scrollLeftTo(targetTask, /* shouldTruncateTarget= */ false, /* smoothScroll= */ true);
+        scrollLeftTo(
+                targetTask,
+                /* shouldTruncateTarget= */ false,
+                /* smoothScroll= */ true,
+                /* waitForLayout= */ false);
     }
 
     private void scrollLeftTo(
-            @NonNull View targetTask, boolean shouldTruncateTarget, boolean smoothScroll) {
+            @NonNull View targetTask,
+            boolean shouldTruncateTarget,
+            boolean smoothScroll,
+            boolean waitForLayout) {
         if (!mDisplayingRecentTasks) {
             return;
         }
         if (smoothScroll && !shouldScroll(targetTask, shouldTruncateTarget)) {
             return;
         }
-        int scrollTo = targetTask.getRight() + mSpacing - mScrollView.getWidth()
-                - (shouldTruncateTarget ? targetTask.getWidth() / 2 : 0);
-        // Scroll so that the focused task is to the right of the list
-        if (smoothScroll) {
-            mScrollView.smoothScrollTo(scrollTo, 0);
-        } else {
-            mScrollView.scrollTo(scrollTo, 0);
-        }
+        runScrollCommand(waitForLayout, () -> {
+            int scrollTo = targetTask.getRight() + mSpacing - mScrollView.getWidth()
+                    - (shouldTruncateTarget ? targetTask.getWidth() / 2 : 0);
+            // Scroll so that the focused task is to the right of the list
+            if (smoothScroll) {
+                mScrollView.smoothScrollTo(scrollTo, 0);
+            } else {
+                mScrollView.scrollTo(scrollTo, 0);
+            }
+        });
     }
 
     private boolean shouldScroll(@NonNull View targetTask, boolean shouldTruncateTarget) {
@@ -514,6 +545,21 @@
         return isTargetTruncated && !shouldTruncateTarget;
     }
 
+    private void runScrollCommand(boolean waitForLayout, @NonNull Runnable scrollCommand) {
+        if (!waitForLayout) {
+            scrollCommand.run();
+            return;
+        }
+        mScrollView.getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        scrollCommand.run();
+                        mScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                    }
+                });
+    }
+
     @Nullable
     protected KeyboardQuickSwitchTaskView getTaskAt(int index) {
         return !mDisplayingRecentTasks || index < 0 || index >= mContent.getChildCount()
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index a293f74..cbb991d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -24,7 +24,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
 import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
 import com.android.quickstep.SystemUiProxy;
@@ -92,27 +92,19 @@
 
     protected void closeQuickSwitchView(boolean animate) {
         if (mCloseAnimation != null) {
-            if (animate) {
-                // Let currently-running animation finish.
-                return;
-            } else {
-                mCloseAnimation.cancel();
+            // Let currently-running animation finish.
+            if (!animate) {
+                mCloseAnimation.end();
             }
+            return;
         }
         if (!animate) {
-            mCloseAnimation = null;
             onCloseComplete();
             return;
         }
         mCloseAnimation = mKeyboardQuickSwitchView.getCloseAnimation();
 
-        mCloseAnimation.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                mCloseAnimation = null;
-                onCloseComplete();
-            }
-        });
+        mCloseAnimation.addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
         mCloseAnimation.start();
     }
 
@@ -160,6 +152,7 @@
     }
 
     private void onCloseComplete() {
+        mCloseAnimation = null;
         mOverlayContext.getDragLayer().removeView(mKeyboardQuickSwitchView);
         mControllerCallbacks.onCloseComplete();
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 4e834ec..e6dfe0f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -41,6 +41,7 @@
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.taskbar.bubbles.BubbleBarController;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.MultiPropertyFactory;
@@ -202,10 +203,18 @@
         return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
     }
 
+    @Override
     public void refreshResumedState() {
         onLauncherResumedOrPaused(mLauncher.hasBeenResumed());
     }
 
+    @Override
+    public void adjustHotseatForBubbleBar(boolean isBubbleBarVisible) {
+        if (mLauncher.getHotseat() != null) {
+            mLauncher.getHotseat().adjustForBubbleBar(isBubbleBarVisible);
+        }
+    }
+
     /**
      * Create Taskbar animation when going from an app to Launcher as part of recents transition.
      * @param toState If known, the state we will end up in when reaching Launcher.
@@ -327,6 +336,21 @@
         return mTaskbarInAppDisplayProgress.value > 0;
     }
 
+    public boolean isBubbleBarEnabled() {
+        return BubbleBarController.BUBBLE_BAR_ENABLED;
+    }
+
+    /** Whether the bubble bar has any bubbles. */
+    public boolean hasBubbles() {
+        if (mControllers == null) {
+            return false;
+        }
+        if (mControllers.bubbleControllers.isEmpty()) {
+            return false;
+        }
+        return mControllers.bubbleControllers.get().bubbleBarViewController.hasBubbles();
+    }
+
     @Override
     public void onExpandPip() {
         super.onExpandPip();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 0b83a88..0aa02f2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -27,11 +27,13 @@
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY;
 import static com.android.launcher3.Utilities.isRunningInTestHarness;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NO_RECREATION;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
 import static com.android.launcher3.taskbar.TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW;
 import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
+import static com.android.launcher3.util.VibratorWrapper.EFFECT_CLICK;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
 
@@ -57,6 +59,7 @@
 import android.view.RoundedCorner;
 import android.view.Surface;
 import android.view.View;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.Toast;
@@ -108,6 +111,7 @@
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.views.RecentsView;
@@ -225,20 +229,23 @@
         }
 
         // Construct controllers.
+        RotationButtonController rotationButtonController = new RotationButtonController(this,
+                c.getColor(R.color.floating_rotation_button_light_color),
+                c.getColor(R.color.floating_rotation_button_dark_color),
+                R.drawable.ic_sysbar_rotate_button_ccw_start_0,
+                R.drawable.ic_sysbar_rotate_button_ccw_start_90,
+                R.drawable.ic_sysbar_rotate_button_cw_start_0,
+                R.drawable.ic_sysbar_rotate_button_cw_start_90,
+                () -> getDisplay().getRotation());
+        rotationButtonController.setBgExecutor(Executors.THREAD_POOL_EXECUTOR);
+
         mControllers = new TaskbarControllers(this,
                 new TaskbarDragController(this),
                 buttonController,
                 isDesktopMode
                         ? new DesktopNavbarButtonsViewController(this, navButtonsView)
                         : new NavbarButtonsViewController(this, navButtonsView),
-                new RotationButtonController(this,
-                        c.getColor(R.color.floating_rotation_button_light_color),
-                        c.getColor(R.color.floating_rotation_button_dark_color),
-                        R.drawable.ic_sysbar_rotate_button_ccw_start_0,
-                        R.drawable.ic_sysbar_rotate_button_ccw_start_90,
-                        R.drawable.ic_sysbar_rotate_button_cw_start_0,
-                        R.drawable.ic_sysbar_rotate_button_cw_start_90,
-                        () -> getDisplay().getRotation()),
+                rotationButtonController,
                 new TaskbarDragLayerController(this, mDragLayer),
                 new TaskbarViewController(this, taskbarView),
                 new TaskbarScrimViewController(this, taskbarScrimView),
@@ -298,6 +305,11 @@
         mNavMode = DisplayController.getNavigationMode(this);
     }
 
+    /** Called when the visibility of the bubble bar changed. */
+    public void bubbleBarVisibilityChanged(boolean isVisible) {
+        mControllers.uiController.adjustHotseatForBubbleBar(isVisible);
+        mControllers.taskbarViewController.resetIconAlignmentController();
+    }
 
     public void init(@NonNull TaskbarSharedState sharedState) {
         mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, getResources(), false);
@@ -323,11 +335,11 @@
             mIsDestroyed = false;
         }
 
-        if (!mAddedWindow) {
+        if (!ENABLE_TASKBAR_NO_RECREATION.get() && !mAddedWindow) {
             mWindowManager.addView(mDragLayer, mWindowLayoutParams);
             mAddedWindow = true;
         } else {
-            mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+            notifyUpdateLayoutParams();
         }
     }
 
@@ -343,6 +355,11 @@
         mControllers.taskbarAllAppsController.toggle();
     }
 
+    /** Toggles Taskbar All Apps overlay with keyboard ready for search. */
+    public void toggleAllAppsSearch() {
+        mControllers.taskbarAllAppsController.toggleSearch();
+    }
+
     @Override
     public DeviceProfile getDeviceProfile() {
         return mDeviceProfile;
@@ -674,7 +691,7 @@
         mIsDestroyed = true;
         setUIController(TaskbarUIController.DEFAULT);
         mControllers.onDestroy();
-        if (!FLAG_HIDE_NAVBAR_WINDOW) {
+        if (!ENABLE_TASKBAR_NO_RECREATION.get() && !FLAG_HIDE_NAVBAR_WINDOW) {
             mWindowManager.removeViewImmediate(mDragLayer);
             mAddedWindow = false;
         }
@@ -806,7 +823,7 @@
         }
         mWindowLayoutParams.height = height;
         mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
-        mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+        notifyUpdateLayoutParams();
     }
 
     /**
@@ -849,7 +866,22 @@
         } else {
             mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
         }
-        mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+        notifyUpdateLayoutParams();
+    }
+
+    /**
+     * Applies forcibly show flag to taskbar window iff transient taskbar is unstashed.
+     */
+    public void applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow) {
+        if (!DisplayController.isTransientTaskbar(this)) {
+            return;
+        }
+        if (shouldForceShow) {
+            mWindowLayoutParams.forciblyShownTypes |= WindowInsets.Type.navigationBars();
+        } else {
+            mWindowLayoutParams.forciblyShownTypes &= ~WindowInsets.Type.navigationBars();
+        }
+        notifyUpdateLayoutParams();
     }
 
     /**
@@ -958,8 +990,8 @@
                         }
 
                     } catch (NullPointerException
-                            | ActivityNotFoundException
-                            | SecurityException e) {
+                             | 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);
@@ -1053,6 +1085,7 @@
 
     /**
      * Called when we detect a long press in the nav region before passing the gesture slop.
+     *
      * @return Whether taskbar handled the long press, and thus should cancel the gesture.
      */
     public boolean onLongPressToUnstashTaskbar() {
@@ -1063,6 +1096,7 @@
      * Called when we want to unstash taskbar when user performs swipes up gesture.
      */
     public void onSwipeToUnstashTaskbar() {
+        VibratorWrapper.INSTANCE.get(this).vibrate(EFFECT_CLICK);
         mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false);
         mControllers.taskbarEduTooltipController.hide();
     }
@@ -1121,7 +1155,7 @@
      * Called when we detect a motion down or up/cancel in the nav region while stashed.
      *
      * @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
-     * @param forceUnstash Whether we force the unstash hint.
+     * @param forceUnstash   Whether we force the unstash hint.
      */
     public void startTaskbarUnstashHint(boolean animateForward, boolean forceUnstash) {
         // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code.
@@ -1229,12 +1263,16 @@
             mWindowLayoutParams.privateFlags &=
                     ~WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
         }
-        mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+        notifyUpdateLayoutParams();
     }
 
     void notifyUpdateLayoutParams() {
         if (mDragLayer.isAttachedToWindow()) {
-            mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+            if (ENABLE_TASKBAR_NO_RECREATION.get()) {
+                mWindowManager.updateViewLayout(mDragLayer.getRootView(), mWindowLayoutParams);
+            } else {
+                mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
index 83a3343..a2c61ce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
@@ -18,6 +18,10 @@
 import android.view.View
 import com.android.launcher3.LauncherPrefs
 import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_PINNED
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_UNPINNED
 import com.android.launcher3.taskbar.TaskbarDividerPopupView.Companion.createAndPopulate
 import java.io.PrintWriter
 
@@ -27,6 +31,7 @@
 
     private lateinit var controllers: TaskbarControllers
     private val launcherPrefs = LauncherPrefs.get(context)
+    private val statsLogManager = context.statsLogManager
 
     fun init(taskbarControllers: TaskbarControllers) {
         controllers = taskbarControllers
@@ -41,6 +46,7 @@
 
             popupView.onCloseCallback =
                 callback@{ didPreferenceChange ->
+                    statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE)
                     context.dragLayer.post { context.onPopupVisibilityChanged(false) }
 
                     if (!didPreferenceChange) {
@@ -49,8 +55,10 @@
 
                     if (launcherPrefs.get(TASKBAR_PINNING)) {
                         animateTransientToPersistentTaskbar()
+                        statsLogManager.logger().log(LAUNCHER_TASKBAR_PINNED)
                     } else {
                         animatePersistentToTransientTaskbar()
+                        statsLogManager.logger().log(LAUNCHER_TASKBAR_UNPINNED)
                     }
                 }
             popupView.changePreference = {
@@ -58,6 +66,7 @@
             }
             context.onPopupVisibilityChanged(true)
             popupView.show()
+            statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN)
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index e215bc9..b200858 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -59,6 +59,7 @@
             return taskMenuViewWithArrow.populateForView(view)
         }
     }
+
     private lateinit var dividerView: View
 
     private val menuWidth =
@@ -178,13 +179,19 @@
 
     override fun closeComplete() {
         onCloseCallback(didPreferenceChange)
+        onCloseCallback = {}
         super.closeComplete()
     }
 
     private fun onClickAlwaysShowTaskbarSwitchOption() {
         didPreferenceChange = true
         changePreference()
+        changePreference = {}
         // Allow switch animation to finish and then close the popup.
-        postDelayed(DIVIDER_POPUP_CLOSING_DELAY) { close(true) }
+        postDelayed(DIVIDER_POPUP_CLOSING_DELAY) {
+            if (isOpen) {
+                close(false)
+            }
+        }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 3c7196a..4ad5c88 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -259,6 +259,8 @@
             DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
             ItemInfo dragInfo, Rect dragRegion, float initialDragViewScale,
             float dragViewScaleOnDrop, DragOptions options) {
+        mActivity.hideKeyboard();
+
         mOptions = options;
 
         mRegistrationX = mMotionDown.x - dragLayerX;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
index c3ec1e5..2c72b2c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
@@ -35,8 +35,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 
-import androidx.annotation.VisibleForTesting;
-
 import com.android.app.animation.Interpolators;
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
@@ -51,8 +49,7 @@
  */
 public class TaskbarHoverToolTipController implements View.OnHoverListener {
 
-    @VisibleForTesting protected static final int HOVER_TOOL_TIP_REVEAL_START_DELAY = 400;
-    private static final int HOVER_TOOL_TIP_REVEAL_DURATION = 300;
+    private static final int HOVER_TOOL_TIP_REVEAL_DURATION = 250;
     private static final int HOVER_TOOL_TIP_EXIT_DURATION = 150;
 
     private final Handler mHoverToolTipHandler = new Handler(Looper.getMainLooper());
@@ -84,6 +81,12 @@
                 R.style.ArrowTipTaskbarStyle);
         mHoverToolTipView = new ArrowTipView(arrowContextWrapper, /* isPointingUp = */ false,
                 R.layout.arrow_toast);
+        int verticalPadding = arrowContextWrapper.getResources().getDimensionPixelSize(
+                R.dimen.taskbar_tooltip_vertical_padding);
+        int horizontalPadding = arrowContextWrapper.getResources().getDimensionPixelSize(
+                R.dimen.taskbar_tooltip_horizontal_padding);
+        mHoverToolTipView.findViewById(R.id.text).setPadding(horizontalPadding, verticalPadding,
+                horizontalPadding, verticalPadding);
 
         AnimatorSet hoverCloseAnimator = new AnimatorSet();
         ObjectAnimator textCloseAnimator = ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 0);
@@ -101,17 +104,18 @@
         mHoverToolTipView.setCustomCloseAnimation(hoverCloseAnimator);
 
         AnimatorSet hoverOpenAnimator = new AnimatorSet();
-        ObjectAnimator textOpenAnimator = ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 255);
-        textOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.33f, 1f));
-        ObjectAnimator scaleOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, SCALE_Y, 1f);
+        ObjectAnimator textOpenAnimator =
+                ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 0, 255);
+        textOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.15f, 0.75f));
+        ObjectAnimator scaleOpenAnimator =
+                ObjectAnimator.ofFloat(mHoverToolTipView, SCALE_Y, 0f, 1f);
         scaleOpenAnimator.setInterpolator(Interpolators.EMPHASIZED);
-        ObjectAnimator alphaOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, ALPHA, 1f);
-        alphaOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.1f, 0.33f));
+        ObjectAnimator alphaOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, ALPHA, 0f, 1f);
+        alphaOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0f, 0.33f));
         hoverOpenAnimator.playTogether(
                 scaleOpenAnimator,
                 textOpenAnimator,
                 alphaOpenAnimator);
-        hoverOpenAnimator.setStartDelay(HOVER_TOOL_TIP_REVEAL_START_DELAY);
         hoverOpenAnimator.setDuration(HOVER_TOOL_TIP_REVEAL_DURATION);
         mHoverToolTipView.setCustomOpenAnimation(hoverOpenAnimator);
 
@@ -120,8 +124,6 @@
                     mHoverToolTipView.setPivotY(bottom);
                     mHoverToolTipView.setY(mTaskbarView.getTop() - (bottom - top));
                 });
-        mHoverToolTipView.setScaleY(0f);
-        mHoverToolTipView.setAlpha(0f);
     }
 
     @Override
@@ -137,6 +139,15 @@
             mActivity.setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, false);
             return true;
         } else if (!isAnyOtherFloatingViewOpen && event.getAction() == ACTION_HOVER_ENTER) {
+            if (!mActivity.isTaskbarWindowFullscreen()) {
+                // First time we want to animate a tooltip open, we set the drag layer to
+                // fullscreen so the tooltip will fit within the window. This causes a layout
+                // pass which will trigger a hover exit and hover enter event while still
+                // hovering the view, so we do not animate open on the first hover enter if we
+                // are not already in fullscreen.
+                mActivity.setTaskbarWindowFullscreen(true);
+                return false;
+            }
             // If hovering above a taskbar icon starts, animate the tooltip open. Do not
             // reveal if any floating views such as folders or edu pop-ups are open.
             startRevealHoverToolTip();
@@ -147,8 +158,7 @@
     }
 
     private void startRevealHoverToolTip() {
-        mHoverToolTipHandler.postDelayed(mRevealHoverToolTipRunnable,
-                HOVER_TOOL_TIP_REVEAL_START_DELAY);
+        mHoverToolTipHandler.post(mRevealHoverToolTipRunnable);
     }
 
     private void revealHoverToolTip() {
@@ -158,14 +168,12 @@
         if (mHoverView instanceof FolderIcon && !((FolderIcon) mHoverView).getIconVisible()) {
             return;
         }
-        mActivity.setTaskbarWindowFullscreen(true);
         Rect iconViewBounds = Utilities.getViewBounds(mHoverView);
         mHoverToolTipView.showAtLocation(mToolTipText, iconViewBounds.centerX(),
                 mTaskbarView.getTop(), /* shouldAutoClose= */ false);
     }
 
     private void startHideHoverToolTip() {
-        mHoverToolTipHandler.removeCallbacks(mRevealHoverToolTipRunnable);
         int accessibilityHideTimeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis(
                 mActivity, /* originalTimeout= */ 0, FLAG_CONTENT_TEXT);
         mHoverToolTipHandler.postDelayed(mHideHoverToolTipRunnable, accessibilityHideTimeout);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index c51a7ec..881f5c4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -15,9 +15,9 @@
  */
 package com.android.launcher3.taskbar
 
-import android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR
 import android.graphics.Insets
 import android.graphics.Region
+import android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR
 import android.os.Binder
 import android.os.IBinder
 import android.view.Gravity
@@ -41,6 +41,7 @@
 import com.android.launcher3.DeviceProfile
 import com.android.launcher3.R
 import com.android.launcher3.anim.AlphaUpdateListener
+import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NO_RECREATION
 import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
 import com.android.launcher3.util.DisplayController
 import java.io.PrintWriter
@@ -97,7 +98,14 @@
                 0
             }
 
-        windowLayoutParams.providedInsets = getProvidedInsets(insetsRoundedCornerFlag)
+        windowLayoutParams.providedInsets =
+            if (ENABLE_TASKBAR_NO_RECREATION.get()) {
+                getProvidedInsets(controllers.sharedState!!.insetsFrameProviders!!,
+                        insetsRoundedCornerFlag)
+            } else {
+                getProvidedInsets(insetsRoundedCornerFlag)
+            }
+
         if (!context.isGestureNav) {
             if (windowLayoutParams.paramsForRotation != null) {
                 for (layoutParams in windowLayoutParams.paramsForRotation) {
@@ -154,6 +162,26 @@
     }
 
     /**
+     * This is for when ENABLE_TASKBAR_NO_RECREATION is enabled. We generate one instance of
+     * providedInsets and use it across the entire lifecycle of TaskbarManager. The only thing
+     * we need to reset is nav bar flags based on insetsRoundedCornerFlag.
+     */
+    private fun getProvidedInsets(providedInsets: Array<InsetsFrameProvider>,
+                                  insetsRoundedCornerFlag: Int): Array<InsetsFrameProvider> {
+        val navBarsFlag =
+                (if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag
+        for (provider in providedInsets) {
+            if (provider.type == navigationBars()) {
+                provider.setFlags(
+                        navBarsFlag,
+                        FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER
+                )
+            }
+        }
+        return providedInsets
+    }
+
+    /**
      * The inset types and number of insets provided have to match for both gesture nav and button
      * nav. The values and the order of the elements in array are allowed to differ.
      * Reason being WM does not allow types and number of insets changing for a given window once it
@@ -198,24 +226,24 @@
         }
 
 
-        val imeInsetsSize = getInsetsForGravity(taskbarHeightForIme, gravity)
+        // When in gesture nav, report the stashed height to the IME, to allow hiding the
+        // IME navigation bar.
+        val imeInsetsSize = if (ENABLE_HIDE_IME_CAPTION_BAR && context.isGestureNav) {
+            getInsetsForGravity(controllers.taskbarStashController.stashedHeight, gravity);
+        } else {
+            getInsetsForGravity(taskbarHeightForIme, gravity)
+        }
         val imeInsetsSizeOverride =
-                if (!ENABLE_HIDE_IME_CAPTION_BAR) {
-                    arrayOf(
-                            InsetsFrameProvider.InsetsSizeOverride(
-                                    TYPE_INPUT_METHOD,
-                                    imeInsetsSize
-                            ),
-                    )
-                } else {
-                    arrayOf()
-                }
+                arrayOf(
+                        InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
+                )
         // Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled.
         val visInsetsSizeForTappableElement =
                 if (context.isGestureNav) getInsetsForGravity(0, gravity)
                 else getInsetsForGravity(tappableHeight, gravity)
         val insetsSizeOverrideForTappableElement =
-                imeInsetsSizeOverride + arrayOf(
+                arrayOf(
+                        InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
                         InsetsFrameProvider.InsetsSizeOverride(
                                 TYPE_VOICE_INTERACTION,
                                 visInsetsSizeForTappableElement
@@ -224,7 +252,7 @@
         if ((context.isGestureNav || TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
                 && provider.type == tappableElement()) {
             provider.insetsSizeOverrides = insetsSizeOverrideForTappableElement
-        } else if (provider.type != systemGestures() && imeInsetsSizeOverride.isNotEmpty()) {
+        } else if (provider.type != systemGestures()) {
             // We only override insets at the bottom of the screen
             provider.insetsSizeOverrides = imeInsetsSizeOverride
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 90f7bea..88ae349 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -18,6 +18,7 @@
 import static com.android.app.animation.Interpolators.EMPHASIZED;
 import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
 import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
@@ -416,6 +417,9 @@
             controllers.bubbleStashController.setBubblesShowingOnOverview(onOverview);
         });
 
+        mControllers.taskbarStashController.updateStateForFlag(FLAG_IN_OVERVIEW,
+                mLauncherState == LauncherState.OVERVIEW);
+
         AnimatorSet animatorSet = new AnimatorSet();
 
         if (hasAnyFlag(changedFlags, FLAG_LAUNCHER_IN_STATE_TRANSITION)) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index c423fb3..e922c4c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -23,9 +23,12 @@
 import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
 import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NO_RECREATION;
 import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
+import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
+import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR;
 
 import android.annotation.SuppressLint;
 import android.app.Activity;
@@ -45,6 +48,8 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Display;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -64,6 +69,7 @@
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.util.AssistUtils;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -104,6 +110,9 @@
             Settings.Secure.NAV_BAR_KIDS_MODE);
 
     private final Context mContext;
+    private WindowManager mWindowManager;
+    private FrameLayout mTaskbarRootLayout;
+    private boolean mAddedWindow;
     private final TaskbarNavButtonController mNavButtonController;
     private final ComponentCallbacks mComponentCallbacks;
 
@@ -134,13 +143,6 @@
 
     private boolean mUserUnlocked = false;
 
-    public static final int SYSTEM_ACTION_ID_TASKBAR = 499;
-
-    /**
-     * For Taskbar broadcast intent filter.
-     */
-    public static final String ACTION_SHOW_TASKBAR = "ACTION_SHOW_TASKBAR";
-
     private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver =
             new SimpleBroadcastReceiver(this::showTaskbarFromBroadcast);
 
@@ -175,8 +177,13 @@
         Display display =
                 service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
         mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
+        if (ENABLE_TASKBAR_NO_RECREATION.get()) {
+            mWindowManager = mContext.getSystemService(WindowManager.class);
+            mTaskbarRootLayout = new FrameLayout(mContext);
+        }
         mNavButtonController = new TaskbarNavButtonController(service,
-                SystemUiProxy.INSTANCE.get(mContext), new Handler());
+                SystemUiProxy.INSTANCE.get(mContext), new Handler(),
+                AssistUtils.newInstance(mContext));
         mComponentCallbacks = new ComponentCallbacks() {
             private Configuration mOldConfig = mContext.getResources().getConfiguration();
 
@@ -256,10 +263,15 @@
             LauncherPrefs.get(mContext).removeListener(mTaskbarPinningPreferenceChangeListener,
                     TASKBAR_PINNING);
             mTaskbarActivityContext.onDestroy();
-            if (!FLAG_HIDE_NAVBAR_WINDOW) {
+            if (!FLAG_HIDE_NAVBAR_WINDOW || ENABLE_TASKBAR_NO_RECREATION.get()) {
                 mTaskbarActivityContext = null;
             }
         }
+        DeviceProfile dp = mUserUnlocked ?
+                LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
+        if (dp == null || !isTaskbarPresent(dp)) {
+            removeTaskbarRootViewFromWindow();
+        }
     }
 
     /**
@@ -287,7 +299,7 @@
             return;
         }
 
-        mTaskbarActivityContext.toggleAllApps();
+        mTaskbarActivityContext.toggleAllAppsSearch();
     }
 
     /**
@@ -308,6 +320,7 @@
         mUserUnlocked = true;
         LauncherAppState.getIDP(mContext).addOnChangeListener(mIdpChangeListener);
         recreateTaskbar();
+        addTaskbarRootViewToWindow();
     }
 
     /**
@@ -388,10 +401,9 @@
                 return;
             }
 
-            if (mTaskbarActivityContext == null) {
+            if (ENABLE_TASKBAR_NO_RECREATION.get() || mTaskbarActivityContext == null) {
                 mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp,
-                    mNavButtonController,
-                    mUnfoldProgressProvider);
+                        mNavButtonController, mUnfoldProgressProvider);
             } else {
                 mTaskbarActivityContext.updateDeviceProfile(dp);
             }
@@ -402,6 +414,13 @@
                     createTaskbarUIControllerForActivity(mActivity));
             }
 
+            if (ENABLE_TASKBAR_NO_RECREATION.get()) {
+                addTaskbarRootViewToWindow();
+                mTaskbarRootLayout.removeAllViews();
+                mTaskbarRootLayout.addView(mTaskbarActivityContext.getDragLayer());
+                mTaskbarActivityContext.notifyUpdateLayoutParams();
+            }
+
             // We to wait until user unlocks the device to attach listener.
             LauncherPrefs.get(mContext).addListener(mTaskbarPinningPreferenceChangeListener,
                 TASKBAR_PINNING);
@@ -523,6 +542,22 @@
         }
     }
 
+    private void addTaskbarRootViewToWindow() {
+        if (ENABLE_TASKBAR_NO_RECREATION.get() && !mAddedWindow
+                && mTaskbarActivityContext != null) {
+            mWindowManager.addView(mTaskbarRootLayout,
+                    mTaskbarActivityContext.getWindowLayoutParams());
+            mAddedWindow = true;
+        }
+    }
+
+    private void removeTaskbarRootViewFromWindow() {
+        if (ENABLE_TASKBAR_NO_RECREATION.get() && mAddedWindow) {
+            mWindowManager.removeViewImmediate(mTaskbarRootLayout);
+            mAddedWindow = false;
+        }
+    }
+
     /** Temp logs for b/254119092. */
     public void debugWhyTaskbarNotDestroyed(String debugReason) {
         StringJoiner log = new StringJoiner("\n");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 6d86b1e..533785f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -109,15 +109,17 @@
     private final TouchInteractionService mService;
     private final SystemUiProxy mSystemUiProxy;
     private final Handler mHandler;
+    private final AssistUtils mAssistUtils;
     @Nullable private StatsLogManager mStatsLogManager;
 
     private final Runnable mResetLongPress = this::resetScreenUnpin;
 
     public TaskbarNavButtonController(TouchInteractionService service,
-            SystemUiProxy systemUiProxy, Handler handler) {
+            SystemUiProxy systemUiProxy, Handler handler, AssistUtils assistUtils) {
         mService = service;
         mSystemUiProxy = systemUiProxy;
         mHandler = handler;
+        mAssistUtils = assistUtils;
     }
 
     public void onButtonClick(@TaskbarButton int buttonType, View view) {
@@ -313,8 +315,7 @@
             return;
         }
         // Attempt to start Assist with AssistUtils, otherwise fall back to SysUi's implementation.
-        if (!AssistUtils.newInstance(mService.getApplicationContext()).tryStartAssistOverride(
-                INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) {
+        if (!mAssistUtils.tryStartAssistOverride(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) {
             Bundle args = new Bundle();
             args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
             mSystemUiProxy.startAssistant(args);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
index cdc6d59..e40757a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
@@ -70,6 +70,10 @@
         invalidate();
     }
 
+    protected float getScrimAlpha() {
+        return mRenderer.getPaint().getAlpha() / 255f;
+    }
+
     /**
      * Sets the roundness of the round corner above Taskbar.
      * @param cornerRoundness 0 has no round corner, 1 has complete round corner.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 1c250bf..a0ce976 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -15,9 +15,13 @@
  */
 package com.android.launcher3.taskbar;
 
+import static android.view.View.VISIBLE;
+
 import static com.android.launcher3.taskbar.bubbles.BubbleBarController.BUBBLE_BAR_ENABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
+import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
 
 import android.animation.ObjectAnimator;
 import android.view.animation.Interpolator;
@@ -35,13 +39,13 @@
 public class TaskbarScrimViewController implements TaskbarControllers.LoggableTaskbarController,
         TaskbarControllers.BackgroundRendererController {
 
-    private static final float SCRIM_ALPHA = 0.6f;
-
     private static final Interpolator SCRIM_ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     private static final Interpolator SCRIM_ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
 
     private final TaskbarActivityContext mActivity;
     private final TaskbarScrimView mScrimView;
+    private boolean mTaskbarVisible;
+    private int mSysUiStateFlags;
 
     // Alpha property for the scrim.
     private final AnimatedFloat mScrimAlpha = new AnimatedFloat(this::updateScrimAlpha);
@@ -62,6 +66,20 @@
     }
 
     /**
+     * Called when the taskbar visibility changes.
+     *
+     * @param visibility the current visibility of {@link TaskbarView}.
+     */
+    public void onTaskbarVisibilityChanged(int visibility) {
+        mTaskbarVisible = visibility == VISIBLE;
+        if (shouldShowScrim()) {
+            showScrim(true, getScrimAlpha(), false /* skipAnim */);
+        } else if (mScrimView.getScrimAlpha() > 0f) {
+            showScrim(false, 0, false /* skipAnim */);
+        }
+    }
+
+    /**
      * Updates the scrim state based on the flags.
      */
     public void updateStateForSysuiFlags(int stateFlags, boolean skipAnim) {
@@ -69,28 +87,39 @@
             // These scrims aren't used if bubble bar & transient taskbar are active.
             return;
         }
-        final boolean bubblesExpanded = (stateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
+        mSysUiStateFlags = stateFlags;
+        showScrim(shouldShowScrim(), getScrimAlpha(), skipAnim);
+    }
+
+    private boolean shouldShowScrim() {
+        final boolean bubblesExpanded = (mSysUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
+        boolean isShadeVisible = (mSysUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0;
+        return bubblesExpanded && !mControllers.navbarButtonsViewController.isImeVisible()
+                && !isShadeVisible
+                && !mControllers.taskbarStashController.isStashed()
+                && mTaskbarVisible;
+    }
+
+    private float getScrimAlpha() {
         final boolean manageMenuExpanded =
-                (stateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
-        final boolean showScrim = !mControllers.navbarButtonsViewController.isImeVisible()
-                && bubblesExpanded
-                && mControllers.taskbarStashController.isTaskbarVisibleAndNotStashing();
-        final float scrimAlpha = manageMenuExpanded
+                (mSysUiStateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
+        return manageMenuExpanded
                 // When manage menu shows there's the first scrim and second scrim so figure out
                 // what the total transparency would be.
-                ? (SCRIM_ALPHA + (SCRIM_ALPHA * (1 - SCRIM_ALPHA)))
-                : showScrim ? SCRIM_ALPHA : 0;
-        showScrim(showScrim, scrimAlpha, skipAnim);
+                ? (BUBBLE_EXPANDED_SCRIM_ALPHA + (BUBBLE_EXPANDED_SCRIM_ALPHA
+                * (1 - BUBBLE_EXPANDED_SCRIM_ALPHA)))
+                : shouldShowScrim() ? BUBBLE_EXPANDED_SCRIM_ALPHA : 0;
     }
 
     private void showScrim(boolean showScrim, float alpha, boolean skipAnim) {
         mScrimView.setOnClickListener(showScrim ? (view) -> onClick() : null);
         mScrimView.setClickable(showScrim);
-        ObjectAnimator anim = mScrimAlpha.animateToValue(showScrim ? alpha : 0);
-        anim.setInterpolator(showScrim ? SCRIM_ALPHA_IN : SCRIM_ALPHA_OUT);
-        anim.start();
         if (skipAnim) {
-            anim.end();
+            mScrimView.setScrimAlpha(alpha);
+        } else {
+            ObjectAnimator anim = mScrimAlpha.animateToValue(showScrim ? alpha : 0);
+            anim.setInterpolator(showScrim ? SCRIM_ALPHA_IN : SCRIM_ALPHA_OUT);
+            anim.start();
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index 66ca7d9..abbd18b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -15,15 +15,28 @@
  */
 package com.android.launcher3.taskbar;
 
+import static android.view.InsetsFrameProvider.SOURCE_DISPLAY;
+import static android.view.WindowInsets.Type.mandatorySystemGestures;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.systemGestures;
+import static android.view.WindowInsets.Type.tappableElement;
+
 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.DISPLAY_PROGRESS_COUNT;
 
 import android.app.PendingIntent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.InsetsFrameProvider;
 
 /**
  * State shared across different taskbar instance
  */
 public class TaskbarSharedState {
 
+    private final IBinder mInsetsOwner = new Binder();
+    private static int INDEX_LEFT = 0;
+    private static int INDEX_RIGHT = 1;
+
     // TaskbarManager#onSystemUiFlagsChanged
     public int sysuiStateFlags;
 
@@ -48,4 +61,14 @@
 
     // Taskbar System Action
     public PendingIntent taskbarSystemActionPendingIntent;
+
+    public final InsetsFrameProvider[] insetsFrameProviders = new InsetsFrameProvider[] {
+            new InsetsFrameProvider(mInsetsOwner, 0, navigationBars()),
+            new InsetsFrameProvider(mInsetsOwner, 0, tappableElement()),
+            new InsetsFrameProvider(mInsetsOwner, 0, mandatorySystemGestures()),
+            new InsetsFrameProvider(mInsetsOwner, INDEX_LEFT, systemGestures())
+                    .setSource(SOURCE_DISPLAY),
+            new InsetsFrameProvider(mInsetsOwner, INDEX_RIGHT, systemGestures())
+                    .setSource(SOURCE_DISPLAY)
+    };
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index b5b453b..f2b60b9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -29,10 +29,11 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW;
 import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED;
-import static com.android.launcher3.taskbar.TaskbarManager.SYSTEM_ACTION_ID_TASKBAR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
 import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
+import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
@@ -96,6 +97,7 @@
     public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 9; // Autohide (transient taskbar).
     public static final int FLAG_STASHED_SYSUI = 1 << 10; //  app pinning,...
     public static final int FLAG_STASHED_DEVICE_LOCKED = 1 << 11; // device is locked: keyguard, ...
+    public static final int FLAG_IN_OVERVIEW = 1 << 12; // launcher is in overview
 
     // If any of these flags are enabled, isInApp should return true.
     private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
@@ -1015,9 +1017,12 @@
 
         updateStateForFlag(FLAG_STASHED_IN_APP_SYSUI, hasAnyFlag(systemUiStateFlags,
                 SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE));
-        updateStateForFlag(FLAG_STASHED_SYSUI,
-                hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING));
 
+        boolean stashForBubbles = hasAnyFlag(FLAG_IN_OVERVIEW)
+                && hasAnyFlag(systemUiStateFlags, SYSUI_STATE_BUBBLES_EXPANDED)
+                && DisplayController.isTransientTaskbar(mActivity);
+        updateStateForFlag(FLAG_STASHED_SYSUI,
+                hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING) || stashForBubbles);
         boolean isLocked = hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED)
                 && !hasAnyFlag(systemUiStateFlags, SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY);
         updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, isLocked);
@@ -1097,6 +1102,7 @@
                     TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR,
                     !hasAnyFlag(FLAG_STASHED_IN_APP_AUTO));
         }
+        mActivity.applyForciblyShownFlagWhileTransientTaskbarUnstashed(!isStashedInApp());
     }
 
     private void notifyStashChange(boolean visible, boolean stashed) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 6fad655..139e8f0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -43,7 +43,6 @@
 import com.android.systemui.shared.recents.model.Task;
 
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.stream.Stream;
 
@@ -336,4 +335,7 @@
                 .stream()
                 .map(mControllers.taskbarPopupController::createSplitShortcutFactory);
     }
+
+    /** Adjusts the hotseat for the bubble bar. */
+    public void adjustHotseatForBubbleBar(boolean isBubbleBarVisible) {}
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index fa5a1ae..3dc30dc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -18,7 +18,8 @@
 import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR;
+import static com.android.launcher3.config.FeatureFlags.enableCursorHoverStates;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
 import android.content.Context;
@@ -33,6 +34,8 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 
+import androidx.annotation.DimenRes;
+import androidx.annotation.DrawableRes;
 import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -90,11 +93,11 @@
     // Only non-null when device supports having an All Apps button.
     private @Nullable IconButtonView mTaskbarDivider;
 
-    private View mQsb;
+    private final View mQsb;
 
-    private float mTransientTaskbarMinWidth;
+    private final float mTransientTaskbarMinWidth;
 
-    private float mTransientTaskbarAllAppsButtonTranslationXOffset;
+    private final float mTaskbarAllAppsButtonTranslationXOffset;
 
     private boolean mShouldTryStartAlign;
 
@@ -120,12 +123,9 @@
         boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivityContext)
                 && !TaskbarManager.isPhoneMode(mActivityContext.getDeviceProfile());
         mIsRtl = Utilities.isRtl(resources);
-        mTransientTaskbarMinWidth = mContext.getResources().getDimension(
-                R.dimen.transient_taskbar_min_width);
-        mTransientTaskbarAllAppsButtonTranslationXOffset =
-                resources.getDimension(isTransientTaskbar
-                        ? R.dimen.transient_taskbar_all_apps_button_translation_x_offset
-                        : R.dimen.taskbar_all_apps_button_translation_x_offset);
+        mTransientTaskbarMinWidth = resources.getDimension(R.dimen.transient_taskbar_min_width);
+        mTaskbarAllAppsButtonTranslationXOffset =
+                resources.getDimension(getAllAppsButtonTranslationXOffset(isTransientTaskbar));
 
         onDeviceProfileChanged(mActivityContext.getDeviceProfile());
 
@@ -149,9 +149,8 @@
         if (!mActivityContext.getPackageManager().hasSystemFeature(FEATURE_PC)) {
             mAllAppsButton = (IconButtonView) LayoutInflater.from(context)
                     .inflate(R.layout.taskbar_all_apps_button, this, false);
-            mAllAppsButton.setIconDrawable(resources.getDrawable(isTransientTaskbar
-                    ? R.drawable.ic_transient_taskbar_all_apps_button
-                    : R.drawable.ic_taskbar_all_apps_button));
+            mAllAppsButton.setIconDrawable(resources.getDrawable(
+                    getAllAppsButton(isTransientTaskbar)));
             mAllAppsButton.setScaleX(mIsRtl ? -1 : 1);
             mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
             mAllAppsButton.setForegroundTint(
@@ -171,6 +170,39 @@
         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
     }
 
+    @DrawableRes
+    private int getAllAppsButton(boolean isTransientTaskbar) {
+        if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
+            return isTransientTaskbar
+                    ? R.drawable.ic_transient_taskbar_all_apps_search_button
+                    : R.drawable.ic_taskbar_all_apps_search_button;
+        } else {
+            return isTransientTaskbar
+                    ? R.drawable.ic_transient_taskbar_all_apps_button
+                    : R.drawable.ic_taskbar_all_apps_button;
+        }
+    }
+
+    @DimenRes
+    private int getAllAppsButtonTranslationXOffset(boolean isTransientTaskbar) {
+        if (isTransientTaskbar) {
+            return R.dimen.transient_taskbar_all_apps_button_translation_x_offset;
+        } else {
+            return ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()
+                    ? R.dimen.taskbar_all_apps_search_button_translation_x_offset
+                    : R.dimen.taskbar_all_apps_button_translation_x_offset;
+        }
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        boolean changed = getVisibility() != visibility;
+        super.setVisibility(visibility);
+        if (changed && mControllerCallbacks != null) {
+            mControllerCallbacks.notifyVisibilityChanged();
+        }
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -324,7 +356,7 @@
                 }
             }
             setClickAndLongClickListenersForIcon(hotseatView);
-            if (ENABLE_CURSOR_HOVER_STATES.get()) {
+            if (enableCursorHoverStates()) {
                 setHoverListenerForIcon(hotseatView);
             }
             nextViewIndex++;
@@ -336,7 +368,7 @@
 
         if (mAllAppsButton != null) {
             mAllAppsButton.setTranslationXForTaskbarAllAppsIcon(getChildCount() > 0
-                    ? mTransientTaskbarAllAppsButtonTranslationXOffset : 0f);
+                    ? mTaskbarAllAppsButtonTranslationXOffset : 0f);
             addView(mAllAppsButton, mIsRtl ? getChildCount() : 0);
 
             // if only all apps button present, don't include divider view.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 3d22e78..b405320 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -448,6 +448,11 @@
         }
     }
 
+    /** Resets the icon alignment controller so that it can be recreated again later. */
+    void resetIconAlignmentController() {
+        mIconAlignControllerLazy = null;
+    }
+
     /**
      * Creates an animation for aligning the Taskbar icons with the provided Launcher device profile
      */
@@ -564,9 +569,18 @@
                 continue;
             }
 
-            float hotseatIconCenter = hotseatPadding.left
-                    + (hotseatCellSize + borderSpacing) * positionInHotseat
-                    + hotseatCellSize / 2f;
+            float hotseatAdjustedBorderSpace =
+                    launcherDp.getHotseatAdjustedBorderSpaceForBubbleBar(child.getContext());
+            float hotseatIconCenter;
+            if (bubbleBarHasBubbles() && hotseatAdjustedBorderSpace != 0) {
+                hotseatIconCenter = hotseatPadding.left + hotseatCellSize
+                        + (hotseatCellSize + hotseatAdjustedBorderSpace) * positionInHotseat
+                        + hotseatCellSize / 2f;
+            } else {
+                hotseatIconCenter = hotseatPadding.left
+                        + (hotseatCellSize + borderSpacing) * positionInHotseat
+                        + hotseatCellSize / 2f;
+            }
             float childCenter = (child.getLeft() + child.getRight()) / 2f;
             float toX = hotseatIconCenter - childCenter;
             if (child instanceof Reorderable) {
@@ -588,6 +602,11 @@
         return controller;
     }
 
+    private boolean bubbleBarHasBubbles() {
+        return mControllers.bubbleControllers.isPresent()
+                && mControllers.bubbleControllers.get().bubbleBarViewController.hasBubbles();
+    }
+
     public void onRotationChanged(DeviceProfile deviceProfile) {
         if (!mControllers.uiController.isIconAlignedWithHotseat()) {
             // We only translate on rotation when icon is aligned with hotseat
@@ -737,5 +756,13 @@
         public void notifyIconLayoutBoundsChanged() {
             mControllers.uiController.onIconLayoutBoundsChanged();
         }
+
+        /**
+         * Notifies the taskbar scrim when the visibility of taskbar changes.
+         */
+        public void notifyVisibilityChanged() {
+            mControllers.taskbarScrimViewController.onTaskbarVisibilityChanged(
+                    mTaskbarView.getVisibility());
+        }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index d786d94..c54bb7e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -128,10 +128,19 @@
 
     /** Toggles visibility of {@link TaskbarAllAppsContainerView} in the overlay window. */
     public void toggle() {
+        toggle(false);
+    }
+
+    /** Toggles visibility of {@link TaskbarAllAppsContainerView} with the keyboard for search. */
+    public void toggleSearch() {
+        toggle(true);
+    }
+
+    private void toggle(boolean showKeyboard) {
         if (isOpen()) {
             mSlideInView.close(true);
         } else {
-            show(true);
+            show(true, showKeyboard);
         }
     }
 
@@ -141,6 +150,10 @@
     }
 
     private void show(boolean animate) {
+        show(animate, false);
+    }
+
+    private void show(boolean animate, boolean showKeyboard) {
         if (mAppsView != null) {
             return;
         }
@@ -161,12 +174,19 @@
 
         mSlideInView = (TaskbarAllAppsSlideInView) mOverlayContext.getLayoutInflater().inflate(
                 R.layout.taskbar_all_apps_sheet, mOverlayContext.getDragLayer(), false);
+        // Ensures All Apps gets touch events in case it is not the top floating view. Floating
+        // views above it may not be able to intercept the touch, so All Apps should try to.
+        mOverlayContext.getDragLayer().addTouchController(mSlideInView);
         mSlideInView.addOnCloseListener(() -> {
             mControllers.getSharedState().allAppsVisible = false;
             cleanUpOverlay();
         });
         TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
-                mOverlayContext, mSlideInView, mControllers, mSearchSessionController);
+                mOverlayContext,
+                mSlideInView,
+                mControllers,
+                mSearchSessionController,
+                showKeyboard);
 
         viewController.show(animate);
         mAppsView = mOverlayContext.getAppsView();
@@ -195,6 +215,7 @@
             mSearchSessionController = null;
         }
         if (mOverlayContext != null) {
+            mOverlayContext.getDragLayer().removeTouchController(mSlideInView);
             mOverlayContext.setSearchSessionController(null);
             mOverlayContext = null;
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 537d2c6..c6c4f77 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -21,12 +21,16 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
 import android.window.OnBackInvokedDispatcher;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
@@ -40,8 +44,11 @@
 /** Wrapper for taskbar all apps with slide-in behavior. */
 public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverlayContext>
         implements Insettable, DeviceProfile.OnDeviceProfileChangeListener {
+    private final Handler mHandler;
+
     private TaskbarAllAppsContainerView mAppsView;
     private float mShiftRange;
+    private @Nullable Runnable mShowOnFullyAttachedToWindowRunnable;
 
     // Initialized in init.
     private TaskbarAllAppsCallbacks mAllAppsCallbacks;
@@ -53,6 +60,7 @@
     public TaskbarAllAppsSlideInView(Context context, AttributeSet attrs,
             int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        mHandler = new Handler(Looper.myLooper());
     }
 
     void init(TaskbarAllAppsCallbacks callbacks) {
@@ -65,14 +73,14 @@
             return;
         }
         mIsOpen = true;
-        attachToContainer();
 
         addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
                 removeOnAttachStateChangeListener(this);
                 // Wait for view and its descendants to be fully attached before starting open.
-                post(() -> showOnFullyAttachedToWindow(animate));
+                mShowOnFullyAttachedToWindowRunnable = () -> showOnFullyAttachedToWindow(animate);
+                mHandler.post(mShowOnFullyAttachedToWindowRunnable);
             }
 
             @Override
@@ -80,6 +88,7 @@
                 removeOnAttachStateChangeListener(this);
             }
         });
+        attachToContainer();
     }
 
     private void showOnFullyAttachedToWindow(boolean animate) {
@@ -114,6 +123,10 @@
 
     @Override
     protected void handleClose(boolean animate) {
+        if (mShowOnFullyAttachedToWindowRunnable != null) {
+            mHandler.removeCallbacks(mShowOnFullyAttachedToWindowRunnable);
+            mShowOnFullyAttachedToWindowRunnable = null;
+        }
         if (mIsOpen) {
             mAllAppsCallbacks.onAllAppsTransitionStart(false);
         }
@@ -207,7 +220,9 @@
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mNoIntercept = !mAppsView.shouldContainerScroll(ev);
+            mNoIntercept = !mAppsView.shouldContainerScroll(ev)
+                    || getTopOpenViewWithType(
+                            mActivityContext, TYPE_ACCESSIBLE & ~TYPE_TASKBAR_ALL_APPS) != null;
         }
         return super.onControllerInterceptTouchEvent(ev);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 85633e9..6d740c0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -41,12 +41,14 @@
     private final TaskbarStashController mTaskbarStashController;
     private final NavbarButtonsViewController mNavbarButtonsViewController;
     private final TaskbarOverlayController mOverlayController;
+    private final boolean mShowKeyboard;
 
     TaskbarAllAppsViewController(
             TaskbarOverlayContext context,
             TaskbarAllAppsSlideInView slideInView,
             TaskbarControllers taskbarControllers,
-            TaskbarSearchSessionController searchSessionController) {
+            TaskbarSearchSessionController searchSessionController,
+            boolean showKeyboard) {
 
         mContext = context;
         mSlideInView = slideInView;
@@ -54,6 +56,7 @@
         mTaskbarStashController = taskbarControllers.taskbarStashController;
         mNavbarButtonsViewController = taskbarControllers.navbarButtonsViewController;
         mOverlayController = taskbarControllers.taskbarOverlayController;
+        mShowKeyboard = showKeyboard;
 
         mSlideInView.init(new TaskbarAllAppsCallbacks(searchSessionController));
         setUpAppDivider();
@@ -81,7 +84,7 @@
     private void setUpTaskbarStashing() {
         if (DisplayController.isTransientTaskbar(mContext)) {
             mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
-            mTaskbarStashController.applyState(mOverlayController.getOpenDuration());
+            mTaskbarStashController.applyState();
         }
 
         mNavbarButtonsViewController.setSlideInViewVisible(true);
@@ -92,7 +95,7 @@
 
             if (DisplayController.isTransientTaskbar(mContext)) {
                 mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
-                mTaskbarStashController.applyState(mOverlayController.getCloseDuration());
+                mTaskbarStashController.applyState();
             }
         });
     }
@@ -120,6 +123,11 @@
         @Override
         public void onAllAppsTransitionEnd(boolean toAllApps) {
             mSearchSessionController.onAllAppsTransitionEnd(toAllApps);
+            if (toAllApps
+                    && mShowKeyboard
+                    && mAppsView.getSearchUiManager().getEditText() != null) {
+                mAppsView.getSearchUiManager().getEditText().requestFocus();
+            }
         }
 
         /** Invoked on back press, returning {@code true} if the search session handled it. */
@@ -128,7 +136,7 @@
         }
 
         void onAllAppsAnimationPending(PendingAnimation animation, boolean toAllApps) {
-            mSearchSessionController.onAllAppsAnimationPending(animation, toAllApps);
+            mSearchSessionController.onAllAppsAnimationPending(animation, toAllApps, mShowKeyboard);
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
index 8a2041f..3d15fbd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
@@ -51,7 +51,11 @@
 
     open fun handleBackInvoked(): Boolean = false
 
-    open fun onAllAppsAnimationPending(animation: PendingAnimation, toAllApps: Boolean) = Unit
+    open fun onAllAppsAnimationPending(
+        animation: PendingAnimation,
+        toAllApps: Boolean,
+        showKeyboard: Boolean,
+    ) = Unit
 
     companion object {
         @JvmStatic
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 24db380..bd11efd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -137,6 +137,7 @@
     private static class BubbleBarViewUpdate {
         boolean expandedChanged;
         boolean expanded;
+        boolean shouldShowEducation;
         String selectedBubbleKey;
         String suppressedBubbleKey;
         String unsuppressedBubbleKey;
@@ -151,6 +152,7 @@
         BubbleBarViewUpdate(BubbleBarUpdate update) {
             expandedChanged = update.expandedChanged;
             expanded = update.expanded;
+            shouldShowEducation = update.shouldShowEducation;
             selectedBubbleKey = update.selectedBubbleKey;
             suppressedBubbleKey = update.suppressedBubbleKey;
             unsuppressedBubbleKey = update.unsupressedBubbleKey;
@@ -188,8 +190,10 @@
         mBubbleStashedHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
 
         bubbleControllers.runAfterInit(() -> {
-            mBubbleBarViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED);
-            mBubbleStashedHandleViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED);
+            mBubbleBarViewController.setHiddenForBubbles(
+                    !BUBBLE_BAR_ENABLED || mBubbles.isEmpty());
+            mBubbleStashedHandleViewController.setHiddenForBubbles(
+                    !BUBBLE_BAR_ENABLED || mBubbles.isEmpty());
             mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse(
                     key -> setSelectedBubble(mBubbles.get(key)));
         });
@@ -366,7 +370,9 @@
                 mBubbleStashController.animateToInitialState(update.expanded);
             }
         }
-
+        if (update.shouldShowEducation) {
+            mBubbleBarViewController.prepareToShowEducation();
+        }
         if (update.expandedChanged) {
             if (update.expanded != mBubbleBarViewController.isExpanded()) {
                 mBubbleBarViewController.setExpandedFromSysui(update.expanded);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 20b8e3b..065dd58 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -18,6 +18,7 @@
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -74,7 +75,8 @@
     // Whether the bar is hidden for a sysui state.
     private boolean mHiddenForSysui;
     // Whether the bar is hidden because there are no bubbles.
-    private boolean mHiddenForNoBubbles;
+    private boolean mHiddenForNoBubbles = true;
+    private boolean mShouldShowEducation;
 
     public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) {
         mActivity = activity;
@@ -98,7 +100,7 @@
         mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight;
         mBubbleBarScale.updateValue(1f);
         mBubbleClickListener = v -> onBubbleClicked(v);
-        mBubbleBarClickListener = v -> setExpanded(true);
+        mBubbleBarClickListener = v -> onBubbleBarClicked();
         mBubbleDragController.setupBubbleBarView(mBarView);
         mBarView.setOnClickListener(mBubbleBarClickListener);
         mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
@@ -121,6 +123,21 @@
         }
     }
 
+    private void onBubbleBarClicked() {
+        if (mShouldShowEducation) {
+            mShouldShowEducation = false;
+            // Get the bubble bar bounds on screen
+            Rect bounds = new Rect();
+            mBarView.getBoundsOnScreen(bounds);
+            // Calculate user education reference position in Screen coordinates
+            Point position = new Point(bounds.centerX(), bounds.top);
+            // Show user education relative to the reference point
+            mSystemUiProxy.showUserEducation(position);
+        } else {
+            setExpanded(true);
+        }
+    }
+
     //
     // The below animators are exposed to BubbleStashController so it can manage the stashing
     // animation.
@@ -195,6 +212,7 @@
         if (mHiddenForNoBubbles != hidden) {
             mHiddenForNoBubbles = hidden;
             updateVisibilityForStateChange();
+            mActivity.bubbleBarVisibilityChanged(!hidden);
         }
     }
 
@@ -326,6 +344,12 @@
         }
     }
 
+    /** Marks as should show education and shows the bubble bar in a collapsed state */
+    public void prepareToShowEducation() {
+        mShouldShowEducation = true;
+        mBubbleStashController.showBubbleBar(false /* expand the bubbles */);
+    }
+
     /**
      * Updates the dragged bubble view in the bubble bar view, and notifies SystemUI
      * that a bubble is being dragged to dismiss.
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index d4e2be9..c4eeea7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -23,7 +23,6 @@
 import static com.android.launcher3.LauncherState.ALL_APPS;
 
 import android.annotation.SuppressLint;
-import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.PixelFormat;
 import android.view.Gravity;
@@ -60,14 +59,26 @@
 
     private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
         @Override
-        public void onTaskCreated(int taskId, ComponentName componentName) {
-            // Created task will be below existing overlay, so move out of the way.
-            hideWindow();
+        public void onTaskMovedToFront(int taskId) {
+            // New front task will be below existing overlay, so move out of the way.
+            hideWindowOnTaskStackChange();
         }
 
         @Override
-        public void onTaskMovedToFront(int taskId) {
-            // New front task will be below existing overlay, so move out of the way.
+        public void onTaskStackChanged() {
+            // The other callbacks are insufficient for All Apps, because there are many cases where
+            // it can relaunch the same task already behind it. However, this callback needs to be a
+            // no-op when only EDU is shown, because going between the EDU steps invokes this
+            // callback.
+            if (mControllers.getSharedState() != null
+                    && mControllers.getSharedState().allAppsVisible) {
+                hideWindowOnTaskStackChange();
+            }
+        }
+
+        private void hideWindowOnTaskStackChange() {
+            // A task was launched while overlay window was open, so stash Taskbar.
+            mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
             hideWindow();
         }
     };
@@ -199,8 +210,10 @@
 
         @Override
         protected void handleClose(boolean animate) {
-            mTaskbarContext.getDragLayer().removeView(this);
-            Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
+            if (mIsOpen) {
+                mTaskbarContext.getDragLayer().removeView(this);
+                Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
+            }
         }
 
         @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index ff00560..e742a3d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -38,6 +38,7 @@
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -69,6 +70,7 @@
             return true;
         }
     };
+    private final List<TouchController> mTouchControllers = new ArrayList<>();
 
     TaskbarOverlayDragLayer(Context context) {
         super(context, null, 1);
@@ -93,10 +95,13 @@
 
     @Override
     public void recreateControllers() {
-        mControllers = mOnClickListeners.isEmpty()
-                ? new TouchController[]{mActivity.getDragController()}
-                : new TouchController[] {
-                        mActivity.getDragController(), mClickListenerTouchController};
+        List<TouchController> controllers = new ArrayList<>();
+        controllers.add(mActivity.getDragController());
+        controllers.addAll(mTouchControllers);
+        if (!mOnClickListeners.isEmpty()) {
+            controllers.add(mClickListenerTouchController);
+        }
+        mControllers = controllers.toArray(new TouchController[0]);
     }
 
     @Override
@@ -183,6 +188,18 @@
         });
     }
 
+    /** Adds a {@link TouchController} to this drag layer. */
+    public void addTouchController(@NonNull TouchController touchController) {
+        mTouchControllers.add(touchController);
+        recreateControllers();
+    }
+
+    /** Removes a {@link TouchController} from this drag layer. */
+    public void removeTouchController(@NonNull TouchController touchController) {
+        mTouchControllers.remove(touchController);
+        recreateControllers();
+    }
+
     /**
      * Taskbar automatically stashes when opening all apps, but we don't report the insets as
      * changing to avoid moving the underlying app. But internally, the apps view should still
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index b50ab97..40909d5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -50,9 +50,9 @@
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
 import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -73,10 +73,13 @@
 import android.os.IBinder;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
+import android.widget.AnalogClock;
+import android.widget.TextClock;
 import android.window.BackEvent;
 import android.window.OnBackAnimationCallback;
 import android.window.OnBackInvokedDispatcher;
@@ -133,7 +136,6 @@
 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
 import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.BackPressHandler;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.IntSet;
@@ -153,6 +155,7 @@
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.TouchInteractionService.TISBinder;
+import com.android.quickstep.util.AsyncClockEventDelegate;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.util.LauncherUnfoldAnimationController;
 import com.android.quickstep.util.QuickstepOnboardingPrefs;
@@ -214,6 +217,8 @@
     private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController;
     private SplitToWorkspaceController mSplitToWorkspaceController;
 
+    private AsyncClockEventDelegate mAsyncClockEventDelegate;
+
     /**
      * If Launcher restarted while in the middle of an Overview split select, it needs this data to
      * recover. In all other cases this will remain null.
@@ -345,9 +350,7 @@
         mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
         RunnableList result = super.startActivitySafely(v, intent, item);
         if (result == null) {
-            if (getTaskbarUIController() == null) {
-                mHotseatPredictionController.setPauseUIUpdate(false);
-            }
+            mHotseatPredictionController.setPauseUIUpdate(false);
         } else {
             result.add(() -> mHotseatPredictionController.setPauseUIUpdate(false));
         }
@@ -483,6 +486,10 @@
             mSplitSelectStateController.onDestroy();
         }
 
+        if (mAsyncClockEventDelegate != null) {
+            mAsyncClockEventDelegate.onDestroy();
+        }
+
         super.onDestroy();
         mHotseatPredictionController.destroy();
         mSplitWithKeyboardShortcutController.onDestroy();
@@ -693,6 +700,14 @@
         }
 
         super.onPause();
+
+        if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+            // If Launcher pauses before both split apps are selected, exit split screen.
+            if (!mSplitSelectStateController.isBothSplitAppsConfirmed()) {
+                mSplitSelectStateController.getSplitAnimationController()
+                        .playPlaceholderDismissAnim(this);
+            }
+        }
     }
 
     @Override
@@ -739,15 +754,6 @@
     }
 
     @Override
-    protected void onScreenOnChanged(boolean isOn) {
-        super.onScreenOnChanged(isOn);
-        if (!isOn) {
-            RecentsView recentsView = getOverviewPanel();
-            recentsView.finishRecentsAnimation(true /* toRecents */, null);
-        }
-    }
-
-    @Override
     public void onAllAppsTransition(float progress) {
         super.onAllAppsTransition(progress);
         onTaskbarInAppDisplayProgressUpdate(progress, ALL_APPS_PAGE_PROGRESS_INDEX);
@@ -1254,10 +1260,8 @@
                 /* callback= */ success -> mSplitSelectStateController.resetState(),
                 /* freezeTaskList= */ true,
                 groupTask.mSplitBounds == null
-                        ? DEFAULT_SPLIT_RATIO
-                        : groupTask.mSplitBounds.appsStackedVertically
-                                ? groupTask.mSplitBounds.topTaskPercent
-                                : groupTask.mSplitBounds.leftTaskPercent);
+                        ? SNAP_TO_50_50
+                        : groupTask.mSplitBounds.snapPosition);
     }
 
     /**
@@ -1272,6 +1276,16 @@
         return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely();
     }
 
+    @Override
+    public boolean isBubbleBarEnabled() {
+        return (mTaskbarUIController != null && mTaskbarUIController.isBubbleBarEnabled());
+    }
+
+    @Override
+    public boolean hasBubbles() {
+        return (mTaskbarUIController != null && mTaskbarUIController.hasBubbles());
+    }
+
     private static final class LauncherTaskViewController extends
             TaskViewTouchController<Launcher> {
 
@@ -1311,5 +1325,31 @@
         if (mAppTransitionManager != null) {
             mAppTransitionManager.dump(prefix + "\t" + RING_APPEAR_ANIMATION_PREFIX, writer);
         }
+        if (mHotseatPredictionController != null) {
+            mHotseatPredictionController.dump(prefix, writer);
+        }
+    }
+
+    @Override
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        switch (name) {
+            case "TextClock", "android.widget.TextClock" -> {
+                TextClock tc = new TextClock(context, attrs);
+                if (mAsyncClockEventDelegate == null) {
+                    mAsyncClockEventDelegate = new AsyncClockEventDelegate(this);
+                }
+                tc.setClockEventDelegate(mAsyncClockEventDelegate);
+                return tc;
+            }
+            case "AnalogClock", "android.widget.AnalogClock" -> {
+                AnalogClock ac = new AnalogClock(context, attrs);
+                if (mAsyncClockEventDelegate == null) {
+                    mAsyncClockEventDelegate = new AsyncClockEventDelegate(this);
+                }
+                ac.setClockEventDelegate(mAsyncClockEventDelegate);
+                return ac;
+            }
+        }
+        return super.onCreateView(parent, name, context, attrs);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index f7bef03..f66bc60 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -37,6 +37,7 @@
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.LauncherWidgetHolder;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -237,6 +238,14 @@
     @Override
     public LauncherAppWidgetHostView createView(@NonNull Context context, int appWidgetId,
             @NonNull LauncherAppWidgetProviderInfo appWidget) {
+
+        if (appWidget.isCustomWidget()) {
+            LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
+            lahv.setAppWidget(appWidgetId, appWidget);
+            CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
+            return lahv;
+        }
+
         LauncherAppWidgetHostView widgetView = getPendingView(appWidgetId);
         if (widgetView != null) {
             removePendingView(appWidgetId);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index b9221ee..3e7d45e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -72,7 +72,7 @@
 
     @Override
     public boolean isTaskbarStashed(Launcher launcher) {
-        if (FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+        if (FeatureFlags.enableGridOnlyOverview()) {
             return true;
         }
         return super.isTaskbarStashed(launcher);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 6651c73..e7b285a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -55,14 +55,12 @@
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 
 import android.animation.ValueAnimator;
-import android.util.Log;
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.AllAppsSwipeController;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.DisplayController;
@@ -96,8 +94,6 @@
     @Override
     public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
             StateAnimationConfig config) {
-        Log.d(TestProtocol.OVERVIEW_OVER_HOME, "creating animation fromState: "
-                + fromState + " toState: " + toState);
         RecentsView overview = mActivity.getOverviewPanel();
         if ((fromState == OVERVIEW || fromState == OVERVIEW_SPLIT_SELECT) && toState == NORMAL) {
             overview.switchToScreenshot(() ->
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 9b8dd5f..ca4f0ea 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1189,7 +1189,8 @@
                 break;
         }
         ActiveGestureLog.INSTANCE.addLog(
-                /* event= */ "onSettledOnEndTarget " + endTarget,
+                new ActiveGestureLog.CompoundString("onSettledOnEndTarget ")
+                        .append(endTarget.name()),
                 /* gestureEvent= */ ON_SETTLED_ON_END_TARGET);
     }
 
@@ -2215,7 +2216,8 @@
             TaskView nextTask = mRecentsView == null ? null : mRecentsView.getNextPageTaskView();
             if (nextTask != null) {
                 int[] taskIds = nextTask.getTaskIds();
-                StringBuilder nextTaskLog = new StringBuilder();
+                ActiveGestureLog.CompoundString nextTaskLog = new ActiveGestureLog.CompoundString(
+                        "Launching task: ");
                 for (TaskIdAttributeContainer c : nextTask.getTaskIdAttributeContainers()) {
                     if (c == null) {
                         continue;
@@ -2234,7 +2236,7 @@
                 if (!hasTaskPreviouslyAppeared) {
                     ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED);
                 }
-                ActiveGestureLog.INSTANCE.addLog("Launching task: " + nextTaskLog);
+                ActiveGestureLog.INSTANCE.addLog(nextTaskLog);
                 nextTask.launchTask(success -> {
                     resultCallback.accept(success);
                     if (success) {
@@ -2310,9 +2312,12 @@
                 // previous quickswitch task launch, then cancel the animation back to the app
                 RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
                 TaskInfo taskInfo = appearedTaskTarget.taskInfo;
-                ActiveGestureLog.INSTANCE.addLog("Unexpected task appeared"
-                        + " id=" + taskInfo.taskId
-                        + " pkg=" + taskInfo.baseIntent.getComponent().getPackageName());
+                ActiveGestureLog.INSTANCE.addLog(
+                        new ActiveGestureLog.CompoundString("Unexpected task appeared")
+                                .append(" id=")
+                                .append(taskInfo.taskId)
+                                .append(" pkg=")
+                                .append(taskInfo.baseIntent.getComponent().getPackageName()));
                 finishRecentsAnimationOnTasksAppeared(null /* onFinishComplete */);
             } else if (handleTaskAppeared(appearedTaskTargets)) {
                 Optional<RemoteAnimationTarget> taskTargetOptional =
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 5a9d80d..3f88139 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -239,7 +239,7 @@
     public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
             PagedOrientationHandler orientedState) {
         if (dp.isTablet) {
-            if (FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+            if (FeatureFlags.enableGridOnlyOverview()) {
                 calculateGridTaskSize(context, dp, outRect, orientedState);
             } else {
                 calculateFocusTaskSize(context, dp, outRect);
@@ -336,7 +336,7 @@
             PagedOrientationHandler orientedState) {
         Resources res = context.getResources();
         Rect potentialTaskRect = new Rect();
-        if (FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+        if (FeatureFlags.enableGridOnlyOverview()) {
             calculateGridSize(dp, potentialTaskRect);
         } else {
             calculateFocusTaskSize(context, dp, potentialTaskRect);
@@ -368,7 +368,7 @@
     public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect,
             PagedOrientationHandler orientedState) {
         calculateTaskSize(context, dp, outRect, orientedState);
-        boolean isGridOnlyOverview = dp.isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get();
+        boolean isGridOnlyOverview = dp.isTablet && FeatureFlags.enableGridOnlyOverview();
         int claimedSpaceBelow = isGridOnlyOverview
                 ? dp.overviewActionsTopMarginPx + dp.overviewActionsHeight + dp.stashedTaskbarHeight
                 : (dp.heightPx - outRect.bottom - dp.getInsets().bottom);
@@ -437,11 +437,13 @@
     }
 
     protected void runOnInitBackgroundStateUI(Runnable callback) {
-        mOnInitBackgroundStateUICallback = callback;
         ACTIVITY_TYPE activity = getCreatedActivity();
         if (activity != null && activity.getStateManager().getState() == mBackgroundState) {
+            callback.run();
             onInitBackgroundStateUI();
+            return;
         }
+        mOnInitBackgroundStateUICallback = callback;
     }
 
     private void onInitBackgroundStateUI() {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index c2d8c62..49ec85e 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -410,7 +410,8 @@
         mEndTarget = target;
         mStateCallback.setState(STATE_END_TARGET_SET);
         ActiveGestureLog.INSTANCE.addLog(
-                /* event= */ "setEndTarget " + mEndTarget,
+                new ActiveGestureLog.CompoundString("setEndTarget ")
+                        .append(mEndTarget.name()),
                 /* gestureEvent= */ SET_END_TARGET);
         switch (mEndTarget) {
             case HOME:
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 857c831..1f8ddf0 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -404,12 +404,16 @@
         AbstractFloatingView.closeAllOpenViewsExcept(mLauncher, false, TYPE_REBIND_SAFE);
         float cornerRadius = Utilities.mapRange(
                 mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
+        final RectF resolveRectF = new RectF();
+        mQuickstepTransitionManager.transferRectToTargetCoordinate(
+                mBackTarget, mCurrentRect, true, resolveRectF);
+
         Pair<RectFSpringAnim, AnimatorSet> pair =
                 mQuickstepTransitionManager.createWallpaperOpenAnimations(
                     new RemoteAnimationTarget[]{mBackTarget},
                     new RemoteAnimationTarget[0],
                     false /* fromUnlock */,
-                    mCurrentRect,
+                    resolveRectF,
                     cornerRadius,
                     mBackInProgress /* fromPredictiveBack */);
         startTransitionAnimations(pair.first, pair.second);
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 42bf1ac..833bf5f 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -78,6 +78,14 @@
      */
     private int mTaskFocusIndexOverride = -1;
 
+    /**
+     * Whether we should incoming toggle commands while a previous toggle command is still ongoing.
+     * This serves as a rate-limiter to prevent overlapping animations that can clobber each other
+     * and prevent clean-up callbacks from running. This thus prevents a recurring set of bugs with
+     * janky recents animations and unresponsive home and overview buttons.
+     */
+    private boolean mWaitForToggleCommandComplete = false;
+
     public OverviewCommandHelper(TouchInteractionService service,
             OverviewComponentObserver observer,
             TaskAnimationManager taskAnimationManager) {
@@ -160,15 +168,20 @@
     private boolean launchTask(RecentsView recents, @Nullable TaskView taskView, CommandInfo cmd) {
         RunnableList callbackList = null;
         if (taskView != null) {
+            mWaitForToggleCommandComplete = true;
             taskView.setEndQuickswitchCuj(true);
             callbackList = taskView.launchTasks();
         }
 
         if (callbackList != null) {
-            callbackList.add(() -> scheduleNextTask(cmd));
+            callbackList.add(() -> {
+                scheduleNextTask(cmd);
+                mWaitForToggleCommandComplete = false;
+            });
             return false;
         } else {
             recents.startHome();
+            mWaitForToggleCommandComplete = false;
             return true;
         }
     }
@@ -178,6 +191,9 @@
      * task is deferred until {@link #scheduleNextTask} is called
      */
     private <T extends StatefulActivity<?>> boolean executeCommand(CommandInfo cmd) {
+        if (mWaitForToggleCommandComplete && cmd.type == TYPE_TOGGLE) {
+            return true;
+        }
         BaseActivityInterface<?, T> activityInterface =
                 mOverviewComponentObserver.getActivityInterface();
         RecentsView recents = activityInterface.getVisibleRecentsView();
@@ -359,6 +375,7 @@
             pw.println("    pendingCommandType=" + mPendingCommands.get(0).type);
         }
         pw.println("  mTaskFocusIndexOverride=" + mTaskFocusIndexOverride);
+        pw.println("  mWaitForToggleCommandComplete=" + mWaitForToggleCommandComplete);
     }
 
     private static class CommandInfo {
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 031d409..9be9294 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -44,9 +44,7 @@
             }
 
             case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
-                final float swipeHeight =
-                        LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile,
-                                PagedOrientationHandler.PORTRAIT);
+                final float swipeHeight = mDeviceProfile.heightPx / 2f;
                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
                 return response;
             }
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 1c74fbe..0a7344a 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -17,6 +17,7 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
 import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
 import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
 
@@ -39,7 +40,6 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.wm.shell.recents.IRecentTasksListener;
 import com.android.wm.shell.util.GroupedRecentTaskInfo;
-import com.android.wm.shell.util.SplitBounds;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -293,7 +293,7 @@
                 task2.setLastSnapshotData(taskInfo2);
             }
             final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
-                    convertSplitBounds(rawTask.getSplitBounds());
+                    convertShellSplitBoundsToLauncher(rawTask.getSplitBounds());
             allTasks.add(new GroupTask(task1, task2, launcherSplitBounds));
         }
 
@@ -313,15 +313,6 @@
         return new DesktopTask(tasks);
     }
 
-    private SplitConfigurationOptions.SplitBounds convertSplitBounds(
-            SplitBounds shellSplitBounds) {
-        return shellSplitBounds == null ?
-                null :
-                new SplitConfigurationOptions.SplitBounds(
-                        shellSplitBounds.leftTopBounds, shellSplitBounds.rightBottomBounds,
-                        shellSplitBounds.leftTopTaskId, shellSplitBounds.rightBottomTaskId);
-    }
-
     private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
         ArrayList<GroupTask> newTasks = new ArrayList<>();
         for (int i = 0; i < tasks.size(); i++) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index d300cc5..3d332c2 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -19,8 +19,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_CANCEL_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_FINISH_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_START_RECENTS_ANIMATION;
 
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -112,7 +113,7 @@
             ActiveGestureLog.INSTANCE.addLog(
                     /* event= */ "RecentsAnimationCallbacks.onAnimationStart (canceled)",
                     /* extras= */ 0,
-                    /* gestureEvent= */ START_RECENTS_ANIMATION);
+                    /* gestureEvent= */ ON_START_RECENTS_ANIMATION);
             notifyAnimationCanceled();
             animationController.finish(false /* toHome */, false /* sendUserLeaveHint */);
             return;
@@ -138,13 +139,14 @@
                 nonAppTargets = new RemoteAnimationTarget[0];
             }
             final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
-                    wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds);
+                    wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds,
+                    extras);
 
             Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
                 ActiveGestureLog.INSTANCE.addLog(
                         /* event= */ "RecentsAnimationCallbacks.onAnimationStart",
                         /* extras= */ targets.apps.length,
-                        /* gestureEvent= */ START_RECENTS_ANIMATION);
+                        /* gestureEvent= */ ON_START_RECENTS_ANIMATION);
                 for (RecentsAnimationListener listener : getListeners()) {
                     listener.onRecentsAnimationStart(mController, targets);
                 }
@@ -158,7 +160,7 @@
         Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
             ActiveGestureLog.INSTANCE.addLog(
                     /* event= */ "RecentsAnimationCallbacks.onAnimationCanceled",
-                    /* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
+                    /* gestureEvent= */ ON_CANCEL_RECENTS_ANIMATION);
             for (RecentsAnimationListener listener : getListeners()) {
                 listener.onRecentsAnimationCanceled(thumbnailDatas);
             }
@@ -192,7 +194,8 @@
     private final void onAnimationFinished(RecentsAnimationController controller) {
         Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
             ActiveGestureLog.INSTANCE.addLog(
-                    /* event= */ "RecentsAnimationCallbacks.onAnimationFinished");
+                    /* event= */ "RecentsAnimationCallbacks.onAnimationFinished",
+                    ON_FINISH_RECENTS_ANIMATION);
             for (RecentsAnimationListener listener : getListeners()) {
                 listener.onRecentsAnimationFinished(controller);
             }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 15e1365..67c56e3 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -20,6 +20,7 @@
 
 import android.app.WindowConfiguration;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.view.RemoteAnimationTarget;
 
 import com.android.quickstep.views.DesktopTaskView;
@@ -35,8 +36,8 @@
 
     public RecentsAnimationTargets(RemoteAnimationTarget[] apps,
             RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
-            Rect homeContentInsets, Rect minimizedHomeBounds) {
-        super(apps, wallpapers, nonApps, MODE_CLOSING);
+            Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras) {
+        super(apps, wallpapers, nonApps, MODE_CLOSING, extras);
         this.homeContentInsets = homeContentInsets;
         this.minimizedHomeBounds = minimizedHomeBounds;
     }
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 80aaad0..e0c7403 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 
+import android.os.Bundle;
 import android.view.RemoteAnimationTarget;
 
 import java.util.ArrayList;
@@ -35,6 +36,7 @@
     public final RemoteAnimationTarget[] apps;
     public final RemoteAnimationTarget[] wallpapers;
     public final RemoteAnimationTarget[] nonApps;
+    public final Bundle extras;
     public final int targetMode;
     public final boolean hasRecents;
 
@@ -42,7 +44,7 @@
 
     public RemoteAnimationTargets(RemoteAnimationTarget[] apps,
             RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
-            int targetMode) {
+            int targetMode, Bundle extras) {
         ArrayList<RemoteAnimationTarget> filteredApps = new ArrayList<>();
         boolean hasRecents = false;
         if (apps != null) {
@@ -61,6 +63,13 @@
         this.targetMode = targetMode;
         this.hasRecents = hasRecents;
         this.nonApps = nonApps;
+        this.extras = extras;
+    }
+
+    public RemoteAnimationTargets(RemoteAnimationTarget[] apps,
+            RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+            int targetMode) {
+        this(apps, wallpapers, nonApps, targetMode, new Bundle());
     }
 
     public RemoteAnimationTarget findTask(int taskId) {
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 84246e9..3af5ab7 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -16,7 +16,9 @@
 
 package com.android.quickstep;
 
-import android.app.WindowConfiguration;
+import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
+import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.Log;
@@ -24,11 +26,12 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.views.DesktopTaskView;
+import com.android.wm.shell.util.SplitBounds;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -43,7 +46,7 @@
     private static final int DEFAULT_NUM_HANDLES = 2;
 
     private RemoteTargetHandle[] mRemoteTargetHandles;
-    private SplitBounds mSplitBounds;
+    private SplitConfigurationOptions.SplitBounds mSplitBounds;
 
     /**
      * Use this constructor if remote targets are split-screen independent
@@ -111,6 +114,16 @@
     }
 
     /**
+     * Calls {@link #assignTargetsForSplitScreen(RemoteAnimationTargets)} with SplitBounds
+     * information specified.
+     */
+    public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
+            SplitConfigurationOptions.SplitBounds splitBounds) {
+        mSplitBounds = splitBounds;
+        return assignTargetsForSplitScreen(targets);
+    }
+
+    /**
      * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this assigns the
      * apps in {@code targets.apps} to the {@link #mRemoteTargetHandles} with index 0 will being
      * the left/top task, index 1 right/bottom.
@@ -129,10 +142,17 @@
             mRemoteTargetHandles = newHandles;
         }
 
-        boolean containsSplitTargets = Arrays.stream(targets.apps)
-                .anyMatch(remoteAnimationTarget ->
-                        remoteAnimationTarget.windowConfiguration.getWindowingMode()
-                                == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+        // If we are in a true split screen case (2 apps running on screen), either:
+        //     a) mSplitBounds was already set (from the clicked GroupedTaskView)
+        //     b) A SplitBounds was passed up from shell (via AbsSwipeUpHandler)
+        // If both of these are null, we are in a 1-app or 1-app-plus-assistant case.
+        if (mSplitBounds == null) {
+            SplitBounds shellSplitBounds = targets.extras.getParcelable(KEY_EXTRA_SPLIT_BOUNDS,
+                    SplitBounds.class);
+            mSplitBounds = convertShellSplitBoundsToLauncher(shellSplitBounds);
+        }
+
+        boolean containsSplitTargets = mSplitBounds != null;
         Log.d(TAG, "containsSplitTargets? " + containsSplitTargets + " handleLength: " +
                 mRemoteTargetHandles.length + " appsLength: " + targets.apps.length);
 
@@ -154,40 +174,12 @@
             }
         } else {
             // Split apps (+ maybe assistant)
-            RemoteAnimationTarget topLeftTarget = Arrays.stream(targets.apps)
-                    .filter(remoteAnimationTarget ->
-                            remoteAnimationTarget.windowConfiguration.getWindowingMode()
-                                    == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
-                    .findFirst().get();
-
-            // Fetch the adjacent target for split screen.
-            RemoteAnimationTarget bottomRightTarget = null;
-            for (int i = 0; i < targets.apps.length; i++) {
-                final RemoteAnimationTarget target = targets.apps[i];
-                if (target.windowConfiguration.getWindowingMode() !=
-                        WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW ||
-                        target == topLeftTarget) {
-                    continue;
-                }
-                Rect topLeftBounds = getStartBounds(topLeftTarget);
-                Rect bounds = getStartBounds(target);
-                if (topLeftBounds.left > bounds.right || topLeftBounds.top > bounds.bottom) {
-                    bottomRightTarget = topLeftTarget;
-                    topLeftTarget = target;
-                    break;
-                } else if (topLeftBounds.right < bounds.left || topLeftBounds.bottom < bounds.top) {
-                    bottomRightTarget = target;
-                    break;
-                }
-            }
+            RemoteAnimationTarget topLeftTarget = targets.findTask(mSplitBounds.leftTopTaskId);
+            RemoteAnimationTarget bottomRightTarget = targets.findTask(
+                    mSplitBounds.rightBottomTaskId);
 
             // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
             // vice versa
-            mSplitBounds = new SplitBounds(
-                    getStartBounds(topLeftTarget),
-                    getStartBounds(bottomRightTarget),
-                    topLeftTarget.taskId,
-                    bottomRightTarget.taskId);
             mRemoteTargetHandles[0].mTransformParams.setTargetSet(
                     createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
             mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
@@ -291,7 +283,7 @@
         return mRemoteTargetHandles;
     }
 
-    public SplitBounds getSplitBounds() {
+    public SplitConfigurationOptions.SplitBounds getSplitBounds() {
         return mSplitBounds;
     }
 
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 99dd634..170847d 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -19,6 +19,8 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING;
 import static com.android.quickstep.util.LogUtils.splitFailureMessage;
 
 import android.app.ActivityManager;
@@ -31,6 +33,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
@@ -61,7 +64,7 @@
 import com.android.internal.view.AppearanceRegion;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.AssistUtils;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -75,6 +78,7 @@
 import com.android.wm.shell.back.IBackAnimation;
 import com.android.wm.shell.bubbles.IBubbles;
 import com.android.wm.shell.bubbles.IBubblesListener;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
 import com.android.wm.shell.desktopmode.IDesktopMode;
 import com.android.wm.shell.desktopmode.IDesktopTaskListener;
 import com.android.wm.shell.draganddrop.IDragAndDrop;
@@ -731,6 +735,18 @@
         }
     }
 
+    /**
+     * Tells SysUI to show user education relative to the reference point provided.
+     * @param position the bubble bar top center position in Screen coordinates.
+     */
+    public void showUserEducation(Point position) {
+        try {
+            mBubbles.showUserEducation(position.x, position.y);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed call showUserEducation");
+        }
+    }
+
     //
     // Splitscreen
     //
@@ -781,12 +797,12 @@
 
     /** Start multiple tasks in split-screen simultaneously. */
     public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
-            @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
+            @StagePosition int splitPosition, @SnapPosition int snapPosition,
             RemoteTransition remoteTransition, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition,
-                        splitRatio, remoteTransition, instanceId);
+                        snapPosition, remoteTransition, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, splitFailureMessage("startTasks", "RemoteException"), e);
             }
@@ -794,12 +810,13 @@
     }
 
     public void startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1,
-            int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
-            float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
+            int taskId, Bundle options2, @StagePosition int splitPosition,
+            @SnapPosition int snapPosition, RemoteTransition remoteTransition,
+            InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2,
-                        splitPosition, splitRatio, remoteTransition, instanceId);
+                        splitPosition, snapPosition, remoteTransition, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, splitFailureMessage("startIntentAndTask", "RemoteException"), e);
             }
@@ -809,13 +826,13 @@
     public void startIntents(PendingIntent pendingIntent1, int userId1,
             @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2,
             int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2,
-            @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
+            @StagePosition int splitPosition, @SnapPosition int snapPosition,
             RemoteTransition remoteTransition, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startIntents(pendingIntent1, userId1, shortcutInfo1, options1,
-                        pendingIntent2, userId2, shortcutInfo2, options2, splitPosition, splitRatio,
-                        remoteTransition, instanceId);
+                        pendingIntent2, userId2, shortcutInfo2, options2, splitPosition,
+                        snapPosition, remoteTransition, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, splitFailureMessage("startIntents", "RemoteException"), e);
             }
@@ -823,12 +840,12 @@
     }
 
     public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId,
-            Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
-            float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
+            Bundle options2, @StagePosition int splitPosition, @SnapPosition int snapPosition,
+            RemoteTransition remoteTransition, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
-                        splitPosition, splitRatio, remoteTransition, instanceId);
+                        splitPosition, snapPosition, remoteTransition, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, splitFailureMessage("startShortcutAndTask", "RemoteException"), e);
             }
@@ -839,12 +856,12 @@
      * Start multiple tasks in split-screen simultaneously.
      */
     public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2,
-            Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
-            float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            Bundle options2, @StagePosition int splitPosition, @SnapPosition int snapPosition,
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2,
-                        splitPosition, splitRatio, adapter, instanceId);
+                        splitPosition, snapPosition, adapter, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, splitFailureMessage(
                         "startTasksWithLegacyTransition", "RemoteException"), e);
@@ -853,13 +870,13 @@
     }
 
     public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
-            Bundle options1, int taskId, Bundle options2,
-            @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
-            RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            Bundle options1, int taskId, Bundle options2, @StagePosition int splitPosition,
+            @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, userId1,
-                        options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
+                        options1, taskId, options2, splitPosition, snapPosition, adapter,
+                        instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, splitFailureMessage(
                         "startIntentAndTaskWithLegacyTransition", "RemoteException"), e);
@@ -868,12 +885,12 @@
     }
 
     public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1,
-            int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
-            float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            int taskId, Bundle options2, @StagePosition int splitPosition,
+            @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1,
-                        taskId, options2, splitPosition, splitRatio, adapter, instanceId);
+                        taskId, options2, splitPosition, snapPosition, adapter, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, splitFailureMessage(
                         "startShortcutAndTaskWithLegacyTransition", "RemoteException"), e);
@@ -888,13 +905,13 @@
     public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
             PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
-            @Nullable Bundle options2, @SplitConfigurationOptions.StagePosition int sidePosition,
-            float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            @Nullable Bundle options2, @StagePosition int sidePosition,
+            @SnapPosition int snapPosition, RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, userId1,
                         shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2,
-                        sidePosition, splitRatio, adapter, instanceId);
+                        sidePosition, snapPosition, adapter, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, splitFailureMessage(
                         "startIntentsWithLegacyTransition", "RemoteException"), e);
@@ -1359,6 +1376,7 @@
     public boolean startRecentsActivity(Intent intent, ActivityOptions options,
             RecentsAnimationListener listener) {
         if (mRecentTasks == null) {
+            ActiveGestureLog.INSTANCE.trackEvent(RECENT_TASKS_MISSING);
             return false;
         }
         final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 0de4ffc..f5a7ecc 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -201,8 +201,9 @@
                     RecentsView recentsView =
                             activityInterface.getCreatedActivity().getOverviewPanel();
                     if (recentsView != null) {
-                        ActiveGestureLog.INSTANCE.addLog("Launching side task id="
-                                + appearedTaskTarget.taskId);
+                        ActiveGestureLog.INSTANCE.addLog(
+                                new ActiveGestureLog.CompoundString("Launching side task id=")
+                                        .append(appearedTaskTarget.taskId));
                         recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId,
                                 appearedTaskTargets,
                                 new RemoteAnimationTarget[0] /* wallpaper */,
@@ -331,7 +332,7 @@
      * Finishes the running recents animation.
      * @param forceFinish will synchronously finish the controller
      */
-    private void finishRunningRecentsAnimation(boolean toHome, boolean forceFinish) {
+    public void finishRunningRecentsAnimation(boolean toHome, boolean forceFinish) {
         if (mController != null) {
             ActiveGestureLog.INSTANCE.addLog(
                     /* event= */ "finishRunningRecentsAnimation", toHome);
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 076f4b1..bd3ccb7 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -80,7 +80,7 @@
         boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
         boolean isTablet = activity.getDeviceProfile().isTablet;
 
-        boolean isGridOnlyOverview = isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get();
+        boolean isGridOnlyOverview = isTablet && FeatureFlags.enableGridOnlyOverview();
         // Add overview actions to the menu when in in-place rotate landscape mode, or in
         // grid-only overview.
         if ((!canLauncherRotate && isInLandscape) || isGridOnlyOverview) {
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 4177ced..2adc790 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -30,6 +30,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.View;
 import android.view.WindowInsets;
@@ -345,7 +346,9 @@
         }
 
         private boolean isAvailable(BaseDraggingActivity activity, int displayId) {
-            return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity)
+            return Settings.Global.getInt(
+                        activity.getContentResolver(),
+                        Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0
                     && !SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", false);
         }
     };
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 97e484a..34965df 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -191,7 +191,8 @@
             if (forDesktop) {
                 remoteTargetHandles = gluer.assignTargetsForDesktop(targets);
             } else if (v.containsMultipleTasks()) {
-                remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets);
+                remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets,
+                        ((GroupedTaskView) v).getSplitBoundsConfig());
             } else {
                 remoteTargetHandles = gluer.assignTargets(targets);
             }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index c1680de..4c3f738 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -28,6 +28,7 @@
 import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
 import static com.android.quickstep.GestureState.DEFAULT_STATE;
 import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType;
 import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER;
@@ -68,6 +69,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.InputDevice;
@@ -100,6 +102,7 @@
 import com.android.launcher3.util.LockedUserState;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.util.ScreenOnTracker;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
 import com.android.quickstep.inputconsumers.AssistantInputConsumer;
@@ -117,6 +120,7 @@
 import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.ActiveGestureLog.CompoundString;
+import com.android.quickstep.util.AssistStateManager;
 import com.android.quickstep.util.AssistUtils;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -449,6 +453,8 @@
     private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
             this::createFallbackSwipeHandler;
 
+    private final ScreenOnTracker.ScreenOnListener mScreenOnListener = this::onScreenOnChanged;
+
     private ActivityManagerWrapper mAM;
     private OverviewCommandHelper mOverviewCommandHelper;
     private OverviewComponentObserver mOverviewComponentObserver;
@@ -485,6 +491,8 @@
         LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
         mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
         sConnected = true;
+
+        ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
     }
 
     private void disposeEventHandlers(String reason) {
@@ -657,6 +665,8 @@
 
         mTaskbarManager.destroy();
         sConnected = false;
+
+        ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener);
         super.onDestroy();
     }
 
@@ -666,6 +676,17 @@
         return mTISBinder;
     }
 
+    protected void onScreenOnChanged(boolean isOn) {
+        if (isOn) {
+            return;
+        }
+        long currentTime = SystemClock.uptimeMillis();
+        MotionEvent cancelEvent = MotionEvent.obtain(
+                currentTime, currentTime, ACTION_CANCEL, 0f, 0f, 0);
+        onInputEvent(cancelEvent);
+        cancelEvent.recycle();
+    }
+
     private void onInputEvent(InputEvent ev) {
         if (!(ev instanceof MotionEvent)) {
             Log.e(TAG, "Unknown event " + ev);
@@ -729,29 +750,42 @@
         }
 
         if (mUncheckedConsumer != InputConsumer.NO_OP) {
-            switch (event.getActionMasked()) {
+            switch (action) {
                 case ACTION_DOWN:
                     // fall through
                 case ACTION_UP:
                     ActiveGestureLog.INSTANCE.addLog(
-                            /* event= */ "onMotionEvent(" + (int) event.getRawX() + ", "
-                                    + (int) event.getRawY() + "): "
-                                    + MotionEvent.actionToString(event.getActionMasked()) + ", "
-                                    + MotionEvent.classificationToString(event.getClassification()),
-                            /* gestureEvent= */ event.getActionMasked() == ACTION_DOWN
+                            new CompoundString("onMotionEvent(")
+                                    .append((int) event.getRawX())
+                                    .append(", ")
+                                    .append((int) event.getRawY())
+                                    .append("): ")
+                                    .append(MotionEvent.actionToString(action))
+                                    .append(", ")
+                                    .append(MotionEvent.classificationToString(
+                                            event.getClassification())),
+                            /* gestureEvent= */ action == ACTION_DOWN
                                     ? MOTION_DOWN
                                     : MOTION_UP);
                     break;
                 case ACTION_MOVE:
-                    ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
-                            + MotionEvent.actionToString(event.getActionMasked()) + ","
-                            + MotionEvent.classificationToString(event.getClassification())
-                            + ", pointerCount: " + event.getPointerCount(), MOTION_MOVE);
+                    ActiveGestureLog.INSTANCE.addLog(
+                            new CompoundString("onMotionEvent: ")
+                                    .append(MotionEvent.actionToString(action))
+                                    .append(",")
+                                    .append(MotionEvent.classificationToString(
+                                            event.getClassification()))
+                                    .append(", pointerCount: ")
+                                    .append(event.getPointerCount()),
+                            MOTION_MOVE);
                     break;
                 default: {
-                    ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
-                            + MotionEvent.actionToString(event.getActionMasked()) + ","
-                            + MotionEvent.classificationToString(event.getClassification()));
+                    ActiveGestureLog.INSTANCE.addLog(
+                            new CompoundString("onMotionEvent: ")
+                                    .append(MotionEvent.actionToString(action))
+                                    .append(",")
+                                    .append(MotionEvent.classificationToString(
+                                            event.getClassification())));
                 }
             }
         }
@@ -1210,7 +1244,9 @@
     }
 
     private void preloadOverview(boolean fromInit) {
+        Trace.beginSection("preloadOverview(fromInit=" + fromInit + ")");
         preloadOverview(fromInit, false);
+        Trace.endSection();
     }
 
     private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
@@ -1260,7 +1296,10 @@
             // We only care about the existing background activity.
             return;
         }
-        if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(),
+        Configuration oldConfig = activity.getResources().getConfiguration();
+        boolean isFoldUnfold = isTablet(oldConfig) != isTablet(newConfig);
+        if (!isFoldUnfold && mOverviewComponentObserver.canHandleConfigChanges(
+                activity.getComponentName(),
                 activity.getResources().getConfiguration().diff(newConfig))) {
             // Since navBar gestural height are different between portrait and landscape,
             // can handle orientation changes and refresh navigation gestural region through
@@ -1275,6 +1314,10 @@
         preloadOverview(false /* fromInit */);
     }
 
+    private static boolean isTablet(Configuration config) {
+        return config.smallestScreenWidthDp >= MIN_TABLET_WIDTH;
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
         // Dump everything
@@ -1310,6 +1353,8 @@
             createdOverviewActivity.getDeviceProfile().dump(this, "", pw);
         }
         mTaskbarManager.dumpLogs("", pw);
+        pw.println("AssistStateManager:");
+        AssistStateManager.INSTANCE.get(this).dump("  ", pw);
     }
 
     private AbsSwipeUpHandler createLauncherSwipeHandler(
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index a9accb7..0b11b00 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep.inputconsumers;
 
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
 import android.content.Context;
 import android.view.GestureDetector;
 import android.view.GestureDetector.SimpleOnGestureListener;
@@ -52,7 +54,7 @@
                     if (longPressRunnable != null) {
                         setActive(motionEvent);
 
-                        longPressRunnable.run();
+                        MAIN_EXECUTOR.post(longPressRunnable);
                     }
                 }
             }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 4c66504..7e61167 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -417,8 +417,9 @@
     private void finishTouchTracking(MotionEvent ev) {
         TraceHelper.INSTANCE.beginSection(UP_EVT);
 
+        boolean isCanceled = ev.getActionMasked() == ACTION_CANCEL;
         if (mPassedWindowMoveSlop && mInteractionHandler != null) {
-            if (ev.getActionMasked() == ACTION_CANCEL) {
+            if (isCanceled) {
                 mInteractionHandler.onGestureCancelled();
             } else {
                 mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
@@ -440,8 +441,10 @@
             if (mActiveCallbacks != null && mInteractionHandler != null) {
                 if (mTaskAnimationManager.isRecentsAnimationRunning()) {
                     // The animation started, but with no movement, in this case, there will be no
-                    // animateToProgress so we have to manually finish here.
-                    mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
+                    // animateToProgress so we have to manually finish here. In the case of
+                    // ACTION_CANCEL, someone else may be doing something so finish synchronously.
+                    mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */,
+                            isCanceled /* forceFinish */);
                 } else {
                     // The animation hasn't started yet, so insert a replacement handler into the
                     // callbacks which immediately finishes the animation after it starts.
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index 0e90e50..200b37d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -19,7 +19,7 @@
 
 import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
 import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
+import static com.android.launcher3.config.FeatureFlags.enableCursorHoverStates;
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING;
 
 import android.content.Context;
@@ -276,7 +276,7 @@
      */
     @Override
     public void onHoverEvent(MotionEvent ev) {
-        if (!ENABLE_CURSOR_HOVER_STATES.get() || mTaskbarActivityContext == null
+        if (!enableCursorHoverStates() || mTaskbarActivityContext == null
                 || !mTaskbarActivityContext.isTaskbarStashed()) {
             return;
         }
@@ -331,7 +331,7 @@
     private boolean isStashedTaskbarHovered(int x, int y) {
         if (!mTaskbarActivityContext.isTaskbarStashed()
                 || mTaskbarActivityContext.isTaskbarAllAppsOpen()
-                || !ENABLE_CURSOR_HOVER_STATES.get()) {
+                || !enableCursorHoverStates()) {
             return false;
         }
         DeviceProfile dp = mTaskbarActivityContext.getDeviceProfile();
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index be66637..b1daac4 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -87,6 +87,7 @@
     private static final String LATENCY_TAG = "StatsLatencyLog";
     private static final String IMPRESSION_TAG = "StatsImpressionLog";
     private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
+    private static final boolean DEBUG = !Utilities.isRunningInTestHarness();
     private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
     // LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
     // from nano to lite, bake constant to prevent robo test failure.
@@ -326,6 +327,11 @@
             if (!Utilities.ATLEAST_R) {
                 return;
             }
+            if (DEBUG) {
+                String name = (event instanceof Enum) ? ((Enum) event).name() :
+                        event.getId() + "";
+                Log.d(TAG, name);
+            }
             LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
 
             if (mSlice == null && mSliceItem != null) {
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 8335523..4d7a5bb 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -34,10 +34,11 @@
     public enum GestureEvent {
         MOTION_DOWN, MOTION_UP, MOTION_MOVE, SET_END_TARGET, SET_END_TARGET_HOME,
         SET_END_TARGET_NEW_TASK, SET_END_TARGET_ALL_APPS, ON_SETTLED_ON_END_TARGET,
+        ON_START_RECENTS_ANIMATION, ON_FINISH_RECENTS_ANIMATION, ON_CANCEL_RECENTS_ANIMATION,
         START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION,
         SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT,
         SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
-        FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED,
+        FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED, RECENT_TASKS_MISSING,
 
         /**
          * These GestureEvents are specifically associated to state flags that get set in
@@ -218,6 +219,40 @@
                                     + "set before/without startRecentsAnimation.",
                             writer);
                     break;
+                case RECENT_TASKS_MISSING:
+                    errorDetected |= printErrorIfTrue(
+                            true,
+                            prefix,
+                            /* errorMessage= */ "SystemUiProxy.mRecentTasks missing,"
+                                    + " couldn't start the recents activity",
+                            writer);
+                    break;
+                case ON_START_RECENTS_ANIMATION:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
+                            prefix,
+                            /* errorMessage= */ "ON_START_RECENTS_ANIMATION "
+                                    + "onAnimationStart callback ran before startRecentsAnimation",
+                            writer);
+                    break;
+                case ON_CANCEL_RECENTS_ANIMATION:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.ON_START_RECENTS_ANIMATION),
+                            prefix,
+                            /* errorMessage= */ "ON_CANCEL_RECENTS_ANIMATION "
+                                    + "onAnimationCanceled callback ran before onAnimationStart "
+                                    + "callback",
+                            writer);
+                    break;
+                case ON_FINISH_RECENTS_ANIMATION:
+                    errorDetected |= printErrorIfTrue(
+                            !encounteredEvents.contains(GestureEvent.ON_START_RECENTS_ANIMATION),
+                            prefix,
+                            /* errorMessage= */ "ON_FINISH_RECENTS_ANIMATION "
+                                    + "onAnimationFinished callback ran before onAnimationStart "
+                                    + "callback",
+                            writer);
+                    break;
                 case MOTION_DOWN:
                 case SET_END_TARGET:
                 case SET_END_TARGET_HOME:
@@ -349,6 +384,30 @@
                 /* errorMessage= */ "onTaskAppeared was expected to be called but wasn't.",
                 writer);
 
+        errorDetected |= printErrorIfTrue(
+                /* condition= */ encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION)
+                        && !encounteredEvents.contains(GestureEvent.ON_START_RECENTS_ANIMATION),
+                prefix,
+                /* errorMessage= */
+                "startRecentAnimation was called but onAnimationStart callback was not",
+                writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */
+                encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION)
+                        && !encounteredEvents.contains(GestureEvent.ON_FINISH_RECENTS_ANIMATION),
+                prefix,
+                /* errorMessage= */
+                "finishController was called but onAnimationFinished callback was not",
+                writer);
+        errorDetected |= printErrorIfTrue(
+                /* condition= */
+                encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION)
+                        && !encounteredEvents.contains(GestureEvent.ON_CANCEL_RECENTS_ANIMATION),
+                prefix,
+                /* errorMessage= */
+                "onRecentsAnimationCanceled was called but onAnimationCanceled was not",
+                writer);
+
         if (!errorDetected) {
             writer.println(prefix + "\tNo errors detected.");
         }
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
index cca4d52..d6a2e93 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -46,7 +46,7 @@
     private static final int TYPE_INTEGER = 2;
     private static final int TYPE_BOOL_TRUE = 3;
     private static final int TYPE_BOOL_FALSE = 4;
-    private static final int TYPE_INPUT_CONSUMER = 5;
+    private static final int TYPE_COMPOUND_STRING = 5;
     private static final int TYPE_GESTURE_EVENT = 6;
 
     private final EventLog[] logs;
@@ -81,7 +81,13 @@
     }
 
     public void addLog(CompoundString compoundString) {
-        addLog(TYPE_INPUT_CONSUMER, "", 0, compoundString, null);
+        addLog(TYPE_COMPOUND_STRING, "", 0, compoundString, null);
+    }
+
+    public void addLog(
+            CompoundString compoundString,
+            @Nullable ActiveGestureErrorDetector.GestureEvent gestureEvent) {
+        addLog(TYPE_COMPOUND_STRING, "", 0, compoundString, gestureEvent);
     }
 
     /**
@@ -185,7 +191,7 @@
                     case TYPE_INTEGER:
                         msg.append(": ").append((int) eventEntry.extras);
                         break;
-                    case TYPE_INPUT_CONSUMER:
+                    case TYPE_COMPOUND_STRING:
                         msg.append(eventEntry.mCompoundString);
                         break;
                     case TYPE_GESTURE_EVENT:
@@ -314,6 +320,15 @@
             return this;
         }
 
+        public CompoundString append(int num) {
+            if (mIsNoOp) {
+                return this;
+            }
+            mSubstrings.add(Integer.toString(num));
+
+            return this;
+        }
+
         @Override
         public String toString() {
             if (mIsNoOp) {
diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
new file mode 100644
index 0000000..d4923b8
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+import java.io.PrintWriter;
+
+/** Class to manage Assistant states. */
+public class AssistStateManager implements ResourceBasedOverride {
+
+    public static final MainThreadInitializedObject<AssistStateManager> INSTANCE =
+            forOverride(AssistStateManager.class, R.string.assist_state_manager_class);
+
+    public AssistStateManager() {}
+
+    /** Whether search is available. */
+    public boolean isSearchAvailable() {
+        return false;
+    }
+
+    /** Dump states. */
+    public void dump(String prefix, PrintWriter writer) {}
+}
diff --git a/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
new file mode 100644
index 0000000..0dee5b3
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static android.content.Intent.ACTION_TIMEZONE_CHANGED;
+import static android.content.Intent.ACTION_TIME_CHANGED;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.widget.TextClock.ClockEventDelegate;
+
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.SettingsCache.OnChangeListener;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Extension of {@link ClockEventDelegate} to support async event registration
+ */
+public class AsyncClockEventDelegate extends ClockEventDelegate implements OnChangeListener {
+
+    private final Context mContext;
+    private final SimpleBroadcastReceiver mReceiver =
+            new SimpleBroadcastReceiver(this::onClockEventReceived);
+
+    private final ArrayMap<BroadcastReceiver, Handler> mTimeEventReceivers = new ArrayMap<>();
+    private final List<ContentObserver> mFormatObservers = new ArrayList<>();
+    private final Uri mFormatUri = Settings.System.getUriFor(Settings.System.TIME_12_24);
+
+    private boolean mFormatRegistered = false;
+    private boolean mDestroyed = false;
+
+    public AsyncClockEventDelegate(Context context) {
+        super(context);
+        mContext = context;
+
+        UI_HELPER_EXECUTOR.execute(() ->
+                mReceiver.register(mContext, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED));
+    }
+
+    @Override
+    public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) {
+        synchronized (mTimeEventReceivers) {
+            mTimeEventReceivers.put(receiver, handler == null ? new Handler() : handler);
+        }
+    }
+
+    @Override
+    public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) {
+        synchronized (mTimeEventReceivers) {
+            mTimeEventReceivers.remove(receiver);
+        }
+    }
+
+    @Override
+    public void registerFormatChangeObserver(ContentObserver observer, int userHandle) {
+        synchronized (mFormatObservers) {
+            if (!mFormatRegistered && !mDestroyed) {
+                SettingsCache.INSTANCE.get(mContext).register(mFormatUri, this);
+                mFormatRegistered = true;
+            }
+            mFormatObservers.add(observer);
+        }
+    }
+
+    @Override
+    public void unregisterFormatChangeObserver(ContentObserver observer) {
+        synchronized (mFormatObservers) {
+            mFormatObservers.remove(observer);
+        }
+    }
+
+    @Override
+    public void onSettingsChanged(boolean isEnabled) {
+        if (mDestroyed) {
+            return;
+        }
+        synchronized (mFormatObservers) {
+            mFormatObservers.forEach(o -> o.dispatchChange(false, mFormatUri));
+        }
+    }
+    @WorkerThread
+    private void onClockEventReceived(Intent intent) {
+        if (mDestroyed) {
+            return;
+        }
+        synchronized (mReceiver) {
+            mTimeEventReceivers.forEach((r, h) -> h.post(() -> r.onReceive(mContext, intent)));
+        }
+    }
+
+    /**
+     * Unregisters all system callbacks and destroys this delegate
+     */
+    public void onDestroy() {
+        mDestroyed = true;
+        SettingsCache.INSTANCE.get(mContext).unregister(mFormatUri, this);
+        UI_HELPER_EXECUTOR.execute(() -> mReceiver.unregisterReceiverSafely(mContext));
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 4bc41bc..a8a96ce 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep.util;
 
+import static com.android.launcher3.testing.shared.TestProtocol.PAUSE_DETECTED_MESSAGE;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.Log;
@@ -201,7 +203,8 @@
             ActiveGestureLog.INSTANCE.addLog(logString);
             boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
             if (mIsPaused) {
-                AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
+                AccessibilityManagerCompat.sendTestProtocolEventToTest(mContext,
+                        PAUSE_DETECTED_MESSAGE);
                 mHasEverBeenPaused = true;
             }
             if (mOnMotionPauseListener != null) {
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt
new file mode 100644
index 0000000..596bb47
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util
+
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.wm.shell.util.SplitBounds
+
+class SplitScreenUtils {
+    companion object {
+        // TODO(b/254378592): Remove these methods when the two classes are reunited
+        /** Converts the shell version of SplitBounds to the launcher version */
+        @JvmStatic
+        fun convertShellSplitBoundsToLauncher(
+            shellSplitBounds: SplitBounds?
+        ): SplitConfigurationOptions.SplitBounds? {
+            return if (shellSplitBounds == null) {
+                null
+            } else {
+                SplitConfigurationOptions.SplitBounds(
+                    shellSplitBounds.leftTopBounds, shellSplitBounds.rightBottomBounds,
+                    shellSplitBounds.leftTopTaskId, shellSplitBounds.rightBottomTaskId,
+                    shellSplitBounds.snapPosition
+                )
+            }
+        }
+
+        /** Converts the launcher version of SplitBounds to the shell version */
+        @JvmStatic
+        fun convertLauncherSplitBoundsToShell(
+            launcherSplitBounds: SplitConfigurationOptions.SplitBounds?
+        ): SplitBounds? {
+            return if (launcherSplitBounds == null) {
+                null
+            } else {
+                SplitBounds(
+                    launcherSplitBounds.leftTopBounds,
+                    launcherSplitBounds.rightBottomBounds,
+                    launcherSplitBounds.leftTopTaskId,
+                    launcherSplitBounds.rightBottomTaskId,
+                    launcherSplitBounds.snapPosition
+                )
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
index 69c4197..95f1fbf 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
@@ -368,7 +368,8 @@
     }
 
     private fun isInitialTaskIntentSet(): Boolean {
-        return initialTaskId != INVALID_TASK_ID || initialIntent != null
+        return initialTaskId != INVALID_TASK_ID || initialIntent != null ||
+                initialPendingIntent != null
     }
 
     fun getInitialTaskId(): Int {
@@ -404,6 +405,7 @@
         secondUser = null
         initialIntent = null
         secondIntent = null
+        initialPendingIntent = null
         secondPendingIntent = null
         itemInfo = null
         splitEvent = null
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 16fe07d..c8831c7 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -22,7 +22,6 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_PENDINGINTENT_PENDINGINTENT;
 import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_PENDINGINTENT_TASK;
@@ -33,6 +32,7 @@
 import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_PENDINGINTENT;
 import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT;
 import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -98,6 +98,7 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
 import com.android.wm.shell.splitscreen.ISplitSelectListener;
 
 import java.io.PrintWriter;
@@ -288,11 +289,11 @@
      * To be called when the both split tasks are ready to be launched. Call after launcher side
      * animations are complete.
      */
-    public void launchSplitTasks(@Nullable Consumer<Boolean> callback) {
+    public void launchSplitTasks(@SnapPosition int snapPosition,
+            @Nullable Consumer<Boolean> callback) {
         Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
                 LogUtils.getShellShareableInstanceId();
-        launchTasks(callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
-                instanceIds.first);
+        launchTasks(callback, false /* freezeTaskList */, snapPosition, instanceIds.first);
 
         mStatsLogManager.logger()
                 .withItemInfo(mSplitSelectDataHolder.getItemInfo())
@@ -301,11 +302,18 @@
     }
 
     /**
-     * A version of {@link #launchTasks(Consumer, boolean, float, InstanceId)} with no success
-     * callback.
+     * A version of {@link #launchSplitTasks(int, Consumer)} that launches with default split ratio.
+     */
+    public void launchSplitTasks(@Nullable Consumer<Boolean> callback) {
+        launchSplitTasks(SNAP_TO_50_50, callback);
+    }
+
+    /**
+     * A version of {@link #launchSplitTasks(int, Consumer)} that launches with a default split
+     * ratio and no callback.
      */
     public void launchSplitTasks() {
-        launchSplitTasks(null);
+        launchSplitTasks(SNAP_TO_50_50, null);
     }
 
     /**
@@ -342,7 +350,7 @@
      *                   foreground (quickswitch, launching previous pairs from overview)
      */
     public void launchTasks(@Nullable Consumer<Boolean> callback, boolean freezeTaskList,
-            float splitRatio, @Nullable InstanceId shellInstanceId) {
+            @SnapPosition int snapPosition, @Nullable InstanceId shellInstanceId) {
         TestLogging.recordEvent(
                 TestProtocol.SEQUENCE_MAIN, "launchSplitTasks");
         final ActivityOptions options1 = ActivityOptions.makeBasic();
@@ -369,33 +377,33 @@
             switch (launchData.getSplitLaunchType()) {
                 case SPLIT_TASK_TASK ->
                         mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId,
-                                null /* options2 */, initialStagePosition, splitRatio,
+                                null /* options2 */, initialStagePosition, snapPosition,
                                 remoteTransition, shellInstanceId);
 
                 case SPLIT_TASK_PENDINGINTENT ->
                         mSystemUiProxy.startIntentAndTask(secondPI, secondUserId, optionsBundle,
-                                firstTaskId, null /*options2*/, initialStagePosition, splitRatio,
+                                firstTaskId, null /*options2*/, initialStagePosition, snapPosition,
                                 remoteTransition, shellInstanceId);
 
                 case SPLIT_TASK_SHORTCUT ->
                         mSystemUiProxy.startShortcutAndTask(secondShortcut, optionsBundle,
-                                firstTaskId, null /*options2*/, initialStagePosition, splitRatio,
+                                firstTaskId, null /*options2*/, initialStagePosition, snapPosition,
                                 remoteTransition, shellInstanceId);
 
                 case SPLIT_PENDINGINTENT_TASK ->
                         mSystemUiProxy.startIntentAndTask(firstPI, firstUserId, optionsBundle,
-                                secondTaskId, null /*options2*/, initialStagePosition, splitRatio,
+                                secondTaskId, null /*options2*/, initialStagePosition, snapPosition,
                                 remoteTransition, shellInstanceId);
 
                 case SPLIT_PENDINGINTENT_PENDINGINTENT ->
                         mSystemUiProxy.startIntents(firstPI, firstUserId, firstShortcut,
                                 optionsBundle, secondPI, secondUserId, secondShortcut,
-                                null /*options2*/, initialStagePosition, splitRatio,
+                                null /*options2*/, initialStagePosition, snapPosition,
                                 remoteTransition, shellInstanceId);
 
                 case SPLIT_SHORTCUT_TASK ->
                         mSystemUiProxy.startShortcutAndTask(firstShortcut, optionsBundle,
-                                secondTaskId, null /*options2*/, initialStagePosition, splitRatio,
+                                secondTaskId, null /*options2*/, initialStagePosition, snapPosition,
                                 remoteTransition, shellInstanceId);
             }
         } else {
@@ -405,40 +413,40 @@
                 case SPLIT_TASK_TASK ->
                         mSystemUiProxy.startTasksWithLegacyTransition(firstTaskId, optionsBundle,
                                 secondTaskId, null /* options2 */, initialStagePosition,
-                                splitRatio, adapter, shellInstanceId);
+                                snapPosition, adapter, shellInstanceId);
 
                 case SPLIT_TASK_PENDINGINTENT ->
                         mSystemUiProxy.startIntentAndTaskWithLegacyTransition(secondPI,
                                 secondUserId, optionsBundle, firstTaskId, null /*options2*/,
-                                initialStagePosition, splitRatio, adapter, shellInstanceId);
+                                initialStagePosition, snapPosition, adapter, shellInstanceId);
 
                 case SPLIT_TASK_SHORTCUT ->
                         mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(secondShortcut,
                                 optionsBundle, firstTaskId, null /*options2*/, initialStagePosition,
-                                splitRatio, adapter, shellInstanceId);
+                                snapPosition, adapter, shellInstanceId);
 
                 case SPLIT_PENDINGINTENT_TASK ->
                         mSystemUiProxy.startIntentAndTaskWithLegacyTransition(firstPI, firstUserId,
                                 optionsBundle, secondTaskId, null /*options2*/,
-                                initialStagePosition, splitRatio, adapter, shellInstanceId);
+                                initialStagePosition, snapPosition, adapter, shellInstanceId);
 
                 case SPLIT_PENDINGINTENT_PENDINGINTENT ->
                         mSystemUiProxy.startIntentsWithLegacyTransition(firstPI, firstUserId,
                                 firstShortcut, optionsBundle, secondPI, secondUserId,
-                                secondShortcut, null /*options2*/, initialStagePosition, splitRatio,
-                                adapter, shellInstanceId);
+                                secondShortcut, null /*options2*/, initialStagePosition,
+                                snapPosition, adapter, shellInstanceId);
 
                 case SPLIT_SHORTCUT_TASK ->
                         mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(firstShortcut,
                                 optionsBundle, secondTaskId, null /*options2*/,
-                                initialStagePosition, splitRatio, adapter, shellInstanceId);
+                                initialStagePosition, snapPosition, adapter, shellInstanceId);
             }
         }
     }
 
     /**
      * Used to launch split screen from a split pair that already exists (usually accessible through
-     * Overview). This is different than {@link #launchTasks(Consumer, boolean, float, InstanceId)}
+     * Overview). This is different than {@link #launchTasks(Consumer, boolean, int, InstanceId)}
      * in that this only launches split screen that are existing tasks. This doesn't determine which
      * API should be used (i.e. launching split with existing tasks vs intents vs shortcuts, etc).
      *
@@ -447,7 +455,7 @@
      */
     public void launchExistingSplitPair(@Nullable GroupedTaskView groupedTaskView,
             int firstTaskId, int secondTaskId, @StagePosition int stagePosition,
-            Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+            Consumer<Boolean> callback, boolean freezeTaskList, @SnapPosition int snapPosition) {
         mLaunchingTaskView = groupedTaskView;
         final ActivityOptions options1 = ActivityOptions.makeBasic();
         if (freezeTaskList) {
@@ -458,21 +466,20 @@
         if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
             final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId,
                     secondTaskId, callback, "LaunchExistingPair");
-            mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId,
-                    null /* options2 */, stagePosition, splitRatio,
-                    remoteTransition, null /*shellInstanceId*/);
+            mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, null /* options2 */,
+                    stagePosition, snapPosition, remoteTransition, null /*shellInstanceId*/);
         } else {
             final RemoteAnimationAdapter adapter = getLegacyRemoteAdapter(firstTaskId,
                     secondTaskId, callback);
-            mSystemUiProxy.startTasksWithLegacyTransition(firstTaskId, optionsBundle,
-                    secondTaskId, null /* options2 */, stagePosition,
-                    splitRatio, adapter, null /*shellInstanceId*/);
+            mSystemUiProxy.startTasksWithLegacyTransition(firstTaskId, optionsBundle, secondTaskId,
+                    null /* options2 */, stagePosition, snapPosition, adapter,
+                    null /*shellInstanceId*/);
         }
     }
 
     /**
      * Launches the initially selected task/intent in fullscreen (note the same SystemUi APIs are
-     * used as {@link #launchSplitTasks(Consumer)} because they are overloaded to launch both
+     * used as {@link #launchSplitTasks(int, Consumer)} because they are overloaded to launch both
      * split and fullscreen tasks)
      */
     public void launchInitialAppFullscreen(Consumer<Boolean> callback) {
@@ -497,14 +504,13 @@
             switch (launchData.getSplitLaunchType()) {
                 case SPLIT_SINGLE_TASK_FULLSCREEN -> mSystemUiProxy.startTasks(firstTaskId,
                         optionsBundle, secondTaskId, null /* options2 */, initialStagePosition,
-                        DEFAULT_SPLIT_RATIO, remoteTransition, instanceId);
+                        SNAP_TO_50_50, remoteTransition, instanceId);
                 case SPLIT_SINGLE_INTENT_FULLSCREEN -> mSystemUiProxy.startIntentAndTask(firstPI,
                         firstUserId, optionsBundle, secondTaskId, null /*options2*/,
-                        initialStagePosition, DEFAULT_SPLIT_RATIO, remoteTransition,
-                        instanceId);
+                        initialStagePosition, SNAP_TO_50_50, remoteTransition, instanceId);
                 case SPLIT_SINGLE_SHORTCUT_FULLSCREEN -> mSystemUiProxy.startShortcutAndTask(
                         initialShortcut, optionsBundle, firstTaskId, null /* options2 */,
-                        initialStagePosition, DEFAULT_SPLIT_RATIO, remoteTransition, instanceId);
+                        initialStagePosition, SNAP_TO_50_50, remoteTransition, instanceId);
             }
         } else {
             final RemoteAnimationAdapter adapter = getLegacyRemoteAdapter(firstTaskId,
@@ -512,16 +518,15 @@
             switch (launchData.getSplitLaunchType()) {
                 case SPLIT_SINGLE_TASK_FULLSCREEN -> mSystemUiProxy.startTasksWithLegacyTransition(
                         firstTaskId, optionsBundle, secondTaskId, null /* options2 */,
-                        initialStagePosition, DEFAULT_SPLIT_RATIO, adapter, instanceId);
+                        initialStagePosition, SNAP_TO_50_50, adapter, instanceId);
                 case SPLIT_SINGLE_INTENT_FULLSCREEN ->
                         mSystemUiProxy.startIntentAndTaskWithLegacyTransition(firstPI, firstUserId,
                                 optionsBundle, secondTaskId, null /*options2*/,
-                                initialStagePosition, DEFAULT_SPLIT_RATIO, adapter,
-                                instanceId);
+                                initialStagePosition, SNAP_TO_50_50, adapter, instanceId);
                 case SPLIT_SINGLE_SHORTCUT_FULLSCREEN ->
                         mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(
                                 initialShortcut, optionsBundle, firstTaskId, null /* options2 */,
-                                initialStagePosition, DEFAULT_SPLIT_RATIO, adapter, instanceId);
+                                initialStagePosition, SNAP_TO_50_50, adapter, instanceId);
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/util/SystemActionConstants.java b/quickstep/src/com/android/quickstep/util/SystemActionConstants.java
new file mode 100644
index 0000000..522930f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SystemActionConstants.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+/**
+ * Constants for registering SystemActions.
+ *
+ * Prefer to use AccessibilityService.GLOBAL_ACTION_* if applicable.
+ */
+public final class SystemActionConstants {
+
+    public static final int SYSTEM_ACTION_ID_TASKBAR = 499;
+    public static final int SYSTEM_ACTION_ID_SEARCH_SCREEN = 500;
+
+    /**
+     * For Taskbar broadcast intent filter.
+     */
+    public static final String ACTION_SHOW_TASKBAR = "ACTION_SHOW_TASKBAR";
+
+    /**
+     * For Search Screen broadcast intent filter.
+     */
+    public static final String ACTION_SEARCH_SCREEN = "ACTION_SEARCH_SCREEN";
+
+    private SystemActionConstants() {}
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java b/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java
index c22e0bc..d7b3431 100644
--- a/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java
+++ b/quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java
@@ -94,8 +94,10 @@
             final Runnable taskLaunchFailedCallback = mTaskLaunchFailedCallback;
             RecentsModel.INSTANCE.getNoCreate().isTaskRemoved(mLaunchedTaskId, (taskRemoved) -> {
                 if (taskRemoved) {
-                    ActiveGestureLog.INSTANCE.addLog("Launch failed, task (id=" + launchedTaskId
-                            + ") finished mid transition");
+                    ActiveGestureLog.INSTANCE.addLog(
+                            new ActiveGestureLog.CompoundString("Launch failed, task (id=")
+                                    .append(launchedTaskId)
+                                    .append(") finished mid transition"));
                     taskLaunchFailedCallback.run();
                 }
             }, (task) -> true /* filter */);
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 25b9bdc..510044d 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -26,6 +26,7 @@
 import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
 import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
 import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
+import static com.android.quickstep.util.SplitScreenUtils.convertLauncherSplitBoundsToShell;
 
 import android.animation.TimeInterpolator;
 import android.content.Context;
@@ -210,7 +211,8 @@
         mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
                 STAGE_POSITION_TOP_OR_LEFT :
                 STAGE_POSITION_BOTTOM_OR_RIGHT;
-        mPositionHelper.setSplitBounds(convertSplitBounds(mSplitBounds), mStagePosition);
+        mPositionHelper.setSplitBounds(convertLauncherSplitBoundsToShell(mSplitBounds),
+                mStagePosition);
     }
 
     /**
@@ -343,8 +345,7 @@
             boolean isRtlEnabled = !mIsRecentsRtl;
             mPositionHelper.updateThumbnailMatrix(
                     mThumbnailPosition, mThumbnailData, mTaskRect.width(), mTaskRect.height(),
-                    mDp.widthPx, mDp.heightPx, mDp.taskbarHeight, mDp.isTablet,
-                    mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
+                    mDp.isTablet, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
             mPositionHelper.getMatrix().invert(mInversePositionMatrix);
             if (DEBUG) {
                 Log.d(TAG, " taskRect: " + mTaskRect);
@@ -353,7 +354,7 @@
 
         float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1);
         mCurrentFullscreenParams.setProgress(fullScreenProgress, recentsViewScale.value,
-                /* taskViewScale= */1f, mTaskRect.width(), mDp, mPositionHelper);
+                /* taskViewScale= */1f);
 
         // Apply thumbnail matrix
         float taskWidth = mTaskRect.width();
@@ -436,16 +437,4 @@
         // Ideally we should use square-root. This is an optimization as one of the dimension is 0.
         return Math.max(Math.abs(mTempPoint[0]), Math.abs(mTempPoint[1]));
     }
-
-    /**
-     * TODO(b/254378592): Remove this after consolidation of classes
-     */
-    public static com.android.wm.shell.util.SplitBounds convertSplitBounds(SplitBounds bounds) {
-        return new com.android.wm.shell.util.SplitBounds(
-                bounds.leftTopBounds,
-                bounds.rightBottomBounds,
-                bounds.leftTopTaskId,
-                bounds.rightBottomTaskId
-        );
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 19ac1f8..cfb4d0d 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -16,7 +16,7 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW;
+import static com.android.launcher3.config.FeatureFlags.enableGridOnlyOverview;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -251,7 +251,7 @@
     private float getOriginalTranslationY() {
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
         if (deviceProfile.isTablet) {
-            if (ENABLE_GRID_ONLY_OVERVIEW.get()) {
+            if (enableGridOnlyOverview()) {
                 return (getRecentsView().getLastComputedTaskSize().height()
                         + deviceProfile.overviewTaskThumbnailTopMarginPx) / 2.0f
                         + deviceProfile.overviewRowSpacing;
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index b08e80f..5135345 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -499,8 +499,7 @@
         for (int i = 0; i < mSnapshotViewMap.size(); i++) {
             if (i == 0) {
                 // All snapshots share the same params. Only update it with the first snapshot.
-                updateFullscreenParams(mSnapshotDrawParams,
-                        mSnapshotView.getPreviewPositionHelper());
+                updateFullscreenParams(mSnapshotDrawParams);
             }
             mSnapshotViewMap.valueAt(i).setFullscreenParams(mSnapshotDrawParams);
         }
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 01f6ae8..7e58763 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -2,8 +2,8 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 
-import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.quickstep.util.SplitScreenUtils.convertLauncherSplitBoundsToShell;
 
 import android.content.Context;
 import android.graphics.PointF;
@@ -28,11 +28,11 @@
 import com.android.quickstep.util.CancellableTask;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.SplitSelectStateController;
-import com.android.quickstep.util.TaskViewSimulator;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
 
 import java.util.HashMap;
 import java.util.function.Consumer;
@@ -114,11 +114,11 @@
         if (mSplitBoundsConfig == null) {
             return;
         }
-        mSnapshotView.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
-                        .convertSplitBounds(splitBoundsConfig),
+        mSnapshotView.getPreviewPositionHelper().setSplitBounds(
+                convertLauncherSplitBoundsToShell(splitBoundsConfig),
                 PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT);
-        mSnapshotView2.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
-                        .convertSplitBounds(splitBoundsConfig),
+        mSnapshotView2.getPreviewPositionHelper().setSplitBounds(
+                convertLauncherSplitBoundsToShell(splitBoundsConfig),
                 PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT);
     }
 
@@ -180,12 +180,20 @@
         invalidate();
     }
 
-    public float getSplitRatio() {
-        if (mSplitBoundsConfig != null) {
-            return mSplitBoundsConfig.appsStackedVertically
-                    ? mSplitBoundsConfig.topTaskPercent : mSplitBoundsConfig.leftTaskPercent;
+    @Nullable
+    public SplitBounds getSplitBoundsConfig() {
+        return mSplitBoundsConfig;
+    }
+
+    /**
+     * Returns the {@link SnapPosition} of this pair of tasks.
+     */
+    public int getSnapPosition() {
+        if (mSplitBoundsConfig == null) {
+            throw new IllegalStateException("mSplitBoundsConfig is null");
         }
-        return DEFAULT_SPLIT_RATIO;
+
+        return mSplitBoundsConfig.snapPosition;
     }
 
     @Override
@@ -251,7 +259,7 @@
         getRecentsView().getSplitSelectController().launchExistingSplitPair(
                 launchingExistingTaskView ? this : null, mTask.key.id,
                 mSecondaryTask.key.id, SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
-                callback, isQuickswitch, getSplitRatio());
+                callback, isQuickswitch, getSnapPosition());
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index eb7598d..892ebf9 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -30,9 +30,7 @@
 import android.content.Context;
 import android.os.Build;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.MotionEvent;
-import android.view.Surface;
 
 import androidx.annotation.Nullable;
 
@@ -44,7 +42,6 @@
 import com.android.launcher3.statehandlers.DesktopVisibilityController;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.PendingSplitSelectInfo;
 import com.android.launcher3.util.SplitConfigurationOptions;
@@ -130,12 +127,18 @@
     @Override
     public void reset() {
         super.reset();
-        setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
+
+        int recentsActivityRotation = getPagedViewOrientedState().getRecentsActivityRotation();
+        setLayoutRotation(recentsActivityRotation, recentsActivityRotation);
     }
 
     @Override
     public void onStateTransitionStart(LauncherState toState) {
         setOverviewStateEnabled(toState.overviewUi);
+        if (toState.overviewUi) {
+            // If overview is enabled, we want to update at the start
+            updateOverviewStateForDesktop(true);
+        }
         setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
         setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
         if (toState == OVERVIEW_MODAL_TASK) {
@@ -162,13 +165,16 @@
             runActionOnRemoteHandles(remoteTargetHandle ->
                     remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
         }
+
+        if (!finalState.overviewUi) {
+            // If overview is disabled, we want to update at the end
+            updateOverviewStateForDesktop(false);
+        }
     }
 
     @Override
     public void setOverviewStateEnabled(boolean enabled) {
         super.setOverviewStateEnabled(enabled);
-        Log.d(TestProtocol.OVERVIEW_OVER_HOME, "overview state enabled state has changed: "
-                + enabled);
         if (enabled) {
             LauncherState state = mActivity.getStateManager().getState();
             boolean hasClearAllButton = (state.getVisibleElements(mActivity)
@@ -273,4 +279,11 @@
             SystemUiProxy.INSTANCE.get(mActivity).showDesktopApps(mActivity.getDisplayId());
         }
     }
+
+    private void updateOverviewStateForDesktop(boolean enabled) {
+        DesktopVisibilityController controller = mActivity.getDesktopVisibilityController();
+        if (controller != null) {
+            controller.setOverviewStateEnabled(enabled);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index b31791a..9141c99 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -289,7 +289,7 @@
             return 0;
         }
 
-        if (mDp.isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+        if (mDp.isTablet && FeatureFlags.enableGridOnlyOverview()) {
             return mDp.stashedTaskbarHeight;
         }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 7cf47ce..9b41ccd 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -42,11 +42,12 @@
 import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW;
+import static com.android.launcher3.config.FeatureFlags.enableGridOnlyOverview;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ACTIONS_SPLIT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
+import static com.android.launcher3.testing.shared.TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE;
 import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -1782,7 +1783,7 @@
                 newFocusedTaskView = getTaskViewAt(1);
             }
         }
-        mFocusedTaskViewId = newFocusedTaskView != null && !ENABLE_GRID_ONLY_OVERVIEW.get()
+        mFocusedTaskViewId = newFocusedTaskView != null && !enableGridOnlyOverview()
                 ? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID;
         updateTaskSize();
         if (newFocusedTaskView != null) {
@@ -1900,7 +1901,7 @@
      * Returns the number of tasks in the bottom row of the overview grid.
      */
     public int getBottomRowTaskCountForTablet() {
-        return getTaskViewCount() - mTopRowIdSet.size() - (ENABLE_GRID_ONLY_OVERVIEW.get() ? 0 : 1);
+        return getTaskViewCount() - mTopRowIdSet.size() - (enableGridOnlyOverview() ? 0 : 1);
     }
 
     protected void onTaskStackUpdated() {
@@ -1978,7 +1979,7 @@
         DeviceProfile dp = mActivity.getDeviceProfile();
         setOverviewGridEnabled(
                 mActivity.getStateManager().getState().displayOverviewTasksAsGrid(dp));
-        if (ENABLE_GRID_ONLY_OVERVIEW.get()) {
+        if (enableGridOnlyOverview()) {
             mActionsView.updateHiddenFlags(HIDDEN_ACTIONS_IN_MENU, dp.isTablet);
         }
         setPageSpacing(dp.overviewPageSpacing);
@@ -2140,7 +2141,7 @@
         Rect outRect = new Rect(mLastComputedTaskSize);
         outRect.offset(
                 -(primaryScroll - (selectedPageScroll + getOffsetFromScrollPosition(selectedPage))),
-                (int) (showAsGrid() && ENABLE_GRID_ONLY_OVERVIEW.get() && !isTopRow
+                (int) (showAsGrid() && enableGridOnlyOverview() && !isTopRow
                         ? mTopBottomRowHeightDiff : 0));
         return outRect;
     }
@@ -2726,7 +2727,7 @@
 
         boolean runningTaskTileHidden = mRunningTaskTileHidden;
         setCurrentTask(runningTaskViewId);
-        mFocusedTaskViewId = ENABLE_GRID_ONLY_OVERVIEW.get() ? INVALID_TASK_ID : runningTaskViewId;
+        mFocusedTaskViewId = enableGridOnlyOverview() ? INVALID_TASK_ID : runningTaskViewId;
         runOnPageScrollsInitialized(() -> setCurrentPage(getRunningTaskIndex()));
         setRunningTaskViewShowScreenshot(false);
         setRunningTaskHidden(runningTaskTileHidden);
@@ -2919,7 +2920,7 @@
                     // Desktop task view is hidden, skip it from grid calculations
                     continue;
                 }
-                if (!ENABLE_GRID_ONLY_OVERVIEW.get()) {
+                if (!enableGridOnlyOverview()) {
                     // Only apply x-translation when using legacy overview grid
                     gridTranslations[i] += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
                 }
@@ -3432,7 +3433,7 @@
 
             int topGridRowSize = mTopRowIdSet.size();
             int bottomGridRowSize = taskCount - mTopRowIdSet.size()
-                    - (ENABLE_GRID_ONLY_OVERVIEW.get() ? 0 : 1);
+                    - (enableGridOnlyOverview() ? 0 : 1);
             boolean topRowLonger = topGridRowSize > bottomGridRowSize;
             boolean bottomRowLonger = bottomGridRowSize > topGridRowSize;
             boolean dismissedTaskFromTop = mTopRowIdSet.contains(dismissedTaskViewId);
@@ -3445,7 +3446,7 @@
             }
             int longRowWidth = Math.max(topGridRowSize, bottomGridRowSize)
                     * (mLastComputedGridTaskSize.width() + mPageSpacing);
-            if (!ENABLE_GRID_ONLY_OVERVIEW.get() && !isStagingFocusedTask) {
+            if (!enableGridOnlyOverview() && !isStagingFocusedTask) {
                 longRowWidth += mLastComputedTaskSize.width() + mPageSpacing;
             }
 
@@ -3853,7 +3854,7 @@
                     } else {
                         // Update focus task and its size.
                         if (finalIsFocusedTaskDismissed && finalNextFocusedTaskView != null) {
-                            mFocusedTaskViewId = ENABLE_GRID_ONLY_OVERVIEW.get()
+                            mFocusedTaskViewId = enableGridOnlyOverview()
                                     ? INVALID_TASK_ID
                                     : finalNextFocusedTaskView.getTaskViewId();
                             mTopRowIdSet.remove(mFocusedTaskViewId);
@@ -4035,7 +4036,8 @@
     }
 
     protected void onDismissAnimationEnds() {
-        AccessibilityManagerCompat.sendDismissAnimationEndsEventToTest(getContext());
+        AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
+                DISMISS_ANIMATION_ENDS_MESSAGE);
     }
 
     public PendingAnimation createAllTasksDismissAnimation(long duration) {
@@ -4358,7 +4360,7 @@
         int midpoint = runningTask == null ? -1 : indexOfChild(runningTask);
         int modalMidpoint = getCurrentPage();
         boolean isModalGridWithoutFocusedTask =
-                showAsGrid && ENABLE_GRID_ONLY_OVERVIEW.get() && mTaskModalness > 0;
+                showAsGrid && enableGridOnlyOverview() && mTaskModalness > 0;
         if (isModalGridWithoutFocusedTask) {
             modalMidpoint = indexOfChild(mSelectedTask);
         }
@@ -4420,7 +4422,7 @@
                 redrawLiveTile();
             }
 
-            if (showAsGrid && ENABLE_GRID_ONLY_OVERVIEW.get() && child instanceof TaskView) {
+            if (showAsGrid && enableGridOnlyOverview() && child instanceof TaskView) {
                 float totalTranslationY = getVerticalOffsetSize(i, modalOffset);
                 FloatProperty translationPropertyY =
                         ((TaskView) child).getSecondaryTaskOffsetTranslationProperty();
@@ -4529,7 +4531,7 @@
      * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
      */
     private float getVerticalOffsetSize(int childIndex, float offsetProgress) {
-        if (offsetProgress == 0 || !(showAsGrid() && ENABLE_GRID_ONLY_OVERVIEW.get())
+        if (offsetProgress == 0 || !(showAsGrid() && enableGridOnlyOverview())
                 || mSelectedTask == null) {
             // Don't bother calculating everything below if we won't offset vertically.
             return 0;
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index f746203..dff0580 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -558,13 +558,12 @@
                     .getRecentsActivityRotation();
             boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
             mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
-                    getMeasuredWidth(), getMeasuredHeight(), dp.widthPx, dp.heightPx,
-                    dp.taskbarHeight, dp.isTablet, currentRotation, isRtl);
+                    getMeasuredWidth(), getMeasuredHeight(), dp.isTablet, currentRotation, isRtl);
 
             mBitmapShader.setLocalMatrix(mPreviewPositionHelper.getMatrix());
             mPaint.setShader(mBitmapShader);
         }
-        getTaskView().updateCurrentFullscreenParams(mPreviewPositionHelper);
+        getTaskView().updateCurrentFullscreenParams();
         invalidate();
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index a2976a8..f84b549 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -110,7 +110,6 @@
 import com.android.quickstep.util.TransformParams;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 
@@ -444,7 +443,7 @@
                 || DesktopTaskView.DESKTOP_MODE_SUPPORTED;
 
         boolean willDrawBorder =
-                keyboardFocusHighlightEnabled || FeatureFlags.ENABLE_CURSOR_HOVER_STATES.get();
+                keyboardFocusHighlightEnabled || FeatureFlags.enableCursorHoverStates();
         setWillNotDraw(!willDrawBorder);
 
         if (willDrawBorder) {
@@ -462,7 +461,7 @@
                             /* targetView= */ this)) : null;
 
             mHoverBorderAnimator =
-                    FeatureFlags.ENABLE_CURSOR_HOVER_STATES.get() ? new BorderAnimator(
+                    FeatureFlags.enableCursorHoverStates() ? new BorderAnimator(
                             /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
                             /* borderColor= */ styledAttrs.getColor(
                                     R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR),
@@ -537,7 +536,7 @@
 
     @Override
     public boolean onHoverEvent(MotionEvent event) {
-        if (FeatureFlags.ENABLE_CURSOR_HOVER_STATES.get()) {
+        if (FeatureFlags.enableCursorHoverStates()) {
             switch (event.getAction()) {
                 case MotionEvent.ACTION_HOVER_ENTER:
                     mHoverBorderAnimator.buildAnimator(/* isAppearing= */ true).start();
@@ -554,7 +553,7 @@
 
     @Override
     public boolean onInterceptHoverEvent(MotionEvent event) {
-        if (FeatureFlags.ENABLE_CURSOR_HOVER_STATES.get()) {
+        if (FeatureFlags.enableCursorHoverStates()) {
             // avoid triggering hover event on child elements which would cause HOVER_EXIT for this
             // task view
             return true;
@@ -1137,7 +1136,7 @@
         if (dp.isTablet) {
             int alignedOptionIndex = 0;
             if (getRecentsView().isOnGridBottomRow(menuContainer.getTaskView()) && dp.isLandscape) {
-                if (FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+                if (FeatureFlags.enableGridOnlyOverview()) {
                     // With no focused task, there is less available space below the tasks, so align
                     // the arrow to the third option in the menu.
                     alignedOptionIndex = 2;
@@ -1708,21 +1707,20 @@
     }
 
     protected void updateSnapshotRadius() {
-        updateCurrentFullscreenParams(mSnapshotView.getPreviewPositionHelper());
+        updateCurrentFullscreenParams();
         mSnapshotView.setFullscreenParams(mCurrentFullscreenParams);
     }
 
-    void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
-        updateFullscreenParams(mCurrentFullscreenParams, previewPositionHelper);
+    void updateCurrentFullscreenParams() {
+        updateFullscreenParams(mCurrentFullscreenParams);
     }
 
-    protected void updateFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams,
-            PreviewPositionHelper previewPositionHelper) {
+    protected void updateFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
         if (getRecentsView() == null) {
             return;
         }
         fullscreenParams.setProgress(mFullscreenProgress, getRecentsView().getScaleX(),
-                getScaleX(), getWidth(), mActivity.getDeviceProfile(), previewPositionHelper);
+                getScaleX());
     }
 
     /**
@@ -1893,8 +1891,7 @@
         /**
          * Sets the progress in range [0, 1]
          */
-        public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale,
-                int previewWidth, DeviceProfile dp, PreviewPositionHelper pph) {
+        public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale) {
             mCurrentDrawnCornerRadius =
                     Utilities.mapRange(fullscreenProgress, mCornerRadius, mWindowCornerRadius)
                             / parentScale / taskViewScale;
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
index 6c0d44d..1b208a7 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
@@ -18,11 +18,11 @@
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 
 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS;
-import static com.android.launcher3.taskbar.TaskbarHoverToolTipController.HOVER_TOOL_TIP_REVEAL_START_DELAY;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doAnswer;
@@ -125,16 +125,31 @@
     public void onHover_hoverEnterIcon_revealToolTip() {
         when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
         when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
+        when(taskbarActivityContext.isTaskbarWindowFullscreen()).thenReturn(true);
 
         boolean hoverHandled =
                 mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent);
-
-        // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run
-        verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true);
         waitForIdleSync();
+
         assertThat(hoverHandled).isTrue();
         verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
                 true);
+        verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(anyBoolean());
+    }
+
+    @Test
+    public void onHover_hoverEnterIcon_setFullScreenFirstHover() {
+        when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
+        when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
+        when(taskbarActivityContext.isTaskbarWindowFullscreen()).thenReturn(false);
+
+        boolean hoverHandled =
+                mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent);
+        waitForIdleSync();
+
+        assertThat(hoverHandled).isFalse();
+        verify(taskbarActivityContext, never()).setAutohideSuspendFlag(
+                FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, true);
         verify(taskbarActivityContext).setTaskbarWindowFullscreen(true);
     }
 
@@ -156,17 +171,16 @@
     public void onHover_hoverEnterFolderIcon_revealToolTip() {
         when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
         when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
+        when(taskbarActivityContext.isTaskbarWindowFullscreen()).thenReturn(true);
 
         boolean hoverHandled =
                 mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
-
-        // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run
-        verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true);
         waitForIdleSync();
+
         assertThat(hoverHandled).isTrue();
         verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
                 true);
-        verify(taskbarActivityContext).setTaskbarWindowFullscreen(true);
+        verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(anyBoolean());
     }
 
     @Test
@@ -222,7 +236,6 @@
     }
 
     private void waitForIdleSync() {
-        mTestableLooper.moveTimeForward(HOVER_TOOL_TIP_REVEAL_START_DELAY + 1);
         mTestableLooper.processAllMessages();
     }
 }
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index b3d04c6..58be345 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -17,6 +17,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -33,6 +34,7 @@
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.util.AssistUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,6 +56,8 @@
     @Mock
     Handler mockHandler;
     @Mock
+    AssistUtils mockAssistUtils;
+    @Mock
     StatsLogManager mockStatsLogManager;
     @Mock
     StatsLogManager.StatsLogger mockStatsLogger;
@@ -79,7 +83,7 @@
                 .thenReturn(mockTaskbarActivityContext);
         doReturn(mockStatsLogManager).when(mockTaskbarActivityContext).getStatsLogManager();
         mNavButtonController = new TaskbarNavButtonController(mockService,
-                mockSystemUiProxy, mockHandler);
+                mockSystemUiProxy, mockHandler, mockAssistUtils);
     }
 
     @Test
@@ -108,16 +112,42 @@
     }
 
     @Test
-    public void testLongPressHome_enabled() {
+    public void testLongPressHome_enabled_withoutOverride() {
         mNavButtonController.setAssistantLongPressEnabled(true /*assistantLongPressEnabled*/);
+        when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(false);
+
         mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
+        verify(mockAssistUtils, times(1)).tryStartAssistOverride(anyInt());
         verify(mockSystemUiProxy, times(1)).startAssistant(any());
     }
 
     @Test
-    public void testLongPressHome_disabled() {
-        mNavButtonController.setAssistantLongPressEnabled(false /*assistantLongPressEnabled*/);
+    public void testLongPressHome_enabled_withOverride() {
+        mNavButtonController.setAssistantLongPressEnabled(true /*assistantLongPressEnabled*/);
+        when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(true);
+
         mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
+        verify(mockAssistUtils, times(1)).tryStartAssistOverride(anyInt());
+        verify(mockSystemUiProxy, never()).startAssistant(any());
+    }
+
+    @Test
+    public void testLongPressHome_disabled_withoutOverride() {
+        mNavButtonController.setAssistantLongPressEnabled(false /*assistantLongPressEnabled*/);
+        when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(false);
+
+        mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
+        verify(mockAssistUtils, never()).tryStartAssistOverride(anyInt());
+        verify(mockSystemUiProxy, never()).startAssistant(any());
+    }
+
+    @Test
+    public void testLongPressHome_disabled_withOverride() {
+        mNavButtonController.setAssistantLongPressEnabled(false /*assistantLongPressEnabled*/);
+        when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(true);
+
+        mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
+        verify(mockAssistUtils, never()).tryStartAssistOverride(anyInt());
         verify(mockSystemUiProxy, never()).startAssistant(any());
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index a67d787..a10b24d 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -61,6 +61,7 @@
 import com.android.launcher3.util.rule.FailureWatcher;
 import com.android.launcher3.util.rule.SamplerRule;
 import com.android.launcher3.util.rule.ScreenRecordRule;
+import com.android.launcher3.util.rule.TestIsolationRule;
 import com.android.launcher3.util.rule.TestStabilityRule;
 import com.android.launcher3.util.rule.ViewCaptureRule;
 import com.android.quickstep.views.RecentsView;
@@ -94,9 +95,6 @@
     public final TestRule mDisableHeadsUpNotification = disableHeadsUpNotification();
 
     @Rule
-    public final TestRule mSetLauncherCommand;
-
-    @Rule
     public final TestRule mOrderSensitiveRules;
 
     @Rule
@@ -116,19 +114,7 @@
             Utilities.enableRunningInTestHarnessForTests();
         }
 
-        final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(
-                RecentsActivity.ACTIVITY_TRACKER::getCreatedActivity);
-        mOrderSensitiveRules = RuleChain
-                .outerRule(new SamplerRule())
-                .around(new NavigationModeSwitchRule(mLauncher))
-                .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData))
-                .around(viewCaptureRule);
-
-        mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
-                getHomeIntentInPackage(context),
-                MATCH_DISABLED_COMPONENTS).get(0).activityInfo;
-
-        mSetLauncherCommand = (base, desc) -> new Statement() {
+        final TestRule setLauncherCommand = (base, desc) -> new Statement() {
             @Override
             public void evaluate() throws Throwable {
                 TestCommandReceiver.callCommand(TestCommandReceiver.ENABLE_TEST_LAUNCHER);
@@ -151,6 +137,22 @@
                 }
             }
         };
+
+        final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(
+                RecentsActivity.ACTIVITY_TRACKER::getCreatedActivity);
+        mOrderSensitiveRules = RuleChain
+                .outerRule(new SamplerRule())
+                .around(new TestStabilityRule())
+                .around(new NavigationModeSwitchRule(mLauncher))
+                .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData))
+                .around(viewCaptureRule)
+                .around(new TestIsolationRule(mLauncher))
+                .around(setLauncherCommand);
+
+        mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
+                getHomeIntentInPackage(context),
+                MATCH_DISABLED_COMPONENTS).get(0).activityInfo;
+
         if (TestHelpers.isInLauncherProcess()) {
             mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand(
                     TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()).
@@ -175,7 +177,6 @@
 
     // b/143488140
     //@NavigationModeSwitch
-    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/266606727
     @Test
     public void goToOverviewFromHome() {
         mDevice.pressHome();
@@ -223,7 +224,6 @@
 
     // b/143488140
     //@NavigationModeSwitch
-    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/266606727
     @Test
     public void testOverview() {
         startAppFast(getAppPackageName());
diff --git a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
index bb1afdf..db06b6b 100644
--- a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt
@@ -16,17 +16,13 @@
 package com.android.quickstep
 
 import android.content.Context
-import android.graphics.Rect
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.launcher3.FakeInvariantDeviceProfileTest
 import com.android.quickstep.util.TaskCornerRadius
 import com.android.quickstep.views.TaskView.FullscreenDrawParams
-import com.android.systemui.shared.recents.model.ThumbnailData
-import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
 import com.android.systemui.shared.system.QuickStepContract
 import com.google.common.truth.Truth.assertThat
-import kotlin.math.roundToInt
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -39,10 +35,6 @@
 @RunWith(AndroidJUnit4::class)
 class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() {
 
-    private val TASK_SCALE = 0.7f
-    private var mThumbnailData: ThumbnailData = mock(ThumbnailData::class.java)
-
-    private val mPreviewPositionHelper = PreviewPositionHelper()
     private lateinit var params: FullscreenDrawParams
 
     @Before
@@ -53,32 +45,11 @@
     @Test
     fun setStartProgress_correctCornerRadiusForTablet() {
         initializeVarsForTablet()
-        val dp = newDP()
-        val previewRect = Rect(0, 0, 100, 100)
-        val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt()
-        val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt()
-        val currentRotation = 0
-        val isRtl = false
 
-        mPreviewPositionHelper.updateThumbnailMatrix(
-            previewRect,
-            mThumbnailData,
-            canvasWidth,
-            canvasHeight,
-            dp.widthPx,
-            dp.heightPx,
-            dp.taskbarHeight,
-            dp.isTablet,
-            currentRotation,
-            isRtl
-        )
         params.setProgress(
             /* fullscreenProgress= */ 0f,
             /* parentScale= */ 1.0f,
-            /* taskViewScale= */ 1.0f,
-            /* previewWidth= */ 0,
-            dp,
-            mPreviewPositionHelper
+            /* taskViewScale= */ 1.0f
         )
 
         val expectedRadius = TaskCornerRadius.get(context)
@@ -88,32 +59,11 @@
     @Test
     fun setFullProgress_correctCornerRadiusForTablet() {
         initializeVarsForTablet()
-        val dp = newDP()
-        val previewRect = Rect(0, 0, 100, 100)
-        val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt()
-        val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt()
-        val currentRotation = 0
-        val isRtl = false
 
-        mPreviewPositionHelper.updateThumbnailMatrix(
-            previewRect,
-            mThumbnailData,
-            canvasWidth,
-            canvasHeight,
-            dp.widthPx,
-            dp.heightPx,
-            dp.taskbarHeight,
-            dp.isTablet,
-            currentRotation,
-            isRtl
-        )
         params.setProgress(
             /* fullscreenProgress= */ 1.0f,
             /* parentScale= */ 1.0f,
-            /* taskViewScale= */ 1.0f,
-            /* previewWidth= */ 0,
-            dp,
-            mPreviewPositionHelper
+            /* taskViewScale= */ 1.0f
         )
 
         val expectedRadius = QuickStepContract.getWindowCornerRadius(context)
@@ -123,32 +73,11 @@
     @Test
     fun setStartProgress_correctCornerRadiusForPhone() {
         initializeVarsForPhone()
-        val dp = newDP()
-        val previewRect = Rect(0, 0, 100, 100)
-        val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt()
-        val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt()
-        val currentRotation = 0
-        val isRtl = false
 
-        mPreviewPositionHelper.updateThumbnailMatrix(
-            previewRect,
-            mThumbnailData,
-            canvasWidth,
-            canvasHeight,
-            dp.widthPx,
-            dp.heightPx,
-            dp.taskbarHeight,
-            dp.isTablet,
-            currentRotation,
-            isRtl
-        )
         params.setProgress(
             /* fullscreenProgress= */ 0f,
             /* parentScale= */ 1.0f,
-            /* taskViewScale= */ 1.0f,
-            /* previewWidth= */ 0,
-            dp,
-            mPreviewPositionHelper
+            /* taskViewScale= */ 1.0f
         )
 
         val expectedRadius = TaskCornerRadius.get(context)
@@ -158,32 +87,11 @@
     @Test
     fun setFullProgress_correctCornerRadiusForPhone() {
         initializeVarsForPhone()
-        val dp = newDP()
-        val previewRect = Rect(0, 0, 100, 100)
-        val canvasWidth = (dp.widthPx * TASK_SCALE).roundToInt()
-        val canvasHeight = (dp.heightPx * TASK_SCALE).roundToInt()
-        val currentRotation = 0
-        val isRtl = false
 
-        mPreviewPositionHelper.updateThumbnailMatrix(
-            previewRect,
-            mThumbnailData,
-            canvasWidth,
-            canvasHeight,
-            dp.widthPx,
-            dp.heightPx,
-            dp.taskbarHeight,
-            dp.isTablet,
-            currentRotation,
-            isRtl
-        )
         params.setProgress(
             /* fullscreenProgress= */ 1.0f,
             /* parentScale= */ 1.0f,
-            /* taskViewScale= */ 1.0f,
-            /* previewWidth= */ 0,
-            dp,
-            mPreviewPositionHelper
+            /* taskViewScale= */ 1.0f
         )
 
         val expectedRadius = QuickStepContract.getWindowCornerRadius(context)
@@ -207,10 +115,7 @@
         spyParams.setProgress(
             /* fullscreenProgress= */ 0f,
             /* parentScale= */ 1.0f,
-            /* taskViewScale= */ 1.0f,
-            /* unused previewWidth= */ -1,
-            /* unusedDp= */ null,
-            /* unused previewPositionHelper= */ null
+            /* taskViewScale= */ 1.0f
         )
         assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1TaskRadius)
 
@@ -218,10 +123,7 @@
         spyParams.setProgress(
             /* fullscreenProgress= */ 0f,
             /* parentScale= */ 1.0f,
-            /* taskViewScale= */ 1.0f,
-            /* unused previewWidth= */ -1,
-            /* unusedDp= */ null,
-            /* unused previewPositionHelper= */ null
+            /* taskViewScale= */ 1.0f
         )
         assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2TaskRadius)
     }
@@ -243,10 +145,7 @@
         spyParams.setProgress(
             /* fullscreenProgress= */ 1.0f,
             /* parentScale= */ 1.0f,
-            /* taskViewScale= */ 1.0f,
-            /* unused previewWidth= */ -1,
-            /* unusedDp= */ null,
-            /* unused previewPositionHelper= */ null
+            /* taskViewScale= */ 1.0f
         )
         assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1WindowRadius)
 
@@ -255,9 +154,6 @@
             /* fullscreenProgress= */ 1.0f,
             /* parentScale= */ 1.0f,
             /* taskViewScale= */ 1.0f,
-            /* unused previewWidth= */ -1,
-            /* unusedDp= */ null,
-            /* unused previewPositionHelper= */ null
         )
         assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2WindowRadius)
     }
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 20aa410..a89eab5 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -49,7 +49,6 @@
 
     @Test
     @NavigationModeSwitch
-    @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/187761685
     public void testStressPressHome() {
         for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
             // Destroy Launcher activity.
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
index 1b5313b..9a2826d 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
@@ -22,6 +22,7 @@
 
 import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -38,6 +39,7 @@
 
     @Test
     @TaskbarModeSwitch(mode = PERSISTENT)
+    @Ignore // b/301575789
     public void testHideTaskbarPersistsOnRecreate() {
         getTaskbar().hide();
         mLauncher.recreateTaskbar();
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 25f90ca..5531c6e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -16,11 +16,13 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
 import static com.android.launcher3.testing.shared.TestProtocol.FLAKY_QUICK_SWITCH_TO_PREVIOUS_APP;
 import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
 import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
 import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
+import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -50,6 +52,7 @@
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.ui.TaplTestsLauncher3;
 import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.TestStabilityRule;
@@ -242,6 +245,19 @@
                 isInState(() -> LauncherState.OVERVIEW));
     }
 
+    @Test
+    @TaskbarModeSwitch(mode = TRANSIENT)
+    public void testSwitchToOverviewWithStashedTaskbar() throws Exception {
+        try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
+            startTestAppsWithCheck();
+            // Set ignoreTaskbarVisibility, as transient taskbar will be stashed after app launch.
+            mLauncher.setIgnoreTaskbarVisibility(true);
+            mLauncher.getLaunchedAppState().switchToOverview();
+        } finally {
+            mLauncher.setIgnoreTaskbarVisibility(false);
+        }
+    }
+
     @Ignore
     @Test
     @NavigationModeSwitch
@@ -271,24 +287,6 @@
     }
 
     @Test
-    @PortraitLandscape
-    public void testAllAppsFromHome() throws Exception {
-        // Test opening all apps
-        assertNotNull("switchToAllApps() returned null",
-                mLauncher.getWorkspace().switchToAllApps());
-
-        TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllApps());
-
-        // Testing pressHome.
-        assertTrue("Launcher internal state is not All Apps",
-                isInState(() -> LauncherState.ALL_APPS));
-        assertNotNull("pressHome returned null", mLauncher.goHome());
-        assertTrue("Launcher internal state is not Home",
-                isInState(() -> LauncherState.NORMAL));
-        assertNotNull("getHome returned null", mLauncher.getWorkspace());
-    }
-
-    @Test
     @NavigationModeSwitch
     @PortraitLandscape
     @PlatinumTest(focusArea = "launcher")
@@ -502,7 +500,6 @@
     }
 
     @Test
-    @ScreenRecord // b/242163205
     public void testDisableRotationCheckForPhone() throws Exception {
         assumeFalse(mLauncher.isTablet());
         try {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index a90c326..cc56faf 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -37,6 +37,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -107,6 +108,7 @@
     }
 
     @Test
+    @Ignore("Enable once App Pairs flagged on. These cause memory leaks b/297135374")
     public void testSaveAppPairMenuItemExistsOnSplitPair() throws Exception {
         assumeTrue("App pairs feature is currently not enabled, no test needed",
                 FeatureFlags.ENABLE_APP_PAIRS.get());
@@ -122,6 +124,7 @@
     }
 
     @Test
+    @Ignore("Enable once App Pairs flagged on. These cause memory leaks b/297135374")
     public void testSaveAppPairMenuItemDoesNotExistOnSingleTask() throws Exception {
         assumeTrue("App pairs feature is currently not enabled, no test needed",
                 FeatureFlags.ENABLE_APP_PAIRS.get());
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index 4ff2f9c..093c45d 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -151,6 +151,13 @@
                 .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
     }
 
+    @Test
+    @PortraitLandscape
+    public void testDismissAllAppsByTappingOutsideSheet() {
+        getTaskbar().openAllApps().dismissByTappingOutsideForTablet(/* tapRight= */ true);
+        getTaskbar().openAllApps().dismissByTappingOutsideForTablet(/* tapRight= */ false);
+    }
+
     private boolean isTaskbarTestModeTransient() {
         return TRANSIENT == mTaskbarMode;
     }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
index 3869bf7..b3cec4e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
@@ -66,12 +66,10 @@
 
     @Test
     @TaskbarModeSwitch(mode = TRANSIENT)
-    public void testClickHoveredTaskbarToGoHome() {
+    public void testClickHoveredTaskbarToGoHome() throws Exception {
         try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) {
             getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
             mLauncher.getLaunchedAppState().clickStashedTaskbarToGoHome();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
         }
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt
index fc767fa..c6c5be4 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt
@@ -381,7 +381,7 @@
     }
 
     @Test
-    fun clearState() {
+    fun clearState_task() {
         splitSelectDataHolder.setInitialTaskSelect(
             sampleTaskInfo,
             STAGE_POSITION_TOP_OR_LEFT,
@@ -392,4 +392,18 @@
         splitSelectDataHolder.resetState()
         assertFalse(splitSelectDataHolder.isSplitSelectActive())
     }
+
+    @Test
+    fun clearState_intent() {
+        splitSelectDataHolder.setInitialTaskSelect(
+                sampleIntent,
+                STAGE_POSITION_TOP_OR_LEFT,
+                sampleItemInfo,
+                null,
+                INVALID_TASK_ID
+        )
+        splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser)
+        splitSelectDataHolder.resetState()
+        assertFalse(splitSelectDataHolder.isSplitSelectActive())
+    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index 4e7fcf0..f198741 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -20,7 +20,6 @@
 import android.app.ActivityManager
 import android.app.PendingIntent
 import android.content.ComponentName
-import android.content.Context
 import android.content.Intent
 import android.graphics.Rect
 import android.os.Handler
@@ -38,7 +37,7 @@
 import com.android.quickstep.RecentsModel
 import com.android.quickstep.SystemUiProxy
 import com.android.systemui.shared.recents.model.Task
-import java.util.function.Consumer
+import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNull
@@ -50,6 +49,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import java.util.function.Consumer
 
 @RunWith(AndroidJUnit4::class)
 class SplitSelectStateControllerTest {
@@ -560,7 +560,7 @@
         return GroupTask(
             task1,
             task2,
-            SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1)
+            SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1, SNAP_TO_50_50)
         )
     }
 
@@ -592,7 +592,7 @@
         return GroupTask(
             task1,
             task2,
-            SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1)
+            SplitConfigurationOptions.SplitBounds(Rect(), Rect(), -1, -1, SNAP_TO_50_50)
         )
     }
 }
diff --git a/res/drawable-sw720dp/ic_transient_taskbar_all_apps_search_button.xml b/res/drawable-sw720dp/ic_transient_taskbar_all_apps_search_button.xml
new file mode 100644
index 0000000..654f0b3
--- /dev/null
+++ b/res/drawable-sw720dp/ic_transient_taskbar_all_apps_search_button.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="52dp"
+    android:height="52dp"
+    android:viewportWidth="52"
+    android:viewportHeight="52">
+  <path
+      android:pathData="M39.4,17.5c0,3.1 -2.5,5.5 -5.5,5.5c-3.1,0 -5.5,-2.5 -5.5,-5.5s2.5,-5.5 5.5,-5.5C36.9,12 39.4,14.5 39.4,17.5z"
+      android:fillColor="#FF000000"/>
+  <path
+      android:pathData="M23.1,17.5c0,3.1 -2.5,5.5 -5.5,5.5S12,20.6 12,17.5s2.5,-5.5 5.5,-5.5S23.1,14.5 23.1,17.5z"
+      android:fillColor="#FF000000"/>
+  <path
+      android:pathData="M17.5,33.9m-5.5,0a5.5,5.5 0,1 1,11 0a5.5,5.5 0,1 1,-11 0"
+      android:fillColor="#FF000000"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M33.9,30c2.1,0 3.9,1.7 3.9,3.9s-1.7,3.9 -3.9,3.9S30,36.1 30,33.9S31.8,30 33.9,30M33.9,28.3c-3.1,0 -5.6,2.5 -5.6,5.6c0,3.1 2.5,5.6 5.6,5.6c3.1,0 5.6,-2.5 5.6,-5.6C39.5,30.8 37,28.3 33.9,28.3L33.9,28.3z"/>
+  <path
+      android:pathData="M36.9,37L43.2,43.3"
+      android:strokeWidth="1.7"
+      android:fillColor="#00000000"
+      android:strokeColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_taskbar_all_apps_search_button.xml b/res/drawable/ic_taskbar_all_apps_search_button.xml
new file mode 100644
index 0000000..8fbe539
--- /dev/null
+++ b/res/drawable/ic_taskbar_all_apps_search_button.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="44dp"
+    android:height="44dp"
+    android:viewportWidth="44"
+    android:viewportHeight="44">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M34.1,14.9c0,2.7 -2.2,4.9 -4.9,4.9s-4.9,-2.2 -4.9,-4.9s2.2,-4.9 4.9,-4.9S34.1,12.2 34.1,14.9z"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M19.7,14.9c0,2.7 -2.2,4.9 -4.9,4.9S10,17.6 10,14.9s2.2,-4.9 4.9,-4.9S19.7,12.2 19.7,14.9z"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M14.9,29.2m-4.9,0a4.9,4.9 0,1 1,9.8 0a4.9,4.9 0,1 1,-9.8 0"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M29.3,25.9c1.9,0 3.4,1.5 3.4,3.4c0,1.9 -1.5,3.4 -3.4,3.4s-3.4,-1.5 -3.4,-3.4C25.9,27.4 27.4,25.9 29.3,25.9M29.3,24.4c-2.7,0 -4.9,2.2 -4.9,4.9s2.2,4.9 4.9,4.9c2.7,0 4.9,-2.2 4.9,-4.9S32,24.4 29.3,24.4L29.3,24.4z"/>
+  <path
+      android:pathData="M31.9,32L37.4,37.5"
+      android:strokeWidth="1.5"
+      android:fillColor="#00000000"
+      android:strokeColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_transient_taskbar_all_apps_search_button.xml b/res/drawable/ic_transient_taskbar_all_apps_search_button.xml
new file mode 100644
index 0000000..59a666b
--- /dev/null
+++ b/res/drawable/ic_transient_taskbar_all_apps_search_button.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M37.4,15.5c0,3.1 -2.5,5.5 -5.5,5.5c-3.1,0 -5.5,-2.5 -5.5,-5.5s2.5,-5.5 5.5,-5.5C34.9,10 37.4,12.5 37.4,15.5z"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M21.1,15.5c0,3.1 -2.5,5.5 -5.5,5.5S10,18.6 10,15.5s2.5,-5.5 5.5,-5.5S21.1,12.5 21.1,15.5z"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M15.5,31.9m-5.5,0a5.5,5.5 0,1 1,11 0a5.5,5.5 0,1 1,-11 0"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M31.9,28c2.1,0 3.9,1.7 3.9,3.9s-1.7,3.9 -3.9,3.9c-2.1,0 -3.9,-1.7 -3.9,-3.9S29.8,28 31.9,28M31.9,26.3c-3.1,0 -5.6,2.5 -5.6,5.6c0,3.1 2.5,5.6 5.6,5.6c3.1,0 5.6,-2.5 5.6,-5.6C37.5,28.8 35,26.3 31.9,26.3L31.9,26.3z"/>
+  <path
+      android:pathData="M34.9,35L41.2,41.3"
+      android:strokeWidth="1.7"
+      android:fillColor="#00000000"
+      android:strokeColor="#000000"/>
+</vector>
diff --git a/res/layout/all_apps_prediction_row_icon.xml b/res/layout/all_apps_prediction_row_icon.xml
new file mode 100644
index 0000000..c9b3275
--- /dev/null
+++ b/res/layout/all_apps_prediction_row_icon.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    style="@style/BaseIcon.AllApps"
+    android:id="@+id/icon"
+    launcher:iconDisplay="prediction_row"
+    launcher:centerVertically="true" />
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index ee7805f..333fded 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -35,7 +35,7 @@
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lai ja %2$d kõrge"</string>
     <string name="widget_preview_context_description" msgid="9045841361655787574">"Vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
-    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Vidina teisaldamiseks avakuval puudutage vidinat ja hoidke seda all"</string>
+    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Vidina teisaldamiseks avakuval puudutage vidinat pikalt"</string>
     <string name="add_to_home_screen" msgid="9168649446635919791">"Lisa avakuvale"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g> lisati avakuvale"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Soovitused"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 72ab9b2..a35a70b 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -61,7 +61,7 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
     <string name="label_application" msgid="8531721983832654978">"ಆ್ಯಪ್"</string>
     <string name="all_apps_label" msgid="5015784846527570951">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು"</string>
-    <string name="notifications_header" msgid="1404149926117359025">"ಅಧಿಸೂಚನೆಗಳು"</string>
+    <string name="notifications_header" msgid="1404149926117359025">"ನೋಟಿಫಿಕೇಶನ್‌ಗಳು"</string>
     <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ಶಾರ್ಟ್‌ಕಟ್ ಸರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ಶಾರ್ಟ್‌ಕಟ್ ಸರಿಸಲು ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಲು ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
     <string name="out_of_space" msgid="6455557115204099579">"ಈ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 54cc691..c73295d 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -35,7 +35,7 @@
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d रूंद बाय %2$d उंच"</string>
     <string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट"</string>
-    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"होम स्क्रीनवर ते हलवण्यासाठी विजेटला स्पर्श करा आणि धरून ठेवा"</string>
+    <string name="add_item_request_drag_hint" msgid="8730547755622776606">"होम स्क्रीनवर हलवण्यासाठी विजेटला स्पर्श करून धरून ठेवा"</string>
     <string name="add_to_home_screen" msgid="9168649446635919791">"होम स्क्रीनवर जोडा"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> हे विजेट तुमच्या होम स्क्रीनवर जोडले आहे"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"सूचना"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 3cc6bff..0f0960e 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -104,7 +104,7 @@
     <string name="settings_button_text" msgid="8873672322605444408">"主畫面設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string>
     <string name="allow_rotation_title" msgid="7222049633713050106">"允許旋轉主畫面"</string>
-    <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"隨手機旋轉"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"通知圓點"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"開啟"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"關閉"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 05eaf88..f046eca 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -86,6 +86,7 @@
             <enum name="search_result_tall" value="6" />
             <enum name="search_result_small" value="7" />
             <enum name="prediction_row" value="8" />
+            <enum name="search_result_app_row" value="9" />
         </attr>
         <attr name="centerVertically" format="boolean" />
     </declare-styleable>
diff --git a/res/values/config.xml b/res/values/config.xml
index 8f9731c..cd9c9de 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -241,4 +241,7 @@
         <item>@dimen/iconSize66dp</item>
         <item>@dimen/iconSize72dp</item>
     </integer-array>
+
+    <!--  Used for custom widgets  -->
+    <array name="custom_widget_providers"/>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1079e00..c6fce28 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -385,6 +385,9 @@
     <dimen name="taskbar_button_margin_split">0dp</dimen>
     <dimen name="taskbar_button_margin_6_5">0dp</dimen>
 
+    <!-- Bubble bar (placeholders to compile in Launcher3 without Quickstep) -->
+    <dimen name="bubblebar_hotseat_adjustment_threshold">0dp</dimen>
+
     <!-- Size of the maximum radius for the enforced rounded rectangles. -->
     <dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 347c7af..bd004e9 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,9 +16,9 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V2;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING;
+import static com.android.launcher3.config.FeatureFlags.enableCursorHoverStates;
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
 import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
@@ -99,8 +99,9 @@
     private static final int DISPLAY_FOLDER = 2;
     protected static final int DISPLAY_TASKBAR = 5;
     public static final int DISPLAY_SEARCH_RESULT = 6;
-    private static final int DISPLAY_SEARCH_RESULT_SMALL = 7;
+    public static final int DISPLAY_SEARCH_RESULT_SMALL = 7;
     public static final int DISPLAY_PREDICTION_ROW = 8;
+    public static final int DISPLAY_SEARCH_RESULT_APP_ROW = 9;
 
     private static final float MIN_LETTER_SPACING = -0.05f;
     private static final int MAX_SEARCH_LOOP_COUNT = 20;
@@ -198,7 +199,7 @@
     public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         mActivity = ActivityContext.lookupContext(context);
-        FastBitmapDrawable.setFlagHoverEnabled(ENABLE_CURSOR_HOVER_STATES.get());
+        FastBitmapDrawable.setFlagHoverEnabled(enableCursorHoverStates());
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
@@ -214,7 +215,8 @@
             setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
             defaultIconSize = grid.iconSizePx;
             setCenterVertically(grid.iconCenterVertically);
-        } else if (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW) {
+        } else if (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW
+                || mDisplay == DISPLAY_SEARCH_RESULT_APP_ROW) {
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
             setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
             defaultIconSize = grid.allAppsIconSizePx;
@@ -397,8 +399,8 @@
     }
 
     protected boolean shouldUseTheme() {
-        return mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
-                || mDisplay == DISPLAY_TASKBAR;
+        return (mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
+                || mDisplay == DISPLAY_TASKBAR) && Themes.isThemedIconEnabled(getContext());
     }
 
     /**
@@ -1105,8 +1107,13 @@
     }
 
     public boolean isDisplaySearchResult() {
-        return mDisplay == DISPLAY_SEARCH_RESULT ||
-                mDisplay == DISPLAY_SEARCH_RESULT_SMALL;
+        return mDisplay == DISPLAY_SEARCH_RESULT
+                || mDisplay == DISPLAY_SEARCH_RESULT_SMALL
+                || mDisplay == DISPLAY_SEARCH_RESULT_APP_ROW;
+    }
+
+    public int getIconDisplay() {
+        return mDisplay;
     }
 
     @Override
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index a48c928..c05afc9 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -205,9 +205,11 @@
     public int hotseatBarEndOffset;
     public int hotseatQsbSpace;
     public int springLoadedHotseatBarTopMarginPx;
-    // Start is the side next to the nav bar, end is the side next to the workspace
-    public final int hotseatBarSidePaddingStartPx;
-    public final int hotseatBarSidePaddingEndPx;
+    // These 2 values are only used for isVerticalBar
+    // Padding between edge of screen and hotseat
+    public final int mHotseatBarEdgePaddingPx;
+    // Space between hotseat and workspace (not used in responsive)
+    public final int mHotseatBarWorkspaceSpacePx;
     public int hotseatQsbWidth; // only used when isQsbInline
     public final int hotseatQsbHeight;
     public final int hotseatQsbVisualHeight;
@@ -217,6 +219,9 @@
     private final int mMinHotseatQsbWidthPx;
     private final int mMaxHotseatIconSpacePx;
     public final int inlineNavButtonsEndSpacingPx;
+    // Space required for the bubble bar between the hotseat and the edge of the screen. If there's
+    // not enough space, the hotseat will adjust itself for the bubble bar.
+    private final int mBubbleBarSpaceThresholdPx;
 
     // Bottom sheets
     public int bottomSheetTopPadding;
@@ -294,6 +299,7 @@
     public final int taskbarIconSize;
     // If true, used to layout taskbar in 3 button navigation mode.
     public final boolean startAlignTaskbar;
+    public final boolean isTransientTaskbar;
 
     // DragController
     public int flingToDeleteThresholdVelocity;
@@ -361,7 +367,11 @@
             }
         }
 
-        if (DisplayController.isTransientTaskbar(context)) {
+        isTransientTaskbar = DisplayController.isTransientTaskbar(context);
+        if (!isTaskbarPresent) {
+            taskbarIconSize = taskbarHeight = stashedTaskbarHeight = taskbarBottomMargin = 0;
+            startAlignTaskbar = false;
+        } else if (isTransientTaskbar) {
             float invTransientIconSizeDp = inv.transientTaskbarIconSize[mTypeIndex];
             taskbarIconSize = pxFromDp(invTransientIconSizeDp, mMetrics);
             taskbarHeight = Math.round((taskbarIconSize * ICON_VISIBLE_AREA_FACTOR)
@@ -497,46 +507,56 @@
         numShownAllAppsColumns =
                 isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
 
-        int hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics);
+        int hotseatBarBottomSpace;
         int minQsbMargin = res.getDimensionPixelSize(R.dimen.min_qsb_margin);
 
         if (mIsResponsiveGrid) {
             HotseatSpecs hotseatSpecs =
                     HotseatSpecs.create(new ResourceHelper(context,
                             isTwoPanels ? inv.hotseatSpecsTwoPanelId : inv.hotseatSpecsId));
-            mResponsiveHotseatSpec = hotseatSpecs.getCalculatedHeightSpec(heightPx);
+            mResponsiveHotseatSpec =
+                    isVerticalBarLayout() ? hotseatSpecs.getCalculatedWidthSpec(widthPx)
+                            : hotseatSpecs.getCalculatedHeightSpec(heightPx);
             hotseatQsbSpace = mResponsiveHotseatSpec.getHotseatQsbSpace();
+            hotseatBarBottomSpace =
+                    isVerticalBarLayout() ? 0 : mResponsiveHotseatSpec.getEdgePadding();
+            mHotseatBarEdgePaddingPx =
+                    isVerticalBarLayout() ? mResponsiveHotseatSpec.getEdgePadding() : 0;
+            mHotseatBarWorkspaceSpacePx = 0;
         } else {
             hotseatQsbSpace = pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics);
+            hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics);
+            mHotseatBarEdgePaddingPx =
+                    isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
+            mHotseatBarWorkspaceSpacePx =
+                    res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
         }
 
-        // Have a little space between the inset and the QSB
-        if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) {
-            int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace);
+        if (!isVerticalBarLayout()) {
+            // Have a little space between the inset and the QSB
+            if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) {
+                int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace);
 
-            // Only change the spaces if there is space
-            if (availableSpace > 0) {
-                // Make sure there is enough space between hotseat/QSB and QSB/navBar
-                if (availableSpace < minQsbMargin * 2) {
-                    minQsbMargin = availableSpace / 2;
-                    hotseatQsbSpace = minQsbMargin;
-                } else {
-                    hotseatQsbSpace -= minQsbMargin;
+                // Only change the spaces if there is space
+                if (availableSpace > 0) {
+                    // Make sure there is enough space between hotseat/QSB and QSB/navBar
+                    if (availableSpace < minQsbMargin * 2) {
+                        minQsbMargin = availableSpace / 2;
+                        hotseatQsbSpace = minQsbMargin;
+                    } else {
+                        hotseatQsbSpace -= minQsbMargin;
+                    }
                 }
-            }
-            hotseatBarBottomSpacePx = mInsets.bottom + minQsbMargin;
+                hotseatBarBottomSpacePx = mInsets.bottom + minQsbMargin;
 
-        } else {
-            hotseatBarBottomSpacePx = hotseatBarBottomSpace;
+            } else {
+                hotseatBarBottomSpacePx = hotseatBarBottomSpace;
+            }
         }
 
         springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize(
                 R.dimen.spring_loaded_hotseat_top_margin);
-        hotseatBarSidePaddingEndPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
-        // Add a bit of space between nav bar and hotseat in vertical bar layout.
-        hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
-        updateHotseatSizes(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
+        updateHotseatSizes(pxFromDp(inv.iconSize[mTypeIndex], mMetrics));
         if (areNavButtonsInline && !isPhone) {
             inlineNavButtonsEndSpacingPx =
                     res.getDimensionPixelSize(inv.inlineNavButtonsEndSpacing);
@@ -553,6 +573,9 @@
             hotseatBarEndOffset = 0;
         }
 
+        mBubbleBarSpaceThresholdPx =
+                res.getDimensionPixelSize(R.dimen.bubblebar_hotseat_adjustment_threshold);
+
         // Needs to be calculated after hotseatBarSizePx is correct,
         // for the available height to be correct
         if (mIsResponsiveGrid) {
@@ -562,10 +585,9 @@
             int availableResponsiveWidth =
                     availableWidthPx - (isVerticalBarLayout() ? hotseatBarSizePx : 0);
             int numColumns = getPanelCount() * inv.numColumns;
-            // don't use availableHeightPx because it subtracts bottom padding,
-            // but the workspace go behind it
-            int availableResponsiveHeight =
-                    heightPx - mInsets.top - (isVerticalBarLayout() ? 0 : hotseatBarSizePx);
+            // don't use availableHeightPx because it subtracts mInsets.bottom
+            int availableResponsiveHeight = heightPx - mInsets.top
+                            - (isVerticalBarLayout() ? 0 : hotseatBarSizePx);
             mResponsiveWidthSpec = workspaceSpecs.getCalculatedWidthSpec(numColumns,
                     availableResponsiveWidth);
             mResponsiveHeightSpec = workspaceSpecs.getCalculatedHeightSpec(inv.numRows,
@@ -738,8 +760,8 @@
         hotseatCellHeightPx = getIconSizeWithOverlap(hotseatIconSizePx);
 
         if (isVerticalBarLayout()) {
-            hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx
-                    + hotseatBarSidePaddingEndPx;
+            hotseatBarSizePx = hotseatIconSizePx + mHotseatBarEdgePaddingPx
+                    + mHotseatBarWorkspaceSpacePx;
         } else if (isQsbInline) {
             hotseatBarSizePx = Math.max(hotseatIconSizePx, hotseatQsbVisualHeight)
                     + hotseatBarBottomSpacePx;
@@ -1007,6 +1029,7 @@
                 iconSizePx = mIconSizeSteps.getIconSmallerThan(cellWidthPx);
             }
 
+            // TODO(b/296400197): isVerticalBar shouldn't show labels anymore
             iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
             int iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx);
             int cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
@@ -1491,7 +1514,8 @@
         if (isVerticalBarLayout()) {
             if (mIsResponsiveGrid) {
                 padding.top = mResponsiveHeightSpec.getStartPaddingPx();
-                padding.bottom = mResponsiveHeightSpec.getEndPaddingPx();
+                padding.bottom = Math.max(0,
+                        mResponsiveHeightSpec.getEndPaddingPx() - mInsets.bottom);
                 if (isSeascape()) {
                     padding.left = hotseatBarSizePx + mResponsiveWidthSpec.getEndPaddingPx();
                     padding.right = mResponsiveWidthSpec.getStartPaddingPx();
@@ -1504,9 +1528,9 @@
                 padding.bottom = edgeMarginPx;
                 if (isSeascape()) {
                     padding.left = hotseatBarSizePx;
-                    padding.right = hotseatBarSidePaddingStartPx;
+                    padding.right = mHotseatBarEdgePaddingPx;
                 } else {
-                    padding.left = hotseatBarSidePaddingStartPx;
+                    padding.left = mHotseatBarEdgePaddingPx;
                     padding.right = hotseatBarSizePx;
                 }
             }
@@ -1540,6 +1564,32 @@
         paddings.bottom -= insets.bottom;
     }
 
+
+    /**
+     * Returns the new border space that should be used between hotseat icons after adjusting it to
+     * the bubble bar.
+     *
+     * <p>If there's no adjustment needed, this method returns {@code 0}.
+     */
+    public float getHotseatAdjustedBorderSpaceForBubbleBar(Context context) {
+        // only need to adjust when QSB is on top of the hotseat.
+        if (isQsbInline) {
+            return 0;
+        }
+
+        // no need to adjust if there's enough space for the bubble bar to the right of the hotseat.
+        if (getHotseatLayoutPadding(context).right > mBubbleBarSpaceThresholdPx) {
+            return 0;
+        }
+
+        // The adjustment is shrinking the hotseat's width by 1 icon on either side.
+        int iconsWidth =
+                iconSizePx * numShownHotseatIcons + hotseatBorderSpace * (numShownHotseatIcons - 1);
+        int newWidth = iconsWidth - 2 * iconSizePx;
+        // Evenly space the icons within the boundaries of the new width.
+        return (float) (newWidth - iconSizePx * numShownHotseatIcons) / (numShownHotseatIcons - 1);
+    }
+
     /**
      * Returns the padding for hotseat view
      */
@@ -1560,11 +1610,11 @@
                     + diffOverlapFactor), 0);
 
             if (isSeascape()) {
-                hotseatBarPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
-                        hotseatBarSidePaddingEndPx, paddingBottom);
+                hotseatBarPadding.set(mInsets.left + mHotseatBarEdgePaddingPx, paddingTop,
+                        mHotseatBarWorkspaceSpacePx, paddingBottom);
             } else {
-                hotseatBarPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
-                        mInsets.right + hotseatBarSidePaddingStartPx, paddingBottom);
+                hotseatBarPadding.set(mHotseatBarWorkspaceSpacePx, paddingTop,
+                        mInsets.right + mHotseatBarEdgePaddingPx, paddingBottom);
             }
         } else if (isTaskbarPresent) {
             // Center the QSB vertically with hotseat
@@ -1700,7 +1750,7 @@
 
     /** Gets the space that the overview actions will take, including bottom margin. */
     public int getOverviewActionsClaimedSpace() {
-        int overviewActionsSpace = isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()
+        int overviewActionsSpace = isTablet && FeatureFlags.enableGridOnlyOverview()
                 ? 0
                 : (overviewActionsTopMarginPx + overviewActionsHeight);
         return overviewActionsSpace + getOverviewActionsClaimedSpaceBelow();
@@ -1910,10 +1960,10 @@
         writer.println(prefix + "\tinv.hotseatColumnSpan: " + inv.hotseatColumnSpan[mTypeIndex]);
         writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
         writer.println(prefix + pxToDpStr("hotseatBarBottomSpacePx", hotseatBarBottomSpacePx));
-        writer.println(prefix + pxToDpStr("hotseatBarSidePaddingStartPx",
-                hotseatBarSidePaddingStartPx));
-        writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
-                hotseatBarSidePaddingEndPx));
+        writer.println(prefix + pxToDpStr("mHotseatBarEdgePaddingPx",
+                mHotseatBarEdgePaddingPx));
+        writer.println(prefix + pxToDpStr("mHotseatBarWorkspaceSpacePx",
+                mHotseatBarWorkspaceSpacePx));
         writer.println(prefix + pxToDpStr("hotseatBarEndOffset", hotseatBarEndOffset));
         writer.println(prefix + pxToDpStr("hotseatQsbSpace", hotseatQsbSpace));
         writer.println(prefix + pxToDpStr("hotseatQsbHeight", hotseatQsbHeight));
@@ -2147,5 +2197,4 @@
                     mIsGestureMode, mViewScaleProvider, mOverrideProvider);
         }
     }
-
 }
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index c16b319..67ed55d 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.testing.shared.TestProtocol.SCROLL_FINISHED_MESSAGE;
+
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -170,7 +172,8 @@
         super.onScrollStateChanged(state);
 
         if (state == SCROLL_STATE_IDLE) {
-            AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
+            AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
+                    SCROLL_FINISHED_MESSAGE);
         }
     }
 
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 03afba1..3b12b86 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,6 +16,12 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_BUBBLE_ADJUSTMENT_ANIM;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -27,6 +33,10 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import com.android.launcher3.util.HorizontalInsettableView;
+import com.android.launcher3.util.MultiTranslateDelegate;
+import com.android.launcher3.views.ActivityContext;
+
 /**
  * View class that represents the bottom row of the home screen.
  */
@@ -34,6 +44,7 @@
 
     // Ratio of empty space, qsb should take up to appear visually centered.
     public static final float QSB_CENTER_FACTOR = .325f;
+    private static final int BUBBLE_BAR_ADJUSTMENT_ANIMATION_DURATION_MS = 250;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mHasVerticalHotseat;
@@ -72,9 +83,36 @@
     }
 
     public void resetLayout(boolean hasVerticalHotseat) {
+        ActivityContext activityContext = ActivityContext.lookupContext(getContext());
+        boolean bubbleBarEnabled = activityContext.isBubbleBarEnabled();
+        boolean hasBubbles = activityContext.hasBubbles();
         removeAllViewsInLayout();
         mHasVerticalHotseat = hasVerticalHotseat;
         DeviceProfile dp = mActivity.getDeviceProfile();
+
+        if (bubbleBarEnabled) {
+            float adjustedBorderSpace = dp.getHotseatAdjustedBorderSpaceForBubbleBar(getContext());
+            if (hasBubbles && Float.compare(adjustedBorderSpace, 0f) != 0) {
+                getShortcutsAndWidgets().setTranslationProvider(child -> {
+                    int index = getShortcutsAndWidgets().indexOfChild(child);
+                    float borderSpaceDelta = adjustedBorderSpace - dp.hotseatBorderSpace;
+                    return dp.iconSizePx + index * borderSpaceDelta;
+                });
+                if (mQsb instanceof HorizontalInsettableView) {
+                    HorizontalInsettableView insettableQsb = (HorizontalInsettableView) mQsb;
+                    final float insetFraction = (float) dp.iconSizePx / dp.hotseatQsbWidth;
+                    // post this to the looper so that QSB has a chance to redraw itself, e.g.
+                    // after device rotation
+                    mQsb.post(() -> insettableQsb.setHorizontalInsets(insetFraction));
+                }
+            } else {
+                getShortcutsAndWidgets().setTranslationProvider(null);
+                if (mQsb instanceof HorizontalInsettableView) {
+                    ((HorizontalInsettableView) mQsb).setHorizontalInsets(0);
+                }
+            }
+        }
+
         resetCellSize(dp);
         if (hasVerticalHotseat) {
             setGridSize(1, dp.numShownHotseatIcons);
@@ -83,6 +121,62 @@
         }
     }
 
+    /**
+     * Adjust the hotseat icons for the bubble bar.
+     *
+     * <p>When the bubble bar becomes visible, if needed, this method animates the hotseat icons
+     * to reduce the spacing between them and make room for the bubble bar. The QSB width is
+     * animated as well to align with the hotseat icons.
+     *
+     * <p>When the bubble bar goes away, any adjustments that were previously made are reversed.
+     */
+    public void adjustForBubbleBar(boolean isBubbleBarVisible) {
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        float adjustedBorderSpace = dp.getHotseatAdjustedBorderSpaceForBubbleBar(getContext());
+        if (Float.compare(adjustedBorderSpace, 0f) == 0) {
+            return;
+        }
+
+        ShortcutAndWidgetContainer icons = getShortcutsAndWidgets();
+        AnimatorSet animatorSet = new AnimatorSet();
+        float borderSpaceDelta = adjustedBorderSpace - dp.hotseatBorderSpace;
+
+        // update the translation provider for future layout passes of hotseat icons.
+        if (isBubbleBarVisible) {
+            icons.setTranslationProvider(child -> {
+                int index = icons.indexOfChild(child);
+                return dp.iconSizePx + index * borderSpaceDelta;
+            });
+        } else {
+            icons.setTranslationProvider(null);
+        }
+
+        for (int i = 0; i < icons.getChildCount(); i++) {
+            View child = icons.getChildAt(i);
+            float tx = isBubbleBarVisible ? dp.iconSizePx + i * borderSpaceDelta : 0;
+            if (child instanceof Reorderable) {
+                MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate();
+                animatorSet.play(
+                        mtd.getTranslationX(INDEX_BUBBLE_ADJUSTMENT_ANIM).animateToValue(tx));
+            } else {
+                animatorSet.play(ObjectAnimator.ofFloat(child, VIEW_TRANSLATE_X, tx));
+            }
+        }
+        if (mQsb instanceof HorizontalInsettableView) {
+            HorizontalInsettableView horizontalInsettableQsb = (HorizontalInsettableView) mQsb;
+            ValueAnimator qsbAnimator = ValueAnimator.ofFloat(0f, 1f);
+            qsbAnimator.addUpdateListener(animation -> {
+                float fraction = qsbAnimator.getAnimatedFraction();
+                float insetFraction = isBubbleBarVisible
+                        ? (float) dp.iconSizePx * fraction / dp.hotseatQsbWidth
+                        : (float) dp.iconSizePx * (1 - fraction) / dp.hotseatQsbWidth;
+                horizontalInsettableQsb.setHorizontalInsets(insetFraction);
+            });
+            animatorSet.play(qsbAnimator);
+        }
+        animatorSet.setDuration(BUBBLE_BAR_ADJUSTMENT_ANIMATION_DURATION_MS).start();
+    }
+
     @Override
     public void setInsets(Rect insets) {
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index e3de79e..c619bc5 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -18,7 +18,6 @@
 
 import static com.android.launcher3.LauncherPrefs.GRID_NAME;
 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;
@@ -316,7 +315,7 @@
         int type = displayInfo.supportedBounds.stream()
                 .mapToInt(bounds -> displayInfo.isTablet(bounds) ? flagTablet : flagPhone)
                 .reduce(0, (a, b) -> a | b);
-        if ((type == (flagPhone | flagTablet)) && ENABLE_TWO_PANEL_HOME.get()) {
+        if (type == (flagPhone | flagTablet)) {
             // device has profiles supporting both phone and table modes
             return TYPE_MULTI_DISPLAY;
         } else if (type == flagTablet) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 606b2c4..b8e7737 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -68,6 +68,7 @@
 import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.WARM;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
 import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
+import static com.android.launcher3.model.data.LauncherAppWidgetInfo.CUSTOM_WIDGET_ID;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -144,6 +145,7 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.BaseSearchConfig;
 import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.apppairs.AppPairIcon;
 import com.android.launcher3.celllayout.CellPosMapper;
@@ -885,7 +887,7 @@
                 if (widgetInfo != null) {
                     // Since the view was just bound, also launch the configure activity if needed
                     LauncherAppWidgetProviderInfo provider = mAppWidgetManager
-                            .getLauncherAppWidgetInfo(widgetId);
+                            .getLauncherAppWidgetInfo(widgetId, info.getTargetComponent());
                     if (provider != null) {
                         new WidgetAddFlowHandler(provider)
                                 .startConfigActivity(this, widgetInfo,
@@ -1499,7 +1501,8 @@
             AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
 
         if (appWidgetInfo == null) {
-            appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
+            appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId,
+                    itemInfo.getTargetComponent());
         }
 
         if (hostView == null) {
@@ -1727,7 +1730,16 @@
         if (getStateManager().isInStableState(ALL_APPS)) {
             getStateManager().goToState(NORMAL, alreadyOnHome);
         } else {
-            showAllAppsFromIntent(alreadyOnHome);
+            AbstractFloatingView.closeAllOpenViews(this);
+            getStateManager().goToState(ALL_APPS, true /* animated */,
+                    new AnimationSuccessListener() {
+                        @Override
+                        public void onAnimationSuccess(Animator animator) {
+                            if (mAppsView.getSearchUiManager().getEditText() != null) {
+                                mAppsView.getSearchUiManager().getEditText().requestFocus();
+                            }
+                        }
+                    });
         }
     }
 
@@ -1985,8 +1997,8 @@
             // In this case, we either need to start an activity to get permission to bind
             // the widget, or we need to start an activity to configure the widget, or both.
             if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) {
-                appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider(
-                        info.componentName);
+                appWidgetId = CustomWidgetManager.INSTANCE.get(this)
+                        .allocateCustomAppWidgetId(info.componentName);
             } else {
                 appWidgetId = getAppWidgetHolder().allocateAppWidgetId();
             }
@@ -2313,6 +2325,7 @@
      * <p>
      * Implementation of the method from LauncherModel.Callbacks.
      */
+    @Override
     public void startBinding() {
         TraceHelper.INSTANCE.beginSection("startBinding");
         // Floating panels (except the full widget sheet) are associated with individual icons. If
@@ -2632,9 +2645,10 @@
                     }
                 }
             } else {
-                appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
+                appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId,
+                        item.getTargetComponent());
                 if (appWidgetInfo == null) {
-                    if (item.appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
+                    if (item.appWidgetId <= CUSTOM_WIDGET_ID) {
                         removalReason =
                                 "CustomWidgetManager cannot find provider from that widget id.";
                     } else {
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 427eaa3..36d37c7 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -376,9 +376,10 @@
     }
 }
 
-// This is hard-coded to false for now until it is time to release this optimization. It is only
-// a var because the unit tests are setting this to true so they can run.
-@VisibleForTesting var isBootAwareStartupDataEnabled: Boolean = false
+// It is a var because the unit tests are setting this to true so they can run.
+@VisibleForTesting
+var isBootAwareStartupDataEnabled: Boolean =
+    com.android.launcher3.config.FeatureFlags.ENABLE_BOOT_AWARE_STARTUP_DATA.get()
 
 private val BOOT_AWARE_ITEMS: MutableSet<ConstantItem<*>> = mutableSetOf()
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 4b4a4a5..5ce88a3 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -19,6 +19,7 @@
 import static com.android.app.animation.Interpolators.SCROLL;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
+import static com.android.launcher3.testing.shared.TestProtocol.SCROLL_FINISHED_MESSAGE;
 import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
 import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
 import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
@@ -492,7 +493,8 @@
      */
     protected void onPageEndTransition() {
         mCurrentPageScrollDiff = 0;
-        AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
+        AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
+                SCROLL_FINISHED_MESSAGE);
         AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
                 AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
         if (mOnPageTransitionEndCallback != null) {
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 5e7f21b..b22b690 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.CellLayout.FOLDER;
 import static com.android.launcher3.CellLayout.HOTSEAT;
 import static com.android.launcher3.CellLayout.WORKSPACE;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_BUBBLE_ADJUSTMENT_ANIM;
 import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_WIDGET_CENTERING;
 
 import android.app.WallpaperManager;
@@ -33,6 +34,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.celllayout.CellLayoutLayoutParams;
 import com.android.launcher3.folder.FolderIcon;
@@ -62,6 +65,9 @@
     private final ActivityContext mActivity;
     private boolean mInvertIfRtl = false;
 
+    @Nullable
+    private TranslationProvider mTranslationProvider = null;
+
     public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) {
         super(context);
         mActivity = ActivityContext.lookupContext(context);
@@ -229,6 +235,16 @@
                     childLeft, childTop, childLeft + lp.width, childTop + lp.height);
         }
         child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
+        if (mTranslationProvider != null) {
+            final float tx = mTranslationProvider.getTranslationX(child);
+            if (child instanceof Reorderable) {
+                ((Reorderable) child).getTranslateDelegate()
+                        .getTranslationX(INDEX_BUBBLE_ADJUSTMENT_ANIM)
+                        .setValue(tx);
+            } else {
+                child.setTranslationX(tx);
+            }
+        }
 
         if (lp.dropped) {
             lp.dropped = false;
@@ -298,4 +314,13 @@
             cl.clearFolderLeaveBehind();
         }
     }
+
+    void setTranslationProvider(@Nullable TranslationProvider provider) {
+        mTranslationProvider = provider;
+    }
+
+    /** Provides translation values to apply when laying out child views. */
+    interface TranslationProvider {
+        float getTranslationX(View child);
+    }
 }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index e8c6ff9..7b27a71 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -16,13 +16,14 @@
 
 package com.android.launcher3;
 
+import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction;
+
 import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
-import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ICON_BADGED;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
-import android.annotation.TargetApi;
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.Person;
@@ -33,6 +34,9 @@
 import android.content.pm.ShortcutInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BlendMode;
+import android.graphics.BlendModeColorFilter;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.LightingColorFilter;
@@ -43,8 +47,10 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.DeadObjectException;
@@ -59,6 +65,7 @@
 import android.text.style.TtsSpan;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.Pair;
 import android.util.TypedValue;
 import android.view.MotionEvent;
 import android.view.View;
@@ -68,10 +75,13 @@
 import androidx.annotation.ChecksSdkIntAtLeast;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.graphics.TintedDrawableSpan;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.ShortcutCachingLogic;
 import com.android.launcher3.icons.ThemedIconDrawable;
 import com.android.launcher3.model.data.ItemInfo;
@@ -571,103 +581,112 @@
     }
 
     /**
-     * Returns the full drawable for info without any flattening or pre-processing.
+     * Returns the full drawable for info as multiple layers of AdaptiveIconDrawable. The second
+     * drawable in the Pair is the badge used with the icon.
      *
-     * @param shouldThemeIcon If true, will theme icons when applicable
-     * @param outObj this is set to the internal data associated with {@code info},
-     *               eg {@link LauncherActivityInfo} or {@link ShortcutInfo}.
+     * @param useTheme If true, will theme icons when applicable
      */
-    @TargetApi(Build.VERSION_CODES.TIRAMISU)
-    public static Drawable getFullDrawable(Context context, ItemInfo info, int width, int height,
-            boolean shouldThemeIcon, Object[] outObj, boolean[] outIsIconThemed) {
-        Drawable icon = loadFullDrawableWithoutTheme(context, info, width, height, outObj);
-        if (ATLEAST_T && icon instanceof AdaptiveIconDrawable && shouldThemeIcon) {
-            AdaptiveIconDrawable aid = (AdaptiveIconDrawable) icon.mutate();
-            Drawable mono = aid.getMonochrome();
-            if (mono != null && Themes.isThemedIconEnabled(context)) {
-                outIsIconThemed[0] = true;
-                int[] colors = ThemedIconDrawable.getColors(context);
-                mono = mono.mutate();
-                mono.setTint(colors[1]);
-                return new AdaptiveIconDrawable(new ColorDrawable(colors[0]), mono);
-            }
-        }
-        return icon;
-    }
-
-    private static Drawable loadFullDrawableWithoutTheme(Context context, ItemInfo info,
-            int width, int height, Object[] outObj) {
-        ActivityContext activity = ActivityContext.lookupContext(context);
+    @SuppressLint("UseCompatLoadingForDrawables")
+    @Nullable
+    @WorkerThread
+    public static <T extends Context & ActivityContext> Pair<AdaptiveIconDrawable, Drawable>
+            getFullDrawable(T context, ItemInfo info, int width, int height, boolean useTheme) {
+        useTheme &= Themes.isThemedIconEnabled(context);
         LauncherAppState appState = LauncherAppState.getInstance(context);
+        Drawable mainIcon = null;
+
+        Drawable badge = null;
+        if ((info instanceof ItemInfoWithIcon iiwi) && !iiwi.usingLowResIcon()) {
+            badge = iiwi.bitmap.getBadgeDrawable(context, useTheme);
+        }
+
         if (info instanceof PendingAddShortcutInfo) {
             ShortcutConfigActivityInfo activityInfo =
                     ((PendingAddShortcutInfo) info).getActivityInfo(context);
-            outObj[0] = activityInfo;
-            return activityInfo.getFullResIcon(appState.getIconCache());
-        }
-        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+            mainIcon = activityInfo.getFullResIcon(appState.getIconCache());
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
             LauncherActivityInfo activityInfo = context.getSystemService(LauncherApps.class)
                     .resolveActivity(info.getIntent(), info.user);
-            outObj[0] = activityInfo;
-            return activityInfo == null ? null : LauncherAppState.getInstance(context)
-                    .getIconProvider().getIcon(
-                            activityInfo, activity.getDeviceProfile().inv.fillResIconDpi);
+            if (activityInfo == null) {
+                return null;
+            }
+            mainIcon = appState.getIconProvider().getIcon(
+                    activityInfo, appState.getInvariantDeviceProfile().fillResIconDpi);
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-            List<ShortcutInfo> si = ShortcutKey.fromItemInfo(info)
+            List<ShortcutInfo> siList = ShortcutKey.fromItemInfo(info)
                     .buildRequest(context)
                     .query(ShortcutRequest.ALL);
-            if (si.isEmpty()) {
+            if (siList.isEmpty()) {
                 return null;
             } else {
-                outObj[0] = si.get(0);
-                return ShortcutCachingLogic.getIcon(context, si.get(0),
+                ShortcutInfo si = siList.get(0);
+                mainIcon = ShortcutCachingLogic.getIcon(context, si,
                         appState.getInvariantDeviceProfile().fillResIconDpi);
+                // Only fetch badge if the icon is on workspace
+                if (info.id != ItemInfo.NO_ID && badge == null) {
+                    badge = appState.getIconCache().getShortcutInfoBadge(si)
+                            .newIcon(context, FLAG_THEMED);
+                }
             }
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
             FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
-                    activity, info.id, new Point(width, height));
+                    context, info.id, new Point(width, height));
             if (icon == null) {
                 return null;
             }
-            outObj[0] = icon;
-            return icon;
-        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
-                && info instanceof ItemInfoWithIcon) {
-            return ((ItemInfoWithIcon) info).bitmap.newIcon(context);
-        } else {
+            mainIcon =  icon;
+            badge = icon.getBadge();
+        }
+
+        if (mainIcon == null) {
             return null;
         }
-    }
-
-    /**
-     * For apps icons and shortcut icons that have badges, this method creates a drawable that can
-     * later on be rendered on top of the layers for the badges. For app icons, work profile badges
-     * can only be applied. For deep shortcuts, when dragged from the pop up container, there's no
-     * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge
-     **/
-    @TargetApi(Build.VERSION_CODES.O)
-    public static Drawable getBadge(Context context, ItemInfo info, Object obj,
-            boolean isIconThemed) {
-        LauncherAppState appState = LauncherAppState.getInstance(context);
-        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-            boolean iconBadged = (info instanceof ItemInfoWithIcon)
-                    && (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0;
-            if ((info.id == ItemInfo.NO_ID && !iconBadged)
-                    || !(obj instanceof ShortcutInfo)) {
-                // The item is not yet added on home screen.
-                return new ColorDrawable(Color.TRANSPARENT);
-            }
-            ShortcutInfo si = (ShortcutInfo) obj;
-            return LauncherAppState.getInstance(appState.getContext())
-                    .getIconCache().getShortcutInfoBadge(si).newIcon(context, FLAG_THEMED);
-        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
-            return ((FolderAdaptiveIcon) obj).getBadge();
+        AdaptiveIconDrawable result;
+        if (mainIcon instanceof AdaptiveIconDrawable aid) {
+            result = aid;
         } else {
-            return Process.myUserHandle().equals(info.user)
-                    ? new ColorDrawable(Color.TRANSPARENT)
-                    : context.getDrawable(isIconThemed
-                            ? R.drawable.ic_work_app_badge_themed : R.drawable.ic_work_app_badge);
+            // Wrap the main icon in AID
+            try (LauncherIcons li = LauncherIcons.obtain(context)) {
+                result = li.wrapToAdaptiveIcon(mainIcon);
+            }
         }
+        if (result == null) {
+            return null;
+        }
+
+        // Inject monochrome icon drawable
+        if (ATLEAST_T && useTheme) {
+            result.mutate();
+            int[] colors = ThemedIconDrawable.getColors(context);
+            Drawable mono = result.getMonochrome();
+
+            if (mono != null) {
+                mono.setTint(colors[1]);
+            } else  if (info instanceof ItemInfoWithIcon iiwi) {
+                // Inject a previously generated monochrome icon
+                Bitmap monoBitmap = iiwi.bitmap.getMono();
+                if (monoBitmap != null) {
+                    // Use BitmapDrawable instead of FastBitmapDrawable so that the colorState is
+                    // preserved in constantState
+                    mono = new BitmapDrawable(monoBitmap);
+                    mono.setColorFilter(new BlendModeColorFilter(colors[1], BlendMode.SRC_IN));
+                    // Inset the drawable according to the AdaptiveIconDrawable layers
+                    mono = new InsetDrawable(mono, getExtraInsetFraction() / 2);
+                }
+            }
+            if (mono != null) {
+                result = new AdaptiveIconDrawable(new ColorDrawable(colors[0]), mono);
+            }
+        }
+
+        if (badge == null) {
+            badge = Process.myUserHandle().equals(info.user)
+                    ? new ColorDrawable(Color.TRANSPARENT)
+                    : context.getDrawable(useTheme
+                            ? R.drawable.ic_work_app_badge_themed
+                            : R.drawable.ic_work_app_badge);
+        }
+        return Pair.create(result, badge);
     }
 
     public static float squaredHypot(float x, float y) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 8be8fed..a1a3974 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3413,7 +3413,8 @@
             if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
                 widgetInfo = widgetHelper.findProvider(item.providerName, item.user);
             } else {
-                widgetInfo = widgetHelper.getLauncherAppWidgetInfo(item.appWidgetId);
+                widgetInfo = widgetHelper.getLauncherAppWidgetInfo(item.appWidgetId,
+                        item.getTargetComponent());
             }
 
             if (widgetInfo != null) {
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 28a3312..144381c 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth;
 
-import android.animation.LayoutTransition;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -92,8 +91,6 @@
 
         setInsets(mActivityContext.getDeviceProfile().getInsets());
         updateStringFromCache();
-
-        getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
     }
 
     @Override
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 24cc0ac..d37b1f0 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -74,38 +74,12 @@
         Log.d(TestProtocol.PERMANENT_DIAG_TAG, "sendStateEventToTest: " + stateOrdinal);
     }
 
-    public static void sendScrollFinishedEventToTest(Context context) {
+    public static void sendTestProtocolEventToTest(Context context, String testProtocolEvent) {
         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
         if (accessibilityManager == null) return;
 
-        sendEventToTest(accessibilityManager, context, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
+        sendEventToTest(accessibilityManager, context, testProtocolEvent, null);
     }
-
-    public static void sendPauseDetectedEventToTest(Context context) {
-        final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
-        if (accessibilityManager == null) return;
-
-        sendEventToTest(accessibilityManager, context, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
-    }
-
-    public static void sendDismissAnimationEndsEventToTest(Context context) {
-        final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
-        if (accessibilityManager == null) return;
-
-        sendEventToTest(accessibilityManager, context, TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE,
-                null);
-    }
-
-    /**
-     * Notify running tests of a folder opened.
-     */
-    public static void sendFolderOpenedEventToTest(Context context) {
-        final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
-        if (accessibilityManager == null) return;
-
-        sendEventToTest(accessibilityManager, context, TestProtocol.FOLDER_OPENED_MESSAGE, null);
-    }
-
     private static void sendEventToTest(
             AccessibilityManager accessibilityManager,
             Context context, String eventTag, Bundle data) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 4285755..8b8f50a 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -27,6 +27,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.BuildConfig;
+import com.android.launcher3.Flags;
 import com.android.launcher3.Utilities;
 
 import java.util.function.Predicate;
@@ -141,7 +142,7 @@
 
     // TODO(Block 6): Clean up flags
     public static final BooleanFlag ENABLE_ALL_APPS_SEARCH_IN_TASKBAR = getDebugFlag(270393900,
-            "ENABLE_ALL_APPS_SEARCH_IN_TASKBAR", DISABLED,
+            "ENABLE_ALL_APPS_SEARCH_IN_TASKBAR", TEAMFOOD,
             "Enables Search box in Taskbar All Apps.");
 
     public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(270395140,
@@ -175,22 +176,17 @@
             "MULTI_SELECT_EDIT_MODE", DISABLED, "Enable new multi-select edit mode "
                     + "for home screen");
 
+    public static final BooleanFlag SMARTSPACE_AS_A_WIDGET = getDebugFlag(299181941,
+            "SMARTSPACE_AS_A_WIDGET", DISABLED, "Enable SmartSpace as a widget");
+
     // TODO(Block 10): Clean up flags
     public static final BooleanFlag ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION = getDebugFlag(270614790,
             "ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION", DISABLED,
             "Enables predictive back animation from all apps and widgets to home");
 
     // TODO(Block 11): Clean up flags
-    public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(270392643,
-            "ENABLE_TWO_PANEL_HOME", ENABLED,
-            "Uses two panel on home screen. Only applicable on large screen devices.");
-
-    public static final BooleanFlag FOLDABLE_WORKSPACE_REORDER = getDebugFlag(270395070,
-            "FOLDABLE_WORKSPACE_REORDER", DISABLED,
-            "In foldables, when reordering the icons and widgets, is now going to use both sides");
-
     public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(270395274,
-            "FOLDABLE_SINGLE_PAGE", ENABLED, "Use a single page for the workspace");
+            "FOLDABLE_SINGLE_PAGE", DISABLED, "Use a single page for the workspace");
 
     public static final BooleanFlag ENABLE_PARAMETRIZE_REORDER = getDebugFlag(289420844,
             "ENABLE_PARAMETRIZE_REORDER", DISABLED,
@@ -213,6 +209,10 @@
     public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(270395798,
             "ENABLE_TRANSIENT_TASKBAR", ENABLED, "Enables transient taskbar.");
 
+    public static final BooleanFlag ENABLE_TASKBAR_NO_RECREATION = getDebugFlag(299193589,
+            "ENABLE_TASKBAR_NO_RECREATION", DISABLED,
+            "Enables taskbar with no recreation from lifecycle changes of TaskbarActivityContext.");
+
     // TODO(Block 16): Clean up flags
     // When enabled the promise icon is visible in all apps while installation an app.
     public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(270390012,
@@ -265,6 +265,11 @@
             "Enables taskbar pinning to allow user to switch between transient and persistent "
                     + "taskbar flavors");
 
+    public static final BooleanFlag ENABLE_BOOT_AWARE_STARTUP_DATA = getDebugFlag(251502424,
+            "ENABLE_BOOT_AWARE_STARTUP_DATA", DISABLED, "Marks LauncherPref data as (and allows it "
+                    + "to) available while the device is locked. Enabling this causes a 1-time "
+                    + "migration of certain SharedPreferences data. Improves startup latency.");
+
     // TODO(Block 18): Clean up flags
     public static final BooleanFlag ENABLE_APP_PAIRS = getDebugFlag(274189428,
             "ENABLE_APP_PAIRS", DISABLED,
@@ -308,13 +313,23 @@
                     + "start receiving the events");
 
     // TODO(Block 23): Clean up flags
+    // Aconfig migration complete for ENABLE_GRID_ONLY_OVERVIEW.
+    @VisibleForTesting
     public static final BooleanFlag ENABLE_GRID_ONLY_OVERVIEW = getDebugFlag(270397206,
             "ENABLE_GRID_ONLY_OVERVIEW", TEAMFOOD,
             "Enable a grid-only overview without a focused task.");
+    public static boolean enableGridOnlyOverview() {
+        return ENABLE_GRID_ONLY_OVERVIEW.get() || Flags.enableGridOnlyOverview();
+    }
 
+    // Aconfig migration complete for ENABLE_CURSOR_HOVER_STATES.
+    @VisibleForTesting
     public static final BooleanFlag ENABLE_CURSOR_HOVER_STATES = getDebugFlag(243191650,
             "ENABLE_CURSOR_HOVER_STATES", TEAMFOOD,
             "Enables cursor hover states for certain elements.");
+    public static boolean enableCursorHoverStates() {
+        return ENABLE_CURSOR_HOVER_STATES.get() || Flags.enableCursorHoverStates();
+    }
 
     // TODO(Block 24): Clean up flags
     public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(270393455,
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index adfdc89..c2d9e02 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -20,7 +20,6 @@
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-import static com.android.launcher3.Utilities.getBadge;
 import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
@@ -45,6 +44,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -244,13 +244,12 @@
     public void setItemInfo(final ItemInfo info) {
         // Load the adaptive icon on a background thread and add the view in ui thread.
         MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> {
-            Object[] outObj = new Object[1];
-            boolean[] outIsIconThemed = new boolean[1];
             int w = mWidth;
             int h = mHeight;
-            Drawable dr = Utilities.getFullDrawable(mActivity, info, w, h,
-                    true /* shouldThemeIcon */, outObj, outIsIconThemed);
-            if (dr instanceof AdaptiveIconDrawable) {
+            Pair<AdaptiveIconDrawable, Drawable> fullDrawable = Utilities.getFullDrawable(
+                    mActivity, info, w, h, true /* shouldThemeIcon */);
+            if (fullDrawable != null) {
+                AdaptiveIconDrawable adaptiveIcon = fullDrawable.first;
                 int blurMargin = (int) mActivity.getResources()
                         .getDimension(R.dimen.blur_size_medium_outline) / 2;
 
@@ -258,24 +257,15 @@
                 bounds.inset(blurMargin, blurMargin);
                 // Badge is applied after icon normalization so the bounds for badge should not
                 // be scaled down due to icon normalization.
-                mBadge = getBadge(mActivity, info, outObj[0], outIsIconThemed[0]);
+                mBadge = fullDrawable.second;
                 FastBitmapDrawable.setBadgeBounds(mBadge, bounds);
 
-                // Do not draw the background in case of folder as its translucent
-                final boolean shouldDrawBackground = !(dr instanceof FolderAdaptiveIcon);
-
                 try (LauncherIcons li = LauncherIcons.obtain(mActivity)) {
-                    Drawable nDr; // drawable to be normalized
-                    if (shouldDrawBackground) {
-                        nDr = dr;
-                    } else {
-                        // Since we just want the scale, avoid heavy drawing operations
-                        nDr = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null);
-                    }
-                    Utilities.scaleRectAboutCenter(bounds,
-                            li.getNormalizer().getScale(nDr, null, null, null));
+                    // Since we just want the scale, avoid heavy drawing operations
+                    Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(
+                            new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null),
+                            null, null, null));
                 }
-                AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
 
                 // Shrink very tiny bit so that the clip path is smaller than the original bitmap
                 // that has anti aliased edges and shadows.
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 55a539a..57e1641 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -25,6 +25,7 @@
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
+import static com.android.launcher3.testing.shared.TestProtocol.FOLDER_OPENED_MESSAGE;
 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.animation.Animator;
@@ -644,6 +645,11 @@
      * is played.
      */
     private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) {
+        if (items == null || items.size() <= 1) {
+            Log.d(TAG, "Couldn't animate folder open because items is: " + items);
+            return;
+        }
+
         Folder openFolder = getOpen(mActivityContext);
         if (openFolder != null && openFolder != this) {
             // Close any open folder before opening a folder.
@@ -692,7 +698,8 @@
             public void onAnimationEnd(Animator animation) {
                 setState(STATE_OPEN);
                 announceAccessibilityChanges();
-                AccessibilityManagerCompat.sendFolderOpenedEventToTest(getContext());
+                AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
+                        FOLDER_OPENED_MESSAGE);
 
                 mContent.setFocusOnFirstChild();
             }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 53d0efb..d00d901 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.folder;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
+import static com.android.launcher3.config.FeatureFlags.enableCursorHoverStates;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
@@ -805,7 +805,7 @@
     @Override
     public void onHoverChanged(boolean hovered) {
         super.onHoverChanged(hovered);
-        if (ENABLE_CURSOR_HOVER_STATES.get()) {
+        if (enableCursorHoverStates()) {
             mBackground.setHovered(hovered);
         }
     }
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index b51373c..4208343 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.graphics;
 
+import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT_APP_ROW;
+
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -100,7 +102,8 @@
             height = mView.getHeight();
         }
 
-        if (mView instanceof BubbleTextView) {
+        if (mView instanceof BubbleTextView btv
+                && btv.getIconDisplay() == DISPLAY_SEARCH_RESULT_APP_ROW) {
             FastBitmapDrawable icon = ((BubbleTextView) mView).getIcon();
             Drawable drawable = icon.getConstantState().newDrawable();
             float xInset = (float) blurSizeOutline / (float) (width + blurSizeOutline);
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 265378c..4307566 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -654,7 +654,19 @@
         LAUNCHER_APP_PAIR_SAVE(1456),
 
         @UiEvent(doc = "App launched through pending intent")
-        LAUNCHER_APP_LAUNCH_PENDING_INTENT(1394)
+        LAUNCHER_APP_LAUNCH_PENDING_INTENT(1394),
+
+        @UiEvent(doc = "User long pressed on taskbar divider icon to open popup menu")
+        LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN(1488),
+
+        @UiEvent(doc = "User long pressed on taskbar divider icon to close popup menu")
+        LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE(1489),
+
+        @UiEvent(doc = "User has pinned taskbar using taskbar divider menu")
+        LAUNCHER_TASKBAR_PINNED(1490),
+
+        @UiEvent(doc = "User has unpinned taskbar using taskbar divider menu")
+        LAUNCHER_TASKBAR_UNPINNED(1491)
 
         // ADD MORE
         ;
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index dbb29b8..68544cf 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -305,6 +305,10 @@
             Executor pendingExecutor = pendingTasks::add;
             bindWorkspaceItems(otherWorkspaceItems, pendingExecutor);
             bindAppWidgets(otherAppWidgets, pendingExecutor);
+
+            StringCache cacheClone = mBgDataModel.stringCache.clone();
+            executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
+
             executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor);
             pendingExecutor.execute(
                     () -> {
@@ -319,9 +323,6 @@
                         c.onInitialBindComplete(
                                 currentScreenIds, pendingTasks, workspaceItemCount, isBindSync);
                     }, mUiExecutor);
-
-            StringCache cacheClone = mBgDataModel.stringCache.clone();
-            executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
         }
 
         private void bindWorkspaceItems(
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index c233872..78875a3 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -539,8 +539,8 @@
                             verifyPackage(cn.getPackageName());
 
                             int widgetId = c.getInt(indexAppWidgetId);
-                            LauncherAppWidgetProviderInfo pInfo =
-                                    widgetManagerHelper.getLauncherAppWidgetInfo(widgetId);
+                            LauncherAppWidgetProviderInfo pInfo = widgetManagerHelper
+                                    .getLauncherAppWidgetInfo(widgetId, cn);
                             Point spans = null;
                             if (pInfo != null) {
                                 spans = pInfo.getMinSpans();
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 0e68db2..08a6cf0 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -95,6 +95,7 @@
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -740,8 +741,13 @@
 
                     ComponentKey providerKey = new ComponentKey(component, c.user);
                     if (!mWidgetProvidersMap.containsKey(providerKey)) {
-                        mWidgetProvidersMap.put(providerKey,
-                                widgetHelper.findProvider(component, c.user));
+                        if (customWidget) {
+                            mWidgetProvidersMap.put(providerKey, CustomWidgetManager.INSTANCE
+                                    .get(mApp.getContext()).getWidgetProvider(component));
+                        } else {
+                            mWidgetProvidersMap.put(providerKey,
+                                    widgetHelper.findProvider(component, c.user));
+                        }
                     }
                     final AppWidgetProviderInfo provider = mWidgetProvidersMap.get(providerKey);
 
@@ -814,7 +820,8 @@
                             return;
                         }
                         LauncherAppWidgetProviderInfo widgetProviderInfo =
-                                widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
+                                widgetHelper.getLauncherAppWidgetInfo(appWidgetId,
+                                        appWidgetInfo.getTargetComponent());
                         if (widgetProviderInfo != null
                                 && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
                                 || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index 7e6cbef..6c2f589 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -23,8 +23,6 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
-import android.os.Build;
-import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 
@@ -177,12 +175,6 @@
         info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
                 ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
 
-        if (appInfo.targetSdkVersion >= Build.VERSION_CODES.O
-                && Process.myUserHandle().equals(lai.getUser())) {
-            // The icon for a non-primary user is badged, hence it's not exactly an adaptive icon.
-            info.runtimeStatusFlags |= FLAG_ADAPTIVE_ICON;
-        }
-
         // Sets the progress level, installation and incremental download flags.
         info.setProgressLevel(
                 PackageManagerHelper.getLoadingProgress(lai),
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index b4a935a..dc180d8 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -83,17 +83,6 @@
     public static final int FLAG_SYSTEM_MASK = FLAG_SYSTEM_YES | FLAG_SYSTEM_NO;
 
     /**
-     * Flag indicating that the icon is an {@link android.graphics.drawable.AdaptiveIconDrawable}
-     * that can be optimized in various way.
-     */
-    public static final int FLAG_ADAPTIVE_ICON = 1 << 8;
-
-    /**
-     * Flag indicating that the icon is badged.
-     */
-    public static final int FLAG_ICON_BADGED = 1 << 9;
-
-    /**
      * The icon is being installed. If {@link WorkspaceItemInfo#FLAG_RESTORED_ICON} or
      * {@link WorkspaceItemInfo#FLAG_AUTOINSTALL_ICON} is set, then the icon is either being
      * installed or is in a broken state.
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 1f908eb..575551b 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -120,6 +120,10 @@
                 deletedShortcuts.add(lc.id);
                 continue;
             }
+            if (TextUtils.isEmpty(lc.getTitle())) {
+                deletedShortcuts.add(lc.id);
+                continue;
+            }
 
             // Make sure the target intent can be launched without any permissions. Otherwise remove
             // the shortcut
diff --git a/src/com/android/launcher3/responsive/HotseatSpecs.kt b/src/com/android/launcher3/responsive/HotseatSpecs.kt
index 482508d..37a682f 100644
--- a/src/com/android/launcher3/responsive/HotseatSpecs.kt
+++ b/src/com/android/launcher3/responsive/HotseatSpecs.kt
@@ -21,14 +21,28 @@
 import com.android.launcher3.R
 import com.android.launcher3.util.ResourceHelper
 
-class HotseatSpecs(val specs: List<HotseatSpec>) {
+class HotseatSpecs(widthSpecs: List<HotseatSpec>, heightSpecs: List<HotseatSpec>) {
+
+    val widthSpecs: List<HotseatSpec>
+    val heightSpecs: List<HotseatSpec>
+
+    init {
+        this.widthSpecs = widthSpecs.sortedBy { it.maxAvailableSize }
+        this.heightSpecs = heightSpecs.sortedBy { it.maxAvailableSize }
+    }
 
     fun getCalculatedHeightSpec(availableHeight: Int): CalculatedHotseatSpec {
-        val spec = specs.firstOrNull { availableHeight <= it.maxAvailableSize }
+        val spec = heightSpecs.firstOrNull { availableHeight <= it.maxAvailableSize }
         check(spec != null) { "No available height spec found within $availableHeight." }
         return CalculatedHotseatSpec(availableHeight, spec)
     }
 
+    fun getCalculatedWidthSpec(availableWidth: Int): CalculatedHotseatSpec {
+        val spec = widthSpecs.firstOrNull { availableWidth <= it.maxAvailableSize }
+        check(spec != null) { "No available width spec found within $availableWidth." }
+        return CalculatedHotseatSpec(availableWidth, spec)
+    }
+
     companion object {
         private const val XML_HOTSEAT_SPEC = "hotseatSpec"
 
@@ -36,7 +50,9 @@
         fun create(resourceHelper: ResourceHelper): HotseatSpecs {
             val parser = ResponsiveSpecsParser(resourceHelper)
             val specs = parser.parseXML(XML_HOTSEAT_SPEC, ::HotseatSpec)
-            return HotseatSpecs(specs.filter { it.specType == ResponsiveSpec.SpecType.HEIGHT })
+            val (widthSpecs, heightSpecs) =
+                specs.partition { it.specType == ResponsiveSpec.SpecType.WIDTH }
+            return HotseatSpecs(widthSpecs, heightSpecs)
         }
     }
 }
@@ -44,7 +60,8 @@
 data class HotseatSpec(
     val maxAvailableSize: Int,
     val specType: ResponsiveSpec.SpecType,
-    val hotseatQsbSpace: SizeSpec
+    val hotseatQsbSpace: SizeSpec,
+    val edgePadding: SizeSpec
 ) {
 
     init {
@@ -63,7 +80,8 @@
                         R.styleable.ResponsiveSpec_specType,
                         ResponsiveSpec.SpecType.HEIGHT.ordinal
                     )],
-        hotseatQsbSpace = specs.getOrError(SizeSpec.XmlTags.HOTSEAT_QSB_SPACE)
+        hotseatQsbSpace = specs.getOrError(SizeSpec.XmlTags.HOTSEAT_QSB_SPACE),
+        edgePadding = specs.getOrError(SizeSpec.XmlTags.EDGE_PADDING)
     )
 
     fun isValid(): Boolean {
@@ -82,7 +100,10 @@
     }
 
     private fun allSpecsAreValid(): Boolean {
-        return hotseatQsbSpace.isValid() && hotseatQsbSpace.onlyFixedSize()
+        return hotseatQsbSpace.isValid() &&
+            hotseatQsbSpace.onlyFixedSize() &&
+            edgePadding.isValid() &&
+            edgePadding.onlyFixedSize()
     }
 
     companion object {
@@ -95,13 +116,18 @@
     var hotseatQsbSpace: Int = 0
         private set
 
+    var edgePadding: Int = 0
+        private set
+
     init {
         hotseatQsbSpace = spec.hotseatQsbSpace.getCalculatedValue(availableSpace)
+        edgePadding = spec.edgePadding.getCalculatedValue(availableSpace)
     }
 
     override fun hashCode(): Int {
         var result = availableSpace.hashCode()
         result = 31 * result + hotseatQsbSpace.hashCode()
+        result = 31 * result + edgePadding.hashCode()
         result = 31 * result + spec.hashCode()
         return result
     }
@@ -110,12 +136,14 @@
         return other is CalculatedHotseatSpec &&
             availableSpace == other.availableSpace &&
             hotseatQsbSpace == other.hotseatQsbSpace &&
+            edgePadding == other.edgePadding &&
             spec == other.spec
     }
 
     override fun toString(): String {
         return "${this::class.simpleName}(" +
             "availableSpace=$availableSpace, hotseatQsbSpace=$hotseatQsbSpace, " +
+            "edgePadding=$edgePadding, " +
             "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" +
             ")"
     }
diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecs.kt b/src/com/android/launcher3/responsive/ResponsiveSpecs.kt
index 72a0ea4..a43c44a 100644
--- a/src/com/android/launcher3/responsive/ResponsiveSpecs.kt
+++ b/src/com/android/launcher3/responsive/ResponsiveSpecs.kt
@@ -24,10 +24,9 @@
  * @param widthSpecs List of width responsive specifications
  * @param heightSpecs List of height responsive specifications
  */
-abstract class ResponsiveSpecs<T : ResponsiveSpec>(
-    val widthSpecs: List<T>,
+abstract class ResponsiveSpecs<T : ResponsiveSpec>(widthSpecs: List<T>, heightSpecs: List<T>) {
+    val widthSpecs: List<T>
     val heightSpecs: List<T>
-) {
 
     init {
         check(widthSpecs.isNotEmpty() && heightSpecs.isNotEmpty()) {
@@ -35,6 +34,9 @@
                 "width list size = ${widthSpecs.size}; " +
                 "height list size = ${heightSpecs.size}."
         }
+
+        this.widthSpecs = widthSpecs.sortedBy { it.maxAvailableSize }
+        this.heightSpecs = heightSpecs.sortedBy { it.maxAvailableSize }
     }
 
     /**
diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt
index c868c9f..2db843b 100644
--- a/src/com/android/launcher3/responsive/SizeSpec.kt
+++ b/src/com/android/launcher3/responsive/SizeSpec.kt
@@ -121,6 +121,7 @@
         const val GUTTER = "gutter"
         const val CELL_SIZE = "cellSize"
         const val HOTSEAT_QSB_SPACE = "hotseatQsbSpace"
+        const val EDGE_PADDING = "edgePadding"
     }
 
     companion object {
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 360ff7e..9ac8f6b 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -27,7 +27,6 @@
 import android.animation.AnimatorSet;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.Log;
 
 import androidx.annotation.FloatRange;
 
@@ -36,7 +35,6 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.shared.TestProtocol;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -227,7 +225,6 @@
 
     private void goToState(
             STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
-        Log.d(TestProtocol.OVERVIEW_OVER_HOME, "go to state " + state);
 
         animated &= areAnimatorsEnabled();
         if (mActivity.isInState(state)) {
@@ -383,8 +380,6 @@
         mState = state;
         mActivity.onStateSetStart(mState);
 
-        Log.d(TestProtocol.OVERVIEW_OVER_HOME, "Notifying listeners for state transition start"
-                + " to state: " + state.toString());
         for (int i = mListeners.size() - 1; i >= 0; i--) {
             mListeners.get(i).onStateTransitionStart(state);
         }
@@ -402,8 +397,6 @@
             setRestState(null);
         }
 
-        Log.d(TestProtocol.OVERVIEW_OVER_HOME, "Notifying " + mListeners.size() + " listeners "
-                + "for end transition for state: " + state.toString());
         for (int i = mListeners.size() - 1; i >= 0; i--) {
             mListeners.get(i).onStateTransitionComplete(state);
         }
@@ -441,7 +434,6 @@
      * Cancels the current animation.
      */
     public void cancelAnimation() {
-        Log.d(TestProtocol.OVERVIEW_OVER_HOME, "current animation cancelled");
         mConfig.reset();
         // It could happen that a new animation is set as a result of an endListener on the
         // existing animation.
@@ -465,7 +457,6 @@
      * @param toState The state we are animating towards.
      */
     public void setCurrentAnimation(AnimatorSet anim, STATE_TYPE toState) {
-        Log.d(TestProtocol.OVERVIEW_OVER_HOME, "setting animation to " + toState.toString());
         cancelAnimation();
         setCurrentAnimation(anim);
         anim.addListener(createStateAnimationListener(toState));
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 5d412ff..a75f326 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -16,8 +16,7 @@
 package com.android.launcher3.testing;
 
 import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
+import static com.android.launcher3.config.FeatureFlags.enableGridOnlyOverview;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
@@ -33,6 +32,7 @@
 import android.view.WindowInsets;
 
 import androidx.annotation.Nullable;
+import androidx.core.view.WindowInsetsCompat;
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
@@ -143,6 +143,14 @@
                 }, this::getCurrentActivity);
             }
 
+            case TestProtocol.REQUEST_IME_INSETS: {
+                return getUIProperty(Bundle::putParcelable, activity -> {
+                    WindowInsetsCompat insets = WindowInsetsCompat.toWindowInsetsCompat(
+                            activity.getWindow().getDecorView().getRootWindowInsets());
+                    return insets.getInsets(WindowInsetsCompat.Type.ime()).toPlatformInsets();
+                }, this::getCurrentActivity);
+            }
+
             case TestProtocol.REQUEST_ICON_HEIGHT: {
                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
                         mDeviceProfile.allAppsCellHeightPx);
@@ -236,12 +244,6 @@
                 return response;
             }
 
-            case TestProtocol.REQUEST_IS_TRACKPAD_GESTURE_ENABLED: {
-                response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
-                        ENABLE_TRACKPAD_GESTURE.get());
-                return response;
-            }
-
             case TestProtocol.REQUEST_ALL_APPS_TOP_PADDING: {
                 return getLauncherUIProperty(Bundle::putInt,
                         l -> l.getAppsView().getActiveRecyclerView().getClipBounds().top);
@@ -256,7 +258,7 @@
 
             case TestProtocol.REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW: {
                 response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
-                        ENABLE_GRID_ONLY_OVERVIEW.get());
+                        enableGridOnlyOverview());
                 return response;
             }
 
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index 3db2ddf..70691f8 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -70,15 +70,12 @@
     public static void recordMotionEvent(String sequence, String message, MotionEvent event) {
         final int action = event.getAction();
         if (Utilities.isRunningInTestHarness() && action != MotionEvent.ACTION_MOVE) {
-            // "Expecting" in TAPL ACTION_DOWN, UP and CANCEL events was thought to be producing
-            // considerable noise in tests due to failed checks for expected events. So we are not
-            // sending them to TAPL.
+            // "Expecting" in TAPL motion events was thought to be producing considerable noise in
+            // tests due to failed checks for expected events. So we are not sending them to TAPL.
             // Other events, such as EVENT_PILFER_POINTERS produce less noise and are thought to
             // be more useful.
-            final boolean reportToTapl = action != MotionEvent.ACTION_DOWN
-                    && action != MotionEvent.ACTION_UP
-                    && action != MotionEvent.ACTION_CANCEL;
-            recordEventSlow(sequence, message + ": " + event, reportToTapl);
+            // That's why we pass false as the value for the 'reportToTapl' parameter.
+            recordEventSlow(sequence, message + ": " + event, false);
             registerEventNotFromTest(event);
         }
     }
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 6a972eb..dc4621e 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -582,7 +582,8 @@
                 ? splitInfo.dividerHeightPercent
                 : splitInfo.dividerWidthPercent;
 
-        float scale = (float) outRect.height() / dp.availableHeightPx;
+        int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight;
+        float scale = (float) outRect.height() / (dp.availableHeightPx - taskbarHeight);
         float topTaskHeight = dp.availableHeightPx * topLeftTaskPercent;
         float scaledTopTaskHeight = topTaskHeight * scale;
         float dividerHeight = dp.availableHeightPx * dividerBarPercent;
@@ -639,7 +640,8 @@
             // Reset unused translations
             primarySnapshot.setTranslationY(0);
         } else {
-            float scale = (float) totalThumbnailHeight / dp.availableHeightPx;
+            int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight;
+            float scale = (float) totalThumbnailHeight / (dp.availableHeightPx - taskbarHeight);
             float topTaskHeight = dp.availableHeightPx * taskPercent;
             float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale);
             float scaledTopTaskHeight = topTaskHeight * scale;
diff --git a/src/com/android/launcher3/util/MultiTranslateDelegate.java b/src/com/android/launcher3/util/MultiTranslateDelegate.java
index 1cb7a45..0e32ba7 100644
--- a/src/com/android/launcher3/util/MultiTranslateDelegate.java
+++ b/src/com/android/launcher3/util/MultiTranslateDelegate.java
@@ -40,6 +40,9 @@
     // Specific for widgets
     public static final int INDEX_WIDGET_CENTERING = 3;
 
+    // Specific for hotseat items when adjusting for bubbles
+    public static final int INDEX_BUBBLE_ADJUSTMENT_ANIM = 3;
+
     public static final int COUNT = 5;
 
     private final MultiPropertyFactory<View> mTranslationX;
diff --git a/src/com/android/launcher3/util/SettingsCache.java b/src/com/android/launcher3/util/SettingsCache.java
index 0c5b722..06cb00e 100644
--- a/src/com/android/launcher3/util/SettingsCache.java
+++ b/src/com/android/launcher3/util/SettingsCache.java
@@ -154,7 +154,7 @@
      */
     public void unregister(Uri uri, OnChangeListener listener) {
         List<OnChangeListener> listenersToRemoveFrom = mListenerMap.get(uri);
-        if (!listenersToRemoveFrom.contains(listener)) {
+        if (listenersToRemoveFrom == null) {
             return;
         }
 
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 1ae43d0..f4a0225 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -77,11 +77,6 @@
     public @interface StageType {}
     ///////////////////////////////////
 
-    /**
-     * Default split ratio for launching app pair from overview.
-     */
-    public static final float DEFAULT_SPLIT_RATIO = 0.5f;
-
     public static class SplitPositionOption {
         public final int iconResId;
         public final int textResId;
@@ -116,6 +111,8 @@
         public final float leftTaskPercent;
         public final float dividerWidthPercent;
         public final float dividerHeightPercent;
+        public final int snapPosition;
+
         /**
          * If {@code true}, that means at the time of creation of this object, the
          * split-screened apps were vertically stacked. This is useful in scenarios like
@@ -135,11 +132,12 @@
         public final int rightBottomTaskId;
 
         public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
-                int rightBottomTaskId) {
+                int rightBottomTaskId, int snapPosition) {
             this.leftTopBounds = leftTopBounds;
             this.rightBottomBounds = rightBottomBounds;
             this.leftTopTaskId = leftTopTaskId;
             this.rightBottomTaskId = rightBottomTaskId;
+            this.snapPosition = snapPosition;
 
             if (rightBottomBounds.top > leftTopBounds.top) {
                 // vertical apps, horizontal divider
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 84ea871..04f2ffa 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -273,13 +273,19 @@
             final WindowInsetsController wic = root.getWindowInsetsController();
             WindowInsets insets = root.getRootWindowInsets();
             boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
-            if (wic != null && isImeShown) {
-                StatsLogManager slm  = getStatsLogManager();
-                slm.keyboardStateManager().setKeyboardState(HIDE);
+            if (wic != null) {
+                // Only hide the keyboard if it is actually showing.
+                if (isImeShown) {
+                    StatsLogManager slm = getStatsLogManager();
+                    slm.keyboardStateManager().setKeyboardState(HIDE);
 
-                // this method cannot be called cross threads
-                wic.hide(WindowInsets.Type.ime());
-                slm.logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
+                    // this method cannot be called cross threads
+                    wic.hide(WindowInsets.Type.ime());
+                    slm.logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
+                }
+
+                // If the WindowInsetsController is not null, we end here regardless of whether we
+                // hid the keyboard or not.
                 return;
             }
         }
@@ -442,6 +448,16 @@
         return CellPosMapper.DEFAULT;
     }
 
+    /** Whether bubbles are enabled. */
+    default boolean isBubbleBarEnabled() {
+        return false;
+    }
+
+    /** Whether the bubble bar has bubbles. */
+    default boolean hasBubbles() {
+        return false;
+    }
+
     /**
      * Returns the ActivityContext associated with the given Context, or throws an exception if
      * the Context is not associated with any ActivityContext.
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index b44dbeb..2f0da03 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -155,7 +155,7 @@
         setOrientation(LinearLayout.VERTICAL);
 
         mArrowView = findViewById(R.id.arrow);
-        updateArrowTipInView();
+        updateArrowTipInView(mIsPointingUp);
         setAlpha(0);
 
         // Create default open animator.
@@ -364,17 +364,18 @@
 
             // Adjust the tooltip vertically.
             @Px int viewHeight = getHeight();
+            boolean isPointingUp = mIsPointingUp;
             if (mIsPointingUp
                     ? (yCoordUpPointingTip + viewHeight > parentViewHeight)
                     : (yCoordDownPointingTip - viewHeight < 0)) {
                 // Flip the view if it exceeds the vertical bounds of screen.
-                mIsPointingUp = !mIsPointingUp;
-                updateArrowTipInView();
+                isPointingUp = !mIsPointingUp;
             }
+            updateArrowTipInView(isPointingUp);
             // Place the tooltip such that its top is at yCoordUpPointingTip if arrow is displayed
             // pointing upwards, otherwise place it such that its bottom is at
             // yCoordDownPointingTip.
-            setY(mIsPointingUp ? yCoordUpPointingTip : yCoordDownPointingTip - viewHeight);
+            setY(isPointingUp ? yCoordUpPointingTip : yCoordDownPointingTip - viewHeight);
 
             // Adjust the arrow's relative position on tooltip to make sure the actual position of
             // arrow's pointed tip is always at arrowXCoord.
@@ -391,10 +392,10 @@
         return this;
     }
 
-    private void updateArrowTipInView() {
+    private void updateArrowTipInView(boolean isPointingUp) {
         ViewGroup.LayoutParams arrowLp = mArrowView.getLayoutParams();
         ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
-                arrowLp.width, arrowLp.height, mIsPointingUp));
+                arrowLp.width, arrowLp.height, isPointingUp));
         Paint arrowPaint = arrowDrawable.getPaint();
         @Px int arrowTipRadius = getContext().getResources()
                 .getDimensionPixelSize(R.dimen.arrow_toast_corner_radius);
@@ -403,7 +404,7 @@
         mArrowView.setBackground(arrowDrawable);
         // Add negative margin so that the rounded corners on base of arrow are not visible.
         removeView(mArrowView);
-        if (mIsPointingUp) {
+        if (isPointingUp) {
             addView(mArrowView, 0);
             ((ViewGroup.MarginLayoutParams) arrowLp).setMargins(0, 0, 0, -1 * arrowTipRadius);
         } else {
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 41b98c7..32c70a3 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -18,7 +18,6 @@
 import static android.view.Gravity.LEFT;
 
 import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.Utilities.getBadge;
 import static com.android.launcher3.Utilities.getFullDrawable;
 import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -36,6 +35,7 @@
 import android.os.CancellationSignal;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -288,28 +288,20 @@
         } else {
             int width = (int) pos.width();
             int height = (int) pos.height();
-            Object[] tmpObjArray = new Object[1];
-            boolean[] outIsIconThemed = new boolean[1];
+            Pair<AdaptiveIconDrawable, Drawable> fullIcon = null;
             if (supportsAdaptiveIcons) {
-                boolean shouldThemeIcon = btvIcon instanceof FastBitmapDrawable
-                        && ((FastBitmapDrawable) btvIcon).isThemed();
-                drawable = getFullDrawable(
-                        l, info, width, height, shouldThemeIcon, tmpObjArray, outIsIconThemed);
-                if (drawable instanceof AdaptiveIconDrawable) {
-                    badge = getBadge(l, info, tmpObjArray[0], outIsIconThemed[0]);
-                } else {
-                    // The drawable we get back is not an adaptive icon, so we need to use the
-                    // BubbleTextView icon that is already legacy treated.
-                    drawable = btvIcon;
-                }
+                boolean shouldThemeIcon = (btvIcon instanceof FastBitmapDrawable fbd)
+                        && fbd.isCreatedForTheme();
+                fullIcon = getFullDrawable(l, info, width, height, shouldThemeIcon);
+            } else if (!(originalView instanceof BubbleTextView)) {
+                fullIcon = getFullDrawable(l, info, width, height, true /* shouldThemeIcon */);
+            }
+
+            if (fullIcon != null) {
+                drawable = fullIcon.first;
+                badge = fullIcon.second;
             } else {
-                if (originalView instanceof BubbleTextView) {
-                    // Similar to DragView, we simply use the BubbleTextView icon here.
-                    drawable = btvIcon;
-                } else {
-                    drawable = getFullDrawable(l, info, width, height, true /* shouldThemeIcon */,
-                            tmpObjArray, outIsIconThemed);
-                }
+                drawable = btvIcon;
             }
         }
 
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 76a044d..1aa49c7 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -23,9 +23,11 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Handler;
+import android.os.Parcelable;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Log;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.view.MotionEvent;
@@ -110,7 +112,7 @@
         if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
             setOnLightBackground(true);
         }
-        mColorExtractor = LocalColorExtractor.newInstance(getContext());
+        mColorExtractor = new LocalColorExtractor(); // no-op
     }
 
     @Override
@@ -498,4 +500,13 @@
          */
         void notifyBoundChangeOnPreLayout(View v, int left, int top, int right, int bottom);
     }
+
+    @Override
+    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+        try {
+            super.dispatchRestoreInstanceState(container);
+        } catch (Exception e) {
+            Log.i(TAG, "Exception: " + e);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index 10aef9a..ef51d15 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -67,7 +67,7 @@
      */
     public int maxSpanY;
 
-    private boolean mIsMinSizeFulfilled;
+    protected boolean mIsMinSizeFulfilled;
 
     public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
             AppWidgetProviderInfo info) {
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index 737cdbd..0860e72 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -57,10 +57,15 @@
     /**
      * @see AppWidgetManager#getAppWidgetInfo(int)
      */
-    public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) {
-        if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
-            return CustomWidgetManager.INSTANCE.get(mContext).getWidgetProvider(appWidgetId);
+    public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(
+            int appWidgetId, ComponentName componentName) {
+
+        // For custom widgets.
+        if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID && !CustomWidgetManager
+                .INSTANCE.get(mContext).getWidgetIdForCustomProvider(componentName).equals("")) {
+            return CustomWidgetManager.INSTANCE.get(mContext).getWidgetProvider(componentName);
         }
+
         AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
         return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
     }
diff --git a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
index 8b3bbce..44571a6 100644
--- a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
@@ -33,14 +33,15 @@
 public class CustomAppWidgetProviderInfo extends LauncherAppWidgetProviderInfo
         implements Parcelable {
 
-    public final int providerId;
+    public final String providerId;
 
-    protected CustomAppWidgetProviderInfo(Parcel parcel, boolean readSelf, int providerId) {
+    protected CustomAppWidgetProviderInfo(Parcel parcel, boolean readSelf, String providerId) {
         super(parcel);
         if (readSelf) {
-            this.providerId = parcel.readInt();
+            this.providerId = parcel.readString();
 
-            provider = new ComponentName(parcel.readString(), CLS_CUSTOM_WIDGET_PREFIX + providerId);
+            provider = new ComponentName(parcel.readString(),
+                    CLS_CUSTOM_WIDGET_PREFIX + parcel.readString());
 
             label = parcel.readString();
             initialLayout = parcel.readInt();
@@ -58,7 +59,10 @@
     }
 
     @Override
-    public void initSpans(Context context, InvariantDeviceProfile idp) { }
+    public void initSpans(Context context, InvariantDeviceProfile idp) {
+        mIsMinSizeFulfilled = Math.min(spanX, minSpanX) <= idp.numColumns
+                && Math.min(spanY, minSpanY) <= idp.numRows;
+    }
 
     @Override
     public String getLabel(PackageManager packageManager) {
@@ -73,8 +77,9 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         super.writeToParcel(out, flags);
-        out.writeInt(providerId);
+        out.writeString(providerId);
         out.writeString(provider.getPackageName());
+        out.writeString(provider.getClassName());
 
         out.writeString(label);
         out.writeInt(initialLayout);
@@ -93,7 +98,7 @@
 
         @Override
         public CustomAppWidgetProviderInfo createFromParcel(Parcel parcel) {
-            return new CustomAppWidgetProviderInfo(parcel, true, 0);
+            return new CustomAppWidgetProviderInfo(parcel, true, "");
         }
 
         @Override
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
index 2e2a968..7cf0221 100644
--- a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
@@ -16,7 +16,8 @@
 
 package com.android.launcher3.widget.custom;
 
-import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX;
+import static com.android.launcher3.config.FeatureFlags.SMARTSPACE_AS_A_WIDGET;
+import static com.android.launcher3.model.data.LauncherAppWidgetInfo.CUSTOM_WIDGET_ID;
 
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -24,12 +25,12 @@
 import android.content.Context;
 import android.os.Parcel;
 import android.os.Process;
-import android.util.SparseArray;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.R;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.PackageUserKey;
@@ -39,8 +40,11 @@
 import com.android.systemui.plugins.CustomWidgetPlugin;
 import com.android.systemui.plugins.PluginListener;
 
+import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 
@@ -52,24 +56,37 @@
     public static final MainThreadInitializedObject<CustomWidgetManager> INSTANCE =
             new MainThreadInitializedObject<>(CustomWidgetManager::new);
 
+    private static final String TAG = "CustomWidgetManager";
     private final Context mContext;
-    /**
-     * auto provider Id is an ever-increasing number that serves as the providerId whenever a new
-     * custom widget has been connected.
-     */
-    private int mAutoProviderId = 0;
-    private final SparseArray<CustomWidgetPlugin> mPlugins;
+    private final HashMap<String, CustomWidgetPlugin> mPlugins;
     private final List<CustomAppWidgetProviderInfo> mCustomWidgets;
-    private final SparseArray<ComponentName> mWidgetsIdMap;
+    private final HashMap<ComponentName, String> mWidgetsIdMap;
     private Consumer<PackageUserKey> mWidgetRefreshCallback;
 
     private CustomWidgetManager(Context context) {
         mContext = context;
-        mPlugins = new SparseArray<>();
+        mPlugins = new HashMap<>();
         mCustomWidgets = new ArrayList<>();
-        mWidgetsIdMap = new SparseArray<>();
+        mWidgetsIdMap = new HashMap<>();
         PluginManagerWrapper.INSTANCE.get(context)
                 .addPluginListener(this, CustomWidgetPlugin.class, true);
+
+        if (SMARTSPACE_AS_A_WIDGET.get()) {
+            for (String s: context.getResources()
+                    .getStringArray(R.array.custom_widget_providers)) {
+                try {
+                    Class<?> cls = Class.forName(s);
+                    CustomWidgetPlugin plugin = (CustomWidgetPlugin)
+                            cls.getDeclaredConstructor(Context.class).newInstance(context);
+                    mPlugins.put(plugin.getId(), plugin);
+                    onPluginConnected(mPlugins.get(plugin.getId()), context);
+                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
+                         | ClassCastException | NoSuchMethodException
+                         | InvocationTargetException e) {
+                    Log.e(TAG, "Exception found when trying to add custom widgets: " + e);
+                }
+            }
+        }
     }
 
     @Override
@@ -79,28 +96,41 @@
 
     @Override
     public void onPluginConnected(CustomWidgetPlugin plugin, Context context) {
-        mPlugins.put(mAutoProviderId, plugin);
         List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(context)
                 .getInstalledProvidersForProfile(Process.myUserHandle());
         if (providers.isEmpty()) return;
         Parcel parcel = Parcel.obtain();
         providers.get(0).writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        CustomAppWidgetProviderInfo info = newInfo(mAutoProviderId, plugin, parcel, context);
+        CustomAppWidgetProviderInfo info = newInfo(plugin.getId(), plugin, parcel, context);
         parcel.recycle();
         mCustomWidgets.add(info);
-        mWidgetsIdMap.put(mAutoProviderId, info.provider);
-        mWidgetRefreshCallback.accept(null);
-        mAutoProviderId++;
+        mWidgetsIdMap.put(info.provider, plugin.getId());
     }
 
     @Override
     public void onPluginDisconnected(CustomWidgetPlugin plugin) {
-        int providerId = findProviderId(plugin);
-        if (providerId == -1) return;
-        mPlugins.remove(providerId);
-        mCustomWidgets.remove(getWidgetProvider(providerId));
-        mWidgetsIdMap.remove(providerId);
+        String providerId = plugin.getId();
+        if (mPlugins.containsKey(providerId)) {
+            mPlugins.remove(providerId);
+        }
+
+        ComponentName cn = null;
+        for (Map.Entry entry: mWidgetsIdMap.entrySet()) {
+            if (entry.getValue().equals(providerId)) {
+                cn = (ComponentName) entry.getKey();
+            }
+        }
+
+        if (cn != null) {
+            mWidgetsIdMap.remove(cn);
+            for (int i = 0; i < mCustomWidgets.size(); i++) {
+                if (mCustomWidgets.get(i).getComponent().equals(cn)) {
+                    mCustomWidgets.remove(i);
+                    return;
+                }
+            }
+        }
     }
 
     /**
@@ -131,12 +161,11 @@
     /**
      * Returns the widget id for a specific provider.
      */
-    public int getWidgetIdForCustomProvider(@NonNull ComponentName provider) {
-        int index = mWidgetsIdMap.indexOfValue(provider);
-        if (index >= 0) {
-            return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - mWidgetsIdMap.keyAt(index);
+    public String getWidgetIdForCustomProvider(@NonNull ComponentName provider) {
+        if (mWidgetsIdMap.containsKey(provider)) {
+            return mWidgetsIdMap.get(provider);
         } else {
-            return AppWidgetManager.INVALID_APPWIDGET_ID;
+            return "";
         }
     }
 
@@ -144,38 +173,26 @@
      * Returns the widget provider in respect to given widget id.
      */
     @Nullable
-    public LauncherAppWidgetProviderInfo getWidgetProvider(int widgetId) {
-        ComponentName cn = mWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId);
+    public LauncherAppWidgetProviderInfo getWidgetProvider(ComponentName componentName) {
         for (LauncherAppWidgetProviderInfo info : mCustomWidgets) {
-            if (info.provider.equals(cn)) return info;
+            if (info.provider.equals(componentName)) return info;
         }
         return null;
     }
 
-    private static CustomAppWidgetProviderInfo newInfo(int providerId, CustomWidgetPlugin plugin,
+    private static CustomAppWidgetProviderInfo newInfo(String providerId, CustomWidgetPlugin plugin,
             Parcel parcel, Context context) {
         CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(
                 parcel, false, providerId);
-        info.provider = new ComponentName(
-                context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId);
-
-        info.label = plugin.getLabel();
-        info.resizeMode = plugin.getResizeMode();
-
-        info.spanX = plugin.getSpanX();
-        info.spanY = plugin.getSpanY();
-        info.minSpanX = plugin.getMinSpanX();
-        info.minSpanY = plugin.getMinSpanY();
+        plugin.updateWidgetInfo(info, context);
         return info;
     }
 
-    private int findProviderId(CustomWidgetPlugin plugin) {
-        for (int i = 0; i < mPlugins.size(); i++) {
-            int providerId = mPlugins.keyAt(i);
-            if (mPlugins.get(providerId) == plugin) {
-                return providerId;
-            }
-        }
-        return -1;
+    /**
+     * Returns an id to set as the appWidgetId for a custom widget.
+     */
+    public int allocateCustomAppWidgetId(ComponentName componentName) {
+        return CUSTOM_WIDGET_ID - mCustomWidgets.indexOf(getWidgetProvider(componentName));
     }
+
 }
diff --git a/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java b/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java
index 56ebcc5..af4f22c 100644
--- a/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java
+++ b/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java
@@ -17,6 +17,8 @@
 package com.android.systemui.plugins;
 
 import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
@@ -30,42 +32,20 @@
     int VERSION = 1;
 
     /**
-     * The label to display to the user in the AppWidget picker.
-     */
-    String getLabel();
-
-    /**
-     * The default width of the widget when added to a host, in dp. The widget will get
-     * at least this width, and will often be given more, depending on the host.
-     */
-    int getSpanX();
-
-    /**
-     * The default height of the widget when added to a host, in dp. The widget will get
-     * at least this height, and will often be given more, depending on the host.
-     */
-    int getSpanY();
-
-    /**
-     * Minimum width (in dp) which the widget can be resized to. This field has no effect if it
-     * is greater than minWidth or if horizontal resizing isn't enabled.
-     */
-    int getMinSpanX();
-
-    /**
-     * Minimum height (in dp) which the widget can be resized to. This field has no effect if it
-     * is greater than minHeight or if vertical resizing isn't enabled.
-     */
-    int getMinSpanY();
-
-    /**
-     * The rules by which a widget can be resized.
-     */
-    int getResizeMode();
-
-    /**
      * Notify the plugin that container of the widget has been rendered, where the custom widget
      * can be attached to.
      */
     void onViewCreated(AppWidgetHostView parent);
+
+    /**
+     * Get the UUID for the custom widget.
+     */
+    String getId();
+
+    /**
+     * Used to modify a widgets' info.
+     */
+    default void updateWidgetInfo(AppWidgetProviderInfo info, Context context) {
+
+    }
 }
diff --git a/tests/Android.bp b/tests/Android.bp
index d40efce..1471c08 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -42,6 +42,9 @@
 filegroup {
     name: "launcher-oop-tests-src",
     srcs: [
+      "src/com/android/launcher3/allapps/OopTaplOpenCloseAllApps.java",
+      "src/com/android/launcher3/dragging/TaplDragTest.java",
+      "src/com/android/launcher3/dragging/TaplUninstallRemove.java",
       "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
       "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
       "src/com/android/launcher3/ui/TaplTestsLauncher3.java",
@@ -54,6 +57,7 @@
       "src/com/android/launcher3/util/rule/SamplerRule.java",
       "src/com/android/launcher3/util/rule/ScreenRecordRule.java",
       "src/com/android/launcher3/util/rule/ShellCommandRule.java",
+      "src/com/android/launcher3/util/rule/TestIsolationRule.java",
       "src/com/android/launcher3/util/rule/TestStabilityRule.java",
       "src/com/android/launcher3/util/rule/TISBindRule.java",
       "src/com/android/launcher3/util/viewcapture_analysis/*.java",
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt
new file mode 100644
index 0000000..b8f4c0b
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:false
+	isPhone:true
+	transposeLayoutWithOrientation:true
+	isGestureMode:true
+	isLandscape:false
+	isMultiWindowMode:false
+	isTwoPanels:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 1080.0px (411.42856dp)
+	heightPx: 2400.0px (914.2857dp)
+	availableWidthPx: 1080.0px (411.42856dp)
+	availableHeightPx: 2219.0px (845.3333dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 118.0px (44.95238dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 63.0px (24.0dp)
+	aspectRatio:2.2222223
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 5
+	inv.numColumns: 5
+	inv.numSearchContainerColumns: 5
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 159.0px (60.57143dp)
+	cellHeightPx: 229.0px (87.2381dp)
+	getCellSize().x: 207.0px (78.85714dp)
+	getCellSize().y: 383.0px (145.90475dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 21.0px (8.0dp)
+	cellLayoutPaddingPx.top: 28.0px (10.666667dp)
+	cellLayoutPaddingPx.right: 21.0px (8.0dp)
+	cellLayoutPaddingPx.bottom: 28.0px (10.666667dp)
+	iconSizePx: 147.0px (56.0dp)
+	iconTextSizePx: 38.0px (14.476191dp)
+	iconDrawablePaddingPx: 12.0px (4.571429dp)
+	inv.numFolderRows: 4
+	inv.numFolderColumns: 4
+	folderCellWidthPx: 195.0px (74.28571dp)
+	folderCellHeightPx: 230.0px (87.61905dp)
+	folderChildIconSizePx: 147.0px (56.0dp)
+	folderChildTextSizePx: 38.0px (14.476191dp)
+	folderChildDrawablePaddingPx: 4.0px (1.5238096dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 146.0px (55.61905dp)
+	bottomSheetOpenDuration: 267
+	bottomSheetCloseDuration: 267
+	bottomSheetWorkspaceScale: 1.0
+	bottomSheetDepth: 0.0
+	allAppsShiftRange: 788.0px (300.1905dp)
+	allAppsTopPadding: 0.0px (0.0dp)
+	allAppsOpenDuration: 600
+	allAppsCloseDuration: 300
+	allAppsIconSizePx: 147.0px (56.0dp)
+	allAppsIconTextSizePx: 38.0px (14.476191dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 315.0px (120.0dp)
+	allAppsCellWidthPx: 189.0px (72.0dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 5
+	allAppsLeftRightPadding: 0.0px (0.0dp)
+	allAppsLeftRightMargin: 0.0px (0.0dp)
+	hotseatBarSizePx: 273.0px (104.0dp)
+	inv.hotseatColumnSpan: 5
+	hotseatCellHeightPx: 166.0px (63.238094dp)
+	hotseatBarBottomSpacePx: 126.0px (48.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 107.0px (40.761906dp)
+	getHotseatLayoutPadding(context).left: 21.0px (8.0dp)
+	getHotseatLayoutPadding(context).right: 21.0px (8.0dp)
+	numShownHotseatIcons: 5
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:false
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 0.0px (0.0dp)
+	workspacePadding.top: 0.0px (0.0dp)
+	workspacePadding.right: 0.0px (0.0dp)
+	workspacePadding.bottom: 245.0px (93.333336dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 773.0px (294.4762dp)
+	unscaled extraSpace: 773.0px (294.4762dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 63.0px (24.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 84.0px (32.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1927.0px (734.0952dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.7781155px (0.29642496dp)
+	getCellLayoutHeight(): 1974.0px (752.0dp)
+	getCellLayoutWidth(): 1080.0px (411.42856dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt
new file mode 100644
index 0000000..a512277
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:false
+	isPhone:true
+	transposeLayoutWithOrientation:true
+	isGestureMode:false
+	isLandscape:false
+	isMultiWindowMode:false
+	isTwoPanels:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 1080.0px (411.42856dp)
+	heightPx: 2400.0px (914.2857dp)
+	availableWidthPx: 1080.0px (411.42856dp)
+	availableHeightPx: 2156.0px (821.3333dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 118.0px (44.95238dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 126.0px (48.0dp)
+	aspectRatio:2.2222223
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 5
+	inv.numColumns: 5
+	inv.numSearchContainerColumns: 5
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 159.0px (60.57143dp)
+	cellHeightPx: 229.0px (87.2381dp)
+	getCellSize().x: 207.0px (78.85714dp)
+	getCellSize().y: 379.0px (144.38095dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 21.0px (8.0dp)
+	cellLayoutPaddingPx.top: 28.0px (10.666667dp)
+	cellLayoutPaddingPx.right: 21.0px (8.0dp)
+	cellLayoutPaddingPx.bottom: 28.0px (10.666667dp)
+	iconSizePx: 147.0px (56.0dp)
+	iconTextSizePx: 38.0px (14.476191dp)
+	iconDrawablePaddingPx: 12.0px (4.571429dp)
+	inv.numFolderRows: 4
+	inv.numFolderColumns: 4
+	folderCellWidthPx: 195.0px (74.28571dp)
+	folderCellHeightPx: 230.0px (87.61905dp)
+	folderChildIconSizePx: 147.0px (56.0dp)
+	folderChildTextSizePx: 38.0px (14.476191dp)
+	folderChildDrawablePaddingPx: 4.0px (1.5238096dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 146.0px (55.61905dp)
+	bottomSheetOpenDuration: 267
+	bottomSheetCloseDuration: 267
+	bottomSheetWorkspaceScale: 1.0
+	bottomSheetDepth: 0.0
+	allAppsShiftRange: 788.0px (300.1905dp)
+	allAppsTopPadding: 0.0px (0.0dp)
+	allAppsOpenDuration: 600
+	allAppsCloseDuration: 300
+	allAppsIconSizePx: 147.0px (56.0dp)
+	allAppsIconTextSizePx: 38.0px (14.476191dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 315.0px (120.0dp)
+	allAppsCellWidthPx: 189.0px (72.0dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 5
+	allAppsLeftRightPadding: 0.0px (0.0dp)
+	allAppsLeftRightMargin: 0.0px (0.0dp)
+	hotseatBarSizePx: 294.0px (112.0dp)
+	inv.hotseatColumnSpan: 5
+	hotseatCellHeightPx: 166.0px (63.238094dp)
+	hotseatBarBottomSpacePx: 147.0px (56.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 128.0px (48.761906dp)
+	getHotseatLayoutPadding(context).left: 21.0px (8.0dp)
+	getHotseatLayoutPadding(context).right: 21.0px (8.0dp)
+	numShownHotseatIcons: 5
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:false
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 0.0px (0.0dp)
+	workspacePadding.top: 0.0px (0.0dp)
+	workspacePadding.right: 0.0px (0.0dp)
+	workspacePadding.bottom: 203.0px (77.333336dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 752.0px (286.4762dp)
+	unscaled extraSpace: 752.0px (286.4762dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 126.0px (48.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 84.0px (32.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1906.0px (726.0952dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.77572966px (0.29551607dp)
+	getCellLayoutHeight(): 1953.0px (744.0dp)
+	getCellLayoutWidth(): 1080.0px (411.42856dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
new file mode 100644
index 0000000..a3a8dc5
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:false
+	isPhone:true
+	transposeLayoutWithOrientation:true
+	isGestureMode:true
+	isLandscape:true
+	isMultiWindowMode:false
+	isTwoPanels:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 2400.0px (914.2857dp)
+	heightPx: 1080.0px (411.42856dp)
+	availableWidthPx: 2282.0px (869.3333dp)
+	availableHeightPx: 943.0px (359.2381dp)
+	mInsets.left: 118.0px (44.95238dp)
+	mInsets.top: 74.0px (28.190475dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 63.0px (24.0dp)
+	aspectRatio:2.2222223
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 5
+	inv.numColumns: 5
+	inv.numSearchContainerColumns: 5
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 152.0px (57.904762dp)
+	cellHeightPx: 166.0px (63.238094dp)
+	getCellSize().x: 393.0px (149.71428dp)
+	getCellSize().y: 180.0px (68.57143dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 53.0px (20.190475dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 53.0px (20.190475dp)
+	cellLayoutPaddingPx.bottom: 40.0px (15.238095dp)
+	iconSizePx: 147.0px (56.0dp)
+	iconTextSizePx: 0.0px (0.0dp)
+	iconDrawablePaddingPx: 0.0px (0.0dp)
+	inv.numFolderRows: 4
+	inv.numFolderColumns: 4
+	folderCellWidthPx: 163.0px (62.095238dp)
+	folderCellHeightPx: 192.0px (73.14286dp)
+	folderChildIconSizePx: 123.0px (46.857143dp)
+	folderChildTextSizePx: 32.0px (12.190476dp)
+	folderChildDrawablePaddingPx: 3.0px (1.1428572dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 53.0px (20.190475dp)
+	folderFooterHeight: 123.0px (46.857143dp)
+	bottomSheetTopPadding: 114.0px (43.42857dp)
+	bottomSheetOpenDuration: 267
+	bottomSheetCloseDuration: 267
+	bottomSheetWorkspaceScale: 1.0
+	bottomSheetDepth: 0.0
+	allAppsShiftRange: 788.0px (300.1905dp)
+	allAppsTopPadding: 0.0px (0.0dp)
+	allAppsOpenDuration: 600
+	allAppsCloseDuration: 300
+	allAppsIconSizePx: 147.0px (56.0dp)
+	allAppsIconTextSizePx: 38.0px (14.476191dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 321.0px (122.28571dp)
+	allAppsCellWidthPx: 189.0px (72.0dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 5
+	allAppsLeftRightPadding: 0.0px (0.0dp)
+	allAppsLeftRightMargin: 0.0px (0.0dp)
+	hotseatBarSizePx: 252.0px (96.0dp)
+	inv.hotseatColumnSpan: 5
+	hotseatCellHeightPx: 166.0px (63.238094dp)
+	hotseatBarBottomSpacePx: 0.0px (0.0dp)
+	mHotseatBarEdgePaddingPx: 63.0px (24.0dp)
+	mHotseatBarWorkspaceSpacePx: 42.0px (16.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)
+	getHotseatLayoutPadding(context).top: 64.0px (24.380953dp)
+	getHotseatLayoutPadding(context).bottom: 112.0px (42.666668dp)
+	getHotseatLayoutPadding(context).left: 42.0px (16.0dp)
+	getHotseatLayoutPadding(context).right: 63.0px (24.0dp)
+	numShownHotseatIcons: 5
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:false
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 0.0px (0.0dp)
+	workspacePadding.left: 10.0px (3.8095238dp)
+	workspacePadding.top: 0.0px (0.0dp)
+	workspacePadding.right: 199.0px (75.809525dp)
+	workspacePadding.bottom: 0.0px (0.0dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 73.0px (27.809525dp)
+	unscaled extraSpace: 73.0px (27.809525dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 63.0px (24.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 16.0px (6.095238dp)
+	dropTargetBarSizePx: 95.0px (36.190475dp)
+	dropTargetBarBottomMarginPx: 16.0px (6.095238dp)
+	getCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)
+	getCellLayoutSpringLoadShrunkBottom(): 952.0px (362.66666dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.79639447px (0.30338836dp)
+	getCellLayoutHeight(): 943.0px (359.2381dp)
+	getCellLayoutWidth(): 2073.0px (789.7143dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
new file mode 100644
index 0000000..55066cb
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:false
+	isPhone:true
+	transposeLayoutWithOrientation:true
+	isGestureMode:false
+	isLandscape:true
+	isMultiWindowMode:false
+	isTwoPanels:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 2400.0px (914.2857dp)
+	heightPx: 1080.0px (411.42856dp)
+	availableWidthPx: 2156.0px (821.3333dp)
+	availableHeightPx: 1006.0px (383.2381dp)
+	mInsets.left: 118.0px (44.95238dp)
+	mInsets.top: 74.0px (28.190475dp)
+	mInsets.right: 126.0px (48.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:2.2222223
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 5
+	inv.numColumns: 5
+	inv.numSearchContainerColumns: 5
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 152.0px (57.904762dp)
+	cellHeightPx: 166.0px (63.238094dp)
+	getCellSize().x: 368.0px (140.19048dp)
+	getCellSize().y: 193.0px (73.52381dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 53.0px (20.190475dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 53.0px (20.190475dp)
+	cellLayoutPaddingPx.bottom: 40.0px (15.238095dp)
+	iconSizePx: 147.0px (56.0dp)
+	iconTextSizePx: 0.0px (0.0dp)
+	iconDrawablePaddingPx: 0.0px (0.0dp)
+	inv.numFolderRows: 4
+	inv.numFolderColumns: 4
+	folderCellWidthPx: 173.0px (65.90476dp)
+	folderCellHeightPx: 205.0px (78.09524dp)
+	folderChildIconSizePx: 131.0px (49.904762dp)
+	folderChildTextSizePx: 34.0px (12.952381dp)
+	folderChildDrawablePaddingPx: 4.0px (1.5238096dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 56.0px (21.333334dp)
+	folderFooterHeight: 131.0px (49.904762dp)
+	bottomSheetTopPadding: 114.0px (43.42857dp)
+	bottomSheetOpenDuration: 267
+	bottomSheetCloseDuration: 267
+	bottomSheetWorkspaceScale: 1.0
+	bottomSheetDepth: 0.0
+	allAppsShiftRange: 788.0px (300.1905dp)
+	allAppsTopPadding: 0.0px (0.0dp)
+	allAppsOpenDuration: 600
+	allAppsCloseDuration: 300
+	allAppsIconSizePx: 147.0px (56.0dp)
+	allAppsIconTextSizePx: 38.0px (14.476191dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 321.0px (122.28571dp)
+	allAppsCellWidthPx: 189.0px (72.0dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 5
+	allAppsLeftRightPadding: 0.0px (0.0dp)
+	allAppsLeftRightMargin: 0.0px (0.0dp)
+	hotseatBarSizePx: 252.0px (96.0dp)
+	inv.hotseatColumnSpan: 5
+	hotseatCellHeightPx: 166.0px (63.238094dp)
+	hotseatBarBottomSpacePx: 0.0px (0.0dp)
+	mHotseatBarEdgePaddingPx: 63.0px (24.0dp)
+	mHotseatBarWorkspaceSpacePx: 42.0px (16.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)
+	getHotseatLayoutPadding(context).top: 64.0px (24.380953dp)
+	getHotseatLayoutPadding(context).bottom: 49.0px (18.666666dp)
+	getHotseatLayoutPadding(context).left: 42.0px (16.0dp)
+	getHotseatLayoutPadding(context).right: 189.0px (72.0dp)
+	numShownHotseatIcons: 5
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:false
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 0.0px (0.0dp)
+	workspacePadding.left: 10.0px (3.8095238dp)
+	workspacePadding.top: 0.0px (0.0dp)
+	workspacePadding.right: 199.0px (75.809525dp)
+	workspacePadding.bottom: 0.0px (0.0dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 136.0px (51.809525dp)
+	unscaled extraSpace: 136.0px (51.809525dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 16.0px (6.095238dp)
+	dropTargetBarSizePx: 95.0px (36.190475dp)
+	dropTargetBarBottomMarginPx: 16.0px (6.095238dp)
+	getCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1008.0px (384.0dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.8021869px (0.305595dp)
+	getCellLayoutHeight(): 1006.0px (383.2381dp)
+	getCellLayoutWidth(): 1947.0px (741.7143dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt
new file mode 100644
index 0000000..6e764c2
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.0 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:true
+	isLandscape:true
+	isMultiWindowMode:false
+	isTwoPanels:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 2560.0px (1280.0dp)
+	heightPx: 1600.0px (800.0dp)
+	availableWidthPx: 2560.0px (1280.0dp)
+	availableHeightPx: 1496.0px (748.0dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 104.0px (52.0dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.6
+	isResponsiveGrid:false
+	isScalableGrid:true
+	inv.numRows: 5
+	inv.numColumns: 6
+	inv.numSearchContainerColumns: 3
+	minCellSize: PointF(120.0, 104.0)dp
+	cellWidthPx: 240.0px (120.0dp)
+	cellHeightPx: 208.0px (104.0dp)
+	getCellSize().x: 240.0px (120.0dp)
+	getCellSize().y: 208.0px (104.0dp)
+	cellLayoutBorderSpacePx Horizontal: 128.0px (64.0dp)
+	cellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)
+	cellLayoutPaddingPx.left: 59.0px (29.5dp)
+	cellLayoutPaddingPx.top: 25.0px (12.5dp)
+	cellLayoutPaddingPx.right: 59.0px (29.5dp)
+	cellLayoutPaddingPx.bottom: 59.0px (29.5dp)
+	iconSizePx: 120.0px (60.0dp)
+	iconTextSizePx: 28.0px (14.0dp)
+	iconDrawablePaddingPx: 9.0px (4.5dp)
+	inv.numFolderRows: 3
+	inv.numFolderColumns: 3
+	folderCellWidthPx: 240.0px (120.0dp)
+	folderCellHeightPx: 208.0px (104.0dp)
+	folderChildIconSizePx: 120.0px (60.0dp)
+	folderChildTextSizePx: 28.0px (14.0dp)
+	folderChildDrawablePaddingPx: 11.0px (5.5dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 0.0px (0.0dp)
+	folderTopPadding: 48.0px (24.0dp)
+	folderFooterHeight: 112.0px (56.0dp)
+	bottomSheetTopPadding: 104.0px (52.0dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 0.0
+	allAppsShiftRange: 1496.0px (748.0dp)
+	allAppsTopPadding: 104.0px (52.0dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 120.0px (60.0dp)
+	allAppsIconTextSizePx: 28.0px (14.0dp)
+	allAppsIconDrawablePaddingPx: 9.0px (4.5dp)
+	allAppsCellHeightPx: 284.0px (142.0dp)
+	allAppsCellWidthPx: 252.0px (126.0dp)
+	allAppsBorderSpacePxX: 32.0px (16.0dp)
+	allAppsBorderSpacePxY: 32.0px (16.0dp)
+	numShownAllAppsColumns: 6
+	allAppsLeftRightPadding: 32.0px (16.0dp)
+	allAppsLeftRightMargin: 412.0px (206.0dp)
+	hotseatBarSizePx: 200.0px (100.0dp)
+	inv.hotseatColumnSpan: 4
+	hotseatCellHeightPx: 135.0px (67.5dp)
+	hotseatBarBottomSpacePx: 80.0px (40.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)
+	getHotseatLayoutPadding(context).left: 668.0px (334.0dp)
+	getHotseatLayoutPadding(context).right: 668.0px (334.0dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 100.0px (50.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 1224.0px (612.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 240.0px (120.0dp)
+	workspacePadding.left: 181.0px (90.5dp)
+	workspacePadding.top: 0.0px (0.0dp)
+	workspacePadding.right: 181.0px (90.5dp)
+	workspacePadding.bottom: 244.0px (122.0dp)
+	iconScale: 1.0px (0.5dp)
+	cellScaleToFit : 1.0px (0.5dp)
+	extraSpace: 80.0px (40.0dp)
+	unscaled extraSpace: 80.0px (40.0dp)
+	maxEmptySpace: 200.0px (100.0dp)
+	workspaceTopPadding: 25.0px (12.5dp)
+	workspaceBottomPadding: 55.0px (27.5dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
+	dropTargetBarSizePx: 144.0px (72.0dp)
+	dropTargetBarBottomMarginPx: 64.0px (32.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.76677316px (0.38338658dp)
+	getCellLayoutHeight(): 1252.0px (626.0dp)
+	getCellLayoutWidth(): 2198.0px (1099.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt
new file mode 100644
index 0000000..7650082
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.0 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:false
+	isLandscape:true
+	isMultiWindowMode:false
+	isTwoPanels:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 2560.0px (1280.0dp)
+	heightPx: 1600.0px (800.0dp)
+	availableWidthPx: 2560.0px (1280.0dp)
+	availableHeightPx: 1496.0px (748.0dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 104.0px (52.0dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.6
+	isResponsiveGrid:false
+	isScalableGrid:true
+	inv.numRows: 5
+	inv.numColumns: 6
+	inv.numSearchContainerColumns: 3
+	minCellSize: PointF(120.0, 104.0)dp
+	cellWidthPx: 240.0px (120.0dp)
+	cellHeightPx: 208.0px (104.0dp)
+	getCellSize().x: 240.0px (120.0dp)
+	getCellSize().y: 208.0px (104.0dp)
+	cellLayoutBorderSpacePx Horizontal: 128.0px (64.0dp)
+	cellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)
+	cellLayoutPaddingPx.left: 59.0px (29.5dp)
+	cellLayoutPaddingPx.top: 25.0px (12.5dp)
+	cellLayoutPaddingPx.right: 59.0px (29.5dp)
+	cellLayoutPaddingPx.bottom: 59.0px (29.5dp)
+	iconSizePx: 120.0px (60.0dp)
+	iconTextSizePx: 28.0px (14.0dp)
+	iconDrawablePaddingPx: 9.0px (4.5dp)
+	inv.numFolderRows: 3
+	inv.numFolderColumns: 3
+	folderCellWidthPx: 240.0px (120.0dp)
+	folderCellHeightPx: 208.0px (104.0dp)
+	folderChildIconSizePx: 120.0px (60.0dp)
+	folderChildTextSizePx: 28.0px (14.0dp)
+	folderChildDrawablePaddingPx: 11.0px (5.5dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 0.0px (0.0dp)
+	folderTopPadding: 48.0px (24.0dp)
+	folderFooterHeight: 112.0px (56.0dp)
+	bottomSheetTopPadding: 104.0px (52.0dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 0.0
+	allAppsShiftRange: 1496.0px (748.0dp)
+	allAppsTopPadding: 104.0px (52.0dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 120.0px (60.0dp)
+	allAppsIconTextSizePx: 28.0px (14.0dp)
+	allAppsIconDrawablePaddingPx: 9.0px (4.5dp)
+	allAppsCellHeightPx: 284.0px (142.0dp)
+	allAppsCellWidthPx: 252.0px (126.0dp)
+	allAppsBorderSpacePxX: 32.0px (16.0dp)
+	allAppsBorderSpacePxY: 32.0px (16.0dp)
+	numShownAllAppsColumns: 6
+	allAppsLeftRightPadding: 32.0px (16.0dp)
+	allAppsLeftRightMargin: 412.0px (206.0dp)
+	hotseatBarSizePx: 200.0px (100.0dp)
+	inv.hotseatColumnSpan: 4
+	hotseatCellHeightPx: 135.0px (67.5dp)
+	hotseatBarBottomSpacePx: 80.0px (40.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)
+	getHotseatLayoutPadding(context).left: 668.0px (334.0dp)
+	getHotseatLayoutPadding(context).right: 668.0px (334.0dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 100.0px (50.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 1224.0px (612.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 240.0px (120.0dp)
+	workspacePadding.left: 181.0px (90.5dp)
+	workspacePadding.top: 0.0px (0.0dp)
+	workspacePadding.right: 181.0px (90.5dp)
+	workspacePadding.bottom: 244.0px (122.0dp)
+	iconScale: 1.0px (0.5dp)
+	cellScaleToFit : 1.0px (0.5dp)
+	extraSpace: 80.0px (40.0dp)
+	unscaled extraSpace: 80.0px (40.0dp)
+	maxEmptySpace: 200.0px (100.0dp)
+	workspaceTopPadding: 25.0px (12.5dp)
+	workspaceBottomPadding: 55.0px (27.5dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
+	dropTargetBarSizePx: 144.0px (72.0dp)
+	dropTargetBarBottomMarginPx: 64.0px (32.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.76677316px (0.38338658dp)
+	getCellLayoutHeight(): 1252.0px (626.0dp)
+	getCellLayoutWidth(): 2198.0px (1099.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt
new file mode 100644
index 0000000..2b241a1
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.0 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:true
+	isLandscape:false
+	isMultiWindowMode:false
+	isTwoPanels:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 1600.0px (800.0dp)
+	heightPx: 2560.0px (1280.0dp)
+	availableWidthPx: 1600.0px (800.0dp)
+	availableHeightPx: 2456.0px (1228.0dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 104.0px (52.0dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.6
+	isResponsiveGrid:false
+	isScalableGrid:true
+	inv.numRows: 5
+	inv.numColumns: 6
+	inv.numSearchContainerColumns: 3
+	minCellSize: PointF(102.0, 120.0)dp
+	cellWidthPx: 204.0px (102.0dp)
+	cellHeightPx: 240.0px (120.0dp)
+	getCellSize().x: 204.0px (102.0dp)
+	getCellSize().y: 240.0px (120.0dp)
+	cellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)
+	cellLayoutBorderSpacePx Vertical: 128.0px (64.0dp)
+	cellLayoutPaddingPx.left: 72.0px (36.0dp)
+	cellLayoutPaddingPx.top: 72.0px (36.0dp)
+	cellLayoutPaddingPx.right: 72.0px (36.0dp)
+	cellLayoutPaddingPx.bottom: 72.0px (36.0dp)
+	iconSizePx: 120.0px (60.0dp)
+	iconTextSizePx: 28.0px (14.0dp)
+	iconDrawablePaddingPx: 9.0px (4.5dp)
+	inv.numFolderRows: 3
+	inv.numFolderColumns: 3
+	folderCellWidthPx: 204.0px (102.0dp)
+	folderCellHeightPx: 240.0px (120.0dp)
+	folderChildIconSizePx: 120.0px (60.0dp)
+	folderChildTextSizePx: 28.0px (14.0dp)
+	folderChildDrawablePaddingPx: 22.0px (11.0dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 0.0px (0.0dp)
+	folderTopPadding: 48.0px (24.0dp)
+	folderFooterHeight: 112.0px (56.0dp)
+	bottomSheetTopPadding: 704.0px (352.0dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 0.0
+	allAppsShiftRange: 1810.0px (905.0dp)
+	allAppsTopPadding: 750.0px (375.0dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 120.0px (60.0dp)
+	allAppsIconTextSizePx: 28.0px (14.0dp)
+	allAppsIconDrawablePaddingPx: 9.0px (4.5dp)
+	allAppsCellHeightPx: 316.0px (158.0dp)
+	allAppsCellWidthPx: 192.0px (96.0dp)
+	allAppsBorderSpacePxX: 16.0px (8.0dp)
+	allAppsBorderSpacePxY: 32.0px (16.0dp)
+	numShownAllAppsColumns: 6
+	allAppsLeftRightPadding: 32.0px (16.0dp)
+	allAppsLeftRightMargin: 152.0px (76.0dp)
+	hotseatBarSizePx: 272.0px (136.0dp)
+	inv.hotseatColumnSpan: 6
+	hotseatCellHeightPx: 135.0px (67.5dp)
+	hotseatBarBottomSpacePx: 152.0px (76.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 137.0px (68.5dp)
+	getHotseatLayoutPadding(context).left: 150.0px (75.0dp)
+	getHotseatLayoutPadding(context).right: 150.0px (75.0dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 116.0px (58.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 1300.0px (650.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)
+	workspacePadding.left: 36.0px (18.0dp)
+	workspacePadding.top: 132.0px (66.0dp)
+	workspacePadding.right: 36.0px (18.0dp)
+	workspacePadding.bottom: 468.0px (234.0dp)
+	iconScale: 1.0px (0.5dp)
+	cellScaleToFit : 1.0px (0.5dp)
+	extraSpace: 424.0px (212.0dp)
+	unscaled extraSpace: 424.0px (212.0dp)
+	maxEmptySpace: 19998.0px (9999.0dp)
+	workspaceTopPadding: 204.0px (102.0dp)
+	workspaceBottomPadding: 220.0px (110.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 220.0px (110.0dp)
+	dropTargetBarSizePx: 144.0px (72.0dp)
+	dropTargetBarBottomMarginPx: 96.0px (48.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)
+	getCellLayoutSpringLoadShrunkBottom(): 2072.0px (1036.0dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.8125px (0.40625dp)
+	getCellLayoutHeight(): 1856.0px (928.0dp)
+	getCellLayoutWidth(): 1528.0px (764.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt
new file mode 100644
index 0000000..6d38d27
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.0 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:false
+	isLandscape:false
+	isMultiWindowMode:false
+	isTwoPanels:false
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 1600.0px (800.0dp)
+	heightPx: 2560.0px (1280.0dp)
+	availableWidthPx: 1600.0px (800.0dp)
+	availableHeightPx: 2456.0px (1228.0dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 104.0px (52.0dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.6
+	isResponsiveGrid:false
+	isScalableGrid:true
+	inv.numRows: 5
+	inv.numColumns: 6
+	inv.numSearchContainerColumns: 3
+	minCellSize: PointF(102.0, 120.0)dp
+	cellWidthPx: 204.0px (102.0dp)
+	cellHeightPx: 240.0px (120.0dp)
+	getCellSize().x: 204.0px (102.0dp)
+	getCellSize().y: 240.0px (120.0dp)
+	cellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)
+	cellLayoutBorderSpacePx Vertical: 128.0px (64.0dp)
+	cellLayoutPaddingPx.left: 72.0px (36.0dp)
+	cellLayoutPaddingPx.top: 72.0px (36.0dp)
+	cellLayoutPaddingPx.right: 72.0px (36.0dp)
+	cellLayoutPaddingPx.bottom: 72.0px (36.0dp)
+	iconSizePx: 120.0px (60.0dp)
+	iconTextSizePx: 28.0px (14.0dp)
+	iconDrawablePaddingPx: 9.0px (4.5dp)
+	inv.numFolderRows: 3
+	inv.numFolderColumns: 3
+	folderCellWidthPx: 204.0px (102.0dp)
+	folderCellHeightPx: 240.0px (120.0dp)
+	folderChildIconSizePx: 120.0px (60.0dp)
+	folderChildTextSizePx: 28.0px (14.0dp)
+	folderChildDrawablePaddingPx: 22.0px (11.0dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 0.0px (0.0dp)
+	folderTopPadding: 48.0px (24.0dp)
+	folderFooterHeight: 112.0px (56.0dp)
+	bottomSheetTopPadding: 704.0px (352.0dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 0.0
+	allAppsShiftRange: 1810.0px (905.0dp)
+	allAppsTopPadding: 750.0px (375.0dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 120.0px (60.0dp)
+	allAppsIconTextSizePx: 28.0px (14.0dp)
+	allAppsIconDrawablePaddingPx: 9.0px (4.5dp)
+	allAppsCellHeightPx: 316.0px (158.0dp)
+	allAppsCellWidthPx: 192.0px (96.0dp)
+	allAppsBorderSpacePxX: 16.0px (8.0dp)
+	allAppsBorderSpacePxY: 32.0px (16.0dp)
+	numShownAllAppsColumns: 6
+	allAppsLeftRightPadding: 32.0px (16.0dp)
+	allAppsLeftRightMargin: 152.0px (76.0dp)
+	hotseatBarSizePx: 272.0px (136.0dp)
+	inv.hotseatColumnSpan: 6
+	hotseatCellHeightPx: 135.0px (67.5dp)
+	hotseatBarBottomSpacePx: 152.0px (76.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 137.0px (68.5dp)
+	getHotseatLayoutPadding(context).left: 150.0px (75.0dp)
+	getHotseatLayoutPadding(context).right: 150.0px (75.0dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 116.0px (58.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 1300.0px (650.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)
+	workspacePadding.left: 36.0px (18.0dp)
+	workspacePadding.top: 132.0px (66.0dp)
+	workspacePadding.right: 36.0px (18.0dp)
+	workspacePadding.bottom: 468.0px (234.0dp)
+	iconScale: 1.0px (0.5dp)
+	cellScaleToFit : 1.0px (0.5dp)
+	extraSpace: 424.0px (212.0dp)
+	unscaled extraSpace: 424.0px (212.0dp)
+	maxEmptySpace: 19998.0px (9999.0dp)
+	workspaceTopPadding: 204.0px (102.0dp)
+	workspaceBottomPadding: 220.0px (110.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 220.0px (110.0dp)
+	dropTargetBarSizePx: 144.0px (72.0dp)
+	dropTargetBarBottomMarginPx: 96.0px (48.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)
+	getCellLayoutSpringLoadShrunkBottom(): 2072.0px (1036.0dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.8125px (0.40625dp)
+	getCellLayoutHeight(): 1856.0px (928.0dp)
+	getCellLayoutWidth(): 1528.0px (764.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt
new file mode 100644
index 0000000..5799de7
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:true
+	isLandscape:true
+	isMultiWindowMode:false
+	isTwoPanels:true
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 2208.0px (841.1429dp)
+	heightPx: 1840.0px (700.9524dp)
+	availableWidthPx: 2208.0px (841.1429dp)
+	availableHeightPx: 1730.0px (659.0476dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 110.0px (41.904762dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.2
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 4
+	inv.numColumns: 4
+	inv.numSearchContainerColumns: 4
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 154.0px (58.666668dp)
+	cellHeightPx: 218.0px (83.04762dp)
+	getCellSize().x: 270.0px (102.85714dp)
+	getCellSize().y: 342.0px (130.28572dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 0.0px (0.0dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 0.0px (0.0dp)
+	cellLayoutPaddingPx.bottom: 0.0px (0.0dp)
+	iconSizePx: 141.0px (53.714287dp)
+	iconTextSizePx: 34.0px (12.952381dp)
+	iconDrawablePaddingPx: 13.0px (4.952381dp)
+	inv.numFolderRows: 3
+	inv.numFolderColumns: 4
+	folderCellWidthPx: 189.0px (72.0dp)
+	folderCellHeightPx: 219.0px (83.42857dp)
+	folderChildIconSizePx: 141.0px (53.714287dp)
+	folderChildTextSizePx: 34.0px (12.952381dp)
+	folderChildDrawablePaddingPx: 5.0px (1.9047619dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 110.0px (41.904762dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 1.0
+	allAppsShiftRange: 1730.0px (659.0476dp)
+	allAppsTopPadding: 110.0px (41.904762dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 141.0px (53.714287dp)
+	allAppsIconTextSizePx: 34.0px (12.952381dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 315.0px (120.0dp)
+	allAppsCellWidthPx: 183.0px (69.71429dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 8
+	allAppsLeftRightPadding: 42.0px (16.0dp)
+	allAppsLeftRightMargin: 183.0px (69.71429dp)
+	hotseatBarSizePx: 267.0px (101.71429dp)
+	inv.hotseatColumnSpan: 4
+	hotseatCellHeightPx: 159.0px (60.57143dp)
+	hotseatBarBottomSpacePx: 126.0px (48.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
+	getHotseatLayoutPadding(context).left: 113.0px (43.04762dp)
+	getHotseatLayoutPadding(context).right: 113.0px (43.04762dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 21.0px (8.0dp)
+	workspacePadding.top: 30.0px (11.428572dp)
+	workspacePadding.right: 21.0px (8.0dp)
+	workspacePadding.bottom: 330.0px (125.71429dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 498.0px (189.71428dp)
+	unscaled extraSpace: 498.0px (189.71428dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)
+	getCellLayoutHeight(): 1370.0px (521.9048dp)
+	getCellLayoutWidth(): 1083.0px (412.57144dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt
new file mode 100644
index 0000000..b4956ff
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:false
+	isLandscape:true
+	isMultiWindowMode:false
+	isTwoPanels:true
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 2208.0px (841.1429dp)
+	heightPx: 1840.0px (700.9524dp)
+	availableWidthPx: 2208.0px (841.1429dp)
+	availableHeightPx: 1730.0px (659.0476dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 110.0px (41.904762dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.2
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 4
+	inv.numColumns: 4
+	inv.numSearchContainerColumns: 4
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 154.0px (58.666668dp)
+	cellHeightPx: 218.0px (83.04762dp)
+	getCellSize().x: 270.0px (102.85714dp)
+	getCellSize().y: 342.0px (130.28572dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 0.0px (0.0dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 0.0px (0.0dp)
+	cellLayoutPaddingPx.bottom: 0.0px (0.0dp)
+	iconSizePx: 141.0px (53.714287dp)
+	iconTextSizePx: 34.0px (12.952381dp)
+	iconDrawablePaddingPx: 13.0px (4.952381dp)
+	inv.numFolderRows: 3
+	inv.numFolderColumns: 4
+	folderCellWidthPx: 189.0px (72.0dp)
+	folderCellHeightPx: 219.0px (83.42857dp)
+	folderChildIconSizePx: 141.0px (53.714287dp)
+	folderChildTextSizePx: 34.0px (12.952381dp)
+	folderChildDrawablePaddingPx: 5.0px (1.9047619dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 110.0px (41.904762dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 1.0
+	allAppsShiftRange: 1730.0px (659.0476dp)
+	allAppsTopPadding: 110.0px (41.904762dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 141.0px (53.714287dp)
+	allAppsIconTextSizePx: 34.0px (12.952381dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 315.0px (120.0dp)
+	allAppsCellWidthPx: 183.0px (69.71429dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 8
+	allAppsLeftRightPadding: 42.0px (16.0dp)
+	allAppsLeftRightMargin: 183.0px (69.71429dp)
+	hotseatBarSizePx: 267.0px (101.71429dp)
+	inv.hotseatColumnSpan: 4
+	hotseatCellHeightPx: 159.0px (60.57143dp)
+	hotseatBarBottomSpacePx: 126.0px (48.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
+	getHotseatLayoutPadding(context).left: 113.0px (43.04762dp)
+	getHotseatLayoutPadding(context).right: 113.0px (43.04762dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 21.0px (8.0dp)
+	workspacePadding.top: 30.0px (11.428572dp)
+	workspacePadding.right: 21.0px (8.0dp)
+	workspacePadding.bottom: 330.0px (125.71429dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 498.0px (189.71428dp)
+	unscaled extraSpace: 498.0px (189.71428dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 0.0px (0.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)
+	getCellLayoutHeight(): 1370.0px (521.9048dp)
+	getCellLayoutWidth(): 1083.0px (412.57144dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
new file mode 100644
index 0000000..15afb61
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:true
+	isLandscape:false
+	isMultiWindowMode:false
+	isTwoPanels:true
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 1840.0px (700.9524dp)
+	heightPx: 2208.0px (841.1429dp)
+	availableWidthPx: 1840.0px (700.9524dp)
+	availableHeightPx: 2075.0px (790.4762dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 133.0px (50.666668dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.2
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 4
+	inv.numColumns: 4
+	inv.numSearchContainerColumns: 4
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 154.0px (58.666668dp)
+	cellHeightPx: 218.0px (83.04762dp)
+	getCellSize().x: 224.0px (85.333336dp)
+	getCellSize().y: 430.0px (163.80952dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 0.0px (0.0dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 0.0px (0.0dp)
+	cellLayoutPaddingPx.bottom: 0.0px (0.0dp)
+	iconSizePx: 141.0px (53.714287dp)
+	iconTextSizePx: 34.0px (12.952381dp)
+	iconDrawablePaddingPx: 13.0px (4.952381dp)
+	inv.numFolderRows: 3
+	inv.numFolderColumns: 4
+	folderCellWidthPx: 189.0px (72.0dp)
+	folderCellHeightPx: 219.0px (83.42857dp)
+	folderChildIconSizePx: 141.0px (53.714287dp)
+	folderChildTextSizePx: 34.0px (12.952381dp)
+	folderChildDrawablePaddingPx: 5.0px (1.9047619dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 133.0px (50.666668dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 1.0
+	allAppsShiftRange: 1826.0px (695.619dp)
+	allAppsTopPadding: 382.0px (145.5238dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 141.0px (53.714287dp)
+	allAppsIconTextSizePx: 34.0px (12.952381dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 315.0px (120.0dp)
+	allAppsCellWidthPx: 183.0px (69.71429dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 8
+	allAppsLeftRightPadding: 42.0px (16.0dp)
+	allAppsLeftRightMargin: 1.0px (0.3809524dp)
+	hotseatBarSizePx: 267.0px (101.71429dp)
+	inv.hotseatColumnSpan: 4
+	hotseatCellHeightPx: 159.0px (60.57143dp)
+	hotseatBarBottomSpacePx: 126.0px (48.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
+	getHotseatLayoutPadding(context).left: 98.0px (37.333332dp)
+	getHotseatLayoutPadding(context).right: 98.0px (37.333332dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 21.0px (8.0dp)
+	workspacePadding.top: 24.0px (9.142858dp)
+	workspacePadding.right: 21.0px (8.0dp)
+	workspacePadding.bottom: 330.0px (125.71429dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 849.0px (323.42856dp)
+	unscaled extraSpace: 849.0px (323.42856dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 168.0px (64.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 490.0px (186.66667dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1770.0px (674.2857dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.7437536px (0.2833347dp)
+	getCellLayoutHeight(): 1721.0px (655.619dp)
+	getCellLayoutWidth(): 899.0px (342.4762dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
new file mode 100644
index 0000000..6cbed1f
--- /dev/null
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
@@ -0,0 +1,127 @@
+DeviceProfile:
+	1 dp = 2.625 px
+	isTablet:true
+	isPhone:false
+	transposeLayoutWithOrientation:false
+	isGestureMode:false
+	isLandscape:false
+	isMultiWindowMode:false
+	isTwoPanels:true
+	windowX: 0.0px (0.0dp)
+	windowY: 0.0px (0.0dp)
+	widthPx: 1840.0px (700.9524dp)
+	heightPx: 2208.0px (841.1429dp)
+	availableWidthPx: 1840.0px (700.9524dp)
+	availableHeightPx: 2075.0px (790.4762dp)
+	mInsets.left: 0.0px (0.0dp)
+	mInsets.top: 133.0px (50.666668dp)
+	mInsets.right: 0.0px (0.0dp)
+	mInsets.bottom: 0.0px (0.0dp)
+	aspectRatio:1.2
+	isResponsiveGrid:false
+	isScalableGrid:false
+	inv.numRows: 4
+	inv.numColumns: 4
+	inv.numSearchContainerColumns: 4
+	minCellSize: PointF(0.0, 0.0)dp
+	cellWidthPx: 154.0px (58.666668dp)
+	cellHeightPx: 218.0px (83.04762dp)
+	getCellSize().x: 224.0px (85.333336dp)
+	getCellSize().y: 430.0px (163.80952dp)
+	cellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)
+	cellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)
+	cellLayoutPaddingPx.left: 0.0px (0.0dp)
+	cellLayoutPaddingPx.top: 0.0px (0.0dp)
+	cellLayoutPaddingPx.right: 0.0px (0.0dp)
+	cellLayoutPaddingPx.bottom: 0.0px (0.0dp)
+	iconSizePx: 141.0px (53.714287dp)
+	iconTextSizePx: 34.0px (12.952381dp)
+	iconDrawablePaddingPx: 13.0px (4.952381dp)
+	inv.numFolderRows: 3
+	inv.numFolderColumns: 4
+	folderCellWidthPx: 189.0px (72.0dp)
+	folderCellHeightPx: 219.0px (83.42857dp)
+	folderChildIconSizePx: 141.0px (53.714287dp)
+	folderChildTextSizePx: 34.0px (12.952381dp)
+	folderChildDrawablePaddingPx: 5.0px (1.9047619dp)
+	folderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)
+	folderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)
+	folderContentPaddingLeftRight: 21.0px (8.0dp)
+	folderTopPadding: 63.0px (24.0dp)
+	folderFooterHeight: 147.0px (56.0dp)
+	bottomSheetTopPadding: 133.0px (50.666668dp)
+	bottomSheetOpenDuration: 500
+	bottomSheetCloseDuration: 500
+	bottomSheetWorkspaceScale: 0.97
+	bottomSheetDepth: 1.0
+	allAppsShiftRange: 1826.0px (695.619dp)
+	allAppsTopPadding: 382.0px (145.5238dp)
+	allAppsOpenDuration: 500
+	allAppsCloseDuration: 500
+	allAppsIconSizePx: 141.0px (53.714287dp)
+	allAppsIconTextSizePx: 34.0px (12.952381dp)
+	allAppsIconDrawablePaddingPx: 21.0px (8.0dp)
+	allAppsCellHeightPx: 315.0px (120.0dp)
+	allAppsCellWidthPx: 183.0px (69.71429dp)
+	allAppsBorderSpacePxX: 42.0px (16.0dp)
+	allAppsBorderSpacePxY: 42.0px (16.0dp)
+	numShownAllAppsColumns: 8
+	allAppsLeftRightPadding: 42.0px (16.0dp)
+	allAppsLeftRightMargin: 1.0px (0.3809524dp)
+	hotseatBarSizePx: 267.0px (101.71429dp)
+	inv.hotseatColumnSpan: 4
+	hotseatCellHeightPx: 159.0px (60.57143dp)
+	hotseatBarBottomSpacePx: 126.0px (48.0dp)
+	mHotseatBarEdgePaddingPx: 0.0px (0.0dp)
+	mHotseatBarWorkspaceSpacePx: 0.0px (0.0dp)
+	hotseatBarEndOffset: 0.0px (0.0dp)
+	hotseatQsbSpace: 0.0px (0.0dp)
+	hotseatQsbHeight: 0.0px (0.0dp)
+	springLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)
+	getHotseatLayoutPadding(context).top: 0.0px (0.0dp)
+	getHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)
+	getHotseatLayoutPadding(context).left: 98.0px (37.333332dp)
+	getHotseatLayoutPadding(context).right: 98.0px (37.333332dp)
+	numShownHotseatIcons: 6
+	hotseatBorderSpace: 0.0px (0.0dp)
+	isQsbInline: false
+	hotseatQsbWidth: 0.0px (0.0dp)
+	isTaskbarPresent:false
+	isTaskbarPresentInApps:true
+	taskbarHeight: 0.0px (0.0dp)
+	stashedTaskbarHeight: 0.0px (0.0dp)
+	taskbarBottomMargin: 0.0px (0.0dp)
+	taskbarIconSize: 0.0px (0.0dp)
+	desiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)
+	workspacePadding.left: 21.0px (8.0dp)
+	workspacePadding.top: 24.0px (9.142858dp)
+	workspacePadding.right: 21.0px (8.0dp)
+	workspacePadding.bottom: 330.0px (125.71429dp)
+	iconScale: 1.0px (0.3809524dp)
+	cellScaleToFit : 1.0px (0.3809524dp)
+	extraSpace: 849.0px (323.42856dp)
+	unscaled extraSpace: 849.0px (323.42856dp)
+	maxEmptySpace: 0.0px (0.0dp)
+	workspaceTopPadding: 0.0px (0.0dp)
+	workspaceBottomPadding: 0.0px (0.0dp)
+	overviewTaskMarginPx: 0.0px (0.0dp)
+	overviewTaskIconSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizePx: 0.0px (0.0dp)
+	overviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)
+	overviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)
+	overviewActionsTopMarginPx: 0.0px (0.0dp)
+	overviewActionsHeight: 0.0px (0.0dp)
+	overviewActionsClaimedSpaceBelow: 0.0px (0.0dp)
+	overviewActionsButtonSpacing: 0.0px (0.0dp)
+	overviewPageSpacing: 0.0px (0.0dp)
+	overviewRowSpacing: 0.0px (0.0dp)
+	overviewGridSideMargin: 0.0px (0.0dp)
+	dropTargetBarTopMarginPx: 168.0px (64.0dp)
+	dropTargetBarSizePx: 147.0px (56.0dp)
+	dropTargetBarBottomMarginPx: 42.0px (16.0dp)
+	getCellLayoutSpringLoadShrunkTop(): 490.0px (186.66667dp)
+	getCellLayoutSpringLoadShrunkBottom(): 1770.0px (674.2857dp)
+	workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+	getWorkspaceSpringLoadScale(): 0.7437536px (0.2833347dp)
+	getCellLayoutHeight(): 1721.0px (655.619dp)
+	getCellLayoutWidth(): 899.0px (342.4762dp)
diff --git a/tests/res/xml/invalid_hotseat_file_case_1.xml b/tests/res/xml/invalid_hotseat_file_case_1.xml
index fcbc5ea..aaaf8bb 100644
--- a/tests/res/xml/invalid_hotseat_file_case_1.xml
+++ b/tests/res/xml/invalid_hotseat_file_case_1.xml
@@ -19,12 +19,14 @@
         launcher:specType="height"
         launcher:maxAvailableSize="847dp">
         <hotseatQsbSpace launcher:ofAvailableSpace="1" />
+        <edgePadding launcher:fixedSize="36dp" />
     </hotseatSpec>
 
     <hotseatSpec
         launcher:specType="height"
         launcher:maxAvailableSize="9999dp">
         <hotseatQsbSpace launcher:fixedSize="36dp" />
+        <edgePadding launcher:fixedSize="36dp" />
     </hotseatSpec>
 
 </hotseatSpecs>
\ No newline at end of file
diff --git a/tests/res/xml/valid_hotseat_file.xml b/tests/res/xml/valid_hotseat_file.xml
index c7f52e8..f698bd1 100644
--- a/tests/res/xml/valid_hotseat_file.xml
+++ b/tests/res/xml/valid_hotseat_file.xml
@@ -16,15 +16,17 @@
 <hotseatSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
 
     <hotseatSpec
-        launcher:specType="height"
-        launcher:maxAvailableSize="847dp">
+        launcher:maxAvailableSize="847dp"
+        launcher:specType="height">
         <hotseatQsbSpace launcher:fixedSize="24dp" />
+        <edgePadding launcher:fixedSize="48dp" />
     </hotseatSpec>
 
     <hotseatSpec
-        launcher:specType="height"
-        launcher:maxAvailableSize="9999dp">
+        launcher:maxAvailableSize="9999dp"
+        launcher:specType="height">
         <hotseatQsbSpace launcher:fixedSize="36dp" />
+        <edgePadding launcher:fixedSize="48dp" />
     </hotseatSpec>
 
 </hotseatSpecs>
\ No newline at end of file
diff --git a/tests/res/xml/valid_hotseat_land_file.xml b/tests/res/xml/valid_hotseat_land_file.xml
new file mode 100644
index 0000000..fc4a836
--- /dev/null
+++ b/tests/res/xml/valid_hotseat_land_file.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<hotseatSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+
+    <hotseatSpec
+        launcher:maxAvailableSize="743dp"
+        launcher:specType="width">
+        <hotseatQsbSpace launcher:fixedSize="0dp" />
+        <edgePadding launcher:fixedSize="48dp" />
+    </hotseatSpec>
+
+    <hotseatSpec
+        launcher:maxAvailableSize="9999dp"
+        launcher:specType="width">
+        <hotseatQsbSpace launcher:fixedSize="0dp" />
+        <edgePadding launcher:fixedSize="64dp" />
+    </hotseatSpec>
+
+</hotseatSpecs>
\ No newline at end of file
diff --git a/tests/res/xml/valid_workspace_unsorted_file.xml b/tests/res/xml/valid_workspace_unsorted_file.xml
new file mode 100644
index 0000000..1216c81
--- /dev/null
+++ b/tests/res/xml/valid_workspace_unsorted_file.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<workspaceSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+
+    <workspaceSpec
+        launcher:specType="height"
+        launcher:maxAvailableSize="9999dp">
+        <startPadding launcher:fixedSize="8dp" />
+        <endPadding launcher:ofRemainderSpace="1" />
+        <gutter launcher:fixedSize="16dp" />
+        <cellSize launcher:fixedSize="104dp" />
+    </workspaceSpec>
+
+    <!-- 584 grid height -->
+    <workspaceSpec
+        launcher:specType="height"
+        launcher:maxAvailableSize="584dp">
+        <startPadding launcher:fixedSize="0dp" />
+        <endPadding launcher:fixedSize="32dp" />
+        <gutter launcher:fixedSize="16dp" />
+        <cellSize launcher:ofAvailableSpace="0.15808" />
+    </workspaceSpec>
+
+    <!-- 584 grid height + 28 remainder space -->
+    <workspaceSpec
+        launcher:specType="height"
+        launcher:maxAvailableSize="612dp">
+        <startPadding launcher:fixedSize="0dp" />
+        <endPadding launcher:ofRemainderSpace="1" />
+        <gutter launcher:fixedSize="16dp" />
+        <cellSize launcher:fixedSize="104dp" />
+    </workspaceSpec>
+
+    <!-- Width spec is always the same -->
+    <workspaceSpec
+        launcher:specType="width"
+        launcher:maxAvailableSize="9999dp">
+        <startPadding launcher:fixedSize="22dp" />
+        <endPadding launcher:fixedSize="22dp" />
+        <gutter launcher:fixedSize="16dp" />
+        <cellSize launcher:ofRemainderSpace="0.25" />
+    </workspaceSpec>
+
+</workspaceSpecs>
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index cdf8f08..0798e97 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -28,6 +28,7 @@
     public static final String PAUSE_DETECTED_MESSAGE = "TAPL_PAUSE_DETECTED";
     public static final String DISMISS_ANIMATION_ENDS_MESSAGE = "TAPL_DISMISS_ANIMATION_ENDS";
     public static final String FOLDER_OPENED_MESSAGE = "TAPL_FOLDER_OPENED";
+    public static final String SEARCH_RESULT_COMPLETE = "SEARCH_RESULT_COMPLETE";
     public static final int NORMAL_STATE_ORDINAL = 0;
     public static final int SPRING_LOADED_STATE_ORDINAL = 1;
     public static final int OVERVIEW_STATE_ORDINAL = 2;
@@ -102,6 +103,7 @@
     public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
     public static final String REQUEST_TARGET_INSETS = "target-insets";
     public static final String REQUEST_WINDOW_INSETS = "window-insets";
+    public static final String REQUEST_IME_INSETS = "ime-insets";
     public static final String REQUEST_PID = "pid";
     public static final String REQUEST_FORCE_GC = "gc";
     public static final String REQUEST_RECENT_TASKS_LIST = "recent-tasks-list";
@@ -120,7 +122,6 @@
             "get-activities-created-count";
     public static final String REQUEST_GET_ACTIVITIES = "get-activities";
     public static final String REQUEST_HAS_TIS = "has-touch-interaction-service";
-    public static final String REQUEST_IS_TRACKPAD_GESTURE_ENABLED = "is-trackpad-gesture-enabled";
     public static final String REQUEST_TASKBAR_ALL_APPS_TOP_PADDING =
             "taskbar-all-apps-top-padding";
     public static final String REQUEST_ALL_APPS_TOP_PADDING = "all-apps-top-padding";
@@ -156,7 +157,6 @@
     public static final String TWO_TASKBAR_LONG_CLICKS = "b/262282528";
     public static final String FLAKY_QUICK_SWITCH_TO_PREVIOUS_APP = "b/286084688";
     public static final String ICON_MISSING = "b/282963545";
-    public static final String OVERVIEW_OVER_HOME = "b/279059025";
     public static final String INCORRECT_HOME_STATE = "b/293191790";
 
     public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index dd79ca8..ed8e324 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -26,6 +26,7 @@
 import com.android.launcher3.util.DisplayController
 import com.android.launcher3.util.NavigationMode
 import com.android.launcher3.util.WindowBounds
+import com.android.launcher3.util.rule.TestStabilityRule
 import com.android.launcher3.util.window.CachedDisplayInfo
 import com.android.launcher3.util.window.WindowManagerProxy
 import java.io.BufferedReader
@@ -36,6 +37,7 @@
 import kotlin.math.min
 import org.junit.After
 import org.junit.Before
+import org.junit.Rule
 import org.mockito.ArgumentMatchers
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.`when` as whenever
@@ -54,6 +56,8 @@
     private lateinit var originalDisplayController: DisplayController
     private lateinit var originalWindowManagerProxy: WindowManagerProxy
 
+    @Rule @JvmField val testStabilityRule = TestStabilityRule()
+
     @Before
     open fun setUp() {
         val appContext: Context = ApplicationProvider.getApplicationContext()
diff --git a/tests/src/com/android/launcher3/allapps/OopTaplOpenCloseAllApps.java b/tests/src/com/android/launcher3/allapps/OopTaplOpenCloseAllApps.java
new file mode 100644
index 0000000..7d6b7f9
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/OopTaplOpenCloseAllApps.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import static com.android.launcher3.ui.TaplTestsLauncher3.expectFail;
+import static com.android.launcher3.ui.TaplTestsLauncher3.initialize;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.tapl.AllApps;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test that we can open and close the all apps in multiple situations.
+ * The test runs in Out of process (Oop) and in process.
+ */
+public class OopTaplOpenCloseAllApps extends AbstractLauncherUiTest {
+
+    /**
+     * Calls static method initialize
+     */
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        initialize(this);
+    }
+
+    /**
+     * Make sure we can go home after pressing the context menu on an Icon on the AllApps.
+     */
+    @Test
+    public void testPressHomeOnAllAppsContextMenu() {
+        final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+        try {
+            allApps.getAppIcon("TestActivity7").openMenu();
+        } finally {
+            allApps.unfreeze();
+        }
+        mLauncher.goHome();
+    }
+
+    /**
+     * Make sure we can open AllApps from the Workspace.
+     */
+    @Test
+    @PortraitLandscape
+    public void testWorkspaceSwitchToAllApps() {
+        assertNotNull("switchToAllApps() returned null",
+                mLauncher.getWorkspace().switchToAllApps());
+        assertTrue("Launcher internal state is not All Apps",
+                isInState(() -> LauncherState.ALL_APPS));
+    }
+
+    /**
+     * Make sure we can go to Workspace from AllApps
+     */
+    @Test
+    @PortraitLandscape
+    public void testAllAppsSwitchToWorkspace() {
+        assertNotNull("switchToWorkspace() returned null",
+                mLauncher.getWorkspace().switchToAllApps()
+                        .switchToWorkspace(/* swipeDown= */ true));
+        assertTrue("Launcher internal state is not Workspace",
+                isInState(() -> LauncherState.NORMAL));
+    }
+
+    /**
+     * Make sure the swipe up gesture can take us back to the workspace from AllApps
+     */
+    @Test
+    @PortraitLandscape
+    public void testAllAppsSwipeUpToWorkspace() {
+        assertNotNull("testAllAppsSwipeUpToWorkspace() returned null",
+                mLauncher.getWorkspace().switchToAllApps()
+                        .switchToWorkspace(/* swipeDown= */ false));
+        assertTrue("Launcher internal state is not Workspace",
+                isInState(() -> LauncherState.NORMAL));
+    }
+
+    /**
+     * Make sure we can go to the Workspace from AllApps on tablets by tapping on the background.
+     */
+    @Test
+    @PortraitLandscape
+    public void testAllAppsDeadzoneForTablet() {
+        assumeTrue(mLauncher.isTablet());
+
+        mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
+                true /* tapRight */);
+        mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
+                false /* tapRight */);
+    }
+
+    /**
+     * Make sure that AllApps closes when pressing the home button
+     */
+    @Test
+    @PortraitLandscape
+    public void testAllAppsFromHome() {
+        // Test opening all apps
+        assertNotNull("switchToAllApps() returned null",
+                mLauncher.getWorkspace().switchToAllApps());
+
+        runAllAppsTest(mLauncher.getAllApps());
+
+        // Testing pressHome.
+        assertTrue("Launcher internal state is not All Apps",
+                isInState(() -> LauncherState.ALL_APPS));
+        assertNotNull("pressHome returned null", mLauncher.goHome());
+        assertTrue("Launcher internal state is not Home",
+                isInState(() -> LauncherState.NORMAL));
+        assertNotNull("getHome returned null", mLauncher.getWorkspace());
+    }
+
+    /**
+     * Makes sure the state of AllApps is correct.
+     */
+    public void runAllAppsTest(AllApps allApps) {
+        allApps.freeze();
+        try {
+            assertNotNull("allApps parameter is null", allApps);
+
+            assertTrue(
+                    "Launcher internal state is not All Apps",
+                    isInState(() -> LauncherState.ALL_APPS));
+
+            // Test flinging forward and backward.
+            executeOnLauncher(launcher -> assertEquals(
+                    "All Apps started in already scrolled state", 0,
+                    getAllAppsScroll(launcher)));
+
+            allApps.flingForward();
+            assertTrue("Launcher internal state is not All Apps",
+                    isInState(() -> LauncherState.ALL_APPS));
+            final Integer flingForwardY = getFromLauncher(
+                    launcher -> getAllAppsScroll(launcher));
+            executeOnLauncher(
+                    launcher -> assertTrue("flingForward() didn't scroll App Apps",
+                            flingForwardY > 0));
+
+            allApps.flingBackward();
+            assertTrue(
+                    "Launcher internal state is not All Apps",
+                    isInState(() -> LauncherState.ALL_APPS));
+            final Integer flingBackwardY = getFromLauncher(
+                    launcher -> getAllAppsScroll(launcher));
+            executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
+                    flingBackwardY < flingForwardY));
+
+            // Test scrolling down to YouTube.
+            assertNotNull("All apps: can't find YouTube", allApps.getAppIcon("YouTube"));
+            // Test scrolling up to Camera.
+            assertNotNull("All apps: can't find Camera", allApps.getAppIcon("Camera"));
+            // Test failing to find a non-existing app.
+            final AllApps allAppsFinal = allApps;
+            expectFail("All apps: could find a non-existing app",
+                    () -> allAppsFinal.getAppIcon("NO APP"));
+
+            assertTrue(
+                    "Launcher internal state is not All Apps",
+                    isInState(() -> LauncherState.ALL_APPS));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
new file mode 100644
index 0000000..c652b98
--- /dev/null
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.dragging;
+
+import static com.android.launcher3.ui.TaplTestsLauncher3.APP_NAME;
+import static com.android.launcher3.ui.TaplTestsLauncher3.GMAIL_APP_NAME;
+import static com.android.launcher3.ui.TaplTestsLauncher3.MAPS_APP_NAME;
+import static com.android.launcher3.ui.TaplTestsLauncher3.STORE_APP_NAME;
+import static com.android.launcher3.ui.TaplTestsLauncher3.initialize;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.platform.test.annotations.PlatinumTest;
+import android.util.Log;
+
+import com.android.launcher3.tapl.Folder;
+import com.android.launcher3.tapl.FolderIcon;
+import com.android.launcher3.tapl.HomeAllApps;
+import com.android.launcher3.tapl.HomeAppIcon;
+import com.android.launcher3.tapl.HomeAppIconMenuItem;
+import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * This test run in both Out of process (Oop) and in-process (Ipc).
+ * Tests multiple facets of the drag interaction in the Launcher:
+ *    * Can create a folder by dragging items.
+ *    * Can create a shortcut by dragging it to Workspace.
+ *    * Can create shortcuts in multiple spaces in the Workspace.
+ *    * Can cancel a drag icon to workspace by dragging outside of the Workspace.
+ *    * Can drag an icon from AllApps into the workspace
+ *    * Can drag an icon on the Workspace to other positions of the Workspace.
+ */
+public class TaplDragTest extends AbstractLauncherUiTest {
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        initialize(this);
+    }
+
+    /**
+     * Adds two icons to the Workspace and combines them into a folder, then makes sure the icons
+     * are no longer in the Workspace then adds a third one to test adding an icon to an existing
+     * folder instead of creating one and drags it to the folder.
+     */
+    @Test
+    @PortraitLandscape
+    @ScreenRecord
+    @Ignore // b/233075289
+    @PlatinumTest(focusArea = "launcher")
+    public void testDragToFolder() {
+        // TODO: add the use case to drag an icon to an existing folder. Currently it either fails
+        // on tablets or phones due to difference in resolution.
+        final HomeAppIcon playStoreIcon = createShortcutIfNotExist(STORE_APP_NAME, 0, 1);
+        final HomeAppIcon gmailIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
+
+        FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon);
+        Folder folder = folderIcon.open();
+        folder.getAppIcon(STORE_APP_NAME);
+        folder.getAppIcon(GMAIL_APP_NAME);
+        Workspace workspace = folder.close();
+
+        workspace.verifyWorkspaceAppIconIsGone(STORE_APP_NAME + " should be moved to a folder.",
+                STORE_APP_NAME);
+        workspace.verifyWorkspaceAppIconIsGone(GMAIL_APP_NAME + " should be moved to a folder.",
+                GMAIL_APP_NAME);
+
+        final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME);
+        folderIcon = mapIcon.dragToIcon(folderIcon);
+        folder = folderIcon.open();
+        folder.getAppIcon(MAPS_APP_NAME);
+        workspace = folder.close();
+
+        workspace.verifyWorkspaceAppIconIsGone(MAPS_APP_NAME + " should be moved to a folder.",
+                MAPS_APP_NAME);
+    }
+
+
+    /** Drags a shortcut from a long press menu into the workspace.
+     * 1. Open all apps and wait for load complete.
+     * 2. Find the app and long press it to show shortcuts.
+     * 3. Press icon center until shortcuts appear
+     * 4. Drags shortcut to any free space in the Workspace.
+     */
+    @Test
+    @PortraitLandscape
+    @PlatinumTest(focusArea = "launcher")
+    public void testDragShortcut() {
+
+        final HomeAllApps allApps = mLauncher
+                .getWorkspace()
+                .switchToAllApps();
+        allApps.freeze();
+        try {
+            final HomeAppIconMenuItem menuItem = allApps
+                    .getAppIcon(APP_NAME)
+                    .openDeepShortcutMenu()
+                    .getMenuItem(0);
+            final String actualShortcutName = menuItem.getText();
+            final String expectedShortcutName = "Shortcut 1";
+
+            assertEquals(expectedShortcutName, actualShortcutName);
+            menuItem.dragToWorkspace(false, false);
+            mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName)
+                    .launch(getAppPackageName());
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    /**
+     * Similar to testDragShortcut but it adds shortcuts to multiple positions of the Workspace
+     * namely the corners and the center.
+     */
+    @Test
+    @PortraitLandscape
+    @PlatinumTest(focusArea = "launcher")
+    public void testDragShortcutToMultipleWorkspaceCells() {
+        Point[] targets = TestUtil.getCornersAndCenterPositions(mLauncher);
+
+        for (Point target : targets) {
+            final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+            allApps.freeze();
+            try {
+                allApps.getAppIcon(APP_NAME)
+                        .openDeepShortcutMenu()
+                        .getMenuItem(0)
+                        .dragToWorkspace(target.x, target.y);
+            } finally {
+                allApps.unfreeze();
+            }
+        }
+    }
+
+    /**
+     * Drags an icon to the workspace but instead of submitting it, it gets dragged outside of the
+     * Workspace to cancel it.
+     */
+
+    @Test
+    @PortraitLandscape
+    @PlatinumTest(focusArea = "launcher")
+    public void testDragAndCancelAppIcon() {
+        final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
+        Point positionBeforeDrag =
+                mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
+        assertNotNull("App not found in Workspace before dragging.", positionBeforeDrag);
+
+        mLauncher.getWorkspace().dragAndCancelAppIcon(homeAppIcon);
+
+        Point positionAfterDrag =
+                mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
+        assertNotNull("App not found in Workspace after dragging.", positionAfterDrag);
+        assertEquals("App not returned to same position in Workspace after drag & cancel",
+                positionBeforeDrag, positionAfterDrag);
+    }
+
+    /**
+     * Drags app icon from AllApps into the Workspace.
+     * 1. Open all apps and wait for load complete.
+     * 2. Drag icon to homescreen.
+     * 3. Verify that the icon works on homescreen.
+     */
+    @PlatinumTest(focusArea = "launcher")
+    @Test
+    @PortraitLandscape
+    public void testDragAppIcon() {
+
+        final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+        try {
+            allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
+            mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
+        } finally {
+            allApps.unfreeze();
+        }
+        executeOnLauncher(launcher -> assertTrue(
+                "Launcher activity is the top activity; expecting another activity to be the top "
+                        + "one",
+                isInLaunchedApp(launcher)));
+    }
+
+    /**
+     * Similar start to testDragAppIcon but after dragging the icon to the workspace, it drags the
+     * icon inside of the workspace to different positions.
+     * @throws Exception
+     */
+    @Test
+    @PortraitLandscape
+    @PlatinumTest(focusArea = "launcher")
+    public void testDragAppIconToMultipleWorkspaceCells() throws Exception {
+        long startTime, endTime, elapsedTime;
+        Point[] targets = TestUtil.getCornersAndCenterPositions(mLauncher);
+
+        for (Point target : targets) {
+            startTime = SystemClock.uptimeMillis();
+            final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+            allApps.freeze();
+            try {
+                allApps.getAppIcon(APP_NAME).dragToWorkspace(target.x, target.y);
+            } finally {
+                allApps.unfreeze();
+            }
+            // Reset the workspace for the next shortcut creation.
+            initialize(this, true);
+            endTime = SystemClock.uptimeMillis();
+            elapsedTime = endTime - startTime;
+            Log.d("testDragAppIconToWorkspaceCellTime",
+                    "Milliseconds taken to drag app icon to workspace cell: " + elapsedTime);
+        }
+
+        // test to move a shortcut to other cell.
+        final HomeAppIcon launcherTestAppIcon = createShortcutInCenterIfNotExist(APP_NAME);
+        for (Point target : targets) {
+            startTime = SystemClock.uptimeMillis();
+            launcherTestAppIcon.dragToWorkspace(target.x, target.y);
+            endTime = SystemClock.uptimeMillis();
+            elapsedTime = endTime - startTime;
+            Log.d("testDragAppIconToWorkspaceCellTime",
+                    "Milliseconds taken to move shortcut to other cell: " + elapsedTime);
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java
new file mode 100644
index 0000000..712806c
--- /dev/null
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.dragging;
+
+import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
+import static com.android.launcher3.ui.TaplTestsLauncher3.APP_NAME;
+import static com.android.launcher3.ui.TaplTestsLauncher3.DUMMY_APP_NAME;
+import static com.android.launcher3.ui.TaplTestsLauncher3.MAPS_APP_NAME;
+import static com.android.launcher3.ui.TaplTestsLauncher3.STORE_APP_NAME;
+import static com.android.launcher3.ui.TaplTestsLauncher3.initialize;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Point;
+import android.platform.test.annotations.PlatinumTest;
+import android.util.Log;
+
+import com.android.launcher3.tapl.HomeAllApps;
+import com.android.launcher3.tapl.HomeAppIcon;
+import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.rule.ScreenRecordRule;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Test runs in Out of process (Oop) and In process (Ipc)
+ * Test the behaviour of uninstalling and removing apps both from AllApps and from the Workspace.
+ */
+public class TaplUninstallRemove extends AbstractLauncherUiTest {
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        initialize(this);
+    }
+
+    /**
+     * Deletes app both built-in and user-installed from the Workspace and makes sure it's no longer
+     * in the Workspace.
+     */
+    @Test
+    @PortraitLandscape
+    public void testDeleteFromWorkspace() {
+        for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) {
+            final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName);
+            Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon);
+            workspace.verifyWorkspaceAppIconIsGone(
+                    appName + " app was found after being deleted from workspace",
+                    appName);
+        }
+    }
+
+    private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) {
+        final HomeAllApps allApps = workspace.switchToAllApps();
+        Wait.atMost(appName + " app was found on all apps after being uninstalled",
+                () -> allApps.tryGetAppIcon(appName) == null,
+                DEFAULT_UI_TIMEOUT, mLauncher);
+    }
+
+    private void installDummyAppAndWaitForUIUpdate() throws IOException {
+        TestUtil.installDummyApp();
+        waitForLauncherUIUpdate();
+    }
+
+    private void waitForLauncherUIUpdate() {
+        // Wait for model thread completion as it may be processing
+        // the install event from the SystemService
+        mLauncher.waitForModelQueueCleared();
+        // Wait for Launcher UI thread completion, as it may be processing updating the UI in
+        // response to the model update. Not that `waitForLauncherInitialized` is just a proxy
+        // method, we can use any method which touches Launcher UI thread,
+        mLauncher.waitForLauncherInitialized();
+    }
+
+    /**
+     * Makes sure you can uninstall an app from the Workspace.
+     * @throws Exception
+     */
+    @Test
+    @PortraitLandscape
+    // TODO(b/293944634): Remove Screenrecord after flaky debug, and add
+    // @PlatinumTest(focusArea = "launcher") back
+    @ScreenRecordRule.ScreenRecord
+    public void testUninstallFromWorkspace() throws Exception {
+        installDummyAppAndWaitForUIUpdate();
+        try {
+            verifyAppUninstalledFromAllApps(
+                    createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
+        } finally {
+            TestUtil.uninstallDummyApp();
+        }
+    }
+
+    /**
+     * Makes sure you can uninstall an app from AllApps.
+     * @throws Exception
+     */
+    @Test
+    @PortraitLandscape
+    @PlatinumTest(focusArea = "launcher")
+    public void testUninstallFromAllApps() throws Exception {
+        installDummyAppAndWaitForUIUpdate();
+        try {
+            Workspace workspace = mLauncher.getWorkspace();
+            final HomeAllApps allApps = workspace.switchToAllApps();
+            workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall();
+            verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME);
+        } finally {
+            TestUtil.uninstallDummyApp();
+        }
+    }
+
+    /**
+     * Adds three icons to the workspace and removes one of them by dragging to uninstall.
+     */
+    @Test
+    @PlatinumTest(focusArea = "launcher")
+    public void uninstallWorkspaceIcon() throws IOException {
+        Point[] gridPositions = TestUtil.getCornersAndCenterPositions(mLauncher);
+        StringBuilder sb = new StringBuilder();
+        for (Point p : gridPositions) {
+            sb.append(p).append(", ");
+        }
+        Log.d(ICON_MISSING, "allGridPositions: " + sb);
+        createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]);
+        createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]);
+        installDummyAppAndWaitForUIUpdate();
+        try {
+            createShortcutIfNotExist(DUMMY_APP_NAME, gridPositions[2]);
+            Map<String, Point> initialPositions =
+                    mLauncher.getWorkspace().getWorkspaceIconsPositions();
+            assertThat(initialPositions.keySet())
+                    .containsAtLeast(DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME);
+
+            mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall();
+            mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone(
+                    DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME);
+
+            Map<String, Point> finalPositions =
+                    mLauncher.getWorkspace().getWorkspaceIconsPositions();
+            assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME);
+        } finally {
+            TestUtil.uninstallDummyApp();
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
index 270672f..9b67310 100644
--- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
+++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt
@@ -15,14 +15,14 @@
  */
 package com.android.launcher3.nonquickstep
 
+import android.content.Context
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
 import com.android.launcher3.AbstractDeviceProfileTest
 import com.android.launcher3.DeviceProfile
 import com.android.launcher3.InvariantDeviceProfile
 import com.google.common.truth.Truth.assertThat
-import java.io.PrintWriter
-import java.io.StringWriter
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -30,142 +30,14 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
-
+    private val testContext: Context = InstrumentationRegistry.getInstrumentation().context
+    private val folderName: String = "DeviceProfileDumpTest"
     @Test
     fun phonePortrait3Button() {
         initializeVarsForPhone(deviceSpecs["phone"]!!, isGestureMode = false)
         val dp = getDeviceProfileForGrid("5_by_5")
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.625 px\n" +
-                    "\tisTablet:false\n" +
-                    "\tisPhone:true\n" +
-                    "\ttransposeLayoutWithOrientation:true\n" +
-                    "\tisGestureMode:false\n" +
-                    "\tisLandscape:false\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:false\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 1080.0px (411.42856dp)\n" +
-                    "\theightPx: 2400.0px (914.2857dp)\n" +
-                    "\tavailableWidthPx: 1080.0px (411.42856dp)\n" +
-                    "\tavailableHeightPx: 2156.0px (821.3333dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 118.0px (44.95238dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 126.0px (48.0dp)\n" +
-                    "\taspectRatio:2.2222223\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:false\n" +
-                    "\tinv.numRows: 5\n" +
-                    "\tinv.numColumns: 5\n" +
-                    "\tinv.numSearchContainerColumns: 5\n" +
-                    "\tminCellSize: PointF(0.0, 0.0)dp\n" +
-                    "\tcellWidthPx: 159.0px (60.57143dp)\n" +
-                    "\tcellHeightPx: 229.0px (87.2381dp)\n" +
-                    "\tgetCellSize().x: 207.0px (78.85714dp)\n" +
-                    "\tgetCellSize().y: 379.0px (144.38095dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 21.0px (8.0dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 28.0px (10.666667dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 21.0px (8.0dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" +
-                    "\ticonSizePx: 147.0px (56.0dp)\n" +
-                    "\ticonTextSizePx: 38.0px (14.476191dp)\n" +
-                    "\ticonDrawablePaddingPx: 12.0px (4.571429dp)\n" +
-                    "\tinv.numFolderRows: 4\n" +
-                    "\tinv.numFolderColumns: 4\n" +
-                    "\tfolderCellWidthPx: 195.0px (74.28571dp)\n" +
-                    "\tfolderCellHeightPx: 230.0px (87.61905dp)\n" +
-                    "\tfolderChildIconSizePx: 147.0px (56.0dp)\n" +
-                    "\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                    "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 146.0px (55.61905dp)\n" +
-                    "\tbottomSheetOpenDuration: 267\n" +
-                    "\tbottomSheetCloseDuration: 267\n" +
-                    "\tbottomSheetWorkspaceScale: 1.0\n" +
-                    "\tbottomSheetDepth: 0.0\n" +
-                    "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
-                    "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
-                    "\tallAppsOpenDuration: 600\n" +
-                    "\tallAppsCloseDuration: 300\n" +
-                    "\tallAppsIconSizePx: 147.0px (56.0dp)\n" +
-                    "\tallAppsIconTextSizePx: 38.0px (14.476191dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                    "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
-                    "\tallAppsCellWidthPx: 189.0px (72.0dp)\n" +
-                    "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 5\n" +
-                    "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSizePx: 294.0px (112.0dp)\n" +
-                    "\tinv.hotseatColumnSpan: 5\n" +
-                    "\thotseatCellHeightPx: 166.0px (63.238094dp)\n" +
-                    "\thotseatBarBottomSpacePx: 147.0px (56.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 128.0px (48.761906dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 21.0px (8.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 21.0px (8.0dp)\n" +
-                    "\tnumShownHotseatIcons: 5\n" +
-                    "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 0.0px (0.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:false\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.left: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.right: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.bottom: 203.0px (77.333336dp)\n" +
-                    "\ticonScale: 1.0px (0.3809524dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                    "\textraSpace: 752.0px (286.4762dp)\n" +
-                    "\tunscaled extraSpace: 752.0px (286.4762dp)\n" +
-                    "\tmaxEmptySpace: 0.0px (0.0dp)\n" +
-                    "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                    "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 126.0px (48.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 84.0px (32.0dp)\n" +
-                    "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 1906.0px (726.0952dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.77572966px (0.29551607dp)\n" +
-                    "\tgetCellLayoutHeight(): 1953.0px (744.0dp)\n" +
-                    "\tgetCellLayoutWidth(): 1080.0px (411.42856dp)\n"
-            )
+        assertDump(dp, "phonePortrait3Button")
     }
 
     @Test
@@ -173,136 +45,7 @@
         initializeVarsForPhone(deviceSpecs["phone"]!!)
         val dp = getDeviceProfileForGrid("5_by_5")
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.625 px\n" +
-                    "\tisTablet:false\n" +
-                    "\tisPhone:true\n" +
-                    "\ttransposeLayoutWithOrientation:true\n" +
-                    "\tisGestureMode:true\n" +
-                    "\tisLandscape:false\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:false\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 1080.0px (411.42856dp)\n" +
-                    "\theightPx: 2400.0px (914.2857dp)\n" +
-                    "\tavailableWidthPx: 1080.0px (411.42856dp)\n" +
-                    "\tavailableHeightPx: 2219.0px (845.3333dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 118.0px (44.95238dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 63.0px (24.0dp)\n" +
-                    "\taspectRatio:2.2222223\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:false\n" +
-                    "\tinv.numRows: 5\n" +
-                    "\tinv.numColumns: 5\n" +
-                    "\tinv.numSearchContainerColumns: 5\n" +
-                    "\tminCellSize: PointF(0.0, 0.0)dp\n" +
-                    "\tcellWidthPx: 159.0px (60.57143dp)\n" +
-                    "\tcellHeightPx: 229.0px (87.2381dp)\n" +
-                    "\tgetCellSize().x: 207.0px (78.85714dp)\n" +
-                    "\tgetCellSize().y: 383.0px (145.90475dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 21.0px (8.0dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 28.0px (10.666667dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 21.0px (8.0dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" +
-                    "\ticonSizePx: 147.0px (56.0dp)\n" +
-                    "\ticonTextSizePx: 38.0px (14.476191dp)\n" +
-                    "\ticonDrawablePaddingPx: 12.0px (4.571429dp)\n" +
-                    "\tinv.numFolderRows: 4\n" +
-                    "\tinv.numFolderColumns: 4\n" +
-                    "\tfolderCellWidthPx: 195.0px (74.28571dp)\n" +
-                    "\tfolderCellHeightPx: 230.0px (87.61905dp)\n" +
-                    "\tfolderChildIconSizePx: 147.0px (56.0dp)\n" +
-                    "\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                    "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 146.0px (55.61905dp)\n" +
-                    "\tbottomSheetOpenDuration: 267\n" +
-                    "\tbottomSheetCloseDuration: 267\n" +
-                    "\tbottomSheetWorkspaceScale: 1.0\n" +
-                    "\tbottomSheetDepth: 0.0\n" +
-                    "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
-                    "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
-                    "\tallAppsOpenDuration: 600\n" +
-                    "\tallAppsCloseDuration: 300\n" +
-                    "\tallAppsIconSizePx: 147.0px (56.0dp)\n" +
-                    "\tallAppsIconTextSizePx: 38.0px (14.476191dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                    "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
-                    "\tallAppsCellWidthPx: 189.0px (72.0dp)\n" +
-                    "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 5\n" +
-                    "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSizePx: 273.0px (104.0dp)\n" +
-                    "\tinv.hotseatColumnSpan: 5\n" +
-                    "\thotseatCellHeightPx: 166.0px (63.238094dp)\n" +
-                    "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 107.0px (40.761906dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 21.0px (8.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 21.0px (8.0dp)\n" +
-                    "\tnumShownHotseatIcons: 5\n" +
-                    "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 0.0px (0.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:false\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.left: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.right: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.bottom: 245.0px (93.333336dp)\n" +
-                    "\ticonScale: 1.0px (0.3809524dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                    "\textraSpace: 773.0px (294.4762dp)\n" +
-                    "\tunscaled extraSpace: 773.0px (294.4762dp)\n" +
-                    "\tmaxEmptySpace: 0.0px (0.0dp)\n" +
-                    "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                    "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 63.0px (24.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 84.0px (32.0dp)\n" +
-                    "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 1927.0px (734.0952dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.7781155px (0.29642496dp)\n" +
-                    "\tgetCellLayoutHeight(): 1974.0px (752.0dp)\n" +
-                    "\tgetCellLayoutWidth(): 1080.0px (411.42856dp)\n"
-            )
+        assertDump(dp, "phonePortrait")
     }
 
     @Test
@@ -310,136 +53,7 @@
         initializeVarsForPhone(deviceSpecs["phone"]!!, isVerticalBar = true, isGestureMode = false)
         val dp = getDeviceProfileForGrid("5_by_5")
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.625 px\n" +
-                    "\tisTablet:false\n" +
-                    "\tisPhone:true\n" +
-                    "\ttransposeLayoutWithOrientation:true\n" +
-                    "\tisGestureMode:false\n" +
-                    "\tisLandscape:true\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:false\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 2400.0px (914.2857dp)\n" +
-                    "\theightPx: 1080.0px (411.42856dp)\n" +
-                    "\tavailableWidthPx: 2156.0px (821.3333dp)\n" +
-                    "\tavailableHeightPx: 1006.0px (383.2381dp)\n" +
-                    "\tmInsets.left: 118.0px (44.95238dp)\n" +
-                    "\tmInsets.top: 74.0px (28.190475dp)\n" +
-                    "\tmInsets.right: 126.0px (48.0dp)\n" +
-                    "\tmInsets.bottom: 0.0px (0.0dp)\n" +
-                    "\taspectRatio:2.2222223\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:false\n" +
-                    "\tinv.numRows: 5\n" +
-                    "\tinv.numColumns: 5\n" +
-                    "\tinv.numSearchContainerColumns: 5\n" +
-                    "\tminCellSize: PointF(0.0, 0.0)dp\n" +
-                    "\tcellWidthPx: 152.0px (57.904762dp)\n" +
-                    "\tcellHeightPx: 166.0px (63.238094dp)\n" +
-                    "\tgetCellSize().x: 368.0px (140.19048dp)\n" +
-                    "\tgetCellSize().y: 193.0px (73.52381dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 53.0px (20.190475dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 53.0px (20.190475dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 40.0px (15.238095dp)\n" +
-                    "\ticonSizePx: 147.0px (56.0dp)\n" +
-                    "\ticonTextSizePx: 0.0px (0.0dp)\n" +
-                    "\ticonDrawablePaddingPx: 0.0px (0.0dp)\n" +
-                    "\tinv.numFolderRows: 4\n" +
-                    "\tinv.numFolderColumns: 4\n" +
-                    "\tfolderCellWidthPx: 173.0px (65.90476dp)\n" +
-                    "\tfolderCellHeightPx: 205.0px (78.09524dp)\n" +
-                    "\tfolderChildIconSizePx: 131.0px (49.904762dp)\n" +
-                    "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                    "\tfolderTopPadding: 56.0px (21.333334dp)\n" +
-                    "\tfolderFooterHeight: 131.0px (49.904762dp)\n" +
-                    "\tbottomSheetTopPadding: 114.0px (43.42857dp)\n" +
-                    "\tbottomSheetOpenDuration: 267\n" +
-                    "\tbottomSheetCloseDuration: 267\n" +
-                    "\tbottomSheetWorkspaceScale: 1.0\n" +
-                    "\tbottomSheetDepth: 0.0\n" +
-                    "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
-                    "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
-                    "\tallAppsOpenDuration: 600\n" +
-                    "\tallAppsCloseDuration: 300\n" +
-                    "\tallAppsIconSizePx: 147.0px (56.0dp)\n" +
-                    "\tallAppsIconTextSizePx: 38.0px (14.476191dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                    "\tallAppsCellHeightPx: 321.0px (122.28571dp)\n" +
-                    "\tallAppsCellWidthPx: 189.0px (72.0dp)\n" +
-                    "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 5\n" +
-                    "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSizePx: 252.0px (96.0dp)\n" +
-                    "\tinv.hotseatColumnSpan: 5\n" +
-                    "\thotseatCellHeightPx: 166.0px (63.238094dp)\n" +
-                    "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 63.0px (24.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 42.0px (16.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 64.0px (24.380953dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 49.0px (18.666666dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 42.0px (16.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 189.0px (72.0dp)\n" +
-                    "\tnumShownHotseatIcons: 5\n" +
-                    "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 0.0px (0.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:false\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.left: 10.0px (3.8095238dp)\n" +
-                    "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.right: 199.0px (75.809525dp)\n" +
-                    "\tworkspacePadding.bottom: 0.0px (0.0dp)\n" +
-                    "\ticonScale: 1.0px (0.3809524dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                    "\textraSpace: 136.0px (51.809525dp)\n" +
-                    "\tunscaled extraSpace: 136.0px (51.809525dp)\n" +
-                    "\tmaxEmptySpace: 0.0px (0.0dp)\n" +
-                    "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                    "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 16.0px (6.095238dp)\n" +
-                    "\tdropTargetBarSizePx: 95.0px (36.190475dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 16.0px (6.095238dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 1008.0px (384.0dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.8021869px (0.305595dp)\n" +
-                    "\tgetCellLayoutHeight(): 1006.0px (383.2381dp)\n" +
-                    "\tgetCellLayoutWidth(): 1947.0px (741.7143dp)\n"
-            )
+        assertDump(dp, "phoneVerticalBar3Button")
     }
 
     @Test
@@ -447,136 +61,7 @@
         initializeVarsForPhone(deviceSpecs["phone"]!!, isVerticalBar = true)
         val dp = getDeviceProfileForGrid("5_by_5")
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.625 px\n" +
-                    "\tisTablet:false\n" +
-                    "\tisPhone:true\n" +
-                    "\ttransposeLayoutWithOrientation:true\n" +
-                    "\tisGestureMode:true\n" +
-                    "\tisLandscape:true\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:false\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 2400.0px (914.2857dp)\n" +
-                    "\theightPx: 1080.0px (411.42856dp)\n" +
-                    "\tavailableWidthPx: 2282.0px (869.3333dp)\n" +
-                    "\tavailableHeightPx: 943.0px (359.2381dp)\n" +
-                    "\tmInsets.left: 118.0px (44.95238dp)\n" +
-                    "\tmInsets.top: 74.0px (28.190475dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 63.0px (24.0dp)\n" +
-                    "\taspectRatio:2.2222223\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:false\n" +
-                    "\tinv.numRows: 5\n" +
-                    "\tinv.numColumns: 5\n" +
-                    "\tinv.numSearchContainerColumns: 5\n" +
-                    "\tminCellSize: PointF(0.0, 0.0)dp\n" +
-                    "\tcellWidthPx: 152.0px (57.904762dp)\n" +
-                    "\tcellHeightPx: 166.0px (63.238094dp)\n" +
-                    "\tgetCellSize().x: 393.0px (149.71428dp)\n" +
-                    "\tgetCellSize().y: 180.0px (68.57143dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 53.0px (20.190475dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 53.0px (20.190475dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 40.0px (15.238095dp)\n" +
-                    "\ticonSizePx: 147.0px (56.0dp)\n" +
-                    "\ticonTextSizePx: 0.0px (0.0dp)\n" +
-                    "\ticonDrawablePaddingPx: 0.0px (0.0dp)\n" +
-                    "\tinv.numFolderRows: 4\n" +
-                    "\tinv.numFolderColumns: 4\n" +
-                    "\tfolderCellWidthPx: 163.0px (62.095238dp)\n" +
-                    "\tfolderCellHeightPx: 192.0px (73.14286dp)\n" +
-                    "\tfolderChildIconSizePx: 123.0px (46.857143dp)\n" +
-                    "\tfolderChildTextSizePx: 32.0px (12.190476dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 3.0px (1.1428572dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                    "\tfolderTopPadding: 53.0px (20.190475dp)\n" +
-                    "\tfolderFooterHeight: 123.0px (46.857143dp)\n" +
-                    "\tbottomSheetTopPadding: 114.0px (43.42857dp)\n" +
-                    "\tbottomSheetOpenDuration: 267\n" +
-                    "\tbottomSheetCloseDuration: 267\n" +
-                    "\tbottomSheetWorkspaceScale: 1.0\n" +
-                    "\tbottomSheetDepth: 0.0\n" +
-                    "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
-                    "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
-                    "\tallAppsOpenDuration: 600\n" +
-                    "\tallAppsCloseDuration: 300\n" +
-                    "\tallAppsIconSizePx: 147.0px (56.0dp)\n" +
-                    "\tallAppsIconTextSizePx: 38.0px (14.476191dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                    "\tallAppsCellHeightPx: 321.0px (122.28571dp)\n" +
-                    "\tallAppsCellWidthPx: 189.0px (72.0dp)\n" +
-                    "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 5\n" +
-                    "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSizePx: 252.0px (96.0dp)\n" +
-                    "\tinv.hotseatColumnSpan: 5\n" +
-                    "\thotseatCellHeightPx: 166.0px (63.238094dp)\n" +
-                    "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 63.0px (24.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 42.0px (16.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 64.0px (24.380953dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 112.0px (42.666668dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 42.0px (16.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 63.0px (24.0dp)\n" +
-                    "\tnumShownHotseatIcons: 5\n" +
-                    "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 0.0px (0.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:false\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.left: 10.0px (3.8095238dp)\n" +
-                    "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.right: 199.0px (75.809525dp)\n" +
-                    "\tworkspacePadding.bottom: 0.0px (0.0dp)\n" +
-                    "\ticonScale: 1.0px (0.3809524dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                    "\textraSpace: 73.0px (27.809525dp)\n" +
-                    "\tunscaled extraSpace: 73.0px (27.809525dp)\n" +
-                    "\tmaxEmptySpace: 0.0px (0.0dp)\n" +
-                    "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                    "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 63.0px (24.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 16.0px (6.095238dp)\n" +
-                    "\tdropTargetBarSizePx: 95.0px (36.190475dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 16.0px (6.095238dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 952.0px (362.66666dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.79639447px (0.30338836dp)\n" +
-                    "\tgetCellLayoutHeight(): 943.0px (359.2381dp)\n" +
-                    "\tgetCellLayoutWidth(): 2073.0px (789.7143dp)\n"
-            )
+        assertDump(dp, "phoneVerticalBar")
     }
 
     @Test
@@ -585,136 +70,7 @@
         val dp = getDeviceProfileForGrid("6_by_5")
         dp.isTaskbarPresentInApps = true
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.0 px\n" +
-                    "\tisTablet:true\n" +
-                    "\tisPhone:false\n" +
-                    "\ttransposeLayoutWithOrientation:false\n" +
-                    "\tisGestureMode:false\n" +
-                    "\tisLandscape:true\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:false\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 2560.0px (1280.0dp)\n" +
-                    "\theightPx: 1600.0px (800.0dp)\n" +
-                    "\tavailableWidthPx: 2560.0px (1280.0dp)\n" +
-                    "\tavailableHeightPx: 1496.0px (748.0dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 104.0px (52.0dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 0.0px (0.0dp)\n" +
-                    "\taspectRatio:1.6\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:true\n" +
-                    "\tinv.numRows: 5\n" +
-                    "\tinv.numColumns: 6\n" +
-                    "\tinv.numSearchContainerColumns: 3\n" +
-                    "\tminCellSize: PointF(120.0, 104.0)dp\n" +
-                    "\tcellWidthPx: 240.0px (120.0dp)\n" +
-                    "\tcellHeightPx: 208.0px (104.0dp)\n" +
-                    "\tgetCellSize().x: 240.0px (120.0dp)\n" +
-                    "\tgetCellSize().y: 208.0px (104.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 128.0px (64.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 59.0px (29.5dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 25.0px (12.5dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 59.0px (29.5dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" +
-                    "\ticonSizePx: 120.0px (60.0dp)\n" +
-                    "\ticonTextSizePx: 28.0px (14.0dp)\n" +
-                    "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" +
-                    "\tinv.numFolderRows: 3\n" +
-                    "\tinv.numFolderColumns: 3\n" +
-                    "\tfolderCellWidthPx: 240.0px (120.0dp)\n" +
-                    "\tfolderCellHeightPx: 208.0px (104.0dp)\n" +
-                    "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
-                    "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 11.0px (5.5dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
-                    "\tfolderTopPadding: 48.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 112.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 104.0px (52.0dp)\n" +
-                    "\tbottomSheetOpenDuration: 500\n" +
-                    "\tbottomSheetCloseDuration: 500\n" +
-                    "\tbottomSheetWorkspaceScale: 0.97\n" +
-                    "\tbottomSheetDepth: 0.0\n" +
-                    "\tallAppsShiftRange: 1496.0px (748.0dp)\n" +
-                    "\tallAppsTopPadding: 104.0px (52.0dp)\n" +
-                    "\tallAppsOpenDuration: 500\n" +
-                    "\tallAppsCloseDuration: 500\n" +
-                    "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
-                    "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" +
-                    "\tallAppsCellHeightPx: 284.0px (142.0dp)\n" +
-                    "\tallAppsCellWidthPx: 252.0px (126.0dp)\n" +
-                    "\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 6\n" +
-                    "\tallAppsLeftRightPadding: 32.0px (16.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 412.0px (206.0dp)\n" +
-                    "\thotseatBarSizePx: 200.0px (100.0dp)\n" +
-                    "\tinv.hotseatColumnSpan: 4\n" +
-                    "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
-                    "\thotseatBarBottomSpacePx: 80.0px (40.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 668.0px (334.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 668.0px (334.0dp)\n" +
-                    "\tnumShownHotseatIcons: 6\n" +
-                    "\thotseatBorderSpace: 100.0px (50.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 1224.0px (612.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:true\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 240.0px (120.0dp)\n" +
-                    "\tworkspacePadding.left: 181.0px (90.5dp)\n" +
-                    "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.right: 181.0px (90.5dp)\n" +
-                    "\tworkspacePadding.bottom: 244.0px (122.0dp)\n" +
-                    "\ticonScale: 1.0px (0.5dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.5dp)\n" +
-                    "\textraSpace: 80.0px (40.0dp)\n" +
-                    "\tunscaled extraSpace: 80.0px (40.0dp)\n" +
-                    "\tmaxEmptySpace: 200.0px (100.0dp)\n" +
-                    "\tworkspaceTopPadding: 25.0px (12.5dp)\n" +
-                    "\tworkspaceBottomPadding: 55.0px (27.5dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 64.0px (32.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.76677316px (0.38338658dp)\n" +
-                    "\tgetCellLayoutHeight(): 1252.0px (626.0dp)\n" +
-                    "\tgetCellLayoutWidth(): 2198.0px (1099.0dp)\n"
-            )
+        assertDump(dp, "tabletLandscape3Button")
     }
 
     @Test
@@ -723,136 +79,7 @@
         val dp = getDeviceProfileForGrid("6_by_5")
         dp.isTaskbarPresentInApps = true
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.0 px\n" +
-                    "\tisTablet:true\n" +
-                    "\tisPhone:false\n" +
-                    "\ttransposeLayoutWithOrientation:false\n" +
-                    "\tisGestureMode:true\n" +
-                    "\tisLandscape:true\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:false\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 2560.0px (1280.0dp)\n" +
-                    "\theightPx: 1600.0px (800.0dp)\n" +
-                    "\tavailableWidthPx: 2560.0px (1280.0dp)\n" +
-                    "\tavailableHeightPx: 1496.0px (748.0dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 104.0px (52.0dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 0.0px (0.0dp)\n" +
-                    "\taspectRatio:1.6\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:true\n" +
-                    "\tinv.numRows: 5\n" +
-                    "\tinv.numColumns: 6\n" +
-                    "\tinv.numSearchContainerColumns: 3\n" +
-                    "\tminCellSize: PointF(120.0, 104.0)dp\n" +
-                    "\tcellWidthPx: 240.0px (120.0dp)\n" +
-                    "\tcellHeightPx: 208.0px (104.0dp)\n" +
-                    "\tgetCellSize().x: 240.0px (120.0dp)\n" +
-                    "\tgetCellSize().y: 208.0px (104.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 128.0px (64.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 59.0px (29.5dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 25.0px (12.5dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 59.0px (29.5dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" +
-                    "\ticonSizePx: 120.0px (60.0dp)\n" +
-                    "\ticonTextSizePx: 28.0px (14.0dp)\n" +
-                    "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" +
-                    "\tinv.numFolderRows: 3\n" +
-                    "\tinv.numFolderColumns: 3\n" +
-                    "\tfolderCellWidthPx: 240.0px (120.0dp)\n" +
-                    "\tfolderCellHeightPx: 208.0px (104.0dp)\n" +
-                    "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
-                    "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 11.0px (5.5dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
-                    "\tfolderTopPadding: 48.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 112.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 104.0px (52.0dp)\n" +
-                    "\tbottomSheetOpenDuration: 500\n" +
-                    "\tbottomSheetCloseDuration: 500\n" +
-                    "\tbottomSheetWorkspaceScale: 0.97\n" +
-                    "\tbottomSheetDepth: 0.0\n" +
-                    "\tallAppsShiftRange: 1496.0px (748.0dp)\n" +
-                    "\tallAppsTopPadding: 104.0px (52.0dp)\n" +
-                    "\tallAppsOpenDuration: 500\n" +
-                    "\tallAppsCloseDuration: 500\n" +
-                    "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
-                    "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" +
-                    "\tallAppsCellHeightPx: 284.0px (142.0dp)\n" +
-                    "\tallAppsCellWidthPx: 252.0px (126.0dp)\n" +
-                    "\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 6\n" +
-                    "\tallAppsLeftRightPadding: 32.0px (16.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 412.0px (206.0dp)\n" +
-                    "\thotseatBarSizePx: 200.0px (100.0dp)\n" +
-                    "\tinv.hotseatColumnSpan: 4\n" +
-                    "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
-                    "\thotseatBarBottomSpacePx: 80.0px (40.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 668.0px (334.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 668.0px (334.0dp)\n" +
-                    "\tnumShownHotseatIcons: 6\n" +
-                    "\thotseatBorderSpace: 100.0px (50.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 1224.0px (612.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:true\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 240.0px (120.0dp)\n" +
-                    "\tworkspacePadding.left: 181.0px (90.5dp)\n" +
-                    "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
-                    "\tworkspacePadding.right: 181.0px (90.5dp)\n" +
-                    "\tworkspacePadding.bottom: 244.0px (122.0dp)\n" +
-                    "\ticonScale: 1.0px (0.5dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.5dp)\n" +
-                    "\textraSpace: 80.0px (40.0dp)\n" +
-                    "\tunscaled extraSpace: 80.0px (40.0dp)\n" +
-                    "\tmaxEmptySpace: 200.0px (100.0dp)\n" +
-                    "\tworkspaceTopPadding: 25.0px (12.5dp)\n" +
-                    "\tworkspaceBottomPadding: 55.0px (27.5dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 64.0px (32.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.76677316px (0.38338658dp)\n" +
-                    "\tgetCellLayoutHeight(): 1252.0px (626.0dp)\n" +
-                    "\tgetCellLayoutWidth(): 2198.0px (1099.0dp)\n"
-            )
+        assertDump(dp, "tabletLandscape")
     }
 
     @Test
@@ -861,136 +88,7 @@
         val dp = getDeviceProfileForGrid("6_by_5")
         dp.isTaskbarPresentInApps = true
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.0 px\n" +
-                    "\tisTablet:true\n" +
-                    "\tisPhone:false\n" +
-                    "\ttransposeLayoutWithOrientation:false\n" +
-                    "\tisGestureMode:false\n" +
-                    "\tisLandscape:false\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:false\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 1600.0px (800.0dp)\n" +
-                    "\theightPx: 2560.0px (1280.0dp)\n" +
-                    "\tavailableWidthPx: 1600.0px (800.0dp)\n" +
-                    "\tavailableHeightPx: 2456.0px (1228.0dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 104.0px (52.0dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 0.0px (0.0dp)\n" +
-                    "\taspectRatio:1.6\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:true\n" +
-                    "\tinv.numRows: 5\n" +
-                    "\tinv.numColumns: 6\n" +
-                    "\tinv.numSearchContainerColumns: 3\n" +
-                    "\tminCellSize: PointF(102.0, 120.0)dp\n" +
-                    "\tcellWidthPx: 204.0px (102.0dp)\n" +
-                    "\tcellHeightPx: 240.0px (120.0dp)\n" +
-                    "\tgetCellSize().x: 204.0px (102.0dp)\n" +
-                    "\tgetCellSize().y: 240.0px (120.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 128.0px (64.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 72.0px (36.0dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 72.0px (36.0dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 72.0px (36.0dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" +
-                    "\ticonSizePx: 120.0px (60.0dp)\n" +
-                    "\ticonTextSizePx: 28.0px (14.0dp)\n" +
-                    "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" +
-                    "\tinv.numFolderRows: 3\n" +
-                    "\tinv.numFolderColumns: 3\n" +
-                    "\tfolderCellWidthPx: 204.0px (102.0dp)\n" +
-                    "\tfolderCellHeightPx: 240.0px (120.0dp)\n" +
-                    "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
-                    "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 22.0px (11.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
-                    "\tfolderTopPadding: 48.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 112.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 704.0px (352.0dp)\n" +
-                    "\tbottomSheetOpenDuration: 500\n" +
-                    "\tbottomSheetCloseDuration: 500\n" +
-                    "\tbottomSheetWorkspaceScale: 0.97\n" +
-                    "\tbottomSheetDepth: 0.0\n" +
-                    "\tallAppsShiftRange: 1810.0px (905.0dp)\n" +
-                    "\tallAppsTopPadding: 750.0px (375.0dp)\n" +
-                    "\tallAppsOpenDuration: 500\n" +
-                    "\tallAppsCloseDuration: 500\n" +
-                    "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
-                    "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" +
-                    "\tallAppsCellHeightPx: 316.0px (158.0dp)\n" +
-                    "\tallAppsCellWidthPx: 192.0px (96.0dp)\n" +
-                    "\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 6\n" +
-                    "\tallAppsLeftRightPadding: 32.0px (16.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 152.0px (76.0dp)\n" +
-                    "\thotseatBarSizePx: 272.0px (136.0dp)\n" +
-                    "\tinv.hotseatColumnSpan: 6\n" +
-                    "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
-                    "\thotseatBarBottomSpacePx: 152.0px (76.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 137.0px (68.5dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 150.0px (75.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 150.0px (75.0dp)\n" +
-                    "\tnumShownHotseatIcons: 6\n" +
-                    "\thotseatBorderSpace: 116.0px (58.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 1300.0px (650.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:true\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)\n" +
-                    "\tworkspacePadding.left: 36.0px (18.0dp)\n" +
-                    "\tworkspacePadding.top: 132.0px (66.0dp)\n" +
-                    "\tworkspacePadding.right: 36.0px (18.0dp)\n" +
-                    "\tworkspacePadding.bottom: 468.0px (234.0dp)\n" +
-                    "\ticonScale: 1.0px (0.5dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.5dp)\n" +
-                    "\textraSpace: 424.0px (212.0dp)\n" +
-                    "\tunscaled extraSpace: 424.0px (212.0dp)\n" +
-                    "\tmaxEmptySpace: 19998.0px (9999.0dp)\n" +
-                    "\tworkspaceTopPadding: 204.0px (102.0dp)\n" +
-                    "\tworkspaceBottomPadding: 220.0px (110.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 220.0px (110.0dp)\n" +
-                    "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 96.0px (48.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 2072.0px (1036.0dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.8125px (0.40625dp)\n" +
-                    "\tgetCellLayoutHeight(): 1856.0px (928.0dp)\n" +
-                    "\tgetCellLayoutWidth(): 1528.0px (764.0dp)\n"
-            )
+        assertDump(dp, "tabletPortrait3Button")
     }
 
     @Test
@@ -999,136 +97,7 @@
         val dp = getDeviceProfileForGrid("6_by_5")
         dp.isTaskbarPresentInApps = true
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.0 px\n" +
-                    "\tisTablet:true\n" +
-                    "\tisPhone:false\n" +
-                    "\ttransposeLayoutWithOrientation:false\n" +
-                    "\tisGestureMode:true\n" +
-                    "\tisLandscape:false\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:false\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 1600.0px (800.0dp)\n" +
-                    "\theightPx: 2560.0px (1280.0dp)\n" +
-                    "\tavailableWidthPx: 1600.0px (800.0dp)\n" +
-                    "\tavailableHeightPx: 2456.0px (1228.0dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 104.0px (52.0dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 0.0px (0.0dp)\n" +
-                    "\taspectRatio:1.6\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:true\n" +
-                    "\tinv.numRows: 5\n" +
-                    "\tinv.numColumns: 6\n" +
-                    "\tinv.numSearchContainerColumns: 3\n" +
-                    "\tminCellSize: PointF(102.0, 120.0)dp\n" +
-                    "\tcellWidthPx: 204.0px (102.0dp)\n" +
-                    "\tcellHeightPx: 240.0px (120.0dp)\n" +
-                    "\tgetCellSize().x: 204.0px (102.0dp)\n" +
-                    "\tgetCellSize().y: 240.0px (120.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 128.0px (64.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 72.0px (36.0dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 72.0px (36.0dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 72.0px (36.0dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" +
-                    "\ticonSizePx: 120.0px (60.0dp)\n" +
-                    "\ticonTextSizePx: 28.0px (14.0dp)\n" +
-                    "\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" +
-                    "\tinv.numFolderRows: 3\n" +
-                    "\tinv.numFolderColumns: 3\n" +
-                    "\tfolderCellWidthPx: 204.0px (102.0dp)\n" +
-                    "\tfolderCellHeightPx: 240.0px (120.0dp)\n" +
-                    "\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
-                    "\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 22.0px (11.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
-                    "\tfolderTopPadding: 48.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 112.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 704.0px (352.0dp)\n" +
-                    "\tbottomSheetOpenDuration: 500\n" +
-                    "\tbottomSheetCloseDuration: 500\n" +
-                    "\tbottomSheetWorkspaceScale: 0.97\n" +
-                    "\tbottomSheetDepth: 0.0\n" +
-                    "\tallAppsShiftRange: 1810.0px (905.0dp)\n" +
-                    "\tallAppsTopPadding: 750.0px (375.0dp)\n" +
-                    "\tallAppsOpenDuration: 500\n" +
-                    "\tallAppsCloseDuration: 500\n" +
-                    "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
-                    "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" +
-                    "\tallAppsCellHeightPx: 316.0px (158.0dp)\n" +
-                    "\tallAppsCellWidthPx: 192.0px (96.0dp)\n" +
-                    "\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 32.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 6\n" +
-                    "\tallAppsLeftRightPadding: 32.0px (16.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 152.0px (76.0dp)\n" +
-                    "\thotseatBarSizePx: 272.0px (136.0dp)\n" +
-                    "\tinv.hotseatColumnSpan: 6\n" +
-                    "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
-                    "\thotseatBarBottomSpacePx: 152.0px (76.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 137.0px (68.5dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 150.0px (75.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 150.0px (75.0dp)\n" +
-                    "\tnumShownHotseatIcons: 6\n" +
-                    "\thotseatBorderSpace: 116.0px (58.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 1300.0px (650.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:true\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)\n" +
-                    "\tworkspacePadding.left: 36.0px (18.0dp)\n" +
-                    "\tworkspacePadding.top: 132.0px (66.0dp)\n" +
-                    "\tworkspacePadding.right: 36.0px (18.0dp)\n" +
-                    "\tworkspacePadding.bottom: 468.0px (234.0dp)\n" +
-                    "\ticonScale: 1.0px (0.5dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.5dp)\n" +
-                    "\textraSpace: 424.0px (212.0dp)\n" +
-                    "\tunscaled extraSpace: 424.0px (212.0dp)\n" +
-                    "\tmaxEmptySpace: 19998.0px (9999.0dp)\n" +
-                    "\tworkspaceTopPadding: 204.0px (102.0dp)\n" +
-                    "\tworkspaceBottomPadding: 220.0px (110.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 220.0px (110.0dp)\n" +
-                    "\tdropTargetBarSizePx: 144.0px (72.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 96.0px (48.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 2072.0px (1036.0dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.8125px (0.40625dp)\n" +
-                    "\tgetCellLayoutHeight(): 1856.0px (928.0dp)\n" +
-                    "\tgetCellLayoutWidth(): 1528.0px (764.0dp)\n"
-            )
+        assertDump(dp, "tabletPortrait")
     }
 
     @Test
@@ -1142,136 +111,7 @@
         val dp = getDeviceProfileForGrid("4_by_4")
         dp.isTaskbarPresentInApps = true
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.625 px\n" +
-                    "\tisTablet:true\n" +
-                    "\tisPhone:false\n" +
-                    "\ttransposeLayoutWithOrientation:false\n" +
-                    "\tisGestureMode:false\n" +
-                    "\tisLandscape:true\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:true\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 2208.0px (841.1429dp)\n" +
-                    "\theightPx: 1840.0px (700.9524dp)\n" +
-                    "\tavailableWidthPx: 2208.0px (841.1429dp)\n" +
-                    "\tavailableHeightPx: 1730.0px (659.0476dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 110.0px (41.904762dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 0.0px (0.0dp)\n" +
-                    "\taspectRatio:1.2\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:false\n" +
-                    "\tinv.numRows: 4\n" +
-                    "\tinv.numColumns: 4\n" +
-                    "\tinv.numSearchContainerColumns: 4\n" +
-                    "\tminCellSize: PointF(0.0, 0.0)dp\n" +
-                    "\tcellWidthPx: 154.0px (58.666668dp)\n" +
-                    "\tcellHeightPx: 218.0px (83.04762dp)\n" +
-                    "\tgetCellSize().x: 270.0px (102.85714dp)\n" +
-                    "\tgetCellSize().y: 342.0px (130.28572dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" +
-                    "\ticonSizePx: 141.0px (53.714287dp)\n" +
-                    "\ticonTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                    "\tinv.numFolderRows: 3\n" +
-                    "\tinv.numFolderColumns: 4\n" +
-                    "\tfolderCellWidthPx: 189.0px (72.0dp)\n" +
-                    "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" +
-                    "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
-                    "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                    "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
-                    "\tbottomSheetOpenDuration: 500\n" +
-                    "\tbottomSheetCloseDuration: 500\n" +
-                    "\tbottomSheetWorkspaceScale: 0.97\n" +
-                    "\tbottomSheetDepth: 1.0\n" +
-                    "\tallAppsShiftRange: 1730.0px (659.0476dp)\n" +
-                    "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
-                    "\tallAppsOpenDuration: 500\n" +
-                    "\tallAppsCloseDuration: 500\n" +
-                    "\tallAppsIconSizePx: 141.0px (53.714287dp)\n" +
-                    "\tallAppsIconTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                    "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
-                    "\tallAppsCellWidthPx: 183.0px (69.71429dp)\n" +
-                    "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 8\n" +
-                    "\tallAppsLeftRightPadding: 42.0px (16.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 183.0px (69.71429dp)\n" +
-                    "\thotseatBarSizePx: 267.0px (101.71429dp)\n" +
-                    "\tinv.hotseatColumnSpan: 4\n" +
-                    "\thotseatCellHeightPx: 159.0px (60.57143dp)\n" +
-                    "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 113.0px (43.04762dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 113.0px (43.04762dp)\n" +
-                    "\tnumShownHotseatIcons: 6\n" +
-                    "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 0.0px (0.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:true\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.left: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.top: 30.0px (11.428572dp)\n" +
-                    "\tworkspacePadding.right: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" +
-                    "\ticonScale: 1.0px (0.3809524dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                    "\textraSpace: 498.0px (189.71428dp)\n" +
-                    "\tunscaled extraSpace: 498.0px (189.71428dp)\n" +
-                    "\tmaxEmptySpace: 0.0px (0.0dp)\n" +
-                    "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                    "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)\n" +
-                    "\tgetCellLayoutHeight(): 1370.0px (521.9048dp)\n" +
-                    "\tgetCellLayoutWidth(): 1083.0px (412.57144dp)\n"
-            )
+        assertDump(dp, "twoPanelLandscape3Button")
     }
 
     @Test
@@ -1284,136 +124,7 @@
         val dp = getDeviceProfileForGrid("4_by_4")
         dp.isTaskbarPresentInApps = true
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.625 px\n" +
-                    "\tisTablet:true\n" +
-                    "\tisPhone:false\n" +
-                    "\ttransposeLayoutWithOrientation:false\n" +
-                    "\tisGestureMode:true\n" +
-                    "\tisLandscape:true\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:true\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 2208.0px (841.1429dp)\n" +
-                    "\theightPx: 1840.0px (700.9524dp)\n" +
-                    "\tavailableWidthPx: 2208.0px (841.1429dp)\n" +
-                    "\tavailableHeightPx: 1730.0px (659.0476dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 110.0px (41.904762dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 0.0px (0.0dp)\n" +
-                    "\taspectRatio:1.2\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:false\n" +
-                    "\tinv.numRows: 4\n" +
-                    "\tinv.numColumns: 4\n" +
-                    "\tinv.numSearchContainerColumns: 4\n" +
-                    "\tminCellSize: PointF(0.0, 0.0)dp\n" +
-                    "\tcellWidthPx: 154.0px (58.666668dp)\n" +
-                    "\tcellHeightPx: 218.0px (83.04762dp)\n" +
-                    "\tgetCellSize().x: 270.0px (102.85714dp)\n" +
-                    "\tgetCellSize().y: 342.0px (130.28572dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" +
-                    "\ticonSizePx: 141.0px (53.714287dp)\n" +
-                    "\ticonTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                    "\tinv.numFolderRows: 3\n" +
-                    "\tinv.numFolderColumns: 4\n" +
-                    "\tfolderCellWidthPx: 189.0px (72.0dp)\n" +
-                    "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" +
-                    "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
-                    "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                    "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
-                    "\tbottomSheetOpenDuration: 500\n" +
-                    "\tbottomSheetCloseDuration: 500\n" +
-                    "\tbottomSheetWorkspaceScale: 0.97\n" +
-                    "\tbottomSheetDepth: 1.0\n" +
-                    "\tallAppsShiftRange: 1730.0px (659.0476dp)\n" +
-                    "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
-                    "\tallAppsOpenDuration: 500\n" +
-                    "\tallAppsCloseDuration: 500\n" +
-                    "\tallAppsIconSizePx: 141.0px (53.714287dp)\n" +
-                    "\tallAppsIconTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                    "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
-                    "\tallAppsCellWidthPx: 183.0px (69.71429dp)\n" +
-                    "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 8\n" +
-                    "\tallAppsLeftRightPadding: 42.0px (16.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 183.0px (69.71429dp)\n" +
-                    "\thotseatBarSizePx: 267.0px (101.71429dp)\n" +
-                    "\tinv.hotseatColumnSpan: 4\n" +
-                    "\thotseatCellHeightPx: 159.0px (60.57143dp)\n" +
-                    "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 113.0px (43.04762dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 113.0px (43.04762dp)\n" +
-                    "\tnumShownHotseatIcons: 6\n" +
-                    "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 0.0px (0.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:true\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.left: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.top: 30.0px (11.428572dp)\n" +
-                    "\tworkspacePadding.right: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" +
-                    "\ticonScale: 1.0px (0.3809524dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                    "\textraSpace: 498.0px (189.71428dp)\n" +
-                    "\tunscaled extraSpace: 498.0px (189.71428dp)\n" +
-                    "\tmaxEmptySpace: 0.0px (0.0dp)\n" +
-                    "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                    "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)\n" +
-                    "\tgetCellLayoutHeight(): 1370.0px (521.9048dp)\n" +
-                    "\tgetCellLayoutWidth(): 1083.0px (412.57144dp)\n"
-            )
+        assertDump(dp, "twoPanelLandscape")
     }
 
     @Test
@@ -1426,136 +137,7 @@
         val dp = getDeviceProfileForGrid("4_by_4")
         dp.isTaskbarPresentInApps = true
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.625 px\n" +
-                    "\tisTablet:true\n" +
-                    "\tisPhone:false\n" +
-                    "\ttransposeLayoutWithOrientation:false\n" +
-                    "\tisGestureMode:false\n" +
-                    "\tisLandscape:false\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:true\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 1840.0px (700.9524dp)\n" +
-                    "\theightPx: 2208.0px (841.1429dp)\n" +
-                    "\tavailableWidthPx: 1840.0px (700.9524dp)\n" +
-                    "\tavailableHeightPx: 2075.0px (790.4762dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 133.0px (50.666668dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 0.0px (0.0dp)\n" +
-                    "\taspectRatio:1.2\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:false\n" +
-                    "\tinv.numRows: 4\n" +
-                    "\tinv.numColumns: 4\n" +
-                    "\tinv.numSearchContainerColumns: 4\n" +
-                    "\tminCellSize: PointF(0.0, 0.0)dp\n" +
-                    "\tcellWidthPx: 154.0px (58.666668dp)\n" +
-                    "\tcellHeightPx: 218.0px (83.04762dp)\n" +
-                    "\tgetCellSize().x: 224.0px (85.333336dp)\n" +
-                    "\tgetCellSize().y: 430.0px (163.80952dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" +
-                    "\ticonSizePx: 141.0px (53.714287dp)\n" +
-                    "\ticonTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                    "\tinv.numFolderRows: 3\n" +
-                    "\tinv.numFolderColumns: 4\n" +
-                    "\tfolderCellWidthPx: 189.0px (72.0dp)\n" +
-                    "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" +
-                    "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
-                    "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                    "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 133.0px (50.666668dp)\n" +
-                    "\tbottomSheetOpenDuration: 500\n" +
-                    "\tbottomSheetCloseDuration: 500\n" +
-                    "\tbottomSheetWorkspaceScale: 0.97\n" +
-                    "\tbottomSheetDepth: 1.0\n" +
-                    "\tallAppsShiftRange: 1826.0px (695.619dp)\n" +
-                    "\tallAppsTopPadding: 382.0px (145.5238dp)\n" +
-                    "\tallAppsOpenDuration: 500\n" +
-                    "\tallAppsCloseDuration: 500\n" +
-                    "\tallAppsIconSizePx: 141.0px (53.714287dp)\n" +
-                    "\tallAppsIconTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                    "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
-                    "\tallAppsCellWidthPx: 183.0px (69.71429dp)\n" +
-                    "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 8\n" +
-                    "\tallAppsLeftRightPadding: 42.0px (16.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 1.0px (0.3809524dp)\n" +
-                    "\thotseatBarSizePx: 267.0px (101.71429dp)\n" +
-                    "\tinv.hotseatColumnSpan: 4\n" +
-                    "\thotseatCellHeightPx: 159.0px (60.57143dp)\n" +
-                    "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 98.0px (37.333332dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 98.0px (37.333332dp)\n" +
-                    "\tnumShownHotseatIcons: 6\n" +
-                    "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 0.0px (0.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:true\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.left: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.top: 24.0px (9.142858dp)\n" +
-                    "\tworkspacePadding.right: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" +
-                    "\ticonScale: 1.0px (0.3809524dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                    "\textraSpace: 849.0px (323.42856dp)\n" +
-                    "\tunscaled extraSpace: 849.0px (323.42856dp)\n" +
-                    "\tmaxEmptySpace: 0.0px (0.0dp)\n" +
-                    "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                    "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 168.0px (64.0dp)\n" +
-                    "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 490.0px (186.66667dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 1770.0px (674.2857dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.7437536px (0.2833347dp)\n" +
-                    "\tgetCellLayoutHeight(): 1721.0px (655.619dp)\n" +
-                    "\tgetCellLayoutWidth(): 899.0px (342.4762dp)\n"
-            )
+        assertDump(dp, "twoPanelPortrait3Button")
     }
 
     @Test
@@ -1564,147 +146,17 @@
         val dp = getDeviceProfileForGrid("4_by_4")
         dp.isTaskbarPresentInApps = true
 
-        assertThat(dump(dp))
-            .isEqualTo(
-                "DeviceProfile:\n" +
-                    "\t1 dp = 2.625 px\n" +
-                    "\tisTablet:true\n" +
-                    "\tisPhone:false\n" +
-                    "\ttransposeLayoutWithOrientation:false\n" +
-                    "\tisGestureMode:true\n" +
-                    "\tisLandscape:false\n" +
-                    "\tisMultiWindowMode:false\n" +
-                    "\tisTwoPanels:true\n" +
-                    "\twindowX: 0.0px (0.0dp)\n" +
-                    "\twindowY: 0.0px (0.0dp)\n" +
-                    "\twidthPx: 1840.0px (700.9524dp)\n" +
-                    "\theightPx: 2208.0px (841.1429dp)\n" +
-                    "\tavailableWidthPx: 1840.0px (700.9524dp)\n" +
-                    "\tavailableHeightPx: 2075.0px (790.4762dp)\n" +
-                    "\tmInsets.left: 0.0px (0.0dp)\n" +
-                    "\tmInsets.top: 133.0px (50.666668dp)\n" +
-                    "\tmInsets.right: 0.0px (0.0dp)\n" +
-                    "\tmInsets.bottom: 0.0px (0.0dp)\n" +
-                    "\taspectRatio:1.2\n" +
-                    "\tisResponsiveGrid:false\n" +
-                    "\tisScalableGrid:false\n" +
-                    "\tinv.numRows: 4\n" +
-                    "\tinv.numColumns: 4\n" +
-                    "\tinv.numSearchContainerColumns: 4\n" +
-                    "\tminCellSize: PointF(0.0, 0.0)dp\n" +
-                    "\tcellWidthPx: 154.0px (58.666668dp)\n" +
-                    "\tcellHeightPx: 218.0px (83.04762dp)\n" +
-                    "\tgetCellSize().x: 224.0px (85.333336dp)\n" +
-                    "\tgetCellSize().y: 430.0px (163.80952dp)\n" +
-                    "\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutBorderSpacePx Vertical: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.left: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.top: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.right: 0.0px (0.0dp)\n" +
-                    "\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" +
-                    "\ticonSizePx: 141.0px (53.714287dp)\n" +
-                    "\ticonTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                    "\tinv.numFolderRows: 3\n" +
-                    "\tinv.numFolderColumns: 4\n" +
-                    "\tfolderCellWidthPx: 189.0px (72.0dp)\n" +
-                    "\tfolderCellHeightPx: 219.0px (83.42857dp)\n" +
-                    "\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
-                    "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
-                    "\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
-                    "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                    "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                    "\tfolderFooterHeight: 147.0px (56.0dp)\n" +
-                    "\tbottomSheetTopPadding: 133.0px (50.666668dp)\n" +
-                    "\tbottomSheetOpenDuration: 500\n" +
-                    "\tbottomSheetCloseDuration: 500\n" +
-                    "\tbottomSheetWorkspaceScale: 0.97\n" +
-                    "\tbottomSheetDepth: 1.0\n" +
-                    "\tallAppsShiftRange: 1826.0px (695.619dp)\n" +
-                    "\tallAppsTopPadding: 382.0px (145.5238dp)\n" +
-                    "\tallAppsOpenDuration: 500\n" +
-                    "\tallAppsCloseDuration: 500\n" +
-                    "\tallAppsIconSizePx: 141.0px (53.714287dp)\n" +
-                    "\tallAppsIconTextSizePx: 34.0px (12.952381dp)\n" +
-                    "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                    "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
-                    "\tallAppsCellWidthPx: 183.0px (69.71429dp)\n" +
-                    "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                    "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                    "\tnumShownAllAppsColumns: 8\n" +
-                    "\tallAppsLeftRightPadding: 42.0px (16.0dp)\n" +
-                    "\tallAppsLeftRightMargin: 1.0px (0.3809524dp)\n" +
-                    "\thotseatBarSizePx: 267.0px (101.71429dp)\n" +
-                    "\tinv.hotseatColumnSpan: 4\n" +
-                    "\thotseatCellHeightPx: 159.0px (60.57143dp)\n" +
-                    "\thotseatBarBottomSpacePx: 126.0px (48.0dp)\n" +
-                    "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                    "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbSpace: 0.0px (0.0dp)\n" +
-                    "\thotseatQsbHeight: 0.0px (0.0dp)\n" +
-                    "\tspringLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).bottom: 108.0px (41.142857dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).left: 98.0px (37.333332dp)\n" +
-                    "\tgetHotseatLayoutPadding(context).right: 98.0px (37.333332dp)\n" +
-                    "\tnumShownHotseatIcons: 6\n" +
-                    "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                    "\tisQsbInline: false\n" +
-                    "\thotseatQsbWidth: 0.0px (0.0dp)\n" +
-                    "\tisTaskbarPresent:false\n" +
-                    "\tisTaskbarPresentInApps:true\n" +
-                    "\ttaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\tstashedTaskbarHeight: 0.0px (0.0dp)\n" +
-                    "\ttaskbarBottomMargin: 0.0px (0.0dp)\n" +
-                    "\ttaskbarIconSize: 0.0px (0.0dp)\n" +
-                    "\tdesiredWorkspaceHorizontalMarginPx: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.left: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.top: 24.0px (9.142858dp)\n" +
-                    "\tworkspacePadding.right: 21.0px (8.0dp)\n" +
-                    "\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" +
-                    "\ticonScale: 1.0px (0.3809524dp)\n" +
-                    "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                    "\textraSpace: 849.0px (323.42856dp)\n" +
-                    "\tunscaled extraSpace: 849.0px (323.42856dp)\n" +
-                    "\tmaxEmptySpace: 0.0px (0.0dp)\n" +
-                    "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                    "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizePx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                    "\toverviewTaskThumbnailTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsTopMarginPx: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsHeight: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsClaimedSpaceBelow: 0.0px (0.0dp)\n" +
-                    "\toverviewActionsButtonSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewPageSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewRowSpacing: 0.0px (0.0dp)\n" +
-                    "\toverviewGridSideMargin: 0.0px (0.0dp)\n" +
-                    "\tdropTargetBarTopMarginPx: 168.0px (64.0dp)\n" +
-                    "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
-                    "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkTop(): 490.0px (186.66667dp)\n" +
-                    "\tgetCellLayoutSpringLoadShrunkBottom(): 1770.0px (674.2857dp)\n" +
-                    "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                    "\tgetWorkspaceSpringLoadScale(): 0.7437536px (0.2833347dp)\n" +
-                    "\tgetCellLayoutHeight(): 1721.0px (655.619dp)\n" +
-                    "\tgetCellLayoutWidth(): 899.0px (342.4762dp)\n"
-            )
+        assertDump(dp, "twoPanelPortrait")
     }
 
     private fun getDeviceProfileForGrid(gridName: String): DeviceProfile {
         return InvariantDeviceProfile(context, gridName).getDeviceProfile(context)
     }
 
-    private fun dump(dp: DeviceProfile): String {
-        val stringWriter = StringWriter()
-        val printWriter = PrintWriter(stringWriter)
-        dp.dump(context, "", printWriter)
-        printWriter.flush()
-        return stringWriter.toString()
+    private fun assertDump(dp: DeviceProfile, filename: String) {
+        val dump = dump(context!!, dp, "${folderName}_$filename.txt")
+        val expected = readDumpFromAssets(testContext, "$folderName/$filename.txt")
+
+        assertThat(dump).isEqualTo(expected)
     }
 }
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 15f6177..a21c9b9 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -104,6 +104,7 @@
     @After
     public void teardown() {
         mModelHelper.destroy();
+        LauncherPrefs.get(mContext).removeSync(RESTORE_DEVICE);
     }
 
     @Test
diff --git a/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt
index 0ecf7ba..5865036 100644
--- a/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt
+++ b/tests/src/com/android/launcher3/responsive/CalculatedHotseatSpecTest.kt
@@ -50,6 +50,7 @@
 
         assertThat(heightSpec.availableSpace).isEqualTo(availableHeight)
         assertThat(heightSpec.hotseatQsbSpace).isEqualTo(95)
+        assertThat(heightSpec.edgePadding).isEqualTo(126)
     }
 
     /**
@@ -71,5 +72,27 @@
 
         assertThat(heightSpec.availableSpace).isEqualTo(availableHeight)
         assertThat(heightSpec.hotseatQsbSpace).isEqualTo(81)
+        assertThat(heightSpec.edgePadding).isEqualTo(162)
+    }
+
+    /**
+     * This test tests:
+     * - (width spec) gets the correct breakpoint from the XML - skips the first breakpoint
+     */
+    @Test
+    fun normalPhoneLandscape_returnsSecondBreakpointSpec() {
+        val deviceSpec = deviceSpecs["phone"]!!
+        initializeVarsForPhone(deviceSpec, isVerticalBar = true)
+
+        // Hotseat uses the whole device width
+        val availableWidth = deviceSpec.naturalSize.second
+
+        val hotseatSpecs =
+            HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_hotseat_land_file))
+        val widthSpec = hotseatSpecs.getCalculatedWidthSpec(availableWidth)
+
+        assertThat(widthSpec.availableSpace).isEqualTo(availableWidth)
+        assertThat(widthSpec.hotseatQsbSpace).isEqualTo(0)
+        assertThat(widthSpec.edgePadding).isEqualTo(168)
     }
 }
diff --git a/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt
index 0af694e..8f56c5f 100644
--- a/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt
+++ b/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt
@@ -104,4 +104,43 @@
         assertThat(heightSpec.gutterPx).isEqualTo(54)
         assertThat(heightSpec.cellSizePx).isEqualTo(260)
     }
+
+    /**
+     * This test tests:
+     * - (height spec) gets the correct breakpoint from the XML - use the first breakpoint
+     * - (height spec) do the correct calculations for remainder space and fixed size
+     * - (width spec) do the correct calculations for remainder space and fixed size
+     */
+    @Test
+    fun smallPhone_returnsFirstBreakpointSpec_unsortedFile() {
+        val deviceSpec = deviceSpecs["phone"]!!
+        deviceSpec.densityDpi = 540 // larger display size
+        initializeVarsForPhone(deviceSpec)
+
+        val availableWidth = deviceSpec.naturalSize.first
+        // Hotseat size is roughly 640px on a real device,
+        // it doesn't need to be precise on unit tests
+        val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 640
+
+        val workspaceSpecs =
+            WorkspaceSpecs.create(
+                TestResourceHelper(context!!, TestR.xml.valid_workspace_unsorted_file)
+            )
+        val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth)
+        val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight)
+
+        assertThat(widthSpec.availableSpace).isEqualTo(availableWidth)
+        assertThat(widthSpec.cells).isEqualTo(4)
+        assertThat(widthSpec.startPaddingPx).isEqualTo(74)
+        assertThat(widthSpec.endPaddingPx).isEqualTo(74)
+        assertThat(widthSpec.gutterPx).isEqualTo(54)
+        assertThat(widthSpec.cellSizePx).isEqualTo(193)
+
+        assertThat(heightSpec.availableSpace).isEqualTo(availableHeight)
+        assertThat(heightSpec.cells).isEqualTo(5)
+        assertThat(heightSpec.startPaddingPx).isEqualTo(0)
+        assertThat(heightSpec.endPaddingPx).isEqualTo(108)
+        assertThat(heightSpec.gutterPx).isEqualTo(54)
+        assertThat(heightSpec.cellSizePx).isEqualTo(260)
+    }
 }
diff --git a/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt b/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt
index c764e47..f650e91 100644
--- a/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt
+++ b/tests/src/com/android/launcher3/responsive/HotseatSpecsTest.kt
@@ -23,7 +23,6 @@
 import com.android.launcher3.AbstractDeviceProfileTest
 import com.android.launcher3.tests.R as TestR
 import com.android.launcher3.util.TestResourceHelper
-import com.android.systemui.util.dpToPx
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -43,25 +42,55 @@
     fun parseValidFile() {
         val hotseatSpecs =
             HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_hotseat_file))
-        assertThat(hotseatSpecs.specs.size).isEqualTo(2)
 
-        val expectedSpecs =
+        val expectedHeightSpecs =
             listOf(
                 HotseatSpec(
                     maxAvailableSize = 847.dpToPx(),
                     specType = ResponsiveSpec.SpecType.HEIGHT,
-                    hotseatQsbSpace = SizeSpec(24f.dpToPx())
+                    hotseatQsbSpace = SizeSpec(24f.dpToPx()),
+                    edgePadding = SizeSpec(48f.dpToPx())
                 ),
                 HotseatSpec(
                     maxAvailableSize = 9999.dpToPx(),
                     specType = ResponsiveSpec.SpecType.HEIGHT,
-                    hotseatQsbSpace = SizeSpec(36f.dpToPx())
+                    hotseatQsbSpace = SizeSpec(36f.dpToPx()),
+                    edgePadding = SizeSpec(48f.dpToPx())
                 ),
             )
 
-        assertThat(hotseatSpecs.specs.size).isEqualTo(expectedSpecs.size)
-        assertThat(hotseatSpecs.specs[0]).isEqualTo(expectedSpecs[0])
-        assertThat(hotseatSpecs.specs[1]).isEqualTo(expectedSpecs[1])
+        assertThat(hotseatSpecs.heightSpecs.size).isEqualTo(expectedHeightSpecs.size)
+        assertThat(hotseatSpecs.heightSpecs[0]).isEqualTo(expectedHeightSpecs[0])
+        assertThat(hotseatSpecs.heightSpecs[1]).isEqualTo(expectedHeightSpecs[1])
+
+        assertThat(hotseatSpecs.widthSpecs.size).isEqualTo(0)
+    }
+
+    @Test
+    fun parseValidLandscapeFile() {
+        val hotseatSpecs =
+            HotseatSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_hotseat_land_file))
+        assertThat(hotseatSpecs.heightSpecs.size).isEqualTo(0)
+
+        val expectedWidthSpecs =
+            listOf(
+                HotseatSpec(
+                    maxAvailableSize = 743.dpToPx(),
+                    specType = ResponsiveSpec.SpecType.WIDTH,
+                    hotseatQsbSpace = SizeSpec(0f),
+                    edgePadding = SizeSpec(48f.dpToPx())
+                ),
+                HotseatSpec(
+                    maxAvailableSize = 9999.dpToPx(),
+                    specType = ResponsiveSpec.SpecType.WIDTH,
+                    hotseatQsbSpace = SizeSpec(0f),
+                    edgePadding = SizeSpec(64f.dpToPx())
+                ),
+            )
+
+        assertThat(hotseatSpecs.widthSpecs.size).isEqualTo(expectedWidthSpecs.size)
+        assertThat(hotseatSpecs.widthSpecs[0]).isEqualTo(expectedWidthSpecs[0])
+        assertThat(hotseatSpecs.widthSpecs[1]).isEqualTo(expectedWidthSpecs[1])
     }
 
     @Test(expected = IllegalStateException::class)
diff --git a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java
index 7e9d9da..c7431f2 100644
--- a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java
+++ b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java
@@ -184,7 +184,7 @@
         mStartPoint = icon.getVisibleCenter();
         mEndPoint = new Point(mStartPoint.x, mStartPoint.y);
         mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint,
-                LauncherInstrumentation.GestureScope.INSIDE);
+                LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
         assertThat(findObjectByResourceName("popup_container")).isNotNull();
         return appName;
     }
@@ -206,7 +206,7 @@
         mStartPoint = icon.getVisibleCenter();
         mEndPoint = new Point(mStartPoint.x, mStartPoint.y);
         mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint,
-                LauncherInstrumentation.GestureScope.INSIDE);
+                LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
         assertThat(findObjectByResourceName("popup_container")).isNotNull();
         return appName;
     }
@@ -214,12 +214,12 @@
     private void moveAppToCenterOfScreen() {
         mEndPoint.set(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() / 2);
         mLauncher.movePointer(mDownTime, SystemClock.uptimeMillis(), DRAG_TIME_MS, true,
-                mStartPoint, mEndPoint, LauncherInstrumentation.GestureScope.INSIDE);
+                mStartPoint, mEndPoint, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
     }
 
     private void dropApp() {
         mLauncher.sendPointer(mDownTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP,
-                mEndPoint, LauncherInstrumentation.GestureScope.INSIDE);
+                mEndPoint, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
     }
 
     private void removeAppByName(String appName) {
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 5240e6a..f734fe5 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -16,11 +16,9 @@
 package com.android.launcher3.ui;
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
 import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
 import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -68,6 +66,7 @@
 import com.android.launcher3.util.rule.SamplerRule;
 import com.android.launcher3.util.rule.ScreenRecordRule;
 import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.util.rule.TestIsolationRule;
 import com.android.launcher3.util.rule.TestStabilityRule;
 import com.android.launcher3.util.rule.ViewCaptureRule;
 
@@ -123,6 +122,10 @@
                 }, DEFAULT_UI_TIMEOUT, launcher);
     }
 
+    public static String getAppPackageName() {
+        return getInstrumentation().getContext().getPackageName();
+    }
+
     private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher) {
         sActivityLeakReported = true;
         return "Activity leak detector has found leaked activities, "
@@ -206,7 +209,8 @@
         final RuleChain inner = RuleChain
                 .outerRule(new PortraitLandscapeRunner(this))
                 .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData))
-                .around(viewCaptureRule);
+                .around(viewCaptureRule)
+                .around(new TestIsolationRule(mLauncher));
 
         return TestHelpers.isInLauncherProcess()
                 ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index f0875f8..ad11268 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -1,9 +1,12 @@
 package com.android.launcher3.ui;
 
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
+
 import android.util.Log;
 import android.view.Surface;
 
 import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.util.rule.TestStabilityRule;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -30,8 +33,12 @@
 
     @Override
     public Statement apply(Statement base, Description description) {
-        if (!TestHelpers.isInLauncherProcess() ||
-                description.getAnnotation(PortraitLandscape.class) == null) {
+        if (!TestHelpers.isInLauncherProcess()
+                || description.getAnnotation(PortraitLandscape.class) == null
+                // If running in presubmit, don't run in both orientations.
+                // It's important to keep presubmits fast even if we will occasionally miss
+                // regressions in presubmit.
+                || TestStabilityRule.getRunFlavor() == PLATFORM_PRESUBMIT) {
             return base;
         }
 
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index c462f59..8f2ac98 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -16,26 +16,17 @@
 
 package com.android.launcher3.ui;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
 import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
 import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
 
 import android.content.Intent;
-import android.graphics.Point;
-import android.os.SystemClock;
 import android.platform.test.annotations.PlatinumTest;
-import android.util.Log;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
@@ -50,17 +41,13 @@
 import com.android.launcher3.tapl.AppIcon;
 import com.android.launcher3.tapl.AppIconMenu;
 import com.android.launcher3.tapl.AppIconMenuItem;
-import com.android.launcher3.tapl.Folder;
-import com.android.launcher3.tapl.FolderIcon;
 import com.android.launcher3.tapl.HomeAllApps;
 import com.android.launcher3.tapl.HomeAppIcon;
-import com.android.launcher3.tapl.HomeAppIconMenuItem;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.TestUtil;
-import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.TISBindRule;
 import com.android.launcher3.util.rule.TestStabilityRule.Stability;
@@ -69,22 +56,18 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.IOException;
-import java.util.Map;
-
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
-    private static final String APP_NAME = "LauncherTestApp";
-    private static final String DUMMY_APP_NAME = "Aardwolf";
-    private static final String MAPS_APP_NAME = "Maps";
-    private static final String STORE_APP_NAME = "Play Store";
-    private static final String GMAIL_APP_NAME = "Gmail";
+    public static final String APP_NAME = "LauncherTestApp";
+    public static final String DUMMY_APP_NAME = "Aardwolf";
+    public static final String MAPS_APP_NAME = "Maps";
+    public static final String STORE_APP_NAME = "Play Store";
+    public static final String GMAIL_APP_NAME = "Gmail";
     private static final String READ_DEVICE_CONFIG_PERMISSION =
             "android.permission.READ_DEVICE_CONFIG";
 
@@ -157,107 +140,6 @@
         mLauncher.goHome();
     }
 
-    @Test
-    public void testPressHomeOnAllAppsContextMenu() throws Exception {
-        final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
-        allApps.freeze();
-        try {
-            allApps.getAppIcon("TestActivity7").openMenu();
-        } finally {
-            allApps.unfreeze();
-        }
-        mLauncher.goHome();
-    }
-
-    public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
-        allApps.freeze();
-        try {
-            assertNotNull("allApps parameter is null", allApps);
-
-            assertTrue(
-                    "Launcher internal state is not All Apps",
-                    test.isInState(() -> LauncherState.ALL_APPS));
-
-            // Test flinging forward and backward.
-            test.executeOnLauncher(launcher -> assertEquals(
-                    "All Apps started in already scrolled state", 0,
-                    test.getAllAppsScroll(launcher)));
-
-            allApps.flingForward();
-            assertTrue("Launcher internal state is not All Apps",
-                    test.isInState(() -> LauncherState.ALL_APPS));
-            final Integer flingForwardY = test.getFromLauncher(
-                    launcher -> test.getAllAppsScroll(launcher));
-            test.executeOnLauncher(
-                    launcher -> assertTrue("flingForward() didn't scroll App Apps",
-                            flingForwardY > 0));
-
-            allApps.flingBackward();
-            assertTrue(
-                    "Launcher internal state is not All Apps",
-                    test.isInState(() -> LauncherState.ALL_APPS));
-            final Integer flingBackwardY = test.getFromLauncher(
-                    launcher -> test.getAllAppsScroll(launcher));
-            test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
-                    flingBackwardY < flingForwardY));
-
-            // Test scrolling down to YouTube.
-            assertNotNull("All apps: can't find YouTube", allApps.getAppIcon("YouTube"));
-            // Test scrolling up to Camera.
-            assertNotNull("All apps: can't find Camera", allApps.getAppIcon("Camera"));
-            // Test failing to find a non-existing app.
-            final AllApps allAppsFinal = allApps;
-            expectFail("All apps: could find a non-existing app",
-                    () -> allAppsFinal.getAppIcon("NO APP"));
-
-            assertTrue(
-                    "Launcher internal state is not All Apps",
-                    test.isInState(() -> LauncherState.ALL_APPS));
-        } finally {
-            allApps.unfreeze();
-        }
-    }
-
-    @Test
-    @PortraitLandscape
-    public void testWorkspaceSwitchToAllApps() {
-        assertNotNull("switchToAllApps() returned null",
-                mLauncher.getWorkspace().switchToAllApps());
-        assertTrue("Launcher internal state is not All Apps",
-                isInState(() -> LauncherState.ALL_APPS));
-    }
-
-    @Test
-    @PortraitLandscape
-    public void testAllAppsSwitchToWorkspace() {
-        assertNotNull("switchToWorkspace() returned null",
-                mLauncher.getWorkspace().switchToAllApps()
-                        .switchToWorkspace(/* swipeDown= */ true));
-        assertTrue("Launcher internal state is not Workspace",
-                isInState(() -> LauncherState.NORMAL));
-    }
-
-    @Test
-    @PortraitLandscape
-    public void testAllAppsSwipeUpToWorkspace() {
-        assertNotNull("testAllAppsSwipeUpToWorkspace() returned null",
-                mLauncher.getWorkspace().switchToAllApps()
-                        .switchToWorkspace(/* swipeDown= */ false));
-        assertTrue("Launcher internal state is not Workspace",
-                isInState(() -> LauncherState.NORMAL));
-    }
-
-    @Test
-    @PortraitLandscape
-    public void testAllAppsDeadzoneForTablet() throws Exception {
-        assumeTrue(mLauncher.isTablet());
-
-        mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
-                true /* tapRight */);
-        mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
-                false /* tapRight */);
-    }
-
     @PlatinumTest(focusArea = "launcher")
     @Test
     public void testWorkspace() throws Exception {
@@ -414,89 +296,6 @@
             allApps.unfreeze();
         }
     }
-
-    @PlatinumTest(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.
-        // 3. Verify that the icon works on homescreen.
-        final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
-        allApps.freeze();
-        try {
-            allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
-            mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
-        } finally {
-            allApps.unfreeze();
-        }
-        executeOnLauncher(launcher -> assertTrue(
-                "Launcher activity is the top activity; expecting another activity to be the top "
-                        + "one",
-                isInLaunchedApp(launcher)));
-    }
-
-    @Test
-    @PortraitLandscape
-    @PlatinumTest(focusArea = "launcher")
-    public void testDragShortcut() throws Throwable {
-        // 1. Open all apps and wait for load complete.
-        // 2. Find the app and long press it to show shortcuts.
-        // 3. Press icon center until shortcuts appear
-        final HomeAllApps allApps = mLauncher
-                .getWorkspace()
-                .switchToAllApps();
-        allApps.freeze();
-        try {
-            final HomeAppIconMenuItem menuItem = allApps
-                    .getAppIcon(APP_NAME)
-                    .openDeepShortcutMenu()
-                    .getMenuItem(0);
-            final String actualShortcutName = menuItem.getText();
-            final String expectedShortcutName = "Shortcut 1";
-
-            assertEquals(expectedShortcutName, actualShortcutName);
-            menuItem.dragToWorkspace(false, false);
-            mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName)
-                    .launch(getAppPackageName());
-        } finally {
-            allApps.unfreeze();
-        }
-    }
-
-    @Test
-    @PortraitLandscape
-    @ScreenRecord
-    @Ignore // b/233075289
-    @PlatinumTest(focusArea = "launcher")
-    public void testDragToFolder() {
-        // TODO: add the use case to drag an icon to an existing folder. Currently it either fails
-        // on tablets or phones due to difference in resolution.
-        final HomeAppIcon playStoreIcon = createShortcutIfNotExist(STORE_APP_NAME, 0, 1);
-        final HomeAppIcon gmailIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
-
-        FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon);
-        Folder folder = folderIcon.open();
-        folder.getAppIcon(STORE_APP_NAME);
-        folder.getAppIcon(GMAIL_APP_NAME);
-        Workspace workspace = folder.close();
-
-        workspace.verifyWorkspaceAppIconIsGone(STORE_APP_NAME + " should be moved to a folder.",
-                STORE_APP_NAME);
-        workspace.verifyWorkspaceAppIconIsGone(GMAIL_APP_NAME + " should be moved to a folder.",
-                GMAIL_APP_NAME);
-
-        final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME);
-        folderIcon = mapIcon.dragToIcon(folderIcon);
-        folder = folderIcon.open();
-        folder.getAppIcon(MAPS_APP_NAME);
-        workspace = folder.close();
-
-        workspace.verifyWorkspaceAppIconIsGone(MAPS_APP_NAME + " should be moved to a folder.",
-                MAPS_APP_NAME);
-    }
-
     @FlakyTest(bugId = 256615483)
     @Test
     @PortraitLandscape
@@ -516,169 +315,6 @@
 
     @Test
     @PortraitLandscape
-    @PlatinumTest(focusArea = "launcher")
-    public void testDragAndCancelAppIcon() {
-        final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
-        Point positionBeforeDrag =
-                mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
-        assertNotNull("App not found in Workspace before dragging.", positionBeforeDrag);
-
-        mLauncher.getWorkspace().dragAndCancelAppIcon(homeAppIcon);
-
-        Point positionAfterDrag =
-                mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
-        assertNotNull("App not found in Workspace after dragging.", positionAfterDrag);
-        assertEquals("App not returned to same position in Workspace after drag & cancel",
-                positionBeforeDrag, positionAfterDrag);
-    }
-
-    @Test
-    @PortraitLandscape
-    public void testDeleteFromWorkspace() throws Exception {
-        // test delete both built-in apps and user-installed app from workspace
-        for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) {
-            final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName);
-            Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon);
-            workspace.verifyWorkspaceAppIconIsGone(
-                    appName + " app was found after being deleted from workspace",
-                    appName);
-        }
-    }
-
-    private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) {
-        final HomeAllApps allApps = workspace.switchToAllApps();
-        Wait.atMost(appName + " app was found on all apps after being uninstalled",
-                () -> allApps.tryGetAppIcon(appName) == null,
-                DEFAULT_UI_TIMEOUT, mLauncher);
-    }
-
-    @Test
-    @PortraitLandscape
-    // TODO(b/293944634): Remove Screenrecord after flaky debug, and add
-    // @PlatinumTest(focusArea = "launcher") back
-    @ScreenRecord
-    public void testUninstallFromWorkspace() throws Exception {
-        installDummyAppAndWaitForUIUpdate();
-        try {
-            verifyAppUninstalledFromAllApps(
-                    createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
-        } finally {
-            TestUtil.uninstallDummyApp();
-        }
-    }
-
-    @Test
-    @PortraitLandscape
-    @PlatinumTest(focusArea = "launcher")
-    public void testUninstallFromAllApps() throws Exception {
-        installDummyAppAndWaitForUIUpdate();
-        try {
-            Workspace workspace = mLauncher.getWorkspace();
-            final HomeAllApps allApps = workspace.switchToAllApps();
-            workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall();
-            verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME);
-        } finally {
-            TestUtil.uninstallDummyApp();
-        }
-    }
-
-    @Test
-    @PortraitLandscape
-    @PlatinumTest(focusArea = "launcher")
-    public void testDragAppIconToWorkspaceCell() throws Exception {
-        long startTime, endTime, elapsedTime;
-        Point[] targets = getCornersAndCenterPositions();
-
-        for (Point target : targets) {
-            startTime = SystemClock.uptimeMillis();
-            final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
-            allApps.freeze();
-            try {
-                allApps.getAppIcon(APP_NAME).dragToWorkspace(target.x, target.y);
-            } finally {
-                allApps.unfreeze();
-            }
-            // Reset the workspace for the next shortcut creation.
-            initialize(this, true);
-            endTime = SystemClock.uptimeMillis();
-            elapsedTime = endTime - startTime;
-            Log.d("testDragAppIconToWorkspaceCellTime",
-                    "Milliseconds taken to drag app icon to workspace cell: " + elapsedTime);
-        }
-
-        // test to move a shortcut to other cell.
-        final HomeAppIcon launcherTestAppIcon = createShortcutInCenterIfNotExist(APP_NAME);
-        for (Point target : targets) {
-            startTime = SystemClock.uptimeMillis();
-            launcherTestAppIcon.dragToWorkspace(target.x, target.y);
-            endTime = SystemClock.uptimeMillis();
-            elapsedTime = endTime - startTime;
-            Log.d("testDragAppIconToWorkspaceCellTime",
-                    "Milliseconds taken to move shortcut to other cell: " + elapsedTime);
-        }
-    }
-
-    /**
-     * Adds three icons to the workspace and removes one of them by dragging to uninstall.
-     */
-    @Test
-    @ScreenRecord // b/241821721
-    @PlatinumTest(focusArea = "launcher")
-    public void uninstallWorkspaceIcon() throws IOException {
-        Point[] gridPositions = getCornersAndCenterPositions();
-        StringBuilder sb = new StringBuilder();
-        for (Point p : gridPositions) {
-            sb.append(p).append(", ");
-        }
-        Log.d(ICON_MISSING, "allGridPositions: " + sb);
-        createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]);
-        createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]);
-        installDummyAppAndWaitForUIUpdate();
-        try {
-            createShortcutIfNotExist(DUMMY_APP_NAME, gridPositions[2]);
-            Map<String, Point> initialPositions =
-                    mLauncher.getWorkspace().getWorkspaceIconsPositions();
-            assertThat(initialPositions.keySet())
-                    .containsAtLeast(DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME);
-
-            mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall();
-            mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone(
-                    DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME);
-
-            // Debug for b/288944469 I want to test if we are not waiting enough after removing
-            // the icon to request the list of icons again, since the items are not removed
-            // immediately. This should reduce the flake rate
-            SystemClock.sleep(500);
-            Map<String, Point> finalPositions =
-                    mLauncher.getWorkspace().getWorkspaceIconsPositions();
-            assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME);
-        } finally {
-            TestUtil.uninstallDummyApp();
-        }
-    }
-
-    @Test
-    @PortraitLandscape
-    @PlatinumTest(focusArea = "launcher")
-    public void testDragShortcutToWorkspaceCell() throws Exception {
-        Point[] targets = getCornersAndCenterPositions();
-
-        for (Point target : targets) {
-            final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
-            allApps.freeze();
-            try {
-                allApps.getAppIcon(APP_NAME)
-                        .openDeepShortcutMenu()
-                        .getMenuItem(0)
-                        .dragToWorkspace(target.x, target.y);
-            } finally {
-                allApps.unfreeze();
-            }
-        }
-    }
-
-    @Test
-    @PortraitLandscape
     public void testAddDeleteShortcutOnHotseat() {
         mLauncher.getWorkspace()
                 .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
@@ -689,40 +325,6 @@
                 mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME));
     }
 
-    private void installDummyAppAndWaitForUIUpdate() throws IOException {
-        TestUtil.installDummyApp();
-        waitForLauncherUIUpdate();
-    }
-
-    private void waitForLauncherUIUpdate() {
-        // Wait for model thread completion as it may be processing
-        // the install event from the SystemService
-        mLauncher.waitForModelQueueCleared();
-        // Wait for Launcher UI thread completion, as it may be processing updating the UI in
-        // response to the model update. Not that `waitForLauncherInitialized` is just a proxy
-        // method, we can use any method which touches Launcher UI thread,
-        mLauncher.waitForLauncherInitialized();
-    }
-
-    /**
-     * @return List of workspace grid coordinates. Those are not pixels. See {@link
-     * Workspace#getIconGridDimensions()}
-     */
-    private Point[] getCornersAndCenterPositions() {
-        final Point dimensions = mLauncher.getWorkspace().getIconGridDimensions();
-        return new Point[]{
-                new Point(0, 1),
-                new Point(0, dimensions.y - 2),
-                new Point(dimensions.x - 1, 1),
-                new Point(dimensions.x - 1, dimensions.y - 2),
-                new Point(dimensions.x / 2, dimensions.y / 2)
-        };
-    }
-
-    public static String getAppPackageName() {
-        return getInstrumentation().getContext().getPackageName();
-    }
-
     @Test
     public void testGetAppIconName() {
         HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
@@ -734,4 +336,28 @@
             allApps.unfreeze();
         }
     }
+
+    @PlatinumTest(focusArea = "launcher")
+    @Test
+    public void testAddAndDeletePageAndFling() {
+        Workspace workspace = mLauncher.getWorkspace();
+        // Get the first app from the hotseat
+        HomeAppIcon hotSeatIcon = workspace.getHotseatAppIcon(0);
+        String appName = hotSeatIcon.getIconName();
+
+        // Add one page by dragging app to page 1.
+        workspace.dragIcon(hotSeatIcon, workspace.pagesPerScreen());
+        assertEquals("Incorrect Page count Number",
+                workspace.pagesPerScreen() * 2,
+                workspace.getPageCount());
+
+        // Delete one page by dragging app to hot seat.
+        workspace.getWorkspaceAppIcon(appName).dragToHotseat(0);
+
+        // Refresh workspace to avoid using stale container error.
+        workspace = mLauncher.getWorkspace();
+        assertEquals("Incorrect Page count Number",
+                workspace.pagesPerScreen(),
+                workspace.getPageCount());
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 5b9adcd..ac710fd 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -19,8 +19,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
 import static com.android.launcher3.util.TestUtil.installDummyAppForUser;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -39,7 +37,6 @@
 import com.android.launcher3.allapps.WorkProfileManager;
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.util.TestUtil;
-import com.android.launcher3.util.rule.TestStabilityRule.Stability;
 
 import org.junit.After;
 import org.junit.Before;
@@ -103,8 +100,6 @@
     }
 
     private void waitForWorkTabSetup() {
-        // Added for b/243688989 flake to determine if we really are in allApps or not at this point
-        mLauncher.getAllApps();
         waitForLauncherCondition("Work tab not setup", launcher -> {
             if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) {
                 launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
@@ -115,7 +110,6 @@
     }
 
     @Test
-    @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/243688989
     public void workTabExists() {
         assumeTrue(mWorkProfileSetupSuccessful);
         waitForWorkTabSetup();
@@ -176,7 +170,6 @@
     }
 
     @Test
-    @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/243688989
     public void testEdu() {
         assumeTrue(mWorkProfileSetupSuccessful);
         waitForWorkTabSetup();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 9dca24b..64ab206 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.ui.widget;
 
 import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
-
 import static org.junit.Assert.assertNotNull;
 
 import android.platform.test.annotations.PlatinumTest;
@@ -96,4 +95,26 @@
         mLauncher.getWorkspace().getWorkspaceAppIcon("Shortcut")
                 .launch(getAppPackageName());
     }
+
+    /**
+     * Test dragging a widget to the workspace and resize it.
+     */
+    @Test
+    public void testResizeWidget() throws Throwable {
+        new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+
+        waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
+
+        final LauncherAppWidgetProviderInfo widgetInfo =
+                TestViewHelpers.findWidgetProvider(false /* hasConfigureScreen */);
+
+        WidgetResizeFrame resizeFrame = mLauncher
+                .getWorkspace()
+                .openAllWidgets()
+                .getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager()))
+                .dragWidgetToWorkspace();
+
+        assertNotNull("Widget resize frame not shown after widget add", resizeFrame);
+        resizeFrame.resize();
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index a6b5369..5c753f9 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -48,7 +48,6 @@
 import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.Wait.Condition;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.ShellCommandRule;
 
 import org.junit.Before;
@@ -85,7 +84,6 @@
     public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
 
     @Test
-    @ScreenRecord // b/215673732
     public void testPinWidgetNoConfig() throws Throwable {
         runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
                 ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
@@ -94,7 +92,6 @@
     }
 
     @Test
-    @ScreenRecord // b/215673732
     public void testPinWidgetNoConfig_customPreview() throws Throwable {
         // Command to set custom preview
         Intent command = RequestPinItemActivity.getCommandIntent(
@@ -108,7 +105,6 @@
     }
 
     @Test
-    @ScreenRecord // b/215673732
     public void testPinWidgetWithConfig() throws Throwable {
         runTest("pinWidgetWithConfig", true,
                 (info, view) -> info instanceof LauncherAppWidgetInfo &&
diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
index 0b2f335..62a8179 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
@@ -178,14 +178,15 @@
     public void testDragIconToPage3() {
         Workspace workspace = mLauncher.getWorkspace();
 
-        workspace.dragIcon(workspace.getHotseatAppIcon("Phone"), 3);
+        // b/299522368 sometimes the phone app is not present in the hotseat.
+        workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 3);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1, 2, 3);
             assertItemsOnPage(launcher, 0, "Play Store", "Maps");
             assertPageEmpty(launcher, 1);
             assertPageEmpty(launcher, 2);
-            assertItemsOnPage(launcher, 3, "Phone");
+            assertItemsOnPage(launcher, 3, "Chrome");
         });
     }
 
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index 21059e6..957bf95 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.content.pm.LauncherApps;
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
@@ -46,6 +47,8 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.FeatureFlags.BooleanFlag;
 import com.android.launcher3.config.FeatureFlags.IntFlag;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.Workspace;
 
 import org.junit.Assert;
 
@@ -125,6 +128,21 @@
     }
 
     /**
+     * @return Grid coordinates from the center and corners of the Workspace. Those are not pixels.
+     * See {@link Workspace#getIconGridDimensions()}
+     */
+    public static Point[] getCornersAndCenterPositions(LauncherInstrumentation launcher) {
+        final Point dimensions = launcher.getWorkspace().getIconGridDimensions();
+        return new Point[]{
+                new Point(0, 1),
+                new Point(0, dimensions.y - 2),
+                new Point(dimensions.x - 1, 1),
+                new Point(dimensions.x - 1, dimensions.y - 2),
+                new Point(dimensions.x / 2, dimensions.y / 2)
+        };
+    }
+
+    /**
      * Utility class to override a boolean flag during test. Note that the returned SafeCloseable
      * must be closed to restore the original state
      */
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index f2ae9d3..62d70ad 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -103,7 +103,8 @@
 
             if (viewCaptureDataSupplier != null) {
                 out.putNextEntry(new ZipEntry("FS/data/misc/wmtrace/failed_test.vc"));
-                viewCaptureDataSupplier.get().writeTo(out);
+                final ExportedData exportedData = viewCaptureDataSupplier.get();
+                if (exportedData != null) exportedData.writeTo(out);
                 out.closeEntry();
             }
         } catch (Exception ignored) {
diff --git a/tests/src/com/android/launcher3/util/rule/TestIsolationRule.java b/tests/src/com/android/launcher3/util/rule/TestIsolationRule.java
new file mode 100644
index 0000000..592cc9b
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/TestIsolationRule.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util.rule;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Isolates tests from some of the state created by the previous test.
+ */
+public class TestIsolationRule implements TestRule {
+    final LauncherInstrumentation mLauncher;
+
+    public TestIsolationRule(LauncherInstrumentation launcher) {
+        mLauncher = launcher;
+    }
+
+    @NonNull
+    @Override
+    public Statement apply(@NonNull Statement base, @NonNull Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                base.evaluate();
+                // Make sure that Launcher workspace looks correct.
+                mLauncher.goHome();
+                AbstractLauncherUiTest.checkDetectedLeaks(mLauncher);
+            }
+        };
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
index 0f08eef..ccbae4f 100644
--- a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
+++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
@@ -20,6 +20,7 @@
 import android.media.permission.SafeCloseable
 import android.os.Bundle
 import androidx.test.core.app.ApplicationProvider
+import androidx.test.platform.app.InstrumentationRegistry
 import com.android.app.viewcapture.SimpleViewCapture
 import com.android.app.viewcapture.ViewCapture.MAIN_EXECUTOR
 import com.android.app.viewcapture.data.ExportedData
@@ -50,6 +51,13 @@
         private set
 
     override fun apply(base: Statement, description: Description): Statement {
+        // Skip view capture collection in Launcher3 tests to avoid hidden API check exception.
+        if (
+            "com.android.launcher3.tests" ==
+                InstrumentationRegistry.getInstrumentation().context.packageName
+        )
+            return base
+
         return object : Statement() {
             override fun evaluate() {
                 viewCaptureData = null
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index 10afe13..425c3c0 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -45,8 +45,8 @@
             mLauncher.clickObject(
                     mLauncher.waitForObjectInContainer(
                             mWidgetCell.getParent().getParent().getParent().getParent(),
-                            By.text(ADD_AUTOMATICALLY)),
-                    LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER);
+                            By.text(ADD_AUTOMATICALLY))
+            );
             mLauncher.waitUntilLauncherObjectGone(getSelector());
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index fb08ea4..44875d5 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -48,6 +48,8 @@
 
     private static final int MAX_SCROLL_ATTEMPTS = 40;
 
+    private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
+
     private final int mHeight;
     private final int mIconHeight;
 
@@ -338,6 +340,28 @@
     }
 
     /**
+     * Taps outside bottom sheet to dismiss it. Available on tablets only.
+     * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
+     */
+    public void dismissByTappingOutsideForTablet(boolean tapRight) {
+        mLauncher.assertTrue("Device must be a tablet", mLauncher.isTablet());
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "want to tap outside AllApps bottom sheet on the "
+                             + (tapRight ? "right" : "left"))) {
+            final UiObject2 allAppsBottomSheet =
+                    mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
+            mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
+            try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
+                    "tapped outside AllApps bottom sheet")) {
+                verifyVisibleContainerOnDismiss();
+            }
+        }
+    }
+
+    protected abstract void verifyVisibleContainerOnDismiss();
+
+    /**
      * Return the QSB UI object on the AllApps screen.
      * @return the QSB UI object.
      */
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index ebcca00..677f204 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -66,10 +66,6 @@
     }
 
 
-    protected boolean zeroButtonToOverviewGestureStartsInLauncher() {
-        return mLauncher.isTablet();
-    }
-
     protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
         return false;
     }
@@ -146,12 +142,9 @@
         final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
         final int startY = getSwipeStartY();
         final Point start = new Point(centerX, startY);
-        final LauncherInstrumentation.GestureScope gestureScope =
-                zeroButtonToOverviewGestureStartsInLauncher()
-                        ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
-                        : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
 
-        mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
+        mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start,
+                LauncherInstrumentation.GestureScope.EXPECT_PILFER);
 
         if (!mLauncher.isLauncher3()) {
             mLauncher.expectEvent(TestProtocol.SEQUENCE_PILFER,
@@ -167,10 +160,6 @@
         final Point start = new Point(centerX, startY);
         final Point end =
                 new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
-        final LauncherInstrumentation.GestureScope gestureScope =
-                zeroButtonToOverviewGestureStartsInLauncher()
-                        ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
-                        : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
 
         mLauncher.movePointer(
                 downTime,
@@ -178,7 +167,7 @@
                 ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
                 start,
                 end,
-                gestureScope);
+                LauncherInstrumentation.GestureScope.EXPECT_PILFER);
     }
 
     private void sendUpPointerToEnterOverviewToLauncher(long downTime) {
@@ -189,13 +178,9 @@
         final Point end =
                 new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
 
-        final LauncherInstrumentation.GestureScope gestureScope =
-                zeroButtonToOverviewGestureStartsInLauncher()
-                        ? LauncherInstrumentation.GestureScope.INSIDE
-                        : LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER;
-
         mLauncher.sendPointer(downTime, SystemClock.uptimeMillis(),
-                MotionEvent.ACTION_UP, end, gestureScope);
+                MotionEvent.ACTION_UP, end,
+                LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
     }
 
     /**
@@ -227,7 +212,6 @@
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "want to quick switch to the previous app")) {
             verifyActiveContainer();
-            final boolean launcherWasVisible = mLauncher.isLauncherVisible();
             if (mLauncher.getNavigationModel() == NavigationModel.ZERO_BUTTON
                     || mLauncher.getTrackpadGestureType() == TrackpadGestureType.FOUR_FINGER) {
                 final int startX;
@@ -249,13 +233,10 @@
                     endY = startY;
                 }
 
-                LauncherInstrumentation.GestureScope gestureScope =
-                        launcherWasVisible
-                                ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
-                                : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
                 mLauncher.executeAndWaitForEvent(
                         () -> mLauncher.linearGesture(
-                                startX, startY, endX, endY, 20, false, gestureScope),
+                                startX, startY, endX, endY, 20, false,
+                                LauncherInstrumentation.GestureScope.EXPECT_PILFER),
                         event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
                         () -> "Quick switch gesture didn't change window state", "swiping");
             } else {
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index ee9dd1a..252435b 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -59,11 +59,6 @@
     }
 
     @Override
-    protected boolean zeroButtonToOverviewGestureStartsInLauncher() {
-        return true;
-    }
-
-    @Override
     protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
         return true;
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 33c6334..8542f91 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -23,7 +23,6 @@
 import com.android.launcher3.testing.shared.TestProtocol;
 
 public class HomeAllApps extends AllApps {
-    private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
 
     HomeAllApps(LauncherInstrumentation launcher) {
         super(launcher);
@@ -60,8 +59,8 @@
                     endY,
                     5 /* steps */,
                     NORMAL_STATE_ORDINAL,
-                    swipeDown ? LauncherInstrumentation.GestureScope.INSIDE
-                            : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
+                    swipeDown ? LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER
+                            : LauncherInstrumentation.GestureScope.EXPECT_PILFER);
 
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                     "swiped to workspace")) {
@@ -98,25 +97,6 @@
                 .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
-    /**
-     * Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
-     * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
-     */
-    public Workspace dismissByTappingOutsideForTablet(boolean tapRight) {
-        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
-             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
-                     "want to tap outside AllApps bottom sheet on the "
-                             + (tapRight ? "right" : "left"))) {
-            final UiObject2 allAppsBottomSheet =
-                    mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
-            mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
-            try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
-                    "tapped outside AllApps bottom sheet")) {
-                return mLauncher.getWorkspace();
-            }
-        }
-    }
-
     @NonNull
     @Override
     public Qsb getQsb() {
@@ -128,4 +108,9 @@
         return mLauncher.getTestInfo(TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
                 .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
+
+    @Override
+    protected void verifyVisibleContainerOnDismiss() {
+        mLauncher.getWorkspace();
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 48e327f..3450ea7 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -16,10 +16,14 @@
 
 package com.android.launcher3.tapl;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
 import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
 
+import android.app.UiAutomation;
 import android.graphics.Point;
 import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
 
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
@@ -83,12 +87,18 @@
      * fired when the click is executed.
      */
     public LaunchedAppState launchIntoSplitScreen() {
-        try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                "want to launch split tasks from " + launchableType())) {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                     "want to launch split tasks from " + launchableType())) {
             LauncherInstrumentation.log("Launchable.launch before click "
-                    + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
-
-            mLauncher.clickLauncherObject(mObject);
+                    + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(
+                    mObject));
+            mLauncher.executeAndWaitForLauncherEvent(
+                    () -> mLauncher.clickLauncherObject(mObject),
+                    accessibilityEvent ->
+                            accessibilityEvent.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                    () -> "Unable to click object to launch split",
+                    "Click launcher object to launch split");
 
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
                 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, OverviewTask.SPLIT_START_EVENT);
@@ -152,7 +162,7 @@
                 downTime,
                 MotionEvent.ACTION_DOWN,
                 iconCenter,
-                LauncherInstrumentation.GestureScope.INSIDE);
+                LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
         LauncherInstrumentation.log("movePointerForStartDrag: sent down");
         expectLongClickEvents.run();
         waitForLongPressConfirmation();
@@ -165,7 +175,7 @@
                 downTime,
                 downTime,
                 /* slowDown= */ true,
-                LauncherInstrumentation.GestureScope.INSIDE);
+                LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
     }
 
     private int getStartDragThreshold() {
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 230be06..30417c0 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -128,13 +128,13 @@
                     mLauncher.getRealDisplaySize().x / 2, unstashTargetY);
 
             mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, unstashTarget,
-                    LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
+                    LauncherInstrumentation.GestureScope.EXPECT_PILFER);
             LauncherInstrumentation.log("showTaskbar: sent down");
 
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
                 mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
                 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
-                        LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
+                        LauncherInstrumentation.GestureScope.EXPECT_PILFER);
 
                 return new Taskbar(mLauncher);
             }
@@ -183,7 +183,7 @@
                         downTime,
                         SystemClock.uptimeMillis(),
                         /* slowDown= */ false,
-                        LauncherInstrumentation.GestureScope.INSIDE);
+                        LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
 
                 try (LauncherInstrumentation.Closable c3 = launcher.addContextLayer(
                         "moved pointer to drop point")) {
@@ -194,7 +194,7 @@
                             SystemClock.uptimeMillis(),
                             MotionEvent.ACTION_UP,
                             endPoint,
-                            LauncherInstrumentation.GestureScope.INSIDE);
+                            LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
                     LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: "
                             + "after drop");
 
@@ -326,7 +326,7 @@
                         null, InputDevice.SOURCE_MOUSE);
                 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
                         new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y),
-                        LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER,
+                        LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER,
                         InputDevice.SOURCE_MOUSE);
                 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_BUTTON_PRESS,
                         new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y),
@@ -336,7 +336,7 @@
                         null, InputDevice.SOURCE_MOUSE);
                 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP,
                         new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y),
-                        LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER,
+                        LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER,
                         InputDevice.SOURCE_MOUSE);
 
                 return mLauncher.getWorkspace();
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index e6fc244..71ca77f 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -21,7 +21,6 @@
 import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 import static android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT;
-
 import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID;
 import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -29,12 +28,14 @@
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
+import android.app.UiModeManager;
 import android.content.ComponentName;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.Point;
@@ -102,12 +103,6 @@
     static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
     static final Pattern EVENT_START = Pattern.compile("start:");
 
-    static final Pattern EVENT_HOVER_ENTER_TIS = getTouchEventPatternTIS("ACTION_HOVER_ENTER");
-    static final Pattern EVENT_HOVER_EXIT_TIS = getTouchEventPatternTIS("ACTION_HOVER_EXIT");
-    static final Pattern EVENT_BUTTON_PRESS_TIS = getTouchEventPatternTIS("ACTION_BUTTON_PRESS");
-    static final Pattern EVENT_BUTTON_RELEASE_TIS =
-            getTouchEventPatternTIS("ACTION_BUTTON_RELEASE");
-
     private static final Pattern EVENT_KEY_BACK_DOWN =
             getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
     private static final Pattern EVENT_KEY_BACK_UP =
@@ -127,12 +122,10 @@
 
     public enum NavigationModel {ZERO_BUTTON, THREE_BUTTON}
 
-    // Where the gesture happens: outside of Launcher, inside or from inside to outside and
-    // whether the gesture recognition triggers pilfer.
+    // Defines whether the gesture recognition triggers pilfer.
     public enum GestureScope {
-        OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE,
-        INSIDE_TO_OUTSIDE_WITH_KEYCODE, // For gestures that will trigger a keycode from TIS.
-        OUTSIDE_WITH_KEYCODE,
+        DONT_EXPECT_PILFER,
+        EXPECT_PILFER,
     }
 
     public enum TrackpadGestureType {
@@ -181,6 +174,8 @@
     static final long DEFAULT_POLL_INTERVAL = 1000;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
     private static final String ANDROID_PACKAGE = "android";
+    private static final String ASSISTANT_PACKAGE = "com.google.android.googlequicksearchbox";
+    private static final String ASSISTANT_GO_HOME_RES_ID = "home_icon";
 
     private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
 
@@ -204,28 +199,6 @@
     private TrackpadGestureType mTrackpadGestureType = TrackpadGestureType.NONE;
     private int mPointerCount = 0;
 
-    private static Pattern getTouchEventPatternWithPointerCount(String prefix, String action,
-            int pointerCount) {
-        return Pattern.compile(
-                prefix + ": MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
-                        + ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount="
-                        + pointerCount);
-    }
-
-    private static Pattern getTouchEventPatternWithPointerCount(String action, int pointerCount) {
-        return getTouchEventPatternWithPointerCount("Touch event", action, pointerCount);
-    }
-
-    private static Pattern getTouchEventPatternTIS(String action) {
-        return getTouchEventPatternWithPointerCount("TouchInteractionService.onInputEvent", action,
-                1);
-    }
-
-    private static Pattern getTouchEventPatternTIS(String action, int pointerCount) {
-        return getTouchEventPatternWithPointerCount("TouchInteractionService.onInputEvent", action,
-                pointerCount);
-    }
-
     private static Pattern getKeyEventPattern(String action, String keyCode) {
         return Pattern.compile("Key event: KeyEvent.*action=" + action + ".*keyCode=" + keyCode);
     }
@@ -368,6 +341,11 @@
                 .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
+    Insets getImeInsets() {
+        return getTestInfo(TestProtocol.REQUEST_IME_INSETS)
+                .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
     public boolean isTablet() {
         return getTestInfo(TestProtocol.REQUEST_IS_TABLET)
                 .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -743,6 +721,10 @@
         mExpectedRotationCheckEnabled = expectedRotationCheckEnabled;
     }
 
+    public boolean getExpectedRotationCheckEnabled() {
+        return mExpectedRotationCheckEnabled;
+    }
+
     public String getNavigationModeMismatchError(boolean waitForCorrectState) {
         final int waitTime = waitForCorrectState ? WAIT_TIME_MS : 0;
         final NavigationModel navigationModel = getNavigationModel();
@@ -973,8 +955,8 @@
         GestureScope gestureScope = gestureStartFromLauncher
                 // Without the navigation bar layer, the gesture scope on tablets remains inside the
                 // launcher process.
-                ? (isTablet() ? GestureScope.INSIDE : GestureScope.INSIDE_TO_OUTSIDE)
-                : GestureScope.OUTSIDE_WITH_PILFER;
+                ? (isTablet() ? GestureScope.DONT_EXPECT_PILFER : GestureScope.EXPECT_PILFER)
+                : GestureScope.EXPECT_PILFER;
         linearGesture(
                 displaySize.x / 2, displaySize.y - 1,
                 displaySize.x / 2, 0,
@@ -1046,8 +1028,7 @@
                             displaySize.x / 2, startY,
                             displaySize.x / 2, endY,
                             ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, NORMAL_STATE_ORDINAL,
-                            gestureStartFromLauncher ? GestureScope.INSIDE_TO_OUTSIDE
-                                    : GestureScope.OUTSIDE_WITH_PILFER);
+                            GestureScope.EXPECT_PILFER);
                 }
             } else {
                 log("Hierarchy before clicking home:");
@@ -1055,7 +1036,7 @@
                 action = "clicking home button";
 
                 runToState(
-                        waitForNavigationUiObject("home")::click,
+                        getHomeButton()::click,
                         NORMAL_STATE_ORDINAL,
                         !hasLauncherObject(WORKSPACE_RES_ID)
                                 && (hasLauncherObject(APPS_RES_ID)
@@ -1082,15 +1063,12 @@
             if (getNavigationModel() == NavigationModel.ZERO_BUTTON
                     || isThreeFingerTrackpadGesture) {
                 final Point displaySize = getRealDisplaySize();
-                final GestureScope gestureScope =
-                        launcherVisible ? GestureScope.INSIDE_TO_OUTSIDE_WITH_KEYCODE
-                                : GestureScope.OUTSIDE_WITH_KEYCODE;
                 // TODO(b/225505986): change startY and endY back to displaySize.y / 2 once the
                 //  issue is solved.
                 int startX = isThreeFingerTrackpadGesture ? displaySize.x / 4 : 0;
                 int endX = isThreeFingerTrackpadGesture ? displaySize.x * 3 / 4 : displaySize.x / 2;
                 linearGesture(startX, displaySize.y / 4, endX, displaySize.y / 4,
-                        10, false, gestureScope);
+                        10, false, GestureScope.DONT_EXPECT_PILFER);
             } else {
                 waitForNavigationUiObject("back").click();
             }
@@ -1239,6 +1217,28 @@
     }
 
     @NonNull
+    private UiObject2 getHomeButton() {
+        UiModeManager uiManager =
+                (UiModeManager) getContext().getSystemService(Context.UI_MODE_SERVICE);
+        if (uiManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
+            return waitForAssistantHomeButton();
+        } else {
+            return waitForNavigationUiObject("home");
+        }
+    }
+
+    /* Assistant Home button is present when system is in car mode. */
+    @NonNull
+    UiObject2 waitForAssistantHomeButton() {
+        final UiObject2 object = mDevice.wait(
+                Until.findObject(By.res(ASSISTANT_PACKAGE, ASSISTANT_GO_HOME_RES_ID)),
+                WAIT_TIME_MS);
+        assertNotNull(
+                "Can't find an assistant UI object with id: " + ASSISTANT_GO_HOME_RES_ID, object);
+        return object;
+    }
+
+    @NonNull
     UiObject2 waitForNavigationUiObject(String resId) {
         String resPackage = getNavigationButtonResPackage();
         final UiObject2 object = mDevice.wait(
@@ -1500,15 +1500,17 @@
      * animations because in some scenarios there is a playing animations when the click is
      * attempted.
      */
-    void clickObject(UiObject2 uiObject, GestureScope gestureScope) {
+    void clickObject(UiObject2 uiObject) {
         final long clickTime = SystemClock.uptimeMillis();
         final Point center = uiObject.getVisibleCenter();
-        sendPointer(clickTime, clickTime, MotionEvent.ACTION_DOWN, center, gestureScope);
-        sendPointer(clickTime, clickTime, MotionEvent.ACTION_UP, center, gestureScope);
+        sendPointer(clickTime, clickTime, MotionEvent.ACTION_DOWN, center,
+                GestureScope.DONT_EXPECT_PILFER);
+        sendPointer(clickTime, clickTime, MotionEvent.ACTION_UP, center,
+                GestureScope.DONT_EXPECT_PILFER);
     }
 
     void clickLauncherObject(UiObject2 object) {
-        clickObject(object, GestureScope.INSIDE);
+        clickObject(object);
     }
 
     void scrollToLastVisibleRow(
@@ -1601,7 +1603,8 @@
 
         executeAndWaitForLauncherEvent(
                 () -> linearGesture(
-                        startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE),
+                        startX, startY, endX, endY, steps, slowDown,
+                        GestureScope.DONT_EXPECT_PILFER),
                 event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
                 () -> "Didn't receive a scroll end message: " + startX + ", " + startY
                         + ", " + endX + ", " + endY,
@@ -1721,11 +1724,6 @@
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
-    boolean isTrackpadGestureEnabled() {
-        return getTestInfo(TestProtocol.REQUEST_IS_TRACKPAD_GESTURE_ENABLED).getBoolean(
-                TestProtocol.TEST_INFO_RESPONSE_FIELD);
-    }
-
     boolean isGridOnlyOverviewEnabled() {
         return getTestInfo(TestProtocol.REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW).getBoolean(
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -1743,7 +1741,6 @@
         int pointerCount = mPointerCount;
 
         boolean isTrackpadGesture = mTrackpadGestureType != TrackpadGestureType.NONE;
-        boolean isTwoFingerTrackpadGesture = mTrackpadGestureType == TrackpadGestureType.TWO_FINGER;
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN:
                 if (isTrackpadGesture) {
@@ -1752,59 +1749,18 @@
                 }
                 break;
             case MotionEvent.ACTION_UP:
-                if (hasTIS
-                        && (gestureScope == GestureScope.OUTSIDE_WITH_PILFER
-                        || gestureScope == GestureScope.INSIDE_TO_OUTSIDE)) {
+                if (hasTIS && gestureScope == GestureScope.EXPECT_PILFER) {
                     expectEvent(TestProtocol.SEQUENCE_PILFER, EVENT_PILFER_POINTERS);
                 }
                 break;
-            case MotionEvent.ACTION_HOVER_ENTER:
-                expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_HOVER_ENTER_TIS);
-                break;
-            case MotionEvent.ACTION_HOVER_EXIT:
-                expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_HOVER_EXIT_TIS);
-                break;
             case MotionEvent.ACTION_POINTER_DOWN:
                 mPointerCount++;
-                if (gestureScope != GestureScope.OUTSIDE_WITH_PILFER
-                        && gestureScope != GestureScope.OUTSIDE_WITHOUT_PILFER
-                        && gestureScope != GestureScope.OUTSIDE_WITH_KEYCODE
-                        && (!isTrackpadGesture || isTwoFingerTrackpadGesture)) {
-                    expectEvent(TestProtocol.SEQUENCE_MAIN, getTouchEventPatternWithPointerCount(
-                            "ACTION_POINTER_DOWN", mPointerCount));
-                }
-                if (hasTIS && (isTrackpadGestureEnabled()
-                        || getNavigationModel() != NavigationModel.THREE_BUTTON)) {
-                    expectEvent(TestProtocol.SEQUENCE_TIS, getTouchEventPatternTIS(
-                            "ACTION_POINTER_DOWN", mPointerCount));
-                }
                 pointerCount = mPointerCount;
                 break;
             case MotionEvent.ACTION_POINTER_UP:
-                if (gestureScope != GestureScope.OUTSIDE_WITH_PILFER
-                        && gestureScope != GestureScope.OUTSIDE_WITHOUT_PILFER
-                        && gestureScope != GestureScope.OUTSIDE_WITH_KEYCODE
-                        && (!isTrackpadGesture || isTwoFingerTrackpadGesture)) {
-                    expectEvent(TestProtocol.SEQUENCE_MAIN, getTouchEventPatternWithPointerCount(
-                            "ACTION_POINTER_UP", mPointerCount));
-                }
                 // When the gesture is handled outside, it's cancelled within launcher.
-                if (hasTIS && (isTrackpadGestureEnabled()
-                        || getNavigationModel() != NavigationModel.THREE_BUTTON)) {
-                    if (gestureScope != GestureScope.INSIDE_TO_OUTSIDE_WITH_KEYCODE
-                            && gestureScope != GestureScope.OUTSIDE_WITH_KEYCODE) {
-                        expectEvent(TestProtocol.SEQUENCE_TIS, getTouchEventPatternTIS(
-                                "ACTION_POINTER_UP", mPointerCount));
-                    }
-                }
                 mPointerCount--;
                 break;
-            case MotionEvent.ACTION_BUTTON_PRESS:
-                expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_BUTTON_PRESS_TIS);
-                break;
-            case MotionEvent.ACTION_BUTTON_RELEASE:
-                expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_BUTTON_RELEASE_TIS);
-                break;
         }
 
         final MotionEvent event = isTrackpadGesture
@@ -1883,11 +1839,12 @@
             @NonNull final UiObject2 target, @NonNull String resName, Pattern longClickEvent) {
         final Point targetCenter = target.getVisibleCenter();
         final long downTime = SystemClock.uptimeMillis();
-        sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter, GestureScope.INSIDE);
+        sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter,
+                GestureScope.DONT_EXPECT_PILFER);
         expectEvent(TestProtocol.SEQUENCE_MAIN, longClickEvent);
         final UiObject2 result = waitForLauncherObject(resName);
         sendPointer(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, targetCenter,
-                GestureScope.INSIDE);
+                GestureScope.DONT_EXPECT_PILFER);
         return result;
     }
 
@@ -2163,14 +2120,18 @@
                         ? containerBounds.right + 1
                         : containerBounds.left - 1;
             }
-            int y = containerBounds.top + containerBounds.height() / 2;
+            // If IME is visible and overlaps the container bounds, touch above it.
+            int bottomBound = Math.min(
+                    containerBounds.bottom,
+                    getRealDisplaySize().y - getImeInsets().bottom);
+            int y = (bottomBound - containerBounds.top) / 2;
 
             final long downTime = SystemClock.uptimeMillis();
             final Point tapTarget = new Point(x, y);
             sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, tapTarget,
-                    LauncherInstrumentation.GestureScope.INSIDE);
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
             sendPointer(downTime, downTime, MotionEvent.ACTION_UP, tapTarget,
-                    LauncherInstrumentation.GestureScope.INSIDE);
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
         }
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 39b93b4..e4cfc52 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -124,7 +124,7 @@
         final int centerY = taskBounds.centerY();
         mLauncher.executeAndWaitForLauncherEvent(
                 () -> mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false,
-                        LauncherInstrumentation.GestureScope.INSIDE),
+                        LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER),
                 event -> TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE.equals(event.getClassName()),
                 () -> "Didn't receive a dismiss animation ends message: " + centerX + ", "
                         + centerY, "swiping to dismiss");
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
index 7c29a6c..859e504 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiObject2;
@@ -50,15 +52,23 @@
         }
     }
 
-    /** Taps the app info item from the overview task menu and returns the LaunchedAppState
-     * representing the App info settings page. */
+    /**
+     * Taps the app info item from the overview task menu and returns the LaunchedAppState
+     * representing the App info settings page.
+     */
     @NonNull
     public LaunchedAppState tapAppInfoMenuItem() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "before tapping the app info menu item")) {
-            mLauncher.clickLauncherObject(
-                    mLauncher.findObjectInContainer(mMenu, By.text("App info")));
+
+            mLauncher.executeAndWaitForLauncherEvent(
+                    () -> mLauncher.clickLauncherObject(
+                            mLauncher.findObjectInContainer(mMenu, By.text("App info"))),
+                    accessibilityEvent ->
+                            accessibilityEvent.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                    () -> "Unable to start Settings by clicking App Info",
+                    "Tap the app info menu item");
 
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                     "tapped app info menu item")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Qsb.java b/tests/tapl/com/android/launcher3/tapl/Qsb.java
index 7f3f61d..0f2aff8 100644
--- a/tests/tapl/com/android/launcher3/tapl/Qsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/Qsb.java
@@ -29,6 +29,8 @@
 
     private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
     private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
+    private static final String LENS_ICON_RES_ID = "lens_icon";
+    private static final String LENS_APP_TEXT_RES_ID = "lens_camera_cutout_text";
     protected final LauncherInstrumentation mLauncher;
     private final UiObject2 mContainer;
     private final String mQsbResName;
@@ -76,6 +78,37 @@
     }
 
     /**
+     * Launches lens app by tapping lens icon on qsb.
+     */
+    @NonNull
+    public LaunchedAppState launchLens() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to click lens icon button");
+             LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            UiObject2 lensIcon = mLauncher.waitForLauncherObject(LENS_ICON_RES_ID);
+
+            LauncherInstrumentation.log("Qsb.launchLens before click "
+                    + lensIcon.getVisibleCenter() + " in "
+                    + mLauncher.getVisibleBounds(lensIcon));
+
+            mLauncher.clickLauncherObject(lensIcon);
+
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+                // Package name is not enough to check if the app is launched, because many
+                // elements are having googlequicksearchbox as package name. So it checks if the
+                // corresponding text resource is displayed
+                BySelector selector = By.res(ASSISTANT_APP_PACKAGE, LENS_APP_TEXT_RES_ID);
+                mLauncher.assertTrue(
+                        "Lens app didn't start: (" + selector + ")",
+                        mLauncher.getDevice().wait(Until.hasObject(selector),
+                                LauncherInstrumentation.WAIT_TIME_MS)
+                );
+                return new LaunchedAppState(mLauncher);
+            }
+        }
+    }
+
+    /**
      * Show search result page from tapping qsb.
      */
     public SearchResultFromQsb showSearchResult() {
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index d02e747..513d6bb 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -20,6 +20,8 @@
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiObject2;
 
+import com.android.launcher3.testing.shared.TestProtocol;
+
 import java.util.ArrayList;
 
 /**
@@ -43,8 +45,11 @@
     public void searchForInput(String input) {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to search for result with an input");
-             LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-            mLauncher.waitForLauncherObject(INPUT_RES).setText(input);
+            LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            mLauncher.executeAndWaitForLauncherEvent(
+                    () -> mLauncher.waitForLauncherObject(INPUT_RES).setText(input),
+                    event -> TestProtocol.SEARCH_RESULT_COMPLETE.equals(event.getClassName()),
+                    () -> "Didn't receive a search result completed message", "searching");
         }
     }
 
@@ -91,7 +96,7 @@
      * Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
      * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
      */
-    public Workspace dismissByTappingOutsideForTablet(boolean tapRight) {
+    public void dismissByTappingOutsideForTablet(boolean tapRight) {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "want to tap outside AllApps bottom sheet on the "
@@ -101,8 +106,12 @@
             mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
             try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
                     "tapped outside AllApps bottom sheet")) {
-                return mLauncher.getWorkspace();
+                verifyVisibleContainerOnDismiss();
             }
         }
     }
+
+    protected void verifyVisibleContainerOnDismiss() {
+        mLauncher.getWorkspace();
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
index 6c6ab05..00291a3 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
@@ -45,4 +45,9 @@
     protected TaskbarSearchWebSuggestion createWebSuggestion(UiObject2 webSuggestion) {
         return new TaskbarSearchWebSuggestion(mLauncher, webSuggestion);
     }
+
+    @Override
+    protected void verifyVisibleContainerOnDismiss() {
+        mLauncher.getLaunchedAppState().assertTaskbarVisible();
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index 051630e..4293ee8 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -76,13 +76,13 @@
                     mLauncher.getRealDisplaySize().x - 1, mLauncher.getRealDisplaySize().y - 1);
 
             mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, stashTarget,
-                    LauncherInstrumentation.GestureScope.INSIDE);
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
             LauncherInstrumentation.log("hideTaskbar: sent down");
 
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
                 mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
                 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget,
-                        LauncherInstrumentation.GestureScope.INSIDE);
+                        LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
             }
         } finally {
             mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
@@ -92,7 +92,7 @@
     /**
      * Opens the Taskbar all apps page.
      */
-    public AllAppsFromTaskbar openAllApps() {
+    public TaskbarAllApps openAllApps() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to open taskbar all apps");
              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
@@ -101,7 +101,15 @@
                     mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID),
                     getAllAppsButtonSelector()));
 
-            return new AllAppsFromTaskbar(mLauncher);
+            return getAllApps();
+        }
+    }
+
+    /** Returns {@link TaskbarAllApps} if it is open, otherwise fails. */
+    public TaskbarAllApps getAllApps() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get taskbar all apps object")) {
+            return new TaskbarAllApps(mLauncher);
         }
     }
 
@@ -147,9 +155,9 @@
                     mLauncher.getRealDisplaySize().y - 1);
 
             mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, tapTarget,
-                    LauncherInstrumentation.GestureScope.INSIDE);
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
             mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, tapTarget,
-                    LauncherInstrumentation.GestureScope.INSIDE);
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
         }
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java
similarity index 89%
rename from tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
rename to tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java
index 0e0291f..c1234fe 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java
@@ -23,9 +23,9 @@
 /**
  * Operations on AllApps opened from the Taskbar.
  */
-public class AllAppsFromTaskbar extends AllApps {
+public class TaskbarAllApps extends AllApps {
 
-    AllAppsFromTaskbar(LauncherInstrumentation launcher) {
+    TaskbarAllApps(LauncherInstrumentation launcher) {
         super(launcher);
     }
 
@@ -68,4 +68,9 @@
     public TaskbarAllAppsQsb getQsb() {
         return new TaskbarAllAppsQsb(mLauncher, verifyActiveContainer());
     }
+
+    @Override
+    protected void verifyVisibleContainerOnDismiss() {
+        mLauncher.getLaunchedAppState().assertTaskbarVisible();
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java b/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
index 8f51d04..ec1cbd8 100644
--- a/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
+++ b/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
@@ -15,6 +15,16 @@
  */
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.tapl.Launchable.DEFAULT_DRAG_STEPS;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
+import androidx.test.uiautomator.UiObject2;
+
 /** The resize frame that is shown for a widget on the workspace. */
 public class WidgetResizeFrame {
 
@@ -34,4 +44,36 @@
             mLauncher.getDevice().pressHome();
         }
     }
+
+    /** Resizes the widget to double its height, and returns the resize frame. */
+    public WidgetResizeFrame resize() {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "want to resize the widget frame.")) {
+            UiObject2 widget = mLauncher.waitForLauncherObject("widget_resize_frame");
+            UiObject2 bottomResizeHandle =
+                    mLauncher.waitForLauncherObject("widget_resize_bottom_handle");
+            Rect originalWidgetSize = widget.getVisibleBounds();
+            Point targetStart = bottomResizeHandle.getVisibleCenter();
+            Point targetDest = bottomResizeHandle.getVisibleCenter();
+            targetDest.offset(0, originalWidgetSize.height());
+
+            final long downTime = SystemClock.uptimeMillis();
+            mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetStart,
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+            mLauncher.movePointer(targetStart, targetDest, DEFAULT_DRAG_STEPS,
+                    true, downTime, downTime, true,
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+            mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, targetDest,
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+                         "want to return resized widget resize frame")) {
+                float newHeight = mLauncher.waitForLauncherObject(
+                        "widget_resize_frame").getVisibleBounds().height();
+                assertTrue("Widget not resized.", newHeight >= originalWidgetSize.height() * 2);
+                return this;
+            }
+        }
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 8604988..cf48ebc 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -27,6 +27,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.SystemClock;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
@@ -104,7 +105,8 @@
                     windowCornerRadius,
                     startY - swipeHeight - mLauncher.getTouchSlop(),
                     12,
-                    ALL_APPS_STATE_ORDINAL, LauncherInstrumentation.GestureScope.INSIDE);
+                    ALL_APPS_STATE_ORDINAL,
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
 
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                     "swiped to all apps")) {
@@ -285,6 +287,10 @@
         final UiObject2 workspace = verifyActiveContainer();
         List<UiObject2> workspaceIcons =
                 mLauncher.waitForObjectsInContainer(workspace, AppIcon.getAnyAppIconSelector());
+        Log.d("b/288944469", "List size = " + workspaceIcons.size());
+        for (int i = 0; i < workspaceIcons.size(); i++) {
+            Log.d("b/288944469", "index = " + i + " tesxt = " + workspaceIcons.get(i).getText());
+        }
         return workspaceIcons.stream()
                 .collect(
                         Collectors.toMap(
@@ -386,7 +392,7 @@
                     Until.hasObject(installerAlert), LauncherInstrumentation.WAIT_TIME_MS));
             final UiObject2 ok = device.findObject(By.text("OK"));
             assertNotNull("OK button is not shown", ok);
-            launcher.clickObject(ok, LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER);
+            launcher.clickObject(ok);
             assertTrue("Uninstall alert is not dismissed after clicking OK", device.wait(
                     Until.gone(installerAlert), LauncherInstrumentation.WAIT_TIME_MS));
 
@@ -453,7 +459,7 @@
         launcher.runToState(
                 () -> launcher.sendPointer(
                         downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest,
-                        LauncherInstrumentation.GestureScope.INSIDE),
+                        LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER),
                 NORMAL_STATE_ORDINAL,
                 "sending UP event");
         if (expectedEvents != null) {
@@ -542,7 +548,7 @@
                 executeAndWaitForPageScroll(launcher,
                         () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS,
                                 true, downTime, downTime, true,
-                                LauncherInstrumentation.GestureScope.INSIDE));
+                                LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER));
                 targetDest.x += displayX * (targetDest.x > 0 ? -1 : 1);
                 dragStart = screenEdge;
             }
@@ -551,7 +557,7 @@
             // we just have to put move the icon to the destination and drop it
             launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
                     downTime, SystemClock.uptimeMillis(), false,
-                    LauncherInstrumentation.GestureScope.INSIDE);
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
             dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
         }
     }
@@ -584,7 +590,7 @@
             // we just have to put move the icon to the destination and drop it
             launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
                     downTime, SystemClock.uptimeMillis(), false,
-                    LauncherInstrumentation.GestureScope.INSIDE);
+                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
             dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
         }
     }
@@ -621,7 +627,7 @@
             executeAndWaitForPageScroll(launcher,
                     () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS,
                             true, downTime, downTime, true,
-                            LauncherInstrumentation.GestureScope.INSIDE));
+                            LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER));
             currentPage = Workspace.geCurrentPage(launcher);
             currentPosition = screenEdge;
         }
@@ -650,7 +656,7 @@
 
         launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true,
                 downTime, SystemClock.uptimeMillis(), false,
-                LauncherInstrumentation.GestureScope.INSIDE);
+                LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
         dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
     }