Merge "Add error detection for aborting the recents scrolling during transition to home" into tm-qpr-dev
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 1aa5d03..9a000d6 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -41,8 +41,10 @@
  */
 public class WidgetsModel {
 
-    // True is the widget support is disabled.
+    // True if the widget support is disabled.
     public static final boolean GO_DISABLE_WIDGETS = true;
+    // True if the shortcut support is disabled.
+    public static final boolean GO_DISABLE_SHORTCUTS = true;
     public static final boolean GO_DISABLE_NOTIFICATION_DOTS = true;
 
     private static final ArrayList<WidgetsListBaseEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
diff --git a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
index 2ffb28e..0c1f05f 100644
--- a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
+++ b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
@@ -25,12 +25,14 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.taskbar.LauncherTaskbarUIController;
 import com.android.launcher3.testing.DebugTestInformationHandler;
 import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.TouchInteractionService.TISBinder;
+import com.android.quickstep.util.TISBindHelper;
 
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
 
 /**
  * Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
@@ -49,29 +51,26 @@
         Bundle response = new Bundle();
         switch (method) {
             case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING:
-                runOnUIThread(l -> {
-                    enableManualTaskbarStashing(l, true);
+                runOnTISBinder(tisBinder -> {
+                    enableManualTaskbarStashing(tisBinder, true);
                 });
                 return response;
 
             case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING:
-                runOnUIThread(l -> {
-                    enableManualTaskbarStashing(l, false);
+                runOnTISBinder(tisBinder -> {
+                    enableManualTaskbarStashing(tisBinder, false);
                 });
                 return response;
 
             case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
-                runOnUIThread(l -> {
-                    enableManualTaskbarStashing(l, true);
-
-                    QuickstepLauncher quickstepLauncher = (QuickstepLauncher) l;
-                    LauncherTaskbarUIController taskbarUIController =
-                            quickstepLauncher.getTaskbarUIController();
+                runOnTISBinder(tisBinder -> {
+                    enableManualTaskbarStashing(tisBinder, true);
 
                     // Allow null-pointer to catch illegal states.
-                    taskbarUIController.unstashTaskbarIfStashed();
+                    tisBinder.getTaskbarManager().getCurrentActivityContext()
+                            .unstashTaskbarIfStashed();
 
-                    enableManualTaskbarStashing(l, false);
+                    enableManualTaskbarStashing(tisBinder, false);
                 });
                 return response;
 
@@ -82,6 +81,11 @@
                 return response;
             }
 
+            case TestProtocol.REQUEST_RECREATE_TASKBAR:
+                // Allow null-pointer to catch illegal states.
+                runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
+                return response;
+
             default:
                 response = super.call(method, arg, extras);
                 if (response != null) return response;
@@ -89,24 +93,26 @@
         }
     }
 
-    private void enableManualTaskbarStashing(Launcher launcher, boolean enable) {
-        QuickstepLauncher quickstepLauncher = (QuickstepLauncher) launcher;
-        LauncherTaskbarUIController taskbarUIController =
-                quickstepLauncher.getTaskbarUIController();
-
+    private void enableManualTaskbarStashing(TISBinder tisBinder, boolean enable) {
         // Allow null-pointer to catch illegal states.
-        taskbarUIController.enableManualStashingForTests(enable);
+        tisBinder.getTaskbarManager().getCurrentActivityContext().enableManualStashingForTests(
+                enable);
     }
 
     /**
-     * Runs the given command on the UI thread.
+     * Runs the given command on the UI thread, after ensuring we are connected to
+     * TouchInteractionService.
      */
-    private static void runOnUIThread(UIThreadCommand command) {
+    private void runOnTISBinder(Consumer<TISBinder> connectionCallback) {
         try {
-            MAIN_EXECUTOR.submit(() -> {
-                command.execute(Launcher.ACTIVITY_TRACKER.getCreatedActivity());
-                return null;
-            }).get();
+            CountDownLatch countDownLatch = new CountDownLatch(1);
+            TISBindHelper helper = MAIN_EXECUTOR.submit(() ->
+                    new TISBindHelper(mContext, tisBinder -> {
+                        connectionCallback.accept(tisBinder);
+                        countDownLatch.countDown();
+                    })).get();
+            countDownLatch.await();
+            MAIN_EXECUTOR.submit(helper::onDestroy);
         } catch (ExecutionException | InterruptedException e) {
             throw new RuntimeException(e);
         }
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 3b4a28b..198a676 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -43,4 +43,9 @@
     <!-- Accessibility actions -->
     <item type="id" name="action_move_to_top_or_left" />
     <item type="id" name="action_move_to_bottom_or_right" />
+
+    <!-- The max scale for the wallpaper when it's zoomed in -->
+    <item name="config_wallpaperMaxScale" format="float" type="dimen">
+        @*android:dimen/config_wallpaperMaxScale
+    </item>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 521b3fe..e7a6cfe 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -42,7 +42,7 @@
 import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.launcher3.statehandlers.DepthController.STATE_DEPTH;
 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
@@ -384,7 +384,7 @@
                 @Override
                 public void onAnimationStart(Animator animation) {
                     mLauncher.addOnResumeCallback(() ->
-                            ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
+                            ObjectAnimator.ofFloat(mLauncher.getDepthController(), STATE_DEPTH,
                                     mLauncher.getStateManager().getState().getDepth(
                                             mLauncher)).start());
                 }
@@ -408,7 +408,7 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 mLauncher.addOnResumeCallback(() ->
-                        ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
+                        ObjectAnimator.ofFloat(mLauncher.getDepthController(), STATE_DEPTH,
                                 mLauncher.getStateManager().getState().getDepth(
                                         mLauncher)).start());
             }
@@ -1051,7 +1051,7 @@
                 && BlurUtils.supportsBlursOnWindows();
 
         MyDepthController depthController = new MyDepthController(mLauncher);
-        ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, DEPTH,
+        ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, STATE_DEPTH,
                         BACKGROUND_APP.getDepth(mLauncher))
                 .setDuration(APP_LAUNCH_DURATION);
 
@@ -1594,7 +1594,8 @@
                             true /* animateOverviewScrim */, launcherView).getAnimators());
 
                     if (!areAllTargetsTranslucent(appTargets)) {
-                        anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
+                        anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController(),
+                                STATE_DEPTH,
                                 BACKGROUND_APP.getDepth(mLauncher), NORMAL.getDepth(mLauncher)));
                     }
 
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index f42b39f..e8374b8 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 
@@ -92,8 +93,10 @@
                 ? R.color.all_apps_label_text_dark
                 : R.color.all_apps_label_text);
 
-        mShowAllAppsLabel = !ActivityContext.lookupContext(
-                getContext()).getOnboardingPrefs().hasReachedMaxCount(ALL_APPS_VISITED_COUNT);
+        OnboardingPrefs<?> onboardingPrefs = ActivityContext.lookupContext(
+                getContext()).getOnboardingPrefs();
+        mShowAllAppsLabel = onboardingPrefs == null || !onboardingPrefs.hasReachedMaxCount(
+                ALL_APPS_VISITED_COUNT);
     }
 
     public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
@@ -216,8 +219,8 @@
 
             CharSequence allAppsLabelText = getResources().getText(R.string.all_apps_label);
             mAllAppsLabelLayout = StaticLayout.Builder.obtain(
-                    allAppsLabelText, 0, allAppsLabelText.length(), mPaint,
-                    Math.round(mPaint.measureText(allAppsLabelText.toString())))
+                            allAppsLabelText, 0, allAppsLabelText.length(), mPaint,
+                            Math.round(mPaint.measureText(allAppsLabelText.toString())))
                     .setAlignment(Layout.Alignment.ALIGN_CENTER)
                     .setMaxLines(1)
                     .setIncludePad(true)
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 770dfb2..de0b14d 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -117,14 +117,15 @@
         // TODO: Implement caching and preloading
         super.loadItems(ums, pinnedShortcuts);
 
-        WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(
-                mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns);
-        FixedContainerItems allAppsItems = new FixedContainerItems(mAllAppsState.containerId,
-                mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
-        mDataModel.extraItems.put(mAllAppsState.containerId, allAppsItems);
+        WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts,
+                mIDP.numDatabaseAllAppsColumns, mAllAppsState.containerId);
+        FixedContainerItems allAppsPredictionItems = new FixedContainerItems(
+                mAllAppsState.containerId, mAllAppsState.storage.read(mApp.getContext(),
+                allAppsFactory, ums.allUsers::get));
+        mDataModel.extraItems.put(mAllAppsState.containerId, allAppsPredictionItems);
 
-        WorkspaceItemFactory hotseatFactory =
-                new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseHotseatIcons);
+        WorkspaceItemFactory hotseatFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts,
+                mIDP.numDatabaseHotseatIcons, mHotseatState.containerId);
         FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId,
                 mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
         mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems);
@@ -432,15 +433,17 @@
         private final UserManagerState mUMS;
         private final Map<ShortcutKey, ShortcutInfo> mPinnedShortcuts;
         private final int mMaxCount;
+        private final int mContainer;
 
         private int mReadCount = 0;
 
         protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums,
-                Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount) {
+                Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount, int container) {
             mAppState = appState;
             mUMS = ums;
             mPinnedShortcuts = pinnedShortcuts;
             mMaxCount = maxCount;
+            mContainer = container;
         }
 
         @Nullable
@@ -458,6 +461,7 @@
                         return null;
                     }
                     AppInfo info = new AppInfo(lai, user, mUMS.isUserQuiet(user));
+                    info.container = mContainer;
                     mAppState.getIconCache().getTitleAndIcon(info, lai, false);
                     mReadCount++;
                     return info.makeWorkspaceItem(mAppState.getContext());
@@ -472,6 +476,7 @@
                         return null;
                     }
                     WorkspaceItemInfo wii = new WorkspaceItemInfo(si, mAppState.getContext());
+                    wii.container = mContainer;
                     mAppState.getIconCache().getShortcutIcon(wii, si);
                     mReadCount++;
                     return wii;
diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
deleted file mode 100644
index 8c11081..0000000
--- a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 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.statehandlers;
-
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
-import static com.android.quickstep.AnimatedFloat.VALUE;
-
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.statemanager.StateManager.StateHandler;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SystemUiProxy;
-
-/**
- * State handler for animating back button alpha in two-button nav mode.
- */
-public class BackButtonAlphaHandler implements StateHandler<LauncherState> {
-
-    private final QuickstepLauncher mLauncher;
-    private final AnimatedFloat mBackAlpha = new AnimatedFloat(this::updateBackAlpha);
-
-    public BackButtonAlphaHandler(QuickstepLauncher launcher) {
-        mLauncher = launcher;
-    }
-
-    @Override
-    public void setState(LauncherState state) { }
-
-    @Override
-    public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
-            PendingAnimation animation) {
-        if (DisplayController.getNavigationMode(mLauncher) != TWO_BUTTONS) {
-            return;
-        }
-
-        mBackAlpha.value = SystemUiProxy.INSTANCE.get(mLauncher).getLastNavButtonAlpha();
-        animation.setFloat(mBackAlpha, VALUE,
-                mLauncher.shouldBackButtonBeHidden(toState) ? 0 : 1, LINEAR);
-    }
-
-    private void updateBackAlpha() {
-        UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
-                QuickstepLauncher.SET_BACK_BUTTON_ALPHA, mBackAlpha.value, false /* animate */);
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 1311b1d..e3fd3f9 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -141,7 +141,7 @@
 
     @Override
     public void setState(LauncherState toState) {
-        if (mSurface == null || mIgnoreStateChangesDuringMultiWindowAnimation) {
+        if (mIgnoreStateChangesDuringMultiWindowAnimation) {
             return;
         }
 
@@ -165,7 +165,8 @@
 
         float toDepth = toState.getDepth(mLauncher);
         if (Float.compare(mDepth, toDepth) != 0) {
-            animation.setFloat(this, DEPTH, toDepth, config.getInterpolator(ANIM_DEPTH, LINEAR));
+            animation.setFloat(this, STATE_DEPTH, toDepth,
+                    config.getInterpolator(ANIM_DEPTH, LINEAR));
         }
     }
 
@@ -179,7 +180,7 @@
     public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
         mIgnoreStateChangesDuringMultiWindowAnimation = true;
 
-        ObjectAnimator mwAnimation = ObjectAnimator.ofFloat(this, DEPTH,
+        ObjectAnimator mwAnimation = ObjectAnimator.ofFloat(this, STATE_DEPTH,
                 mLauncher.getStateManager().getState().getDepth(mLauncher, isInMultiWindowMode))
                 .setDuration(300);
         mwAnimation.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
index d69b8d2..9393b0f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
@@ -43,8 +43,8 @@
         mLauncher.getHotseat().setIconsAlpha(1f);
     }
 
-    @Override
     /** Disable taskbar stashing in desktop environment. */
+    @Override
     public boolean supportsVisualStashing() {
         return false;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 6c740ba..520487e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
 
 import android.animation.Animator;
@@ -30,7 +31,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherState;
@@ -124,24 +124,6 @@
     }
 
     /**
-     * Enables manual taskbar stashing. This method should only be used for tests that need to
-     * stash/unstash the taskbar.
-     */
-    @VisibleForTesting
-    public void enableManualStashingForTests(boolean enableManualStashing) {
-        mControllers.taskbarStashController.enableManualStashingForTests(enableManualStashing);
-    }
-
-    /**
-     * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
-     * taskbar at the end of a test.
-     */
-    @VisibleForTesting
-    public void unstashTaskbarIfStashed() {
-        mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
-    }
-
-    /**
      * Adds the Launcher resume animator to the given animator set.
      *
      * This should be used to run a Launcher resume animation whose progress matches a
@@ -188,6 +170,13 @@
             }
         }
 
+        if (ENABLE_SHELL_TRANSITIONS
+                && !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) {
+            // Launcher is resumed, but in a state where taskbar is still independent, so
+            // ignore the state change.
+            return null;
+        }
+
         mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
         return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index f1f18c1..4fda50e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.taskbar;
 
+import static android.view.View.AccessibilityDelegate;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
@@ -582,6 +583,26 @@
         return mHomeButtonAlpha;
     }
 
+    /**
+     * Sets the AccessibilityDelegate for the home button.
+     */
+    public void setHomeButtonAccessibilityDelegate(AccessibilityDelegate accessibilityDelegate) {
+        if (mHomeButton == null) {
+            return;
+        }
+        mHomeButton.setAccessibilityDelegate(accessibilityDelegate);
+    }
+
+    /**
+     * Sets the AccessibilityDelegate for the back button.
+     */
+    public void setBackButtonAccessibilityDelegate(AccessibilityDelegate accessibilityDelegate) {
+        if (mBackButton == null) {
+            return;
+        }
+        mBackButton.setAccessibilityDelegate(accessibilityDelegate);
+    }
+
     /** Use to set the translationY for the all nav+contextual buttons */
     public AnimatedFloat getTaskbarNavButtonTranslationY() {
         return mTaskbarNavButtonTranslationY;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e1bcbe2..8697b69 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -80,7 +80,7 @@
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.TraceHelper;
@@ -773,6 +773,24 @@
         mControllers.taskbarStashController.startUnstashHint(animateForward);
     }
 
+    /**
+     * Enables manual taskbar stashing. This method should only be used for tests that need to
+     * stash/unstash the taskbar.
+     */
+    @VisibleForTesting
+    public void enableManualStashingForTests(boolean enableManualStashing) {
+        mControllers.taskbarStashController.enableManualStashingForTests(enableManualStashing);
+    }
+
+    /**
+     * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
+     * taskbar at the end of a test.
+     */
+    @VisibleForTesting
+    public void unstashTaskbarIfStashed() {
+        mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
+    }
+
     protected boolean isUserSetupComplete() {
         return mIsUserSetupComplete;
     }
@@ -850,6 +868,10 @@
         btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv));
     }
 
+    public boolean isInApp() {
+        return mControllers.taskbarStashController.isInApp();
+    }
+
     protected void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "TaskbarActivityContext:");
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
index c99cebb..6c793a6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -18,13 +18,17 @@
 
 import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
 import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 
 import static com.android.launcher3.taskbar.NavbarButtonsViewController.ALPHA_INDEX_IMMERSIVE_MODE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IMMERSIVE_MODE;
 
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.view.MotionEvent;
+import android.view.View;
 
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.util.MultiValueAlpha;
@@ -52,6 +56,21 @@
             this::updateIconDimmingAlpha);
     private final Consumer<MultiValueAlpha> mImmersiveModeAlphaUpdater = alpha -> alpha.getProperty(
             ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
+    private final View.AccessibilityDelegate mKidsModeAccessibilityDelegate =
+            new View.AccessibilityDelegate() {
+                @Override
+                public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                    if (action == ACTION_ACCESSIBILITY_FOCUS || action == ACTION_CLICK) {
+                        // Animate undimming of icons on an a11y event, followed by starting the
+                        // dimming animation (after its timeout has expired). Both can be called in
+                        // succession, as the playing of the two animations in a row is managed by
+                        // mHandler's message queue.
+                        startIconUndimming();
+                        startIconDimming();
+                    }
+                    return super.performAccessibilityAction(host, action, args);
+                }
+            };
 
     // Initialized in init.
     private TaskbarControllers mControllers;
@@ -77,12 +96,21 @@
             } else {
                 startIconUndimming();
             }
+            mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(
+                    mKidsModeAccessibilityDelegate);
+            mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(
+                    mKidsModeAccessibilityDelegate);
+        } else {
+            mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null);
+            mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null);
         }
     }
 
     /** Clean up animations. */
     public void onDestroy() {
         startIconUndimming();
+        mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null);
+        mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null);
     }
 
     private void startIconUndimming() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 35c5b96..bc69088 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -36,6 +36,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
@@ -44,6 +45,7 @@
 import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.quickstep.RecentsActivity;
@@ -82,7 +84,7 @@
     // It's destruction/creation will be managed by the activity.
     private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider =
             new NonDestroyableScopedUnfoldTransitionProgressProvider();
-    private DisplayController.NavigationMode mNavMode;
+    private NavigationMode mNavMode;
 
     private TaskbarActivityContext mTaskbarActivityContext;
     private StatefulActivity mActivity;
@@ -277,7 +279,8 @@
      * we fully want to destroy an existing taskbar and create a new one.
      * In other case (folding/unfolding) we don't need to remove and add window.
      */
-    private void recreateTaskbar() {
+    @VisibleForTesting
+    public void recreateTaskbar() {
         DeviceProfile dp = mUserUnlocked ?
                 LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 3ea9173..d9d55e7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -210,7 +210,10 @@
                 StashedHandleViewController.ALPHA_INDEX_STASHED);
         mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
 
-        boolean isManuallyStashedInApp = supportsManualStashing()
+        // We use supportsVisualStashing() here instead of supportsManualStashing() because we want
+        // it to work properly for tests that recreate taskbar. This check is here just to ensure
+        // that taskbar unstashes when going to 3 button mode (supportsVisualStashing() false).
+        boolean isManuallyStashedInApp = supportsVisualStashing()
                 && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
         boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
         updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
@@ -218,7 +221,10 @@
         updateStateForFlag(FLAG_IN_SETUP, isInSetup);
         updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode()
                 && !mActivity.isThreeButtonNav());
-        applyState();
+        // For now, assume we're in an app, since LauncherTaskbarUIController won't be able to tell
+        // us that we're paused until a bit later. This avoids flickering upon recreating taskbar.
+        updateStateForFlag(FLAG_IN_APP, true);
+        applyState(/* duration = */ 0);
 
         notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp());
     }
@@ -228,8 +234,7 @@
      * state.
      */
     public boolean supportsVisualStashing() {
-        return mControllers.uiController.supportsVisualStashing() ||
-                (isPhoneMode() && !mActivity.isThreeButtonNav());
+        return !mActivity.isThreeButtonNav() && mControllers.uiController.supportsVisualStashing();
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index fcc34c6..114bfec 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -49,9 +49,13 @@
         return true;
     }
 
+    /**
+     * This should only be called by TaskbarStashController so that a TaskbarUIController can
+     * disable stashing. All other controllers should use
+     * {@link TaskbarStashController#supportsVisualStashing()} as the source of truth.
+     */
     public boolean supportsVisualStashing() {
-        if (mControllers == null) return false;
-        return !mControllers.taskbarActivityContext.isThreeButtonNav();
+        return true;
     }
 
     protected void onStashedInAppChanged() { }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index c4837a0..0e62da3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -16,8 +16,7 @@
 package com.android.launcher3.taskbar.allapps;
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
-import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
@@ -60,7 +59,7 @@
         if (animate) {
             mOpenCloseAnimator.setValues(
                     PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-            mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
+            mOpenCloseAnimator.setInterpolator(EMPHASIZED);
             mOpenCloseAnimator.setDuration(
                     ALL_APPS.getTransitionDuration(mActivityContext, true /* isToState */)).start();
         } else {
@@ -87,7 +86,7 @@
 
     @Override
     protected Interpolator getIdleInterpolator() {
-        return EMPHASIZED_ACCELERATE;
+        return EMPHASIZED;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 8782ee6..2e4e739 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -28,11 +28,10 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.quickstep.views.FloatingTaskView.PRIMARY_TRANSLATE_OFFSCREEN;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
-import static com.android.quickstep.views.RecentsView.FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.quickstep.views.RecentsView.SPLIT_INSTRUCTIONS_FADE;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA;
 
@@ -112,6 +111,7 @@
             // TODO (b/238651489): Refactor state management to avoid need for double check
             FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView();
             if (floatingTask != null) {
+                // We are in split selection state currently, transitioning to another state
                 DragLayer dragLayer = mLauncher.getDragLayer();
                 RectF onScreenRectF = new RectF();
                 Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), floatingTask,
@@ -127,8 +127,8 @@
                 );
 
                 setter.setFloat(
-                        mRecentsView,
-                        FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN,
+                        mRecentsView.getFirstFloatingTaskView(),
+                        PRIMARY_TRANSLATE_OFFSCREEN,
                         mRecentsView.getPagedOrientationHandler()
                                 .getFloatingTaskOffscreenTranslationTarget(
                                         floatingTask,
@@ -140,14 +140,14 @@
                                 ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
                                 LINEAR
                         ));
-                setter.setFloat(
-                        mRecentsView,
-                        SPLIT_INSTRUCTIONS_FADE,
-                        1,
+                setter.setViewAlpha(
+                        mRecentsView.getSplitInstructionsView(),
+                        0,
                         config.getInterpolator(
                                 ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
                                 LINEAR
-                        ));
+                        )
+                );
             }
         }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 0f3ea15..12d7a98 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,22 +17,21 @@
 
 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
 
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
 import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.NO_OFFSET;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_WIDGET_PICKER_DEPTH;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
@@ -45,9 +44,9 @@
 import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
-import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
 import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.util.BaseDepthController.WIDGET_DEPTH;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
 import android.animation.AnimatorSet;
@@ -64,6 +63,7 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.IBinder;
+import android.os.SystemProperties;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
@@ -71,7 +71,6 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -79,6 +78,7 @@
 import com.android.launcher3.QuickstepAccessibilityDelegate;
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -95,7 +95,6 @@
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.proxy.ProxyActivityStarter;
 import com.android.launcher3.proxy.StartActivityParams;
-import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -113,16 +112,16 @@
 import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.util.PendingSplitSelectInfo;
 import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.TouchController;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
+import com.android.launcher3.util.ViewCapture;
 import com.android.launcher3.widget.LauncherAppWidgetHost;
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.RecentsModel;
@@ -159,18 +158,10 @@
 
 public class QuickstepLauncher extends Launcher {
 
+    public static final boolean ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
+            SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", false);
+
     public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
-    /**
-     * Reusable command for applying the shelf height on the background thread.
-     */
-    public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
-            SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
-    /**
-     * Reusable command for applying the back button alpha on the background thread.
-     */
-    public static final AsyncCommand SET_BACK_BUTTON_ALPHA =
-            (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setNavBarButtonAlpha(
-                    Float.intBitsToFloat(arg1), arg2 != 0);
 
     private FixedContainerItems mAllAppsPredictions;
     private HotseatPredictionController mHotseatPredictionController;
@@ -191,6 +182,8 @@
      */
     private PendingSplitSelectInfo mPendingSplitSelectInfo = null;
 
+    private SafeCloseable mViewCapture;
+
     @Override
     protected void setupViews() {
         super.setupViews();
@@ -291,11 +284,6 @@
 
     @Override
     protected void onActivityFlagsChanged(int changeBits) {
-        if ((changeBits
-                & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
-            onLauncherStateOrFocusChanged();
-        }
-
         if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
             mDepthController.setActivityStarted(isStarted());
         }
@@ -349,16 +337,17 @@
      */
     private void onStateOrResumeChanging(boolean inTransition) {
         LauncherState state = getStateManager().getState();
-        boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0;
-        if (started) {
-            DeviceProfile profile = getDeviceProfile();
-            boolean willUserBeActive =
-                    (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
-            boolean visible = (state == NORMAL || state == OVERVIEW)
-                    && (willUserBeActive || isUserActive())
-                    && !profile.isVerticalBarLayout();
-            UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
-                    profile.hotseatBarSizePx);
+        if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) {
+            boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0;
+            if (started) {
+                DeviceProfile profile = getDeviceProfile();
+                boolean willUserBeActive =
+                        (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
+                boolean visible = (state == NORMAL || state == OVERVIEW)
+                        && (willUserBeActive || isUserActive())
+                        && !profile.isVerticalBarLayout();
+                SystemUiProxy.INSTANCE.get(this).setShelfHeight(visible, profile.hotseatBarSizePx);
+            }
         }
         if (state == NORMAL && !inTransition) {
             ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
@@ -404,6 +393,7 @@
 
         super.onDestroy();
         mHotseatPredictionController.destroy();
+        mViewCapture.close();
     }
 
     @Override
@@ -503,6 +493,7 @@
         }
         addMultiWindowModeChangedListener(mDepthController);
         initUnfoldTransitionProgressProvider();
+        mViewCapture = ViewCapture.INSTANCE.get(this).startCapture(getWindow());
     }
 
     @Override
@@ -596,6 +587,11 @@
     public void onWidgetsTransition(float progress) {
         super.onWidgetsTransition(progress);
         onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
+        if (ENABLE_WIDGET_PICKER_DEPTH.get() && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            WIDGET_DEPTH.set(getDepthController(),
+                    Utilities.mapToRange(progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth,
+                            EMPHASIZED));
+        }
     }
 
     private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) {
@@ -717,7 +713,6 @@
         super.collectStateHandlers(out);
         out.add(getDepthController());
         out.add(new RecentsViewStateController(this));
-        out.add(new BackButtonAlphaHandler(this));
     }
 
     public DepthController getDepthController() {
@@ -776,39 +771,6 @@
     }
 
     @Override
-    public void onDragLayerHierarchyChanged() {
-        onLauncherStateOrFocusChanged();
-    }
-
-    public boolean shouldBackButtonBeHidden(LauncherState toState) {
-        NavigationMode mode = DisplayController.getNavigationMode(this);
-        boolean shouldBackButtonBeHidden = mode.hasGestures
-                && toState.hasFlag(FLAG_HIDE_BACK_BUTTON)
-                && hasWindowFocus()
-                && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
-        if (shouldBackButtonBeHidden) {
-            // Show the back button if there is a floating view visible.
-            shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
-                    TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
-        }
-        return shouldBackButtonBeHidden;
-    }
-
-    /**
-     * Sets the back button visibility based on the current state/window focus.
-     */
-    private void onLauncherStateOrFocusChanged() {
-        boolean shouldBackButtonBeHidden = shouldBackButtonBeHidden(getStateManager().getState());
-        if (DisplayController.getNavigationMode(this) == TWO_BUTTONS) {
-            UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
-                    shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
-        }
-        if (getDragLayer() != null) {
-            getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
-        }
-    }
-
-    @Override
     public void finishBindingItems(IntSet pagesBoundFirst) {
         super.finishBindingItems(pagesBoundFirst);
         // Instantiate and initialize WellbeingModel now that its loading won't interfere with
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 9f2efc4..fd184c6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
@@ -41,9 +42,9 @@
     @Override
     public <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfileListenable>
     int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) {
-        return !context.getDeviceProfile().isTablet && isToState
-                ? 600
-                : isToState ? 500 : 300;
+        return isToState
+                ? context.getDeviceProfile().allAppsOpenDuration
+                : context.getDeviceProfile().allAppsCloseDuration;
     }
 
     @Override
@@ -77,10 +78,15 @@
     }
 
     @Override
-    protected float getDepthUnchecked(Context context) {
-        // The scrim fades in at approximately 50% of the swipe gesture.
-        // This means that the depth should be greater than 1, in order to fully zoom out.
-        return 2f;
+    protected <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
+            float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
+        if (context.getDeviceProfile().isTablet) {
+            return context.getDeviceProfile().bottomSheetDepth;
+        } else {
+            // The scrim fades in at approximately 50% of the swipe gesture.
+            // This means that the depth should be greater than 1, in order to fully zoom out.
+            return 2f;
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 2eade67..4150d40 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.uioverrides.states;
 
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
 
 import android.content.Context;
 import android.graphics.Color;
@@ -101,6 +102,12 @@
         return Color.TRANSPARENT;
     }
 
+    @Override
+    public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
+        if (ENABLE_SHELL_TRANSITIONS) return false;
+        return super.isTaskbarAlignedWithHotseat(launcher);
+    }
+
     public static float[] getOverviewScaleAndOffsetForBackgroundState(
             BaseDraggingActivity activity) {
         return new float[] {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index e5cd53a..56ac4c5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -45,7 +45,7 @@
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.views.RecentsView;
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 5a59f85..81a5c1c 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -33,6 +33,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
+import static com.android.launcher3.uioverrides.QuickstepLauncher.ENABLE_PIP_KEEP_CLEAR_ALGORITHM;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
@@ -339,6 +340,8 @@
             return ActiveGestureErrorDetector.GestureEvent.STATE_CAPTURE_SCREENSHOT;
         } else if (stateFlag == STATE_HANDLER_INVALIDATED) {
             return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED;
+        } else if (stateFlag == STATE_LAUNCHER_DRAWN) {
+            return ActiveGestureErrorDetector.GestureEvent.STATE_LAUNCHER_DRAWN;
         }
         return null;
     }
@@ -1469,12 +1472,13 @@
         homeToWindowPositionMap.invert(windowToHomePositionMap);
         windowToHomePositionMap.mapRect(startRect);
 
+        final Rect hotseatKeepClearArea = getKeepClearAreaForHotseat();
         final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
                 .startSwipePipToHome(taskInfo.topActivity,
                         taskInfo.topActivityInfo,
                         runningTaskTarget.taskInfo.pictureInPictureParams,
                         homeRotation,
-                        mDp.hotseatBarSizePx);
+                        hotseatKeepClearArea);
         final Rect appBounds = new Rect();
         final WindowConfiguration winConfig = taskInfo.configuration.windowConfiguration;
         // Adjust the appBounds for TaskBar by using the calculated window crop Rect
@@ -1537,6 +1541,35 @@
         return swipePipToHomeAnimator;
     }
 
