Initial implementation of Shrink-wrap

Change-Id: If73c7f7ca19ca62ff43134f515584354afef8507
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 44c0c05..5fe05dd 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -46,11 +46,6 @@
             launcher:scrollIndicatorPaddingLeft="@dimen/qsb_bar_height"
             launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height">
 
-            <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
-            <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
-            <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
-            <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
-            <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
         </com.android.launcher3.Workspace>
 
         <include
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 0a20e88..2aec4e8 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -63,12 +63,6 @@
             launcher:pageSpacing="@dimen/workspace_page_spacing"
             launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left"
             launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right">
-
-            <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
-            <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
-            <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
-            <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
-            <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
         </com.android.launcher3.Workspace>
 
         <include layout="@layout/hotseat"
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index e115e43..35598a2 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -709,7 +709,10 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
+        if (getParent() instanceof Workspace) {
+            Workspace workspace = (Workspace) getParent();
+            mCellInfo.screenId = workspace.getIdForScreen(this);
+        }
     }
 
     public void setTagToCellInfoForPoint(int touchX, int touchY) {
@@ -3334,7 +3337,7 @@
         int cellY = -1;
         int spanX;
         int spanY;
-        int screen;
+        long screenId;
         long container;
 
         @Override
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 7b15e9e..9eafc22 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -1066,7 +1066,7 @@
         Runnable onCompleteRunnable = new Runnable() {
             @Override
             public void run() {
-                CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container, mInfo.screen);
+                CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container, mInfo.screenId);
 
                View child = null;
                 // Move the item from the folder to the workspace, in the position of the folder
@@ -1075,7 +1075,7 @@
                     child = mLauncher.createShortcut(R.layout.application, cellLayout,
                             finalItem);
                     LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container,
-                            mInfo.screen, mInfo.cellX, mInfo.cellY);
+                            mInfo.screenId, mInfo.cellX, mInfo.cellY);
                 }
                 if (getItemCount() <= 1) {
                     // Remove the folder
@@ -1089,7 +1089,7 @@
                 // We add the child after removing the folder to prevent both from existing at
                 // the same time in the CellLayout.
                 if (child != null) {
-                    mLauncher.getWorkspace().addInScreen(child, mInfo.container, mInfo.screen,
+                    mLauncher.getWorkspace().addInScreen(child, mInfo.container, mInfo.screenId,
                             mInfo.cellX, mInfo.cellY, mInfo.spanX, mInfo.spanY);
                 }
             }
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 50f7efd..93f39ff 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -122,10 +122,10 @@
         fi.spanX = 1;
         fi.spanY = 1;
         fi.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-        fi.screen = mAllAppsButtonRank;
+        fi.screenId = mAllAppsButtonRank;
         fi.itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
         fi.title = "All Apps";
-        LauncherModel.addItemToDatabase(launcher, fi, fi.container, fi.screen, fi.cellX,
+        LauncherModel.addItemToDatabase(launcher, fi, fi.container, fi.screenId, fi.cellX,
                 fi.cellY, false);
         FolderIcon folder = FolderIcon.fromXml(R.layout.folder_icon, launcher,
                 getLayout(), fi, iconCache);
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 07d68da..d647d96 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -333,6 +333,7 @@
         return false;
     }
 
+    // TODO: this needs to be updated to take a screenId instead of a screen index
     private static boolean findEmptyCell(Context context, ArrayList<ItemInfo> items, int[] xy,
             int screen) {
         final int xCount = LauncherModel.getCellCountX();
@@ -344,7 +345,7 @@
         for (int i = 0; i < items.size(); ++i) {
             item = items.get(i);
             if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                if (item.screen == screen) {
+                if (item.screenId == screen) {
                     cellX = item.cellX;
                     cellY = item.cellY;
                     spanX = item.spanX;
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index fb41834..1d7ebba 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -55,7 +55,7 @@
     /**
      * Iindicates the screen in which the shortcut appears.
      */
-    int screen = -1;
+    long screenId = -1;
     
     /**
      * Indicates the X position of the associated cell.
@@ -111,7 +111,7 @@
         cellY = info.cellY;
         spanX = info.spanX;
         spanY = info.spanY;
-        screen = info.screen;
+        screenId = info.screenId;
         itemType = info.itemType;
         container = info.container;
         // tempdebug:
@@ -141,7 +141,7 @@
     void onAddToDatabase(ContentValues values) { 
         values.put(LauncherSettings.BaseLauncherColumns.ITEM_TYPE, itemType);
         values.put(LauncherSettings.Favorites.CONTAINER, container);
-        values.put(LauncherSettings.Favorites.SCREEN, screen);
+        values.put(LauncherSettings.Favorites.SCREEN, screenId);
         values.put(LauncherSettings.Favorites.CELLX, cellX);
         values.put(LauncherSettings.Favorites.CELLY, cellY);
         values.put(LauncherSettings.Favorites.SPANX, spanX);
@@ -188,7 +188,7 @@
     @Override
     public String toString() {
         return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container
-            + " screen=" + screen + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
+            + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
             + " spanY=" + spanY + " dropPos=" + dropPos + ")";
     }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0253103..305f249 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -295,7 +295,7 @@
 
     // Holds the page that we need to animate to, and the icon views that we need to animate up
     // when we scroll to that page on resume.
-    private int mNewShortcutAnimatePage = -1;
+    private long mNewShortcutAnimateScreenId = -1;
     private ArrayList<View> mNewShortcutAnimateViews = new ArrayList<View>();
     private ImageView mFolderIconImageView;
     private Bitmap mFolderIconBitmap;
@@ -324,7 +324,7 @@
         int requestCode;
         Intent intent;
         long container;
-        int screen;
+        long screenId;
         int cellX;
         int cellY;
     }
@@ -592,20 +592,20 @@
         boolean result = false;
         switch (args.requestCode) {
             case REQUEST_PICK_APPLICATION:
-                completeAddApplication(args.intent, args.container, args.screen, args.cellX,
+                completeAddApplication(args.intent, args.container, args.screenId, args.cellX,
                         args.cellY);
                 break;
             case REQUEST_PICK_SHORTCUT:
                 processShortcut(args.intent);
                 break;
             case REQUEST_CREATE_SHORTCUT:
-                completeAddShortcut(args.intent, args.container, args.screen, args.cellX,
+                completeAddShortcut(args.intent, args.container, args.screenId, args.cellX,
                         args.cellY);
                 result = true;
                 break;
             case REQUEST_CREATE_APPWIDGET:
                 int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
-                completeAddAppWidget(appWidgetId, args.container, args.screen, null, null);
+                completeAddAppWidget(appWidgetId, args.container, args.screenId, null, null);
                 result = true;
                 break;
             case REQUEST_PICK_WALLPAPER:
@@ -661,7 +661,7 @@
             args.requestCode = requestCode;
             args.intent = data;
             args.container = mPendingAddInfo.container;
-            args.screen = mPendingAddInfo.screen;
+            args.screenId = mPendingAddInfo.screenId;
             args.cellX = mPendingAddInfo.cellX;
             args.cellY = mPendingAddInfo.cellY;
             if (isWorkspaceLocked()) {
@@ -678,7 +678,7 @@
 
     private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
         CellLayout cellLayout =
-                (CellLayout) mWorkspace.getChildAt(mPendingAddInfo.screen);
+                (CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
         Runnable onCompleteRunnable = null;
         int animationType = 0;
 
@@ -692,7 +692,7 @@
                 @Override
                 public void run() {
                     completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
-                            mPendingAddInfo.screen, layout, null);
+                            mPendingAddInfo.screenId, layout, null);
                     exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
                             null);
                 }
@@ -826,22 +826,7 @@
 
     // Add a fullscreen unpadded view to the workspace to the left all other screens.
     public void addCustomContentToLeft(View customContent) {
-        CellLayout customScreen = (CellLayout)
-                getLayoutInflater().inflate(R.layout.workspace_custom_content, null);
-
-        int spanX = customScreen.getCountX();
-        int spanY = customScreen.getCountY();
-
-        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
-        lp.canReorder  = false;
-
-        customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
-
-        mWorkspace.addView(customScreen, 0);
-
-        // Ensure that the current page and default page are maintained.
-        mWorkspace.incrementNumScreensToLeft();
-        mWorkspace.setCurrentPage(mWorkspace.getCurrentPage() + 1);
+        mWorkspace.addCustomContentToLeft(customContent);
     }
 
     @Override
@@ -955,11 +940,11 @@
         }
 
         final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
-        final int pendingAddScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
+        final long pendingAddScreen = savedState.getLong(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
 
         if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
             mPendingAddInfo.container = pendingAddContainer;
-            mPendingAddInfo.screen = pendingAddScreen;
+            mPendingAddInfo.screenId = pendingAddScreen;
             mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
             mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
             mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
@@ -969,7 +954,6 @@
             mRestoring = true;
         }
 
-
         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
         if (renameFolder) {
             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
@@ -977,7 +961,6 @@
             mRestoring = true;
         }
 
-
         // Restore the AppsCustomize tab
         if (mAppsCustomizeTabHost != null) {
             String curTab = savedState.getString("apps_customize_currentTab");
@@ -1086,9 +1069,9 @@
      * @param data The intent describing the application.
      * @param cellInfo The position on screen where to create the shortcut.
      */
-    void completeAddApplication(Intent data, long container, int screen, int cellX, int cellY) {
+    void completeAddApplication(Intent data, long container, long screenId, int cellX, int cellY) {
         final int[] cellXY = mTmpAddItemCellCoordinates;
-        final CellLayout layout = getCellLayout(container, screen);
+        final CellLayout layout = getCellLayout(container, screenId);
 
         // First we check if we already know the exact location where we want to add this item.
         if (cellX >= 0 && cellY >= 0) {
@@ -1105,7 +1088,7 @@
             info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
                     Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
             info.container = ItemInfo.NO_ID;
-            mWorkspace.addApplicationShortcut(info, layout, container, screen, cellXY[0], cellXY[1],
+            mWorkspace.addApplicationShortcut(info, layout, container, screenId, cellXY[0], cellXY[1],
                     isWorkspaceLocked(), cellX, cellY);
         } else {
             Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
@@ -1118,11 +1101,11 @@
      * @param data The intent describing the shortcut.
      * @param cellInfo The position on screen where to create the shortcut.
      */
-    private void completeAddShortcut(Intent data, long container, int screen, int cellX,
+    private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
             int cellY) {
         int[] cellXY = mTmpAddItemCellCoordinates;
         int[] touchXY = mPendingAddInfo.dropPos;
-        CellLayout layout = getCellLayout(container, screen);
+        CellLayout layout = getCellLayout(container, screenId);
 
         boolean foundCellSpan = false;
 
@@ -1162,11 +1145,10 @@
             return;
         }
 
-        int adjustedScreen = screen - getWorkspace().mNumPagesToLeft;
-        LauncherModel.addItemToDatabase(this, info, container, adjustedScreen, cellXY[0], cellXY[1], false);
+        LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1], false);
 
         if (!mRestoring) {
-            mWorkspace.addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1,
+            mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
                     isWorkspaceLocked());
         }
     }
@@ -1204,14 +1186,14 @@
      * @param appWidgetId The app widget id
      * @param cellInfo The position on screen where to create the widget.
      */
-    private void completeAddAppWidget(final int appWidgetId, long container, int screen,
+    private void completeAddAppWidget(final int appWidgetId, long container, long screenId,
             AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) {
         if (appWidgetInfo == null) {
             appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
         }
 
         // Calculate the grid spans needed to fit this widget
-        CellLayout layout = getCellLayout(container, screen);
+        CellLayout layout = getCellLayout(container, screenId);
 
         int[] minSpanXY = getMinSpanForWidget(this, appWidgetInfo);
         int[] spanXY = getSpanForWidget(this, appWidgetInfo);
@@ -1263,9 +1245,8 @@
         launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
         launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
 
-        int adjustedScreen = screen - getWorkspace().mNumPagesToLeft;
         LauncherModel.addItemToDatabase(this, launcherInfo,
-                container, adjustedScreen, cellXY[0], cellXY[1], false);
+                container, screenId, cellXY[0], cellXY[1], false);
 
         if (!mRestoring) {
             if (hostView == null) {
@@ -1281,7 +1262,7 @@
             launcherInfo.hostView.setVisibility(View.VISIBLE);
             launcherInfo.notifyWidgetSizeChanged(this);
 
-            mWorkspace.addInScreen(launcherInfo.hostView, container, screen, cellXY[0], cellXY[1],
+            mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, cellXY[0], cellXY[1],
                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
 
             addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
@@ -1560,10 +1541,10 @@
         // this state is reflected.
         closeFolder();
 
-        if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screen > -1 &&
+        if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
                 mWaitingForResult) {
             outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screen);
+            outState.putLong(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screenId);
             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX);
             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
@@ -1789,7 +1770,7 @@
 
     private void resetAddInfo() {
         mPendingAddInfo.container = ItemInfo.NO_ID;
-        mPendingAddInfo.screen = -1;
+        mPendingAddInfo.screenId = -1;
         mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
         mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
         mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
@@ -1808,7 +1789,7 @@
             startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
         } else {
             // Otherwise just add it
-            completeAddAppWidget(appWidgetId, info.container, info.screen, boundWidget,
+            completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
                     appWidgetInfo);
             // Exit spring loaded mode if necessary after adding the widget
             exitSpringLoadedDragModeDelayed(true, false, null);
@@ -1819,15 +1800,15 @@
      * Process a shortcut drop.
      *
      * @param componentName The name of the component
-     * @param screen The screen where it should be added
+     * @param screenId The ID of the screen where it should be added
      * @param cell The cell it should be added to, optional
      * @param position The location on the screen where it was dropped, optional
      */
-    void processShortcutFromDrop(ComponentName componentName, long container, int screen,
+    void processShortcutFromDrop(ComponentName componentName, long container, long screenId,
             int[] cell, int[] loc) {
         resetAddInfo();
         mPendingAddInfo.container = container;
-        mPendingAddInfo.screen = screen;
+        mPendingAddInfo.screenId = screenId;
         mPendingAddInfo.dropPos = loc;
 
         if (cell != null) {
@@ -1844,15 +1825,15 @@
      * Process a widget drop.
      *
      * @param info The PendingAppWidgetInfo of the widget being added.
-     * @param screen The screen where it should be added
+     * @param screenId The ID of the screen where it should be added
      * @param cell The cell it should be added to, optional
      * @param position The location on the screen where it was dropped, optional
      */
-    void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, int screen,
+    void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId,
             int[] cell, int[] span, int[] loc) {
         resetAddInfo();
         mPendingAddInfo.container = info.container = container;
-        mPendingAddInfo.screen = info.screen = screen;
+        mPendingAddInfo.screenId = info.screenId = screenId;
         mPendingAddInfo.dropPos = loc;
         mPendingAddInfo.minSpanX = info.minSpanX;
         mPendingAddInfo.minSpanY = info.minSpanY;
@@ -1921,20 +1902,20 @@
         startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
     }
 
-    FolderIcon addFolder(CellLayout layout, long container, final int screen, int cellX,
+    FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
             int cellY) {
         final FolderInfo folderInfo = new FolderInfo();
         folderInfo.title = getText(R.string.folder_name);
 
         // Update the model
-        LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screen, cellX, cellY,
+        LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId, cellX, cellY,
                 false);
         sFolders.put(folderInfo.id, folderInfo);
 
         // Create the view
         FolderIcon newFolder =
             FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
-        mWorkspace.addInScreen(newFolder, container, screen, cellX, cellY, 1, 1,
+        mWorkspace.addInScreen(newFolder, container, screenId, cellX, cellY, 1, 1,
                 isWorkspaceLocked());
         return newFolder;
     }
@@ -2231,7 +2212,7 @@
         // it is actually opened. There have been a few instances where this gets out of sync.
         if (info.opened && openFolder == null) {
             Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
-                    + info.screen + " (" + info.cellX + ", " + info.cellY + ")");
+                    + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
             info.opened = false;
         }
 
@@ -2469,7 +2450,7 @@
     /**
      * Returns the CellLayout of the specified container at the specified screen.
      */
-    CellLayout getCellLayout(long container, int screen) {
+    CellLayout getCellLayout(long container, long screenId) {
         if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
             if (mHotseat != null) {
                 return mHotseat.getLayout();
@@ -2477,7 +2458,7 @@
                 return null;
             }
         } else {
-            return (CellLayout) mWorkspace.getChildAt(screen);
+            return (CellLayout) mWorkspace.getScreenWithId(screenId);
         }
     }
 
@@ -3440,7 +3421,7 @@
         mOnResumeCallbacks.clear();
 
         final Workspace workspace = mWorkspace;
-        mNewShortcutAnimatePage = -1;
+        mNewShortcutAnimateScreenId = -1;
         mNewShortcutAnimateViews.clear();
         mWorkspace.clearDropTargets();
         int count = workspace.getChildCount();
@@ -3455,6 +3436,15 @@
         }
     }
 
+    @Override
+    public void bindScreens(ArrayList<Long> orderedScreenIds) {
+        int count = orderedScreenIds.size();
+        for (int i = 0; i < count; i++) {
+            mWorkspace.insertNewWorkspaceScreenOnBind(orderedScreenIds.get(i));
+        }
+        mWorkspace.addExtraEmptyScreen();
+    }
+
     /**
      * Bind the items start-end from the list.
      *
@@ -3489,8 +3479,9 @@
                     ShortcutInfo info = (ShortcutInfo) item;
                     String uri = info.intent.toUri(0).toString();
                     View shortcut = createShortcut(info);
-                    workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
-                            item.cellY, 1, 1, false);
+
+                    workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX,
+                            item.cellY, 1, 1);
                     boolean animateIconUp = false;
                     synchronized (newApps) {
                         if (newApps.contains(uri)) {
@@ -3502,7 +3493,7 @@
                         shortcut.setAlpha(0f);
                         shortcut.setScaleX(0f);
                         shortcut.setScaleY(0f);
-                        mNewShortcutAnimatePage = item.screen;
+                        mNewShortcutAnimateScreenId = item.screenId;
                         if (!mNewShortcutAnimateViews.contains(shortcut)) {
                             mNewShortcutAnimateViews.add(shortcut);
                         }
@@ -3512,8 +3503,8 @@
                     FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
                             (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                             (FolderInfo) item, mIconCache);
-                    workspace.addInScreen(newFolder, item.container, item.screen, item.cellX,
-                            item.cellY, 1, 1, false);
+                    workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX,
+                            item.cellY, 1, 1);
                     break;
             }
         }
@@ -3567,7 +3558,7 @@
         item.hostView.setTag(item);
         item.onBindAppWidget(this);
 
-        workspace.addInScreen(item.hostView, item.container, item.screen, item.cellX,
+        workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX,
                 item.cellY, item.spanX, item.spanY, false);
         addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
 
@@ -3625,13 +3616,13 @@
                 }
             };
 
-            boolean willSnapPage = mNewShortcutAnimatePage > -1 &&
-                    mNewShortcutAnimatePage != mWorkspace.getCurrentPage();
+            boolean willSnapPage = mNewShortcutAnimateScreenId > -1 &&
+                    mNewShortcutAnimateScreenId != mWorkspace.getCurrentPage();
             if (canRunNewAppsAnimation()) {
                 // If the user has not interacted recently, then either snap to the new page to show
                 // the new-apps animation or just run them if they are to appear on the current page
                 if (willSnapPage) {
-                    mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable);
+                    mWorkspace.snapToScreenId(mNewShortcutAnimateScreenId, newAppsRunnable);
                 } else {
                     runNewAppsAnimation(false);
                 }
@@ -3715,7 +3706,7 @@
         }
 
         // Clean up
-        mNewShortcutAnimatePage = -1;
+        mNewShortcutAnimateScreenId = -1;
         mNewShortcutAnimateViews.clear();
         new Thread("clearNewAppsThread") {
             public void run() {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 2e76a65..041882f 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -48,14 +48,13 @@
 import android.os.SystemClock;
 import android.util.Log;
 
-import com.android.launcher3.R;
 import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
 
 import java.lang.ref.WeakReference;
 import java.net.URISyntaxException;
 import java.text.Collator;
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -63,6 +62,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.TreeMap;
 
 /**
  * Maintains in-memory state of the Launcher. It is expected that there should be only one
@@ -138,6 +138,10 @@
 
     // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database
     static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>();
+
+    // sBgWorkspaceScreens is the ordered set of workspace screens.
+    static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
+
     // </ only access in worker thread >
 
     private IconCache mIconCache;
@@ -153,6 +157,7 @@
         public int getCurrentWorkspaceScreen();
         public void startBinding();
         public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
+        public void bindScreens(ArrayList<Long> orderedScreenIds);
         public void bindFolders(HashMap<Long,FolderInfo> folders);
         public void finishBindingItems(boolean upgradePath);
         public void bindAppWidget(LauncherAppWidgetInfo info);
@@ -257,13 +262,13 @@
      * <container, screen, cellX, cellY>
      */
     static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
-            int screen, int cellX, int cellY) {
+            long screenId, int cellX, int cellY) {
         if (item.container == ItemInfo.NO_ID) {
             // From all apps
-            addItemToDatabase(context, item, container, screen, cellX, cellY, false);
+            addItemToDatabase(context, item, container, screenId, cellX, cellY, false);
         } else {
             // From somewhere else
-            moveItemInDatabase(context, item, container, screen, cellX, cellY);
+            moveItemInDatabase(context, item, container, screenId, cellX, cellY);
         }
     }
 
@@ -280,7 +285,7 @@
                         modelShortcut.id == shortcut.id &&
                         modelShortcut.itemType == shortcut.itemType &&
                         modelShortcut.container == shortcut.container &&
-                        modelShortcut.screen == shortcut.screen &&
+                        modelShortcut.screenId == shortcut.screenId &&
                         modelShortcut.cellX == shortcut.cellX &&
                         modelShortcut.cellY == shortcut.cellY &&
                         modelShortcut.spanX == shortcut.spanX &&
@@ -444,10 +449,10 @@
      * Move an item in the DB to a new <container, screen, cellX, cellY>
      */
     static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
-            final int screen, final int cellX, final int cellY) {
+            final long screenId, final int cellX, final int cellY) {
         String transaction = "DbDebug    Modify item (" + item.title + ") in db, id: " + item.id +
-                " (" + item.container + ", " + item.screen + ", " + item.cellX + ", " + item.cellY +
-                ") --> " + "(" + container + ", " + screen + ", " + cellX + ", " + cellY + ")";
+                " (" + item.container + ", " + item.screenId + ", " + item.cellX + ", " + item.cellY +
+                ") --> " + "(" + container + ", " + screenId + ", " + cellX + ", " + cellY + ")";
         Launcher.sDumpLogs.add(transaction);
         Log.d(TAG, transaction);
         item.container = container;
@@ -456,18 +461,18 @@
 
         // We store hotseat items in canonical form which is this orientation invariant position
         // in the hotseat
-        if (context instanceof Launcher && screen < 0 &&
+        if (context instanceof Launcher && screenId < 0 &&
                 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            item.screen = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
+            item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
         } else {
-            item.screen = screen;
+            item.screenId = screenId;
         }
 
         final ContentValues values = new ContentValues();
         values.put(LauncherSettings.Favorites.CONTAINER, item.container);
         values.put(LauncherSettings.Favorites.CELLX, item.cellX);
         values.put(LauncherSettings.Favorites.CELLY, item.cellY);
-        values.put(LauncherSettings.Favorites.SCREEN, item.screen);
+        values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
 
         updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
     }
@@ -485,7 +490,7 @@
         for (int i = 0; i < count; i++) {
             ItemInfo item = items.get(i);
             String transaction = "DbDebug    Modify item (" + item.title + ") in db, id: "
-                    + item.id + " (" + item.container + ", " + item.screen + ", " + item.cellX
+                    + item.id + " (" + item.container + ", " + item.screenId + ", " + item.cellX
                     + ", " + item.cellY + ") --> " + "(" + container + ", " + screen + ", "
                     + item.cellX + ", " + item.cellY + ")";
             Launcher.sDumpLogs.add(transaction);
@@ -495,17 +500,17 @@
             // in the hotseat
             if (context instanceof Launcher && screen < 0 &&
                     container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-                item.screen = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
+                item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
                         item.cellY);
             } else {
-                item.screen = screen;
+                item.screenId = screen;
             }
 
             final ContentValues values = new ContentValues();
             values.put(LauncherSettings.Favorites.CONTAINER, item.container);
             values.put(LauncherSettings.Favorites.CELLX, item.cellX);
             values.put(LauncherSettings.Favorites.CELLY, item.cellY);
-            values.put(LauncherSettings.Favorites.SCREEN, item.screen);
+            values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
 
             contentValues.add(values);
         }
@@ -516,10 +521,10 @@
      * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
      */
     static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
-            final int screen, final int cellX, final int cellY, final int spanX, final int spanY) {
+            final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
         String transaction = "DbDebug    Modify item (" + item.title + ") in db, id: " + item.id +
-                " (" + item.container + ", " + item.screen + ", " + item.cellX + ", " + item.cellY +
-                ") --> " + "(" + container + ", " + screen + ", " + cellX + ", " + cellY + ")";
+                " (" + item.container + ", " + item.screenId + ", " + item.cellX + ", " + item.cellY +
+                ") --> " + "(" + container + ", " + screenId + ", " + cellX + ", " + cellY + ")";
         Launcher.sDumpLogs.add(transaction);
         Log.d(TAG, transaction);
         item.cellX = cellX;
@@ -529,11 +534,11 @@
 
         // We store hotseat items in canonical form which is this orientation invariant position
         // in the hotseat
-        if (context instanceof Launcher && screen < 0 &&
+        if (context instanceof Launcher && screenId < 0 &&
                 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            item.screen = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
+            item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
         } else {
-            item.screen = screen;
+            item.screenId = screenId;
         }
 
         final ContentValues values = new ContentValues();
@@ -542,7 +547,7 @@
         values.put(LauncherSettings.Favorites.CELLY, item.cellY);
         values.put(LauncherSettings.Favorites.SPANX, item.spanX);
         values.put(LauncherSettings.Favorites.SPANY, item.spanY);
-        values.put(LauncherSettings.Favorites.SCREEN, item.screen);
+        values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
 
         updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
     }
@@ -604,7 +609,7 @@
                 item.spanY = c.getInt(spanYIndex);
                 item.container = c.getInt(containerIndex);
                 item.itemType = c.getInt(itemTypeIndex);
-                item.screen = c.getInt(screenIndex);
+                item.screenId = c.getInt(screenIndex);
 
                 items.add(item);
             }
@@ -646,7 +651,7 @@
                 folderInfo.title = c.getString(titleIndex);
                 folderInfo.id = id;
                 folderInfo.container = c.getInt(containerIndex);
-                folderInfo.screen = c.getInt(screenIndex);
+                folderInfo.screenId = c.getInt(screenIndex);
                 folderInfo.cellX = c.getInt(cellXIndex);
                 folderInfo.cellY = c.getInt(cellYIndex);
 
@@ -664,17 +669,17 @@
      * cellY fields of the item. Also assigns an ID to the item.
      */
     static void addItemToDatabase(Context context, final ItemInfo item, final long container,
-            final int screen, final int cellX, final int cellY, final boolean notify) {
+            final long screenId, final int cellX, final int cellY, final boolean notify) {
         item.container = container;
         item.cellX = cellX;
         item.cellY = cellY;
         // We store hotseat items in canonical form which is this orientation invariant position
         // in the hotseat
-        if (context instanceof Launcher && screen < 0 &&
+        if (context instanceof Launcher && screenId < 0 &&
                 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            item.screen = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
+            item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
         } else {
-            item.screen = screen;
+            item.screenId = screenId;
         }
 
         final ContentValues values = new ContentValues();
@@ -682,14 +687,14 @@
         item.onAddToDatabase(values);
 
         LauncherAppState app = LauncherAppState.getInstance();
-        item.id = app.getLauncherProvider().generateNewId();
+        item.id = app.getLauncherProvider().generateNewItemId();
         values.put(LauncherSettings.Favorites._ID, item.id);
         item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
 
         Runnable r = new Runnable() {
             public void run() {
                 String transaction = "DbDebug    Add item (" + item.title + ") to db, id: "
-                        + item.id + " (" + container + ", " + screen + ", " + cellX + ", "
+                        + item.id + " (" + container + ", " + screenId + ", " + cellX + ", "
                         + cellY + ")";
                 Launcher.sDumpLogs.add(transaction);
                 Log.d(TAG, transaction);
@@ -734,9 +739,9 @@
      * Creates a new unique child id, for a given cell span across all layouts.
      */
     static int getCellLayoutChildId(
-            long container, int screen, int localCellX, int localCellY, int spanX, int spanY) {
+            long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
         return (((int) container & 0xFF) << 24)
-                | (screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
+                | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
     }
 
     static int getCellCountX() {
@@ -768,7 +773,7 @@
         Runnable r = new Runnable() {
             public void run() {
                 String transaction = "DbDebug    Delete item (" + item.title + ") from db, id: "
-                        + item.id + " (" + item.container + ", " + item.screen + ", " + item.cellX +
+                        + item.id + " (" + item.container + ", " + item.screenId + ", " + item.cellX +
                         ", " + item.cellY + ")";
                 Launcher.sDumpLogs.add(transaction);
                 Log.d(TAG, transaction);
@@ -809,6 +814,48 @@
     }
 
     /**
+     * Update the order of the workspace screens in the database. The array list contains
+     * a list of screen ids in the order that they should appear.
+     */
+    static void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
+        final ContentResolver cr = context.getContentResolver();
+        final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
+
+        // Remove any negative screen ids -- these aren't persisted
+        Iterator<Long> iter = screens.iterator();
+        while (iter.hasNext()) {
+            long id = iter.next();
+            if (id < 0) {
+                iter.remove();
+            }
+        }
+
+        Runnable r = new Runnable() {
+            @Override
+            public void run() {
+                final ArrayList<Long> screensCopy = new ArrayList<Long>();
+
+                // Clear the table
+                cr.delete(uri, null, null);
+                int count = screens.size();
+                ContentValues[] values = new ContentValues[count];
+                for (int i = 0; i < count; i++) {
+                    ContentValues v = new ContentValues();
+                    long screenId = screens.get(i);
+                    v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
+                    v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
+                    screensCopy.add(screenId);
+                    values[i] = v;
+                }
+                cr.bulkInsert(uri, values);
+                sBgWorkspaceScreens.clear();
+                sBgWorkspaceScreens.addAll(screensCopy);
+            }
+        };
+        runOnWorkerThread(r);
+    }
+
+    /**
      * Remove the contents of the specified folder from the database
      */
     static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
@@ -1219,7 +1266,6 @@
                 }
             }
 
-
             // Update the saved icons if necessary
             if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
             synchronized (sBgLock) {
@@ -1280,23 +1326,23 @@
         }
 
         // check & update map of what's occupied; used to discard overlapping/invalid items
-        private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
-            int containerIndex = item.screen;
+        private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item) {
+            long containerIndex = item.screenId;
             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-                // Return early if we detect that an item is under the hotseat button
-                if (mCallbacks == null || mCallbacks.get().isAllAppsButtonRank(item.screen)) {
-                    return false;
-                }
-
-                // We use the last index to refer to the hotseat and the screen as the rank, so
-                // test and update the occupied state accordingly
-                if (occupied[Launcher.SCREEN_COUNT][item.screen][0] != null) {
-                    Log.e(TAG, "Error loading shortcut into hotseat " + item
-                        + " into position (" + item.screen + ":" + item.cellX + "," + item.cellY
-                        + ") occupied by " + occupied[Launcher.SCREEN_COUNT][item.screen][0]);
-                    return false;
+                if (occupied.containsKey(LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
+                    if (occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
+                            [(int) item.screenId][0] != null) {
+                        Log.e(TAG, "Error loading shortcut into hotseat " + item
+                                + " into position (" + item.screenId + ":" + item.cellX + ","
+                                + item.cellY + ") occupied by "
+                                + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
+                                [(int) item.screenId][0]);
+                            return false;
+                    }
                 } else {
-                    occupied[Launcher.SCREEN_COUNT][item.screen][0] = item;
+                    ItemInfo[][] items = new ItemInfo[mCellCountX + 1][mCellCountY + 1];
+                    items[(int) item.screenId][0] = item;
+                    occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
                     return true;
                 }
             } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
@@ -1304,22 +1350,28 @@
                 return true;
             }
 
+            if (!occupied.containsKey(item.screenId)) {
+                ItemInfo[][] items = new ItemInfo[mCellCountX + 1][mCellCountY + 1];
+                occupied.put(item.screenId, items);
+            }
+
+            ItemInfo[][] screens = occupied.get(item.screenId);
             // Check if any workspace icons overlap with each other
             for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
                 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
-                    if (occupied[containerIndex][x][y] != null) {
+                    if (screens[x][y] != null) {
                         Log.e(TAG, "Error loading shortcut " + item
-                            + " into cell (" + containerIndex + "-" + item.screen + ":"
+                            + " into cell (" + containerIndex + "-" + item.screenId + ":"
                             + x + "," + y
                             + ") occupied by "
-                            + occupied[containerIndex][x][y]);
+                            + screens[x][y]);
                         return false;
                     }
                 }
             }
             for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
                 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
-                    occupied[containerIndex][x][y] = item;
+                    screens[x][y] = item;
                 }
             }
 
@@ -1348,6 +1400,7 @@
                 sBgFolders.clear();
                 sBgItemsIdMap.clear();
                 sBgDbIconCache.clear();
+                sBgWorkspaceScreens.clear();
 
                 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
 
@@ -1356,8 +1409,7 @@
                 // +1 for the hotseat (it can be larger than the workspace)
                 // Load workspace in reverse order to ensure that latest items are loaded first (and
                 // before any earlier duplicates)
-                final ItemInfo occupied[][][] =
-                        new ItemInfo[Launcher.SCREEN_COUNT + 1][mCellCountX + 1][mCellCountY + 1];
+                final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();
 
                 try {
                     final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
@@ -1439,7 +1491,7 @@
                                     info.id = c.getLong(idIndex);
                                     container = c.getInt(containerIndex);
                                     info.container = container;
-                                    info.screen = c.getInt(screenIndex);
+                                    info.screenId = c.getInt(screenIndex);
                                     info.cellX = c.getInt(cellXIndex);
                                     info.cellY = c.getInt(cellYIndex);
                                     // check & update map of what's occupied
@@ -1461,7 +1513,7 @@
                                     }
                                     if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
                                             loadOldDb) {
-                                        info.screen = permuteScreens(info.screen);
+                                        info.screenId = permuteScreens(info.screenId);
                                     }
                                     sBgItemsIdMap.put(info.id, info);
 
@@ -1488,7 +1540,7 @@
                                 folderInfo.id = id;
                                 container = c.getInt(containerIndex);
                                 folderInfo.container = container;
-                                folderInfo.screen = c.getInt(screenIndex);
+                                folderInfo.screenId = c.getInt(screenIndex);
                                 folderInfo.cellX = c.getInt(cellXIndex);
                                 folderInfo.cellY = c.getInt(cellYIndex);
 
@@ -1504,7 +1556,7 @@
                                 }
                                 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
                                         loadOldDb) {
-                                    folderInfo.screen = permuteScreens(folderInfo.screen);
+                                    folderInfo.screenId = permuteScreens(folderInfo.screenId);
                                 }
 
                                 sBgItemsIdMap.put(folderInfo.id, folderInfo);
@@ -1530,7 +1582,7 @@
                                     appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                             provider.provider);
                                     appWidgetInfo.id = id;
