Simplifying add to workspace by not going through the worker thread

Change-Id: I3260786bee257aea93ade49e6fa18008232addbe
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 85653be..8391980 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -3294,4 +3294,8 @@
     public boolean lastDownOnOccupiedCell() {
         return mLastDownOnOccupiedCell;
     }
+
+    public boolean findVacantCell(int spanX, int spanY, int[] outXY) {
+        return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8258984..2ef5d0a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -3702,19 +3702,6 @@
         }
     }
 
-    @Override
-    public void bindAddPendingItem(final PendingAddItemInfo info, final long container,
-            final long screenId, final int[] cell, final int spanX, final int spanY) {
-        showWorkspace(true, new Runnable() {
-
-            @Override
-            public void run() {
-                mWorkspace.snapToPage(mWorkspace.getPageIndexForScreenId(screenId));
-                addPendingItem(info, container, screenId, cell, spanX, spanY);
-            }
-        });
-    }
-
     private boolean shouldShowWeightWatcher() {
         String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
index a60e160..bb32fa1 100644
--- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
@@ -5,13 +5,13 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
-import com.android.launcher3.LauncherModel.ScreenPosProvider;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -19,11 +19,13 @@
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class LauncherAccessibilityDelegate extends AccessibilityDelegate {
 
-    public static final int REMOVE = R.id.action_remove;
-    public static final int INFO = R.id.action_info;
-    public static final int UNINSTALL = R.id.action_uninstall;
-    public static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
-    public static final int MOVE = R.id.action_move;
+    private static final String TAG = "LauncherAccessibilityDelegate";
+
+    private static final int REMOVE = R.id.action_remove;
+    private static final int INFO = R.id.action_info;
+    private static final int UNINSTALL = R.id.action_uninstall;
+    private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
+    private static final int MOVE = R.id.action_move;
 
     enum DragType {
         ICON,
@@ -39,8 +41,7 @@
 
     private DragInfo mDragInfo = null;
 
-    private final SparseArray<AccessibilityAction> mActions =
-            new SparseArray<AccessibilityAction>();
+    private final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
     @Thunk final Launcher mLauncher;
 
     public LauncherAccessibilityDelegate(Launcher launcher) {
@@ -56,7 +57,6 @@
                 launcher.getText(R.string.action_add_to_workspace)));
         mActions.put(MOVE, new AccessibilityAction(MOVE,
                 launcher.getText(R.string.action_move)));
-
     }
 
     @Override
@@ -93,7 +93,7 @@
         return super.performAccessibilityAction(host, action, args);
     }
 