+    private Rect getKeepClearAreaForHotseat() {
+        Rect keepClearArea;
+        if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) {
+            // make the height equal to hotseatBarSizePx only
+            keepClearArea = new Rect(0, 0, 0, mDp.hotseatBarSizePx);
+            return keepClearArea;
+        }
+        // the keep clear area in global screen coordinates, in pixels
+        if (mDp.isPhone) {
+            if (mDp.isSeascape()) {
+                // in seascape the Hotseat is on the left edge of the screen
+                keepClearArea = new Rect(0, 0, mDp.hotseatBarSizePx, mDp.heightPx);
+            } else if (mDp.isLandscape) {
+                // in landscape the Hotseat is on the right edge of the screen
+                keepClearArea = new Rect(mDp.widthPx - mDp.hotseatBarSizePx, 0,
+                        mDp.widthPx, mDp.heightPx);
+            } else {
+                // in portrait mode the Hotseat is at the bottom of the screen
+                keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx,
+                        mDp.widthPx, mDp.heightPx);
+            }
+        } else {
+            // large screens have Hotseat always at the bottom of the screen
+            keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx,
+                    mDp.widthPx, mDp.heightPx);
+        }
+        return keepClearArea;
+    }
+
     private void startInterceptingTouchesForGesture() {
         if (mRecentsAnimationController == null) {
             return;
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 6354282..315a91e 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -55,7 +55,7 @@
 import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index ba61574..466abbe 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -16,7 +16,7 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
 import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
 import static com.android.quickstep.fallback.RecentsState.HOME;
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 4ceafeb..1127e2c 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -45,7 +45,7 @@
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index 56e7fb5..a68bea2 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -116,7 +116,13 @@
             if (gestureEvent == null) {
                 continue;
             }
-            ActiveGestureLog.INSTANCE.trackEvent(gestureEvent);
+            if (gestureEvent.mLogEvent && gestureEvent.mTrackEvent) {
+                ActiveGestureLog.INSTANCE.addLog(gestureEvent.name(), gestureEvent);
+            } else if (gestureEvent.mLogEvent) {
+                ActiveGestureLog.INSTANCE.addLog(gestureEvent.name());
+            } else if (gestureEvent.mTrackEvent) {
+                ActiveGestureLog.INSTANCE.trackEvent(gestureEvent);
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 47c5dd0..1b05fd2 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -34,7 +34,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.window.CachedDisplayInfo;
 
 import java.io.PrintWriter;
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 1e7e89e..e84eb56 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -144,7 +144,7 @@
         RunnableList callbackList = null;
         if (taskView != null) {
             taskView.setEndQuickswitchCuj(true);
-            callbackList = taskView.launchTaskAnimated();
+            callbackList = taskView.launchTasks();
         }
 
         if (callbackList != null) {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 813e687..5a9862c 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -27,9 +27,9 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.quickstep.util.GroupTask;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.GroupTask;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.KeyguardManagerCompat;
 import com.android.wm.shell.recents.IRecentTasksListener;
@@ -254,8 +254,12 @@
 
         TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
         for (GroupedRecentTaskInfo rawTask : rawTasks) {
-            ActivityManager.RecentTaskInfo taskInfo1 = rawTask.mTaskInfo1;
-            ActivityManager.RecentTaskInfo taskInfo2 = rawTask.mTaskInfo2;
+            if (rawTask.getType() == GroupedRecentTaskInfo.TYPE_FREEFORM) {
+                // TODO: add entry for freeform tasks
+                continue;
+            }
+            ActivityManager.RecentTaskInfo taskInfo1 = rawTask.getTaskInfo1();
+            ActivityManager.RecentTaskInfo taskInfo2 = rawTask.getTaskInfo2();
             Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
             Task task1 = loadKeysOnly
                     ? new Task(task1Key)
@@ -272,7 +276,7 @@
                 task2.setLastSnapshotData(taskInfo2);
             }
             final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
-                    convertSplitBounds(rawTask.mSplitBounds);
+                    convertSplitBounds(rawTask.getSplitBounds());
             allTasks.add(new GroupTask(task1, task2, launcherSplitBounds));
         }
 
@@ -310,8 +314,8 @@
                 mSysUiProxy.getRecentTasks(Integer.MAX_VALUE, currentUserId);
         writer.println(prefix + "  rawTasks=[");
         for (GroupedRecentTaskInfo task : rawTasks) {
-            writer.println(prefix + "    t1=" + task.mTaskInfo1.taskId
-                    + " t2=" + (task.mTaskInfo2 != null ? task.mTaskInfo2.taskId : "-1"));
+            writer.println(prefix + "    t1=" + task.getTaskInfo1().taskId
+                    + " t2=" + (task.getTaskInfo2() != null ? task.getTaskInfo2().taskId : "-1"));
         }
         writer.println(prefix + "  ]");
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index b6cfbb0..e87fdad 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -23,9 +23,9 @@
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
-import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
-import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
-import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
+import static com.android.launcher3.util.NavigationMode.TWO_BUTTONS;
 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
@@ -67,7 +67,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.SettingsCache;
 import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
 import com.android.quickstep.util.NavBarPosition;
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 2186a3b..f8b6966 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -23,8 +23,8 @@
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
 import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
-import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -35,8 +35,8 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 0ec7e62..3a7ade3 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,8 +17,8 @@
 
 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
 
-import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.app.ActivityManager;
 import android.app.PendingIntent;
@@ -28,12 +28,14 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
+import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
@@ -43,8 +45,8 @@
 import android.view.SurfaceControl;
 import android.window.IOnBackInvokedCallback;
 
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.Info;
+import androidx.annotation.WorkerThread;
+
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -72,12 +74,14 @@
 /**
  * Holds the reference to SystemUI.
  */
-public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayInfoChangeListener {
+public class SystemUiProxy implements ISystemUiProxy {
     private static final String TAG = SystemUiProxy.class.getSimpleName();
 
     public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
             new MainThreadInitializedObject<>(SystemUiProxy::new);
 
+    private static final int MSG_SET_SHELF_HEIGHT = 1;
+
     private ISystemUiProxy mSystemUiProxy;
     private IPip mPip;
     private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
@@ -106,26 +110,16 @@
     // Used to dedupe calls to SystemUI
     private int mLastShelfHeight;
     private boolean mLastShelfVisible;
-    private float mLastNavButtonAlpha;
-    private boolean mLastNavButtonAnimate;
-    private boolean mHasNavButtonAlphaBeenSet = false;
-    private Runnable mPendingSetNavButtonAlpha = null;
-    private Context mContext;
+
+    private final Context mContext;
+    private final Handler mAsyncHandler;
 
     // TODO(141886704): Find a way to remove this
     private int mLastSystemUiStateFlags;
 
     public SystemUiProxy(Context context) {
-        DisplayController.INSTANCE.get(context).addChangeListener(this);
         mContext = context;
-    }
-
-    @Override
-    public void onDisplayInfoChanged(Context context, Info info, int flags) {
-        if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
-            // Whenever the nav mode changes, force reset the nav button alpha
-            setNavBarButtonAlpha(1f, false);
-        }
+        mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
     }
 
     @Override
@@ -205,11 +199,6 @@
         if (mBackAnimation != null && mBackToLauncherCallback != null) {
             setBackToLauncherCallback(mBackToLauncherCallback);
         }
-
-        if (mPendingSetNavButtonAlpha != null) {
-            mPendingSetNavButtonAlpha.run();
-            mPendingSetNavButtonAlpha = null;
-        }
     }
 
     public void clearProxy() {
@@ -272,31 +261,6 @@
         }
     }
 
-    public float getLastNavButtonAlpha() {
-        return mLastNavButtonAlpha;
-    }
-
-    @Override
-    public void setNavBarButtonAlpha(float alpha, boolean animate) {
-        boolean changed = Float.compare(alpha, mLastNavButtonAlpha) != 0
-                || animate != mLastNavButtonAnimate
-                || !mHasNavButtonAlphaBeenSet;
-        if (changed) {
-            if (mSystemUiProxy == null) {
-                mPendingSetNavButtonAlpha = () -> setNavBarButtonAlpha(alpha, animate);
-            } else {
-                mLastNavButtonAlpha = alpha;
-                mLastNavButtonAnimate = animate;
-                mHasNavButtonAlphaBeenSet = true;
-                try {
-                    mSystemUiProxy.setNavBarButtonAlpha(alpha, animate);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed call setNavBarButtonAlpha", e);
-                }
-            }
-        }
-    }
-
     @Override
     public void onStatusBarMotionEvent(MotionEvent event) {
         if (mSystemUiProxy != null) {
@@ -481,12 +445,20 @@
      * Sets the shelf height.
      */
     public void setShelfHeight(boolean visible, int shelfHeight) {
+        Message.obtain(mAsyncHandler, MSG_SET_SHELF_HEIGHT,
+                visible ? 1 : 0 , shelfHeight).sendToTarget();
+    }
+
+    @WorkerThread
+    private void setShelfHeightAsync(int visibleInt, int shelfHeight) {
+        boolean visible = visibleInt != 0;
         boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
-        if (mPip != null && changed) {
+        IPip pip = mPip;
+        if (pip != null && changed) {
             mLastShelfVisible = visible;
             mLastShelfHeight = shelfHeight;
             try {
-                mPip.setShelfHeight(visible, shelfHeight);
+                pip.setShelfHeight(visible, shelfHeight);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setShelfHeight visible: " + visible
                         + " height: " + shelfHeight, e);
@@ -509,11 +481,12 @@
     }
 
     public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
-            PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) {
+            PictureInPictureParams pictureInPictureParams, int launcherRotation,
+            Rect hotseatKeepClearArea) {
         if (mPip != null) {
             try {
                 return mPip.startSwipePipToHome(componentName, activityInfo,
-                        pictureInPictureParams, launcherRotation, shelfHeight);
+                        pictureInPictureParams, launcherRotation, hotseatKeepClearArea);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startSwipePipToHome", e);
             }
@@ -602,7 +575,21 @@
                 mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
                         taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startTasksWithLegacyTransition");
+                Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition");
+            }
+        }
+    }
+
+    public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, int taskId,
+            Bundle mainOptions, Bundle sideOptions,
+            @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
+            RemoteAnimationAdapter adapter) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId,
+                        mainOptions, sideOptions, sidePosition, splitRatio, adapter);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startShortcutAndTaskWithLegacyTransition");
             }
         }
     }
@@ -880,12 +867,21 @@
         if (mRecentTasks != null
                 && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
             try {
-                return new ArrayList<ActivityManager.RunningTaskInfo>(
-                        Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
+                return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call getRunningTasks", e);
             }
         }
         return new ArrayList<>();
     }
+
+    private boolean handleMessageAsync(Message msg) {
+        switch (msg.what) {
+            case MSG_SET_SHELF_HEIGHT:
+                setShelfHeightAsync(msg.arg1, msg.arg2);
+                return true;
+        }
+
+        return false;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index a809c9c..cc7a356 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -36,14 +36,13 @@
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.launcher3.statehandlers.DepthController.STATE_DEPTH;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.PendingIntent;
@@ -195,10 +194,10 @@
 
         int taskIndex = recentsView.indexOfChild(v);
         Context context = v.getContext();
-        DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
+        BaseActivity baseActivity = BaseActivity.fromContext(context);
+        DeviceProfile dp = baseActivity.getDeviceProfile();
         boolean showAsGrid = dp.isTablet;
-        boolean parallaxCenterAndAdjacentTask =
-                taskIndex != recentsView.getCurrentPage() && !showAsGrid;
+        boolean parallaxCenterAndAdjacentTask = taskIndex != recentsView.getCurrentPage();
         int taskRectTranslationPrimary = recentsView.getScrollOffset(taskIndex);
         int taskRectTranslationSecondary = showAsGrid ? (int) v.getGridTranslationY() : 0;
 
@@ -368,7 +367,7 @@
         });
 
         if (depthController != null) {
-            out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(context),
+            out.setFloat(depthController, STATE_DEPTH, BACKGROUND_APP.getDepth(baseActivity),
                     TOUCH_RESPONSE_INTERPOLATOR);
         }
     }
@@ -602,11 +601,7 @@
             if (raController != null) {
                 raController.setWillFinishToHome(false);
             }
-            Context context = v.getContext();
-            DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
-            launcherAnim = dp.isTablet
-                    ? ObjectAnimator.ofFloat(recentsView, RecentsView.CONTENT_ALPHA, 0)
-                    : recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
+            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
             launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
             launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ba4f549..040c55b 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -82,6 +82,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.ViewCapture;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
 import com.android.quickstep.inputconsumers.AssistantInputConsumer;
@@ -461,7 +462,8 @@
         mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
         mOverviewCommandHelper = new OverviewCommandHelper(this,
                 mOverviewComponentObserver, mTaskAnimationManager);
-        mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
+        mResetGestureInputConsumer = new ResetGestureInputConsumer(
+                mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
         mInputConsumer.registerInputConsumer();
         onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
@@ -1213,6 +1215,8 @@
                 createdOverviewActivity.getDeviceProfile().dump(this, "", pw);
             }
             mTaskbarManager.dumpLogs("", pw);
+
+            ViewCapture.INSTANCE.get(this).dump(pw, fd);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
index 3e01ed0..8a87f63 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
@@ -22,7 +22,7 @@
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.util.NavBarPosition;
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index eb739a6..7c96bf8 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -227,6 +227,11 @@
     }
 
     @Override
+    public void onStateTransitionFailed(RecentsState toState) {
+        reset();
+    }
+
+    @Override
     public void onStateTransitionComplete(RecentsState finalState) {
         if (finalState == HOME) {
             // Clean-up logic that occurs when recents is no longer in use/visible.
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
index d34b40b..349f4d2 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
@@ -17,18 +17,25 @@
 
 import android.view.MotionEvent;
 
+import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.TaskAnimationManager;
 
+import java.util.function.Supplier;
+
 /**
  * A NO_OP input consumer which also resets any pending gesture
  */
 public class ResetGestureInputConsumer implements InputConsumer {
 
     private final TaskAnimationManager mTaskAnimationManager;
+    private final Supplier<TaskbarActivityContext> mActivityContextSupplier;
 
-    public ResetGestureInputConsumer(TaskAnimationManager taskAnimationManager) {
+    public ResetGestureInputConsumer(
+            TaskAnimationManager taskAnimationManager,
+            Supplier<TaskbarActivityContext> activityContextSupplier) {
         mTaskAnimationManager = taskAnimationManager;
+        mActivityContextSupplier = activityContextSupplier;
     }
 
     @Override
@@ -40,7 +47,9 @@
     public void onMotionEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN
                 && mTaskAnimationManager.isRecentsAnimationRunning()) {
-            mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
+            TaskbarActivityContext tac = mActivityContextSupplier.get();
+            mTaskAnimationManager.finishRunningRecentsAnimation(
+                    /* toHome= */ tac != null && !tac.isInApp());
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 4badf30..e7bf7e2 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -44,10 +44,10 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
-import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 99553e8..2ccdfa3 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -46,8 +46,8 @@
 import com.android.launcher3.model.DeviceGridState;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.SettingsCache;
 
 import org.xmlpull.v1.XmlPullParser;
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 5e41259..d2d1233 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -47,7 +47,19 @@
         STATE_GESTURE_STARTED, STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELLED,
         STATE_END_TARGET_ANIMATION_FINISHED, STATE_RECENTS_SCROLLING_FINISHED,
         STATE_CAPTURE_SCREENSHOT, STATE_SCREENSHOT_CAPTURED, STATE_HANDLER_INVALIDATED,
-        STATE_RECENTS_ANIMATION_CANCELED
+        STATE_RECENTS_ANIMATION_CANCELED, STATE_LAUNCHER_DRAWN(true, false);
+
+        public final boolean mLogEvent;
+        public final boolean mTrackEvent;
+
+        GestureEvent() {
+            this(false, true);
+        }
+
+        GestureEvent(boolean logEvent, boolean trackEvent) {
+            mLogEvent = logEvent;
+            mTrackEvent = trackEvent;
+        }
     }
 
     private ActiveGestureErrorDetector() {}
@@ -180,6 +192,7 @@
                     case STATE_END_TARGET_ANIMATION_FINISHED:
                     case STATE_CAPTURE_SCREENSHOT:
                     case STATE_HANDLER_INVALIDATED:
+                    case STATE_LAUNCHER_DRAWN:
                     default:
                         // No-Op
                 }
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 4030630..29ae9a1 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -24,6 +24,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.systemui.shared.system.BlurUtils;
 
 /**
@@ -31,7 +32,7 @@
  */
 public class BaseDepthController {
 
-    public static final FloatProperty<BaseDepthController> DEPTH =
+    private static final FloatProperty<BaseDepthController> DEPTH =
             new FloatProperty<BaseDepthController>("depth") {
                 @Override
                 public void setValue(BaseDepthController depthController, float depth) {
@@ -44,6 +45,19 @@
                 }
             };
 
+    private static final MultiPropertyFactory<BaseDepthController> DEPTH_PROPERTY_FACTORY =
+            new MultiPropertyFactory<>("depthProperty", DEPTH, Float::max);
+
+    private static final int DEPTH_INDEX_STATE_TRANSITION = 1;
+    private static final int DEPTH_INDEX_WIDGET = 2;
+
+    /** Property to set the depth for state transition. */
+    public static final FloatProperty<BaseDepthController> STATE_DEPTH =
+            DEPTH_PROPERTY_FACTORY.get(DEPTH_INDEX_STATE_TRANSITION);
+    /** Property to set the depth for widget picker. */
+    public static final FloatProperty<BaseDepthController> WIDGET_DEPTH =
+            DEPTH_PROPERTY_FACTORY.get(DEPTH_INDEX_WIDGET);
+
     protected final Launcher mLauncher;
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index d0856be..f7136a5 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -23,7 +23,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.LauncherActivityInterface;
 
 public class LayoutUtils {
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
index 527a6d2..59c8263 100644
--- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java
+++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
@@ -15,12 +15,12 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
 
 import android.view.Surface;
 
 import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.NavigationMode;
 
 /**
  * Utility class to check nav bar position.
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index fc44b99..e928b27 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -21,7 +21,7 @@
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
 
 import android.content.SharedPreferences;
 
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 8f32214..7efb1a5 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -31,16 +31,20 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.RemoteAnimationAdapter;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.testing.TestLogging;
@@ -66,6 +70,7 @@
  * and is in the process of either a) selecting a second app or b) exiting intention to invoke split
  */
 public class SplitSelectStateController {
+    private static final String TAG = "SplitSelectStateCtor";
 
     private final Context mContext;
     private final Handler mHandler;
@@ -196,7 +201,7 @@
                     null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
                     new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
                             ActivityThread.currentActivityThread().getApplicationThread()));
-            // TODO: handle intent + task with shell transition
+            // TODO(b/237635859): handle intent/shortcut + task with shell transition
         } else {
             RemoteSplitLaunchAnimationRunner animationRunner =
                     new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2,
@@ -215,9 +220,17 @@
                         taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
                         splitRatio, adapter);
             } else {
-                mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
-                        fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
-                        stagePosition, splitRatio, adapter);
+                final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent,
+                        taskPendingIntent.getCreatorUserHandle());
+                if (shortcutInfo != null) {
+                    mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId2,
+                            mainOpts.toBundle(), null /* sideOptions */, stagePosition, splitRatio,
+                            adapter);
+                } else {
+                    mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
+                            fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
+                            stagePosition, splitRatio, adapter);
+                }
             }
         }
     }
@@ -230,6 +243,28 @@
         this.mRecentsAnimationRunning = running;
     }
 
+    @Nullable
+    private ShortcutInfo getShortcutInfo(Intent intent, UserHandle userHandle) {
+        if (intent == null || intent.getPackage() == null) {
+            return null;
+        }
+
+        final String shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID);
+        if (shortcutId == null) {
+            return null;
+        }
+
+        try {
+            final Context context = mContext.createPackageContextAsUser(
+                    intent.getPackage(), 0 /* flags */, userHandle);
+            return new ShortcutInfo.Builder(context, shortcutId).build();
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Failed to create a ShortcutInfo for " + intent.getPackage());
+        }
+
+        return null;
+    }
+
     /**
      * Requires Shell Transitions
      */
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index d37300c..0a49008 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -102,7 +102,7 @@
     private boolean mLayoutValid = false;
     private int mOrientationStateId;
     private SplitBounds mSplitBounds;
-    private boolean mDrawsBelowRecents;
+    private Boolean mDrawsBelowRecents = null;
     private boolean mIsGridTask;
     private int mTaskRectTranslationX;
     private int mTaskRectTranslationY;
@@ -391,7 +391,8 @@
                 .withWindowCrop(mTmpCropRect)
                 .withCornerRadius(getCurrentCornerRadius());
 
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+        // If mDrawsBelowRecents is unset, no reordering will be enforced.
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mDrawsBelowRecents != null) {
             // In legacy transitions, the animation leashes remain in same hierarchy in the
             // TaskDisplayArea, so we don't want to bump the layer too high otherwise it will
             // conflict with layers that WM core positions (ie. the input consumers).  For shell
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index 7a66ea0..c3bf041 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -14,6 +14,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -49,6 +50,29 @@
  */
 public class FloatingTaskView extends FrameLayout {
 
+    public static final FloatProperty<FloatingTaskView> PRIMARY_TRANSLATE_OFFSCREEN =
+            new FloatProperty<FloatingTaskView>("floatingTaskPrimaryTranslateOffscreen") {
+        @Override
+        public void setValue(FloatingTaskView view, float translation) {
+            ((RecentsView) view.mActivity.getOverviewPanel()).getPagedOrientationHandler()
+                    .setFloatingTaskPrimaryTranslation(
+                            view,
+                            translation,
+                            view.mActivity.getDeviceProfile()
+                    );
+        }
+
+        @Override
+        public Float get(FloatingTaskView view) {
+            return ((RecentsView) view.mActivity.getOverviewPanel())
+                    .getPagedOrientationHandler()
+                    .getFloatingTaskPrimaryTranslation(
+                            view,
+                            view.mActivity.getDeviceProfile()
+                    );
+        }
+    };
+
     private FloatingTaskThumbnailView mThumbnailView;
     private SplitPlaceholderView mSplitPlaceholderView;
     private RectF mStartingPosition;
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index f718be8..3920c56 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,6 +1,6 @@
 package com.android.quickstep.views;
 
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.AbstractFloatingView.getAnyView;
 import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -14,6 +14,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -163,6 +164,21 @@
         }
     }
 
+    @Override
+    protected boolean showTaskMenuWithContainer(IconView iconView) {
+        boolean showedTaskMenu = super.showTaskMenuWithContainer(iconView);
+        if (iconView == mIconView2 && showedTaskMenu && !isGridTask()) {
+            // Adjust the position of the secondary task's menu view (only on phones)
+            TaskMenuView taskMenuView = getAnyView(mActivity, AbstractFloatingView.TYPE_TASK_MENU);
+            DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+            getRecentsView().getPagedOrientationHandler()
+                    .setSecondaryTaskMenuPosition(mSplitBoundsConfig, this,
+                            deviceProfile, mTaskIdAttributeContainer[0].getThumbnailView(),
+                            taskMenuView);
+        }
+        return showedTaskMenu;
+    }
+
     @Nullable
     @Override
     public RunnableList launchTaskAnimated() {
@@ -251,8 +267,7 @@
 
     @Override
     public void setOverlayEnabled(boolean overlayEnabled) {
-        super.setOverlayEnabled(overlayEnabled);
-        mSnapshotView2.setOverlayEnabled(overlayEnabled);
+        // Intentional no-op to prevent setting smart actions overlay on thumbnails
     }
 
     @Override
@@ -318,7 +333,12 @@
     @Override
     protected void applyThumbnailSplashAlpha() {
         super.applyThumbnailSplashAlpha();
-        mSnapshotView2.setSplashAlpha(
-                Utilities.mapToRange(mTaskThumbnailSplashAlpha, 0f, 1f, 1f, 0f, LINEAR));
+        mSnapshotView2.setSplashAlpha(mTaskThumbnailSplashAlpha);
+    }
+
+    @Override
+    void setThumbnailVisibility(int visibility) {
+        super.setThumbnailVisibility(visibility);
+        mSnapshotView2.setVisibility(visibility);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 6a33d36..de7ccad 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -120,6 +120,11 @@
     }
 
     @Override
+    public void onStateTransitionFailed(LauncherState toState) {
+        reset();
+    }
+
+    @Override
     public void onStateTransitionComplete(LauncherState finalState) {
         if (finalState == NORMAL || finalState == SPRING_LOADED) {
             // Clean-up logic that occurs when recents is no longer in use/visible.
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 62ec0ef..08a17c4 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -32,9 +32,9 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
 import com.android.quickstep.util.LayoutUtils;
 
@@ -53,7 +53,9 @@
             HIDDEN_NON_ZERO_ROTATION,
             HIDDEN_NO_TASKS,
             HIDDEN_NO_RECENTS,
-            HIDDEN_SPLIT_SCREEN})
+            HIDDEN_SPLIT_SCREEN,
+            HIDDEN_SPLIT_SELECT_ACTIVE
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActionsHiddenFlags { }
 
@@ -61,6 +63,7 @@
     public static final int HIDDEN_NO_TASKS = 1 << 1;
     public static final int HIDDEN_NO_RECENTS = 1 << 2;
     public static final int HIDDEN_SPLIT_SCREEN = 1 << 3;
+    public static final int HIDDEN_SPLIT_SELECT_ACTIVE = 1 << 4;
 
     @IntDef(flag = true, value = {
             DISABLED_SCROLLING,
@@ -79,6 +82,11 @@
     private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
     private static final int INDEX_SHARE_TARGET_ALPHA = 4;
 
+    public @interface SplitButtonDisabledFlags { }
+
+    public static final int FLAG_IS_NOT_TABLET = 1 << 0;
+    public static final int FLAG_SINGLE_TASK = 1 << 1;
+
     private MultiValueAlpha mMultiValueAlpha;
     private Button mSplitButton;
 
@@ -88,6 +96,9 @@
     @ActionsDisabledFlags
     protected int mDisabledFlags;
 
+    @SplitButtonDisabledFlags
+    private int mSplitButtonDisabledFlags;
+
     @Nullable
     protected T mCallbacks;
 
@@ -182,6 +193,20 @@
         LayoutUtils.setViewEnabled(this, isEnabled);
     }
 
+    /**
+     * Updates the proper flags to indicate whether the "Split screen" button should be enabled.
+     *
+     * @param flag          The flag to update.
+     * @param enable        Whether to enable the disable flag: True will cause view to be disabled.
+     */
+    public void updateSplitButtonFlags(@SplitButtonDisabledFlags int flag, boolean enable) {
+        if (enable) {
+            mSplitButtonDisabledFlags |= flag;
+        } else {
+            mSplitButtonDisabledFlags &= ~flag;
+        }
+    }
+
     public AlphaProperty getContentAlpha() {
         return mMultiValueAlpha.getProperty(INDEX_CONTENT_ALPHA);
     }
@@ -263,12 +288,17 @@
                 0, 0, 0);
     }
 
-    public void setSplitButtonVisible(boolean visible) {
+    /**
+     * Shows/hides the "Split" button based on the status of mHiddenFlags.
+     */
+    public void updateSplitButtonVisibility() {
         if (mSplitButton == null) {
             return;
         }
-
-        mSplitButton.setVisibility(visible ? VISIBLE : GONE);
-        findViewById(R.id.action_split_space).setVisibility(visible ? VISIBLE : GONE);
+        boolean shouldBeVisible = mSplitButtonDisabledFlags == 0
+                // and neither of these flags are active
+                && (mHiddenFlags & (HIDDEN_SPLIT_SCREEN | HIDDEN_SPLIT_SELECT_ACTIVE)) == 0;
+        mSplitButton.setVisibility(shouldBeVisible ? VISIBLE : GONE);
+        findViewById(R.id.action_split_space).setVisibility(shouldBeVisible ? VISIBLE : GONE);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 70b2958..38e5acb 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -43,7 +43,7 @@
 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.statehandlers.DepthController.DEPTH;
+import static com.android.launcher3.statehandlers.DepthController.STATE_DEPTH;
 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;
@@ -52,10 +52,13 @@
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
 import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
+import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET;
+import static com.android.quickstep.views.OverviewActionsView.FLAG_SINGLE_TASK;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_SPLIT_SCREEN;
+import static com.android.quickstep.views.OverviewActionsView.HIDDEN_SPLIT_SELECT_ACTIVE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -399,39 +402,6 @@
                 }
             };
 
-    public static final FloatProperty<RecentsView> FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN =
-            new FloatProperty<RecentsView>("firstFloatingTaskTranslateOffscreen") {
-                @Override
-                public void setValue(RecentsView view, float translation) {
-                    view.getPagedOrientationHandler().setFloatingTaskPrimaryTranslation(
-                            view.mFirstFloatingTaskView,
-                            translation,
-                            view.mActivity.getDeviceProfile()
-                    );
-                }
-
-                @Override
-                public Float get(RecentsView view) {
-                    return view.getPagedOrientationHandler().getFloatingTaskPrimaryTranslation(
-                            view.mFirstFloatingTaskView,
-                            view.mActivity.getDeviceProfile()
-                    );
-                }
-            };
-
-    public static final FloatProperty<RecentsView> SPLIT_INSTRUCTIONS_FADE =
-            new FloatProperty<RecentsView>("splitInstructionsFade") {
-                @Override
-                public void setValue(RecentsView view, float fade) {
-                    view.mSplitInstructionsView.setAlpha(1 - fade);
-                }
-
-                @Override
-                public Float get(RecentsView view) {
-                    return 1 - view.mSplitInstructionsView.getAlpha();
-                }
-            };
-
     // OverScroll constants
     private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
 
@@ -2837,7 +2807,11 @@
 
         RectF startingTaskRect = new RectF();
         if (mSplitHiddenTaskView != null) {
-            mSplitHiddenTaskView.setVisibility(INVISIBLE);
+            // Split staging is initiated, hide the original TaskView thumbnail.
+            // Toggled back on in resetFromSplitSelectionState().
+            mSplitHiddenTaskView.setThumbnailVisibility(INVISIBLE);
+            anim.addFloat(mSplitHiddenTaskView, TaskView.ICON_ALPHA, 1, 0,
+                    clampToProgress(LINEAR, 0, 0.167f));
             mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
                     mSplitHiddenTaskView.getThumbnail(),
                     mSplitHiddenTaskView.getThumbnail().getThumbnail(),
@@ -2865,6 +2839,8 @@
                 InteractionJankMonitorWrapper.end(
                         InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
             } else {
+                // If transition to split select was interrupted, clean up to prevent glitches
+                resetFromSplitSelectionState();
                 InteractionJankMonitorWrapper.cancel(
                         InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
             }
@@ -3424,11 +3400,11 @@
     private void updateCurrentTaskActionsVisibility() {
         boolean isCurrentSplit = getCurrentPageTaskView() instanceof GroupedTaskView;
         mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SCREEN, isCurrentSplit);
-        if (isCurrentSplit) {
-            return;
-        }
-        mActionsView.setSplitButtonVisible(
-                mActivity.getDeviceProfile().isTablet && getTaskViewCount() > 1);
+        mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SELECT_ACTIVE, isSplitSelectionActive());
+        mActionsView.updateSplitButtonFlags(FLAG_IS_NOT_TABLET,
+                !mActivity.getDeviceProfile().isTablet);
+        mActionsView.updateSplitButtonFlags(FLAG_SINGLE_TASK, getTaskViewCount() <= 1);
+        mActionsView.updateSplitButtonVisibility();
     }
 
     /**
@@ -4232,7 +4208,9 @@
         resetTaskVisuals();
         mSplitHiddenTaskViewIndex = -1;
         if (mSplitHiddenTaskView != null) {
-            mSplitHiddenTaskView.setVisibility(VISIBLE);
+            // Toggle thumbnail visibility back on (turned off in
+            // createInitialSplitSelectAnimation()).
+            mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE);
             mSplitHiddenTaskView = null;
         }
     }
@@ -4352,25 +4330,27 @@
 
         int taskIndex = indexOfChild(tv);
         int centerTaskIndex = getCurrentPage();
-        boolean launchingCenterTask = taskIndex == centerTaskIndex;
 
         float toScale = getMaxScaleForFullScreen();
-        RecentsView recentsView = tv.getRecentsView();
+        boolean showAsGrid = showAsGrid();
+        boolean launchingCenterTask = showAsGrid
+                ? tv.isFocusedTask() && isTaskViewFullyVisible(tv)
+                : taskIndex == centerTaskIndex;
         if (launchingCenterTask) {
-            anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale));
-            anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1));
-        } else {
+            anim.play(ObjectAnimator.ofFloat(this, RECENTS_SCALE_PROPERTY, toScale));
+            anim.play(ObjectAnimator.ofFloat(this, FULLSCREEN_PROGRESS, 1));
+        } else if (!showAsGrid) {
             // We are launching an adjacent task, so parallax the center and other adjacent task.
             float displacementX = tv.getWidth() * (toScale - 1f);
             float primaryTranslation = mIsRtl ? -displacementX : displacementX;
             anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex),
                     mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation));
-            int runningTaskIndex = recentsView.getRunningTaskIndex();
+            int runningTaskIndex = getRunningTaskIndex();
             if (ENABLE_QUICKSTEP_LIVE_TILE.get()
                     && runningTaskIndex != -1
                     && runningTaskIndex != taskIndex
-                    && recentsView.getRemoteTargetHandles() != null) {
-                for (RemoteTargetHandle remoteHandle : recentsView.getRemoteTargetHandles()) {
+                    && getRemoteTargetHandles() != null) {
+                for (RemoteTargetHandle remoteHandle : getRemoteTargetHandles()) {
                     anim.play(ObjectAnimator.ofFloat(
                             remoteHandle.getTaskViewSimulator().taskPrimaryTranslation,
                             AnimatedFloat.VALUE,
@@ -4390,7 +4370,7 @@
                         properties));
             }
         }
-        anim.play(ObjectAnimator.ofFloat(recentsView, TASK_THUMBNAIL_SPLASH_ALPHA, 0, 1));
+        anim.play(ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, 0, 1));
         return anim;
     }
 
@@ -4446,7 +4426,7 @@
 
         DepthController depthController = getDepthController();
         if (depthController != null) {
-            ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController, DEPTH,
+            ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController, STATE_DEPTH,
                     BACKGROUND_APP.getDepth(mActivity));
             anim.play(depthAnimator);
         }
@@ -5344,6 +5324,11 @@
         return mFirstFloatingTaskView;
     }
 
+    @Nullable
+    public SplitInstructionsView getSplitInstructionsView() {
+        return mSplitInstructionsView;
+    }
+
     /** Update the current activity locus id to show the enabled state of Overview */
     public void updateLocusId() {
         String locusId = "Overview";
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 7d94505..bcaa462 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -16,7 +16,7 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index c1711d1..6815745 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,8 +16,6 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
 import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
 
 import android.animation.Animator;
@@ -156,23 +154,6 @@
                 mTaskContainer.getThumbnailView(), overscrollShift, deviceProfile));
         setY(pagedOrientationHandler.getTaskMenuY(
                 adjustedY, mTaskContainer.getThumbnailView(), overscrollShift));
-
-        // TODO(b/193432925) temporary menu placement for split screen task menus
-        TaskIdAttributeContainer[] taskIdAttributeContainers =
-                mTaskView.getTaskIdAttributeContainers();
-        if (taskIdAttributeContainers[0].getStagePosition() != STAGE_POSITION_UNDEFINED) {
-            if (mTaskContainer.getStagePosition() != STAGE_POSITION_BOTTOM_OR_RIGHT) {
-                return;
-            }
-            Rect r = new Rect();
-            mTaskContainer.getThumbnailView().getBoundsOnScreen(r);
-            if (deviceProfile.isLandscape) {
-                setX(r.left);
-            } else {
-                setY(r.top);
-
-            }
-        }
     }
 
     public void onRotationChanged() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 1e78377..26bae35 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -323,6 +323,19 @@
                 }
             };
 
+    public static final FloatProperty<TaskView> ICON_ALPHA =
+            new FloatProperty<TaskView>("iconAlpha") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.mIconView.setAlpha(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mIconView.getAlpha();
+                }
+            };
+
     @Nullable
     protected Task mTask;
     protected TaskThumbnailView mSnapshotView;
@@ -676,12 +689,13 @@
     /**
      * Launch of the current task (both live and inactive tasks) with an animation.
      */
-    public void launchTasks() {
+    public RunnableList launchTasks() {
         RecentsView recentsView = getRecentsView();
         RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
+        RunnableList runnableList = new RunnableList();
         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask() && remoteTargetHandles != null) {
             if (!mIsClickableAsLiveTile) {
-                return;
+                return runnableList;
             }
 
             mIsClickableAsLiveTile = false;
@@ -708,7 +722,7 @@
                 // here, try to launch the task as a non live tile task.
                 launchTaskAnimated();
                 mIsClickableAsLiveTile = true;
-                return;
+                return runnableList;
             }
 
             AnimatorSet anim = new AnimatorSet();
@@ -733,13 +747,24 @@
                         launchTaskAnimated();
                     }
                     mIsClickableAsLiveTile = true;
+                    runEndCallback();
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    runEndCallback();
+                }
+
+                private void runEndCallback() {
+                    runnableList.executeAllAndDestroy();
                 }
             });
             anim.start();
             recentsView.onTaskLaunchedInLiveTileMode();
         } else {
-            launchTaskAnimated();
+            return launchTaskAnimated();
         }