-                                    appWidgetInfo.screen = c.getInt(screenIndex);
+                                    appWidgetInfo.screenId = c.getInt(screenIndex);
                                     appWidgetInfo.cellX = c.getInt(cellXIndex);
                                     appWidgetInfo.cellY = c.getInt(cellYIndex);
                                     appWidgetInfo.spanX = c.getInt(spanXIndex);
@@ -1548,7 +1600,8 @@
                                     }
                                     if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
                                             loadOldDb) {
-                                        appWidgetInfo.screen = permuteScreens(appWidgetInfo.screen);
+                                        appWidgetInfo.screenId =
+                                                permuteScreens(appWidgetInfo.screenId);
                                     }
 
                                     appWidgetInfo.container = c.getInt(containerIndex);
@@ -1587,17 +1640,86 @@
                     }
                 }
 
+                if (loadOldDb) {
+                    long maxScreenId = 0;
+                    // If we're importing we use the old screen order.
+                    for (ItemInfo item: sBgItemsIdMap.values()) {
+                        long screenId = item.screenId;
+                        if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
+                                !sBgWorkspaceScreens.contains(screenId)) {
+                            sBgWorkspaceScreens.add(screenId);
+                            if (screenId > maxScreenId) {
+                                maxScreenId = screenId;
+                            }
+                        }
+                    }
+                    Collections.sort(sBgWorkspaceScreens);
+                    mApp.getLauncherProvider().updateMaxScreenId(maxScreenId);
+                    updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
+                } else {
+                    Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
+                    final Cursor sc = contentResolver.query(screensUri, null, null, null, null);
+                    TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>();
+
+                    try {
+                        final int idIndex = sc.getColumnIndexOrThrow(
+                                LauncherSettings.WorkspaceScreens._ID);
+                        final int rankIndex = sc.getColumnIndexOrThrow(
+                                LauncherSettings.WorkspaceScreens.SCREEN_RANK);
+                        while (sc.moveToNext()) {
+                            try {
+                                long screenId = sc.getLong(idIndex);
+                                int rank = sc.getInt(rankIndex);
+
+                                orderedScreens.put(rank, screenId);
+                            } catch (Exception e) {
+                                Log.w(TAG, "Desktop items loading interrupted:", e);
+                            }
+                        }
+                    } finally {
+                        sc.close();
+                    }
+
+                    Iterator<Integer> iter = orderedScreens.keySet().iterator();
+                    while (iter.hasNext()) {
+                        sBgWorkspaceScreens.add(orderedScreens.get(iter.next()));
+                    }
+
+                    // Remove any empty screens
+                    ArrayList<Long> unusedScreens = new ArrayList<Long>();
+                    unusedScreens.addAll(sBgWorkspaceScreens);
+
+                    for (ItemInfo item: sBgItemsIdMap.values()) {
+                        long screenId = item.screenId;
+
+                        if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
+                                unusedScreens.contains(screenId)) {
+                            unusedScreens.remove(screenId);
+                        }
+                    }
+
+                    // If there are any empty screens remove them, and update.
+                    if (unusedScreens.size() != 0) {
+                        sBgWorkspaceScreens.removeAll(unusedScreens);
+                        updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
+                    }
+                }
+
                 if (DEBUG_LOADERS) {
                     Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
                     Log.d(TAG, "workspace layout: ");
+                    Iterator<Long> iter = occupied.keySet().iterator();
+                    int nScreens = occupied.size();
                     for (int y = 0; y < mCellCountY; y++) {
                         String line = "";
-                        for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
+
+                        for (int s = 0; s < nScreens; s++) {
+                            long screenId = iter.next();
                             if (s > 0) {
                                 line += " | ";
                             }
                             for (int x = 0; x < mCellCountX; x++) {
-                                line += ((occupied[s][x][y] != null) ? "#" : ".");
+                                line += ((occupied.get(screenId)[x][y] != null) ? "#" : ".");
                             }
                         }
                         Log.d(TAG, "[ " + line + " ]");
@@ -1608,7 +1730,7 @@
 
         // We rearrange the screens from the old launcher
         // 12345 -> 34512
-        private int permuteScreens(int screen) {
+        private long permuteScreens(long screen) {
             if (screen >= 2) {
                 return screen - 2;
             } else {
@@ -1649,7 +1771,7 @@
             });
             for (ItemInfo info : allWorkspaceItems) {
                 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                    if (info.screen == currentScreen) {
+                    if (info.screenId == currentScreen) {
                         currentScreenItems.add(info);
                         itemsOnScreen.add(info.id);
                     } else {
@@ -1683,7 +1805,7 @@
             for (LauncherAppWidgetInfo widget : appWidgets) {
                 if (widget == null) continue;
                 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
-                        widget.screen == currentScreen) {
+                        widget.screenId == currentScreen) {
                     currentScreenWidgets.add(widget);
                 } else {
                     otherScreenWidgets.add(widget);
@@ -1708,7 +1830,7 @@
                 FolderInfo folder = folders.get(id);
                 if (info == null || folder == null) continue;
                 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
-                        info.screen == currentScreen) {
+                        info.screenId == currentScreen) {
                     currentScreenFolders.put(id, folder);
                 } else {
                     otherScreenFolders.put(id, folder);
@@ -1727,15 +1849,30 @@
                     int cellCountY = LauncherModel.getCellCountY();
                     int screenOffset = cellCountX * cellCountY;
                     int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
-                    long lr = (lhs.container * containerOffset + lhs.screen * screenOffset +
+                    long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
                             lhs.cellY * cellCountX + lhs.cellX);
-                    long rr = (rhs.container * containerOffset + rhs.screen * screenOffset +
+                    long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
                             rhs.cellY * cellCountX + rhs.cellX);
                     return (int) (lr - rr);
                 }
             });
         }
 
+        private void bindWorkspaceScreens(final Callbacks oldCallbacks,
+                final ArrayList<Long> orderedScreens) {
+
+            final Runnable r = new Runnable() {
+                @Override
+                public void run() {
+                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+                    if (callbacks != null) {
+                        callbacks.bindScreens(orderedScreens);
+                    }
+                }
+            };
+            runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
+        }
+
         private void bindWorkspaceItems(final Callbacks oldCallbacks,
                 final ArrayList<ItemInfo> workspaceItems,
                 final ArrayList<LauncherAppWidgetInfo> appWidgets,
@@ -1830,11 +1967,13 @@
                     new ArrayList<LauncherAppWidgetInfo>();
             HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
             HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
+            ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
             synchronized (sBgLock) {
                 workspaceItems.addAll(sBgWorkspaceItems);
                 appWidgets.addAll(sBgAppWidgets);
                 folders.putAll(sBgFolders);
                 itemsIdMap.putAll(sBgItemsIdMap);
+                orderedScreenIds.addAll(sBgWorkspaceScreens);
             }
 
             ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
@@ -1867,6 +2006,8 @@
             };
             runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
 
+            bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
+
             // Load items on the current page
             bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
                     currentFolders, null);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index f971a37..91e58e2 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -66,12 +66,13 @@
 
     private static final String DATABASE_NAME = "launcher.db";
 
-    private static final int DATABASE_VERSION = 12;
+    private static final int DATABASE_VERSION = 13;
 
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = "com.android.launcher3.settings";
 
     static final String TABLE_FAVORITES = "favorites";
+    static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
     static final String PARAMETER_NOTIFY = "notify";
     static final String DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED =
             "DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED";
@@ -90,6 +91,7 @@
             Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
 
     private DatabaseHelper mOpenHelper;
+    private static boolean sLoadOldDb;
 
     @Override
     public boolean onCreate() {
@@ -202,8 +204,18 @@
         }
     }
 
-    public long generateNewId() {
-        return mOpenHelper.generateNewId();
+    public long generateNewItemId() {
+        return mOpenHelper.generateNewItemId();
+    }
+
+    public long generateNewScreenId() {
+        return mOpenHelper.generateNewScreenId();
+    }
+
+    // This is only required one time while loading the workspace during the
+    // upgrade path, and should never be called from anywhere else.
+    public void updateMaxScreenId(long maxScreenId) {
+        mOpenHelper.updateMaxScreenId(maxScreenId);
     }
 
     /**
@@ -213,7 +225,9 @@
         String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
 
-        boolean loadOldDb = false;
+        boolean loadOldDb = false || sLoadOldDb;
+
+        sLoadOldDb = false;
         if (sp.getBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, false)) {
 
             SharedPreferences.Editor editor = sp.edit();
@@ -263,7 +277,8 @@
 
         private final Context mContext;
         private final AppWidgetHost mAppWidgetHost;
-        private long mMaxId = -1;
+        private long mMaxItemId = -1;
+        private long mMaxScreenId = -1;
 
         DatabaseHelper(Context context) {
             super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -272,8 +287,11 @@
 
             // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
             // the DB here
-            if (mMaxId == -1) {
-                mMaxId = initializeMaxId(getWritableDatabase());
+            if (mMaxItemId == -1) {
+                mMaxItemId = initializeMaxItemId(getWritableDatabase());
+            }
+            if (mMaxScreenId == -1) {
+                mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
             }
         }
 
@@ -292,7 +310,8 @@
         public void onCreate(SQLiteDatabase db) {
             if (LOGD) Log.d(TAG, "creating new launcher database");
 
-            mMaxId = 1;
+            mMaxItemId = 1;
+            mMaxScreenId = 0;
 
             db.execSQL("CREATE TABLE favorites (" +
                     "_id INTEGER PRIMARY KEY," +
@@ -314,6 +333,7 @@
                     "uri TEXT," +
                     "displayMode INTEGER" +
                     ");");
+            addWorkspacesTable(db);
 
             // Database was just created, so wipe any previous widgets
             if (mAppWidgetHost != null) {
@@ -327,6 +347,13 @@
             }
         }
 
+        private void addWorkspacesTable(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
+                    LauncherSettings.WorkspaceScreens._ID + " INTEGER," +
+                    LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER" +
+                    ");");
+        }
+
         private void setFlagToLoadDefaultWorkspaceLater() {
             String spKey = LauncherAppState.getSharedPreferencesKey();
             SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
@@ -504,8 +531,8 @@
             if (version < 9) {
                 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
                 // before it gets a change to get set, so we need to read it here when we use it)
-                if (mMaxId == -1) {
-                    mMaxId = initializeMaxId(db);
+                if (mMaxItemId == -1) {
+                    mMaxItemId = initializeMaxItemId(db);
                 }
 
                 // Add default hotseat icons
@@ -524,9 +551,24 @@
                 version = 12;
             }
 
+            if (version < 13) {
+                // With the new shrink-wrapped and re-orderable workspaces, it makes sense
+                // to persist workspace screens and their relative order.
+                mMaxScreenId = 0;
+
+                // This will never happen in the wild, but when we switch to using workspace
+                // screen ids, redo the import from old launcher.
+                sLoadOldDb = true;
+
+                addWorkspacesTable(db);
+                version = 13;
+            }
+
             if (version != DATABASE_VERSION) {
                 Log.w(TAG, "Destroying all old data.");
                 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
+                db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
+
                 onCreate(db);
             }
         }
@@ -672,15 +714,15 @@
         // constructor from the worker thread; however, this doesn't extend until after the
         // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
         // after that point
-        public long generateNewId() {
-            if (mMaxId < 0) {
-                throw new RuntimeException("Error: max id was not initialized");
+        public long generateNewItemId() {
+            if (mMaxItemId < 0) {
+                throw new RuntimeException("Error: max item id was not initialized");
             }
-            mMaxId += 1;
-            return mMaxId;
+            mMaxItemId += 1;
+            return mMaxItemId;
         }
 
-        private long initializeMaxId(SQLiteDatabase db) {
+        private long initializeMaxItemId(SQLiteDatabase db) {
             Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
 
             // get the result
@@ -694,7 +736,44 @@
             }
 
             if (id == -1) {
-                throw new RuntimeException("Error: could not query max id");
+                throw new RuntimeException("Error: could not query max item id");
+            }
+
+            return id;
+        }
+
+        // Generates a new ID to use for an workspace screen in your database. This method
+        // should be only called from the main UI thread. As an exception, we do call it when we
+        // call the constructor from the worker thread; however, this doesn't extend until after the
+        // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
+        // after that point
+        public long generateNewScreenId() {
+            if (mMaxScreenId < 0) {
+                throw new RuntimeException("Error: max screen id was not initialized");
+            }
+            mMaxScreenId += 1;
+            return mMaxScreenId;
+        }
+
+        public void updateMaxScreenId(long maxScreenId) {
+            mMaxScreenId = maxScreenId;
+        }
+
+        private long initializeMaxScreenId(SQLiteDatabase db) {
+            Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
+
+            // get the result
+            final int maxIdIndex = 0;
+            long id = -1;
+            if (c != null && c.moveToNext()) {
+                id = c.getLong(maxIdIndex);
+            }
+            if (c != null) {
+                c.close();
+            }
+
+            if (id == -1) {
+                throw new RuntimeException("Error: could not query max screen id");
             }
 
             return id;
@@ -959,7 +1038,7 @@
                     cn = new ComponentName(packages[0], className);
                     info = packageManager.getActivityInfo(cn, 0);
                 }
-                id = generateNewId();
+                id = generateNewItemId();
                 intent.setComponent(cn);
                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                         Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
@@ -968,7 +1047,7 @@
                 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
                 values.put(Favorites.SPANX, 1);
                 values.put(Favorites.SPANY, 1);
-                values.put(Favorites._ID, generateNewId());
+                values.put(Favorites._ID, generateNewItemId());
                 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
                     return -1;
                 }
@@ -983,7 +1062,7 @@
             values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
             values.put(Favorites.SPANX, 1);
             values.put(Favorites.SPANY, 1);
-            long id = generateNewId();
+            long id = generateNewItemId();
             values.put(Favorites._ID, id);
             if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
                 return -1;
@@ -1088,7 +1167,6 @@
 
             return false;
         }
-
         private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
                 int spanX, int spanY, Bundle extras) {
             boolean allocatedAppWidgets = false;
@@ -1101,7 +1179,7 @@
                 values.put(Favorites.SPANX, spanX);
                 values.put(Favorites.SPANY, spanY);
                 values.put(Favorites.APPWIDGET_ID, appWidgetId);
-                values.put(Favorites._ID, generateNewId());
+                values.put(Favorites._ID, generateNewItemId());
                 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
 
                 allocatedAppWidgets = true;
@@ -1146,7 +1224,7 @@
                 return -1;
             }
 
-            long id = generateNewId();
+            long id = generateNewItemId();
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             values.put(Favorites.INTENT, intent.toUri(0));
             values.put(Favorites.TITLE, r.getString(titleResId));
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index eb395c8..a2b9c81 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -91,6 +91,26 @@
     }
 
     /**
+     * Workspace Screens.
+     *
+     * Tracks the order of workspace screens.
+     */
+    static final class WorkspaceScreens implements BaseColumns {
+        /**
+         * The content:// style URL for this table
+         */
+        static final Uri CONTENT_URI = Uri.parse("content://" +
+                LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_WORKSPACE_SCREENS +
+                "?" + LauncherProvider.PARAMETER_NOTIFY + "=true");
+
+        /**
+         * The rank of this screen -- ie. how it is ordered relative to the other screens.
+         * <P>Type: INTEGER</P>
+         */
+        static final String SCREEN_RANK = "screenRank";
+    }
+
+    /**
      * Favorites.
      */
     static final class Favorites implements BaseLauncherColumns {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index abf8bbd..842dc20 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -491,7 +491,7 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (!mIsDataReady) {
+        if (!mIsDataReady || getChildCount() == 0) {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
             return;
         }
@@ -650,7 +650,7 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (!mIsDataReady) {
+        if (!mIsDataReady || getChildCount() == 0) {
             return;
         }
 
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 5249fec..224b2fc 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -145,7 +145,7 @@
     @Override
     public String toString() {
         return "ShortcutInfo(title=" + title.toString() + "intent=" + intent + "id=" + this.id
-                + " type=" + this.itemType + " container=" + this.container + " screen=" + screen
+                + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId
                 + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY
                 + " dropPos=" + dropPos + ")";
     }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 9a75cc1..853e9ee 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -60,6 +60,7 @@
 
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -105,9 +106,15 @@
     private IBinder mWindowToken;
     private static final float WALLPAPER_SCREENS_SPAN = 2f;
 
-    public int mNumPagesToLeft = 0;
     private int mDefaultPage;
 
+    // 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;
+
+    private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
+    private ArrayList<Long> mScreenOrder = new ArrayList<Long>();
+
     /**
      * CellInfo for the cell that is currently being dragged
      */
@@ -254,6 +261,7 @@
     private float[] mNewBackgroundAlphas;
     private float[] mNewAlphas;
     private float[] mNewRotationYs;
+    private int mLastChildCount = -1;
     private float mTransitionProgress;
 
     private final Runnable mBindPages = new Runnable() {
@@ -375,6 +383,7 @@
             return size;
         }
     }
+
     public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
             int hCell, int vCell, int hSpan, int vSpan) {
         Rect r = new Rect();
@@ -402,13 +411,6 @@
         UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
     }
 
-    // Just a hack so that if a custom content screen is added to the left, we adjust the
-    // default screen accordingly so that it stays the same.
-    void incrementNumScreensToLeft() {
-        mDefaultPage++;
-        mNumPagesToLeft++;
-    }
-
     /**
      * Initializes various states for this workspace.
      */
@@ -489,19 +491,131 @@
         return mTouchState != TOUCH_STATE_REST;
     }
 
-    /**
-     * Adds the specified child in the specified screen. The position and dimension of
-     * the child are defined by x, y, spanX and spanY.
-     *
-     * @param child The child to add in one of the workspace's screens.
-     * @param screen The screen in which to add the child.
-     * @param x The X position of the child in the screen's grid.
-     * @param y The Y position of the child in the screen's grid.
-     * @param spanX The number of cells spanned horizontally by the child.
-     * @param spanY The number of cells spanned vertically by the child.
-     */
-    void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY) {
-        addInScreen(child, container, screen, x, y, spanX, spanY, false);
+    public long insertNewWorkspaceScreen(long screenId) {
+        return insertNewWorkspaceScreen(screenId, true);
+    }
+
+    public long insertNewWorkspaceScreenOnBind(long screenId) {
+        return insertNewWorkspaceScreen(screenId, false);
+    }
+
+    // If screen id is -1, this indicates there is no screen assigned, so we generate
+    // a new screen id.
+    public long insertNewWorkspaceScreen(long screenId, boolean updateDb) {
+        CellLayout newScreen = (CellLayout)
+                mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
+
+        addView(newScreen, getChildCount());
+        mWorkspaceScreens.put(screenId, newScreen);
+        mScreenOrder.add(screenId);
+        if (updateDb) {
+            // On bind we don't need to update the screens in the database.
+            LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+        }
+        return screenId;
+    }
+
+    public void addCustomContentToLeft(View customContent) {
+        CellLayout customScreen = (CellLayout)
+                mLauncher.getLayoutInflater().inflate(R.layout.workspace_custom_content, null);
+
+        int spanX = customScreen.getCountX();
+        int spanY = customScreen.getCountY();
+
+        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
+        lp.canReorder  = false;
+
+        customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
+
+        addView(customScreen, 0);
+
+        mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
+        mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
+
+        // Ensure that the current page and default page are maintained.
+        mDefaultPage++;
+        setCurrentPage(getCurrentPage() + 1);
+    }
+
+    public long commitExtraEmptyScreen() {
+        CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
+        mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
+
+        long newId = LauncherAppState.getInstance().getLauncherProvider().generateNewScreenId();
+        mWorkspaceScreens.put(newId, cl);
+        mScreenOrder.add(newId);
+
+        addExtraEmptyScreen();
+        return newId;
+    }
+
+    public void addExtraEmptyScreen() {
+        insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID, false);
+    }
+
+    public CellLayout getScreenWithId(long screenId) {
+        CellLayout layout = mWorkspaceScreens.get(screenId);
+        return layout;
+    }
+
+    public long getIdForScreen(CellLayout layout) {
+        Iterator<Long> iter = mWorkspaceScreens.keySet().iterator();
+        while (iter.hasNext()) {
+            long id = iter.next();
+            if (mWorkspaceScreens.get(id) == layout) {
+                return id;
+            }
+        }
+        return -1;
+    }
+
+    public int getPageIndexForScreenId(long screenId) {
+        return indexOfChild(mWorkspaceScreens.get(screenId));
+    }
+
+    public long getScreenIdForPageIndex(int index) {
+        return mScreenOrder.get(index);
+    }
+
+    public void stripEmptyScreens() {
+        ArrayList<Long> removeScreens = new ArrayList<Long>();
+        for (Long id: mWorkspaceScreens.keySet()) {
+            CellLayout cl = mWorkspaceScreens.get(id);
+            if (id != EXTRA_EMPTY_SCREEN_ID && cl.getShortcutsAndWidgets().getChildCount() == 0) {
+                removeScreens.add(id);
+            }
+        }
+
+        int pageShift = 0;
+        for (Long id: removeScreens) {
+            CellLayout cl = mWorkspaceScreens.get(id);
+            mWorkspaceScreens.remove(id);
+            mScreenOrder.remove(id);
+            if (indexOfChild(cl) < mCurrentPage) {
+                pageShift++;
+            }
+            removeView(cl);
+        }
+        setCurrentPage(mCurrentPage - pageShift);
+    }
+
+    // See implementation for parameter definition.
+    void addInScreen(View child, long container, long screenId,
+            int x, int y, int spanX, int spanY) {
+        addInScreen(child, container, screenId, x, y, spanX, spanY, false, false);
+    }
+
+    // At bind time, we use the rank (screenId) to compute x and y for hotseat items.
+    // See implementation for parameter definition.
+    void addInScreenFromBind(View child, long container, long screenId, int x, int y,
+            int spanX, int spanY) {
+        addInScreen(child, container, screenId, x, y, spanX, spanY, false, true);
+    }
+
+    // See implementation for parameter definition.
+    void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
+            boolean insert) {
+        addInScreen(child, container, screenId, x, y, spanX, spanY, insert, false);
     }
 
     /**
@@ -509,23 +623,30 @@
      * the child are defined by x, y, spanX and spanY.
      *
      * @param child The child to add in one of the workspace's screens.
-     * @param screen The screen in which to add the child.
+     * @param screenId The screen in which to add the child.
      * @param x The X position of the child in the screen's grid.
      * @param y The Y position of the child in the screen's grid.
      * @param spanX The number of cells spanned horizontally by the child.
      * @param spanY The number of cells spanned vertically by the child.
      * @param insert When true, the child is inserted at the beginning of the children list.
+     * @param computeXYFromRank When true, we use the rank (stored in screenId) to compute
+     *                          the x and y position in which to place hotseat items. Otherwise
+     *                          we use the x and y position to compute the rank.
      */
