Merge "Uninstall an app from workspace via TAPL"
diff --git a/go/quickstep/src/com/android/launcher3/AppSharing.java b/go/quickstep/src/com/android/launcher3/AppSharing.java
index 7c66f6e..c252fba 100644
--- a/go/quickstep/src/com/android/launcher3/AppSharing.java
+++ b/go/quickstep/src/com/android/launcher3/AppSharing.java
@@ -22,6 +22,9 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
@@ -98,14 +101,19 @@
      * The Share App system shortcut, used to initiate p2p sharing of a given app
      */
     public final class Share extends SystemShortcut<Launcher> {
-        private PopupDataProvider mPopupDataProvider;
+        private final PopupDataProvider mPopupDataProvider;
+        private final boolean mSharingEnabledForUser;
 
         public Share(Launcher target, ItemInfo itemInfo) {
             super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo);
             mPopupDataProvider = target.getPopupDataProvider();
 
-            if (ENABLE_SHAREABILITY_CHECK) {
-                mShareabilityMgr = AppShareabilityManager.INSTANCE.get(target);
+            mSharingEnabledForUser = bluetoothSharingEnabled(target);
+            if (!mSharingEnabledForUser) {
+                setEnabled(false);
+            } else if (ENABLE_SHAREABILITY_CHECK) {
+                mShareabilityMgr =
+                        AppShareabilityManager.INSTANCE.get(target.getApplicationContext());
                 checkShareability(/* requestUpdateIfUnknown */ true);
             }
         }
@@ -144,7 +152,12 @@
             sendIntent.setType(APP_MIME_TYPE);
             sendIntent.setComponent(ComponentName.unflattenFromString(mSharingComponent));
 
-            mTarget.startActivitySafely(view, sendIntent, mItemInfo);
+            UserHandle user = mItemInfo.user;
+            if (user != null && !user.equals(Process.myUserHandle())) {
+                mTarget.startActivityAsUser(sendIntent, user);
+            } else {
+                mTarget.startActivitySafely(view, sendIntent, mItemInfo);
+            }
 
             AbstractFloatingView.closeAllOpenViews(mTarget);
         }
@@ -170,8 +183,15 @@
             }
         }
 
+        private boolean bluetoothSharingEnabled(Context context) {
+            return !context.getSystemService(UserManager.class)
+                    .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH_SHARING, mItemInfo.user);
+        }
+
         private void showCannotShareToast(Context context) {
-            CharSequence text = context.getText(R.string.toast_p2p_app_not_shareable);
+            CharSequence text = (mSharingEnabledForUser)
+                    ? context.getText(R.string.toast_p2p_app_not_shareable)
+                    : context.getText(R.string.blocked_by_policy);
             int duration = Toast.LENGTH_SHORT;
             Toast.makeText(context, text, duration).show();
         }
diff --git a/go/quickstep/src/com/android/launcher3/model/AppShareabilityManager.java b/go/quickstep/src/com/android/launcher3/model/AppShareabilityManager.java
index cf80c35..0d0f700 100644
--- a/go/quickstep/src/com/android/launcher3/model/AppShareabilityManager.java
+++ b/go/quickstep/src/com/android/launcher3/model/AppShareabilityManager.java
@@ -47,7 +47,7 @@
  * Each app's status is retrieved from the Play Store's API. Statuses are cached in order
  * to limit extraneous calls to that API (which can be time-consuming).
  */
-public final class AppShareabilityManager {
+public class AppShareabilityManager {
     @Retention(SOURCE)
     @IntDef({
         ShareabilityStatus.UNKNOWN,
diff --git a/quickstep/res/drawable/ic_sysbar_back_kids.xml b/quickstep/res/drawable/ic_sysbar_back_kids.xml
new file mode 100644
index 0000000..ac6d49b
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_back_kids.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M21,11.4v1c0,1 -0.8,1.8 -1.8,1.8l0,0h-8.8l2.7,2.7c0.7,0.7 0.7,1.8 0,2.5c0,0 0,0 0,0l-0.7,0.7c-0.7,0.7 -1.8,0.7 -2.5,0c0,0 0,0 0,0l-5.9,-5.9C2.6,13 2.6,11 3.9,9.7l5.9,-5.9c0.7,-0.7 1.8,-0.7 2.5,0c0,0 0,0 0,0l0.7,0.7c0.7,0.7 0.7,1.8 0,2.5c0,0 0,0 0,0l-2.6,2.6h8.7C20.2,9.6 21,10.5 21,11.4z"/>
+</vector>
diff --git a/quickstep/res/drawable/ic_sysbar_home_kids.xml b/quickstep/res/drawable/ic_sysbar_home_kids.xml
new file mode 100644
index 0000000..2397e70
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_home_kids.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M22.7,11l-7.9,-7c-1.6,-1.4 -3.9,-1.4 -5.5,0L7.4,5.7V4.1C7.4,3.5 6.9,3 6.3,3H5.2C4.5,3 4,3.5 4,4.1c0,0 0,0 0,0v4.6L1.4,11c-0.5,0.4 -0.5,1.1 -0.1,1.5c0,0 0,0 0,0c0.2,0.2 0.5,0.4 0.8,0.4H4v5c0,1.3 1,2.3 2.3,2.3c0,0 0,0 0,0h11.4c1.3,0 2.3,-1 2.3,-2.3v-5h2c0.6,0 1.1,-0.5 1.1,-1.1C23,11.5 22.9,11.2 22.7,11L22.7,11zM14.3,15.6c0,0.6 -0.5,1.1 -1.1,1.2h-2.3c-0.6,0 -1.1,-0.5 -1.1,-1.1v-1.3c0,-0.6 0.5,-1.1 1.1,-1.1c0,0 0,0 0,0h2.3c0.6,0 1.1,0.5 1.1,1.1c0,0 0,0 0,0L14.3,15.6z"/>
+</vector>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c9e33a7..0e5ba2f 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -231,4 +231,10 @@
     <dimen name="taskbar_stashed_handle_height">4dp</dimen>
     <dimen name="taskbar_edu_wave_anim_trans_y">25dp</dimen>
     <dimen name="taskbar_edu_wave_anim_trans_y_return_overshoot">4dp</dimen>
+    <dimen name="taskbar_nav_buttons_width_kids">88dp</dimen>
+    <dimen name="taskbar_nav_buttons_height_kids">40dp</dimen>
+    <dimen name="taskbar_nav_buttons_corner_radius_kids">40dp</dimen>
+    <dimen name="taskbar_back_button_left_margin_kids">96dp</dimen>
+    <dimen name="taskbar_home_button_left_margin_kids">48dp</dimen>
+    <dimen name="taskbar_icon_size_kids">32dp</dimen>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 4269f2d..65b346a 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -336,7 +336,8 @@
                             getSystemService(DeviceStateManager.class),
                             getSystemService(SensorManager.class),
                             getMainThreadHandler(),
-                            getMainExecutor()
+                            getMainExecutor(),
+                            /* tracingTagPrefix= */ "launcher"
                     );
 
             mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
@@ -507,7 +508,7 @@
             ActivityOptionsCompat.setLauncherSourceInfo(
                     activityOptions.options, mLastTouchUpTime);
         }
-        activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+        activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
         addLaunchCookie(item, activityOptions.options);
         return activityOptions;
     }
diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
index b0fba3d..9c3daea 100644
--- a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -34,7 +34,6 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -60,13 +59,13 @@
         Utilities.getDevicePrefs(context).edit()
                 .putBoolean(LAST_PREDICTION_ENABLED_STATE, !mTargets.isEmpty()).apply();
 
-        FixedContainerItems fci = mPredictorState.items;
-        Set<UserHandle> usersForChangedShortcuts = new HashSet<>(fci.items.stream()
-                .filter(info -> info.itemType == ITEM_TYPE_DEEP_SHORTCUT)
-                .map(info -> info.user)
-                .collect(Collectors.toSet()));
-        fci.items.clear();
+        Set<UserHandle> usersForChangedShortcuts =
+                dataModel.extraItems.get(mPredictorState.containerId).items.stream()
+                        .filter(info -> info.itemType == ITEM_TYPE_DEEP_SHORTCUT)
+                        .map(info -> info.user)
+                        .collect(Collectors.toSet());
 
+        FixedContainerItems fci = new FixedContainerItems(mPredictorState.containerId);
         for (AppTarget target : mTargets) {
             WorkspaceItemInfo itemInfo;
             ShortcutInfo si = target.getShortcutInfo();
@@ -109,6 +108,7 @@
             fci.items.add(itemInfo);
         }
 
+        dataModel.extraItems.put(fci.containerId, fci);
         bindExtraContainerItems(fci);
         usersForChangedShortcuts.forEach(
                 u -> dataModel.updateShortcutPinnedState(app.getContext(), u));
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index da18357..0e534f4 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -119,18 +119,19 @@
 
         WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(
                 mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns);
-        mAllAppsState.items.setItems(
+        FixedContainerItems allAppsItems = new FixedContainerItems(mAllAppsState.containerId,
                 mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
-        mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items);
+        mDataModel.extraItems.put(mAllAppsState.containerId, allAppsItems);
 
         WorkspaceItemFactory hotseatFactory =
                 new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseHotseatIcons);
-        mHotseatState.items.setItems(
+        FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId,
                 mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
-        mDataModel.extraItems.put(CONTAINER_HOTSEAT_PREDICTION, mHotseatState.items);
+        mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems);
 
         // Widgets prediction isn't used frequently. And thus, it is not persisted on disk.
-        mDataModel.extraItems.put(CONTAINER_WIDGETS_PREDICTION, mWidgetsRecommendationState.items);
+        mDataModel.extraItems.put(mWidgetsRecommendationState.containerId,
+                new FixedContainerItems(mWidgetsRecommendationState.containerId));
         mActive = true;
     }
 
