Merge "Add unit tests for LoaderTask" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index aca1b3b..8fddc0f 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -13,3 +13,10 @@
     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"
+}
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/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/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/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..eb15cef 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
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..ba2116d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -23,6 +23,7 @@
 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;
@@ -45,6 +46,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 +67,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 +108,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;
 
@@ -175,8 +182,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 +268,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 +304,7 @@
             return;
         }
 
-        mTaskbarActivityContext.toggleAllApps();
+        mTaskbarActivityContext.toggleAllAppsSearch();
     }
 
     /**
@@ -308,6 +325,7 @@
         mUserUnlocked = true;
         LauncherAppState.getIDP(mContext).addOnChangeListener(mIdpChangeListener);
         recreateTaskbar();
+        addTaskbarRootViewToWindow();
     }
 
     /**
@@ -388,10 +406,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 +419,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 +547,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/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 1c250bf..3a733bf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -18,6 +18,7 @@
 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 android.animation.ObjectAnimator;
 import android.view.animation.Interpolator;
@@ -35,8 +36,6 @@
 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);
 
@@ -78,8 +77,9 @@
         final float scrimAlpha = 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;
+                ? (BUBBLE_EXPANDED_SCRIM_ALPHA + (BUBBLE_EXPANDED_SCRIM_ALPHA
+                * (1 - BUBBLE_EXPANDED_SCRIM_ALPHA)))
+                : showScrim ? BUBBLE_EXPANDED_SCRIM_ALPHA : 0;
         showScrim(showScrim, scrimAlpha, skipAnim);
     }
 
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..ee4db73 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -33,6 +33,7 @@
 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.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..0e5ab71 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -18,6 +18,7 @@
 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_ALL_APPS_SEARCH_IN_TASKBAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
@@ -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,30 @@
         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
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -336,7 +359,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..54840f1 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
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index d786d94..e004acc 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;
         }
@@ -166,7 +179,11 @@
             cleanUpOverlay();
         });
         TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
-                mOverlayContext, mSlideInView, mControllers, mSearchSessionController);
+                mOverlayContext,
+                mSlideInView,
+                mControllers,
+                mSearchSessionController,
+                showKeyboard);
 
         viewController.show(animate);
         mAppsView = mOverlayContext.getAppsView();
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 537d2c6..001c3bc 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);
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 85633e9..3a6d613 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();
@@ -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..9126c4b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -62,12 +62,30 @@
         @Override
         public void onTaskCreated(int taskId, ComponentName componentName) {
             // Created task will be below existing overlay, so move out of the way.
-            hideWindow();
+            hideWindowOnTaskStackChange();
         }
 
         @Override
         public void onTaskMovedToFront(int taskId) {
             // New front task will be below existing overlay, so move out of the way.
+            hideWindowOnTaskStackChange();
+        }
+
+        @Override
+        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 +217,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/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index b50ab97..1756117 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -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);
@@ -1272,6 +1278,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 +1327,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/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/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 99dd634..5da41ae 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -31,6 +31,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;
@@ -731,6 +732,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
     //
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/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index c1680de..c615d5f 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;
@@ -449,6 +452,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 +490,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 +664,8 @@
 
         mTaskbarManager.destroy();
         sConnected = false;
+
+        ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener);
         super.onDestroy();
     }
 
@@ -666,6 +675,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 +749,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 +1243,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 +1295,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 +1313,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
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/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/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/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/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/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..b9ff272 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -343,8 +343,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 +352,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();
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/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index eb7598d..437009f 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -16,7 +16,6 @@
 package com.android.quickstep.views;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
 import static com.android.launcher3.LauncherState.EDIT_MODE;
@@ -136,6 +135,10 @@
     @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,6 +165,11 @@
             runActionOnRemoteHandles(remoteTargetHandle ->
                     remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
         }
+
+        if (!finalState.overviewUi) {
+            // If overview is disabled, we want to update at the end
+            updateOverviewStateForDesktop(false);
+        }
     }
 
     @Override
@@ -273,4 +281,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..1bd385e 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;
 
@@ -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..23b5d30 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()).
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/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/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/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/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/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/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 55b8fcc..ec874b9 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -1,12 +1,28 @@
 package com.android.launcher3;
 
+import static android.os.Process.myUserHandle;
+
+import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.IntArray;
 import com.android.launcher3.widget.LauncherWidgetHolder;
 
 public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -31,4 +47,131 @@
             }
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * Updates the app widgets whose id has changed during the restore process.
+     */
+    @WorkerThread
+    public static void restoreAppWidgetIds(Context context, ModelDbController controller,
+            int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
+        if (WidgetsModel.GO_DISABLE_WIDGETS) {
+            Log.e(TAG, "Skipping widget ID remap as widgets not supported");
+            host.deleteHost();
+            return;
+        }
+        if (!RestoreDbTask.isPending(context)) {
+            // Someone has already gone through our DB once, probably LoaderTask. Skip any further
+            // modifications of the DB.
+            Log.e(TAG, "Skipping widget ID remap as DB already in use");
+            for (int widgetId : newWidgetIds) {
+                Log.d(TAG, "Deleting widgetId: " + widgetId);
+                host.deleteAppWidgetId(widgetId);
+            }
+            return;
+        }
+
+        final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
+
+        Log.d(TAG, "restoreAppWidgetIds: "
+                + "oldWidgetIds=" + IntArray.wrap(oldWidgetIds).toConcatString()
+                + ", newWidgetIds=" + IntArray.wrap(newWidgetIds).toConcatString());
+
+        // TODO(b/234700507): Remove the logs after the bug is fixed
+        logDatabaseWidgetInfo(controller);
+
+        for (int i = 0; i < oldWidgetIds.length; i++) {
+            Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
+
+            final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
+            final int state;
+            if (LoaderTask.isValidProvider(provider)) {
+                // This will ensure that we show 'Click to setup' UI if required.
+                state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+            } else {
+                state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+            }
+
+            // b/135926478: Work profile widget restore is broken in platform. This forces us to
+            // recreate the widget during loading with the correct host provider.
+            long mainProfileId = UserCache.INSTANCE.get(context)
+                    .getSerialNumberForUser(myUserHandle());
+            long controllerProfileId = controller.getSerialNumberForUser(myUserHandle());
+            String oldWidgetId = Integer.toString(oldWidgetIds[i]);
+            final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
+            String profileId = Long.toString(mainProfileId);
+            final String[] args = new String[] { oldWidgetId, profileId };
+            Log.d(TAG, "restoreAppWidgetIds: querying profile id=" + profileId
+                    + " with controller profile ID=" + controllerProfileId);
+            int result = new ContentWriter(context,
+                            new ContentWriter.CommitParams(controller, where, args))
+                    .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
+                    .put(LauncherSettings.Favorites.RESTORED, state)
+                    .commit();
+            if (result == 0) {
+                // TODO(b/234700507): Remove the logs after the bug is fixed
+                Log.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in"
+                        + " the database anymore");
+                try (Cursor cursor = controller.getDb().query(
+                        Favorites.TABLE_NAME,
+                        new String[]{Favorites.APPWIDGET_ID},
+                        "appWidgetId=?", new String[]{oldWidgetId}, null, null, null)) {
+                    if (!cursor.moveToFirst()) {
+                        // The widget no long exists.
+                        Log.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: "
+                                + oldWidgetId);
+                        host.deleteAppWidgetId(newWidgetIds[i]);
+                    }
+                }
+            }
+        }
+
+        LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+        if (app != null) {
+            app.getModel().forceReload();
+        }
+    }
+
+    private static void logDatabaseWidgetInfo(ModelDbController controller) {
+        try (Cursor cursor = controller.getDb().query(Favorites.TABLE_NAME,
+                new String[]{Favorites.APPWIDGET_ID, Favorites.RESTORED, Favorites.PROFILE_ID},
+                Favorites.APPWIDGET_ID + "!=" + LauncherAppWidgetInfo.NO_ID, null,
+                null, null, null)) {
+            IntArray widgetIdList = new IntArray();
+            IntArray widgetRestoreList = new IntArray();
+            IntArray widgetProfileIdList = new IntArray();
+
+            if (cursor.moveToFirst()) {
+                final int widgetIdColumnIndex = cursor.getColumnIndex(Favorites.APPWIDGET_ID);
+                final int widgetRestoredColumnIndex = cursor.getColumnIndex(Favorites.RESTORED);
+                final int widgetProfileIdIndex = cursor.getColumnIndex(Favorites.PROFILE_ID);
+                while (!cursor.isAfterLast()) {
+                    int widgetId = cursor.getInt(widgetIdColumnIndex);
+                    int widgetRestoredFlag = cursor.getInt(widgetRestoredColumnIndex);
+                    int widgetProfileId = cursor.getInt(widgetProfileIdIndex);
+
+                    widgetIdList.add(widgetId);
+                    widgetRestoreList.add(widgetRestoredFlag);
+                    widgetProfileIdList.add(widgetProfileId);
+                    cursor.moveToNext();
+                }
+            }
+
+            StringBuilder builder = new StringBuilder();
+            builder.append("[");
+            for (int i = 0; i < widgetIdList.size(); i++) {
+                builder.append("[")
+                        .append(widgetIdList.get(i))
+                        .append(", ")
+                        .append(widgetRestoreList.get(i))
+                        .append(", ")
+                        .append(widgetProfileIdList.get(i))
+                        .append("]");
+            }
+            builder.append("]");
+            Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: "
+                    + builder.toString());
+        } catch (Exception ex) {
+            Log.e(TAG, "Getting widget ids from the database failed", ex);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 347c7af..9b57799 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -397,8 +397,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());
     }
 
     /**
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/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..a136ef0 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,
@@ -308,9 +308,14 @@
                     + "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();
+    }
 
     public static final BooleanFlag ENABLE_CURSOR_HOVER_STATES = getDebugFlag(243191650,
             "ENABLE_CURSOR_HOVER_STATES", TEAMFOOD,
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..fc36ce6 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;
@@ -692,7 +693,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/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/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/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 10005e5..4725dd1 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -28,8 +28,6 @@
 
 import android.app.backup.BackupManager;
 import android.appwidget.AppWidgetHost;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -44,26 +42,20 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
-import androidx.annotation.WorkerThread;
 
+import com.android.launcher3.AppWidgetsRestoredReceiver;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherPrefs;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.DeviceGridState;
-import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDbController;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.uioverrides.ApiWrapper;
-import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.LogConfig;
 
@@ -385,13 +377,11 @@
                 .putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
     }
 
-    @WorkerThread
-    @VisibleForTesting
-    void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) {
+    private void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) {
         LauncherPrefs lp = LauncherPrefs.get(context);
         if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
             AppWidgetHost host = new AppWidgetHost(context, APPWIDGET_HOST_ID);
-            restoreAppWidgetIds(context, controller,
+            AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, controller,
                     IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
                     IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
                     host);
@@ -402,133 +392,6 @@
         lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS);
     }
 
-    /**
-     * Updates the app widgets whose id has changed during the restore process.
-     */
-    @WorkerThread
-    private void restoreAppWidgetIds(Context context, ModelDbController controller,
-            int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
-            Log.e(TAG, "Skipping widget ID remap as widgets not supported");
-            host.deleteHost();
-            return;
-        }
-        if (!RestoreDbTask.isPending(context)) {
-            // Someone has already gone through our DB once, probably LoaderTask. Skip any further
-            // modifications of the DB.
-            Log.e(TAG, "Skipping widget ID remap as DB already in use");
-            for (int widgetId : newWidgetIds) {
-                Log.d(TAG, "Deleting widgetId: " + widgetId);
-                host.deleteAppWidgetId(widgetId);
-            }
-            return;
-        }
-
-        final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
-
-        Log.d(TAG, "restoreAppWidgetIds: "
-                + "oldWidgetIds=" + IntArray.wrap(oldWidgetIds).toConcatString()
-                + ", newWidgetIds=" + IntArray.wrap(newWidgetIds).toConcatString());
-
-        // TODO(b/234700507): Remove the logs after the bug is fixed
-        logDatabaseWidgetInfo(controller);
-
-        for (int i = 0; i < oldWidgetIds.length; i++) {
-            Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
-
-            final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
-            final int state;
-            if (LoaderTask.isValidProvider(provider)) {
-                // This will ensure that we show 'Click to setup' UI if required.
-                state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
-            } else {
-                state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
-            }
-
-            // b/135926478: Work profile widget restore is broken in platform. This forces us to
-            // recreate the widget during loading with the correct host provider.
-            long mainProfileId = UserCache.INSTANCE.get(context)
-                    .getSerialNumberForUser(myUserHandle());
-            long controllerProfileId = controller.getSerialNumberForUser(myUserHandle());
-            String oldWidgetId = Integer.toString(oldWidgetIds[i]);
-            final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
-            String profileId = Long.toString(mainProfileId);
-            final String[] args = new String[] { oldWidgetId, profileId };
-            Log.d(TAG, "restoreAppWidgetIds: querying profile id=" + profileId
-                    + " with controller profile ID=" + controllerProfileId);
-            int result = new ContentWriter(context,
-                    new ContentWriter.CommitParams(controller, where, args))
-                    .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
-                    .put(LauncherSettings.Favorites.RESTORED, state)
-                    .commit();
-            if (result == 0) {
-                // TODO(b/234700507): Remove the logs after the bug is fixed
-                Log.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in"
-                        + " the database anymore");
-                try (Cursor cursor = controller.getDb().query(
-                        Favorites.TABLE_NAME,
-                        new String[]{Favorites.APPWIDGET_ID},
-                        "appWidgetId=?", new String[]{oldWidgetId}, null, null, null)) {
-                    if (!cursor.moveToFirst()) {
-                        // The widget no long exists.
-                        Log.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: "
-                                + oldWidgetId);
-                        host.deleteAppWidgetId(newWidgetIds[i]);
-                    }
-                }
-            }
-        }
-
-        LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-        if (app != null) {
-            app.getModel().forceReload();
-        }
-    }
-
-    private static void logDatabaseWidgetInfo(ModelDbController controller) {
-        try (Cursor cursor = controller.getDb().query(Favorites.TABLE_NAME,
-                new String[]{Favorites.APPWIDGET_ID, Favorites.RESTORED, Favorites.PROFILE_ID},
-                Favorites.APPWIDGET_ID + "!=" + LauncherAppWidgetInfo.NO_ID, null,
-                null, null, null)) {
-            IntArray widgetIdList = new IntArray();
-            IntArray widgetRestoreList = new IntArray();
-            IntArray widgetProfileIdList = new IntArray();
-
-            if (cursor.moveToFirst()) {
-                final int widgetIdColumnIndex = cursor.getColumnIndex(Favorites.APPWIDGET_ID);
-                final int widgetRestoredColumnIndex = cursor.getColumnIndex(Favorites.RESTORED);
-                final int widgetProfileIdIndex = cursor.getColumnIndex(Favorites.PROFILE_ID);
-                while (!cursor.isAfterLast()) {
-                    int widgetId = cursor.getInt(widgetIdColumnIndex);
-                    int widgetRestoredFlag = cursor.getInt(widgetRestoredColumnIndex);
-                    int widgetProfileId = cursor.getInt(widgetProfileIdIndex);
-
-                    widgetIdList.add(widgetId);
-                    widgetRestoreList.add(widgetRestoredFlag);
-                    widgetProfileIdList.add(widgetProfileId);
-                    cursor.moveToNext();
-                }
-            }
-
-            StringBuilder builder = new StringBuilder();
-            builder.append("[");
-            for (int i = 0; i < widgetIdList.size(); i++) {
-                builder.append("[")
-                        .append(widgetIdList.get(i))
-                        .append(", ")
-                        .append(widgetRestoreList.get(i))
-                        .append(", ")
-                        .append(widgetProfileIdList.get(i))
-                        .append("]");
-            }
-            builder.append("]");
-            Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: "
-                    + builder.toString());
-        } catch (Exception ex) {
-            Log.e(TAG, "Getting widget ids from the database failed", ex);
-        }
-    }
-
     public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds,
             @NonNull int[] newIds) {
         LauncherPrefs.get(context).putSync(
diff --git a/src/com/android/launcher3/responsive/HotseatSpecs.kt b/src/com/android/launcher3/responsive/HotseatSpecs.kt
index 482508d..d578b08 100644
--- a/src/com/android/launcher3/responsive/HotseatSpecs.kt
+++ b/src/com/android/launcher3/responsive/HotseatSpecs.kt
@@ -21,14 +21,20 @@
 import com.android.launcher3.R
 import com.android.launcher3.util.ResourceHelper
 
-class HotseatSpecs(val specs: List<HotseatSpec>) {
+class HotseatSpecs(val widthSpecs: List<HotseatSpec>, val heightSpecs: List<HotseatSpec>) {
 
     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 +42,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 +52,8 @@
 data class HotseatSpec(
     val maxAvailableSize: Int,
     val specType: ResponsiveSpec.SpecType,
-    val hotseatQsbSpace: SizeSpec
+    val hotseatQsbSpace: SizeSpec,
+    val edgePadding: SizeSpec
 ) {
 
     init {
@@ -63,7 +72,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 +92,10 @@
     }
 
     private fun allSpecsAreValid(): Boolean {
-        return hotseatQsbSpace.isValid() && hotseatQsbSpace.onlyFixedSize()
+        return hotseatQsbSpace.isValid() &&
+            hotseatQsbSpace.onlyFixedSize() &&
+            edgePadding.isValid() &&
+            edgePadding.onlyFixedSize()
     }
 
     companion object {
@@ -95,13 +108,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 +128,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/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/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/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 84ea871..7b82195 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -442,6 +442,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..f64fd05 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;
@@ -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..9bb8787 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -42,6 +42,7 @@
 filegroup {
     name: "launcher-oop-tests-src",
     srcs: [
+      "src/com/android/launcher3/allapps/OopTaplOpenCloseAllApps.java",
       "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
       "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
       "src/com/android/launcher3/ui/TaplTestsLauncher3.java",
@@ -54,6 +55,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/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index cdf8f08..30ec5cc 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";
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/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..73bb586 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -19,30 +19,17 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
-import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
-import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
-import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
-
-import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
 
 import android.app.backup.BackupManager;
-import android.appwidget.AppWidgetHost;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -56,7 +43,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.model.ModelDbController;
@@ -66,10 +52,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-
-import java.util.Arrays;
-import java.util.stream.IntStream;
 
 /**
  * Tests for {@link RestoreDbTask}
@@ -84,21 +66,11 @@
 
     private LauncherModelHelper mModelHelper;
     private Context mContext;
-    private RestoreDbTask mTask;
-    private ModelDbController mMockController;
-    private SQLiteDatabase mMockDb;
-    private Cursor mMockCursor;
-    private LauncherPrefs mPrefs;
 
     @Before
     public void setup() {
         mModelHelper = new LauncherModelHelper();
         mContext = mModelHelper.sandboxContext;
-        mTask = new RestoreDbTask();
-        mMockController = Mockito.mock(ModelDbController.class);
-        mMockDb = mock(SQLiteDatabase.class);
-        mMockCursor = mock(Cursor.class);
-        mPrefs = new LauncherPrefs(mContext);
     }
 
     @After
@@ -176,7 +148,8 @@
         assertEquals(10, getItemCountForProfile(db, myProfileId_old));
         assertEquals(6, getItemCountForProfile(db, workProfileId_old));
 
-        mTask.sanitizeDB(mContext, controller, controller.getDb(), bm);
+        RestoreDbTask task = new RestoreDbTask();
+        task.sanitizeDB(mContext, controller, controller.getDb(), bm);
 
         // All the data has been migrated to the new user ids
         assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -205,7 +178,8 @@
         assertEquals(10, getItemCountForProfile(db, myProfileId_old));
         assertEquals(6, getItemCountForProfile(db, workProfileId_old));
 
-        mTask.sanitizeDB(mContext, controller, controller.getDb(), bm);
+        RestoreDbTask task = new RestoreDbTask();
+        task.sanitizeDB(mContext, controller, controller.getDb(), bm);
 
         // All the data has been migrated to the new user ids
         assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -214,83 +188,6 @@
         assertEquals(10, getCount(db, "select * from favorites"));
     }
 
-    @Test
-    public void givenLauncherPrefsHasNoIds_whenRestoreAppWidgetIdsIfExists_thenIdsAreRemoved() {
-        // When
-        mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
-        // Then
-        assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
-    }
-
-    @Test
-    public void givenNoPendingRestore_WhenRestoreAppWidgetIds_ThenRemoveNewWidgetIds() {
-        // Given
-        AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID);
-        int[] expectedOldIds = generateOldWidgetIds(expectedHost);
-        int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds);
-        when(mMockController.getDb()).thenReturn(mMockDb);
-        mPrefs.remove(RESTORE_DEVICE);
-
-        // When
-        RestoreDbTask.setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
-        mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
-
-        // Then
-        assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
-        assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
-        verifyZeroInteractions(mMockController);
-    }
-
-    @Test
-    public void givenRestoreWithNonExistingWidgets_WhenRestoreAppWidgetIds_ThenRemoveNewIds() {
-        // Given
-        AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID);
-        int[] expectedOldIds = generateOldWidgetIds(expectedHost);
-        int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds);
-        when(mMockController.getDb()).thenReturn(mMockDb);
-        when(mMockDb.query(any(), any(), any(), any(), any(), any(), any())).thenReturn(
-                mMockCursor);
-        when(mMockCursor.moveToFirst()).thenReturn(false);
-        RestoreDbTask.setPending(mContext);
-
-        // When
-        RestoreDbTask.setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
-        mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
-
-        // Then
-        assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
-        assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
-        verify(mMockController, times(expectedOldIds.length)).update(any(), any(), any(), any());
-    }
-
-    @Test
-    public void givenRestore_WhenRestoreAppWidgetIds_ThenAddNewIds() {
-        // Given
-        AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID);
-        int[] expectedOldIds = generateOldWidgetIds(expectedHost);
-        int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds);
-        int[] allExpectedIds = IntStream.concat(
-                Arrays.stream(expectedOldIds),
-                Arrays.stream(expectedNewIds)
-        ).toArray();
-
-        when(mMockController.getDb()).thenReturn(mMockDb);
-        when(mMockDb.query(any(), any(), any(), any(), any(), any(), any()))
-                .thenReturn(mMockCursor);
-        when(mMockCursor.moveToFirst()).thenReturn(true);
-        when(mMockCursor.isAfterLast()).thenReturn(true);
-        RestoreDbTask.setPending(mContext);
-
-        // When
-        RestoreDbTask.setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
-        mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
-
-        // Then
-        assertThat(expectedHost.getAppWidgetIds()).isEqualTo(allExpectedIds);
-        assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
-        verify(mMockController, times(expectedOldIds.length)).update(any(), any(), any(), any());
-    }
-
     private void addIconsBulk(MyModelDbController controller,
             int count, int screen, long profileId) {
         int columns = LauncherAppState.getIDP(mContext).numColumns;
@@ -373,19 +270,6 @@
         }
     }
 
-    private int[] generateOldWidgetIds(AppWidgetHost host) {
-        // generate some widget ids in case there are none
-        host.allocateAppWidgetId();
-        host.allocateAppWidgetId();
-        return host.getAppWidgetIds();
-    }
-
-    private int[] generateNewWidgetIds(AppWidgetHost host, int[] oldWidgetIds) {
-        // map as many new ids as old ids
-        return Arrays.stream(oldWidgetIds)
-                .map(id -> host.allocateAppWidgetId()).toArray();
-    }
-
     private class MyModelDbController extends ModelDbController {
 
         public final LongSparseArray<UserHandle> users = new LongSparseArray<>();
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/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..707781e 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;
 
@@ -206,7 +205,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..0316f84 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -29,7 +29,6 @@
 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;
@@ -157,107 +156,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 {
@@ -622,7 +520,6 @@
      * 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();
@@ -645,10 +542,6 @@
             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);
@@ -734,4 +627,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/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..5cc4c5e
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/TestIsolationRule.java
@@ -0,0 +1,48 @@
+/*
+ * 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 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();
+            }
+        };
+    }
+}
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/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..2951901 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -60,8 +60,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")) {
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..872fe6f 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);
@@ -973,8 +951,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 +1024,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 +1032,7 @@
                 action = "clicking home button";
 
                 runToState(
-                        waitForNavigationUiObject("home")::click,
+                        getHomeButton()::click,
                         NORMAL_STATE_ORDINAL,
                         !hasLauncherObject(WORKSPACE_RES_ID)
                                 && (hasLauncherObject(APPS_RES_ID)
@@ -1082,15 +1059,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 +1213,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 +1496,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 +1599,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 +1720,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 +1737,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 +1745,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 +1835,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 +2116,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/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 94%
rename from tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
rename to tests/tapl/com/android/launcher3/tapl/TaskbarAllApps.java
index 0e0291f..63185f9 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);
     }
 
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);
     }