-    void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY,
-            boolean insert) {
+    void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
+            boolean insert, boolean computeXYFromRank) {
         if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-            if (screen < 0 || screen >= getChildCount()) {
-                Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
-                    + " (was " + screen + "); skipping child");
+            if (getScreenWithId(screenId) == null) {
+                Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
                 return;
             }
         }
 
+        // If an item is added to the extra empty screen, we convert it to a real
+        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
+            screenId = commitExtraEmptyScreen();
+        }
+
         final CellLayout layout;
         if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
             layout = mLauncher.getHotseat().getLayout();
@@ -536,21 +657,18 @@
                 ((FolderIcon) child).setTextVisible(false);
             }
 
-            if (screen < 0) {
-                screen = mLauncher.getHotseat().getOrderInHotseat(x, y);
+            if (computeXYFromRank) {
+                x = mLauncher.getHotseat().getCellXFromOrder((int) screenId);
+                y = mLauncher.getHotseat().getCellYFromOrder((int) screenId);
             } else {
-                // Note: We do this to ensure that the hotseat is always laid out in the orientation
-                // of the hotseat in order regardless of which orientation they were added
-                x = mLauncher.getHotseat().getCellXFromOrder(screen);
-                y = mLauncher.getHotseat().getCellYFromOrder(screen);
+                screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
             }
         } else {
             // Show folder title if not in the hotseat
             if (child instanceof FolderIcon) {
                 ((FolderIcon) child).setTextVisible(true);
             }
-
-            layout = (CellLayout) getChildAt(screen);
+            layout = getScreenWithId(screenId);
             child.setOnKeyListener(new IconKeyEventListener());
         }
 