@@ -371,14 +372,14 @@
 
     static class PredictorState {
 
-        public final FixedContainerItems items;
+        public final int containerId;
         public final PersistedItemArray<ItemInfo> storage;
         public AppPredictor predictor;
 
         private List<AppTarget> mLastTargets;
 
-        PredictorState(int container, String storageName) {
-            items = new FixedContainerItems(container);
+        PredictorState(int containerId, String storageName) {
+            this.containerId = containerId;
             storage = new PersistedItemArray<>(storageName);
             mLastTargets = Collections.emptyList();
         }
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 4be83dc..9cd9d85 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -59,8 +59,8 @@
         Map<PackageUserKey, List<WidgetItem>> allWidgets =
                 dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
 
-        FixedContainerItems fixedContainerItems = mPredictorState.items;
-        fixedContainerItems.items.clear();
+        FixedContainerItems fixedContainerItems =
+                new FixedContainerItems(mPredictorState.containerId);
 
         if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
             for (AppTarget app : mTargets) {
@@ -100,6 +100,7 @@
                 }
             }
         }
+        dataModel.extraItems.put(mPredictorState.containerId, fixedContainerItems);
         bindExtraContainerItems(fixedContainerItems);
 
         // Don't store widgets prediction to disk because it is not used frequently.
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index a4ecd8b..002a86c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -45,10 +45,12 @@
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Region.Op;
 import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.PaintDrawable;
 import android.util.Property;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -58,6 +60,7 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
@@ -128,6 +131,7 @@
     private View mA11yButton;
     private int mSysuiStateFlags;
     private View mBackButton;
+    private View mHomeButton;
     private FloatingRotationButton mFloatingRotationButton;
 
     public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
@@ -171,6 +175,7 @@
 
         // Force nav buttons (specifically back button) to be visible during setup wizard.
         boolean isInSetup = !mContext.isUserSetupComplete();
+        boolean isInKidsMode = mContext.isNavBarKidsModeActive();
         boolean alwaysShowButtons = isThreeButtonNav || isInSetup;
 
         // Make sure to remove nav bar buttons translation when notification shade is expanded or
@@ -208,6 +213,64 @@
                         & Configuration.UI_MODE_NIGHT_MASK;
                 boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
                 mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
+            } else if (isInKidsMode) {
+                int iconSize = mContext.getResources().getDimensionPixelSize(
+                        R.dimen.taskbar_icon_size_kids);
+                int buttonWidth = mContext.getResources().getDimensionPixelSize(
+                        R.dimen.taskbar_nav_buttons_width_kids);
+                int buttonHeight = mContext.getResources().getDimensionPixelSize(
+                        R.dimen.taskbar_nav_buttons_height_kids);
+                int buttonRadius = mContext.getResources().getDimensionPixelSize(
+                        R.dimen.taskbar_nav_buttons_corner_radius_kids);
+                int paddingleft = (buttonWidth - iconSize) / 2;
+                int paddingRight = paddingleft;
+                int paddingTop = (buttonHeight - iconSize) / 2;
+                int paddingBottom = paddingTop;
+
+                // Update icons
+                ((ImageView) mBackButton).setImageDrawable(
+                        mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids));
+                ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+                mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+                ((ImageView) mHomeButton).setImageDrawable(
+                        mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids));
+                ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+                mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+
+                // Home button layout
+                LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams(
+                        buttonWidth,
+                        buttonHeight
+                );
+                int homeButtonLeftMargin = mContext.getResources().getDimensionPixelSize(
+                        R.dimen.taskbar_home_button_left_margin_kids);
+                homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0);
+                mHomeButton.setLayoutParams(homeLayoutparams);
+
+                // Back button layout
+                LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams(
+                        buttonWidth,
+                        buttonHeight
+                );
+                int backButtonLeftMargin = mContext.getResources().getDimensionPixelSize(
+                        R.dimen.taskbar_back_button_left_margin_kids);
+                backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0);
+                mBackButton.setLayoutParams(backLayoutParams);
+
+                // Button backgrounds
+                int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1);
+                PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha);
+                buttonBackground.setCornerRadius(buttonRadius);
+                mHomeButton.setBackground(buttonBackground);
+                mBackButton.setBackground(buttonBackground);
+
+                // Update alignment within taskbar
+                FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+                        mNavButtonContainer.getLayoutParams();
+                navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd() / 2);
+                navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart());
+                navButtonsLayoutParams.gravity = Gravity.CENTER;
+                mNavButtonContainer.requestLayout();
             }
 
             // Animate taskbar background when any of these flags are enabled
@@ -276,9 +339,9 @@
                             && ((flags & FLAG_KEYGUARD_VISIBLE) == 0 || showingOnKeyguard);
                 }));
         boolean isRtl = Utilities.isRtl(mContext.getResources());
-        mPropertyHolders.add(new StatePropertyHolder(
-                mBackButton, flags -> (flags & FLAG_IME_VISIBLE) != 0, View.ROTATION,
-                isRtl ? 90 : -90, 0));
+        mPropertyHolders.add(new StatePropertyHolder(mBackButton,
+                flags -> (flags & FLAG_IME_VISIBLE) != 0 && !mContext.isNavBarKidsModeActive(),
+                View.ROTATION, isRtl ? 90 : -90, 0));
         // Translate back button to be at end/start of other buttons for keyguard
         int navButtonSize = mContext.getResources().getDimensionPixelSize(
                 R.dimen.taskbar_nav_buttons_size);
@@ -289,16 +352,16 @@
 
 
         // home and recents buttons
-        View homeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
+        mHomeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
                 navButtonController, R.id.home);
-        mPropertyHolders.add(new StatePropertyHolder(homeButton,
+        mPropertyHolders.add(new StatePropertyHolder(mHomeButton,
                 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
                         (flags & FLAG_DISABLE_HOME) == 0));
         View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
                 navContainer, navButtonController, R.id.recent_apps);
         mPropertyHolders.add(new StatePropertyHolder(recentsButton,
-                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
-                        (flags & FLAG_DISABLE_RECENTS) == 0));
+                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && (flags & FLAG_DISABLE_RECENTS) == 0
+                        && !mContext.isNavBarKidsModeActive()));
 
         // A11y button
         mA11yButton = addButton(R.drawable.ic_sysbar_accessibility_button, BUTTON_A11Y,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index b1f027a..1198dc5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -112,6 +112,7 @@
 
     private final boolean mIsSafeModeEnabled;
     private final boolean mIsUserSetupComplete;
+    private final boolean mIsNavBarKidsMode;
     private boolean mIsDestroyed = false;
     // The flag to know if the window is excluded from magnification region computation.
     private boolean mIsExcludeFromMagnificationRegion = false;
@@ -129,6 +130,8 @@
                 () -> getPackageManager().isSafeMode());
         mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
+        mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(
+                Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
 
         final Resources resources = getResources();
         float taskbarIconSize = resources.getDimension(R.dimen.taskbar_icon_size);
@@ -385,7 +388,8 @@
                 | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
         onNotificationShadeExpandChanged((systemUiStateFlags & shadeExpandedFlags) != 0, fromInit);
         mControllers.taskbarViewController.setRecentsButtonDisabled(
-                mControllers.navbarButtonsViewController.isRecentsDisabled());
+                mControllers.navbarButtonsViewController.isRecentsDisabled()
+                        || isNavBarKidsModeActive());
         mControllers.stashedHandleViewController.setIsHomeButtonDisabled(
                 mControllers.navbarButtonsViewController.isHomeDisabled());
         mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
@@ -587,6 +591,10 @@
         return mIsUserSetupComplete;
     }
 