-    public boolean performAction(View host, ItemInfo item, int action) {
+    public boolean performAction(View host, final ItemInfo item, int action) {
         if (action == REMOVE) {
             if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) {
                 announceConfirmation(R.string.item_removed);
@@ -108,32 +108,32 @@
         } else if (action == MOVE) {
             beginAccessibleDrag(host, item);
         } else if (action == ADD_TO_WORKSPACE) {
-            final int preferredPage = mLauncher.getWorkspace().getCurrentPage();
-            final ScreenPosProvider screenProvider = new ScreenPosProvider() {
+            final int[] coordinates = new int[2];
+            final long screenId = findSpaceOnWorkspace(item, coordinates);
+            mLauncher.showWorkspace(true, new Runnable() {
 
                 @Override
-                public int getScreenIndex(ArrayList<Long> screenIDs) {
-                    return preferredPage;
-                }
-            };
-            if (item instanceof AppInfo) {
-                final ArrayList<ItemInfo> addShortcuts = new ArrayList<ItemInfo>();
-                addShortcuts.add(((AppInfo) item).makeShortcut());
-                mLauncher.showWorkspace(true, new Runnable() {
-                    @Override
-                    public void run() {
-                        mLauncher.getModel().addAndBindAddedWorkspaceItems(
-                                mLauncher, addShortcuts, screenProvider, 0, true);
-                        announceConfirmation(R.string.item_added_to_workspace);
+                public void run() {
+                    if (item instanceof AppInfo) {
+                        ShortcutInfo info = ((AppInfo) item).makeShortcut();
+                        LauncherModel.addItemToDatabase(mLauncher, info,
+                                LauncherSettings.Favorites.CONTAINER_DESKTOP,
+                                screenId, coordinates[0], coordinates[1]);
+
+                        ArrayList<ItemInfo> itemList = new ArrayList<>();
+                        itemList.add(info);
+                        mLauncher.bindItems(itemList, 0, itemList.size(), true);
+                    } else if (item instanceof PendingAddItemInfo) {
+                        PendingAddItemInfo info = (PendingAddItemInfo) item;
+                        Workspace workspace = mLauncher.getWorkspace();
+                        workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
+                        mLauncher.addPendingItem(info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
+                                screenId, coordinates, info.spanX, info.spanY);
                     }
-                });
-                return true;
-            } else if (item instanceof PendingAddItemInfo) {
-                mLauncher.getModel().addAndBindPendingItem(
-                        mLauncher, (PendingAddItemInfo) item, screenProvider, 0);
-                announceConfirmation(R.string.item_added_to_workspace);
-                return true;
-            }
+                    announceConfirmation(R.string.item_added_to_workspace);
+                }
+            });
+            return true;
         }
         return false;
     }
@@ -220,4 +220,41 @@
         mDragInfo = null;
         mLauncher.getWorkspace().enableAccessibleDrag(false);
     }
+
+    /**
+     * Find empty space on the workspace and returns the screenId.
+     */
+    private long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
+        Workspace workspace = mLauncher.getWorkspace();
+        ArrayList<Long> workspaceScreens = workspace.getScreenOrder();
+        long screenId;
+
+        // First check if there is space on the current screen.
+        int screenIndex = workspace.getCurrentPage();
+        screenId = workspaceScreens.get(screenIndex);
+        CellLayout layout = (CellLayout) workspace.getPageAt(screenIndex);
+
+        boolean found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
+        screenIndex = workspace.hasCustomContent() ? 1 : 0;
+        while (!found && screenIndex < workspaceScreens.size()) {
+            screenId = workspaceScreens.get(screenIndex);
+            layout = (CellLayout) workspace.getPageAt(screenIndex);
+            found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
+            screenIndex++;
+        }
+
+        if (found) {
+            return screenId;
+        }
+
+        workspace.addExtraEmptyScreen();
+        screenId = workspace.commitExtraEmptyScreen();
+        layout = workspace.getScreenWithId(screenId);
+        found = layout.findCellForSpan(outCoordinates, info.spanX, info.spanY);
+
+        if (!found) {
+            Log.wtf(TAG, "Not enough space on an empty screen");
+        }
+        return screenId;
+    }
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 00afd98..97a2830 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -35,7 +35,6 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Environment;
@@ -203,18 +202,12 @@
         public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
         public void dumpLogsToLocalData();
-        public void bindAddPendingItem(PendingAddItemInfo info, long container, long screenId,
-                int[] cell, int spanX, int spanY);
     }
 
     public interface ItemInfoFilter {
         public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
     }
 
-    public interface ScreenPosProvider {
-        int getScreenIndex(ArrayList<Long> screenIDs);
-    }
-
     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
         Context context = app.getContext();
 
@@ -406,19 +399,7 @@
         runOnWorkerThread(r);
     }
 