+        return runnableList;
     }
 
     /**
@@ -1064,7 +1089,7 @@
     }
 
     protected void applyThumbnailSplashAlpha() {
-        mSnapshotView.setSplashAlpha(Utilities.boundToRange(mTaskThumbnailSplashAlpha, 0f, 1f));
+        mSnapshotView.setSplashAlpha(mTaskThumbnailSplashAlpha);
     }
 
     private void setSplitSelectTranslationX(float x) {
@@ -1488,6 +1513,10 @@
         return display != null ? display.getDisplayId() : DEFAULT_DISPLAY;
     }
 
+    void setThumbnailVisibility(int visibility) {
+        mSnapshotView.setVisibility(visibility);
+    }
+
     /**
      * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
      */
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt
deleted file mode 100644
index 9977207..0000000
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt
+++ /dev/null
@@ -1,1479 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.DeviceProfileBaseTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for DeviceProfile.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTest : DeviceProfileBaseTest() {
-
-    @Test
-    fun phonePortrait3Button() {
-        initializeVarsForPhone(isGestureMode = false)
-        val dp = newDP()
-
-        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" +
-                "\tisScalableGrid:true\n" +
-                "\tinv.numRows: 5\n" +
-                "\tinv.numColumns: 4\n" +
-                "\tinv.numSearchContainerColumns: 4\n" +
-                "\tminCellSize: PointF(80.0, 104.0)dp\n" +
-                "\tcellWidthPx: 210.0px (80.0dp)\n" +
-                "\tcellHeightPx: 272.0px (103.61905dp)\n" +
-                "\tgetCellSize().x: 210.0px (80.0dp)\n" +
-                "\tgetCellSize().y: 272.0px (103.61905dp)\n" +
-                "\tcellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tcellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tcellLayoutPaddingPx.left: 28.0px (10.666667dp)\n" +
-                "\tcellLayoutPaddingPx.top: 28.0px (10.666667dp)\n" +
-                "\tcellLayoutPaddingPx.right: 28.0px (10.666667dp)\n" +
-                "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" +
-                "\ticonSizePx: 157.0px (59.809525dp)\n" +
-                "\ticonTextSizePx: 36.0px (13.714286dp)\n" +
-                "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
-                "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tfolderCellHeightPx: 247.0px (94.09524dp)\n" +
-                "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
-                "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tfolderChildDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 42.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 146.0px (55.61905dp)\n" +
-                "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
-                "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
-                "\tallAppsIconSizePx: 157.0px (59.809525dp)\n" +
-                "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
-                "\tallAppsCellHeightPx: 314.0px (119.61905dp)\n" +
-                "\tallAppsCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                "\tnumShownAllAppsColumns: 4\n" +
-                "\tallAppsLeftRightPadding: 57.0px (21.714285dp)\n" +
-                "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
-                "\thotseatBarSizePx: 511.0px (194.66667dp)\n" +
-                "\tinv.hotseatColumnSpan: 4\n" +
-                "\thotseatCellHeightPx: 177.0px (67.42857dp)\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: 74.0px (28.190475dp)\n" +
-                "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 334.0px (127.2381dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 83.0px (31.619047dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 83.0px (31.619047dp)\n" +
-                "\tnumShownHotseatIcons: 4\n" +
-                "\thotseatBorderSpace: 95.0px (36.190475dp)\n" +
-                "\tisQsbInline: false\n" +
-                "\thotseatQsbWidth: 913.0px (347.8095dp)\n" +
-                "\tisTaskbarPresent:false\n" +
-                "\tisTaskbarPresentInApps:false\n" +
-                "\ttaskbarSize: 0.0px (0.0dp)\n" +
-                "\tdesiredWorkspaceHorizontalMarginPx: 57.0px (21.714285dp)\n" +
-                "\tworkspacePadding.left: 29.0px (11.047619dp)\n" +
-                "\tworkspacePadding.top: 67.0px (25.52381dp)\n" +
-                "\tworkspacePadding.right: 29.0px (11.047619dp)\n" +
-                "\tworkspacePadding.bottom: 504.0px (192.0dp)\n" +
-                "\ticonScale: 0.9981516px (0.38024822dp)\n" +
-                "\tcellScaleToFit : 0.9981516px (0.38024822dp)\n" +
-                "\textraSpace: 211.0px (80.38095dp)\n" +
-                "\tunscaled extraSpace: 211.39073px (80.5298dp)\n" +
-                "\tmaxEmptySpace: 315.0px (120.0dp)\n" +
-                "\tworkspaceTopPadding: 95.0px (36.190475dp)\n" +
-                "\tworkspaceBottomPadding: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskMarginPx: 42.0px (16.0dp)\n" +
-                "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 168.0px (64.0dp)\n" +
-                "\toverviewActionsTopMarginPx: 63.0px (24.0dp)\n" +
-                "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewPageSpacing: 42.0px (16.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(): 1689.0px (643.4286dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.81892747px (0.31197238dp)\n" +
-                "\tgetCellLayoutHeight(): 1585.0px (603.8095dp)\n" +
-                "\tgetCellLayoutWidth(): 1022.0px (389.33334dp)\n")
-    }
-
-    @Test
-    fun phonePortrait() {
-        initializeVarsForPhone()
-        val dp = newDP()
-
-        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" +
-                "\tisScalableGrid:true\n" +
-                "\tinv.numRows: 5\n" +
-                "\tinv.numColumns: 4\n" +
-                "\tinv.numSearchContainerColumns: 4\n" +
-                "\tminCellSize: PointF(80.0, 104.0)dp\n" +
-                "\tcellWidthPx: 210.0px (80.0dp)\n" +
-                "\tcellHeightPx: 272.0px (103.61905dp)\n" +
-                "\tgetCellSize().x: 210.0px (80.0dp)\n" +
-                "\tgetCellSize().y: 272.0px (103.61905dp)\n" +
-                "\tcellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tcellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tcellLayoutPaddingPx.left: 28.0px (10.666667dp)\n" +
-                "\tcellLayoutPaddingPx.top: 28.0px (10.666667dp)\n" +
-                "\tcellLayoutPaddingPx.right: 28.0px (10.666667dp)\n" +
-                "\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" +
-                "\ticonSizePx: 157.0px (59.809525dp)\n" +
-                "\ticonTextSizePx: 36.0px (13.714286dp)\n" +
-                "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
-                "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tfolderCellHeightPx: 247.0px (94.09524dp)\n" +
-                "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
-                "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tfolderChildDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 42.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 146.0px (55.61905dp)\n" +
-                "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
-                "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
-                "\tallAppsIconSizePx: 157.0px (59.809525dp)\n" +
-                "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
-                "\tallAppsCellHeightPx: 314.0px (119.61905dp)\n" +
-                "\tallAppsCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                "\tnumShownAllAppsColumns: 4\n" +
-                "\tallAppsLeftRightPadding: 57.0px (21.714285dp)\n" +
-                "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
-                "\thotseatBarSizePx: 511.0px (194.66667dp)\n" +
-                "\tinv.hotseatColumnSpan: 4\n" +
-                "\thotseatCellHeightPx: 177.0px (67.42857dp)\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: 95.0px (36.190475dp)\n" +
-                "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 200.0px (76.190475dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 0.0px (0.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 334.0px (127.2381dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 83.0px (31.619047dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 83.0px (31.619047dp)\n" +
-                "\tnumShownHotseatIcons: 4\n" +
-                "\thotseatBorderSpace: 95.0px (36.190475dp)\n" +
-                "\tisQsbInline: false\n" +
-                "\thotseatQsbWidth: 913.0px (347.8095dp)\n" +
-                "\tisTaskbarPresent:false\n" +
-                "\tisTaskbarPresentInApps:false\n" +
-                "\ttaskbarSize: 0.0px (0.0dp)\n" +
-                "\tdesiredWorkspaceHorizontalMarginPx: 57.0px (21.714285dp)\n" +
-                "\tworkspacePadding.left: 29.0px (11.047619dp)\n" +
-                "\tworkspacePadding.top: 67.0px (25.52381dp)\n" +
-                "\tworkspacePadding.right: 29.0px (11.047619dp)\n" +
-                "\tworkspacePadding.bottom: 567.0px (216.0dp)\n" +
-                "\ticonScale: 0.9981516px (0.38024822dp)\n" +
-                "\tcellScaleToFit : 0.9981516px (0.38024822dp)\n" +
-                "\textraSpace: 211.0px (80.38095dp)\n" +
-                "\tunscaled extraSpace: 211.39073px (80.5298dp)\n" +
-                "\tmaxEmptySpace: 315.0px (120.0dp)\n" +
-                "\tworkspaceTopPadding: 95.0px (36.190475dp)\n" +
-                "\tworkspaceBottomPadding: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskMarginPx: 42.0px (16.0dp)\n" +
-                "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 168.0px (64.0dp)\n" +
-                "\toverviewActionsTopMarginPx: 63.0px (24.0dp)\n" +
-                "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewPageSpacing: 42.0px (16.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(): 1689.0px (643.4286dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.81892747px (0.31197238dp)\n" +
-                "\tgetCellLayoutHeight(): 1585.0px (603.8095dp)\n" +
-                "\tgetCellLayoutWidth(): 1022.0px (389.33334dp)\n")
-    }
-
-    @Test
-    fun tabletLandscape3Button() {
-        initializeVarsForTablet(isLandscape = true, isGestureMode = false)
-        val dp = newDP()
-        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" +
-                "\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: 32.0px (16.0dp)\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: 14.0px (7.0dp)\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: 16.0px (8.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 32.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 48.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 104.0px (52.0dp)\n" +
-                "\tallAppsShiftRange: 1496.0px (748.0dp)\n" +
-                "\tallAppsTopPadding: 104.0px (52.0dp)\n" +
-                "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
-                "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\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: 64.0px (32.0dp)\n" +
-                "\tallAppsLeftRightMargin: 380.0px (190.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: 705.0px (352.5dp)\n" +
-                "\thotseatQsbSpace: 64.0px (32.0dp)\n" +
-                "\thotseatQsbHeight: 126.0px (63.0dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: -8.0px (-4.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 73.0px (36.5dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 954.0px (477.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 705.0px (352.5dp)\n" +
-                "\tnumShownHotseatIcons: 6\n" +
-                "\thotseatBorderSpace: 36.0px (18.0dp)\n" +
-                "\tisQsbInline: true\n" +
-                "\thotseatQsbWidth: 619.0px (309.5dp)\n" +
-                "\tisTaskbarPresent:true\n" +
-                "\tisTaskbarPresentInApps:true\n" +
-                "\ttaskbarSize: 120.0px (60.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: 237.0px (118.5dp)\n" +
-                "\ticonScale: 1.0px (0.5dp)\n" +
-                "\tcellScaleToFit : 1.0px (0.5dp)\n" +
-                "\textraSpace: 104.0px (52.0dp)\n" +
-                "\tunscaled extraSpace: 104.0px (52.0dp)\n" +
-                "\tmaxEmptySpace: 200.0px (100.0dp)\n" +
-                "\tworkspaceTopPadding: 32.0px (16.0dp)\n" +
-                "\tworkspaceBottomPadding: 72.0px (36.0dp)\n" +
-                "\toverviewTaskMarginPx: 32.0px (16.0dp)\n" +
-                "\toverviewTaskIconSizePx: 96.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 88.0px (44.0dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 88.0px (44.0dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 128.0px (64.0dp)\n" +
-                "\toverviewActionsTopMarginPx: 40.0px (20.0dp)\n" +
-                "\toverviewActionsHeight: 96.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 72.0px (36.0dp)\n" +
-                "\toverviewPageSpacing: 88.0px (44.0dp)\n" +
-                "\toverviewRowSpacing: 72.0px (36.0dp)\n" +
-                "\toverviewGridSideMargin: 128.0px (64.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.76250994px (0.38125497dp)\n" +
-                "\tgetCellLayoutHeight(): 1259.0px (629.5dp)\n" +
-                "\tgetCellLayoutWidth(): 2198.0px (1099.0dp)\n")
-    }
-
-    @Test
-    fun tabletLandscape() {
-        initializeVarsForTablet(isLandscape = true)
-        val dp = newDP()
-        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" +
-                "\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: 32.0px (16.0dp)\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: 14.0px (7.0dp)\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: 16.0px (8.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 32.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 48.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 104.0px (52.0dp)\n" +
-                "\tallAppsShiftRange: 1496.0px (748.0dp)\n" +
-                "\tallAppsTopPadding: 104.0px (52.0dp)\n" +
-                "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
-                "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\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: 64.0px (32.0dp)\n" +
-                "\tallAppsLeftRightMargin: 380.0px (190.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: 64.0px (32.0dp)\n" +
-                "\thotseatQsbHeight: 126.0px (63.0dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: -8.0px (-4.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 73.0px (36.5dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 1040.0px (520.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 300.0px (150.0dp)\n" +
-                "\tnumShownHotseatIcons: 6\n" +
-                "\thotseatBorderSpace: 100.0px (50.0dp)\n" +
-                "\tisQsbInline: true\n" +
-                "\thotseatQsbWidth: 640.0px (320.0dp)\n" +
-                "\tisTaskbarPresent:true\n" +
-                "\tisTaskbarPresentInApps:true\n" +
-                "\ttaskbarSize: 120.0px (60.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: 237.0px (118.5dp)\n" +
-                "\ticonScale: 1.0px (0.5dp)\n" +
-                "\tcellScaleToFit : 1.0px (0.5dp)\n" +
-                "\textraSpace: 104.0px (52.0dp)\n" +
-                "\tunscaled extraSpace: 104.0px (52.0dp)\n" +
-                "\tmaxEmptySpace: 200.0px (100.0dp)\n" +
-                "\tworkspaceTopPadding: 32.0px (16.0dp)\n" +
-                "\tworkspaceBottomPadding: 72.0px (36.0dp)\n" +
-                "\toverviewTaskMarginPx: 32.0px (16.0dp)\n" +
-                "\toverviewTaskIconSizePx: 96.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 88.0px (44.0dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 88.0px (44.0dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 128.0px (64.0dp)\n" +
-                "\toverviewActionsTopMarginPx: 40.0px (20.0dp)\n" +
-                "\toverviewActionsHeight: 96.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 72.0px (36.0dp)\n" +
-                "\toverviewPageSpacing: 88.0px (44.0dp)\n" +
-                "\toverviewRowSpacing: 72.0px (36.0dp)\n" +
-                "\toverviewGridSideMargin: 128.0px (64.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.76250994px (0.38125497dp)\n" +
-                "\tgetCellLayoutHeight(): 1259.0px (629.5dp)\n" +
-                "\tgetCellLayoutWidth(): 2198.0px (1099.0dp)\n")
-    }
-
-    @Test
-    fun tabletPortrait3Button() {
-        initializeVarsForTablet(isGestureMode = false)
-        val dp = newDP()
-        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" +
-                "\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: 14.0px (7.0dp)\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: 16.0px (8.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 32.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 48.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 704.0px (352.0dp)\n" +
-                "\tallAppsShiftRange: 1936.0px (968.0dp)\n" +
-                "\tallAppsTopPadding: 624.0px (312.0dp)\n" +
-                "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
-                "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\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: 56.0px (28.0dp)\n" +
-                "\tallAppsLeftRightMargin: 128.0px (64.0dp)\n" +
-                "\thotseatBarSizePx: 358.0px (179.0dp)\n" +
-                "\tinv.hotseatColumnSpan: 6\n" +
-                "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
-                "\thotseatBarBottomSpacePx: 72.0px (36.0dp)\n" +
-                "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarEndOffset: 558.0px (279.0dp)\n" +
-                "\thotseatQsbSpace: 64.0px (32.0dp)\n" +
-                "\thotseatQsbHeight: 126.0px (63.0dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 158.0px (79.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 150.0px (75.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 558.0px (279.0dp)\n" +
-                "\tnumShownHotseatIcons: 5\n" +
-                "\thotseatBorderSpace: 73.0px (36.5dp)\n" +
-                "\tisQsbInline: false\n" +
-                "\thotseatQsbWidth: 1300.0px (650.0dp)\n" +
-                "\tisTaskbarPresent:true\n" +
-                "\tisTaskbarPresentInApps:true\n" +
-                "\ttaskbarSize: 120.0px (60.0dp)\n" +
-                "\tdesiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)\n" +
-                "\tworkspacePadding.left: 36.0px (18.0dp)\n" +
-                "\tworkspacePadding.top: 87.0px (43.5dp)\n" +
-                "\tworkspacePadding.right: 36.0px (18.0dp)\n" +
-                "\tworkspacePadding.bottom: 513.0px (256.5dp)\n" +
-                "\ticonScale: 1.0px (0.5dp)\n" +
-                "\tcellScaleToFit : 1.0px (0.5dp)\n" +
-                "\textraSpace: 362.0px (181.0dp)\n" +
-                "\tunscaled extraSpace: 362.0px (181.0dp)\n" +
-                "\tmaxEmptySpace: 19998.0px (9999.0dp)\n" +
-                "\tworkspaceTopPadding: 159.0px (79.5dp)\n" +
-                "\tworkspaceBottomPadding: 203.0px (101.5dp)\n" +
-                "\toverviewTaskMarginPx: 32.0px (16.0dp)\n" +
-                "\toverviewTaskIconSizePx: 96.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 88.0px (44.0dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 88.0px (44.0dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 128.0px (64.0dp)\n" +
-                "\toverviewActionsTopMarginPx: 48.0px (24.0dp)\n" +
-                "\toverviewActionsHeight: 96.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 72.0px (36.0dp)\n" +
-                "\toverviewPageSpacing: 88.0px (44.0dp)\n" +
-                "\toverviewRowSpacing: 72.0px (36.0dp)\n" +
-                "\toverviewGridSideMargin: 128.0px (64.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(): 1986.0px (993.0dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.76616377px (0.38308188dp)\n" +
-                "\tgetCellLayoutHeight(): 1856.0px (928.0dp)\n" +
-                "\tgetCellLayoutWidth(): 1528.0px (764.0dp)\n")
-    }
-
-    @Test
-    fun tabletPortrait() {
-        initializeVarsForTablet()
-        val dp = newDP()
-        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" +
-                "\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: 14.0px (7.0dp)\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: 16.0px (8.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 32.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 32.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 32.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 48.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 704.0px (352.0dp)\n" +
-                "\tallAppsShiftRange: 1936.0px (968.0dp)\n" +
-                "\tallAppsTopPadding: 624.0px (312.0dp)\n" +
-                "\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
-                "\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\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: 56.0px (28.0dp)\n" +
-                "\tallAppsLeftRightMargin: 128.0px (64.0dp)\n" +
-                "\thotseatBarSizePx: 358.0px (179.0dp)\n" +
-                "\tinv.hotseatColumnSpan: 6\n" +
-                "\thotseatCellHeightPx: 135.0px (67.5dp)\n" +
-                "\thotseatBarBottomSpacePx: 72.0px (36.0dp)\n" +
-                "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                "\thotseatQsbSpace: 64.0px (32.0dp)\n" +
-                "\thotseatQsbHeight: 126.0px (63.0dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 158.0px (79.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 65.0px (32.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:true\n" +
-                "\tisTaskbarPresentInApps:true\n" +
-                "\ttaskbarSize: 120.0px (60.0dp)\n" +
-                "\tdesiredWorkspaceHorizontalMarginPx: 108.0px (54.0dp)\n" +
-                "\tworkspacePadding.left: 36.0px (18.0dp)\n" +
-                "\tworkspacePadding.top: 87.0px (43.5dp)\n" +
-                "\tworkspacePadding.right: 36.0px (18.0dp)\n" +
-                "\tworkspacePadding.bottom: 513.0px (256.5dp)\n" +
-                "\ticonScale: 1.0px (0.5dp)\n" +
-                "\tcellScaleToFit : 1.0px (0.5dp)\n" +
-                "\textraSpace: 362.0px (181.0dp)\n" +
-                "\tunscaled extraSpace: 362.0px (181.0dp)\n" +
-                "\tmaxEmptySpace: 19998.0px (9999.0dp)\n" +
-                "\tworkspaceTopPadding: 159.0px (79.5dp)\n" +
-                "\tworkspaceBottomPadding: 203.0px (101.5dp)\n" +
-                "\toverviewTaskMarginPx: 32.0px (16.0dp)\n" +
-                "\toverviewTaskIconSizePx: 96.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 88.0px (44.0dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 88.0px (44.0dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 128.0px (64.0dp)\n" +
-                "\toverviewActionsTopMarginPx: 48.0px (24.0dp)\n" +
-                "\toverviewActionsHeight: 96.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 72.0px (36.0dp)\n" +
-                "\toverviewPageSpacing: 88.0px (44.0dp)\n" +
-                "\toverviewRowSpacing: 72.0px (36.0dp)\n" +
-                "\toverviewGridSideMargin: 128.0px (64.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(): 1986.0px (993.0dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.76616377px (0.38308188dp)\n" +
-                "\tgetCellLayoutHeight(): 1856.0px (928.0dp)\n" +
-                "\tgetCellLayoutWidth(): 1528.0px (764.0dp)\n")
-    }
-
-    @Test
-    fun twoPanelLandscape3Button() {
-        initializeVarsForTwoPanel(isLandscape = true, isGestureMode = false)
-        val dp = newDP()
-        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" +
-                "\tisScalableGrid:true\n" +
-                "\tinv.numRows: 4\n" +
-                "\tinv.numColumns: 4\n" +
-                "\tinv.numSearchContainerColumns: 4\n" +
-                "\tminCellSize: PointF(80.0, 102.0)dp\n" +
-                "\tcellWidthPx: 210.0px (80.0dp)\n" +
-                "\tcellHeightPx: 267.0px (101.71429dp)\n" +
-                "\tgetCellSize().x: 210.0px (80.0dp)\n" +
-                "\tgetCellSize().y: 267.0px (101.71429dp)\n" +
-                "\tcellLayoutBorderSpacePx Horizontal: 52.0px (19.809525dp)\n" +
-                "\tcellLayoutBorderSpacePx Vertical: 52.0px (19.809525dp)\n" +
-                "\tcellLayoutPaddingPx.left: 26.0px (9.904762dp)\n" +
-                "\tcellLayoutPaddingPx.top: 18.0px (6.857143dp)\n" +
-                "\tcellLayoutPaddingPx.right: 26.0px (9.904762dp)\n" +
-                "\tcellLayoutPaddingPx.bottom: 26.0px (9.904762dp)\n" +
-                "\ticonSizePx: 157.0px (59.809525dp)\n" +
-                "\ticonTextSizePx: 36.0px (13.714286dp)\n" +
-                "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
-                "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tfolderCellHeightPx: 247.0px (94.09524dp)\n" +
-                "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
-                "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tfolderChildDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 42.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
-                "\tallAppsShiftRange: 1730.0px (659.0476dp)\n" +
-                "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
-                "\tallAppsIconSizePx: 157.0px (59.809525dp)\n" +
-                "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
-                "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
-                "\tallAppsCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tallAppsBorderSpacePxX: 52.0px (19.809525dp)\n" +
-                "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                "\tnumShownAllAppsColumns: 6\n" +
-                "\tallAppsLeftRightPadding: 137.0px (52.190475dp)\n" +
-                "\tallAppsLeftRightMargin: 207.0px (78.85714dp)\n" +
-                "\thotseatBarSizePx: 417.0px (158.85715dp)\n" +
-                "\tinv.hotseatColumnSpan: 6\n" +
-                "\thotseatCellHeightPx: 177.0px (67.42857dp)\n" +
-                "\thotseatBarBottomSpacePx: 53.0px (20.190475dp)\n" +
-                "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarEndOffset: 744.0px (283.42856dp)\n" +
-                "\thotseatQsbSpace: 74.0px (28.190475dp)\n" +
-                "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 197.0px (75.04762dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 43.0px (16.380953dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 106.0px (40.38095dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 744.0px (283.42856dp)\n" +
-                "\tnumShownHotseatIcons: 6\n" +
-                "\thotseatBorderSpace: 83.0px (31.619047dp)\n" +
-                "\tisQsbInline: false\n" +
-                "\thotseatQsbWidth: 1467.0px (558.8571dp)\n" +
-                "\tisTaskbarPresent:true\n" +
-                "\tisTaskbarPresentInApps:true\n" +
-                "\ttaskbarSize: 158.0px (60.190475dp)\n" +
-                "\tdesiredWorkspaceHorizontalMarginPx: 79.0px (30.095238dp)\n" +
-                "\tworkspacePadding.left: 53.0px (20.190475dp)\n" +
-                "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
-                "\tworkspacePadding.right: 53.0px (20.190475dp)\n" +
-                "\tworkspacePadding.bottom: 461.0px (175.61905dp)\n" +
-                "\ticonScale: 0.99864316px (0.3804355dp)\n" +
-                "\tcellScaleToFit : 0.99864316px (0.3804355dp)\n" +
-                "\textraSpace: 57.0px (21.714285dp)\n" +
-                "\tunscaled extraSpace: 57.077446px (21.74379dp)\n" +
-                "\tmaxEmptySpace: 131.0px (49.904762dp)\n" +
-                "\tworkspaceTopPadding: 18.0px (6.857143dp)\n" +
-                "\tworkspaceBottomPadding: 39.0px (14.857142dp)\n" +
-                "\toverviewTaskMarginPx: 32.0px (12.190476dp)\n" +
-                "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 158.0px (60.190475dp)\n" +
-                "\toverviewActionsTopMarginPx: 32.0px (12.190476dp)\n" +
-                "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewPageSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewRowSpacing: 74.0px (28.190475dp)\n" +
-                "\toverviewGridSideMargin: 168.0px (64.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(): 1307.0px (497.90475dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.79432625px (0.30260047dp)\n" +
-                "\tgetCellLayoutHeight(): 1269.0px (483.42856dp)\n" +
-                "\tgetCellLayoutWidth(): 1051.0px (400.38095dp)\n")
-    }
-
-    @Test
-    fun twoPanelLandscape() {
-        initializeVarsForTwoPanel(isLandscape = true)
-        val dp = newDP()
-        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" +
-                "\tisScalableGrid:true\n" +
-                "\tinv.numRows: 4\n" +
-                "\tinv.numColumns: 4\n" +
-                "\tinv.numSearchContainerColumns: 4\n" +
-                "\tminCellSize: PointF(80.0, 102.0)dp\n" +
-                "\tcellWidthPx: 210.0px (80.0dp)\n" +
-                "\tcellHeightPx: 267.0px (101.71429dp)\n" +
-                "\tgetCellSize().x: 210.0px (80.0dp)\n" +
-                "\tgetCellSize().y: 267.0px (101.71429dp)\n" +
-                "\tcellLayoutBorderSpacePx Horizontal: 52.0px (19.809525dp)\n" +
-                "\tcellLayoutBorderSpacePx Vertical: 52.0px (19.809525dp)\n" +
-                "\tcellLayoutPaddingPx.left: 26.0px (9.904762dp)\n" +
-                "\tcellLayoutPaddingPx.top: 18.0px (6.857143dp)\n" +
-                "\tcellLayoutPaddingPx.right: 26.0px (9.904762dp)\n" +
-                "\tcellLayoutPaddingPx.bottom: 26.0px (9.904762dp)\n" +
-                "\ticonSizePx: 157.0px (59.809525dp)\n" +
-                "\ticonTextSizePx: 36.0px (13.714286dp)\n" +
-                "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
-                "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tfolderCellHeightPx: 247.0px (94.09524dp)\n" +
-                "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
-                "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tfolderChildDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 42.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
-                "\tallAppsShiftRange: 1730.0px (659.0476dp)\n" +
-                "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
-                "\tallAppsIconSizePx: 157.0px (59.809525dp)\n" +
-                "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
-                "\tallAppsCellHeightPx: 315.0px (120.0dp)\n" +
-                "\tallAppsCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tallAppsBorderSpacePxX: 52.0px (19.809525dp)\n" +
-                "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                "\tnumShownAllAppsColumns: 6\n" +
-                "\tallAppsLeftRightPadding: 137.0px (52.190475dp)\n" +
-                "\tallAppsLeftRightMargin: 207.0px (78.85714dp)\n" +
-                "\thotseatBarSizePx: 417.0px (158.85715dp)\n" +
-                "\tinv.hotseatColumnSpan: 6\n" +
-                "\thotseatCellHeightPx: 177.0px (67.42857dp)\n" +
-                "\thotseatBarBottomSpacePx: 53.0px (20.190475dp)\n" +
-                "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                "\thotseatQsbSpace: 74.0px (28.190475dp)\n" +
-                "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 197.0px (75.04762dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 43.0px (16.380953dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 370.0px (140.95238dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 370.0px (140.95238dp)\n" +
-                "\tnumShownHotseatIcons: 6\n" +
-                "\thotseatBorderSpace: 105.0px (40.0dp)\n" +
-                "\tisQsbInline: false\n" +
-                "\thotseatQsbWidth: 1467.0px (558.8571dp)\n" +
-                "\tisTaskbarPresent:true\n" +
-                "\tisTaskbarPresentInApps:true\n" +
-                "\ttaskbarSize: 158.0px (60.190475dp)\n" +
-                "\tdesiredWorkspaceHorizontalMarginPx: 79.0px (30.095238dp)\n" +
-                "\tworkspacePadding.left: 53.0px (20.190475dp)\n" +
-                "\tworkspacePadding.top: 0.0px (0.0dp)\n" +
-                "\tworkspacePadding.right: 53.0px (20.190475dp)\n" +
-                "\tworkspacePadding.bottom: 461.0px (175.61905dp)\n" +
-                "\ticonScale: 0.99864316px (0.3804355dp)\n" +
-                "\tcellScaleToFit : 0.99864316px (0.3804355dp)\n" +
-                "\textraSpace: 57.0px (21.714285dp)\n" +
-                "\tunscaled extraSpace: 57.077446px (21.74379dp)\n" +
-                "\tmaxEmptySpace: 131.0px (49.904762dp)\n" +
-                "\tworkspaceTopPadding: 18.0px (6.857143dp)\n" +
-                "\tworkspaceBottomPadding: 39.0px (14.857142dp)\n" +
-                "\toverviewTaskMarginPx: 32.0px (12.190476dp)\n" +
-                "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 158.0px (60.190475dp)\n" +
-                "\toverviewActionsTopMarginPx: 32.0px (12.190476dp)\n" +
-                "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewPageSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewRowSpacing: 74.0px (28.190475dp)\n" +
-                "\toverviewGridSideMargin: 168.0px (64.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(): 1307.0px (497.90475dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.79432625px (0.30260047dp)\n" +
-                "\tgetCellLayoutHeight(): 1269.0px (483.42856dp)\n" +
-                "\tgetCellLayoutWidth(): 1051.0px (400.38095dp)\n")
-    }
-
-    @Test
-    fun twoPanelPortrait3Button() {
-        initializeVarsForTwoPanel(isGestureMode = false)
-        val dp = newDP()
-        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: 2098.0px (799.2381dp)\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" +
-                "\tisScalableGrid:true\n" +
-                "\tinv.numRows: 4\n" +
-                "\tinv.numColumns: 4\n" +
-                "\tinv.numSearchContainerColumns: 4\n" +
-                "\tminCellSize: PointF(68.0, 116.0)dp\n" +
-                "\tcellWidthPx: 178.0px (67.809525dp)\n" +
-                "\tcellHeightPx: 304.0px (115.809525dp)\n" +
-                "\tgetCellSize().x: 178.0px (67.809525dp)\n" +
-                "\tgetCellSize().y: 304.0px (115.809525dp)\n" +
-                "\tcellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tcellLayoutBorderSpacePx Vertical: 52.0px (19.809525dp)\n" +
-                "\tcellLayoutPaddingPx.left: 21.0px (8.0dp)\n" +
-                "\tcellLayoutPaddingPx.top: 21.0px (8.0dp)\n" +
-                "\tcellLayoutPaddingPx.right: 21.0px (8.0dp)\n" +
-                "\tcellLayoutPaddingPx.bottom: 21.0px (8.0dp)\n" +
-                "\ticonSizePx: 136.0px (51.809525dp)\n" +
-                "\ticonTextSizePx: 31.0px (11.809524dp)\n" +
-                "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
-                "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tfolderCellHeightPx: 247.0px (94.09524dp)\n" +
-                "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
-                "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tfolderChildDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 42.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
-                "\tallAppsShiftRange: 2098.0px (799.2381dp)\n" +
-                "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
-                "\tallAppsIconSizePx: 136.0px (51.809525dp)\n" +
-                "\tallAppsIconTextSizePx: 31.0px (11.809524dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
-                "\tallAppsCellHeightPx: 345.0px (131.42857dp)\n" +
-                "\tallAppsCellWidthPx: 178.0px (67.809525dp)\n" +
-                "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                "\tallAppsBorderSpacePxY: 73.0px (27.809525dp)\n" +
-                "\tnumShownAllAppsColumns: 6\n" +
-                "\tallAppsLeftRightPadding: 126.0px (48.0dp)\n" +
-                "\tallAppsLeftRightMargin: 155.0px (59.04762dp)\n" +
-                "\thotseatBarSizePx: 459.0px (174.85715dp)\n" +
-                "\tinv.hotseatColumnSpan: 6\n" +
-                "\thotseatCellHeightPx: 153.0px (58.285713dp)\n" +
-                "\thotseatBarBottomSpacePx: 95.0px (36.190475dp)\n" +
-                "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarEndOffset: 660.0px (251.42857dp)\n" +
-                "\thotseatQsbSpace: 95.0px (36.190475dp)\n" +
-                "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 219.0px (83.42857dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 87.0px (33.142857dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 78.0px (29.714285dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 660.0px (251.42857dp)\n" +
-                "\tnumShownHotseatIcons: 6\n" +
-                "\thotseatBorderSpace: 57.0px (21.714285dp)\n" +
-                "\tisQsbInline: false\n" +
-                "\thotseatQsbWidth: 1236.0px (470.85715dp)\n" +
-                "\tisTaskbarPresent:true\n" +
-                "\tisTaskbarPresentInApps:true\n" +
-                "\ttaskbarSize: 158.0px (60.190475dp)\n" +
-                "\tdesiredWorkspaceHorizontalMarginPx: 58.0px (22.095238dp)\n" +
-                "\tworkspacePadding.left: 37.0px (14.095238dp)\n" +
-                "\tworkspacePadding.top: 68.0px (25.904762dp)\n" +
-                "\tworkspacePadding.right: 37.0px (14.095238dp)\n" +
-                "\tworkspacePadding.bottom: 615.0px (234.28572dp)\n" +
-                "\ticonScale: 0.9978308px (0.38012603dp)\n" +
-                "\tcellScaleToFit : 0.9978308px (0.38012603dp)\n" +
-                "\textraSpace: 235.0px (89.52381dp)\n" +
-                "\tunscaled extraSpace: 235.51086px (89.71842dp)\n" +
-                "\tmaxEmptySpace: 236.0px (89.90476dp)\n" +
-                "\tworkspaceTopPadding: 89.0px (33.904762dp)\n" +
-                "\tworkspaceBottomPadding: 146.0px (55.61905dp)\n" +
-                "\toverviewTaskMarginPx: 32.0px (12.190476dp)\n" +
-                "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 158.0px (60.190475dp)\n" +
-                "\toverviewActionsTopMarginPx: 63.0px (24.0dp)\n" +
-                "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewPageSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewRowSpacing: 74.0px (28.190475dp)\n" +
-                "\toverviewGridSideMargin: 168.0px (64.0dp)\n" +
-                "\tdropTargetBarTopMarginPx: 168.0px (64.0dp)\n" +
-                "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
-                "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
-                "\tgetCellLayoutSpringLoadShrunkTop(): 467.0px (177.90475dp)\n" +
-                "\tgetCellLayoutSpringLoadShrunkBottom(): 1578.0px (601.1429dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.785159px (0.29910818dp)\n" +
-                "\tgetCellLayoutHeight(): 1415.0px (539.0476dp)\n" +
-                "\tgetCellLayoutWidth(): 883.0px (336.38095dp)\n")
-    }
-
-    @Test
-    fun twoPanelPortrait() {
-        initializeVarsForTwoPanel()
-        val dp = newDP()
-        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: 2098.0px (799.2381dp)\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" +
-                "\tisScalableGrid:true\n" +
-                "\tinv.numRows: 4\n" +
-                "\tinv.numColumns: 4\n" +
-                "\tinv.numSearchContainerColumns: 4\n" +
-                "\tminCellSize: PointF(68.0, 116.0)dp\n" +
-                "\tcellWidthPx: 178.0px (67.809525dp)\n" +
-                "\tcellHeightPx: 304.0px (115.809525dp)\n" +
-                "\tgetCellSize().x: 178.0px (67.809525dp)\n" +
-                "\tgetCellSize().y: 304.0px (115.809525dp)\n" +
-                "\tcellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tcellLayoutBorderSpacePx Vertical: 52.0px (19.809525dp)\n" +
-                "\tcellLayoutPaddingPx.left: 21.0px (8.0dp)\n" +
-                "\tcellLayoutPaddingPx.top: 21.0px (8.0dp)\n" +
-                "\tcellLayoutPaddingPx.right: 21.0px (8.0dp)\n" +
-                "\tcellLayoutPaddingPx.bottom: 21.0px (8.0dp)\n" +
-                "\ticonSizePx: 136.0px (51.809525dp)\n" +
-                "\ticonTextSizePx: 31.0px (11.809524dp)\n" +
-                "\ticonDrawablePaddingPx: 17.0px (6.4761906dp)\n" +
-                "\tfolderCellWidthPx: 210.0px (80.0dp)\n" +
-                "\tfolderCellHeightPx: 247.0px (94.09524dp)\n" +
-                "\tfolderChildIconSizePx: 158.0px (60.190475dp)\n" +
-                "\tfolderChildTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tfolderChildDrawablePaddingPx: 13.0px (4.952381dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 42.0px (16.0dp)\n" +
-                "\tfolderTopPadding: 63.0px (24.0dp)\n" +
-                "\tbottomSheetTopPadding: 110.0px (41.904762dp)\n" +
-                "\tallAppsShiftRange: 2098.0px (799.2381dp)\n" +
-                "\tallAppsTopPadding: 110.0px (41.904762dp)\n" +
-                "\tallAppsIconSizePx: 136.0px (51.809525dp)\n" +
-                "\tallAppsIconTextSizePx: 31.0px (11.809524dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 18.0px (6.857143dp)\n" +
-                "\tallAppsCellHeightPx: 345.0px (131.42857dp)\n" +
-                "\tallAppsCellWidthPx: 178.0px (67.809525dp)\n" +
-                "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                "\tallAppsBorderSpacePxY: 73.0px (27.809525dp)\n" +
-                "\tnumShownAllAppsColumns: 6\n" +
-                "\tallAppsLeftRightPadding: 126.0px (48.0dp)\n" +
-                "\tallAppsLeftRightMargin: 155.0px (59.04762dp)\n" +
-                "\thotseatBarSizePx: 459.0px (174.85715dp)\n" +
-                "\tinv.hotseatColumnSpan: 6\n" +
-                "\thotseatCellHeightPx: 153.0px (58.285713dp)\n" +
-                "\thotseatBarBottomSpacePx: 95.0px (36.190475dp)\n" +
-                "\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
-                "\thotseatBarEndOffset: 0.0px (0.0dp)\n" +
-                "\thotseatQsbSpace: 95.0px (36.190475dp)\n" +
-                "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 219.0px (83.42857dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 87.0px (33.142857dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 302.0px (115.04762dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 302.0px (115.04762dp)\n" +
-                "\tnumShownHotseatIcons: 6\n" +
-                "\thotseatBorderSpace: 84.0px (32.0dp)\n" +
-                "\tisQsbInline: false\n" +
-                "\thotseatQsbWidth: 1236.0px (470.85715dp)\n" +
-                "\tisTaskbarPresent:true\n" +
-                "\tisTaskbarPresentInApps:true\n" +
-                "\ttaskbarSize: 158.0px (60.190475dp)\n" +
-                "\tdesiredWorkspaceHorizontalMarginPx: 58.0px (22.095238dp)\n" +
-                "\tworkspacePadding.left: 37.0px (14.095238dp)\n" +
-                "\tworkspacePadding.top: 68.0px (25.904762dp)\n" +
-                "\tworkspacePadding.right: 37.0px (14.095238dp)\n" +
-                "\tworkspacePadding.bottom: 615.0px (234.28572dp)\n" +
-                "\ticonScale: 0.9978308px (0.38012603dp)\n" +
-                "\tcellScaleToFit : 0.9978308px (0.38012603dp)\n" +
-                "\textraSpace: 235.0px (89.52381dp)\n" +
-                "\tunscaled extraSpace: 235.51086px (89.71842dp)\n" +
-                "\tmaxEmptySpace: 236.0px (89.90476dp)\n" +
-                "\tworkspaceTopPadding: 89.0px (33.904762dp)\n" +
-                "\tworkspaceBottomPadding: 146.0px (55.61905dp)\n" +
-                "\toverviewTaskMarginPx: 32.0px (12.190476dp)\n" +
-                "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 158.0px (60.190475dp)\n" +
-                "\toverviewActionsTopMarginPx: 63.0px (24.0dp)\n" +
-                "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewPageSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewRowSpacing: 74.0px (28.190475dp)\n" +
-                "\toverviewGridSideMargin: 168.0px (64.0dp)\n" +
-                "\tdropTargetBarTopMarginPx: 168.0px (64.0dp)\n" +
-                "\tdropTargetBarSizePx: 147.0px (56.0dp)\n" +
-                "\tdropTargetBarBottomMarginPx: 42.0px (16.0dp)\n" +
-                "\tgetCellLayoutSpringLoadShrunkTop(): 467.0px (177.90475dp)\n" +
-                "\tgetCellLayoutSpringLoadShrunkBottom(): 1578.0px (601.1429dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.785159px (0.29910818dp)\n" +
-                "\tgetCellLayoutHeight(): 1415.0px (539.0476dp)\n" +
-                "\tgetCellLayoutWidth(): 883.0px (336.38095dp)\n")
-    }
-
-    @Test
-    fun phoneVerticalBar3Button() {
-        initializeVarsForPhone(isVerticalBar = true, isGestureMode = false)
-        val dp = newDP()
-
-        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" +
-                "\tisScalableGrid:false\n" +
-                "\tinv.numRows: 5\n" +
-                "\tinv.numColumns: 4\n" +
-                "\tinv.numSearchContainerColumns: 4\n" +
-                "\tminCellSize: PointF(80.0, 104.0)dp\n" +
-                "\tcellWidthPx: 153.0px (58.285713dp)\n" +
-                "\tcellHeightPx: 160.0px (60.95238dp)\n" +
-                "\tgetCellSize().x: 461.0px (175.61905dp)\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: 142.0px (54.095238dp)\n" +
-                "\ticonTextSizePx: 0.0px (0.0dp)\n" +
-                "\ticonDrawablePaddingPx: 0.0px (0.0dp)\n" +
-                "\tfolderCellWidthPx: 175.0px (66.666664dp)\n" +
-                "\tfolderCellHeightPx: 205.0px (78.09524dp)\n" +
-                "\tfolderChildIconSizePx: 131.0px (49.904762dp)\n" +
-                "\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
-                "\tfolderChildDrawablePaddingPx: 9.0px (3.4285715dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                "\tfolderTopPadding: 42.0px (16.0dp)\n" +
-                "\tbottomSheetTopPadding: 114.0px (43.42857dp)\n" +
-                "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
-                "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
-                "\tallAppsIconSizePx: 158.0px (60.190475dp)\n" +
-                "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                "\tallAppsCellHeightPx: 329.0px (125.333336dp)\n" +
-                "\tallAppsCellWidthPx: 200.0px (76.190475dp)\n" +
-                "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                "\tnumShownAllAppsColumns: 4\n" +
-                "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
-                "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
-                "\thotseatBarSizePx: 247.0px (94.09524dp)\n" +
-                "\tinv.hotseatColumnSpan: 4\n" +
-                "\thotseatCellHeightPx: 160.0px (60.95238dp)\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: 95.0px (36.190475dp)\n" +
-                "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 65.0px (24.761906dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 48.0px (18.285715dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 42.0px (16.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 189.0px (72.0dp)\n" +
-                "\tnumShownHotseatIcons: 4\n" +
-                "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                "\tisQsbInline: false\n" +
-                "\thotseatQsbWidth: 1525.0px (580.9524dp)\n" +
-                "\tisTaskbarPresent:false\n" +
-                "\tisTaskbarPresentInApps:false\n" +
-                "\ttaskbarSize: 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: 194.0px (73.90476dp)\n" +
-                "\tworkspacePadding.bottom: 0.0px (0.0dp)\n" +
-                "\ticonScale: 1.0px (0.3809524dp)\n" +
-                "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                "\textraSpace: 166.0px (63.238094dp)\n" +
-                "\tunscaled extraSpace: 166.0px (63.238094dp)\n" +
-                "\tmaxEmptySpace: 184.0px (70.09524dp)\n" +
-                "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                "\toverviewTaskMarginPx: 42.0px (16.0dp)\n" +
-                "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 168.0px (64.0dp)\n" +
-                "\toverviewActionsTopMarginPx: 32.0px (12.190476dp)\n" +
-                "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewPageSpacing: 42.0px (16.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(): 983.0px (374.4762dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.777336px (0.296128dp)\n" +
-                "\tgetCellLayoutHeight(): 1006.0px (383.2381dp)\n" +
-                "\tgetCellLayoutWidth(): 1952.0px (743.619dp)\n")
-    }
-
-    @Test
-    fun phoneVerticalBar() {
-        initializeVarsForPhone(isVerticalBar = true)
-        val dp = newDP()
-
-        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" +
-                "\tisScalableGrid:false\n" +
-                "\tinv.numRows: 5\n" +
-                "\tinv.numColumns: 4\n" +
-                "\tinv.numSearchContainerColumns: 4\n" +
-                "\tminCellSize: PointF(80.0, 104.0)dp\n" +
-                "\tcellWidthPx: 153.0px (58.285713dp)\n" +
-                "\tcellHeightPx: 160.0px (60.95238dp)\n" +
-                "\tgetCellSize().x: 493.0px (187.80952dp)\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: 142.0px (54.095238dp)\n" +
-                "\ticonTextSizePx: 0.0px (0.0dp)\n" +
-                "\ticonDrawablePaddingPx: 0.0px (0.0dp)\n" +
-                "\tfolderCellWidthPx: 159.0px (60.57143dp)\n" +
-                "\tfolderCellHeightPx: 187.0px (71.2381dp)\n" +
-                "\tfolderChildIconSizePx: 119.0px (45.333332dp)\n" +
-                "\tfolderChildTextSizePx: 31.0px (11.809524dp)\n" +
-                "\tfolderChildDrawablePaddingPx: 8.0px (3.047619dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Horizontal: 42.0px (16.0dp)\n" +
-                "\tfolderCellLayoutBorderSpacePx Vertical: 42.0px (16.0dp)\n" +
-                "\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
-                "\tfolderTopPadding: 42.0px (16.0dp)\n" +
-                "\tbottomSheetTopPadding: 114.0px (43.42857dp)\n" +
-                "\tallAppsShiftRange: 788.0px (300.1905dp)\n" +
-                "\tallAppsTopPadding: 0.0px (0.0dp)\n" +
-                "\tallAppsIconSizePx: 158.0px (60.190475dp)\n" +
-                "\tallAppsIconTextSizePx: 37.0px (14.095238dp)\n" +
-                "\tallAppsIconDrawablePaddingPx: 21.0px (8.0dp)\n" +
-                "\tallAppsCellHeightPx: 329.0px (125.333336dp)\n" +
-                "\tallAppsCellWidthPx: 200.0px (76.190475dp)\n" +
-                "\tallAppsBorderSpacePxX: 42.0px (16.0dp)\n" +
-                "\tallAppsBorderSpacePxY: 42.0px (16.0dp)\n" +
-                "\tnumShownAllAppsColumns: 4\n" +
-                "\tallAppsLeftRightPadding: 0.0px (0.0dp)\n" +
-                "\tallAppsLeftRightMargin: 0.0px (0.0dp)\n" +
-                "\thotseatBarSizePx: 247.0px (94.09524dp)\n" +
-                "\tinv.hotseatColumnSpan: 4\n" +
-                "\thotseatCellHeightPx: 160.0px (60.95238dp)\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: 95.0px (36.190475dp)\n" +
-                "\thotseatQsbHeight: 165.0px (62.857143dp)\n" +
-                "\tspringLoadedHotseatBarTopMarginPx: 118.0px (44.95238dp)\n" +
-                "\tgetHotseatLayoutPadding(context).top: 65.0px (24.761906dp)\n" +
-                "\tgetHotseatLayoutPadding(context).bottom: 111.0px (42.285713dp)\n" +
-                "\tgetHotseatLayoutPadding(context).left: 42.0px (16.0dp)\n" +
-                "\tgetHotseatLayoutPadding(context).right: 63.0px (24.0dp)\n" +
-                "\tnumShownHotseatIcons: 4\n" +
-                "\thotseatBorderSpace: 0.0px (0.0dp)\n" +
-                "\tisQsbInline: false\n" +
-                "\thotseatQsbWidth: 1621.0px (617.5238dp)\n" +
-                "\tisTaskbarPresent:false\n" +
-                "\tisTaskbarPresentInApps:false\n" +
-                "\ttaskbarSize: 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: 194.0px (73.90476dp)\n" +
-                "\tworkspacePadding.bottom: 0.0px (0.0dp)\n" +
-                "\ticonScale: 1.0px (0.3809524dp)\n" +
-                "\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
-                "\textraSpace: 103.0px (39.238094dp)\n" +
-                "\tunscaled extraSpace: 103.0px (39.238094dp)\n" +
-                "\tmaxEmptySpace: 131.0px (49.904762dp)\n" +
-                "\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
-                "\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
-                "\toverviewTaskMarginPx: 42.0px (16.0dp)\n" +
-                "\toverviewTaskIconSizePx: 126.0px (48.0dp)\n" +
-                "\toverviewTaskIconDrawableSizePx: 116.0px (44.190475dp)\n" +
-                "\toverviewTaskIconDrawableSizeGridPx: 0.0px (0.0dp)\n" +
-                "\toverviewTaskThumbnailTopMarginPx: 168.0px (64.0dp)\n" +
-                "\toverviewActionsTopMarginPx: 32.0px (12.190476dp)\n" +
-                "\toverviewActionsHeight: 126.0px (48.0dp)\n" +
-                "\toverviewActionsButtonSpacing: 95.0px (36.190475dp)\n" +
-                "\toverviewPageSpacing: 42.0px (16.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(): 927.0px (353.14285dp)\n" +
-                "\tworkspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)\n" +
-                "\tgetWorkspaceSpringLoadScale(): 0.76988333px (0.2932889dp)\n" +
-                "\tgetCellLayoutHeight(): 943.0px (359.2381dp)\n" +
-                "\tgetCellLayoutWidth(): 2078.0px (791.619dp)\n")
-    }
-}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index c822578..9c240f0 100644
--- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -19,7 +19,7 @@
 
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 
-import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index 4e49716..262dc37 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -70,7 +70,7 @@
 
     @Test
     public void loadTasksInBackground_onlyKeys_noValidTaskDescription() {
-        GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
+        GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(
                 new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo(), null);
         when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
                 .thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
@@ -90,8 +90,8 @@
         task1.taskDescription = new ActivityManager.TaskDescription(taskDescription);
         ActivityManager.RecentTaskInfo task2 = new ActivityManager.RecentTaskInfo();
         task2.taskDescription = new ActivityManager.TaskDescription();
-        GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
-                task1, task2, null);
+        GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(task1, task2,
+                null);
         when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
                 .thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
 
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index 1df9c02..9337cb5 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -68,6 +68,13 @@
     }
 
     @Test