+    protected boolean isNavBarKidsModeActive() {
+        return mIsNavBarKidsMode && isThreeButtonNav();
+    }
+
     /**
      * Called when we determine the touchable region.
      *
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 7f2e37c..a262674 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -63,11 +63,15 @@
     private static final Uri USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(
             Settings.Secure.USER_SETUP_COMPLETE);
 
+    private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor(
+            Settings.Secure.NAV_BAR_KIDS_MODE);
+
     private final Context mContext;
     private final DisplayController mDisplayController;
     private final SysUINavigationMode mSysUINavigationMode;
     private final TaskbarNavButtonController mNavButtonController;
     private final SettingsCache.OnChangeListener mUserSetupCompleteListener;
+    private final SettingsCache.OnChangeListener mNavBarKidsModeListener;
     private final ComponentCallbacks mComponentCallbacks;
     private final SimpleBroadcastReceiver mShutdownReceiver;
 
@@ -97,6 +101,7 @@
         mNavButtonController = new TaskbarNavButtonController(service,
                 SystemUiProxy.INSTANCE.get(mContext), new Handler());
         mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar();
+        mNavBarKidsModeListener = isNavBarKidsMode -> recreateTaskbar();
         mComponentCallbacks = new ComponentCallbacks() {
             private Configuration mOldConfig = mContext.getResources().getConfiguration();
 
@@ -126,6 +131,8 @@
         mSysUINavigationMode.addModeChangeListener(this);
         SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI,
                 mUserSetupCompleteListener);
+        SettingsCache.INSTANCE.get(mContext).register(NAV_BAR_KIDS_MODE,
+                mNavBarKidsModeListener);
         mContext.registerComponentCallbacks(mComponentCallbacks);
         mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN);
 
@@ -289,6 +296,8 @@
         mSysUINavigationMode.removeModeChangeListener(this);
         SettingsCache.INSTANCE.get(mContext).unregister(USER_SETUP_COMPLETE_URI,
                 mUserSetupCompleteListener);
+        SettingsCache.INSTANCE.get(mContext).unregister(NAV_BAR_KIDS_MODE,
+                mNavBarKidsModeListener);
         mContext.unregisterComponentCallbacks(mComponentCallbacks);
         mContext.unregisterReceiver(mShutdownReceiver);
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index c85b256..0eaea83 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -29,12 +29,9 @@
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 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.TASK_PRIMARY_SPLIT_TRANSLATION;
-import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
 import android.util.FloatProperty;
-import android.util.Pair;
 
 import androidx.annotation.NonNull;
 
@@ -43,7 +40,6 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -100,15 +96,6 @@
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
         setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
-        PagedOrientationHandler orientationHandler =
-                ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
-        Pair<FloatProperty, FloatProperty> taskViewsFloat =
-                orientationHandler.getSplitSelectTaskOffset(
-                        TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
-                        mLauncher.getDeviceProfile());
-        setter.setFloat(mRecentsView, taskViewsFloat.first,
-                toState.getSplitSelectTranslation(mLauncher), LINEAR);
-        setter.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
 
         setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
                 config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 1cf50f7..c058aa7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -71,7 +71,7 @@
             }
         }
         activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_EMPTY);
+        activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_EMPTY);
         Object itemInfo = hostView.getTag();
         if (itemInfo instanceof ItemInfo) {
             mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 19897a1..32ce1c4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -23,13 +23,17 @@
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
+import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
 import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
 
 import android.annotation.TargetApi;
 import android.os.Build;
 import android.util.FloatProperty;
+import android.util.Pair;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
@@ -37,6 +41,7 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.views.ClearAllButton;
 import com.android.quickstep.views.LauncherRecentsView;
@@ -67,11 +72,7 @@
         // DepthController to prevent optimizations which might occlude the layers behind
         mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi);
 
-        if (isSplitSelectionState(state)) {
-            mRecentsView.applySplitPrimaryScrollOffset();
-        } else {
-            mRecentsView.resetSplitPrimaryScrollOffset();
-        }
+        handleSplitSelectionState(state, null);
     }
 
     @Override
@@ -92,16 +93,7 @@
         builder.addListener(AnimatorListeners.forSuccessCallback(() ->
                 mLauncher.getDepthController().setHasContentBehindLauncher(toState.overviewUi)));
 
-        // Create or dismiss split screen select animations
-        LauncherState currentState = mLauncher.getStateManager().getState();
-        if (isSplitSelectionState(toState) && !isSplitSelectionState(currentState)) {
-            builder.add(mRecentsView.createSplitSelectInitAnimation().buildAnim());
-        }
-        if (isSplitSelectionState(toState)) {
-            mRecentsView.applySplitPrimaryScrollOffset();
-        } else {
-            mRecentsView.resetSplitPrimaryScrollOffset();
-        }
+        handleSplitSelectionState(toState, builder);
 
         setAlphas(builder, config, toState);
         builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
@@ -109,10 +101,52 @@
     }
 
     /**
-     * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+     * Create or dismiss split screen select animations.
+     * @param builder if null then this will run the split select animations right away, otherwise
+     *                will add animations to builder.
      */
-    private boolean isSplitSelectionState(@NonNull LauncherState toState) {
-        return toState == OVERVIEW_SPLIT_SELECT;
+    private void handleSplitSelectionState(@NonNull LauncherState toState,
+            @Nullable PendingAnimation builder) {
+        LauncherState currentState = mLauncher.getStateManager().getState();
+        boolean animate = builder != null;
+        PagedOrientationHandler orientationHandler =
+                ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
+        Pair<FloatProperty, FloatProperty> taskViewsFloat =
+                orientationHandler.getSplitSelectTaskOffset(
+                        TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
+                        mLauncher.getDeviceProfile());
+
+        if (isSplitSelectionState(currentState, toState)) {
+            // Animation to "dismiss" selected taskView
+            PendingAnimation splitSelectInitAnimation =
+                    mRecentsView.createSplitSelectInitAnimation();
+            // Add properties to shift remaining taskViews to get out of placeholder view
+            splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.first,
+                    toState.getSplitSelectTranslation(mLauncher), LINEAR);
+            splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
+
+            if (!animate && isSplitSelectionState(currentState, toState)) {
+                splitSelectInitAnimation.buildAnim().start();
+            } else if (animate &&
+                    isSplitSelectionState(currentState, toState)) {
+                builder.add(splitSelectInitAnimation.buildAnim());
+            }
+        }
+
+        if (isSplitSelectionState(currentState, toState)) {
+            mRecentsView.applySplitPrimaryScrollOffset();
+        } else {
+            mRecentsView.resetSplitPrimaryScrollOffset();
+        }
+    }
+
+    /**
+     * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+     *          and {@param fromState} is not {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+     */
+    private boolean isSplitSelectionState(@NonNull LauncherState fromState,
+            @NonNull LauncherState toState) {
+        return fromState != OVERVIEW_SPLIT_SELECT && toState == OVERVIEW_SPLIT_SELECT;
     }
 
     private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 0e2fbbc..75cf5cb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -17,6 +17,7 @@
 
 import static android.view.View.VISIBLE;
 
+import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -44,6 +45,10 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_OPAQUE_THRESHOLD;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_VISIBLE_THRESHOLD;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 
 import android.animation.ValueAnimator;
@@ -52,6 +57,7 @@
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.quickstep.SysUINavigationMode;
@@ -171,6 +177,13 @@
                 mHintToNormalDuration = (int) va.getDuration();
             }
             config.duration = Math.max(config.duration, mHintToNormalDuration);
+        } else if (fromState == ALL_APPS && toState == NORMAL) {
+            config.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
+                    1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
+                    1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
+            config.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(DEACCEL,
+                    1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
+                    1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 3c83d25..59ade49 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -52,22 +52,22 @@
     /**
      * The progress at which all apps content will be fully visible.
      */
-    protected static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;
+    public static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;
 
     /**
      * Minimum clamping progress for fading in all apps content
      */
-    protected static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f;
+    public static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f;
 
     /**
      * Minimum clamping progress for fading in all apps scrim
      */
-    protected static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f;
+    public static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f;
 
     /**
      * Maximum clamping progress for opaque all apps scrim
      */
-    protected static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
+    public static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
 
     private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index c7f1edd..9f1e47f 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1001,7 +1001,7 @@
                 }
                 if (mRecentsAnimationTargets != null) {
                     TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps,
-                            true /*shown*/, true /*animate*/);
+                            true /*shown*/, false /*animate*/);
                 }
                 break;
         }
@@ -1654,7 +1654,7 @@
 
         if (mRecentsAnimationTargets != null) {
             TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps,
-                    true /*shown*/, true /*animate*/);
+                    true /*shown*/, false /*animate*/);
         }
 
         // Leave the pending invisible flag, as it may be used by wallpaper open animation.
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 103f350..306e864 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -255,7 +255,7 @@
         final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(
                 ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
                 onEndCallback);
-        activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+        activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
         return activityOptions;
     }
 
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 0246849..276e1c2 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -23,6 +23,7 @@
 import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
 
 import android.annotation.SuppressLint;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Matrix;
@@ -54,6 +55,7 @@
 import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -115,8 +117,9 @@
      * Does NOT add split options in the following scenarios:
      * * The taskView to add split options is already showing split screen tasks
      * * There aren't at least 2 tasks in overview to show split options for
+     * * Device is in "Lock task mode"
      * * The taskView to show split options for is the focused task AND we haven't started
