Merge "Create taskbar for kids mode."
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/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/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 5a46e12..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" />
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index d2d8fc3..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"
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 2a29f3b..ab8e5e7 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -1061,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 2d0d640..94325d0 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -105,6 +105,7 @@
      */
     public int numRows;
     public int numColumns;
+    public int numSearchContainerColumns;
 
     /**
      * Number of icons per row and column in the folder.
@@ -304,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;
@@ -401,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) {
@@ -679,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;
@@ -705,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(
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/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/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 37a205c..46a7c71 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 3f0d7fd..e457354 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;
@@ -165,15 +167,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(
@@ -338,9 +347,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;
             }
@@ -353,6 +364,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.