-    public void addAndBindAddedWorkspaceItems(final Context context,
-            final ArrayList<ItemInfo> workspaceApps) {
-        addAndBindAddedWorkspaceItems(context, workspaceApps,
-                new ScreenPosProvider() {
-
-                    @Override
-                    public int getScreenIndex(ArrayList<Long> screenIDs) {
-                        return screenIDs.isEmpty() ? 0 : 1;
-                    }
-                }, 1, false);
-    }
-
-    private static boolean findNextAvailableIconSpaceInScreen(ArrayList<Rect> occupiedPos,
+    private static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> occupiedPos,
             int[] xy, int spanX, int spanY) {
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
@@ -426,9 +407,11 @@
         final int yCount = (int) grid.numRows;
         boolean[][] occupied = new boolean[xCount][yCount];
         if (occupiedPos != null) {
-            for (Rect r : occupiedPos) {
-                for (int x = r.left; 0 <= x && x < r.right && x < xCount; x++) {
-                    for (int y = r.top; 0 <= y && y < r.bottom && y < yCount; y++) {
+            for (ItemInfo r : occupiedPos) {
+                int right = r.cellX + r.spanX;
+                int bottom = r.cellY + r.spanY;
+                for (int x = r.cellX; 0 <= x && x < right && x < xCount; x++) {
+                    for (int y = r.cellY; 0 <= y && y < bottom && y < yCount; y++) {
                         occupied[x][y] = true;
                     }
                 }
@@ -443,53 +426,24 @@
      */
     @Thunk static Pair<Long, int[]> findSpaceForItem(
             Context context,
-            ScreenPosProvider preferredScreen,
-            int fallbackStartScreen,
             ArrayList<Long> workspaceScreens,
             ArrayList<Long> addedWorkspaceScreensFinal,
             int spanX, int spanY) {
-        // Load position of items which are on the desktop. We can't use sBgItemsIdMap because
-        // loadWorkspace() may not have been called.
-        final ContentResolver cr = context.getContentResolver();
-        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[] {
-                    LauncherSettings.Favorites.SCREEN,
-                    LauncherSettings.Favorites.CELLX,
-                    LauncherSettings.Favorites.CELLY,
-                    LauncherSettings.Favorites.SPANX,
-                    LauncherSettings.Favorites.SPANY,
-                    LauncherSettings.Favorites.CONTAINER
-                 },
-                 "container=?",
-                 new String[] { Integer.toString(LauncherSettings.Favorites.CONTAINER_DESKTOP) },
-                 null);
+        LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
 
-        final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
-        final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
-        final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
-        final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
-        final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
-        LongSparseArray<ArrayList<Rect>> screenItems = new LongSparseArray<ArrayList<Rect>>();
-        try {
-            while (c.moveToNext()) {
-                Rect rect = new Rect();
-                rect.left = c.getInt(cellXIndex);
-                rect.top = c.getInt(cellYIndex);
-                rect.right = rect.left + Math.max(1, c.getInt(spanXIndex));
-                rect.bottom = rect.top + Math.max(1, c.getInt(spanYIndex));
-
-                long screenId = c.getInt(screenIndex);
-                ArrayList<Rect> items = screenItems.get(screenId);
-                if (items == null) {
-                    items = new ArrayList<Rect>();
-                    screenItems.put(screenId, items);
+        // Use sBgItemsIdMap as all the items are already loaded.
+        // TODO: Throw exception is above condition is not met.
+        synchronized (sBgLock) {
+            for (ItemInfo info : sBgItemsIdMap) {
+                if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                    ArrayList<ItemInfo> items = screenItems.get(info.screenId);
+                    if (items == null) {
+                        items = new ArrayList<>();
+                        screenItems.put(info.screenId, items);
+                    }
+                    items.add(info);
                 }
-                items.add(rect);
             }
-        } catch (Exception e) {
-            screenItems.clear();
-        } finally {
-            c.close();
         }
 
         // Find appropriate space for the item.
@@ -499,7 +453,7 @@
 
         int screenCount = workspaceScreens.size();
         // First check the preferred screen.
-        int preferredScreenIndex = preferredScreen.getScreenIndex(workspaceScreens);
+        int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
         if (preferredScreenIndex < screenCount) {
             screenId = workspaceScreens.get(preferredScreenIndex);
             found = findNextAvailableIconSpaceInScreen(
@@ -507,8 +461,8 @@
         }
 
         if (!found) {
-            // Search on any of the screens.
-            for (int screen = fallbackStartScreen; screen < screenCount; screen++) {
+            // Search on any of the screens starting from the first screen.
+            for (int screen = 1; screen < screenCount; screen++) {
                 screenId = workspaceScreens.get(screen);
                 if (findNextAvailableIconSpaceInScreen(
                         screenItems.get(screenId), cordinates, spanX, spanY)) {
@@ -538,59 +492,9 @@
 
     /**
      * Adds the provided items to the workspace.
-     * @param preferredScreen the screen where we should try to add the app first
-     * @param fallbackStartScreen the screen to start search for empty space if
-     * preferredScreen is not available.
-     */
-    public void addAndBindPendingItem(
-            final Context context,
-            final PendingAddItemInfo addInfo,
-            final ScreenPosProvider preferredScreen,
-            final int fallbackStartScreen) {
-        final Callbacks callbacks = getCallback();
-        // Process the newly added applications and add them to the database first
-        Runnable r = new Runnable() {
-            public void run() {
-                final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
-                ArrayList<Long> workspaceScreens = loadWorkspaceScreensDb(context);
-
-                // Find appropriate space for the item.
-                Pair<Long, int[]> coords = findSpaceForItem(context, preferredScreen,
-                        fallbackStartScreen, workspaceScreens, addedWorkspaceScreensFinal,
-                        addInfo.spanX,
-                        addInfo.spanY);
-                final long screenId = coords.first;
-                final int[] cordinates = coords.second;
-
-                // Update the workspace screens
-                updateWorkspaceScreenOrder(context, workspaceScreens);
-                runOnMainThread(new Runnable() {
-                    public void run() {
-                        Callbacks cb = getCallback();
-                        if (callbacks == cb && cb != null) {
-                            cb.bindAddScreens(addedWorkspaceScreensFinal);
-                            cb.bindAddPendingItem(addInfo,
-                                    LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                                    screenId, cordinates, addInfo.spanX, addInfo.spanY);
-                        }
-                    }
-                });
-            }
-        };
-        runOnWorkerThread(r);
-    }
-
-    /**
-     * Adds the provided items to the workspace.
-     * @param preferredScreen the screen where we should try to add the app first
-     * @param fallbackStartScreen the screen to start search for empty space if
-     * preferredScreen is not available.
      */
     public void addAndBindAddedWorkspaceItems(final Context context,
-            final ArrayList<ItemInfo> workspaceApps,
-            final ScreenPosProvider preferredScreen,
-            final int fallbackStartScreen,
-            final boolean allowDuplicate) {
+            final ArrayList<ItemInfo> workspaceApps) {
         final Callbacks callbacks = getCallback();
         if (workspaceApps.isEmpty()) {
             return;
@@ -607,7 +511,7 @@
                 ArrayList<Long> workspaceScreens = loadWorkspaceScreensDb(context);
                 synchronized(sBgLock) {
                     for (ItemInfo item : workspaceApps) {
-                        if (!allowDuplicate && item instanceof ShortcutInfo) {
+                        if (item instanceof ShortcutInfo) {
                             // Short-circuit this logic if the icon exists somewhere on the workspace
                             if (shortcutExists(context, item.getIntent(), item.user)) {
                                 continue;
@@ -615,8 +519,8 @@
                         }
 
                         // Find appropriate space for the item.
-                        Pair<Long, int[]> coords = findSpaceForItem(context, preferredScreen,
-                                fallbackStartScreen, workspaceScreens, addedWorkspaceScreensFinal,
+                        Pair<Long, int[]> coords = findSpaceForItem(context,
+                                workspaceScreens, addedWorkspaceScreensFinal,
                                 1, 1);
                         long screenId = coords.first;
                         int[] cordinates = coords.second;