-     *   scrolling in overview (if we haven't scrolled, there's a split overview action so
+     *   scrolling in overview (if we haven't scrolled, there's a split overview action button so
      *   we don't need this menu option)
      */
     private static void addSplitOptions(List<SystemShortcut> outShortcuts,
@@ -130,7 +133,11 @@
         boolean isFocusedTask = deviceProfile.overviewShowAsGrid && taskView.isFocusedTask();
         boolean isTaskInExpectedScrollPosition =
                 recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
-        if (taskViewHasMultipleTasks || notEnoughTasksToSplit ||
+        ActivityManager activityManager =
+                (ActivityManager) taskView.getContext().getSystemService(Context.ACTIVITY_SERVICE);
+        boolean isLockTaskMode = activityManager.isInLockTaskMode();
+
+        if (taskViewHasMultipleTasks || notEnoughTasksToSplit || isLockTaskMode ||
                 (isFocusedTask && isTaskInExpectedScrollPosition)) {
             return;
         }
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index cbdbdb5..ecb30e5 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -190,7 +190,7 @@
 
             ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
             if (options != null) {
-                options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+                options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
             }
             if (options != null
                     && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
@@ -242,6 +242,7 @@
         }
     }
 
+    /** @Deprecated */
     TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(R.drawable.ic_split_screen,
             R.string.recent_task_option_split_screen, LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP) {
 
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 14f3ec4..5d9a537 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -84,6 +84,7 @@
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
@@ -606,13 +607,13 @@
         }
 
         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        SurfaceControl[] auxiliarySurfaces = new SurfaceControl[nonApps.length];
+        List<SurfaceControl> auxiliarySurfaces = new ArrayList<>(nonApps.length);
         boolean hasSurfaceToAnimate = false;
         for (int i = 0; i < nonApps.length; ++i) {
             final RemoteAnimationTargetCompat targ = nonApps[i];
             final SurfaceControl leash = targ.leash.getSurfaceControl();
             if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null) {
-                auxiliarySurfaces[i] = leash;
+                auxiliarySurfaces.add(leash);
                 hasSurfaceToAnimate = true;
             }
         }
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d310893..5253e8c 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -64,7 +64,6 @@
     private @StagePosition int mStagePosition;
     private Task mInitialTask;
     private Task mSecondTask;
-    private Rect mInitialBounds;
     private boolean mRecentsAnimationRunning;
     /** If not null, this is the TaskView we want to launch from */
     @Nullable
@@ -86,7 +85,6 @@
             Rect initialBounds) {
         mInitialTask = task;
         mStagePosition = stagePosition;
-        mInitialBounds = initialBounds;
     }
 
     /**
@@ -229,7 +227,6 @@
         mInitialTask = null;
         mSecondTask = null;
         mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
-        mInitialBounds = null;
         mRecentsAnimationRunning = false;
         mLaunchingTaskView = null;
     }
@@ -241,8 +238,4 @@
     public boolean isSplitSelectActive() {
         return mInitialTask != null && mSecondTask == null;
     }
-
-    public Rect getInitialBounds() {
-        return mInitialBounds;
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 10efa3e..f0e57c9 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -3994,7 +3994,7 @@
     }
 
     /** TODO(b/181707736) More gracefully handle exiting split selection state */
-    private void resetFromSplitSelectionState() {
+    protected void resetFromSplitSelectionState() {
         if (mSplitHiddenTaskViewIndex == -1) {
             return;
         }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 1d8a459..3e0eb91 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -470,7 +470,9 @@
         stubInfo.user = componentKey.user;
         stubInfo.intent = new Intent().setComponent(componentKey.componentName);
         stubInfo.title = task.title;
-        stubInfo.screenId = getRecentsView().indexOfChild(this);
+        if (getRecentsView() != null) {
+            stubInfo.screenId = getRecentsView().indexOfChild(this);
+        }
         return stubInfo;
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 8d489e3..318f04e 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -220,7 +220,7 @@
         if (!condition) {
             final AssertionError assertionError = new AssertionError(message);
             if (description != null) {
-                FailureWatcher.onError(launcher.getDevice(), description, assertionError);
+                FailureWatcher.onError(launcher, description, assertionError);
             }
             throw assertionError;
         }
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 08570eb..6db0af3 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -133,6 +133,8 @@
 
         <attr name="numRows" format="integer" />
         <attr name="numColumns" format="integer" />
+        <!--  numSearchContainerColumns defaults to numColumns, if not specified -->
+        <attr name="numSearchContainerColumns" format="integer" />
         <!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
         <attr name="numFolderRows" format="integer" />
         <attr name="numFolderColumns" format="integer" />
@@ -182,76 +184,95 @@
         <attr name="minHeightDps" format="float" />
 
         <!-- These min cell values are only used if GridDisplayOption#isScalable is true -->
-        <attr name="minCellHeightDps" format="float" />
-        <attr name="minCellWidthDps" format="float" />
-        <!-- twoPanelPortraitMinCellWidthDps defaults to minCellHeightDps, if not specified -->
-        <attr name="twoPanelPortraitMinCellHeightDps" format="float" />
-        <!-- twoPanelPortraitMinCellHeightDps defaults to minCellWidthDps, if not specified -->
-        <attr name="twoPanelPortraitMinCellWidthDps" format="float" />
-        <!-- twoPanelLandscapeMinCellHeightDps defaults to minCellHeightDps, if not specified -->
-        <attr name="twoPanelLandscapeMinCellHeightDps" format="float" />
-        <!-- twoPanelLandscapeMinCellWidthDps defaults to minCellWidthDps, if not specified -->
-        <attr name="twoPanelLandscapeMinCellWidthDps" format="float" />
+        <attr name="minCellHeight" format="float" />
+        <attr name="minCellWidth" format="float" />
+        <!-- defaults to minCellHeight, if not specified -->
+        <attr name="minCellHeightTwoPanelPortrait" format="float" />
+        <!-- defaults to minCellWidth, if not specified -->
+        <attr name="minCellWidthTwoPanelPortrait" format="float" />
+        <!-- defaults to minCellHeight, if not specified -->
+        <attr name="minCellHeightTwoPanelLandscape" format="float" />
+        <!-- defaults to minCellWidth, if not specified -->
+        <attr name="minCellWidthTwoPanelLandscape" format="float" />
 
         <!-- These border spaces are only used if GridDisplayOption#isScalable is true -->
         <!-- space to be used horizontally and vertically -->
-        <attr name="borderSpaceDps" format="float" />
-        <!-- space to the right of the cell, defaults to borderSpaceDps if not specified -->
-        <attr name="borderSpaceHorizontalDps" format="float" />
-        <!-- space below the cell, defaults to borderSpaceDps if not specified -->
-        <attr name="borderSpaceVerticalDps" format="float" />
+        <attr name="borderSpace" format="float" />
+        <!-- space to the right of the cell, defaults to borderSpace if not specified -->
+        <attr name="borderSpaceHorizontal" format="float" />
+        <!-- space below the cell, defaults to borderSpace if not specified -->
+        <attr name="borderSpaceVertical" format="float" />
         <!-- space to be used horizontally and vertically in two panels,
-        defaults to borderSpaceDps if not specified -->
-        <attr name="twoPanelPortraitBorderSpaceDps" format="float" />
+        defaults to borderSpace if not specified -->
+        <attr name="borderSpaceTwoPanelPortrait" format="float" />
         <!-- space to the right of the cell in two panels, defaults to
-        twoPanelPortraitBorderSpaceDps if not specified -->
-        <attr name="twoPanelPortraitBorderSpaceHorizontalDps" format="float" />
-        <!-- space below the cell in two panels, defaults to twoPanelPortraitBorderSpaceDps
+        borderSpaceTwoPanelPortrait if not specified -->
+        <attr name="borderSpaceTwoPanelPortraitHorizontal" format="float" />
+        <!-- space below the cell in two panels, defaults to borderSpaceTwoPanelPortrait
         if not specified -->
-        <attr name="twoPanelPortraitBorderSpaceVerticalDps" format="float" />
+        <attr name="borderSpaceTwoPanelPortraitVertical" format="float" />
         <!-- space to be used horizontally and vertically in two panels,
-        defaults to borderSpaceDps if not specified -->
-        <attr name="twoPanelLandscapeBorderSpaceDps" format="float" />
+        defaults to borderSpace if not specified -->
+        <attr name="borderSpaceTwoPanelLandscape" format="float" />
         <!-- space to the right of the cell in two panels, defaults to
-        twoPanelLandscapeBorderSpaceDps if not specified -->
-        <attr name="twoPanelLandscapeBorderSpaceHorizontalDps" format="float" />
-        <!-- space below the cell in two panels, defaults to twoPanelLandscapeBorderSpaceDps
+        borderSpaceTwoPanelLandscape if not specified -->
+        <attr name="borderSpaceTwoPanelLandscapeHorizontal" format="float" />
+        <!-- space below the cell in two panels, defaults to borderSpaceTwoPanelLandscape
         if not specified -->
-        <attr name="twoPanelLandscapeBorderSpaceVerticalDps" format="float" />
+        <attr name="borderSpaceTwoPanelLandscapeVertical" format="float" />
 
-        <!-- allAppsCellSpacingDps defaults to borderSpaceDps, if not specified -->
-        <attr name="allAppsCellSpacingDps" format="float" />
+        <!-- defaults to borderSpace, if not specified -->
+        <attr name="allAppsBorderSpace" format="float" />
+        <!-- defaults to allAppsBorderSpace, if not specified -->
+        <attr name="allAppsBorderSpaceTwoPanelPortrait" format="float" />
+        <!-- defaults to allAppsBorderSpace, if not specified -->
+        <attr name="allAppsBorderSpaceTwoPanelLandscape" format="float" />
         <!-- The following values are only enabled if grid is supported. -->
-        <!-- allAppsIconSize defaults to iconSize, if not specified -->
+        <!-- defaults to iconImageSize, if not specified -->
         <attr name="allAppsIconSize" format="float" />
-        <!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
+        <!-- defaults to allAppsIconSize, if not specified -->
+        <attr name="allAppsIconSizeTwoPanelPortrait" format="float" />
+        <!-- defaults to allAppsIconSize, if not specified -->
+        <attr name="allAppsIconSizeTwoPanelLandscape" format="float" />
+        <!-- defaults to iconTextSize, if not specified -->
         <attr name="allAppsIconTextSize" format="float" />
+        <!-- defaults to allAppsIconTextSize, if not specified -->
+        <attr name="allAppsIconTextSizeTwoPanelPortrait" format="float" />
+        <!-- defaults to allAppsIconTextSize, if not specified -->
+        <attr name="allAppsIconTextSizeTwoPanelLandscape" format="float" />
+
+        <!-- defaults to borderSpaceDps, if not specified -->
+        <attr name="hotseatBorderSpace" format="float" />
+        <!-- defaults to hotseatBorderSpace, if not specified -->
+        <attr name="hotseatBorderSpaceTwoPanelLandscape" format="float" />
+        <!-- defaults to hotseatBorderSpace, if not specified -->
+        <attr name="hotseatBorderSpaceTwoPanelPortrait" format="float" />
 
         <attr name="iconImageSize" format="float" />
-        <!-- landscapeIconSize defaults to iconImageSize, if not specified -->
-        <attr name="landscapeIconSize" format="float" />
-        <!-- twoPanelPortraitIconSize defaults to iconImageSize, if not specified -->
-        <attr name="twoPanelPortraitIconSize" format="float" />
-        <!-- twoPanelLandscapeIconSize defaults to iconImageSize, if not specified -->
-        <attr name="twoPanelLandscapeIconSize" format="float" />
+        <!-- defaults to iconImageSize, if not specified -->
+        <attr name="iconSizeLandscape" format="float" />
+        <!-- defaults to iconSize, if not specified -->
+        <attr name="iconSizeTwoPanelPortrait" format="float" />
+        <!-- defaults to iconSize, if not specified -->
+        <attr name="iconSizeTwoPanelLandscape" format="float" />
 
         <attr name="iconTextSize" format="float" />
-        <!-- landscapeIconTextSize defaults to iconTextSize, if not specified -->
-        <attr name="landscapeIconTextSize" format="float" />
-        <!-- twoPanelPortraitIconTextSize defaults to iconTextSize, if not specified -->
-        <attr name="twoPanelPortraitIconTextSize" format="float" />
-        <!-- twoPanelLandscapeIconTextSize defaults to iconTextSize, if not specified -->
-        <attr name="twoPanelLandscapeIconTextSize" format="float" />
+        <!-- defaults to iconTextSize, if not specified -->
+        <attr name="iconTextSizeLandscape" format="float" />
+        <!-- defaults to iconTextSize, if not specified -->
+        <attr name="iconTextSizeTwoPanelPortrait" format="float" />
+        <!-- defaults to iconTextSize, if not specified -->
+        <attr name="iconTextSizeTwoPanelLandscape" format="float" />
 
         <!-- If set, this display option is used to determine the default grid -->
         <attr name="canBeDefault" format="boolean" />
 
         <!-- Margin on left and right of the workspace when GridDisplayOption#isScalable is true -->
         <attr name="horizontalMargin" format="float"/>
-        <!-- twoPanelLandscapeHorizontalMargin defaults to horizontalMargin if not specified -->
-        <attr name="twoPanelLandscapeHorizontalMargin" format="float"/>
-        <!-- twoPanelPortraitHorizontalMargin defaults to horizontalMargin if not specified -->
-        <attr name="twoPanelPortraitHorizontalMargin" format="float"/>
+        <!-- defaults to horizontalMargin if not specified -->
+        <attr name="horizontalMarginTwoPanelLandscape" format="float"/>
+        <!-- defaults to horizontalMargin if not specified -->
+        <attr name="horizontalMarginTwoPanelPortrait" format="float"/>
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 08698e7..dd201e5 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -140,6 +140,7 @@
         launcher:name="6_by_5"
         launcher:numRows="5"
         launcher:numColumns="6"
+        launcher:numSearchContainerColumns="3"
         launcher:numFolderRows="3"
         launcher:numFolderColumns="3"
         launcher:numHotseatIcons="6"
@@ -152,14 +153,14 @@
             launcher:name="Tablet"
             launcher:minWidthDps="900"
             launcher:minHeightDps="820"
-            launcher:minCellHeightDps="104"
-            launcher:minCellWidthDps="80"
+            launcher:minCellHeight="104"
+            launcher:minCellWidth="80"
             launcher:iconImageSize="60"
             launcher:iconTextSize="14"
-            launcher:borderSpaceDps="16"
+            launcher:borderSpace="16"
             launcher:allAppsIconSize="60"
             launcher:allAppsIconTextSize="14"
-            launcher:allAppsCellSpacingDps="16"
+            launcher:allAppsBorderSpace="16"
             launcher:canBeDefault="true" />
 
     </grid-option>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 624afe1..ab8e5e7 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -161,12 +161,13 @@
     public final int hotseatBarSidePaddingStartPx;
     public final int hotseatBarSidePaddingEndPx;
     public final int hotseatQsbHeight;
+    public final int hotseatBorderSpace;
 
     public final float qsbBottomMarginOriginalPx;
     public int qsbBottomMarginPx;
 
     // All apps
-    public Point allAppsCellSpacePx;
+    public Point allAppsBorderSpacePx;
     public int allAppsOpenVerticalTranslate;
     public int allAppsCellHeightPx;
     public int allAppsCellWidthPx;
@@ -290,9 +291,9 @@
         folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_content_padding_top);
 
         cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv);
-        allAppsCellSpacePx = new Point(
-                pxFromDp(inv.borderSpaces[InvariantDeviceProfile.INDEX_ALL_APPS].x, mMetrics, 1f),
-                pxFromDp(inv.borderSpaces[InvariantDeviceProfile.INDEX_ALL_APPS].y, mMetrics, 1f));
+        allAppsBorderSpacePx = new Point(
+                pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].x, mMetrics),
+                pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].y, mMetrics));
         cellLayoutBorderSpaceOriginalPx = new Point(cellLayoutBorderSpacePx);
         folderCellLayoutBorderSpaceOriginalPx = pxFromDp(inv.folderBorderSpace, mMetrics, 1f);
         folderCellLayoutBorderSpacePx = new Point(folderCellLayoutBorderSpaceOriginalPx,
@@ -349,8 +350,9 @@
         hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
         hotseatExtraVerticalSize =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
+        hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics);
         updateHotseatIconSize(
-                pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics, 1f));
+                pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics));
 
         qsbBottomMarginOriginalPx = isScalableGrid
                 ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