+    public void testHideTaskbarPersistsOnRecreate() {
+        getTaskbar().hide();
+        mLauncher.recreateTaskbar();
+        mLauncher.getLaunchedAppState().assertTaskbarHidden();
+    }
+
+    @Test
     public void testLaunchApp() throws Exception {
         getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
     }
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index d43aafa..190b002 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.ReflectionHelpers;
 import com.android.launcher3.util.RotationUtils;
 import com.android.launcher3.util.WindowBounds;
@@ -164,6 +165,7 @@
                 WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
                 doReturn(cdi).when(wmProxy).getDisplayInfo(any());
                 doReturn(wm).when(wmProxy).getRealBounds(any(), any());
+                doReturn(NavigationMode.NO_BUTTON).when(wmProxy).getNavigationMode(any());
 
                 ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache =
                         new ArrayMap<>();
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
index 11143fb..d15b906 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -20,6 +20,8 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/all_apps_header_pill_height"
     android:layout_gravity="center_horizontal"
+    android:paddingTop="@dimen/all_apps_tabs_vertical_padding"
+    android:paddingBottom="@dimen/all_apps_tabs_vertical_padding"
     android:orientation="horizontal"
     style="@style/TextHeadline">
 
@@ -28,7 +30,6 @@
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_marginEnd="@dimen/all_apps_tabs_button_horizontal_padding"
-        android:layout_marginVertical="@dimen/all_apps_tabs_vertical_padding"
         android:layout_weight="1"
         android:background="@drawable/all_apps_tabs_background"
         android:text="@string/all_apps_personal_tab"
@@ -41,7 +42,6 @@
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_marginStart="@dimen/all_apps_tabs_button_horizontal_padding"
-        android:layout_marginVertical="@dimen/all_apps_tabs_vertical_padding"
         android:layout_weight="1"
         android:background="@drawable/all_apps_tabs_background"
         android:text="@string/all_apps_work_tab"
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index 82b0b8d..95ebd94 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -21,4 +21,5 @@
     android:layout_height="match_parent"
     android:theme="@style/HomeScreenElementTheme"
     android:importantForAccessibility="no"
+    android:preferKeepClear="true"
     launcher:containerType="hotseat" />
\ No newline at end of file
diff --git a/res/layout/search_results_rv_layout.xml b/res/layout/search_results_rv_layout.xml
index 567cb5f..9127521 100644
--- a/res/layout/search_results_rv_layout.xml
+++ b/res/layout/search_results_rv_layout.xml
@@ -18,7 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/search_results_list_view"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:clipToPadding="false"
     android:descendantFocusability="afterDescendants"
     android:focusable="true" />
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 87bf33d..4dee6e7 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -34,7 +34,8 @@
         android:clipChildren="false"
         android:orientation="horizontal"
         android:paddingLeft="12dp"
-        android:paddingRight="12dp" >
+        android:paddingRight="12dp"
+        android:alpha="0">
 
         <com.android.launcher3.folder.FolderNameEditText
             android:id="@+id/folder_name"
diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml
index d2fa5fa..81b28ba 100644
--- a/res/layout/work_mode_fab.xml
+++ b/res/layout/work_mode_fab.xml
@@ -26,6 +26,7 @@
     android:textColor="@color/all_apps_tab_text"
     android:textSize="14sp"
     android:background="@drawable/work_apps_toggle_background"
+    android:forceHasOverlappingRendering="false"
     android:drawablePadding="8dp"
     android:drawableStart="@drawable/ic_corp_off"
     android:layout_marginBottom="@dimen/work_fab_margin_bottom"
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index e075559..c234900 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -101,7 +101,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر أو أكثر"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
-    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"الخلفية والنمط"</string>
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"الخلفية والأسلوب"</string>
     <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>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 812d249..c64e9b2 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -71,7 +71,7 @@
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"ବ୍ୟକ୍ତିଗତ ଆପ୍ ତାଲିକା"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"କାର୍ଯ୍ୟକାରୀ ଆପ୍‌ ତାଲିକା"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"ବାହାର କରନ୍ତୁ"</string>
-    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ଅନଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ଅନଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ଆପ୍‌ ସୂଚନା"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"ଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ଆପ ପରାମର୍ଶ ଦିଅନ୍ତୁ ନାହିଁ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 448a56b..8669949 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -106,13 +106,13 @@
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
     <string name="allow_rotation_title" msgid="7222049633713050106">"Zezwalaj na obrót ekranu głównego"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Po obróceniu telefonu"</string>
-    <string name="notification_dots_title" msgid="9062440428204120317">"Plakietki z powiadomieniami"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Kropki powiadomień"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"Włączono"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"Wyłączono"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Wymagany jest dostęp do powiadomień"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Aby pokazać plakietki z powiadomieniami, włącz powiadomienia aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="msg_missing_notification_access" msgid="281113995110910548">"Aby pokazywać kropki powiadomień, włącz powiadomienia aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Zmień ustawienia"</string>
-    <string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaż plakietki z powiadomieniami"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaż kropki powiadomień"</string>
     <string name="developer_options_title" msgid="700788437593726194">"Opcje programisty"</string>
     <string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Dodawaj ikony aplikacji do ekranu głównego"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"W przypadku nowych aplikacji"</string>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index 072b92d..e718d9c 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -18,4 +18,11 @@
     <!-- The duration of the PagedView page snap animation -->
     <integer name="config_pageSnapAnimationDuration">550</integer>
 
+    <!-- The duration of the Widget picker opening and closing animation -->
+    <integer name="config_bottomSheetOpenDuration">500</integer>
+    <integer name="config_bottomSheetCloseDuration">500</integer>
+
+    <!-- The duration of the AllApps opening and closing animation -->
+    <integer name="config_allAppsOpenDuration">@integer/config_bottomSheetOpenDuration</integer>
+    <integer name="config_allAppsCloseDuration">@integer/config_bottomSheetCloseDuration</integer>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index d4c08d0..d3f5033 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -193,4 +193,15 @@
     <dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
     <dimen name="swipe_back_window_max_delta_y">160dp</dimen>
     <dimen name="swipe_back_window_corner_radius">40dp</dimen>
+
+    <!-- The duration of the bottom sheet opening and closing animation -->
+    <integer name="config_bottomSheetOpenDuration">267</integer>
+    <integer name="config_bottomSheetCloseDuration">267</integer>
+
+    <!-- The duration of the AllApps opening and closing animation -->
+    <integer name="config_allAppsOpenDuration">600</integer>
+    <integer name="config_allAppsCloseDuration">300</integer>
+
+    <!-- The max scale for the wallpaper when it's zoomed in -->
+    <item name="config_wallpaperMaxScale" format="float" type="dimen">0</item>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a9d1127..2e886db 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -226,6 +226,7 @@
     <dimen name="drop_target_text_size">16sp</dimen>
     <dimen name="drop_target_shadow_elevation">2dp</dimen>
     <dimen name="drop_target_bar_margin_horizontal">4dp</dimen>
+    <dimen name="drop_target_button_drawable_size">20dp</dimen>
     <dimen name="drop_target_button_drawable_padding">8dp</dimen>
     <dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
     <dimen name="drop_target_button_drawable_vertical_padding">8dp</dimen>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 21dbc5f..5ee6fce 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -109,10 +109,6 @@
             | TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG
             | TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS | TYPE_OPTIONS_POPUP_DIALOG;
 
-    // Usually we show the back button when a floating view is open. Instead, hide for these types.
-    public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
-            | TYPE_SNACKBAR | TYPE_WIDGET_RESIZE_FRAME | TYPE_LISTENER;
-
     public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER
             & ~TYPE_ALL_APPS_EDU;
 
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 3b24df2..5abe3d3 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -91,7 +91,7 @@
 
         Resources resources = getResources();
         mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
-        mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_text_size);
+        mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_button_drawable_size);
         mDrawablePadding = resources.getDimensionPixelSize(
                 R.dimen.drop_target_button_drawable_padding);
     }
@@ -374,11 +374,63 @@
         hideTooltip();
     }
 
+    /**
+     * Returns if the text will be truncated within the provided availableWidth.
+     */
     public boolean isTextTruncated(int availableWidth) {
-        availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
-                + getCompoundDrawablePadding());
-        CharSequence displayedText = TextUtils.ellipsize(mText, getPaint(), availableWidth,
+        availableWidth -= getPaddingLeft() + getPaddingRight();
+        if (mIconVisible) {
+            availableWidth -= mDrawable.getIntrinsicWidth() + getCompoundDrawablePadding();
+        }
+        if (availableWidth <= 0) {
+            return true;
+        }
+        CharSequence firstLine = TextUtils.ellipsize(mText, getPaint(), availableWidth,
                 TextUtils.TruncateAt.END);
-        return !mText.equals(displayedText);
+        if (!mTextMultiLine) {
+            return !TextUtils.equals(mText, firstLine);
+        }
+        if (TextUtils.equals(mText, firstLine)) {
+            // When multi-line is active, if it can display as one line, then text is not truncated.
+            return false;
+        }
+        CharSequence secondLine =
+                TextUtils.ellipsize(mText.subSequence(firstLine.length(), mText.length()),
+                        getPaint(), availableWidth, TextUtils.TruncateAt.END);
+        return !(TextUtils.equals(mText.subSequence(0, firstLine.length()), firstLine)
+                && TextUtils.equals(mText.subSequence(firstLine.length(), secondLine.length()),
+                secondLine));
+    }
+
+    /**
+     * Reduce the size of the text until it fits the measured width or reaches a minimum.
+     *
+     * The minimum size is defined by {@code R.dimen.button_drop_target_min_text_size} and
+     * it diminishes by intervals defined by
+     * {@code R.dimen.button_drop_target_resize_text_increment}
+     * This functionality is very similar to the option
+     * {@link TextView#setAutoSizeTextTypeWithDefaults(int)} but can't be used in this view because
+     * the layout width is {@code WRAP_CONTENT}.
+     *
+     * @return The biggest text size in SP that makes the text fit or if the text can't fit returns
+     *         the min available value
+     */
+    public float resizeTextToFit() {
+        float minSize = Utilities.pxToSp(getResources()
+                .getDimensionPixelSize(R.dimen.button_drop_target_min_text_size));
+        float step = Utilities.pxToSp(getResources()
+                .getDimensionPixelSize(R.dimen.button_drop_target_resize_text_increment));
+        float textSize = Utilities.pxToSp(getTextSize());
+
+        int availableWidth = getMeasuredWidth();
+        while (textSize > minSize) {
+            if (isTextTruncated(availableWidth)) {
+                textSize -= step;
+                setTextSize(textSize);
+            } else {
+                return textSize;
+            }
+        }
+        return minSize;
     }
 }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 14a467a..4cad919 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.Utilities.pxFromSp;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.icons.GraphicsUtils.getShapePath;
 import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp;
@@ -52,6 +53,7 @@
 
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.Locale;
 
 @SuppressLint("NewApi")
 public class DeviceProfile {
@@ -180,11 +182,19 @@
     private final int hotseatQsbShadowHeight;
     public int hotseatBorderSpace;
 
+    // Bottom sheets
+    public int bottomSheetTopPadding;
+    public int bottomSheetOpenDuration;
+    public int bottomSheetCloseDuration;
+    public float bottomSheetWorkspaceScale;
+    public float bottomSheetDepth;
+
     // All apps
     public Point allAppsBorderSpacePx;
     public int allAppsShiftRange;
     public int allAppsTopPadding;
-    public int bottomSheetTopPadding;
+    public int allAppsOpenDuration;
+    public int allAppsCloseDuration;
     public int allAppsCellHeightPx;
     public int allAppsCellWidthPx;
     public int allAppsIconSizePx;
@@ -315,6 +325,21 @@
         bottomSheetTopPadding = mInsets.top // statusbar height
                 + res.getDimensionPixelSize(R.dimen.bottom_sheet_extra_top_padding)
                 + (isTablet ? 0 : edgeMarginPx); // phones need edgeMarginPx additional padding
+        bottomSheetOpenDuration = res.getInteger(R.integer.config_bottomSheetOpenDuration);
+        bottomSheetCloseDuration = res.getInteger(R.integer.config_bottomSheetCloseDuration);
+        if (isTablet) {
+            bottomSheetWorkspaceScale = workspaceContentScale;
+            // The goal is to set wallpaper to zoom at workspaceContentScale when in AllApps.
+            // When depth is 0, wallpaper zoom is set to maxWallpaperScale.
+            // When depth is 1, wallpaper zoom is set to 1.
+            // For depth to achieve zoom set to maxWallpaperScale * workspaceContentScale:
+            float maxWallpaperScale = res.getFloat(R.dimen.config_wallpaperMaxScale);
+            bottomSheetDepth = Utilities.mapToRange(maxWallpaperScale * workspaceContentScale,
+                    maxWallpaperScale, 1f, 0f, 1f, LINEAR);
+        } else {
+            bottomSheetWorkspaceScale = 1f;
+            bottomSheetDepth = 0f;
+        }
 
         folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale);
         folderContentPaddingLeftRight =
@@ -473,6 +498,8 @@
             allAppsShiftRange =
                     res.getDimensionPixelSize(R.dimen.all_apps_starting_vertical_translate);
         }
+        allAppsOpenDuration = res.getInteger(R.integer.config_allAppsOpenDuration);
+        allAppsCloseDuration = res.getInteger(R.integer.config_allAppsCloseDuration);
 
         flingToDeleteThresholdVelocity = res.getDimensionPixelSize(
                 R.dimen.drag_flingToDeleteMinVelocity);
@@ -513,7 +540,7 @@
     private int getIconToIconWidthForColumns(int columns) {
         return columns * getCellSize().x
                 + (columns - 1) * cellLayoutBorderSpacePx.x
-                - (getCellSize().x - iconSizePx);  // left and right cell space
+                - getCellHorizontalSpace();
     }
 
     private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {
@@ -985,6 +1012,13 @@
     }
 
     /**
+     * Returns the left and right space on the cell, which is the cell width - icon size
+     */
+    public int getCellHorizontalSpace() {
+        return getCellSize().x - iconSizePx;
+    }
+
+    /**
      * Gets the number of panels within the workspace.
      */
     public int getPanelCount() {
@@ -1030,7 +1064,7 @@
                         / getCellLayoutHeight();
         scale = Math.min(scale, 1f);
 
-        // Reduce scale if next pages would not be visible after scaling the workspace
+        // Reduce scale if next pages would not be visible after scaling the workspace.
         int workspaceWidth = availableWidthPx;
         float scaledWorkspaceWidth = workspaceWidth * scale;
         float maxAvailableWidth = workspaceWidth - (2 * workspaceSpringLoadedMinNextPageVisiblePx);
@@ -1347,6 +1381,10 @@
         return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mMetrics.densityDpi) + "dp)";
     }
 
+    private String dpPointFToString(String name, PointF value) {
+        return String.format(Locale.ENGLISH, "\t%s: PointF(%.1f, %.1f)dp", name, value.x, value.y);
+    }
+
     /** Dumps various DeviceProfile variables to the specified writer. */
     public void dump(Context context, String prefix, PrintWriter writer) {
         writer.println(prefix + "DeviceProfile:");
@@ -1382,7 +1420,7 @@
         writer.println(prefix + "\tinv.numSearchContainerColumns: "
                 + inv.numSearchContainerColumns);
 
-        writer.println(prefix + "\tminCellSize: " + inv.minCellSize[mTypeIndex] + "dp");
+        writer.println(prefix + dpPointFToString("minCellSize", inv.minCellSize[mTypeIndex]));
 
         writer.println(prefix + pxToDpStr("cellWidthPx", cellWidthPx));
         writer.println(prefix + pxToDpStr("cellHeightPx", cellHeightPx));
@@ -1422,9 +1460,15 @@
         writer.println(prefix + pxToDpStr("folderTopPadding", folderContentPaddingTop));
 
         writer.println(prefix + pxToDpStr("bottomSheetTopPadding", bottomSheetTopPadding));
+        writer.println(prefix + "\tbottomSheetOpenDuration: " + bottomSheetOpenDuration);
+        writer.println(prefix + "\tbottomSheetCloseDuration: " + bottomSheetCloseDuration);
+        writer.println(prefix + "\tbottomSheetWorkspaceScale: " + bottomSheetWorkspaceScale);
+        writer.println(prefix + "\tbottomSheetDepth: " + bottomSheetDepth);
 
         writer.println(prefix + pxToDpStr("allAppsShiftRange", allAppsShiftRange));
         writer.println(prefix + pxToDpStr("allAppsTopPadding", allAppsTopPadding));
+        writer.println(prefix + "\tallAppsOpenDuration: " + allAppsOpenDuration);
+        writer.println(prefix + "\tallAppsCloseDuration: " + allAppsCloseDuration);
         writer.println(prefix + pxToDpStr("allAppsIconSizePx", allAppsIconSizePx));
         writer.println(prefix + pxToDpStr("allAppsIconTextSizePx", allAppsIconTextSizePx));
         writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx",
@@ -1662,7 +1706,7 @@
                 mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
             }
             if (mIsGestureMode == null) {
-                mIsGestureMode = DisplayController.getNavigationMode(mContext).hasGestures;
+                mIsGestureMode = mInfo.navigationMode.hasGestures;
             }
             if (mDotRendererCache == null) {
                 mDotRendererCache = new SparseArray<>();
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index d64cb26..98ecf3a 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -151,6 +151,8 @@
             int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
 
             ButtonDropTarget firstButton = mTempTargets[0];
+            firstButton.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+                    mLauncher.getDeviceProfile().dropTargetTextSizePx);
             firstButton.setTextVisible(true);
             firstButton.setIconVisible(true);
             firstButton.measure(widthSpec, heightSpec);
@@ -160,14 +162,16 @@
             int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
 
             ButtonDropTarget firstButton = mTempTargets[0];
+            firstButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.dropTargetTextSizePx);
             firstButton.setTextVisible(true);
             firstButton.setIconVisible(true);
             firstButton.setTextMultiLine(false);
-            // Reset second button padding in case it was previously changed to multi-line text.
+            // Reset first button padding in case it was previously changed to multi-line text.
             firstButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
                     verticalPadding);
 
             ButtonDropTarget secondButton = mTempTargets[1];
+            secondButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.dropTargetTextSizePx);
             secondButton.setTextVisible(true);
             secondButton.setIconVisible(true);
             secondButton.setTextMultiLine(false);
@@ -175,20 +179,14 @@
             secondButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
                     verticalPadding);
 
-            float scale = dp.getWorkspaceSpringLoadScale(mLauncher);
-            int scaledPanelWidth = (int) (dp.getCellLayoutWidth() * scale);
-
             int availableWidth;
             if (dp.isTwoPanels) {
-                // Both buttons for two panel fit to the width of one Cell Layout (less
-                // half of the center gap between the buttons).
-                int halfButtonGap = dp.dropTargetGapPx / 2;
-                availableWidth = scaledPanelWidth - halfButtonGap / 2;
+                // Each button for two panel fits to half the width of the screen excluding the
+                // center gap between the buttons.
+                availableWidth = (dp.availableWidthPx - dp.dropTargetGapPx) / 2;
             } else {
-                // Both buttons plus the button gap do not display past the edge of the scaled
-                // workspace, less a pre-defined gap from the edge of the workspace.
-                availableWidth = scaledPanelWidth - dp.dropTargetGapPx
-                        - 2 * dp.dropTargetButtonWorkspaceEdgeGapPx;
+                // Both buttons plus the button gap do not display past the edge of the screen.
+                availableWidth = dp.availableWidthPx - dp.dropTargetGapPx;
             }
 
             int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
@@ -219,6 +217,15 @@
                             horizontalPadding, verticalPadding / 2);
                 }
             }
