Shrink-wrapping model to spec (issue 10397399)

Change-Id: I87e32e8bd93f3b0468bc840200faed8270922a03
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 29c4e3e..fbc6819 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -3560,8 +3560,10 @@
     public void bindScreens(ArrayList<Long> orderedScreenIds) {
         bindAddScreens(orderedScreenIds);
 
-        // Create the new empty page
-        mWorkspace.addExtraEmptyScreen();
+        // If there are no screens, we need to have an empty screen
+        if (orderedScreenIds.size() == 0) {
+            mWorkspace.addExtraEmptyScreen();
+        }
 
         // Create the custom content page (this call updates mDefaultScreen which calls
         // setCurrentPage() so ensure that all pages are added before calling this)
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index bdb0d58..dcb71d2 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -2718,4 +2718,4 @@
     public boolean onHoverEvent(android.view.MotionEvent event) {
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 3f63d74..3a08c44 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -51,6 +51,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
@@ -111,6 +112,8 @@
     private int mOriginalDefaultPage;
     private int mDefaultPage;
 
+    private ShortcutAndWidgetContainer mDragSourceInternal;
+
     // The screen id used for the empty screen always present to the right.
     private final static long EXTRA_EMPTY_SCREEN_ID = -201;
     private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
@@ -353,7 +356,7 @@
         return r;
     }
 
-    public void onDragStart(DragSource source, Object info, int dragAction) {
+    public void onDragStart(final DragSource source, Object info, int dragAction) {
         mIsDragOccuring = true;
         updateChildrenLayersEnabled(false);
         mLauncher.lockScreenOrientation();
@@ -361,6 +364,14 @@
         // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
         InstallShortcutReceiver.enableInstallQueue();
         UninstallShortcutReceiver.enableUninstallQueue();
+        post(new Runnable() {
+            @Override
+            public void run() {
+                if (mIsDragOccuring) {
+                    addExtraEmptyScreenOnDrag();
+                }
+            }
+        });
     }
 
     public void onDragEnd() {
@@ -371,6 +382,9 @@
         // Re-enable any Un/InstallShortcutReceiver and now process any queued items
         InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
         UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
+
+        removeExtraEmptyScreen();
+        mDragSourceInternal = null;
     }
 
     /**
@@ -489,6 +503,10 @@
         String log = "10249126 - insertNewWorkspaceScreen(" + screenId + ", " + insertIndex + ")";
         Launcher.addDumpLog(TAG, log, true);
 
+        if (mWorkspaceScreens.containsKey(screenId)) {
+            throw new RuntimeException("Screen id " + screenId + " already exists!");
+        }
+
         CellLayout newScreen = (CellLayout)
                 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
 
@@ -539,6 +557,49 @@
         mCustomContentCallbacks = callbacks;
     }
 
+    public void addExtraEmptyScreenOnDrag() {
+        boolean lastChildOnScreen = false;
+        boolean childOnFinalScreen = false;
+
+        if (mDragSourceInternal != null) {
+            if (mDragSourceInternal.getChildCount() == 1) {
+                lastChildOnScreen = true;
+            }
+            CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
+            if (indexOfChild(cl) == getChildCount() - 1) {
+                childOnFinalScreen = true;
+            }
+        }
+
+        // If this is the last item on the final screen
+        if (lastChildOnScreen && childOnFinalScreen) {
+            return;
+        }
+        if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
+            insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
+        }
+    }
+
+    public boolean addExtraEmptyScreen() {
+        if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
+            insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
+            return true;
+        }
+        return false;
+    }
+
+    public void removeExtraEmptyScreen() {
+        int nScreens = getChildCount();
+        nScreens = hasCustomContent() ? nScreens - 1 : nScreens;
+
+        if (mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && nScreens > 1) {
+            CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
+            mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
+            mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
+            removeView(cl);
+        }
+    }
+
     public long commitExtraEmptyScreen() {
         Launcher.addDumpLog(TAG, "10249126 - commitExtraEmptyScreen()", true);
 
@@ -554,17 +615,12 @@
         mWorkspaceScreens.put(newId, cl);
         mScreenOrder.add(newId);
 
-        addExtraEmptyScreen();
-
         // Update the model for the new screen
         mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
 
         return newId;
     }
 
-    public void addExtraEmptyScreen() {
-        insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
-    }
 
     public CellLayout getScreenWithId(long screenId) {
         Launcher.addDumpLog(TAG, "10249126 - getScreenWithId(" + screenId + ")", true);
@@ -629,16 +685,27 @@
             }
         }
 
+        // We enforce at least one page to add new items to. In the case that we remove the last
+        // such screen, we convert the last screen to the empty screen
+        int minScreens = hasCustomContent() ? 2 : 1;
+
         int pageShift = 0;
         for (Long id: removeScreens) {
             Launcher.addDumpLog(TAG, "10249126 - \tremove(" + id + ")", true);
             CellLayout cl = mWorkspaceScreens.get(id);
             mWorkspaceScreens.remove(id);
             mScreenOrder.remove(id);
-            if (indexOfChild(cl) < currentPage) {
-                pageShift++;
+
+            if (getChildCount() > minScreens) {
+                if (indexOfChild(cl) < currentPage) {
+                    pageShift++;
+                }
+                removeView(cl);
+            } else {
+                // if this is the last non-custom content screen, convert it to the empty screen
+                mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
+                mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
             }
-            removeView(cl);
         }
 
         if (!removeScreens.isEmpty()) {
@@ -2130,6 +2197,11 @@
 
         mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
                 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
+
+        if (child.getParent() instanceof ShortcutAndWidgetContainer) {
+            mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
+        }
+
         b.recycle();
     }