@@ -596,7 +598,7 @@
     private void updateAllAppsWidth() {
         if (isTwoPanels) {
             int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns)
-                    + (allAppsCellSpacePx.x * (numShownAllAppsColumns + 1));
+                    + (allAppsBorderSpacePx.x * (numShownAllAppsColumns + 1));
             allAppsLeftRightPadding = Math.max(1, (availableWidthPx - usedWidth) / 2);
         } else {
             allAppsLeftRightPadding =
@@ -695,9 +697,9 @@
         // All apps
         if (numShownAllAppsColumns != inv.numColumns) {
             allAppsIconSizePx =
-                    pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_ALL_APPS], mMetrics);
+                    pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics);
             allAppsIconTextSizePx =
-                    pxFromSp(inv.iconTextSize[InvariantDeviceProfile.INDEX_ALL_APPS], mMetrics);
+                    pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics);
             allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
             autoResizeAllAppsCells();
         } else {
@@ -706,7 +708,7 @@
             allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
             allAppsCellHeightPx = getCellSize().y;
         }
-        allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
+        allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx);
         updateAllAppsWidth();
 
         if (isVerticalLayout) {
@@ -895,14 +897,10 @@
             int hotseatTopDiff = hotseatHeight - taskbarOffset;
 
             int endOffset = ApiWrapper.getHotseatEndOffset(context);
-            int requiredWidth = iconSizePx * numShownHotseatIcons;
+            int requiredWidth = iconSizePx * numShownHotseatIcons
+                    + hotseatBorderSpace * (numShownHotseatIcons - 1);
 
-            Resources res = context.getResources();
-            float taskbarIconSize = res.getDimension(R.dimen.taskbar_icon_size);
-            float taskbarIconSpacing = 2 * res.getDimension(R.dimen.taskbar_icon_spacing);
-            int maxSize = (int) (requiredWidth
-                    * (taskbarIconSize + taskbarIconSpacing) / taskbarIconSize);
-            int hotseatSize = Math.min(maxSize, availableWidthPx - endOffset);
+            int hotseatSize = Math.min(requiredWidth, availableWidthPx - endOffset);
             int sideSpacing = (availableWidthPx - hotseatSize) / 2;
             mHotseatPadding.set(sideSpacing, hotseatTopDiff, sideSpacing, taskbarOffset);
 
@@ -1063,8 +1061,10 @@
 
         writer.println(prefix + "\tisScalableGrid:" + isScalableGrid);
 
-        writer.println(prefix + "\tinv.numColumns: " + inv.numColumns);
         writer.println(prefix + "\tinv.numRows: " + inv.numRows);
+        writer.println(prefix + "\tinv.numColumns: " + inv.numColumns);
+        writer.println(prefix + "\tinv.numSearchContainerColumns: "
+                + inv.numSearchContainerColumns);
 
         writer.println(prefix + "\tminCellSize: " + inv.minCellSize[mTypeIndex] + "dp");
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index ff7a90c..94325d0 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -94,18 +94,18 @@
 
     // Used for arrays to specify different sizes (e.g. border spaces, width/height) in different
     // constraints
-    static final int COUNT_SIZES = 5;
+    static final int COUNT_SIZES = 4;
     static final int INDEX_DEFAULT = 0;
     static final int INDEX_LANDSCAPE = 1;
     static final int INDEX_TWO_PANEL_PORTRAIT = 2;
     static final int INDEX_TWO_PANEL_LANDSCAPE = 3;
-    static final int INDEX_ALL_APPS = 4;
 
     /**
      * Number of icons per row and column in the workspace.
      */
     public int numRows;
     public int numColumns;
+    public int numSearchContainerColumns;
 
     /**
      * Number of icons per row and column in the folder.
@@ -122,9 +122,14 @@
 
     public PointF[] borderSpaces;
     public float folderBorderSpace;
+    public float[] hotseatBorderSpaces;
 
     public float[] horizontalMargin;
 
+    public float[] allAppsIconSize;
+    public float[] allAppsIconTextSize;
+    public PointF[] allAppsBorderSpaces;
+
     private SparseArray<TypedValue> mExtraAttrs;
 
     /**
@@ -300,6 +305,7 @@
         GridOption closestProfile = displayOption.grid;
         numRows = closestProfile.numRows;
         numColumns = closestProfile.numColumns;
+        numSearchContainerColumns = closestProfile.numSearchContainerColumns;
         dbFile = closestProfile.dbFile;
         defaultLayoutId = closestProfile.defaultLayoutId;
         demoModeLayoutId = closestProfile.demoModeLayoutId;
@@ -331,14 +337,18 @@
         numShownHotseatIcons = closestProfile.numHotseatIcons;
         numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
                 ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
+        hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
 
         numAllAppsColumns = closestProfile.numAllAppsColumns;
         numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY
                 ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
 
+        allAppsBorderSpaces = displayOption.allAppsBorderSpaces;
+        allAppsIconSize = displayOption.allAppsIconSizes;
+        allAppsIconTextSize = displayOption.allAppsIconTextSizes;
         if (!Utilities.isGridOptionsEnabled(context)) {
-            iconSize[INDEX_ALL_APPS] = iconSize[INDEX_DEFAULT];
-            iconTextSize[INDEX_ALL_APPS] = iconTextSize[INDEX_DEFAULT];
+            allAppsIconSize = iconSize;
+            allAppsIconTextSize = iconTextSize;
         }
 
         if (devicePaddingId != 0) {
@@ -393,8 +403,8 @@
 
     private Object[] toModelState() {
         return new Object[]{
-                numColumns, numRows, numDatabaseHotseatIcons, iconBitmapSize, fillResIconDpi,
-                numDatabaseAllAppsColumns, dbFile};
+                numColumns, numRows, numSearchContainerColumns, numDatabaseHotseatIcons,
+                iconBitmapSize, fillResIconDpi, numDatabaseAllAppsColumns, dbFile};
     }
 
     private void onConfigChanged(Context context) {
@@ -671,6 +681,7 @@
         public final String name;
         public final int numRows;
         public final int numColumns;
+        public final int numSearchContainerColumns;
         public final boolean isEnabled;
 
         private final int numFolderRows;
@@ -697,6 +708,8 @@
             name = a.getString(R.styleable.GridDisplayOption_name);
             numRows = a.getInt(R.styleable.GridDisplayOption_numRows, 0);
             numColumns = a.getInt(R.styleable.GridDisplayOption_numColumns, 0);
+            numSearchContainerColumns = a.getInt(
+                    R.styleable.GridDisplayOption_numSearchContainerColumns, numColumns);
 
             dbFile = a.getString(R.styleable.GridDisplayOption_dbFile);
             defaultLayoutId = a.getResourceId(deviceType == TYPE_MULTI_DISPLAY && a.hasValue(
@@ -756,10 +769,15 @@
         private float folderBorderSpace;
         private final PointF[] borderSpaces = new PointF[COUNT_SIZES];
         private final float[] horizontalMargin = new float[COUNT_SIZES];
+        private final float[] hotseatBorderSpaces = new float[COUNT_SIZES];
 
         private final float[] iconSizes = new float[COUNT_SIZES];
         private final float[] textSizes = new float[COUNT_SIZES];
 
+        private final float[] allAppsIconSizes = new float[COUNT_SIZES];
+        private final float[] allAppsIconTextSizes = new float[COUNT_SIZES];
+        private final PointF[] allAppsBorderSpaces = new PointF[COUNT_SIZES];
+
         DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
             this.grid = grid;
 
@@ -773,97 +791,129 @@
             float x;
             float y;
 
-            x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
-            y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
+            x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidth, 0);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeight, 0);
             minCellSize[INDEX_DEFAULT] = new PointF(x, y);
             minCellSize[INDEX_LANDSCAPE] = new PointF(x, y);
-            minCellSize[INDEX_ALL_APPS] = new PointF(x, y);
 
-            x = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellWidthDps,
+            x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthTwoPanelPortrait,
                     minCellSize[INDEX_DEFAULT].x);
-            y = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellHeightDps,
+            y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightTwoPanelPortrait,
                     minCellSize[INDEX_DEFAULT].y);
             minCellSize[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
 
-            x = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellWidthDps,
+            x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthTwoPanelLandscape,
                     minCellSize[INDEX_DEFAULT].x);
-            y = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellHeightDps,
+            y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightTwoPanelLandscape,
                     minCellSize[INDEX_DEFAULT].y);
             minCellSize[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
 
-            float borderSpace = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceDps, 0);
-            float twoPanelPortraitBorderSpaceDps = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceDps, borderSpace);
-            float twoPanelLandscapeBorderSpaceDps = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceDps, borderSpace);
+            float borderSpace = a.getFloat(R.styleable.ProfileDisplayOption_borderSpace, 0);
+            float borderSpaceTwoPanelPortrait = a.getFloat(
+                    R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortrait, borderSpace);
+            float borderSpaceTwoPanelLandscape = a.getFloat(
+                    R.styleable.ProfileDisplayOption_borderSpaceTwoPanelLandscape, borderSpace);
 
-            x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceHorizontalDps, borderSpace);
-            y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceVerticalDps, borderSpace);
+            x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceHorizontal, borderSpace);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceVertical, borderSpace);
             borderSpaces[INDEX_DEFAULT] = new PointF(x, y);
             borderSpaces[INDEX_LANDSCAPE] = new PointF(x, y);
 
             x = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceHorizontalDps,
-                    twoPanelPortraitBorderSpaceDps);
+                    R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortraitHorizontal,
+                    borderSpaceTwoPanelPortrait);
             y = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceVerticalDps,
-                    twoPanelPortraitBorderSpaceDps);
+                    R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortraitVertical,
+                    borderSpaceTwoPanelPortrait);
             borderSpaces[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
 
             x = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceHorizontalDps,
-                    twoPanelLandscapeBorderSpaceDps);
+                    R.styleable.ProfileDisplayOption_borderSpaceTwoPanelLandscapeHorizontal,
+                    borderSpaceTwoPanelLandscape);
             y = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceVerticalDps,
-                    twoPanelLandscapeBorderSpaceDps);
+                    R.styleable.ProfileDisplayOption_borderSpaceTwoPanelLandscapeVertical,
+                    borderSpaceTwoPanelLandscape);
             borderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
 
-            x = y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellSpacingDps,
-                    borderSpace);
-            borderSpaces[INDEX_ALL_APPS] = new PointF(x, y);
             folderBorderSpace = borderSpace;
 
+            x = y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpace,
+                    borderSpace);
+            allAppsBorderSpaces[INDEX_DEFAULT] = new PointF(x, y);
+            allAppsBorderSpaces[INDEX_LANDSCAPE] = new PointF(x, y);
+            x = y = a.getFloat(
+                    R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelPortrait,
+                    allAppsBorderSpaces[INDEX_DEFAULT].x);
+            allAppsBorderSpaces[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
+            x = y = a.getFloat(
+                    R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelLandscape,
+                    allAppsBorderSpaces[INDEX_DEFAULT].x);
+            allAppsBorderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
+
             iconSizes[INDEX_DEFAULT] =
                     a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
             iconSizes[INDEX_LANDSCAPE] =
-                    a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
-                            iconSizes[INDEX_DEFAULT]);
-            iconSizes[INDEX_ALL_APPS] =
-                    a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconSizeLandscape,
                             iconSizes[INDEX_DEFAULT]);
             iconSizes[INDEX_TWO_PANEL_PORTRAIT] =
-                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitIconSize,
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconSizeTwoPanelPortrait,
                             iconSizes[INDEX_DEFAULT]);
             iconSizes[INDEX_TWO_PANEL_LANDSCAPE] =
-                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconSize,
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconSizeTwoPanelLandscape,
                             iconSizes[INDEX_DEFAULT]);
 
+            allAppsIconSizes[INDEX_DEFAULT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_allAppsIconSize, iconSizes[INDEX_DEFAULT]);
+            allAppsIconSizes[INDEX_LANDSCAPE] = allAppsIconSizes[INDEX_DEFAULT];
+            allAppsIconSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelPortrait,
+                    allAppsIconSizes[INDEX_DEFAULT]);
+            allAppsIconSizes[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelLandscape,
+                    allAppsIconSizes[INDEX_DEFAULT]);
+
             textSizes[INDEX_DEFAULT] =
                     a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
             textSizes[INDEX_LANDSCAPE] =
-                    a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconTextSize,
-                            textSizes[INDEX_DEFAULT]);
-            textSizes[INDEX_ALL_APPS] =
-                    a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize,
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeLandscape,
                             textSizes[INDEX_DEFAULT]);
             textSizes[INDEX_TWO_PANEL_PORTRAIT] =
-                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitIconTextSize,
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeTwoPanelPortrait,
                             textSizes[INDEX_DEFAULT]);
             textSizes[INDEX_TWO_PANEL_LANDSCAPE] =
-                    a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconTextSize,
+                    a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeTwoPanelLandscape,
                             textSizes[INDEX_DEFAULT]);
 
+            allAppsIconTextSizes[INDEX_DEFAULT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_allAppsIconTextSize, textSizes[INDEX_DEFAULT]);
+            allAppsIconTextSizes[INDEX_LANDSCAPE] = allAppsIconTextSizes[INDEX_DEFAULT];
+            allAppsIconTextSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_allAppsIconTextSizeTwoPanelPortrait,
+                    allAppsIconTextSizes[INDEX_DEFAULT]);
+            allAppsIconTextSizes[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_allAppsIconTextSizeTwoPanelLandscape,
+                    allAppsIconTextSizes[INDEX_DEFAULT]);
+
             horizontalMargin[INDEX_DEFAULT] = a.getFloat(
                     R.styleable.ProfileDisplayOption_horizontalMargin, 0);
             horizontalMargin[INDEX_LANDSCAPE] = horizontalMargin[INDEX_DEFAULT];
-            horizontalMargin[INDEX_ALL_APPS] = horizontalMargin[INDEX_DEFAULT];
             horizontalMargin[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelLandscapeHorizontalMargin,
+                    R.styleable.ProfileDisplayOption_horizontalMarginTwoPanelLandscape,
                     horizontalMargin[INDEX_DEFAULT]);
             horizontalMargin[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelPortraitHorizontalMargin,
+                    R.styleable.ProfileDisplayOption_horizontalMarginTwoPanelPortrait,
                     horizontalMargin[INDEX_DEFAULT]);
 
+            hotseatBorderSpaces[INDEX_DEFAULT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatBorderSpace, borderSpace);
+            hotseatBorderSpaces[INDEX_LANDSCAPE] = hotseatBorderSpaces[INDEX_DEFAULT];
+            hotseatBorderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelLandscape,
+                    hotseatBorderSpaces[INDEX_DEFAULT]);
+            hotseatBorderSpaces[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelPortrait,
+                    hotseatBorderSpaces[INDEX_DEFAULT]);
+
             a.recycle();
         }
 
@@ -881,6 +931,9 @@
                 textSizes[i] = 0;
                 borderSpaces[i] = new PointF();
                 minCellSize[i] = new PointF();
+                allAppsIconSizes[i] = 0;
+                allAppsIconTextSizes[i] = 0;
+                allAppsBorderSpaces[i] = new PointF();
             }
         }
 
@@ -893,6 +946,11 @@
                 minCellSize[i].x *= w;
                 minCellSize[i].y *= w;
                 horizontalMargin[i] *= w;
+                hotseatBorderSpaces[i] *= w;
+                allAppsIconSizes[i] *= w;
+                allAppsIconTextSizes[i] *= w;
+                allAppsBorderSpaces[i].x *= w;
+                allAppsBorderSpaces[i].y *= w;
             }
 
             folderBorderSpace *= w;
@@ -909,6 +967,11 @@
                 minCellSize[i].x += p.minCellSize[i].x;
                 minCellSize[i].y += p.minCellSize[i].y;
                 horizontalMargin[i] += p.horizontalMargin[i];
+                hotseatBorderSpaces[i] += p.hotseatBorderSpaces[i];
+                allAppsIconSizes[i] += p.allAppsIconSizes[i];
+                allAppsIconTextSizes[i] += p.allAppsIconTextSizes[i];
+                allAppsBorderSpaces[i].x += p.allAppsBorderSpaces[i].x;
+                allAppsBorderSpaces[i].y += p.allAppsBorderSpaces[i].y;
             }
 
             folderBorderSpace += p.folderBorderSpace;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 10023b4..9991042 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -98,7 +98,7 @@
                 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
         if (FeatureFlags.IS_STUDIO_BUILD) {
-            modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
+            modelChangeReceiver.register(mContext, Context.RECEIVER_EXPORTED, ACTION_FORCE_ROLOAD);
         }
         mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 281dfea..65006ff 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -585,8 +585,8 @@
 
         int cellVSpan = FeatureFlags.EXPANDED_SMARTSPACE.get()
                 ? EXPANDED_SMARTSPACE_HEIGHT : DEFAULT_SMARTSPACE_HEIGHT;
-        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(),
-                cellVSpan);
+        int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns;
+        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, cellHSpan, cellVSpan);
         lp.canReorder = false;
         if (!firstPage.addViewToCellLayout(mQsb, 0, R.id.search_container_workspace, lp, true)) {
             Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index abf002f..f4d64df 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -456,10 +456,10 @@
         }
         IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
                 mDp.numShownHotseatIcons);
-        FixedContainerItems hotseatpredictions =
+        FixedContainerItems hotseatPredictions =
                 dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
-        List<ItemInfo> predictions = hotseatpredictions == null
-                ? Collections.emptyList() : hotseatpredictions.items;
+        List<ItemInfo> predictions = hotseatPredictions == null
+                ? Collections.emptyList() : hotseatPredictions.items;
         int count = Math.min(ranks.size(), predictions.size());
         for (int i = 0; i < count; i++) {
             int rank = ranks.get(i);
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 8b7bebc..a7cfdde 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -372,6 +372,9 @@
         @UiEvent(doc = "Notification dismissed by swiping right.")
         LAUNCHER_NOTIFICATION_DISMISSED(652),
 
+        @UiEvent(doc = "Current grid size is changed to 6.")
+        LAUNCHER_GRID_SIZE_6(930),
+
         @UiEvent(doc = "Current grid size is changed to 5.")
         LAUNCHER_GRID_SIZE_5(662),
 
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index fa11d4e..08c3149 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_5;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_6;
 
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -84,6 +85,8 @@
     public LauncherEvent getWorkspaceSizeEvent() {
         if (!TextUtils.isEmpty(mGridSizeString)) {
             switch (mGridSizeString.charAt(0)) {
+                case '6':
+                    return LAUNCHER_GRID_SIZE_6;
                 case '5':
                     return LAUNCHER_GRID_SIZE_5;
                 case '4':
diff --git a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
index 465a0e8..4dfa5cc 100644
--- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -39,10 +39,17 @@
      * Helper method to register multiple actions
      */
     public void register(Context context, String... actions) {
+        register(context, 0, actions);
+    }
+
+    /**
+     * Helper method to register multiple actions with one or more {@code flags}.
+     */
+    public void register(Context context, int flags, String... actions) {
         IntentFilter filter = new IntentFilter();
         for (String action : actions) {
             filter.addAction(action);
         }
-        context.registerReceiver(this, filter);
+        context.registerReceiver(this, filter, flags);
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
index b048cd4..122a130 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -47,20 +48,12 @@
 @RunWith(AndroidJUnit4.class)
 public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest {
 
-    Workspace mWorkspace;
-
     @Before
     public void setUp() throws Exception {
         super.setUp();
         TaplTestsLauncher3.initialize(this);
-        mWorkspace = mLauncher.getWorkspace();
-    }
 
-    @Test
-    public void testDragIconToRightPanel() {
-        if (!mLauncher.isTwoPanels()) {
-            return;
-        }
+        assumeTrue(mLauncher.isTwoPanels());
 
         // Pre verifying the screens
         executeOnLauncher(launcher -> {
@@ -68,8 +61,15 @@
             assertItemsOnPage(launcher, 0, "Play Store", "Maps");
             assertPageEmpty(launcher, 1);
         });
+    }
 
-        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Chrome"), 1);
+    @Test
+    // TODO(b/197631877) Enable in portrait.
+    // @PortraitLandscape
+    public void testDragIconToRightPanel() {
+        Workspace workspace = mLauncher.getWorkspace();
+
+        workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 1);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1);
@@ -79,19 +79,67 @@
     }
 
     @Test
-    public void testDragIconToPage2() {
-        if (!mLauncher.isTwoPanels()) {
-            return;
-        }
+    // TODO(b/197631877) Enable in portrait.
+    // @PortraitLandscape
+    public void testSinglePageDragIconWhenMultiplePageScrollingIsPossible() {
+        Workspace workspace = mLauncher.getWorkspace();
 
-        // Pre verifying the screens
+        workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 2);
+
+        workspace.flingBackward();
+
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 3);
+
         executeOnLauncher(launcher -> {
-            assertPagesExist(launcher, 0, 1);
-            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertPagesExist(launcher, 0, 1, 2, 3);
+            assertItemsOnPage(launcher, 0, "Play Store");
             assertPageEmpty(launcher, 1);
+            assertItemsOnPage(launcher, 2, "Chrome");
+            assertItemsOnPage(launcher, 3, "Maps");
         });
 
-        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 3);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 2, 3, 4, 5);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertPageEmpty(launcher, 1);
+            assertItemsOnPage(launcher, 2, "Chrome");
+            assertPageEmpty(launcher, 3);
+            assertPageEmpty(launcher, 4);
+            assertItemsOnPage(launcher, 5, "Maps");
+        });
+
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), -1);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 2, 3);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertPageEmpty(launcher, 1);
+            assertItemsOnPage(launcher, 2, "Chrome");
+            assertItemsOnPage(launcher, 3, "Maps");
+        });
+
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), -1);
+
+        workspace.flingForward();
+
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Chrome"), -2);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1);
+            assertItemsOnPage(launcher, 0, "Chrome", "Play Store");
+            assertItemsOnPage(launcher, 1, "Maps");
+        });
+    }
+
+    @Test
+    // TODO(b/197631877) Enable in portrait.
+    // @PortraitLandscape
+    public void testDragIconToPage2() {
+        Workspace workspace = mLauncher.getWorkspace();
+
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 2);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1, 2, 3);
@@ -103,19 +151,12 @@
     }
 
     @Test