@@ -571,7 +689,7 @@
         }
 
         // Get the canonical child id to uniquely represent this view in this screen
-        int childId = LauncherModel.getCellLayoutChildId(container, screen, x, y, spanX, spanY);
+        int childId = LauncherModel.getCellLayoutChildId(container, screenId, x, y, spanX, spanY);
         boolean markCellsAsOccupied = !(child instanceof Folder);
         if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
             // TODO: This branch occurs when the workspace is adding views
@@ -912,7 +1030,8 @@
     private void syncWallpaperOffsetWithScroll() {
         final boolean enableWallpaperEffects = isHardwareAccelerated();
         if (enableWallpaperEffects) {
-            mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
+            // TODO: figure out what to do about parallax, for now disable it
+            //mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
         }
     }
 
@@ -968,6 +1087,10 @@
         snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION);
     }
 
+    protected void snapToScreenId(long screenId, Runnable r) {
+        snapToPage(getPageIndexForScreenId(screenId), r);
+    }
+
     private void computeWallpaperScrollRatio(int page) {
         // Here, we determine what the desired scroll would be with and without a layout scale,
         // and compute a ratio between the two. This allows us to adjust the wallpaper offset
@@ -1558,7 +1681,7 @@
 
     private void initAnimationArrays() {
         final int childCount = getChildCount();
-        if (mOldTranslationXs != null) return;
+        if (mLastChildCount == childCount) return;
         mOldTranslationXs = new float[childCount];
         mOldTranslationYs = new float[childCount];
         mOldScaleXs = new float[childCount];
@@ -1958,16 +2081,15 @@
         showScrollingIndicator(false);
     }
 
-    void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, int screen,
+    void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId,
             int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
         View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
 
         final int[] cellXY = new int[2];
         target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
-        addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
+        addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
 
-        int adjustedScreen = screen - mNumPagesToLeft;
-        LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, adjustedScreen, cellXY[0],
+        LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, cellXY[0],
                 cellXY[1]);
     }
 