+
+            // If text is still truncated, shrink to fit in measured width and resize both targets.
+            float minTextSize =
+                    Math.min(firstButton.resizeTextToFit(), secondButton.resizeTextToFit());
+            if (firstButton.getTextSize() != minTextSize
+                    || secondButton.getTextSize() != minTextSize) {
+                firstButton.setTextSize(minTextSize);
+                secondButton.setTextSize(minTextSize);
+            }
         }
         setMeasuredDimension(width, height);
     }
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 4629ca7..11f2020 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3;
 
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.SHOW;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -33,8 +33,6 @@
  * Note: AppCompatEditText doesn't fully support #displayCompletions and #onCommitCompletion
  */
 public class ExtendedEditText extends EditText {
-
-    private boolean mShowImeAfterFirstLayout;
     private boolean mForceDisableSuggestions = false;
 
     /**
@@ -85,28 +83,21 @@
         return false;
     }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        if (mShowImeAfterFirstLayout) {
-            // soft input only shows one frame after the layout of the EditText happens,
-            post(() -> {
-                showSoftInput();
-                mShowImeAfterFirstLayout = false;
-            });
-        }
-    }
-
-
     public void showKeyboard() {
-        mShowImeAfterFirstLayout = !showSoftInput();
+        onKeyboardShown();
+        showSoftInput();
     }
 
     public void hideKeyboard() {
-        hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getWindowToken());
+        ActivityContext.lookupContext(getContext()).hideKeyboard();
         clearFocus();
     }
 
+    protected void onKeyboardShown() {
+        ActivityContext.lookupContext(getContext()).getStatsLogManager()
+                .keyboardStateManager().setKeyboardState(SHOW);
+    }
+
     private boolean showSoftInput() {
         return requestFocus() &&
                 getContext().getSystemService(InputMethodManager.class)
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4a52d3e..ce95b15 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -42,6 +42,7 @@
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 import static com.android.launcher3.logging.StatsLogManager.EventEnum;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -194,8 +195,6 @@
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.launcher3.util.ViewCapture;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.FloatingIconView;
@@ -393,7 +392,6 @@
     private LauncherState mPrevLauncherState;
 
     private StringCache mStringCache;
-    private ViewCapture mViewCapture;
 
     @Override
     @TargetApi(Build.VERSION_CODES.S)
@@ -1125,6 +1123,7 @@
                         .log(getAllAppsEntryEvent().get());
             }
         }
+        updateDisallowBack();
     }
 
     /**
@@ -1488,23 +1487,11 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         mOverlayManager.onAttachedToWindow();
-        if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
-            View root = getDragLayer().getRootView();
-            if (mViewCapture != null) {
-                mViewCapture.detach();
-            }
-            mViewCapture = new ViewCapture(getWindow());
-            mViewCapture.attach();
-        }
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        if (mViewCapture != null) {
-            mViewCapture.detach();
-            mViewCapture = null;
-        }
         mOverlayManager.onDetachedFromWindow();
         closeContextMenu();
     }
@@ -1667,16 +1654,6 @@
         }
     }
 
-    /**
-     * Hides the keyboard if visible
-     */
-    public void hideKeyboard() {
-        final View v = getWindow().peekDecorView();
-        if (v != null && v.getWindowToken() != null) {
-            UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken());
-        }
-    }
-
     @Override
     public void onRestoreInstanceState(Bundle state) {
         super.onRestoreInstanceState(state);
@@ -2985,7 +2962,6 @@
      */
     @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        SafeCloseable viewDump = mViewCapture == null ? null : mViewCapture.beginDump(writer, fd);
         super.dump(prefix, fd, writer, args);
 
         if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
@@ -3026,10 +3002,6 @@
         mPopupDataProvider.dump(prefix, writer);
         mDeviceProfile.dump(this, prefix, writer);
 
-        if (viewDump != null) {
-            viewDump.close();
-        }
-
         try {
             FileLog.flushAll(writer);
         } catch (Exception e) {
@@ -3152,7 +3124,18 @@
 
     public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { }
 
-    public void onDragLayerHierarchyChanged() { }
+    public void onDragLayerHierarchyChanged() {
+        updateDisallowBack();
+    }
+
+    private void updateDisallowBack() {
+        LauncherRootView rv = getRootView();
+        if (rv != null) {
+            boolean disableBack = getStateManager().getState() == NORMAL
+                    && AbstractFloatingView.getTopOpenView(this) == null;
+            rv.setDisallowBackGesture(disableBack);
+        }
+    }
 
     @Override
     public void returnToHomescreen() {
@@ -3252,12 +3235,10 @@
      * @param progress Transition progress from 0 to 1; where 0 => home and 1 => widgets.
      */
     public void onWidgetsTransition(float progress) {
-        if (mDeviceProfile.isTablet) {
-            float scale =
-                    Utilities.comp(Utilities.comp(mDeviceProfile.workspaceContentScale) * progress);
-            WORKSPACE_WIDGET_SCALE.set(getWorkspace(), scale);
-            HOTSEAT_WIDGET_SCALE.set(getHotseat(), scale);
-        }
+        float scale = Utilities.mapToRange(progress, 0f, 1f, 1f,
+                mDeviceProfile.bottomSheetWorkspaceScale, EMPHASIZED);
+        WORKSPACE_WIDGET_SCALE.set(getWorkspace(), scale);
+        HOTSEAT_WIDGET_SCALE.set(getHotseat(), scale);
     }
 
     private static class NonConfigInstance {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 4532ed4..31a7d18 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -71,17 +71,14 @@
     public static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = BaseState.getFlag(2);
     // Flag to indicate that workspace should draw page background
     public static final int FLAG_WORKSPACE_HAS_BACKGROUNDS = BaseState.getFlag(3);
-    // True if the back button should be hidden when in this state (assuming no floating views are
-    // open, launcher has window focus, etc).
-    public static final int FLAG_HIDE_BACK_BUTTON = BaseState.getFlag(4);
     // Flag to indicate if the state would have scrim over sysui region: statu sbar and nav bar
-    public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(5);
+    public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(4);
     // Flag to inticate that all popups should be closed when this state is enabled.
-    public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(6);
-    public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
+    public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(5);
+    public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(6);
 
     // Flag indicating that hotseat and its contents are not accessible.
-    public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(8);
+    public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(7);
 
 
     public static final float NO_OFFSET = 0;
@@ -110,8 +107,7 @@
      */
     public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
             LAUNCHER_STATE_HOME,
-            FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
-                    FLAG_HAS_SYS_UI_SCRIM) {
+            FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HAS_SYS_UI_SCRIM) {
         @Override
         public int getTransitionDuration(Context context, boolean isToState) {
             // Arbitrary duration, when going to NORMAL we use the state we're coming from instead.
@@ -265,7 +261,8 @@
      *
      * 0 means completely zoomed in, without blurs. 1 is zoomed out, with blurs.
      */
-    public final float getDepth(Context context) {
+    public final  <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
+            float getDepth(DEVICE_PROFILE_CONTEXT context) {
         return getDepth(context,
                 BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode);
     }
@@ -275,14 +272,16 @@
      *
      * @see #getDepth(Context).
      */
-    public final float getDepth(Context context, boolean isMultiWindowMode) {
+    public final <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
+            float getDepth(DEVICE_PROFILE_CONTEXT context, boolean isMultiWindowMode) {
         if (isMultiWindowMode) {
             return 0;
         }
         return getDepthUnchecked(context);
     }
 
-    protected float getDepthUnchecked(Context context) {
+    protected <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfile.DeviceProfileListenable>
+            float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
         return 0f;
     }
 
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index be994ee..b7a22fc 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.model.data.ItemInfo;
@@ -43,6 +44,7 @@
     /**
      * Returns shallow copy of the object.
      */
+    @NonNull
     @Override
     public ItemInfo makeShallowCopy() {
         PendingAddItemInfo itemInfo = new PendingAddItemInfo();
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index e33e44e..aa9cfd1 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.SEARCH;
+
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
@@ -34,7 +36,6 @@
 import com.android.launcher3.views.AppLauncher;
 
 import java.util.ArrayList;
-import java.util.Objects;
 
 /**
  * All apps container view with search support for use in a dragging activity.
@@ -44,6 +45,11 @@
 public class ActivityAllAppsContainerView<T extends Context & AppLauncher
         & DeviceProfileListenable> extends BaseAllAppsContainerView<T> {
 
+    private static final long DEFAULT_SEARCH_TRANSITION_DURATION_MS = 300;
+
+    // Used to animate Search results out and A-Z apps in, or vice-versa.
+    private final SearchTransitionController mSearchTransitionController;
+
     protected SearchUiManager mSearchUiManager;
     /**
      * View that defines the search box. Result is rendered inside the recycler view defined in the
@@ -52,6 +58,7 @@
     private View mSearchContainer;
     /** {@code true} when rendered view is in search state instead of the scroll state. */
     private boolean mIsSearching;
+    private boolean mRebindAdaptersAfterSearchAnimation;
 
     public ActivityAllAppsContainerView(Context context) {
         this(context, null);
@@ -63,6 +70,8 @@
 
     public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+
+        mSearchTransitionController = new SearchTransitionController(this);
     }
 
     public SearchUiManager getSearchUiManager() {
@@ -73,19 +82,10 @@
         return mSearchContainer;
     }
 
-    /** Updates all apps container with the latest search query. */
-    public void setLastSearchQuery(String query) {
-        mIsSearching = true;
-        rebindAdapters();
-        mHeader.setCollapsed(true);
-    }
-
     /** Invoke when the current search session is finished. */
     public void onClearSearchResult() {
-        mIsSearching = false;
-        mHeader.setCollapsed(false);
+        animateToSearchState(false);
         rebindAdapters();
-        mHeader.reset(false);
     }
 
     /**
@@ -93,12 +93,42 @@
      */
     public void setSearchResults(ArrayList<AdapterItem> results) {
         if (getSearchResultList().setSearchResults(results)) {
-            for (int i = 0; i < mAH.size(); i++) {
-                if (mAH.get(i).mRecyclerView != null) {
-                    mAH.get(i).mRecyclerView.onSearchResultsChanged();
-                }
-            }
+            getSearchRecyclerView().onSearchResultsChanged();
         }
+        if (results != null) {
+            animateToSearchState(true);
+        }
+    }
+
+    private void animateToSearchState(boolean goingToSearch) {
+        animateToSearchState(goingToSearch, DEFAULT_SEARCH_TRANSITION_DURATION_MS);
+    }
+
+    private void animateToSearchState(boolean goingToSearch, long durationMs) {
+        if (!mSearchTransitionController.isRunning() && goingToSearch == isSearching()) {
+            return;
+        }
+        if (goingToSearch) {
+            // Fade out the button to pause work apps.
+            mWorkManager.onActivePageChanged(SEARCH);
+        }
+        mSearchTransitionController.animateToSearchState(goingToSearch, durationMs,
+                /* onEndRunnable = */ () -> {
+                    mIsSearching = goingToSearch;
+                    updateSearchResultsVisibility();
+                    int previousPage = getCurrentPage();
+                    if (mRebindAdaptersAfterSearchAnimation) {
+                        rebindAdapters(false);
+                        mRebindAdaptersAfterSearchAnimation = false;
+                    }
+                    if (!goingToSearch) {
+                        setSearchResults(null);
+                        if (mViewPager != null) {
+                            mViewPager.setCurrentPage(previousPage);
+                        }
+                        onActivePageChanged(previousPage);
+                    }
+                });
     }
 
     @Override
@@ -121,6 +151,8 @@
         super.reset(animate);
         // Reset the search bar after transitioning home.
         mSearchUiManager.resetSearch();
+        // Animate to A-Z with 0 time to reset the animation with proper state management.
+        animateToSearchState(false, 0);
     }
 
     @Override
@@ -157,21 +189,30 @@
     }
 
     @Override
+    public void onActivePageChanged(int currentActivePage) {
+        if (mSearchTransitionController.isRunning()) {
+            // Will be called at the end of the animation.
+            return;
+        }
+        super.onActivePageChanged(currentActivePage);
+    }
+
+    @Override
     protected void rebindAdapters(boolean force) {
+        if (mSearchTransitionController.isRunning()) {
+            mRebindAdaptersAfterSearchAnimation = true;
+            return;
+        }
         super.rebindAdapters(force);
         if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
-                || getMainAdapterProvider().getDecorator() == null) {
+                || getMainAdapterProvider().getDecorator() == null
+                || getSearchRecyclerView() == null) {
             return;
         }
 
         RecyclerView.ItemDecoration decoration = getMainAdapterProvider().getDecorator();
-        mAH.stream()
-                .map(adapterHolder -> adapterHolder.mRecyclerView)
-                .filter(Objects::nonNull)
-                .forEach(v -> {
-                    v.removeItemDecoration(decoration); // Remove in case it is already added.
-                    v.addItemDecoration(decoration);
-                });
+        getSearchRecyclerView().removeItemDecoration(decoration); // In case it is already added.
+        getSearchRecyclerView().addItemDecoration(decoration);
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 31e3890..21a7dfb 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -21,7 +21,6 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
 import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
 
 import android.content.Context;
 import android.graphics.Canvas;
@@ -162,8 +161,7 @@
                 requestFocus();
                 mgr.logger().sendToInteractionJankMonitor(
                         LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
-                hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
-                        getApplicationWindowToken());
+                ActivityContext.lookupContext(getContext()).hideKeyboard();
                 break;
             case SCROLL_STATE_IDLE:
                 mgr.logger().sendToInteractionJankMonitor(
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index ecc9d7e..22e8bcf 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -42,7 +42,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.MultiAdditivePropertyFactory;
+import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.views.ScrimView;
 
@@ -141,9 +141,9 @@
 
     private ScrimView mScrimView;
 
-    private final MultiAdditivePropertyFactory<View>
-            mAppsViewTranslationYPropertyFactory = new MultiAdditivePropertyFactory<>(
-            "appsViewTranslationY", View.TRANSLATION_Y);
+    private final MultiPropertyFactory<View>
+            mAppsViewTranslationYPropertyFactory = new MultiPropertyFactory<>(
+            "appsViewTranslationY", View.TRANSLATION_Y, Float::sum);
     private MultiValueAlpha mAppsViewAlpha;
 
     private boolean mIsTablet;
@@ -227,7 +227,7 @@
     @Override
     public void setStateWithAnimation(LauncherState toState,
             StateAnimationConfig config, PendingAnimation builder) {
-        if (NORMAL.equals(toState) && mLauncher.isInState(ALL_APPS)) {
+        if (mLauncher.isInState(ALL_APPS) && !ALL_APPS.equals(toState)) {
             builder.addEndListener(success -> {
                 // Reset pull back progress and alpha after switching states.
                 ALL_APPS_PULL_BACK_TRANSLATION.set(this, 0f);
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 6990e57..42f8b0c 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -127,6 +127,11 @@
         public boolean isContentSame(AdapterItem other) {
             return itemInfo == null && other.itemInfo == null;
         }
+
+        /** Sets the alpha of the decorator for this item. Returns true if successful. */
+        public boolean setDecorationFillAlpha(int alpha) {
+            return false;
+        }
     }
 
     protected final T mActivityContext;
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index f3c5dd6..879a159 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -107,12 +106,13 @@
                     updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
                 }
             };
-    private final WorkProfileManager mWorkManager;
+
+    protected final WorkProfileManager mWorkManager;
 
     private final Paint mNavBarScrimPaint;
     private int mNavBarScrimHeight = 0;
 
-    private AllAppsPagedView mViewPager;
+    protected AllAppsPagedView mViewPager;
     private SearchRecyclerView mSearchRecyclerView;
 
     protected FloatingHeaderView mHeader;
@@ -349,7 +349,7 @@
      * The container for A-Z apps (the ViewPager for main+work tabs, or main RV). This is currently
      * hidden while searching.
      **/
-    private View getAppsRecyclerViewContainer() {
+    protected View getAppsRecyclerViewContainer() {
         return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
     }
 
@@ -503,8 +503,7 @@
                             mActivityContext.getStatsLogManager().logger()
                                     .log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
                         }
-                        hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
-                                getApplicationWindowToken());
+                        mActivityContext.hideKeyboard();
                     });
             findViewById(R.id.tab_work)
                     .setOnClickListener((View view) -> {
@@ -512,8 +511,7 @@
                             mActivityContext.getStatsLogManager().logger()
                                     .log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
                         }
-                        hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
-                                getApplicationWindowToken());
+                        mActivityContext.hideKeyboard();
                     });
             setDeviceManagementResources();
             onActivePageChanged(mViewPager.getNextPage());
@@ -527,7 +525,7 @@
         mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
     }
 
-    private void updateSearchResultsVisibility() {
+    protected void updateSearchResultsVisibility() {
         if (isSearching()) {
             getSearchRecyclerView().setVisibility(VISIBLE);
             getAppsRecyclerViewContainer().setVisibility(GONE);
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index c5bdb69..8ec2aeb 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -42,6 +42,7 @@
 import com.android.systemui.plugins.PluginListener;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Map;
 
 public class FloatingHeaderView extends LinearLayout implements
@@ -82,8 +83,8 @@
     protected final Map<AllAppsRow, PluginHeaderRow> mPluginRows = new ArrayMap<>();
 
     // These two values are necessary to ensure that the header protection is drawn correctly.
-    private final int mHeaderTopAdjustment;
-    private final int mHeaderBottomAdjustment;
+    private final int mTabsAdditionalPaddingTop;
+    private final int mTabsAdditionalPaddingBottom;
     private boolean mHeaderProtectionSupported;
 
     protected ViewGroup mTabLayout;
@@ -91,7 +92,6 @@
     private AllAppsRecyclerView mWorkRV;
     private SearchRecyclerView mSearchRV;
     private AllAppsRecyclerView mCurrentRV;
-    public boolean mHeaderCollapsed;
     protected int mSnappedScrolledY;
     private int mTranslationY;
 
@@ -100,7 +100,12 @@
     protected boolean mTabsHidden;
     protected int mMaxTranslation;
 
-    private boolean mCollapsed = false;
+    // Whether the header has been scrolled off-screen.
+    private boolean mHeaderCollapsed;
+    // Whether floating rows like predicted apps are hidden.
+    private boolean mFloatingRowsCollapsed;
+    // Total height of all current floating rows. Collapsed rows == 0 height.
+    private int mFloatingRowsHeight;
 
     // This is initialized once during inflation and stays constant after that. Fixed views
     // cannot be added or removed dynamically.
@@ -117,9 +122,9 @@
 
     public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
-        mHeaderTopAdjustment = context.getResources()
+        mTabsAdditionalPaddingTop = context.getResources()
                 .getDimensionPixelSize(R.dimen.all_apps_header_top_adjustment);
-        mHeaderBottomAdjustment = context.getResources()
+        mTabsAdditionalPaddingBottom = context.getResources()
                 .getDimensionPixelSize(R.dimen.all_apps_header_bottom_adjustment);
         mHeaderProtectionSupported = context.getResources().getBoolean(
                 R.bool.config_header_protection_supported);
@@ -148,6 +153,7 @@
         }
         mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
         mAllRows = mFixedRows;
+        updateFloatingRowsHeight();
     }
 
     @Override
@@ -179,6 +185,7 @@
                 count++;
             }
         }
+        updateFloatingRowsHeight();
     }
 
     @Override
@@ -195,7 +202,7 @@
         int oldMaxHeight = mMaxTranslation;
         updateExpectedHeight();
 
-        if (mMaxTranslation != oldMaxHeight || mCollapsed) {
+        if (mMaxTranslation != oldMaxHeight || mFloatingRowsCollapsed) {
             BaseAllAppsContainerView<?> parent = (BaseAllAppsContainerView<?>) getParent();
             if (parent != null) {
                 parent.setupHeader();
@@ -258,20 +265,19 @@
     }
 
     private void updateExpectedHeight() {
+        updateFloatingRowsHeight();
         mMaxTranslation = 0;
-        if (mCollapsed) {
+        if (mFloatingRowsCollapsed) {
             return;
         }
-        for (FloatingHeaderRow row : mAllRows) {
-            mMaxTranslation += row.getExpectedHeight();
-        }
+        mMaxTranslation += mFloatingRowsHeight;
         if (!mTabsHidden) {
-            mMaxTranslation += mHeaderBottomAdjustment;
+            mMaxTranslation += mTabsAdditionalPaddingBottom;
         }
     }
 
     public int getMaxTranslation() {
-        if (mMaxTranslation == 0 && mTabsHidden) {
+        if (mMaxTranslation == 0 && (mTabsHidden || mFloatingRowsCollapsed)) {
             return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
         } else if (mMaxTranslation > 0 && mTabsHidden) {
             return mMaxTranslation + getPaddingTop();
@@ -312,7 +318,7 @@
         int uncappedTranslationY = mTranslationY;
         mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
 
-        if (mCollapsed || uncappedTranslationY < mTranslationY - getPaddingTop()) {
+        if (mFloatingRowsCollapsed || uncappedTranslationY < mTranslationY - getPaddingTop()) {
             // we hide it completely if already capped (for opening search anim)
             for (FloatingHeaderRow row : mAllRows) {
                 row.setVerticalScroll(0, true /* isScrolledOut */);
@@ -325,11 +331,11 @@
 
         mTabLayout.setTranslationY(mTranslationY);
 
-        int clipTop = getPaddingTop() - mHeaderTopAdjustment;
+        int clipTop = getPaddingTop() - mTabsAdditionalPaddingTop;
         if (mTabsHidden) {
-            clipTop += getPaddingBottom() - mHeaderBottomAdjustment;
+            clipTop += getPaddingBottom() - mTabsAdditionalPaddingBottom;
         }
-        mRVClip.top = mTabsHidden ? clipTop : 0;
+        mRVClip.top = mTabsHidden || mFloatingRowsCollapsed ? clipTop : 0;
         mHeaderClip.top = clipTop;
         // clipping on a draw might cause additional redraw
         setClipBounds(mHeaderClip);
@@ -347,10 +353,12 @@
     /**
      * Hides all the floating rows
      */
-    public void setCollapsed(boolean collapse) {
-        if (mCollapsed == collapse) return;
+    public void setFloatingRowsCollapsed(boolean collapsed) {
+        if (mFloatingRowsCollapsed == collapsed) {
+            return;
+        }
 
-        mCollapsed = collapse;
+        mFloatingRowsCollapsed = collapsed;
         onHeightUpdated();
     }
 
@@ -376,6 +384,30 @@
         return !mHeaderCollapsed;
     }
 
+    /** Returns true if personal/work tabs are currently in use. */
+    public boolean usingTabs() {
+        return !mTabsHidden;
+    }
+
+    ViewGroup getTabLayout() {
+        return mTabLayout;
+    }
+
+    /** Calculates the combined height of any floating rows (e.g. predicted apps, app divider). */
+    private void updateFloatingRowsHeight() {
+        mFloatingRowsHeight =
+                Arrays.stream(mAllRows).mapToInt(FloatingHeaderRow::getExpectedHeight).sum();
+    }
+
+    /** Gets the combined height of any floating rows (e.g. predicted apps, app divider). */
+    int getFloatingRowsHeight() {
+        return mFloatingRowsHeight;
+    }
+
+    int getTabsAdditionalPaddingBottom() {
+        return mTabsAdditionalPaddingBottom;
+    }
+
     @Override
     public void onAnimationUpdate(ValueAnimator animation) {
         mTranslationY = (Integer) animation.getAnimatedValue();
@@ -447,9 +479,10 @@
 
         // we only want to show protection when work tab is available and header is either
         // collapsed or animating to/from collapsed state
-        if (mTabsHidden || !mHeaderCollapsed) {
+        if (mTabsHidden || mFloatingRowsCollapsed || !mHeaderCollapsed) {
             return 0;
         }
-        return Math.max(getHeight() - getPaddingTop() + mTranslationY, 0);
+        return Math.max(0,
+                getTabLayout().getBottom() - getPaddingTop() + getPaddingBottom() + mTranslationY);
     }
 }
diff --git a/src/com/android/launcher3/allapps/SearchRecyclerView.java b/src/com/android/launcher3/allapps/SearchRecyclerView.java
index 482bd29..9d1dfc0 100644
--- a/src/com/android/launcher3/allapps/SearchRecyclerView.java
+++ b/src/com/android/launcher3/allapps/SearchRecyclerView.java
@@ -17,12 +17,17 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Consumer;
 
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
 /** A RecyclerView for AllApps Search results. */
 public class SearchRecyclerView extends AllAppsRecyclerView {
-    private static final String TAG = "SearchRecyclerView";
+
+    private Consumer<View> mChildAttachedConsumer;
 
     public SearchRecyclerView(Context context) {
         this(context, null);
@@ -41,6 +46,11 @@
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
+    /** This will be called just before a new child is attached to the window. */
+    public void setChildAttachedConsumer(Consumer<View> childAttachedConsumer) {
+        mChildAttachedConsumer = childAttachedConsumer;
+    }
+
     @Override
     protected void updatePoolSize() {
         RecycledViewPool pool = getRecycledViewPool();
@@ -57,4 +67,12 @@
     public RecyclerViewFastScroller getScrollbar() {
         return null;
     }
+
+    @Override
+    public void onChildAttachedToWindow(@NonNull View child) {
+        if (mChildAttachedConsumer != null) {
+            mChildAttachedConsumer.accept(child);
+        }
+        super.onChildAttachedToWindow(child);
+    }
 }
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
new file mode 100644
index 0000000..03529af
--- /dev/null
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.allapps;
+
+import static android.view.View.VISIBLE;
+
+import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
+
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
+
+import android.animation.ObjectAnimator;
+import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+
+/** Coordinates the transition between Search and A-Z in All Apps. */
+public class SearchTransitionController {
+
+    // Interpolator when the user taps the QSB while already in All Apps.
+    private static final Interpolator DEFAULT_INTERPOLATOR_WITHIN_ALL_APPS = DEACCEL_1_7;
+    // Interpolator when the user taps the QSB from home screen, so transition to all apps is
+    // happening simultaneously.
+    private static final Interpolator DEFAULT_INTERPOLATOR_TRANSITIONING_TO_ALL_APPS = LINEAR;
+
+    /**
+     * These values represent points on the [0, 1] animation progress spectrum. They are used to
+     * animate items in the {@link SearchRecyclerView}.
+     */
+    private static final float TOP_CONTENT_FADE_PROGRESS_START = 0.133f;
+    private static final float CONTENT_FADE_PROGRESS_DURATION = 0.083f;
+    private static final float TOP_BACKGROUND_FADE_PROGRESS_START = 0.633f;
+    private static final float BACKGROUND_FADE_PROGRESS_DURATION = 0.15f;
+    private static final float CONTENT_STAGGER = 0.01f;  // Progress before next item starts fading.
+
+    private static final FloatProperty<SearchTransitionController> SEARCH_TO_AZ_PROGRESS =
+            new FloatProperty<SearchTransitionController>("searchToAzProgress") {
+                @Override
+                public Float get(SearchTransitionController controller) {
+                    return controller.getSearchToAzProgress();
+                }
+
+                @Override
+                public void setValue(SearchTransitionController controller, float progress) {
+                    controller.setSearchToAzProgress(progress);
+                }
+            };
+
+    private final ActivityAllAppsContainerView<?> mAllAppsContainerView;
+
+    private ObjectAnimator mSearchToAzAnimator = null;
+    private float mSearchToAzProgress = 1f;
+
+    public SearchTransitionController(ActivityAllAppsContainerView<?> allAppsContainerView) {
+        mAllAppsContainerView = allAppsContainerView;
+    }
+
+    /** Returns true if a transition animation is currently in progress. */
+    public boolean isRunning() {
+        return mSearchToAzAnimator != null;
+    }
+
+    /**
+     * Starts the transition to or from search state. If a transition is already in progress, the
+     * animation will start from that point with the new duration, and the previous onEndRunnable
+     * will not be called.
+     *
+     * @param goingToSearch true if will be showing search results, otherwise will be showing a-z
+     * @param duration time in ms for the animation to run
+     * @param onEndRunnable will be called when the animation finishes, unless another animation is
+     *                      scheduled in the meantime
+     */
+    public void animateToSearchState(boolean goingToSearch, long duration, Runnable onEndRunnable) {
+        float targetProgress = goingToSearch ? 0 : 1;
+
+        if (mSearchToAzAnimator != null) {
+            mSearchToAzAnimator.cancel();
+        }
+
+        mSearchToAzAnimator = ObjectAnimator.ofFloat(this, SEARCH_TO_AZ_PROGRESS, targetProgress);
+        boolean inAllApps = Launcher.getLauncher(
+                mAllAppsContainerView.getContext()).getStateManager().isInStableState(
+                LauncherState.ALL_APPS);
+        mSearchToAzAnimator.setDuration(duration).setInterpolator(
+                inAllApps ? DEFAULT_INTERPOLATOR_WITHIN_ALL_APPS
+                        : DEFAULT_INTERPOLATOR_TRANSITIONING_TO_ALL_APPS);
+        mSearchToAzAnimator.addListener(forEndCallback(() -> mSearchToAzAnimator = null));
+        if (!goingToSearch) {
+            mSearchToAzAnimator.addListener(forSuccessCallback(() -> {
+                mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(false);
+                mAllAppsContainerView.getFloatingHeaderView().reset(false /* animate */);
+                mAllAppsContainerView.getAppsRecyclerViewContainer().setTranslationY(0);
+            }));
+        }
+        mSearchToAzAnimator.addListener(forSuccessCallback(onEndRunnable));
+
+        mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(true);
+        mAllAppsContainerView.getAppsRecyclerViewContainer().setVisibility(VISIBLE);
+        getSearchRecyclerView().setVisibility(VISIBLE);
+        getSearchRecyclerView().setChildAttachedConsumer(this::onSearchChildAttached);
+        mSearchToAzAnimator.start();
+    }
+
+    private SearchRecyclerView getSearchRecyclerView() {
+        return mAllAppsContainerView.getSearchRecyclerView();
+    }
+
+    private void setSearchToAzProgress(float searchToAzProgress) {
+        mSearchToAzProgress = searchToAzProgress;
+        int searchHeight = updateSearchRecyclerViewProgress();
+
+        FloatingHeaderView headerView = mAllAppsContainerView.getFloatingHeaderView();
+
+        // Add predictions + app divider height to account for predicted apps which will now be in
+        // the Search RV instead of the floating header view. Note `getFloatingRowsHeight` returns 0
+        // when predictions are not shown.
+        int appsTranslationY = searchHeight + headerView.getFloatingRowsHeight();
+
+        if (headerView.usingTabs()) {
+            // Move tabs below the search results, and fade them out in 20% of the animation.
+            headerView.setTranslationY(searchHeight);
+            headerView.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));
+
+            // Account for the additional padding added for the tabs.
+            appsTranslationY -=
+                    headerView.getPaddingTop() - headerView.getTabsAdditionalPaddingBottom();
+        }
+
+        View appsContainer = mAllAppsContainerView.getAppsRecyclerViewContainer();
+        appsContainer.setTranslationY(appsTranslationY);
+        // Fade apps out with tabs (in 20% of the total animation).
+        appsContainer.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));
+    }
+
+    /**
+     * Updates the children views of SearchRecyclerView based on the current animation progress.
+     *
+     * @return the total height of animating views (excluding any app icons).
+     */
+    private int updateSearchRecyclerViewProgress() {
+        int numSearchResultsAnimated = 0;
+        int totalHeight = 0;
+        int appRowHeight = 0;
+        Integer top = null;
+        SearchRecyclerView searchRecyclerView = getSearchRecyclerView();
+        for (int i = 0; i < searchRecyclerView.getChildCount(); i++) {
+            View searchResultView = searchRecyclerView.getChildAt(i);
+            if (searchResultView == null) {
+                continue;
+            }
+
+            if (top == null) {
+                top = searchResultView.getTop();
+            }
+
+            if (searchResultView instanceof BubbleTextView) {
+                // The first app icon will set appRowHeight, which will also contribute to
+                // totalHeight. Additional app icons should remove the appRowHeight to remain in
+                // the same row as the first app.
+                searchResultView.setY(top + totalHeight - appRowHeight);
+                if (appRowHeight == 0) {
+                    appRowHeight = searchResultView.getHeight();
+                    totalHeight += appRowHeight;
+                }
+                // Don't scale/fade app row.
+                continue;
+            }
+
+            // Adjust content alpha based on start progress and stagger.
+            float startContentFadeProgress = Math.max(0,
+                    TOP_CONTENT_FADE_PROGRESS_START - CONTENT_STAGGER * numSearchResultsAnimated);
+            float endContentFadeProgress = Math.min(1,
+                    startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION);
+            searchResultView.setAlpha(1 - clampToProgress(mSearchToAzProgress,
+                    startContentFadeProgress, endContentFadeProgress));
+
+            // Adjust background (or decorator) alpha based on start progress and stagger.
+            float startBackgroundFadeProgress = Math.max(0,
+                    TOP_BACKGROUND_FADE_PROGRESS_START
+                            - CONTENT_STAGGER * numSearchResultsAnimated);
+            float endBackgroundFadeProgress = Math.min(1,
+                    startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION);
+            float backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress,
+                    startBackgroundFadeProgress, endBackgroundFadeProgress);
+            int adapterPosition = searchRecyclerView.getChildAdapterPosition(searchResultView);
+            boolean decoratorFilled =
+                    adapterPosition != NO_POSITION
+                            && searchRecyclerView.getApps().getAdapterItems().get(adapterPosition)
+                            .setDecorationFillAlpha((int) (255 * backgroundAlpha));
+            if (!decoratorFilled) {
+                // Try to adjust background alpha instead (e.g. for Search Edu card).
+                Drawable background = searchResultView.getBackground();
+                if (background != null) {
+                    background.setAlpha((int) (255 * backgroundAlpha));
+                }
+            }
+
+            float scaleY = 1 - mSearchToAzProgress;
+            int scaledHeight = (int) (searchResultView.getHeight() * scaleY);
+            searchResultView.setScaleY(scaleY);
+            searchResultView.setY(top + totalHeight);
+
+            numSearchResultsAnimated++;
+            totalHeight += scaledHeight;
+        }
+
+        return totalHeight - appRowHeight;
+    }
+
+    /** Called just before a child is attached to the SearchRecyclerView. */
+    private void onSearchChildAttached(View child) {
+        // Avoid allocating hardware layers for alpha changes.
+        child.forceHasOverlappingRendering(false);
+        if (mSearchToAzProgress > 0) {
+            // Before the child is rendered, apply the animation including it to avoid flicker.
+            updateSearchRecyclerViewProgress();
+        } else {
+            // Apply default states without processing the full layout.
+            child.setAlpha(1);
+            child.setScaleY(1);
+            child.setTranslationY(0);
+            int adapterPosition = getSearchRecyclerView().getChildAdapterPosition(child);
+            if (adapterPosition != NO_POSITION) {
+                getSearchRecyclerView().getApps().getAdapterItems().get(adapterPosition)
+                        .setDecorationFillAlpha(255);
+            }
+            if (child.getBackground() != null) {
+                child.getBackground().setAlpha(255);
+            }
+        }
+    }
+
+    private float getSearchToAzProgress() {
+        return mSearchToAzProgress;
+    }
+}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index fedc91f..a589448 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -170,12 +170,14 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (Utilities.ATLEAST_R && isEnabled()) {
+        if (!Utilities.ATLEAST_R) {
+            return insets;
+        }
+        if (insets.isVisible(WindowInsets.Type.ime())) {
+            Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
+            setTranslationY(mInsets.bottom - keyboardInsets.bottom);
+        } else {
             setTranslationY(0);
-            if (insets.isVisible(WindowInsets.Type.ime())) {
-                Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
-                setTranslationY(mInsets.bottom - keyboardInsets.bottom);
-            }
         }
         return insets;
     }
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 52d8f63..78c305b 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -168,14 +168,11 @@
     public void onSearchResult(String query, ArrayList<AdapterItem> items) {
         if (items != null) {
             mAppsView.setSearchResults(items);
-            mAppsView.setLastSearchQuery(query);
         }
     }
 
     @Override
     public void clearSearchResult() {
-        mAppsView.setSearchResults(null);
-
         // Clear the search query
         mSearchQueryBuilder.clear();
         mSearchQueryBuilder.clearSpans();
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
index a312070..6196df2 100644
--- a/src/com/android/launcher3/anim/AnimationSuccessListener.java
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -19,6 +19,8 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 
+import androidx.annotation.CallSuper;
+
 /**
  * Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations
  */
@@ -27,6 +29,7 @@
     protected boolean mCancelled = false;
 
     @Override
+    @CallSuper
     public void onAnimationCancel(Animator animation) {
         mCancelled = true;
     }
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 0a77aa7..12b4223 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -57,6 +57,10 @@
     public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
     public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
 
+    /**
+     * The default emphasized interpolator. Used for hero / emphasized movement of content.
+     */
+    public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
     public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
             0.3f, 0f, 0.8f, 0.15f);
     public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
@@ -87,7 +91,6 @@
     public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL =
             v -> ACCEL_DEACCEL.getInterpolation(TOUCH_RESPONSE_INTERPOLATOR.getInterpolation(v));
 
-
     /**
      * Inversion of ZOOM_OUT, compounded with an ease-out.
      */
@@ -218,4 +221,14 @@
     public static Interpolator reverse(Interpolator interpolator) {
         return t -> 1 - interpolator.getInterpolation(1 - t);
     }
+
+    // Create the default emphasized interpolator
+    private static PathInterpolator createEmphasizedInterpolator() {
+        Path path = new Path();
+        // Doing the same as fast_out_extra_slow_in
+        path.moveTo(0f, 0f);
+        path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
+        path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
+        return new PathInterpolator(path);
+    }
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 82a47b0..4b647cd 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -65,12 +65,12 @@
      */
     public static final BooleanFlag ENABLE_INPUT_CONSUMER_REASON_LOGGING = getDebugFlag(
             "ENABLE_INPUT_CONSUMER_REASON_LOGGING",
-            false,
+            true,
             "Log the reason why an Input Consumer was selected for a gesture.");
 
     public static final BooleanFlag ENABLE_GESTURE_ERROR_DETECTION = getDebugFlag(
             "ENABLE_GESTURE_ERROR_DETECTION",
-            false,
+            true,
             "Analyze gesture events and log detected errors");
 
     // When enabled the promise icon is visible in all apps while installation an app.
@@ -274,6 +274,10 @@
             "USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", false,
             "Use local overrides for search request timeout");
 
+    public static final BooleanFlag USE_APP_SEARCH_FOR_WEB = getDebugFlag(
+            "USE_APP_SEARCH_FOR_WEB", false,
+            "Use app search to request zero state web suggestions");
+
     public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(
             "CONTINUOUS_VIEW_TREE_CAPTURE", false, "Capture View tree every frame");
 
@@ -281,6 +285,19 @@
             "FOLDABLE_WORKSPACE_REORDER", true,
             "In foldables, when reordering the icons and widgets, is now going to use both sides");
 
+    public static final BooleanFlag SHOW_SEARCH_EDUCARD_QSB = new DeviceFlag(
+            "SHOW_SEARCH_EDUCARD_QSB", false, "Shows Search Educard for QSB entry in OneSearch.");
+
+    public static final BooleanFlag ENABLE_IME_LATENCY_LOGGER = getDebugFlag(
+            "ENABLE_IME_LATENCY_LOGGER", false,
+            "Enable option to log the keyboard latency for both atomic and controlled keyboard "
+                    + "animations on an EditText");
+
+    // Change of wallpaper depth in widget picker is disabled for tests as it causes flakiness on
+    // very slow cuttlefish devices.
+    public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
+            "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index f027b33..7457f30 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -16,24 +16,16 @@
 
 package com.android.launcher3.graphics;
 
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.FastBitmapDrawable;
@@ -41,8 +33,6 @@
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
-import java.nio.ByteBuffer;
-
 /**
  * A utility class to generate preview bitmap for dragging.
  */
@@ -57,9 +47,6 @@
 
     public final int blurSizeOutline;
 
-    private OutlineGeneratorCallback mOutlineGeneratorCallback;
-    public Bitmap generatedDragOutline;
-
     public DragPreviewProvider(View view) {
         this(view, view.getContext());
     }
@@ -129,15 +116,6 @@
         return null;
     }
 
-    public final void generateDragOutline(Bitmap preview) {
-        if (FeatureFlags.IS_STUDIO_BUILD && mOutlineGeneratorCallback != null) {
-            throw new RuntimeException("Drag outline generated twice");
-        }
-
-        mOutlineGeneratorCallback = new OutlineGeneratorCallback(preview);
-        UI_HELPER_EXECUTOR.post(mOutlineGeneratorCallback);
-    }
-
     protected static Rect getDrawableBounds(Drawable d) {
         Rect bounds = new Rect();
         d.copyBounds(bounds);
@@ -184,92 +162,4 @@
     protected Bitmap convertPreviewToAlphaBitmap(Bitmap preview) {
         return preview.copy(Bitmap.Config.ALPHA_8, true);
     }
-
-    private class OutlineGeneratorCallback implements Runnable {
-
-        private final Bitmap mPreviewSnapshot;
-        private final Context mContext;
-        private final boolean mIsIcon;
-
-        OutlineGeneratorCallback(Bitmap preview) {
-            mPreviewSnapshot = preview;
-            mContext = mView.getContext();
-            mIsIcon = mView instanceof BubbleTextView;
-        }
-
-        @Override
-        public void run() {
-            Bitmap preview = convertPreviewToAlphaBitmap(mPreviewSnapshot);
-            if (mIsIcon) {
-                int size = ActivityContext.lookupContext(mContext).getDeviceProfile().iconSizePx;
-                preview = Bitmap.createScaledBitmap(preview, size, size, false);
-            }
-            //else case covers AppWidgetHost (doesn't drag/drop across different device profiles)
-
-            // We start by removing most of the alpha channel so as to ignore shadows, and
-            // other types of partial transparency when defining the shape of the object
-            byte[] pixels = new byte[preview.getWidth() * preview.getHeight()];
-            ByteBuffer buffer = ByteBuffer.wrap(pixels);
-            buffer.rewind();
-            preview.copyPixelsToBuffer(buffer);
-
-            for (int i = 0; i < pixels.length; i++) {
-                if ((pixels[i] & 0xFF) < 188) {
-                    pixels[i] = 0;
-                }
-            }
-
-            buffer.rewind();
-            preview.copyPixelsFromBuffer(buffer);
-
-            final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-            Canvas canvas = new Canvas();
-
-            // calculate the outer blur first
-            paint.setMaskFilter(new BlurMaskFilter(blurSizeOutline, BlurMaskFilter.Blur.OUTER));
-            int[] outerBlurOffset = new int[2];
-            Bitmap thickOuterBlur = preview.extractAlpha(paint, outerBlurOffset);
-
-            paint.setMaskFilter(new BlurMaskFilter(
-                    mContext.getResources().getDimension(R.dimen.blur_size_thin_outline),
-                    BlurMaskFilter.Blur.OUTER));
-            int[] brightOutlineOffset = new int[2];
-            Bitmap brightOutline = preview.extractAlpha(paint, brightOutlineOffset);
-
-            // calculate the inner blur
-            canvas.setBitmap(preview);
-            canvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
-            paint.setMaskFilter(new BlurMaskFilter(blurSizeOutline, BlurMaskFilter.Blur.NORMAL));
-            int[] thickInnerBlurOffset = new int[2];
-            Bitmap thickInnerBlur = preview.extractAlpha(paint, thickInnerBlurOffset);
-
-            // mask out the inner blur
-            paint.setMaskFilter(null);
-            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
-            canvas.setBitmap(thickInnerBlur);
-            canvas.drawBitmap(preview, -thickInnerBlurOffset[0],
-                    -thickInnerBlurOffset[1], paint);
-            canvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(), paint);
-            canvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1], paint);
-
-            // draw the inner and outer blur
-            paint.setXfermode(null);
-            canvas.setBitmap(preview);
-            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
-            canvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
-                    paint);
-            canvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1], paint);
-
-            // draw the bright outline
-            canvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1], paint);
-
-            // cleanup
-            canvas.setBitmap(null);
-            brightOutline.recycle();
-            thickOuterBlur.recycle();
-            thickInnerBlur.recycle();
-
-            generatedDragOutline = preview;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index 4890ba6..d5a79dd 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.icons;
 
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
+import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_SHORTCUTS;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -100,7 +100,7 @@
      * Launcher specific checks
      */
     public static Drawable getIcon(Context context, ShortcutInfo shortcutInfo, int density) {
-        if (GO_DISABLE_WIDGETS) {
+        if (GO_DISABLE_SHORTCUTS) {
             return null;
         }
         try {
diff --git a/src/com/android/launcher3/logging/KeyboardStateManager.java b/src/com/android/launcher3/logging/KeyboardStateManager.java
new file mode 100644
index 0000000..3103af1
--- /dev/null
+++ b/src/com/android/launcher3/logging/KeyboardStateManager.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.logging;
+
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.NO_IME_ACTION;
+
+import android.os.SystemClock;
+
+/**
+ * Class to maintain keyboard states.
+ */
+public class KeyboardStateManager {
+    private long mUpdatedTime;
+
+    public enum KeyboardState {
+        NO_IME_ACTION,
+        SHOW,
+        HIDE,
+    }
+
+    private KeyboardState mKeyboardState;
+
+    public KeyboardStateManager() {
+        mKeyboardState = NO_IME_ACTION;
+    }
+
+    /**
+     * Returns time when keyboard state was updated.
+     */
+    public long getLastUpdatedTime() {
+        return mUpdatedTime;
+    }
+
+    /**
+     * Returns current keyboard state.
+     */
+    public KeyboardState getKeyboardState() {
+        return mKeyboardState;
+    }
+
+    /**
+     * Setter method to set keyboard state.
+     */
+    public void setKeyboardState(KeyboardState keyboardState) {
+        mUpdatedTime = SystemClock.elapsedRealtime();
+        mKeyboardState = keyboardState;
+    }
+}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index c4ec4e3..651372f 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -56,6 +56,7 @@
     private InstanceId mInstanceId;
 
     protected @Nullable ActivityContext mActivityContext = null;
+    private KeyboardStateManager mKeyboardStateManager;
 
     /**
      * Returns event enum based on the two state transition information when swipe
@@ -816,6 +817,16 @@
         return logger;
     }
 
+    /**
+     * Returns a singleton KeyboardStateManager.
+     */
+    public KeyboardStateManager keyboardStateManager() {
+        if (mKeyboardStateManager == null) {
+            mKeyboardStateManager = new KeyboardStateManager();
+        }
+        return mKeyboardStateManager;
+    }
+
     protected StatsLogger createLogger() {
         return new StatsLogger() {
         };
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index de23c4b..ffb0f2f 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -17,7 +17,7 @@
 
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY;
 
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
+import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_SHORTCUTS;
 import static com.android.launcher3.shortcuts.ShortcutRequest.PINNED;
 
 import static java.util.stream.Collectors.groupingBy;
@@ -286,7 +286,7 @@
      * shortcuts and unpinning any extra shortcuts.
      */
     public synchronized void updateShortcutPinnedState(Context context, UserHandle user) {
-        if (GO_DISABLE_WIDGETS) {
+        if (GO_DISABLE_SHORTCUTS) {
             return;
         }
 
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index c25929a..341372e 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -31,6 +31,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.InvariantDeviceProfile;
@@ -47,6 +48,7 @@
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
+import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -776,17 +778,6 @@
             values.put(LauncherSettings.Favorites.SPANY, spanY);
         }
 
-        /**
-         * This method should return an id that should be the same for two folders containing the
-         * same elements.
-         */
-        private String getFolderMigrationId() {
-            return mFolderItems.keySet().stream()
-                    .map(intentString -> mFolderItems.get(intentString).size() + intentString)
-                    .sorted()
-                    .collect(Collectors.joining(","));
-        }
-
         /** This id is not used in the DB is only used while doing the migration and it identifies
          * an entry on each workspace. For example two calculator icons would have the same
          * migration id even thought they have different database ids.
@@ -797,9 +788,47 @@
                     return getFolderMigrationId();
                 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                     return mProvider;
+                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+                    final String intentStr = cleanIntentString(mIntent);
+                    try {
+                        Intent i = Intent.parseUri(intentStr, 0);
+                        return Objects.requireNonNull(i.getComponent()).toString();
+                    } catch (Exception e) {
+                        return intentStr;
+                    }
                 default:
-                    return mIntent;
+                    return cleanIntentString(mIntent);
             }
         }
+
+        /**
+         * This method should return an id that should be the same for two folders containing the
+         * same elements.
+         */
+        @NonNull
+        private String getFolderMigrationId() {
+            return mFolderItems.keySet().stream()
+                    .map(intentString -> mFolderItems.get(intentString).size()
+                            + cleanIntentString(intentString))
+                    .sorted()
+                    .collect(Collectors.joining(","));
+        }
+
+        /**
+         * This is needed because sourceBounds can change and make the id of two equal items
+         * different.
+         */
+        @NonNull
+        private String cleanIntentString(@NonNull String intentStr) {
+            try {
+                Intent i = Intent.parseUri(intentStr, 0);
+                i.setSourceBounds(null);
+                return i.toURI();
+            } catch (URISyntaxException e) {
+                Log.e(TAG, "Unable to parse Intent string", e);
+                return intentStr;
+            }
+
+        }
     }
 }
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index 229bb2d..69f9b53 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -288,6 +288,7 @@
         }
 
         @Override
+        @Nullable
         public Intent getIntent() {
             return intent;
         }
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index 24e7dd3..34972e7 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -29,6 +29,7 @@
 import android.os.UserManager;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.LauncherSettings;
@@ -65,6 +66,7 @@
     }
 
     @Override
+    @Nullable
     public Intent getIntent() {
         return intent;
     }
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 8386adb..524b769 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -26,6 +26,7 @@
 
 import android.os.Process;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherSettings;
@@ -154,7 +155,7 @@
     }
 
     @Override