+    // TODO(b/197631877) Enable in portrait.
+    // @PortraitLandscape
     public void testDragIconToPage3() {
-        if (!mLauncher.isTwoPanels()) {
-            return;
-        }
+        Workspace workspace = mLauncher.getWorkspace();
 
-        // Pre verifying the screens
-        executeOnLauncher(launcher -> {
-            assertPagesExist(launcher, 0, 1);
-            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
-            assertPageEmpty(launcher, 1);
-        });
-
-        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Phone"), 3);
+        workspace.dragIcon(workspace.getHotseatAppIcon("Phone"), 3);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1, 2, 3);
@@ -126,22 +167,61 @@
         });
     }
 
-
     @Test
-    public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {
-        if (!mLauncher.isTwoPanels()) {
-            return;
-        }
+    // TODO(b/197631877) Enable in portrait.
+    // @PortraitLandscape
+    public void testMultiplePageDragIcon() {
+        Workspace workspace = mLauncher.getWorkspace();
 
-        // Pre verifying the screens
+        workspace.dragIcon(workspace.getHotseatAppIcon("Messages"), 2);
+
+        workspace.flingBackward();
+
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 5);
+
         executeOnLauncher(launcher -> {
-            assertPagesExist(launcher, 0, 1);
-            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertPagesExist(launcher, 0, 1, 2, 3, 4, 5);
+            assertItemsOnPage(launcher, 0, "Play Store");
             assertPageEmpty(launcher, 1);
+            assertItemsOnPage(launcher, 2, "Messages");
+            assertPageEmpty(launcher, 3);
+            assertPageEmpty(launcher, 4);
+            assertItemsOnPage(launcher, 5, "Maps");
         });
 
-        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 3);
-        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Chrome"), 0);
+        workspace.flingBackward();
+
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Messages"), 4);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 4, 5, 6, 7);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertPageEmpty(launcher, 1);
+            assertPageEmpty(launcher, 4);
+            assertItemsOnPage(launcher, 5, "Maps");
+            assertItemsOnPage(launcher, 6, "Messages");
+            assertPageEmpty(launcher, 7);
+        });
+
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Messages"), -3);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 4, 5);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertItemsOnPage(launcher, 1, "Messages");
+            assertPageEmpty(launcher, 4);
+            assertItemsOnPage(launcher, 5, "Maps");
+        });
+    }
+
+    @Test
+    // TODO(b/197631877) Enable in portrait.
+    // @PortraitLandscape
+    public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {
+        Workspace workspace = mLauncher.getWorkspace();
+
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 3);
+        workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 0);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1, 2, 3);
@@ -151,7 +231,7 @@
             assertItemsOnPage(launcher, 3, "Maps");
         });
 