@@ -2122,7 +2244,7 @@
 
         if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
         mCreateUserFolderOnDrop = false;
-        final int screen = (targetCell == null) ? mDragInfo.screen : indexOfChild(target);
+        final long screenId = (targetCell == null) ? mDragInfo.screenId : getIdForScreen(target);
 
         boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
         boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
@@ -2140,7 +2262,7 @@
             target.removeView(v);
 
             FolderIcon fi =
-                mLauncher.addFolder(target, container, screen, targetCell[0], targetCell[1]);
+                mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
             destInfo.cellX = -1;
             destInfo.cellY = -1;
             sourceInfo.cellX = -1;
@@ -2215,8 +2337,8 @@
                 long container = hasMovedIntoHotseat ?
                         LauncherSettings.Favorites.CONTAINER_HOTSEAT :
                         LauncherSettings.Favorites.CONTAINER_DESKTOP;
-                int screen = (mTargetCell[0] < 0) ?
-                        mDragInfo.screen : indexOfChild(dropTargetLayout);
+                long screenId = (mTargetCell[0] < 0) ?
+                        mDragInfo.screenId : getIdForScreen(dropTargetLayout);
                 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
                 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
                 // First we find the cell nearest to point at which the item is
@@ -2267,9 +2389,9 @@
                             resultSpan[1]);
                 }
 