-    public void onAddToDatabase(ContentWriter writer) {
+    public void onAddToDatabase(@NonNull ContentWriter writer) {
         super.onAddToDatabase(writer);
         writer.put(LauncherSettings.Favorites.TITLE, title)
                 .put(LauncherSettings.Favorites.OPTIONS, options);
@@ -206,8 +207,9 @@
         return String.format("%s; labelState=%s", super.dumpProperties(), getLabelState());
     }
 
+    @NonNull
     @Override
-    public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
+    public LauncherAtom.ItemInfo buildProto(@Nullable FolderInfo fInfo) {
         FolderIcon.Builder folderIcon = FolderIcon.newBuilder()
                 .setCardinality(contents.size());
         if (LabelState.SUGGESTED.equals(getLabelState())) {
@@ -261,6 +263,7 @@
                                 : LabelState.SUGGESTED;
     }
 
+    @NonNull
     @Override
     public ItemInfo makeShallowCopy() {
         FolderInfo folderInfo = new FolderInfo();
@@ -272,6 +275,7 @@
     /**
      * Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging.
      */
+    @NonNull
     @Override
     public LauncherAtom.ItemInfo buildProto() {
         return buildProto(null);
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 1e8e3ca..466f63f 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -42,6 +42,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherSettings;
@@ -141,30 +142,34 @@
     /**
      * Title of the item
      */
+    @Nullable
     public CharSequence title;
 
     /**
      * Content description of the item.
      */
+    @Nullable
     public CharSequence contentDescription;
 
     /**
      * When the instance is created using {@link #copyFrom}, this field is used to keep track of
      * original {@link ComponentName}.
      */
+    @Nullable
     private ComponentName mComponentName;
 
+    @NonNull
     public UserHandle user;
 
     public ItemInfo() {
         user = Process.myUserHandle();
     }
 
-    protected ItemInfo(ItemInfo info) {
+    protected ItemInfo(@NonNull final ItemInfo info) {
         copyFrom(info);
     }
 
-    public void copyFrom(ItemInfo info) {
+    public void copyFrom(@NonNull final ItemInfo info) {
         id = info.id;
         title = info.title;
         cellX = info.cellX;
@@ -182,6 +187,7 @@
         mComponentName = info.getTargetComponent();
     }
 
+    @Nullable
     public Intent getIntent() {
         return null;
     }
@@ -209,7 +215,7 @@
                         : null;
     }
 
-    public void writeToValues(ContentWriter writer) {
+    public void writeToValues(@NonNull final ContentWriter writer) {
         writer.put(LauncherSettings.Favorites.ITEM_TYPE, itemType)
                 .put(LauncherSettings.Favorites.CONTAINER, container)
                 .put(LauncherSettings.Favorites.SCREEN, screenId)
@@ -220,7 +226,7 @@
                 .put(LauncherSettings.Favorites.RANK, rank);
     }
 
-    public void readFromValues(ContentValues values) {
+    public void readFromValues(@NonNull final ContentValues values) {
         itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
         container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
         screenId = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
@@ -234,7 +240,7 @@
     /**
      * Write the fields of this item to the DB
      */
-    public void onAddToDatabase(ContentWriter writer) {
+    public void onAddToDatabase(@NonNull final ContentWriter writer) {
         if (Workspace.EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) {
             // We should never persist an item on the extra empty screen.
             throw new RuntimeException("Screen id should not be extra empty screen: " + screenId);
@@ -245,10 +251,12 @@
     }
 
     @Override
+    @NonNull
     public final String toString() {
         return getClass().getSimpleName() + "(" + dumpProperties() + ")";
     }
 
+    @NonNull
     protected String dumpProperties() {
         return "id=" + id
                 + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
@@ -288,14 +296,17 @@
     /**
      * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
      */
+    @NonNull
     public LauncherAtom.ItemInfo buildProto() {
         return buildProto(null);
     }
 
     /**
      * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
+     * @param fInfo
      */
-    public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
+    @NonNull
+    public LauncherAtom.ItemInfo buildProto(@Nullable final FolderInfo fInfo) {
         LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder();
         Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent());
         switch (itemType) {
@@ -373,6 +384,7 @@
         return itemBuilder.build();
     }
 
+    @NonNull
     protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
         LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
         itemBuilder.setIsWork(!Process.myUserHandle().equals(user));
@@ -383,6 +395,7 @@
     /**
      * Returns {@link ContainerInfo} used when logging this item.
      */
+    @NonNull
     public ContainerInfo getContainerInfo() {
         switch (container) {
             case CONTAINER_HOTSEAT:
@@ -447,6 +460,7 @@
      * Returns non-AOSP container wrapped by {@link ExtendedContainers} object. Should be overridden
      * by build variants.
      */
+    @NonNull
     protected ExtendedContainers getExtendedContainer() {
         return ExtendedContainers.getDefaultInstance();
     }
@@ -454,6 +468,7 @@
     /**
      * Returns shallow copy of the object.
      */
+    @NonNull
     public ItemInfo makeShallowCopy() {
         ItemInfo itemInfo = new ItemInfo();
         itemInfo.copyFrom(this);
@@ -463,7 +478,8 @@
     /**
      * Sets the title of the item and writes to DB model if needed.
      */
-    public void setTitle(CharSequence title, ModelWriter modelWriter) {
+    public void setTitle(@Nullable final CharSequence title,
+            @Nullable final ModelWriter modelWriter) {
         this.title = title;
     }
 }
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index e57a895..1fbe04f 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -29,6 +29,7 @@
 import android.content.res.Resources;
 import android.os.Process;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.Launcher;
@@ -191,7 +192,7 @@
     }
 
     @Override
-    public void onAddToDatabase(ContentWriter writer) {
+    public void onAddToDatabase(@NonNull ContentWriter writer) {
         super.onAddToDatabase(writer);
         writer.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId)
                 .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString())
@@ -283,8 +284,9 @@
         }
     }
 
+    @NonNull
     @Override
-    public LauncherAtom.ItemInfo buildProto(FolderInfo folderInfo) {
+    public LauncherAtom.ItemInfo buildProto(@Nullable FolderInfo folderInfo) {
         LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
         return info.toBuilder()
                 .setWidget(info.getWidget().toBuilder().setWidgetFeatures(widgetFeatures))
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
index e879313..04042ea 100644
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -24,6 +24,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppState;
@@ -70,7 +71,7 @@
     }
 
     @Override
-    public void copyFrom(com.android.launcher3.model.data.ItemInfo info) {
+    public void copyFrom(@NonNull com.android.launcher3.model.data.ItemInfo info) {
         super.copyFrom(info);
         SearchActionItemInfo itemInfo = (SearchActionItemInfo) info;
         this.mFallbackPackageName = itemInfo.mFallbackPackageName;
@@ -91,6 +92,7 @@
     }
 
     @Override
+    @Nullable
     public Intent getIntent() {
         return mIntent;
     }
@@ -131,8 +133,9 @@
         return new SearchActionItemInfo(this);
     }
 
+    @NonNull
     @Override
-    public ItemInfo buildProto(FolderInfo fInfo) {
+    public ItemInfo buildProto(@Nullable FolderInfo fInfo) {
         SearchActionItem.Builder itemBuilder = SearchActionItem.newBuilder()
                 .setPackageName(mFallbackPackageName);
 
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 2b3da33..1f16474 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -24,6 +24,7 @@
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -130,7 +131,7 @@
     }
 
     @Override
-    public void onAddToDatabase(ContentWriter writer) {
+    public void onAddToDatabase(@NonNull ContentWriter writer) {
         super.onAddToDatabase(writer);
         writer.put(Favorites.TITLE, title)
                 .put(Favorites.INTENT, getIntent())
@@ -147,6 +148,7 @@
     }
 
     @Override
+    @Nullable
     public Intent getIntent() {
         return intent;
     }
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 33b2f55..a55f7e3 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -73,11 +73,11 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mModel = LauncherAppState.getInstance(this).getModel();
+        mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
+        mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
         if (getWindow().getDecorView().isAttachedToWindow()) {
             initUi();
         }
-        mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
-        mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
     }
 
     @Override
diff --git a/src/com/android/launcher3/shortcuts/ShortcutRequest.java b/src/com/android/launcher3/shortcuts/ShortcutRequest.java
index 5291ce4..07d3292 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutRequest.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutRequest.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.shortcuts;
 
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
+import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_SHORTCUTS;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -46,7 +46,7 @@
             | ShortcutQuery.FLAG_MATCH_MANIFEST;
     public static final int PINNED = ShortcutQuery.FLAG_MATCH_PINNED;
 
-    private final ShortcutQuery mQuery = GO_DISABLE_WIDGETS ? null : new ShortcutQuery();
+    private final ShortcutQuery mQuery = GO_DISABLE_SHORTCUTS ? null : new ShortcutQuery();
 
     private final Context mContext;
     private final UserHandle mUserHandle;
@@ -73,7 +73,7 @@
      * @return A list of ShortcutInfo's associated with the given package.
      */
     public ShortcutRequest forPackage(String packageName, @Nullable List<String> shortcutIds) {
-        if (!GO_DISABLE_WIDGETS && packageName != null) {
+        if (!GO_DISABLE_SHORTCUTS && packageName != null) {
             mQuery.setPackage(packageName);
             mQuery.setShortcutIds(shortcutIds);
         }
@@ -81,7 +81,7 @@
     }
 
     public ShortcutRequest withContainer(@Nullable ComponentName activity) {
-        if (!GO_DISABLE_WIDGETS) {
+        if (!GO_DISABLE_SHORTCUTS) {
             if (activity == null) {
                 mFailed = true;
             } else {
@@ -92,7 +92,7 @@
     }
 
     public QueryResult query(int flags) {
-        if (GO_DISABLE_WIDGETS || mFailed) {
+        if (GO_DISABLE_SHORTCUTS || mFailed) {
             return QueryResult.DEFAULT;
         }
         mQuery.setQueryFlags(flags);
@@ -108,7 +108,7 @@
 
     public static class QueryResult extends ArrayList<ShortcutInfo> {
 
-        static final QueryResult DEFAULT = new QueryResult(GO_DISABLE_WIDGETS);
+        static final QueryResult DEFAULT = new QueryResult(GO_DISABLE_SHORTCUTS);
 
         private final boolean mWasSuccess;
 
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 2aa9dde..54e8e5b 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -342,6 +342,12 @@
             public void onAnimationSuccess(Animator animator) {
                 onStateTransitionEnd(state);
             }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                onStateTransitionFailed(state);
+            }
         };
     }
 
@@ -354,6 +360,12 @@
         }
     }
 
+    private void onStateTransitionFailed(STATE_TYPE state) {
+        for (int i = mListeners.size() - 1; i >= 0; i--) {
+            mListeners.get(i).onStateTransitionFailed(state);
+        }
+    }
+
     private void onStateTransitionEnd(STATE_TYPE state) {
         // Only change the stable states after the transitions have finished
         if (state != mCurrentStableState) {
@@ -588,6 +600,11 @@
 
         default void onStateTransitionStart(STATE_TYPE toState) { }
 
+        /**
+         * If the state transition animation fails (e.g. is canceled by the user), this fires.
+         */
+        default void onStateTransitionFailed(STATE_TYPE toState) { }
+
         default void onStateTransitionComplete(STATE_TYPE finalState) { }
     }
 
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index fd8b2e5..c408904 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -21,17 +21,19 @@
 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 
 import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
 
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Handler;
+import android.os.Message;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.UiThreadHelper;
 
 /**
  * Utility class to manage launcher rotation
@@ -39,8 +41,6 @@
 public class RotationHelper implements OnSharedPreferenceChangeListener,
         DisplayController.DisplayInfoChangeListener {
 
-    private static final String TAG = "RotationHelper";
-
     public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
 
     /**
@@ -60,6 +60,7 @@
 
     private BaseActivity mActivity;
     private SharedPreferences mSharedPrefs = null;
+    private final Handler mRequestOrientationHandler;
 
     private boolean mIgnoreAutoRotateSettings;
     private boolean mForceAllowRotationForTesting;
@@ -89,6 +90,8 @@
 
     public RotationHelper(BaseActivity activity) {
         mActivity = activity;
+        mRequestOrientationHandler =
+                new Handler(UI_HELPER_EXECUTOR.getLooper(), this::setOrientationAsync);
     }
 
     private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings) {
@@ -202,10 +205,15 @@
         }
         if (activityFlags != mLastActivityFlags) {
             mLastActivityFlags = activityFlags;
-            UiThreadHelper.setOrientationAsync(mActivity, activityFlags);
+            mRequestOrientationHandler.sendEmptyMessage(activityFlags);
         }
     }
 
+    private boolean setOrientationAsync(Message msg) {
+        mActivity.setRequestedOrientation(msg.what);
+        return true;
+    }
+
     /**
      * @return how many factors {@param newRotation} is rotated 90 degrees clockwise.
      * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise...
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index b63715c..3286afb 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -31,8 +31,7 @@
 
     private static final int STATE_FLAGS = FLAG_MULTI_PAGE
             | FLAG_WORKSPACE_INACCESSIBLE | FLAG_DISABLE_RESTORE
-            | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_WORKSPACE_HAS_BACKGROUNDS
-            | FLAG_HIDE_BACK_BUTTON;
+            | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_WORKSPACE_HAS_BACKGROUNDS;
 
     public SpringLoadedState(int id) {
         super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
diff --git a/src/com/android/launcher3/testing/shared/ResourceUtils.java b/src/com/android/launcher3/testing/shared/ResourceUtils.java
index 551aeaf..d0ae258 100644
--- a/src/com/android/launcher3/testing/shared/ResourceUtils.java
+++ b/src/com/android/launcher3/testing/shared/ResourceUtils.java
@@ -36,6 +36,8 @@
     public static final String STATUS_BAR_HEIGHT_LANDSCAPE = "status_bar_height_landscape";
     public static final String STATUS_BAR_HEIGHT_PORTRAIT = "status_bar_height_portrait";
 
+    public static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
+
     public static int getNavbarSize(String resName, Resources res) {
         return getDimenByName(resName, res, DEFAULT_NAVBAR_VALUE);
     }
diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java
index 67efb58..5116b01 100644
--- a/src/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/src/com/android/launcher3/testing/shared/TestProtocol.java
@@ -86,6 +86,7 @@
     public static final String REQUEST_DISABLE_MANUAL_TASKBAR_STASHING = "disable-taskbar-stashing";
     public static final String REQUEST_UNSTASH_TASKBAR_IF_STASHED = "unstash-taskbar-if-stashed";
     public static final String REQUEST_STASHED_TASKBAR_HEIGHT = "stashed-taskbar-height";
+    public static final String REQUEST_RECREATE_TASKBAR = "recreate-taskbar";
     public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
     public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
     public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 37b76fb..5279dec 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
@@ -199,8 +200,10 @@
                     Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER));
             config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME);
             if (!config.userControlled) {
-                config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE);
+                config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED);
             }
+            config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED);
+            config.setInterpolator(ANIM_DEPTH, EMPHASIZED);
         } else {
             if (config.userControlled) {
                 config.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR_MANUAL));
@@ -238,8 +241,10 @@
             config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
             config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER);
             if (!config.userControlled) {
-                config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_DECELERATE);
+                config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED);
             }
+            config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED);
+            config.setInterpolator(ANIM_DEPTH, EMPHASIZED);
         } else {
             config.setInterpolator(ANIM_DEPTH, config.userControlled ? BLUR_MANUAL : BLUR_ATOMIC);
             config.setInterpolator(ANIM_WORKSPACE_FADE,
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 73f994f..1421ece 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -27,7 +27,6 @@
 import android.view.View.OnLongClickListener;
 
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.dragndrop.DragController;
@@ -118,10 +117,7 @@
             }
         });
 
-        DeviceProfile grid = launcher.getDeviceProfile();
-        DragOptions options = new DragOptions();
-        options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
-        launcher.getWorkspace().beginDragShared(v, launcher.getAppsView(), options);
+        launcher.getWorkspace().beginDragShared(v, launcher.getAppsView(), new DragOptions());
         return false;
     }
 
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 9ac1c0e..89c300d 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -376,6 +376,19 @@
         return isRtl ? 1 : -1;
     }
 
+    @Override
+    public void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
+            DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView) {
+        float topLeftTaskPlusDividerPercent = splitBounds.appsStackedVertically
+                ? (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)
+                : (splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent);
+        FrameLayout.LayoutParams snapshotParams =
+                (FrameLayout.LayoutParams) primarySnaphotView.getLayoutParams();
+        float additionalOffset = (taskView.getHeight() - snapshotParams.topMargin)
+                * topLeftTaskPlusDividerPercent;
+        taskMenuView.setY(taskMenuView.getY() + additionalOffset);
+    }
+
     /* -------------------- */
 
     @Override
@@ -492,8 +505,8 @@
         int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
         int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
         int dividerBar = splitBoundsConfig.appsStackedVertically
-                ? splitBoundsConfig.visualDividerBounds.height()
-                : splitBoundsConfig.visualDividerBounds.width();
+                ? (int) (splitBoundsConfig.dividerHeightPercent * parentHeight)
+                : (int) (splitBoundsConfig.dividerWidthPercent * parentWidth);
         int primarySnapshotHeight;
         int primarySnapshotWidth;
         int secondarySnapshotHeight;
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 1a8d355..cbcb700 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -231,6 +231,14 @@
     int getTaskDragDisplacementFactor(boolean isRtl);
 
     /**
+     * Calls the corresponding {@link View#setX(float)} or {@link View#setY(float)}
+     * on {@param taskMenuView} by taking the space needed by {@param primarySnapshotView} into
+     * account.
+     * This is expected to only be called for secondary (bottom/right) tasks.
+     */
+    void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
+            DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView);
+    /**
      * Maps the velocity from the coordinate plane of the foreground app to that
      * of Launcher's (which now will always be portrait)
      */
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index dd9f642..450205d 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -27,7 +27,7 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
-import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
+import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 
@@ -316,6 +316,26 @@
     }
 
     @Override
+    public void setSecondaryTaskMenuPosition(SplitBounds splitBounds, View taskView,
+            DeviceProfile deviceProfile, View primarySnaphotView, View taskMenuView) {
+        float topLeftTaskPlusDividerPercent = splitBounds.appsStackedVertically
+                ? (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)
+                : (splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent);
+        FrameLayout.LayoutParams snapshotParams =
+                (FrameLayout.LayoutParams) primarySnaphotView.getLayoutParams();
+        float additionalOffset;
+        if (deviceProfile.isLandscape) {
+            additionalOffset = (taskView.getWidth() - snapshotParams.leftMargin)
+                    * topLeftTaskPlusDividerPercent;
+            taskMenuView.setX(taskMenuView.getX() + additionalOffset);
+        } else {
+            additionalOffset = (taskView.getHeight() - snapshotParams.topMargin)
+                    * topLeftTaskPlusDividerPercent;
+            taskMenuView.setY(taskMenuView.getY() + additionalOffset);
+        }
+    }
+
+    @Override
     public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
             int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
             View[] thumbnailViews, int desiredTaskId, View banner) {
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 94f9f25..e57c88d 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -19,11 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
-import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
@@ -46,9 +42,7 @@
 import androidx.annotation.AnyThread;
 import androidx.annotation.UiThread;
 
-import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.util.window.CachedDisplayInfo;
 import com.android.launcher3.util.window.WindowManagerProxy;
 
@@ -56,6 +50,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -81,7 +76,6 @@
             | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS | CHANGE_NAVIGATION_MODE;
 
     private static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
-    private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
     private static final String TARGET_OVERLAY_PACKAGE = "android";
 
     private final Context mContext;
@@ -294,7 +288,7 @@
         // Used for testing
         public Info(Context displayInfoContext,
                 WindowManagerProxy wmProxy,
-                ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) {
+                Map<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) {
             CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext);
             normalizedDisplayInfo = displayInfo.normalize();
             rotation = displayInfo.rotation;
@@ -305,7 +299,7 @@
             fontScale = config.fontScale;
             densityDpi = config.densityDpi;
             mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
-            navigationMode = parseNavigationMode(displayInfoContext);
+            navigationMode = wmProxy.getNavigationMode(displayInfoContext);
 
             mPerDisplayBounds.putAll(perDisplayBoundsCache);
             WindowBounds[] cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
@@ -405,35 +399,4 @@
         }
     }
 
-    public enum NavigationMode {
-        THREE_BUTTONS(false, 0, LAUNCHER_NAVIGATION_MODE_3_BUTTON),
-        TWO_BUTTONS(true, 1, LAUNCHER_NAVIGATION_MODE_2_BUTTON),
-        NO_BUTTON(true, 2, LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON);
-
-        public final boolean hasGestures;
-        public final int resValue;
-        public final LauncherEvent launcherEvent;
-
-        NavigationMode(boolean hasGestures, int resValue, LauncherEvent launcherEvent) {
-            this.hasGestures = hasGestures;
-            this.resValue = resValue;
-            this.launcherEvent = launcherEvent;
-        }
-    }
-
-    private static NavigationMode parseNavigationMode(Context context) {
-        int modeInt = ResourceUtils.getIntegerByName(NAV_BAR_INTERACTION_MODE_RES_NAME,
-                context.getResources(), INVALID_RESOURCE_HANDLE);
-
-        if (modeInt == INVALID_RESOURCE_HANDLE) {
-            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
-        } else {
-            for (NavigationMode m : NavigationMode.values()) {
-                if (m.resValue == modeInt) {
-                    return m;
-                }
-            }
-        }
-        return Utilities.ATLEAST_S ? NavigationMode.NO_BUTTON : NavigationMode.THREE_BUTTONS;
-    }
 }
diff --git a/src/com/android/launcher3/util/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java
index 6bc26e7..ee1d1ff 100644
--- a/src/com/android/launcher3/util/LogConfig.java
+++ b/src/com/android/launcher3/util/LogConfig.java
@@ -40,4 +40,14 @@
      * When turned on, we enable suggest related logging.
      */
     public static final String SEARCH_LOGGING = "SearchLogging";
+
+    /**
+     * When turned on, we enable IME related latency related logging.
+     */
+    public static final String IME_LATENCY_LOGGING = "ImeLatencyLogging";
+
+    /**
+     * When turned on, we enable web suggest appSearch related logging.
+     */
+    public static final String WEB_APP_SEARCH_LOGGING = "WebAppSearchLogging";
 }
diff --git a/src/com/android/launcher3/util/MultiAdditivePropertyFactory.java b/src/com/android/launcher3/util/MultiPropertyFactory.java
similarity index 70%
rename from src/com/android/launcher3/util/MultiAdditivePropertyFactory.java
rename to src/com/android/launcher3/util/MultiPropertyFactory.java
index 50f7027..e7a7785 100644
--- a/src/com/android/launcher3/util/MultiAdditivePropertyFactory.java
+++ b/src/com/android/launcher3/util/MultiPropertyFactory.java
@@ -20,7 +20,6 @@
 import android.util.FloatProperty;
 import android.util.Log;
 import android.util.Property;
-import android.view.View;
 
 /**
  * Allows to combine multiple values set by several sources.
@@ -30,43 +29,55 @@
  * time.
  *
  * This class behaves similarly to [MultiValueAlpha], but is meant to be more abstract and reusable.
- * It sets the addition of all values.
+ * It aggregate all values using the provided [aggregator].
  *
  * @param <T> Type where to apply the property.
  */
-public class MultiAdditivePropertyFactory<T extends View> {
+public class MultiPropertyFactory<T> {
 
     private static final boolean DEBUG = false;
-    private static final String TAG = "MultiAdditivePropertyFactory";
+    private static final String TAG = "MultiPropertyFactory";
     private final String mName;
-    private final ArrayMap<Integer, MultiAdditiveProperty> mProperties =
-            new ArrayMap<>();
+    private final ArrayMap<Integer, MultiProperty> mProperties = new ArrayMap<>();
 
     // This is an optimization for cases when set is called repeatedly with the same setterIndex.
     private float mAggregationOfOthers = 0f;
     private Integer mLastIndexSet = -1;
-    private final Property<View, Float> mProperty;
+    private final Property<T, Float> mProperty;
+    private final FloatBiFunction mAggregator;
 
-    public MultiAdditivePropertyFactory(String name, Property<View, Float> property) {
+    /**
+     * Represents a function that accepts two float and produces a float.
+     */
+    public interface FloatBiFunction {
+        /**
+         * Applies this function to the given arguments.
+         */
+        float apply(float a, float b);
+    }
+
+    public MultiPropertyFactory(String name, Property<T, Float> property,
+            FloatBiFunction aggregator) {
         mName = name;
         mProperty = property;
+        mAggregator = aggregator;
     }
 
     /** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */
-    public MultiAdditiveProperty get(Integer index) {
+    public MultiProperty get(Integer index) {
         return mProperties.computeIfAbsent(index,
-                (k) -> new MultiAdditiveProperty(index, mName + "_" + index));
+                (k) -> new MultiProperty(index, mName + "_" + index));
     }
 
     /**
      * Each [setValue] will be aggregated with the other properties values created by the
      * corresponding factory.
      */
-    class MultiAdditiveProperty extends FloatProperty<T> {
+    class MultiProperty extends FloatProperty<T> {
         private final int mInx;
         private float mValue = 0f;
 
-        MultiAdditiveProperty(int inx, String name) {
+        MultiProperty(int inx, String name) {
             super(name);
             mInx = inx;
         }
@@ -77,12 +88,13 @@
                 mAggregationOfOthers = 0f;
                 mProperties.forEach((key, property) -> {
                     if (key != mInx) {
-                        mAggregationOfOthers += property.mValue;
+                        mAggregationOfOthers =
+                                mAggregator.apply(mAggregationOfOthers, property.mValue);
                     }
                 });
                 mLastIndexSet = mInx;
             }
-            float lastAggregatedValue = mAggregationOfOthers + newValue;
+            float lastAggregatedValue = mAggregator.apply(mAggregationOfOthers, newValue);
             mValue = newValue;
             apply(obj, lastAggregatedValue);
 
@@ -94,13 +106,13 @@
         }
 
         @Override
-        public Float get(T view) {
+        public Float get(T object) {
             // The scale of the view should match mLastAggregatedValue. Still, if it has been
             // changed without using this property, it can differ. As this get method is usually
             // used to set the starting point on an animation, this would result in some jumps
             // when the view scale is different than the last aggregated value. To stay on the
             // safe side, let's return the real view scale.
-            return mProperty.get(view);
+            return mProperty.get(object);
         }
 
         @Override
@@ -109,7 +121,7 @@
         }
     }
 
-    protected void apply(View view, float value) {
-        mProperty.set(view, value);
+    protected void apply(T object, float value) {
+        mProperty.set(object, value);
     }
 }
diff --git a/src/com/android/launcher3/util/NavigationMode.java b/src/com/android/launcher3/util/NavigationMode.java
new file mode 100644
index 0000000..37dd41c
--- /dev/null
+++ b/src/com/android/launcher3/util/NavigationMode.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
+
+import com.android.launcher3.logging.StatsLogManager;
+
+/**
+ * Navigation mode used in the device.
+ */
+public enum NavigationMode {
+    THREE_BUTTONS(false, 0, LAUNCHER_NAVIGATION_MODE_3_BUTTON),
+    TWO_BUTTONS(true, 1, LAUNCHER_NAVIGATION_MODE_2_BUTTON),
+    NO_BUTTON(true, 2, LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON);
+
+    public final boolean hasGestures;
+    public final int resValue;
+    public final StatsLogManager.LauncherEvent launcherEvent;
+
+    NavigationMode(boolean hasGestures, int resValue, StatsLogManager.LauncherEvent launcherEvent) {
+        this.hasGestures = hasGestures;
+        this.resValue = resValue;
+        this.launcherEvent = launcherEvent;
+    }
+}
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index f4cf21e..d942b7a 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -43,13 +43,14 @@
     public static final String SEARCH_ONBOARDING_COUNT = "launcher.search_onboarding_count";
     public static final String TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen";
     public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
+    public static final String QSB_SEARCH_ONBOARDING_CARD_DISMISSED = "launcher.qsb_edu_dismiss";
     // When adding a new key, add it here as well, to be able to reset it from Developer Options.
     public static final Map<String, String[]> ALL_PREF_KEYS = Map.of(
             "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT },
             "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT,
                     HOTSEAT_LONGPRESS_TIP_SEEN },
             "Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT,
-                    SEARCH_ONBOARDING_COUNT},
+                    SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED},
             "Taskbar Education", new String[] { TASKBAR_EDU_SEEN },
             "All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT}
     );
@@ -61,7 +62,8 @@
             HOME_BOUNCE_SEEN,
             HOTSEAT_LONGPRESS_TIP_SEEN,
             SEARCH_KEYBOARD_EDU_SEEN,
-            TASKBAR_EDU_SEEN
+            TASKBAR_EDU_SEEN,
+            QSB_SEARCH_ONBOARDING_CARD_DISMISSED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventBoolKey {}
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index 91cf835..79cafa0 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -34,7 +34,7 @@
      * Returns true when we should show depp shortcuts in shortcut menu for the item.
      */
     public static boolean supportsDeepShortcuts(ItemInfo info) {
-        return isActive(info) && isApp(info) && !WidgetsModel.GO_DISABLE_WIDGETS;
+        return isActive(info) && isApp(info) && !WidgetsModel.GO_DISABLE_SHORTCUTS;
     }
 
     /**
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
deleted file mode 100644
index 7e6711f..0000000
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowInsetsController;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.views.ActivityContext;
-
-/**
- * Utility class for offloading some class from UI thread
- */
-public class UiThreadHelper {
-
-    private static final MainThreadInitializedObject<Handler> HANDLER =
-            new MainThreadInitializedObject<>(
-                    c -> new Handler(UI_HELPER_EXECUTOR.getLooper(), new UiCallbacks(c)));
-
-    private static final int MSG_HIDE_KEYBOARD = 1;
-    private static final int MSG_SET_ORIENTATION = 2;
-    private static final int MSG_RUN_COMMAND = 3;
-    private static final String STATS_LOGGER_KEY = "STATS_LOGGER_KEY";
-
-    @SuppressLint("NewApi")
-    public static void hideKeyboardAsync(ActivityContext activityContext, IBinder token) {
-        View root = activityContext.getDragLayer();
-
-        if (Utilities.ATLEAST_R) {
-            Preconditions.assertUIThread();
-            //  Hide keyboard with WindowInsetsController if could. In case
-            //  hideSoftInputFromWindow may get ignored by input connection being finished
-            //  when the screen is off.
-            //
-            // In addition, inside IMF, the keyboards are closed asynchronously that launcher no
-            // longer need to post to the message queue.
-            final WindowInsetsController wic = root.getWindowInsetsController();
-            WindowInsets insets = root.getRootWindowInsets();
-            boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
-            if (wic != null && isImeShown) {
-                // this method cannot be called cross threads
-                wic.hide(WindowInsets.Type.ime());
-                activityContext.getStatsLogManager().logger()
-                        .log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
-                return;
-            }
-        }
-        // Since the launcher context cannot be accessed directly from callback, adding secondary
-        // message to log keyboard close event asynchronously.
-        Bundle mHideKeyboardLoggerMsg = new Bundle();
-        mHideKeyboardLoggerMsg.putParcelable(
-                STATS_LOGGER_KEY,
-                Message.obtain(
-                        HANDLER.get(root.getContext()),
-                        () -> activityContext
-                                .getStatsLogManager()
-                                .logger()
-                                .log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED)
-                )
-        );
-
-        Message mHideKeyboardMsg = Message.obtain(HANDLER.get(root.getContext()), MSG_HIDE_KEYBOARD,
-                token);
-        mHideKeyboardMsg.setData(mHideKeyboardLoggerMsg);
-        mHideKeyboardMsg.sendToTarget();
-    }
-
-    public static void setOrientationAsync(Activity activity, int orientation) {
-        Message.obtain(HANDLER.get(activity), MSG_SET_ORIENTATION, orientation, 0, activity)
-                .sendToTarget();
-    }
-
-    public static void setBackButtonAlphaAsync(Context context, AsyncCommand command, float alpha,
-            boolean animate) {
-        runAsyncCommand(context, command, Float.floatToIntBits(alpha), animate ? 1 : 0);
-    }
-
-    public static void runAsyncCommand(Context context, AsyncCommand command, int arg1, int arg2) {
-        Message.obtain(HANDLER.get(context), MSG_RUN_COMMAND, arg1, arg2, command).sendToTarget();
-    }
-
-    private static class UiCallbacks implements Handler.Callback {
-
-        private final Context mContext;
-        private final InputMethodManager mIMM;
-
-        UiCallbacks(Context context) {
-            mContext = context;
-            mIMM = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
-        }
-
-        @Override
-        public boolean handleMessage(Message message) {
-            switch (message.what) {
-                case MSG_HIDE_KEYBOARD:
-                    if (mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0)) {
-                        // log keyboard close event only when keyboard is actually closed
-                        ((Message) message.getData().getParcelable(STATS_LOGGER_KEY))
-                                .sendToTarget();
-                    }
-                    return true;
-                case MSG_SET_ORIENTATION:
-                    ((Activity) message.obj).setRequestedOrientation(message.arg1);
-                    return true;
-                case MSG_RUN_COMMAND:
-                    ((AsyncCommand) message.obj).execute(mContext, message.arg1, message.arg2);
-                    return true;
-            }
-            return false;
-        }
-    }
-
-    public interface AsyncCommand {
-        void execute(Context proxy, int arg1, int arg2);
-    }
-}
diff --git a/src/com/android/launcher3/util/ViewCapture.java b/src/com/android/launcher3/util/ViewCapture.java
index e368ac3..0cf3ad7 100644
--- a/src/com/android/launcher3/util/ViewCapture.java
+++ b/src/com/android/launcher3/util/ViewCapture.java
@@ -17,18 +17,25 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.Executors.createAndStartNewLooper;
 
 import static java.util.stream.Collectors.toList;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.Trace;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Base64OutputStream;
 import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
 import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnDrawListener;
 import android.view.Window;
@@ -36,6 +43,7 @@
 import androidx.annotation.UiThread;
 import androidx.annotation.WorkerThread;
 
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.view.ViewCaptureData.ExportedData;
 import com.android.launcher3.view.ViewCaptureData.FrameData;
 import com.android.launcher3.view.ViewCaptureData.ViewNode;
@@ -45,13 +53,14 @@
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Future;
 import java.util.zip.GZIPOutputStream;
 
 /**
  * Utility class for capturing view data every frame
  */
-public class ViewCapture implements OnDrawListener {
+public class ViewCapture {
 
     private static final String TAG = "ViewCapture";
 
@@ -61,104 +70,27 @@
     // Launcher. This allows the first free frames avoid object allocation during view capture.
     private static final int INIT_POOL_SIZE = 300;
 
-    private final Window mWindow;
-    private final View mRoot;
-    private final Resources mResources;
+    public static final MainThreadInitializedObject<ViewCapture> INSTANCE =
+            new MainThreadInitializedObject<>(ViewCapture::new);
 
-    private final Handler mHandler;
-    private final ViewRef mViewRef = new ViewRef();
+    private final List<WindowListener> mListeners = new ArrayList<>();
 
-    private int mFrameIndexBg = -1;
-    private final long[] mFrameTimesBg = new long[MEMORY_SIZE];
-    private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
+    private final Context mContext;
+    private final LooperExecutor mExecutor;
 
     // Pool used for capturing view tree on the UI thread.
     private ViewRef mPool = new ViewRef();
 
-    /**
-     * @param window the window for the capture data
-     */
-    public ViewCapture(Window window) {
-        mWindow = window;
-        mRoot = mWindow.getDecorView();
-        mResources = mRoot.getResources();
-        mHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::captureViewPropertiesBg);
-    }
-
-    /**
-     * Attaches the ViewCapture to the root
-     */
-    public void attach() {
-        mHandler.post(this::initPool);
-    }
-
-    /**
-     * Removes a previously attached ViewCapture from the root
-     */
-    public void detach() {
-        mHandler.post(() -> MAIN_EXECUTOR.execute(
-                () -> mRoot.getViewTreeObserver().removeOnDrawListener(this)));
-    }
-
-    @Override
-    public void onDraw() {
-        Trace.beginSection("view_capture");
-        captureViewTree(mRoot, mViewRef);
-        Message m = Message.obtain(mHandler);
-        m.obj = mViewRef.next;
-        mHandler.sendMessage(m);
-        Trace.endSection();
-    }
-
-    /**
-     * Captures the View property on the background thread, and transfer all the ViewRef objects
-     * back to the pool
-     */
-    @WorkerThread
-    private boolean captureViewPropertiesBg(Message msg) {
-        ViewRef start = (ViewRef) msg.obj;
-        long time = msg.getWhen();
-        if (start == null) {
-            return false;
+    private ViewCapture(Context context) {
+        mContext = context;
+        if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+            Looper looper = createAndStartNewLooper("ViewCapture",
+                    Process.THREAD_PRIORITY_FOREGROUND);
+            mExecutor = new LooperExecutor(looper);
+            mExecutor.execute(this::initPool);
+        } else {
+            mExecutor = UI_HELPER_EXECUTOR;
         }
-        mFrameIndexBg++;
-        if (mFrameIndexBg >= MEMORY_SIZE) {
-            mFrameIndexBg = 0;
-        }
-        mFrameTimesBg[mFrameIndexBg] = time;
-
-        ViewPropertyRef recycle = mNodesBg[mFrameIndexBg];
-
-        ViewPropertyRef result = null;
-        ViewPropertyRef resultEnd = null;
-
-        ViewRef current = start;
-        ViewRef last = start;
-        while (current != null) {
-            ViewPropertyRef propertyRef = recycle;
-            if (propertyRef == null) {
-                propertyRef = new ViewPropertyRef();
-            } else {
-                recycle = recycle.next;
-                propertyRef.next = null;
-            }
-
-            propertyRef.transfer(current);
-            last = current;
-            current = current.next;
-
-            if (result == null) {
-                result = propertyRef;
-                resultEnd = result;
-            } else {
-                resultEnd.next = propertyRef;
-                resultEnd = propertyRef;
-            }
-        }
-        mNodesBg[mFrameIndexBg] = result;
-        ViewRef end = last;
-        MAIN_EXECUTOR.execute(() -> addToPool(start, end));
-        return true;
     }
 
     @UiThread
@@ -177,36 +109,59 @@
             current = current.next;
         }
 
-        ViewRef end = current;
-        MAIN_EXECUTOR.execute(() ->  {
-            addToPool(start, end);
-            if (mRoot.isAttachedToWindow()) {
-                mRoot.getViewTreeObserver().addOnDrawListener(this);
-            }
-        });
-    }
-
-    private String getName() {
-        String title = mWindow.getAttributes().getTitle().toString();
-        return TextUtils.isEmpty(title) ? mWindow.toString() : title;
+        ViewRef finalCurrent = current;
+        MAIN_EXECUTOR.execute(() -> addToPool(start, finalCurrent));
     }
 
     /**
-     * Starts the dump process which is completed on closing the returned object.
+     * Attaches the ViewCapture to the provided window and returns a handle to detach the listener
      */
-    public SafeCloseable beginDump(PrintWriter writer, FileDescriptor out) {
-        Future<ExportedData> task = UI_HELPER_EXECUTOR.submit(this::dumpToProto);
+    public SafeCloseable startCapture(Window window) {
+        String title = window.getAttributes().getTitle().toString();
+        String name = TextUtils.isEmpty(title) ? window.toString() : title;
+        return startCapture(window.getDecorView(), name);
+    }
 
+    /**
+     * Attaches the ViewCapture to the provided window and returns a handle to detach the listener
+     */
+    public SafeCloseable startCapture(View view, String name) {
+        if (!FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+            return () -> { };
+        }
+
+        WindowListener listener = new WindowListener(view, name);
+        mExecutor.execute(() -> MAIN_EXECUTOR.execute(listener::attachToRoot));
+        mListeners.add(listener);
         return () -> {
+            mListeners.remove(listener);
+            listener.destroy();
+        };
+    }
+
+    /**
+     * Dumps all the active view captures
+     */
+    public void dump(PrintWriter writer, FileDescriptor out) {
+        if (!FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+            return;
+        }
+        ViewIdProvider idProvider = new ViewIdProvider(mContext.getResources());
+
+        // Collect all the tasks first so that all the tasks are posted on the executor
+        List<Pair<String, Future<ExportedData>>> tasks = mListeners.stream()
+                .map(l -> Pair.create(l.name, mExecutor.submit(() -> l.dumpToProto(idProvider))))
+                .collect(toList());
+
+        tasks.forEach(pair -> {
             writer.println();
             writer.println(" ContinuousViewCapture:");
-            writer.println(" window " + getName() + ":");
-            writer.println("  pkg:" + mRoot.getContext().getPackageName());
+            writer.println(" window " + pair.first + ":");
+            writer.println("  pkg:" + mContext.getPackageName());
             writer.print("  data:");
             writer.flush();
-
             try (OutputStream os = new FileOutputStream(out)) {
-                ExportedData data = task.get();
+                ExportedData data = pair.second.get();
                 OutputStream encodedOS = new GZIPOutputStream(new Base64OutputStream(os,
                         Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP));
                 data.writeTo(encodedOS);
@@ -217,51 +172,156 @@
             }
             writer.println();
             writer.println("--end--");
-        };
+        });
     }
 
-    @WorkerThread
-    private ExportedData dumpToProto() {
-        ExportedData.Builder dataBuilder = ExportedData.newBuilder();
-        Resources res = mResources;
-        ArrayList<Class> classList = new ArrayList<>();
+    private class WindowListener implements OnDrawListener {
 
-        int size = (mNodesBg[MEMORY_SIZE - 1] == null) ? mFrameIndexBg + 1 : MEMORY_SIZE;
-        for (int i = size - 1; i >= 0; i--) {
-            int index = (MEMORY_SIZE + mFrameIndexBg - i) % MEMORY_SIZE;
-            ViewNode.Builder nodeBuilder = ViewNode.newBuilder();
-            mNodesBg[index].toProto(res, classList, nodeBuilder);
-            dataBuilder.addFrameData(FrameData.newBuilder()
-                    .setNode(nodeBuilder)
-                    .setTimestamp(mFrameTimesBg[index]));
-        }
-        return dataBuilder
-                .addAllClassname(classList.stream().map(Class::getName).collect(toList()))
-                .build();
-    }
+        private final View mRoot;
+        public final String name;
 
-    private ViewRef captureViewTree(View view, ViewRef start) {
-        ViewRef ref;
-        if (mPool != null) {
-            ref = mPool;
-            mPool = mPool.next;
-            ref.next = null;
-        } else {
-            ref = new ViewRef();
+        private final Handler mHandler;
+        private final ViewRef mViewRef = new ViewRef();
+
+        private int mFrameIndexBg = -1;
+        private final long[] mFrameTimesBg = new long[MEMORY_SIZE];
+        private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
+
+        private boolean mDestroyed = false;
+
+        WindowListener(View view, String name) {
+            mRoot = view;
+            this.name = name;
+            mHandler = new Handler(mExecutor.getLooper(), this::captureViewPropertiesBg);
         }
-        ref.view = view;
-        start.next = ref;
-        if (view instanceof ViewGroup) {
-            ViewRef result = ref;
-            ViewGroup parent = (ViewGroup) view;
-            int childCount = ref.childCount = parent.getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                result = captureViewTree(parent.getChildAt(i), result);
+
+        @Override
+        public void onDraw() {
+            Trace.beginSection("view_capture");
+            captureViewTree(mRoot, mViewRef);
+            Message m = Message.obtain(mHandler);
+            m.obj = mViewRef.next;
+            mHandler.sendMessage(m);
+            Trace.endSection();
+        }
+
+        /**
+         * Captures the View property on the background thread, and transfer all the ViewRef objects
+         * back to the pool
+         */
+        @WorkerThread
+        private boolean captureViewPropertiesBg(Message msg) {
+            ViewRef start = (ViewRef) msg.obj;
+            long time = msg.getWhen();
+            if (start == null) {
+                return false;
             }
-            return result;
-        } else {
-            ref.childCount = 0;
-            return ref;
+            mFrameIndexBg++;
+            if (mFrameIndexBg >= MEMORY_SIZE) {
+                mFrameIndexBg = 0;
+            }
+            mFrameTimesBg[mFrameIndexBg] = time;
+
+            ViewPropertyRef recycle = mNodesBg[mFrameIndexBg];
+
+            ViewPropertyRef result = null;
+            ViewPropertyRef resultEnd = null;
+
+            ViewRef current = start;
+            ViewRef last = start;
+            while (current != null) {
+                ViewPropertyRef propertyRef = recycle;
+                if (propertyRef == null) {
+                    propertyRef = new ViewPropertyRef();
+                } else {
+                    recycle = recycle.next;
+                    propertyRef.next = null;
+                }
+
+                propertyRef.transfer(current);
+                last = current;
+                current = current.next;
+
+                if (result == null) {
+                    result = propertyRef;
+                    resultEnd = result;
+                } else {
+                    resultEnd.next = propertyRef;
+                    resultEnd = propertyRef;
+                }
+            }
+            mNodesBg[mFrameIndexBg] = result;
+            ViewRef end = last;
+            MAIN_EXECUTOR.execute(() -> addToPool(start, end));
+            return true;
+        }
+
+        void attachToRoot() {
+            if (mRoot.isAttachedToWindow()) {
+                mRoot.getViewTreeObserver().addOnDrawListener(this);
+            } else {
+                mRoot.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+                    @Override
+                    public void onViewAttachedToWindow(View v) {
+                        if (!mDestroyed) {
+                            mRoot.getViewTreeObserver().addOnDrawListener(WindowListener.this);
+                        }
+                        mRoot.removeOnAttachStateChangeListener(this);
+                    }
+
+                    @Override
+                    public void onViewDetachedFromWindow(View v) { }
+                });
+            }
+        }
+
+        void destroy() {
+            mRoot.getViewTreeObserver().removeOnDrawListener(this);
+            mDestroyed = true;
+        }
+
+        @WorkerThread
+        private ExportedData dumpToProto(ViewIdProvider idProvider) {
+            ExportedData.Builder dataBuilder = ExportedData.newBuilder();
+            ArrayList<Class> classList = new ArrayList<>();
+
+            int size = (mNodesBg[MEMORY_SIZE - 1] == null) ? mFrameIndexBg + 1 : MEMORY_SIZE;
+            for (int i = size - 1; i >= 0; i--) {
+                int index = (MEMORY_SIZE + mFrameIndexBg - i) % MEMORY_SIZE;
+                ViewNode.Builder nodeBuilder = ViewNode.newBuilder();
+                mNodesBg[index].toProto(idProvider, classList, nodeBuilder);
+                dataBuilder.addFrameData(FrameData.newBuilder()
+                        .setNode(nodeBuilder)
+                        .setTimestamp(mFrameTimesBg[index]));
+            }
+            return dataBuilder
+                    .addAllClassname(classList.stream().map(Class::getName).collect(toList()))
+                    .build();
+        }
+
+        private ViewRef captureViewTree(View view, ViewRef start) {
+            ViewRef ref;
+            if (mPool != null) {
+                ref = mPool;
+                mPool = mPool.next;
+                ref.next = null;
+            } else {
+                ref = new ViewRef();
+            }
+            ref.view = view;
+            start.next = ref;
+            if (view instanceof ViewGroup) {
+                ViewRef result = ref;
+                ViewGroup parent = (ViewGroup) view;
+                int childCount = ref.childCount = parent.getChildCount();
+                for (int i = 0; i < childCount; i++) {
+                    result = captureViewTree(parent.getChildAt(i), result);
+                }
+                return result;
+            } else {
+                ref.childCount = 0;
+                return ref;
+            }
         }
     }
 
@@ -318,18 +378,8 @@
          * at the end of the iteration.
          * @return
          */
-        public ViewPropertyRef toProto(Resources res, ArrayList<Class> classList,
+        public ViewPropertyRef toProto(ViewIdProvider idProvider, ArrayList<Class> classList,
                 ViewNode.Builder outBuilder) {
-            String resolvedId;
-            if (id >= 0) {
-                try {
-                    resolvedId = res.getResourceTypeName(id) + '/' + res.getResourceEntryName(id);
-                } catch (Resources.NotFoundException e) {
-                    resolvedId = "id/" + "0x" + Integer.toHexString(id).toUpperCase();
-                }
-            } else {
-                resolvedId = "NO_ID";
-            }
             int classnameIndex = classList.indexOf(clazz);
             if (classnameIndex < 0) {
                 classnameIndex = classList.size();
@@ -338,7 +388,7 @@
             outBuilder
                     .setClassnameIndex(classnameIndex)
                     .setHashcode(hashCode)
-                    .setId(resolvedId)
+                    .setId(idProvider.getName(id))
                     .setLeft(left)
                     .setTop(top)
                     .setWidth(right - left)
@@ -356,7 +406,7 @@
             ViewPropertyRef result = next;
             for (int i = 0; (i < childCount) && (result != null); i++) {
                 ViewNode.Builder childBuilder = ViewNode.newBuilder();
-                result = result.toProto(res, classList, childBuilder);
+                result = result.toProto(idProvider, classList, childBuilder);
                 outBuilder.addChildren(childBuilder);
             }
             return result;
@@ -368,4 +418,31 @@
         public int childCount = 0;
         public ViewRef next;
     }
+
+    private static final class ViewIdProvider {
+
+        private final SparseArray<String> mNames = new SparseArray<>();
+        private final Resources mRes;
+
+        ViewIdProvider(Resources res) {
+            mRes = res;
+        }
+
+        String getName(int id) {
+            String name = mNames.get(id);
+            if (name == null) {
+                if (id >= 0) {
+                    try {
+                        name = mRes.getResourceTypeName(id) + '/' + mRes.getResourceEntryName(id);
+                    } catch (Resources.NotFoundException e) {
+                        name = "id/" + "0x" + Integer.toHexString(id).toUpperCase();
+                    }
+                } else {
+                    name = "NO_ID";
+                }
+                mNames.put(id, name);
+            }
+            return name;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 582ff8d..fb2ae73 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -17,14 +17,15 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT;
 import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
 import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAV_BAR_INTERACTION_MODE_RES_NAME;
 import static com.android.launcher3.testing.shared.ResourceUtils.STATUS_BAR_HEIGHT;
 import static com.android.launcher3.testing.shared.ResourceUtils.STATUS_BAR_HEIGHT_LANDSCAPE;
 import static com.android.launcher3.testing.shared.ResourceUtils.STATUS_BAR_HEIGHT_PORTRAIT;
-import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
 import static com.android.launcher3.util.RotationUtils.deltaRotation;
 import static com.android.launcher3.util.RotationUtils.rotateRect;
@@ -40,6 +41,7 @@
 import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.Surface;
@@ -48,9 +50,10 @@
 import android.view.WindowMetrics;
 
 import com.android.launcher3.R;
-import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.shared.ResourceUtils;
 import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.ResourceBasedOverride;
 import com.android.launcher3.util.WindowBounds;
 
@@ -59,6 +62,7 @@
  */
 public class WindowManagerProxy implements ResourceBasedOverride {
 
+    private static final String TAG = "WindowManagerProxy";
     public static final int MIN_TABLET_WIDTH = 600;
 
     public static final MainThreadInitializedObject<WindowManagerProxy> INSTANCE =
@@ -343,4 +347,24 @@
         return displayInfoContext.getSystemService(DisplayManager.class).getDisplay(
                 DEFAULT_DISPLAY);
     }
+
+    /**
+     * Returns the current navigation mode from resource.
+     */
+    public NavigationMode getNavigationMode(Context context) {
+        int modeInt = ResourceUtils.getIntegerByName(NAV_BAR_INTERACTION_MODE_RES_NAME,
+                context.getResources(), INVALID_RESOURCE_HANDLE);
+
+        if (modeInt == INVALID_RESOURCE_HANDLE) {
+            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+        } else {
+            for (NavigationMode m : NavigationMode.values()) {
+                if (m.resValue == modeInt) {
+                    return m;
+                }
+            }
+        }
+        return Utilities.ATLEAST_S ? NavigationMode.NO_BUTTON :
+                NavigationMode.THREE_BUTTONS;
+    }
 }
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 93078e4..dd5b22e 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -15,16 +15,26 @@
  */
 package com.android.launcher3.views;
 
+import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
+import android.view.inputmethod.InputMethodManager;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
 import com.android.launcher3.dot.DotInfo;
@@ -36,6 +46,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.ViewCache;
 
 /**
@@ -141,6 +152,7 @@
     default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) { }
 
     /** Onboarding preferences for any onboarding data within this context. */
+    @Nullable
     default OnboardingPrefs<?> getOnboardingPrefs() {
         return null;
     }
@@ -201,4 +213,47 @@
             ActivityAllAppsContainerView<?> appsView) {
         return null;
     }
+
+    /**
+     * Hides the keyboard if it is visible
+     */
+    default void hideKeyboard() {
+        View root = getDragLayer();
+        if (root == null) {
+            return;
+        }
+        if (Utilities.ATLEAST_R) {
+            Preconditions.assertUIThread();
+            //  Hide keyboard with WindowInsetsController if could. In case
+            //  hideSoftInputFromWindow may get ignored by input connection being finished
+            //  when the screen is off.
+            //
+            // In addition, inside IMF, the keyboards are closed asynchronously that launcher no
+            // longer need to post to the message queue.
+            final WindowInsetsController wic = root.getWindowInsetsController();
+            WindowInsets insets = root.getRootWindowInsets();
+            boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
+            if (wic != null && isImeShown) {
+                StatsLogManager slm  = getStatsLogManager();
+                slm.keyboardStateManager().setKeyboardState(HIDE);
+
+                // this method cannot be called cross threads
+                wic.hide(WindowInsets.Type.ime());
+                slm.logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
+                return;
+            }
+        }
+
+        InputMethodManager imm = root.getContext().getSystemService(InputMethodManager.class);
+        IBinder token = root.getWindowToken();
+        if (imm != null && token != null) {
+            UI_HELPER_EXECUTOR.execute(() -> {
+                if (imm.hideSoftInputFromWindow(token, 0)) {
+                    // log keyboard close event only when keyboard is actually closed
+                    MAIN_EXECUTOR.execute(() ->
+                            getStatsLogManager().logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED));
+                }
+            });
+        }
+    }
 }
diff --git a/src/com/android/launcher3/views/AppLauncher.java b/src/com/android/launcher3/views/AppLauncher.java
index 19e66ab..dc07e45 100644
--- a/src/com/android/launcher3/views/AppLauncher.java
+++ b/src/com/android/launcher3/views/AppLauncher.java
@@ -16,7 +16,7 @@
 package com.android.launcher3.views;
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
+import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_SHORTCUTS;
 
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
@@ -190,7 +190,7 @@
      */
     default void startShortcut(String packageName, String id, Rect sourceBounds,
             Bundle startActivityOptions, UserHandle user) {
-        if (GO_DISABLE_WIDGETS) {
+        if (GO_DISABLE_SHORTCUTS) {
             return;
         }
         try {
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 6e6512d..40e4ce1 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -20,8 +20,6 @@
 
 import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
 
-import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
-
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
@@ -313,7 +311,7 @@
     }
 
     private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
-        hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getWindowToken());
+        ActivityContext.lookupContext(getContext()).hideKeyboard();
         mIsDragging = true;
         if (mCanThumbDetach) {
             mIsThumbDetached = true;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 5cffd48..2ac1e94 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.widget;
 
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -26,6 +28,7 @@
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 import android.view.WindowInsets;
+import android.view.animation.Interpolator;
 import android.widget.Toast;
 
 import androidx.annotation.GuardedBy;
@@ -246,6 +249,12 @@
         return true;
     }
 
+    @Override
+    protected Interpolator getIdleInterpolator() {
+        return mActivityContext.getDeviceProfile().isTablet
+                ? EMPHASIZED : super.getIdleInterpolator();
+    }
+
     //
     // Drag related handling methods that implement {@link DragSource} interface.
     //
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index 470a800..ccf4b2e 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -19,6 +19,9 @@
 import android.content.Context;
 import android.os.Bundle;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.logger.LauncherAtom;
@@ -66,8 +69,9 @@
         return WidgetSizes.getWidgetSizeOptions(context, componentName, spanX, spanY);
     }
 
+    @NonNull
     @Override
-    public LauncherAtom.ItemInfo buildProto(FolderInfo folderInfo) {
+    public LauncherAtom.ItemInfo buildProto(@Nullable FolderInfo folderInfo) {
         LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
         return info.toBuilder()
                 .addItemAttributes(LauncherAppWidgetInfo.getAttribute(sourceContainer))
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 0d9198f..da8e25c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -85,7 +85,6 @@
         implements ProviderChangedListener, OnActivePageChangedListener,
         WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
 
-    private static final long DEFAULT_OPEN_DURATION = 267;
     private static final long FADE_IN_DURATION = 150;
     private static final long EDUCATION_TIP_DELAY_MS = 200;
     private static final long EDUCATION_DIALOG_DELAY_MS = 500;
@@ -582,7 +581,7 @@
             mOpenCloseAnimator.setValues(
                     PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
             mOpenCloseAnimator
-                    .setDuration(DEFAULT_OPEN_DURATION)
+                    .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
                     .setInterpolator(AnimationUtils.loadInterpolator(
                             getContext(), android.R.interpolator.linear_out_slow_in));
             mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@@ -603,7 +602,7 @@
 
     @Override
     protected void handleClose(boolean animate) {
-        handleClose(animate, DEFAULT_OPEN_DURATION);
+        handleClose(animate, mActivityContext.getDeviceProfile().bottomSheetCloseDuration);
     }
 
     @Override
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 702f343..13ad7a4 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -62,6 +62,8 @@
 
     // True is the widget support is disabled.
     public static final boolean GO_DISABLE_WIDGETS = false;
+    // True is the shortcut support is disabled.
+    public static final boolean GO_DISABLE_SHORTCUTS = false;
     public static final boolean GO_DISABLE_NOTIFICATION_DOTS = false;
 
     private static final String TAG = "WidgetsModel";
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index bf35dd8..a581f91 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -32,7 +32,6 @@
 public class AllAppsState extends LauncherState {
 
     private static final float PARALLAX_COEFFICIENT = .125f;
-    private static final float WORKSPACE_SCALE_FACTOR = 0.97f;
 
     private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE;
 
@@ -43,9 +42,9 @@
     @Override
     public <DEVICE_PROFILE_CONTEXT extends Context & DeviceProfileListenable>
     int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) {
-        return !context.getDeviceProfile().isTablet && isToState
-                ? 600
-                : isToState ? 500 : 300;
+        return isToState
+                ? context.getDeviceProfile().allAppsOpenDuration
+                : context.getDeviceProfile().allAppsCloseDuration;
     }
 
     @Override
@@ -60,7 +59,8 @@
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new ScaleAndTranslation(WORKSPACE_SCALE_FACTOR, NO_OFFSET, NO_OFFSET);
+        return new ScaleAndTranslation(launcher.getDeviceProfile().workspaceContentScale, NO_OFFSET,
+                NO_OFFSET);
     }
 
     @Override
@@ -71,7 +71,7 @@
             ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
                     .getWorkspaceScaleAndTranslation(launcher);
             return new ScaleAndTranslation(
-                    WORKSPACE_SCALE_FACTOR,
+                    launcher.getDeviceProfile().workspaceContentScale,
                     overviewScaleAndTranslation.translationX,
                     overviewScaleAndTranslation.translationY);
         }
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
index 3ca05bc..a32ce3c 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
@@ -30,39 +30,117 @@
 
 public class CellLayoutBoard {
 
+    public static class CellType {
+        // The cells marked by this will be filled by 1x1 widgets and will be ignored when
+        // validating
+        public static final char IGNORE = 'x';
+        // The cells marked by this will be filled by app icons
+        public static final char ICON = 'i';
+        // Empty space
+        public static final char EMPTY = '-';
+        // Widget that will be saved as "main widget" for easier retrieval
+        public static final char MAIN_WIDGET = 'm';
+        // Everything else will be consider a widget
+    }
+
+    public static class WidgetRect {
+        public char mType;
+        public Rect mBounds;
+
+        WidgetRect(char type, Rect bounds) {
+            this.mType = type;
+            this.mBounds = bounds;
+        }
+
+        int getSpanX() {
+            return mBounds.right - mBounds.left + 1;
+        }
+
+        int getSpanY() {
+            return mBounds.top - mBounds.bottom + 1;
+        }
+
+        int getCellX() {
+            return mBounds.left;
+        }
+
+        int getCellY() {
+            return mBounds.bottom;
+        }
+
+        boolean shouldIgnore() {
+            return this.mType == CellType.IGNORE;
+        }
+
+        @Override
+        public String toString() {
+            return "WidgetRect type = " + mType + " bounds = " + mBounds.toString();
+        }
+    }
+
+    public static class IconPoint {
+        public Point coord;
+        public char mType;
+
+        public IconPoint(Point coord, char type) {
+            this.coord = coord;
+            mType = type;
+        }
+
+        public char getType() {
+            return mType;
+        }
+
+        public void setType(char type) {
+            mType = type;
+        }
+
+        public Point getCoord() {
+            return coord;
+        }
+
+        public void setCoord(Point coord) {
+            this.coord = coord;
+        }
+    }
+
     static final int INFINITE = 99999;
 
-    char[][] mBoard = new char[30][30];
+    char[][] mWidget = new char[30][30];
 
-    List<TestBoardWidget> mWidgetsRects = new ArrayList<>();
-    Map<Character, TestBoardWidget> mWidgetsMap = new HashMap<>();
+    List<WidgetRect> mWidgetsRects = new ArrayList<>();
+    Map<Character, WidgetRect> mWidgetsMap = new HashMap<>();
 
-    List<TestBoardAppIcon> mIconPoints = new ArrayList<>();
-    Map<Character, TestBoardAppIcon> mIconsMap = new HashMap<>();
+    List<IconPoint> mIconPoints = new ArrayList<>();
+    Map<Character, IconPoint> mIconsMap = new HashMap<>();
 
     Point mMain = new Point();
 
     CellLayoutBoard() {
-        for (int x = 0; x < mBoard.length; x++) {
-            for (int y = 0; y < mBoard[0].length; y++) {
-                mBoard[x][y] = '-';
+        for (int x = 0; x < mWidget.length; x++) {
+            for (int y = 0; y < mWidget[0].length; y++) {
+                mWidget[x][y] = CellType.EMPTY;
             }
         }
     }
 
-    public List<TestBoardWidget> getWidgets() {
+    public List<WidgetRect> getWidgets() {
         return mWidgetsRects;
     }
 
+    public List<IconPoint> getIcons() {
+        return mIconPoints;
+    }
+
     public Point getMain() {
         return mMain;
     }
 
-    public TestBoardWidget getWidgetRect(char c) {
+    public WidgetRect getWidgetRect(char c) {
         return mWidgetsMap.get(c);
     }
 
-    public static TestBoardWidget getWidgetRect(int x, int y, Set<Point> used, char[][] board) {
+    public static WidgetRect getWidgetRect(int x, int y, Set<Point> used, char[][] board) {
         char type = board[x][y];
         Queue<Point> search = new ArrayDeque<Point>();
         Point current = new Point(x, y);
@@ -91,20 +169,20 @@
                 }
             }
         }
-        return new TestBoardWidget(type, widgetRect);
+        return new WidgetRect(type, widgetRect);
     }
 
     public static boolean isWidget(char type) {
-        return type != 'i' && type != '-';
+        return type != CellType.ICON && type != CellType.EMPTY;
     }
 
     public static boolean isIcon(char type) {
-        return type == 'i';
+        return type == CellType.ICON;
     }
 
-    private static List<TestBoardWidget> getRects(char[][] board) {
+    private static List<WidgetRect> getRects(char[][] board) {
         Set<Point> used = new HashSet<>();
-        List<TestBoardWidget> widgetsRects = new ArrayList<>();
+        List<WidgetRect> widgetsRects = new ArrayList<>();
         for (int x = 0; x < board.length; x++) {
             for (int y = 0; y < board[0].length; y++) {
                 if (!used.contains(new Point(x, y)) && isWidget(board[x][y])) {
@@ -115,12 +193,12 @@
         return widgetsRects;
     }
 
-    private static List<TestBoardAppIcon> getIconPoints(char[][] board) {
-        List<TestBoardAppIcon> iconPoints = new ArrayList<>();
+    private static List<IconPoint> getIconPoints(char[][] board) {
+        List<IconPoint> iconPoints = new ArrayList<>();
         for (int x = 0; x < board.length; x++) {
             for (int y = 0; y < board[0].length; y++) {
                 if (isIcon(board[x][y])) {
-                    iconPoints.add(new TestBoardAppIcon(new Point(x, y), board[x][y]));
+                    iconPoints.add(new IconPoint(new Point(x, y), board[x][y]));
                 }
             }
         }
@@ -135,18 +213,18 @@
             String line = lines[y];
             for (int x = 0; x < line.length(); x++) {
                 char c = line.charAt(x);
-                if (c == 'm') {
+                if (c == CellType.MAIN_WIDGET) {
                     board.mMain = new Point(x, y);
                 }
-                if (c != '-') {
-                    board.mBoard[x][y] = line.charAt(x);
+                if (c != CellType.EMPTY) {
+                    board.mWidget[x][y] = line.charAt(x);
                 }
             }
         }
-        board.mWidgetsRects = getRects(board.mBoard);
+        board.mWidgetsRects = getRects(board.mWidget);
         board.mWidgetsRects.forEach(
                 widgetRect -> board.mWidgetsMap.put(widgetRect.mType, widgetRect));
-        board.mIconPoints = getIconPoints(board.mBoard);
+        board.mIconPoints = getIconPoints(board.mWidget);
         return board;
     }
 }
diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
new file mode 100644
index 0000000..8ce932d
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.ContentWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+public class FavoriteItemsTransaction {
+    private ArrayList<ItemInfo> mItemsToSubmit;
+    private Context mContext;
+    private ContentResolver mResolver;
+    public AbstractLauncherUiTest mTest;
+
+    public FavoriteItemsTransaction(Context context, AbstractLauncherUiTest test) {
+        mItemsToSubmit = new ArrayList<>();
+        mContext = context;
+        mResolver = mContext.getContentResolver();
+        mTest = test;
+    }
+
+    public FavoriteItemsTransaction addItem(ItemInfo itemInfo) {
+        this.mItemsToSubmit.add(itemInfo);
+        return this;
+    }
+
+    public FavoriteItemsTransaction removeLast() {
+        this.mItemsToSubmit.remove(this.mItemsToSubmit.size() - 1);
+        return this;
+    }
+
+    /**
+     * Commits all the ItemInfo into the database of Favorites
+     **/
+    public void commit() throws ExecutionException, InterruptedException {
+        List<ContentValues> values = new ArrayList<>();
+        for (ItemInfo item : this.mItemsToSubmit) {
+            ContentWriter writer = new ContentWriter(mContext);
+            item.onAddToDatabase(writer);
+            writer.put(LauncherSettings.Favorites._ID, item.id);
+            values.add(writer.getValues(mContext));
+        }
+        // Submit the icons to the database in the model thread to prevent race conditions
+        MODEL_EXECUTOR.submit(() -> mResolver.bulkInsert(LauncherSettings.Favorites.CONTENT_URI,
+                values.toArray(new ContentValues[0]))).get();
+        // Reload the state of the Launcher
+        MAIN_EXECUTOR.submit(() -> LauncherAppState.getInstance(
+                mContext).getModel().forceReload()).get();
+    }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
index 5f5dfea..2846cae 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -15,16 +15,13 @@
  */
 package com.android.launcher3.celllayout;
 
-import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
-
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.util.Log;
 import android.view.View;
 
-import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -34,13 +31,14 @@
 import com.android.launcher3.celllayout.testcases.PushReorderCase;
 import com.android.launcher3.celllayout.testcases.ReorderTestCase;
 import com.android.launcher3.celllayout.testcases.SimpleReorderCase;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.tapl.Widget;
+import com.android.launcher3.tapl.WidgetResizeFrame;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TaplTestsLauncher3;
-import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.views.DoubleShadowBubbleTextView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 import org.junit.Assume;
 import org.junit.Before;
@@ -49,6 +47,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
 
 
 @SmallTest
@@ -60,6 +59,8 @@
 
     private static final String TAG = ReorderWidgets.class.getSimpleName();
 
+    TestWorkspaceBuilder mWorkspaceBuilder;
+
     private View getViewAt(int cellX, int cellY) {
         return getFromLauncher(l -> l.getWorkspace().getScreenWithId(
                 l.getWorkspace().getScreenIdForPageIndex(0)).getChildAt(cellX, cellY));
@@ -76,6 +77,7 @@
 
     @Before
     public void setup() throws Throwable {
+        mWorkspaceBuilder = new TestWorkspaceBuilder(this, mTargetContext);
         TaplTestsLauncher3.initialize(this);
         clearHomescreen();
     }
@@ -86,78 +88,47 @@
     private boolean validateBoard(CellLayoutBoard board) {
         boolean match = true;
         Point cellDimensions = getCellDimensions();
-        for (TestBoardWidget widgetRect: board.getWidgets()) {
+        for (CellLayoutBoard.WidgetRect widgetRect : board.getWidgets()) {
             if (widgetRect.shouldIgnore()) {
                 continue;
             }
             View widget = getViewAt(widgetRect.getCellX(), widgetRect.getCellY());
+            assertTrue("The view selected at " + board + " is not a widget",
+                    widget instanceof LauncherAppWidgetHostView);
             match &= widgetRect.getSpanX()
                     == Math.round(widget.getWidth() / (float) cellDimensions.x);
             match &= widgetRect.getSpanY()
                     == Math.round(widget.getHeight() / (float) cellDimensions.y);
             if (!match) return match;
         }
+        for (CellLayoutBoard.IconPoint iconPoint : board.getIcons()) {
+            View icon = getViewAt(iconPoint.getCoord().x, iconPoint.getCoord().y);
+            assertTrue("The view selected at " + iconPoint.coord + " is not an Icon",
+                    icon instanceof DoubleShadowBubbleTextView);
+        }
         return match;
     }
 
-    /**
-     * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases.
-     */
-    private void fillWithWidgets(TestBoardWidget widgetRect) {
-        int initX = widgetRect.getCellX();
-        int initY = widgetRect.getCellY();
-        for (int x = 0; x < widgetRect.getSpanX(); x++) {
-            for (int y = 0; y < widgetRect.getSpanY(); y++) {
-                int auxX = initX + x;
-                int auxY = initY + y;
-                try {
-                    // this widgets are filling, we don't care if we can't place them
-                    addWidgetInCell(
-                            new TestBoardWidget('x',
-                                    new Rect(auxX, auxY, auxX, auxY))
-                    );
-                } catch (Exception e) {
-                    Log.d(TAG, "Unable to place filling widget at " + auxX + "," + auxY);
-                }
-            }
-        }
-    }
-
-    private void addWidgetInCell(TestBoardWidget widgetRect) {
-        LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
-        LauncherAppWidgetInfo item = createWidgetInfo(info,
-                ApplicationProvider.getApplicationContext(), true);
-        item.cellX = widgetRect.getCellX();
-        item.cellY = widgetRect.getCellY();
-
-        item.spanX = widgetRect.getSpanX();
-        item.spanY = widgetRect.getSpanY();
-        addItemToScreen(item);
-    }
-
-    private void addCorrespondingWidgetRect(TestBoardWidget widgetRect) {
-        if (widgetRect.mType == 'x') {
-            fillWithWidgets(widgetRect);
-        } else {
-            addWidgetInCell(widgetRect);
-        }
-    }
-
-    private void runTestCase(ReorderTestCase testCase) {
+    private void runTestCase(ReorderTestCase testCase)
+            throws ExecutionException, InterruptedException {
         Point mainWidgetCellPos = testCase.mStart.getMain();
 
-        testCase.mStart.getWidgets().forEach(this::addCorrespondingWidgetRect);
+        FavoriteItemsTransaction transaction =
+                new FavoriteItemsTransaction(mTargetContext, this);
+        mWorkspaceBuilder.buildFromBoard(testCase.mStart, transaction).commit();
 
-        mLauncher.getWorkspace()
-                .getWidgetAtCell(mainWidgetCellPos.x, mainWidgetCellPos.y)
-                .dragWidgetToWorkspace(testCase.moveMainTo.x, testCase.moveMainTo.y)
-                .dismiss(); // dismiss resize frame
+        Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.x,
+                mainWidgetCellPos.y);
+        assertNotNull(widget);
+        WidgetResizeFrame resizeFrame = widget.dragWidgetToWorkspace(testCase.moveMainTo.x,
+                testCase.moveMainTo.y);
+        resizeFrame.dismiss();
 
         boolean isValid = false;
         for (CellLayoutBoard board : testCase.mEnd) {
             isValid |= validateBoard(board);
         }
-        assertTrue("None of the valid boards match with the current state", isValid);
+        assertTrue("Non of the valid boards match with the current state", isValid);
     }
 
     /**
@@ -165,7 +136,8 @@
      *
      * @param testCaseMap map containing all the tests per grid size (Point)
      */
-    private void runTestCaseMap(Map<Point, ReorderTestCase> testCaseMap, String testName) {
+    private void runTestCaseMap(Map<Point, ReorderTestCase> testCaseMap, String testName)
+            throws ExecutionException, InterruptedException {
         Point iconGridDimensions = mLauncher.getWorkspace().getIconGridDimensions();
         Log.d(TAG, "Running test " + testName + " for grid " + iconGridDimensions);
         Assume.assumeTrue(
@@ -176,26 +148,26 @@
 
     @ScreenRecord // b/242323136
     @Test
-    public void simpleReorder() {
+    public void simpleReorder()  throws ExecutionException, InterruptedException {
         runTestCaseMap(SimpleReorderCase.TEST_BY_GRID_SIZE,
                 SimpleReorderCase.class.getSimpleName());
     }
 
     @ScreenRecord // b/242323136
     @Test
-    public void pushTest() {
+    public void pushTest()  throws ExecutionException, InterruptedException {
         runTestCaseMap(PushReorderCase.TEST_BY_GRID_SIZE, PushReorderCase.class.getSimpleName());
     }
 
     @ScreenRecord // b/242323136
     @Test
-    public void fullReorder() {
+    public void fullReorder()  throws ExecutionException, InterruptedException {
         runTestCaseMap(FullReorderCase.TEST_BY_GRID_SIZE, FullReorderCase.class.getSimpleName());
     }
 
     @ScreenRecord // b/242323136
     @Test
-    public void moveOutReorder() {
+    public void moveOutReorder()  throws ExecutionException, InterruptedException {
         runTestCaseMap(MoveOutReorderCase.TEST_BY_GRID_SIZE,
                 MoveOutReorderCase.class.getSimpleName());
     }
diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
new file mode 100644
index 0000000..16448af
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+
+public class TestWorkspaceBuilder {
+
+    private static final String TAG = "CellLayoutBoardBuilder";
+    private static final ComponentName APP_COMPONENT_NAME = new ComponentName(
+            "com.google.android.calculator", "com.android.calculator2.Calculator");
+
+    public AbstractLauncherUiTest mTest;
+
+    private UserHandle mMyUser;
+
+    private Context mContext;
+    private ContentResolver mResolver;
+
+    public TestWorkspaceBuilder(AbstractLauncherUiTest test, Context context) {
+        mTest = test;
+        mMyUser = Process.myUserHandle();
+        mContext = context;
+        mResolver = mContext.getContentResolver();
+    }
+
+    /**
+     * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases.
+     */
+    private FavoriteItemsTransaction fillWithWidgets(CellLayoutBoard.WidgetRect widgetRect,
+            FavoriteItemsTransaction transaction) {
+        int initX = widgetRect.getCellX();
+        int initY = widgetRect.getCellY();
+        for (int x = initX; x < initX + widgetRect.getSpanX(); x++) {
+            for (int y = initY; y < initY + widgetRect.getSpanY(); y++) {
+                try {
+                    // this widgets are filling, we don't care if we can't place them
+                    ItemInfo item = createWidgetInCell(
+                            new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE,
+                                    new Rect(x, y, x, y))
+                    );
+                    transaction.addItem(item);
+                } catch (Exception e) {
+                    Log.d(TAG, "Unable to place filling widget at " + x + "," + y);
+                }
+            }
+        }
+        return transaction;
+    }
+
+    private int getID() {
+        return LauncherSettings.Settings.call(
+                        mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+                .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+    }
+
+    private AppInfo getApp() {
+        return new AppInfo(APP_COMPONENT_NAME, "test icon", mMyUser,
+                AppInfo.makeLaunchIntent(APP_COMPONENT_NAME));
+    }
+
+    private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect,
+            FavoriteItemsTransaction transaction) {
+        if (widgetRect.mType == 'x') {
+            fillWithWidgets(widgetRect, transaction);
+        } else {
+            transaction.addItem(createWidgetInCell(widgetRect));
+        }
+    }
+
+    /**
+     * Builds the given board into the transaction
+     */
+    public FavoriteItemsTransaction buildFromBoard(CellLayoutBoard board,
+            FavoriteItemsTransaction transaction) {
+        board.getWidgets().forEach(
+                (widgetRect) -> addCorrespondingWidgetRect(widgetRect, transaction));
+        board.getIcons().forEach((iconPoint) ->
+                transaction.addItem(createIconInCell(iconPoint))
+        );
+        return transaction;
+    }
+
+    /**
+     * Fills the hotseat row with apps instead of suggestions, for this to work the workspace should
+     * be clean otherwise this doesn't overrides the existing icons.
+     */
+    public FavoriteItemsTransaction fillHotseatIcons(FavoriteItemsTransaction transaction) {
+        int hotseatCount = InvariantDeviceProfile.INSTANCE.get(mContext).numShownHotseatIcons;
+        for (int i = 0; i < hotseatCount; i++) {
+            transaction.addItem(getHotseatValues(i));
+        }
+        return transaction;
+    }
+
+    private ItemInfo createWidgetInCell(CellLayoutBoard.WidgetRect widgetRect) {
+        LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(mTest, false);
+        LauncherAppWidgetInfo item = createWidgetInfo(info,
+                ApplicationProvider.getApplicationContext(), true);
+        item.id = getID();
+        item.cellX = widgetRect.getCellX();
+        item.cellY = widgetRect.getCellY();
+        item.spanX = widgetRect.getSpanX();
+        item.spanY = widgetRect.getSpanY();
+        item.screenId = FIRST_SCREEN_ID;
+        return item;
+    }
+
+    private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint) {
+        WorkspaceItemInfo item = new WorkspaceItemInfo(getApp());
+        item.id = getID();
+        item.screenId = FIRST_SCREEN_ID;
+        item.cellX = iconPoint.getCoord().x;
+        item.cellY = iconPoint.getCoord().y;
+        item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1;
+        item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+        return item;
+    }
+
+    private ItemInfo getHotseatValues(int x) {
+        WorkspaceItemInfo item = new WorkspaceItemInfo(getApp());
+        item.id = getID();
+        item.cellX = x;
+        item.cellY = 0;
+        item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1;
+        item.rank = x;
+        item.screenId = x;
+        item.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+        return item;
+    }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
index 1326389..a98882c 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
@@ -19,6 +19,10 @@
 
 import java.util.Map;
 
+/**
+ * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
+ * in the board mean refer to {@code CellType}
+ */
 public class FullReorderCase {
 
     /** 5x5 Test
@@ -27,13 +31,13 @@
             + "xxxxx\n"
             + "222mm\n"
             + "222mm\n"
-            + "ad111\n"
-            + "bc111";
+            + "ii111\n"
+            + "ii111";
     private static final Point MOVE_TO_5x5 = new Point(0, 4);
     private static final String END_BOARD_STR_5x5 = ""
             + "xxxxx\n"
-            + "222ad\n"
-            + "222bc\n"
+            + "222ii\n"
+            + "222ii\n"
             + "mm111\n"
             + "mm111";
     private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
@@ -46,13 +50,13 @@
             + "xxxxxx\n"
             + "2222mm\n"
             + "2222mm\n"
-            + "ad1111\n"
-            + "bc1111";
+            + "ii1111\n"
+            + "ii1111";
     private static final Point MOVE_TO_6x5 = new Point(0, 4);
     private static final String END_BOARD_STR_6x5 = ""
             + "xxxxxx\n"
-            + "2222ad\n"
-            + "2222bc\n"
+            + "2222ii\n"
+            + "2222ii\n"
             + "mm1111\n"
             + "mm1111";
     private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
@@ -64,13 +68,13 @@
     private static final String START_BOARD_STR_4x4 = ""
             + "xxxx\n"
             + "22mm\n"
-            + "admm\n"
-            + "bc11";
+            + "iimm\n"
+            + "ii11";
     private static final Point MOVE_TO_4x4 = new Point(0, 3);
     private static final String END_BOARD_STR_4x4 = ""
             + "xxxx\n"
-            + "22ad\n"
-            + "mmbc\n"
+            + "22ii\n"
+            + "mmii\n"
             + "mm11";
 
     private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4,
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
index 1701390..a222d3d 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
@@ -19,6 +19,10 @@
 
 import java.util.Map;
 
+/**
+ * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
+ * in the board mean refer to {@code CellType}
+ */
 public class MoveOutReorderCase {
 
     /** 5x5 Test
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
index bb8d5e9..e16ff42 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
@@ -19,6 +19,10 @@
 
 import java.util.Map;
 
+/**
+ * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
+ * in the board mean refer to {@code CellType}
+ */
 public class PushReorderCase {
 
     /** 5x5 Test
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
index 30269a0..546c48b 100644
--- a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
+++ b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
@@ -19,6 +19,10 @@
 
 import java.util.Map;
 
+/**
+ * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character
+ * in the board mean refer to {@code CellType}
+ */
 public class SimpleReorderCase {
 
     /** 5x5 Test
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 304153f..a66b09a 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -319,7 +319,7 @@
     /**
      * Adds {@param item} on the homescreen on the 0th screen
      */
-    protected void addItemToScreen(ItemInfo item) {
+    public void addItemToScreen(ItemInfo item) {
         WidgetUtils.addItemToScreen(item, mTargetContext);
         resetLoaderState();
 
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 5baa7d2..be49974 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -214,9 +214,9 @@
         // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
         executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
                 isWorkspaceScrollable(launcher)));
-        workspace.verifyWorkspaceAppIconIsGone(
-                "Chrome app was found on empty workspace", "Chrome");
-
+        assertEquals("Initial workspace doesn't have the correct page", workspace.pagesPerScreen(),
+                workspace.getPageCount());
+        workspace.verifyWorkspaceAppIconIsGone("Chrome app was found on empty workspace", "Chrome");
         workspace.ensureWorkspaceIsScrollable();
 
         executeOnLauncher(
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 5761abd..302bd2f 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
+
 import android.util.Log;
 import android.view.View;
 
@@ -48,6 +49,7 @@
 
     private int mProfileUserId;
     private boolean mWorkProfileSetupSuccessful;
+    private final String TAG = "WorkProfileTest";
 
     @Before
     @Override
@@ -56,18 +58,17 @@
         String output =
                 mDevice.executeShellCommand(
                         "pm create-user --profileOf 0 --managed TestProfile");
-        Log.d("b/203817455", "pm create-user; output: " + output);
-
-        if (output.startsWith("Success")){
-            assertTrue("Failed to create work profile", output.startsWith("Success"));
-            mWorkProfileSetupSuccessful = true;
-        } else {
-            return; // no need to setup launcher since all tests will skip.
-        }
+        // b/203817455
+        updateWorkProfileSetupSuccessful("pm create-user", output);
 
         String[] tokens = output.split("\\s+");
         mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
-        mDevice.executeShellCommand("am start-user " + mProfileUserId);
+        output = mDevice.executeShellCommand("am start-user " + mProfileUserId);
+        updateWorkProfileSetupSuccessful("am start-user", output);
+
+        if (!mWorkProfileSetupSuccessful) {
+            return; // no need to setup launcher since all tests will skip.
+        }
 
         mDevice.pressHome();
         waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
@@ -107,6 +108,7 @@
     @Test
     public void workTabExists() {
         assumeTrue(mWorkProfileSetupSuccessful);
+        waitForWorkTabSetup();
         waitForLauncherCondition("Personal tab is missing",
                 launcher -> launcher.getAppsView().isPersonalTabVisible(),
                 LauncherInstrumentation.WAIT_TIME_MS);
@@ -134,7 +136,11 @@
                 LauncherInstrumentation.WAIT_TIME_MS);
 
         //start work profile toggle OFF test
-        executeOnLauncher(l -> l.getAppsView().getWorkManager().getWorkModeSwitch().performClick());
+        executeOnLauncher(l -> {
+            // Ensure updates are not deferred so notification happens when apps pause.
+            l.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
+            l.getAppsView().getWorkManager().getWorkModeSwitch().performClick();
+        });
 
         waitForLauncherCondition("Work profile toggle OFF failed", launcher -> {
             manager.reset(); // pulls current state from system
@@ -183,4 +189,14 @@
             }
         }, LauncherInstrumentation.WAIT_TIME_MS);
     }
+
+    private void updateWorkProfileSetupSuccessful(String cli, String output) {
+        Log.d(TAG, "updateWorkProfileSetupSuccessful, cli=" + cli + " " + "output=" + output);
+        if (output.startsWith("Success")) {
+            assertTrue(output, output.startsWith("Success"));
+            mWorkProfileSetupSuccessful = true;
+        } else {
+            mWorkProfileSetupSuccessful = false;
+        }
+    }
 }
diff --git a/tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
similarity index 75%
rename from tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt
rename to tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
index 309d055..a4f189c 100644
--- a/tests/src/com/android/launcher3/util/MultiAdditivePropertyTest.kt
+++ b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt
@@ -16,26 +16,32 @@
 
 package com.android.launcher3.util
 
-import android.view.View
+import android.util.FloatProperty
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 
-/** Unit tests for [MultiAdditivePropertyFactory] */
+/** Unit tests for [MultiPropertyFactory] */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class MultiAdditivePropertyTest {
+class MultiPropertyFactoryTest {
 
     private val received = mutableListOf<Float>()
 
-    private val factory =
-        object : MultiAdditivePropertyFactory<View?>("Test", View.TRANSLATION_X) {
-            override fun apply(obj: View?, value: Float) {
-                received.add(value)
-            }
+    private val receiveProperty: FloatProperty<Any> = object : FloatProperty<Any>("receive") {
+        override fun setValue(obj: Any?, value: Float) {
+            received.add(value)
         }
+        override fun get(o: Any): Float {
+            return 0f
+        }
+    }
+
+    private val factory = MultiPropertyFactory("depth_property", receiveProperty) {
+        x: Float, y: Float -> x + y
+    }
 
     private val p1 = factory.get(1)
     private val p2 = factory.get(2)
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
index 5f92199..c365708 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
@@ -15,12 +15,21 @@
  */
 package com.android.launcher3.tapl;
 
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
 /**
  * Operations on home screen qsb.
  */
 public class HomeQsb {
 
     private final LauncherInstrumentation mLauncher;
+    private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
+    private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
+
 
     HomeQsb(LauncherInstrumentation launcher) {
         mLauncher = launcher;
@@ -28,6 +37,35 @@
     }
 
     /**
+     * Launch assistant app by tapping mic icon on qsb.
+     */
+    @NonNull
+    public LaunchedAppState launchAssistant() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to click assistant mic icon button");
+             LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            UiObject2 assistantIcon = mLauncher.waitForLauncherObject(ASSISTANT_ICON_RES_ID);
+
+            LauncherInstrumentation.log("HomeQsb.launchAssistant before click "
+                    + assistantIcon.getVisibleCenter() + " in "
+                    + mLauncher.getVisibleBounds(assistantIcon));
+
+            mLauncher.clickLauncherObject(assistantIcon);
+
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+                // assert Assistant App Launched
+                BySelector selector = By.pkg(ASSISTANT_APP_PACKAGE);
+                mLauncher.assertTrue(
+                        "assistant app didn't start: (" + selector + ")",
+                        mLauncher.getDevice().wait(Until.hasObject(selector),
+                                LauncherInstrumentation.WAIT_TIME_MS)
+                );
+                return new LaunchedAppState(mLauncher);
+            }
+        }
+    }
+
+    /**
      * Show search result page from tapping qsb.
      */
     public SearchResultFromQsb showSearchResult() {
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 04167839..a17651b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
@@ -54,13 +55,23 @@
     public Taskbar getTaskbar() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to get the taskbar")) {
-            mLauncher.waitForLauncherObject("taskbar_view");
+            mLauncher.waitForLauncherObject(TASKBAR_RES_ID);
 
             return new Taskbar(mLauncher);
         }
     }
 
     /**
+     * Waits for the taskbar to be hidden, or fails.
+     */
+    public void assertTaskbarHidden() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "waiting for taskbar to be hidden")) {
+            mLauncher.waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+        }
+    }
+
+    /**
      * Returns the Taskbar in a visible state.
      *
      * The taskbar must already be hidden when calling this method.
@@ -71,7 +82,7 @@
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                      "want to show the taskbar")) {
-            mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+            mLauncher.waitUntilLauncherObjectGone(TASKBAR_RES_ID);
 
             final long downTime = SystemClock.uptimeMillis();
             final int unstashTargetY = mLauncher.getRealDisplaySize().y
@@ -85,7 +96,7 @@
             LauncherInstrumentation.log("showTaskbar: sent down");
 
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
-                mLauncher.waitForLauncherObject("taskbar_view");
+                mLauncher.waitForLauncherObject(TASKBAR_RES_ID);
                 mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
                         LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index fa7e8e9..1fb8cc7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -168,7 +168,7 @@
     private static final String OVERVIEW_RES_ID = "overview_panel";
     private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
     private static final String CONTEXT_MENU_RES_ID = "popup_container";
-    private static final String TASKBAR_RES_ID = "taskbar_view";
+    static final String TASKBAR_RES_ID = "taskbar_view";
     private static final String SPLIT_PLACEHOLDER_RES_ID = "split_placeholder";
     public static final int WAIT_TIME_MS = 30000;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
@@ -1755,6 +1755,15 @@
         getTestInfo(TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED);
     }
 
+    /**
+     * Recreates the taskbar (outside of tests this is done for certain configuration changes).
+     * The expected behavior is that the taskbar retains its current state after being recreated.
+     * For example, if taskbar is currently stashed, it should still be stashed after recreating.
+     */
+    public void recreateTaskbar() {
+        getTestInfo(TestProtocol.REQUEST_RECREATE_TASKBAR);
+    }
+
     public List<String> getHotseatIconNames() {
         return getTestInfo(TestProtocol.REQUEST_HOTSEAT_ICON_NAMES)
                 .getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index 5d9be36..0f9d5f5 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
 import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
 
@@ -51,7 +52,7 @@
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to get a taskbar icon")) {
             return new TaskbarAppIcon(mLauncher, mLauncher.waitForObjectInContainer(
-                    mLauncher.waitForLauncherObject("taskbar_view"),
+                    mLauncher.waitForLauncherObject(TASKBAR_RES_ID),
                     AppIcon.getAppIconSelector(appName, mLauncher)));
         }
     }
@@ -67,7 +68,7 @@
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to hide the taskbar");
              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-            mLauncher.waitForLauncherObject("taskbar_view");
+            mLauncher.waitForLauncherObject(TASKBAR_RES_ID);
 
             final long downTime = SystemClock.uptimeMillis();
             Point stashTarget = new Point(
@@ -96,7 +97,7 @@
              LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
 
             mLauncher.clickLauncherObject(mLauncher.waitForObjectInContainer(
-                    mLauncher.waitForLauncherObject("taskbar_view"), getAllAppsButtonSelector()));
+                    mLauncher.waitForLauncherObject(TASKBAR_RES_ID), getAllAppsButtonSelector()));
 
             return new AllAppsFromTaskbar(mLauncher);
         }
@@ -107,7 +108,7 @@
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to get all taskbar icons")) {
             return mLauncher.waitForObjectsInContainer(
-                    mLauncher.waitForLauncherObject("taskbar_view"),
+                    mLauncher.waitForLauncherObject(TASKBAR_RES_ID),
                     AppIcon.getAnyAppIconSelector())
                     .stream()
                     .map(UiObject2::getText)
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index efbdb23..5fab7eb 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -192,6 +192,12 @@
         }
     }
 
+    /** Returns the number of pages. */
+    public int getPageCount() {
+        final UiObject2 workspace = verifyActiveContainer();
+        return workspace.getChildCount();
+    }
+
     /**
      * Returns the number of pages that are visible on the screen simultaneously.
      */