-        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), -1);
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), -1);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1, 2, 3);
@@ -163,8 +243,8 @@
 
         // Move Chrome to the right panel as well, to make sure pages are not deleted whichever
         // page is the empty one
-        mWorkspace.flingForward();
-        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Chrome"), 1);
+        workspace.flingForward();
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Chrome"), 1);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1, 2, 3);
@@ -175,22 +255,14 @@
         });
     }
 
-
     @Test
+    // TODO(b/197631877) Enable in portrait.
+    // @PortraitLandscape
     public void testEmptyPagesGetRemovedIfBothPagesAreEmpty() {
-        if (!mLauncher.isTwoPanels()) {
-            return;
-        }
+        Workspace workspace = mLauncher.getWorkspace();
 
-        // Pre verifying the screens
-        executeOnLauncher(launcher -> {
-            assertPagesExist(launcher, 0, 1);
-            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
-            assertPageEmpty(launcher, 1);
-        });
-
-        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Play Store"), 2);
-        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Camera"), 1);
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Play Store"), 2);
+        workspace.dragIcon(workspace.getHotseatAppIcon("Camera"), 1);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1, 2, 3);
@@ -200,9 +272,9 @@
             assertItemsOnPage(launcher, 3, "Camera");
         });
 
-        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Camera"), -1);
-        mWorkspace.flingForward();
-        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Play Store"), -2);
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Camera"), -1);
+        workspace.flingForward();
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Play Store"), -2);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1);
@@ -212,20 +284,13 @@
     }
 
     @Test