-                if (mCurrentPage != screen && !hasMovedIntoHotseat) {
-                    snapScreen = screen;
-                    snapToPage(screen);
+                if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
+                    snapScreen = getPageIndexForScreenId(screenId);
+                    snapToPage(snapScreen);
                 }
 
                 if (foundCell) {
@@ -2277,7 +2399,7 @@
                     if (hasMovedLayouts) {
                         // Reparent the view
                         getParentCellLayoutForView(cell).removeView(cell);
-                        addInScreen(cell, container, screen, mTargetCell[0], mTargetCell[1],
+                        addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
                                 info.spanX, info.spanY);
                     }
 
@@ -2288,7 +2410,7 @@
                     lp.cellHSpan = item.spanX;
                     lp.cellVSpan = item.spanY;
                     lp.isLockedToGrid = true;
-                    cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screen,
+                    cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screenId,
                             mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
 
                     if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
@@ -2319,10 +2441,7 @@
                         }
                     }
 
-                    //TODO: This is a hack on top of a hack, but items aren't being saved
-                    // to the correct screen due to the extra screen.
-                    int adjustedScreen = screen - mNumPagesToLeft;
-                    LauncherModel.moveItemInDatabase(mLauncher, info, container, adjustedScreen, lp.cellX,
+                    LauncherModel.moveItemInDatabase(mLauncher, info, container, screenId, lp.cellX,
                             lp.cellY);
                 } else {
                     // If we can't find a drop location, we return the item to its original position
@@ -2369,22 +2488,22 @@
         }
     }
 
