When placing a widget, go to a page with enough space.

The search for this page starts at the current one and
continues to the right (on LTR) until a page is found that
can accomodate the widget, taking possible resizing and
reordering into account.

Bug: 11338870
Change-Id: I2e9a310eb8f74024dca9150f55a525e1309c2f07
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 653ee7e..700bf9e 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2966,6 +2966,26 @@
         return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
     }
 
+    /**
+     * Returns whether an item can be placed in this CellLayout (after rearranging and/or resizing
+     * if necessary).
+     */
+    public boolean hasReorderSolution(ItemInfo itemInfo) {
+        int[] cellPoint = new int[2];
+        // Check for a solution starting at every cell.
+        for (int cellX = 0; cellX < getCountX(); cellX++) {
+            for (int cellY = 0; cellY < getCountY(); cellY++) {
+                cellToPoint(cellX, cellY, cellPoint);
+                if (findReorderSolution(cellPoint[0], cellPoint[1], itemInfo.minSpanX,
+                        itemInfo.minSpanY, itemInfo.spanX, itemInfo.spanY, mDirectionVector, null,
+                        true, new ItemConfiguration()).isSolution) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
         int x2 = x + spanX - 1;
         int y2 = y + spanY - 1;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 5f54e1d..e4d6448 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -441,7 +441,7 @@
                     Math.min(newPage, mTempVisiblePagesRange[1]));
         }
         // Ensure that it is clamped by the actual set of children in all cases
-        validatedPage = Math.max(0, Math.min(validatedPage, getPageCount() - 1));
+        validatedPage = Utilities.boundInRange(validatedPage, 0, getPageCount() - 1);
         return validatedPage;
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 5c8c402..58209c3 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1927,6 +1927,17 @@
     }
 
     public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
+        // Find a page that has enough space to place this widget (after rearranging/resizing).
+        // Start at the current page and search right (on LTR) until finding a page with enough
+        // space. Since an empty screen is the furthest right, a page must be found.
+        for (int pageIndex = getCurrentPage(); pageIndex <= getPageCount(); pageIndex++) {
+            CellLayout page = (CellLayout) getPageAt(pageIndex);
+            if (page.hasReorderSolution(info)) {
+                setCurrentPage(pageIndex);
+                break;
+            }
+        }
+
         int[] size = estimateItemSize(info, false);
 
         // The outline is used to visualize where the item will land if dropped
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index e6059d5..81a8465 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -253,9 +253,11 @@
 
         // Start the drag
         mLauncher.lockScreenOrientation();
-        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
         mDragController.startDrag(image, preview, this, createItemInfo,
                 bounds, DragController.DRAG_ACTION_COPY, scale);
+        // This call expects the extra empty screen to already be created, which is why we call it
+        // after mDragController.startDrag().
+        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
 
         preview.recycle();
         return true;