+    // TODO(b/197631877) Enable in portrait.
+    // @PortraitLandscape
     public void testMiddleEmptyPagesGetRemoved() {
-        if (!mLauncher.isTwoPanels()) {
-            return;
-        }
+        Workspace workspace = mLauncher.getWorkspace();
 
-        // Pre verifying the screens
-        executeOnLauncher(launcher -> {
-            assertPagesExist(launcher, 0, 1);
-            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
-            assertPageEmpty(launcher, 1);
-        });
-
-        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
-        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Messages"), 3);
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 2);
+        workspace.dragIcon(workspace.getHotseatAppIcon("Messages"), 3);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1, 2, 3, 4, 5);
@@ -237,8 +302,8 @@
             assertItemsOnPage(launcher, 5, "Messages");
         });
 
-        mWorkspace.flingBackward();
-        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
+        workspace.flingBackward();
+        workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 2);
 
         executeOnLauncher(launcher -> {
             assertPagesExist(launcher, 0, 1, 4, 5);
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 65aaa24..657f213 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -84,7 +84,7 @@
 
     @Override
     protected void failed(Throwable e, Description description) {
-        onError(mDevice, description, e);
+        onError(mLauncher, description, e);
     }
 
     static File diagFile(Description description, String prefix, String ext) {
@@ -93,7 +93,9 @@
                         + description.getMethodName() + "." + ext);
     }
 
-    public static void onError(UiDevice device, Description description, Throwable e) {
+    public static void onError(LauncherInstrumentation launcher, Description description,
+            Throwable e) {
+        final UiDevice device = launcher.getDevice();
         Log.d("b/196820244", "onError 1");
         if (device == null) return;
         Log.d("b/196820244", "onError 2");
@@ -128,6 +130,11 @@
         }
 
         dumpCommand("logcat -d -s TestRunner", diagFile(description, "FilteredLogcat", "txt"));
+
+        // Dump bugreport
+        if (launcher.getSystemAnomalyMessage(false, false) != null) {
+            dumpCommand("bugreportz -s", diagFile(description, "Bugreport", "zip"));
+        }
     }
 
     private static void dumpStringCommand(String cmd, OutputStream out) throws IOException {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index b42f5c1..bc2afaf 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -426,7 +426,7 @@
         }
     }
 
-    private String getSystemAnomalyMessage(
+    public String getSystemAnomalyMessage(
             boolean ignoreNavmodeChangeStates, boolean ignoreOnlySystemUiViews) {
         try {
             {
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index c71c11f..16987e9 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED;
+
 import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
@@ -170,15 +172,22 @@
     }
 
     /**
-     * Drags an icon to the (currentPage + pageDelta) page if the page already exists.
-     * If the target page doesn't exist, the icon will be put onto an existing page that is the
-     * closest to the target page.
+     * Drags an icon to the (currentPage + pageDelta) page.
+     * If the target page doesn't exist yet, a new page will be created.
+     * In case the target page can't be created (e.g. existing pages are 0, 1, current: 0,
+     * pageDelta: 3, the latest page that can be created is 2) the icon will be dragged onto the
+     * page that can be created and is closest to the target page.
      *
      * @param appIcon   - icon to drag.
      * @param pageDelta - how many pages should the icon be dragged from the current page.
-     *                    It can be a negative value.
+     *                    It can be a negative value. currentPage + pageDelta should be greater
+     *                    than or equal to 0.
      */
     public void dragIcon(AppIcon appIcon, int pageDelta) {
+        if (mHotseat.getVisibleBounds().height() > mHotseat.getVisibleBounds().width()) {
+            throw new UnsupportedOperationException(
+                    "dragIcon does NOT support dragging when the hotseat is on the side.");
+        }
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             final UiObject2 workspace = verifyActiveContainer();
             try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
@@ -385,9 +394,11 @@
             while (targetDest.x > displayX || targetDest.x < 0) {
                 int edgeX = targetDest.x > 0 ? displayX : 0;
                 Point screenEdge = new Point(edgeX, targetDest.y);
-                launcher.movePointer(dragStart, screenEdge, DEFAULT_DRAG_STEPS, isDecelerating,
-                        downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
-                launcher.waitForIdle(); // Wait for the page change to happen
+                Point finalDragStart = dragStart;
+                executeAndWaitForPageScroll(launcher,
+                        () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS,
+                                isDecelerating, downTime, true,
+                                LauncherInstrumentation.GestureScope.INSIDE));
                 targetDest.x += displayX * (targetDest.x > 0 ? -1 : 1);
                 dragStart = screenEdge;
             }
@@ -400,6 +411,13 @@
         }
     }
 
+    private static void executeAndWaitForPageScroll(LauncherInstrumentation launcher,
+            Runnable command) {
+        launcher.executeAndWaitForEvent(command,
+                event -> event.getEventType() == TYPE_VIEW_SCROLLED,
+                () -> "Page scroll didn't happen", "Scrolling page");
+    }
+
     /**
      * Flings to get to screens on the right. Waits for scrolling and a possible overscroll
      * recoil to complete.