-    public void setFinalScrollForPageChange(int screen) {
-        if (screen >= 0) {
+    public void setFinalScrollForPageChange(int pageIndex) {
+        CellLayout cl = (CellLayout) getChildAt(pageIndex);
+        if (cl != null) {
             mSavedScrollX = getScrollX();
-            CellLayout cl = (CellLayout) getChildAt(screen);
             mSavedTranslationX = cl.getTranslationX();
             mSavedRotationY = cl.getRotationY();
-            final int newX = getChildOffset(screen) - getRelativeChildOffset(screen);
+            final int newX = getChildOffset(pageIndex) - getRelativeChildOffset(pageIndex);
             setScrollX(newX);
             cl.setTranslationX(0f);
             cl.setRotationY(0f);
         }
     }
 
-    public void resetFinalScrollForPageChange(int screen) {
-        if (screen >= 0) {
-            CellLayout cl = (CellLayout) getChildAt(screen);
+    public void resetFinalScrollForPageChange(int pageIndex) {
+        if (pageIndex >= 0) {
+            CellLayout cl = (CellLayout) getChildAt(pageIndex);
             setScrollX(mSavedScrollX);
             cl.setTranslationX(mSavedTranslationX);
             cl.setRotationY(mSavedRotationY);
@@ -3047,10 +3166,11 @@
         final long container = mLauncher.isHotseatLayout(cellLayout) ?
                 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
                     LauncherSettings.Favorites.CONTAINER_DESKTOP;
-        final int screen = indexOfChild(cellLayout);
-        if (!mLauncher.isHotseatLayout(cellLayout) && screen != mCurrentPage
+        final long screenId = getIdForScreen(cellLayout);
+        if (!mLauncher.isHotseatLayout(cellLayout)
+                && screenId != getScreenIdForPageIndex(mCurrentPage)
                 && mState != State.SPRING_LOADED) {
-            snapToPage(screen);
+            snapToScreenId(screenId, null);
         }
 
         if (info instanceof PendingAddItemInfo) {
@@ -3101,11 +3221,11 @@
                         span[0] = item.spanX;
                         span[1] = item.spanY;
                         mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
-                                container, screen, mTargetCell, span, null);
+                                container, screenId, mTargetCell, span, null);
                         break;
                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                         mLauncher.processShortcutFromDrop(pendingInfo.componentName,
-                                container, screen, mTargetCell, null);
+                                container, screenId, mTargetCell, null);
                         break;
                     default:
                         throw new IllegalStateException("Unknown item type: " +
@@ -3177,14 +3297,13 @@
             } else {
                 cellLayout.findCellForSpan(mTargetCell, 1, 1);
             }
-            addInScreen(view, container, screen, mTargetCell[0], mTargetCell[1], info.spanX,
+            addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX,
                     info.spanY, insertAtFirst);
             cellLayout.onDropChild(view);
             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
             cellLayout.getShortcutsAndWidgets().measureChild(view);
 
-            int adjustedScreen = screen - mNumPagesToLeft;
-            LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, adjustedScreen,
+            LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
                     lp.cellX, lp.cellY);
 
             if (d.dragView != null) {
@@ -3400,7 +3519,7 @@
             if (mLauncher.isHotseatLayout(target)) {
                 cellLayout = mLauncher.getHotseat().getLayout();
             } else {
-                cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
+                cellLayout = getScreenWithId(mDragInfo.screenId);
             }
             cellLayout.onDropChild(mDragInfo.cell);
         }
@@ -3410,6 +3529,8 @@
         mDragOutline = null;
         mDragInfo = null;
 
+        stripEmptyScreens();
+
         // Hide the scrolling indicator after you pick up an item
         hideScrollingIndicator(false);
     }
@@ -3417,11 +3538,11 @@
     void updateItemLocationsInDatabase(CellLayout cl) {
         int count = cl.getShortcutsAndWidgets().getChildCount();
 
-        int screen = indexOfChild(cl);
+        long screenId = getIdForScreen(cl);
         int container = Favorites.CONTAINER_DESKTOP;
 
         if (mLauncher.isHotseatLayout(cl)) {
-            screen = -1;
+            screenId = -1;
             container = Favorites.CONTAINER_HOTSEAT;
         }
 
@@ -3431,8 +3552,7 @@
             // Null check required as the AllApps button doesn't have an item info
             if (info != null && info.requiresDbUpdate) {
                 info.requiresDbUpdate = false;
-                int adjustedScreen = screen - mNumPagesToLeft;
-                LauncherModel.modifyItemInDatabase(mLauncher, info, container, adjustedScreen, info.cellX,
+                LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX,
                         info.cellY, info.spanX, info.spanY);
             }
         }
@@ -3512,11 +3632,11 @@
     void saveWorkspaceScreenToDb(CellLayout cl) {
         int count = cl.getShortcutsAndWidgets().getChildCount();
 
-        int screen = indexOfChild(cl);
+        long screenId = getIdForScreen(cl);
         int container = Favorites.CONTAINER_DESKTOP;
 
         if (mLauncher.isHotseatLayout(cl)) {
-            screen = -1;
+            screenId = -1;
             container = Favorites.CONTAINER_HOTSEAT;
         }
 
@@ -3525,7 +3645,7 @@
             ItemInfo info = (ItemInfo) v.getTag();
             // Null check required as the AllApps button doesn't have an item info
             if (info != null) {
-                LauncherModel.addItemToDatabase(mLauncher, info, container, screen, info.cellX,
+                LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, info.cellX,
                         info.cellY, false);
             }
             if (v instanceof FolderIcon) {