Adding support for drag and drop of folders and shortcuts. 

also:
- Long press on empty space on workspace now brings up customization tray
- Fixed: while dragging, items appeared to be dropping on folders two cells to the right
- Fixed: Disabling drops on folders when the workspace is shrunken
- Fixed: account for scaling of dragged items when checking if they overlap
          with shrunken workspace screens
- Making folder icons dimmable to match shortcuts and widgets
- When deciding with shrunken workspace screen we're dragging to, we now use the closest screen rather than the one that has been overlapped the most
- Refactored drag/add mechanism, removing array of occupied cells from CellInfo
- Removed dead code/variables
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index 04e1cd9..3c39474 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -511,4 +511,8 @@
     @Override
     public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {}
+
+    public boolean isDropEnabled() {
+        return true;
+    }
 }
diff --git a/src/com/android/launcher2/ApplicationInfoDropTarget.java b/src/com/android/launcher2/ApplicationInfoDropTarget.java
index 0e342a7..cb45b3a 100644
--- a/src/com/android/launcher2/ApplicationInfoDropTarget.java
+++ b/src/com/android/launcher2/ApplicationInfoDropTarget.java
@@ -110,6 +110,10 @@
         }
     }
 
+    public boolean isDropEnabled() {
+        return true;
+    }
+
     public void onDragEnd() {
         if (mActive) {
             mActive = false;
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 4d1c299..19b5852 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -40,8 +40,6 @@
 public class CellLayout extends ViewGroup {
     static final String TAG = "CellLayout";
 
-    private boolean mPortrait;
-
     private int mCellWidth;
     private int mCellHeight;
 
@@ -90,9 +88,6 @@
     // When a drag operation is in progress, holds the nearest cell to the touch point
     private final int[] mDragCell = new int[2];
 
-    private boolean mDirtyTag;
-    private boolean mLastDownOnOccupiedCell = false;
-
     private final WallpaperManager mWallpaperManager;
 
     public CellLayout(Context context) {
@@ -135,6 +130,7 @@
 
         mCountX = LauncherModel.getCellCountX();
         mCountY = LauncherModel.getCellCountY();
+        mOccupied = new boolean[mCountX][mCountY];
 
         a.recycle();
 
@@ -214,15 +210,60 @@
             // We might be in the middle or end of shrinking/fading to a dimmed view
             // Make sure this view's alpha is set the same as all the rest of the views
             child.setAlpha(getAlpha());
-
             addView(child, index, lp);
 
+            markCellsAsOccupiedForView(child);
+
             return true;
         }
         return false;
     }
 
     @Override
+    public void removeAllViews() {
+        clearOccupiedCells();
+    }
+
+    @Override
+    public void removeAllViewsInLayout() {
+        clearOccupiedCells();
+    }
+
+    @Override
+    public void removeView(View view) {
+        markCellsAsUnoccupiedForView(view);
+        super.removeView(view);
+    }
+
+    @Override
+    public void removeViewAt(int index) {
+        markCellsAsUnoccupiedForView(getChildAt(index));
+        super.removeViewAt(index);
+    }
+
+    @Override
+    public void removeViewInLayout(View view) {
+        markCellsAsUnoccupiedForView(view);
+        super.removeViewInLayout(view);
+    }
+
+    @Override
+    public void removeViews(int start, int count) {
+        for (int i = start; i < start + count; i++) {
+            markCellsAsUnoccupiedForView(getChildAt(i));
+        }
+        super.removeViews(start, count);
+    }
+
+    @Override
+    public void removeViewsInLayout(int start, int count) {
+        for (int i = start; i < start + count; i++) {
+            markCellsAsUnoccupiedForView(getChildAt(i));
+        }
+        super.removeViewsInLayout(start, count);
+    }
+
+    @Override
     public void requestChildFocus(View child, View focused) {
         super.requestChildFocus(child, focused);
         if (child != null) {
@@ -258,45 +299,24 @@
                     cellInfo.cellY = lp.cellY;
                     cellInfo.spanX = lp.cellHSpan;
                     cellInfo.spanY = lp.cellVSpan;
-                    cellInfo.intersectX = lp.cellX;
-                    cellInfo.intersectY = lp.cellY;
                     cellInfo.valid = true;
                     found = true;
-                    mDirtyTag = false;
                     break;
                 }
             }
         }
 
-        mLastDownOnOccupiedCell = found;
-
         if (!found) {
             final int cellXY[] = mTmpCellXY;
             pointToCellExact(x, y, cellXY);
 
-            final boolean portrait = mPortrait;
-            final int xCount = mCountX;
-            final int yCount = mCountY;
-
-            final boolean[][] occupied = mOccupied;
-            findOccupiedCells(xCount, yCount, occupied, null, true);
-
             cellInfo.cell = null;
             cellInfo.cellX = cellXY[0];
             cellInfo.cellY = cellXY[1];
             cellInfo.spanX = 1;
             cellInfo.spanY = 1;
-            cellInfo.intersectX = cellXY[0];
-            cellInfo.intersectY = cellXY[1];
-            cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
-                    cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
-
-            // Instead of finding the interesting vacant cells here, wait until a
-            // caller invokes getTag() to retrieve the result. Finding the vacant
-            // cells is a bit expensive and can generate many new objects, it's
-            // therefore better to defer it until we know we actually need it.
-
-            mDirtyTag = true;
+            cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < mCountX &&
+                    cellXY[1] < mCountY && !mOccupied[cellXY[0]][cellXY[1]];
         }
         setTag(cellInfo);
     }
@@ -319,7 +339,6 @@
             cellInfo.spanX = 0;
             cellInfo.spanY = 0;
             cellInfo.valid = false;
-            mDirtyTag = false;
             setTag(cellInfo);
         }
 
@@ -328,31 +347,7 @@
 
     @Override
     public CellInfo getTag() {
-        final CellInfo info = (CellInfo) super.getTag();
-        if (mDirtyTag && info.valid) {
-            final int xCount = mCountX;
-            final int yCount = mCountY;
-
-            final boolean[][] occupied = mOccupied;
-            findOccupiedCells(xCount, yCount, occupied, null, true);
-
-            info.updateOccupiedCells(occupied, mCountX, mCountY);
-
-            mDirtyTag = false;
-        }
-        return info;
-    }
-
-    /**
-     * Check if the column 'x' is empty from rows 'top' to 'bottom', inclusive.
-     */
-    private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
-        for (int y = top; y <= bottom; y++) {
-            if (occupied[x][y]) {
-                return false;
-            }
-        }
-        return true;
+        return (CellInfo) super.getTag();
     }
 
     /**
@@ -367,42 +362,6 @@
         return true;
     }
 
-    CellInfo updateOccupiedCells(boolean[] occupiedCells, View ignoreView) {
-        final int xCount = mCountX;
-        final int yCount = mCountY;
-
-        boolean[][] occupied = mOccupied;
-
-        if (occupiedCells != null) {
-            for (int y = 0; y < yCount; y++) {
-                for (int x = 0; x < xCount; x++) {
-                    occupied[x][y] = occupiedCells[y * xCount + x];
-                }
-            }
-        } else {
-            findOccupiedCells(xCount, yCount, occupied, ignoreView, true);
-        }
-
-        CellInfo cellInfo = new CellInfo();
-
-        cellInfo.cellX = -1;
-        cellInfo.cellY = -1;
-        cellInfo.intersectX = -1;
-        cellInfo.intersectY = -1;
-        cellInfo.spanY = 0;
-        cellInfo.spanX = 0;
-        cellInfo.screen = mCellInfo.screen;
-
-        cellInfo.updateOccupiedCells(occupied, mCountX, mCountY);
-
-        cellInfo.valid = cellInfo.existsEmptyCell();
-
-        // Assume the caller will perform their own cell searching, otherwise we
-        // risk causing an unnecessary rebuild after findCellForSpan()
-
-        return cellInfo;
-    }
-
     /**
      * Given a point, return the cell that strictly encloses that point
      * @param x X coordinate of the point
@@ -492,19 +451,13 @@
         final int cellWidth = mCellWidth;
         final int cellHeight = mCellHeight;
 
-        if (mOccupied == null) {
-            mOccupied = new boolean[mCountX][mCountY];
-        }
-
         int numWidthGaps = mCountX - 1;
         int numHeightGaps = mCountY - 1;
 
-        int vSpaceLeft = heightSpecSize - mTopPadding
-                - mBottomPadding - (cellHeight * mCountY);
+        int vSpaceLeft = heightSpecSize - mTopPadding - mBottomPadding - (cellHeight * mCountY);
         mHeightGap = vSpaceLeft / numHeightGaps;
 
-        int hSpaceLeft = widthSpecSize - mLeftPadding
-                - mRightPadding - (cellWidth * mCountX);
+        int hSpaceLeft = widthSpecSize - mLeftPadding - mRightPadding - (cellWidth * mCountX);
         mWidthGap = hSpaceLeft / numWidthGaps;
 
         // center it around the min gaps
@@ -519,8 +472,7 @@
             lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
                     mLeftPadding, mTopPadding);
 
-            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
-                    MeasureSpec.EXACTLY);
+            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
             int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
                     MeasureSpec.EXACTLY);
 
@@ -538,7 +490,7 @@
     }
 
     @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+    public void onLayout(boolean changed, int l, int t, int r, int b) {
         int count = getChildCount();
 
         for (int i = 0; i < count; i++) {
@@ -621,15 +573,29 @@
         }
     }
 
-    private boolean isVacant(int originX, int originY, int spanX, int spanY) {
+    private boolean isVacantIgnoring(
+            int originX, int originY, int spanX, int spanY, View ignoreView) {
+        if (ignoreView != null) {
+            markCellsAsUnoccupiedForView(ignoreView);
+        }
         for (int i = 0; i < spanY; i++) {
             if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
+                if (ignoreView != null) {
+                    markCellsAsOccupiedForView(ignoreView);
+                }
                 return false;
             }
         }
+        if (ignoreView != null) {
+            markCellsAsOccupiedForView(ignoreView);
+        }
         return true;
     }
 
+    private boolean isVacant(int originX, int originY, int spanX, int spanY) {
+        return isVacantIgnoring(originX, originY, spanX, spanY, null);
+    }
+
     public View getChildAt(int x, int y) {
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
@@ -687,7 +653,8 @@
         result[1] = Math.max(0, result[1]); // Snap to top
     }
 
-    void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) {
+    void visualizeDropLocation(
+            View view, int originX, int originY, int spanX, int spanY, View draggedItem) {
         final int[] originCell = mDragCell;
         final int[] cellXY = mTmpCellXY;
         estimateDropCell(originX, originY, spanX, spanY, cellXY);
@@ -709,12 +676,8 @@
             bottomRight[0] += mCellWidth;
             bottomRight[1] += mCellHeight;
 
-            final int countX = mCountX;
-            final int countY = mCountY;
-            // TODO: It's not necessary to do this every time, but it's not especially expensive
-            findOccupiedCells(countX, countY, mOccupied, view, false);
-
-            boolean vacant = isVacant(originCell[0], originCell[1], spanX, spanY);
+            boolean vacant =
+                isVacantIgnoring(originCell[0], originCell[1], spanX, spanY, draggedItem);
             mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable;
 
             // mDragRect will be rendered in onDraw()
@@ -736,8 +699,7 @@
      * @return The X, Y cell of a vacant area that can contain this object,
      *         nearest the requested location.
      */
-    int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
-            CellInfo vacantCells, int[] recycle) {
+    int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, int[] recycle) {
 
         // Keep track of best-scoring drop area
         final int[] bestXY = recycle != null ? recycle : new int[2];
@@ -777,10 +739,130 @@
         }
     }
 
+    boolean existsEmptyCell() {
+        return findCellForSpan(null, 1, 1);
+    }
+
     /**
-     * Called when a drag and drop operation has finished (successfully or not).
+     * Finds the upper-left coordinate of the first rectangle in the grid that can
+     * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
+     * then this method will only return coordinates for rectangles that contain the cell
+     * (intersectX, intersectY)
+     *
+     * @param cellXY The array that will contain the position of a vacant cell if such a cell
+     *               can be found.
+     * @param spanX The horizontal span of the cell we want to find.
+     * @param spanY The vertical span of the cell we want to find.
+     *
+     * @return True if a vacant cell of the specified dimension was found, false otherwise.
      */
-    void onDragComplete() {
+    boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
+        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null);
+    }
+
+    /**
+     * Like above, but ignores any cells occupied by the item "ignoreView"
+     *
+     * @param cellXY The array that will contain the position of a vacant cell if such a cell
+     *               can be found.
+     * @param spanX The horizontal span of the cell we want to find.
+     * @param spanY The vertical span of the cell we want to find.
+     * @param ignoreView The home screen item we should treat as not occupying any space
+     * @return
+     */
+    boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
+        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, ignoreView);
+    }
+
+    /**
+     * Like above, but if intersectX and intersectY are not -1, then this method will try to
+     * return coordinates for rectangles that contain the cell [intersectX, intersectY]
+     *
+     * @param spanX The horizontal span of the cell we want to find.
+     * @param spanY The vertical span of the cell we want to find.
+     * @param ignoreView The home screen item we should treat as not occupying any space
+     * @param intersectX The X coordinate of the cell that we should try to overlap
+     * @param intersectX The Y coordinate of the cell that we should try to overlap
+     *
+     * @return True if a vacant cell of the specified dimension was found, false otherwise.
+     */
+    boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
+            int intersectX, int intersectY) {
+        return findCellForSpanThatIntersectsIgnoring(
+                cellXY, spanX, spanY, intersectX, intersectY, null);
+    }
+
+    /**
+     * The superset of the above two methods
+     */
+    boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
+            int intersectX, int intersectY, View ignoreView) {
+        if (ignoreView != null) {
+            markCellsAsUnoccupiedForView(ignoreView);
+        }
+
+        while (true) {
+            int startX = 0;
+            if (intersectX >= 0) {
+                startX = Math.max(startX, intersectX - (spanX - 1));
+            }
+            int endX = mCountX - (spanX - 1);
+            if (intersectX >= 0) {
+                endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0));
+            }
+            int startY = 0;
+            if (intersectY >= 0) {
+                startY = Math.max(startY, intersectY - (spanY - 1));
+            }
+            int endY = mCountY - (spanY - 1);
+            if (intersectY >= 0) {
+                endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0));
+            }
+
+            for (int x = startX; x < endX; x++) {
+                inner:
+                for (int y = startY; y < endY; y++) {
+                    for (int i = 0; i < spanX; i++) {
+                        for (int j = 0; j < spanY; j++) {
+                            if (mOccupied[x + i][y + j]) {
+                                // small optimization: we can skip to below the row we just found
+                                // an occupied cell
+                                y += j;
+                                continue inner;
+                            }
+                        }
+                    }
+                    if (cellXY != null) {
+                        cellXY[0] = x;
+                        cellXY[1] = y;
+                    }
+                    if (ignoreView != null) {
+                        markCellsAsOccupiedForView(ignoreView);
+                    }
+                    return true;
+                }
+            }
+            if (intersectX == -1 && intersectY == -1) {
+                break;
+            } else {
+                // if we failed to find anything, try again but without any requirements of
+                // intersecting
+                intersectX = -1;
+                intersectY = -1;
+                continue;
+            }
+        }
+
+        if (ignoreView != null) {
+            markCellsAsOccupiedForView(ignoreView);
+        }
+        return false;
+    }
+
+    /**
+     * Called when drag has left this CellLayout or has been completed (successfully or not)
+     */
+    void onDragExit() {
         // Invalidate the drag data
         mDragCell[0] = -1;
         mDragCell[1] = -1;
@@ -803,14 +885,14 @@
             mDragRect.setEmpty();
             child.requestLayout();
         }
-        onDragComplete();
+        onDragExit();
     }
 
     void onDropAborted(View child) {
         if (child != null) {
             ((LayoutParams) child.getLayoutParams()).isDragging = false;
         }
-        onDragComplete();
+        onDragExit();
     }
 
     /**
@@ -889,13 +971,8 @@
      * @return True if a vacant cell was found
      */
     public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
-        final int xCount = mCountX;
-        final int yCount = mCountY;
-        final boolean[][] occupied = mOccupied;
 
-        findOccupiedCells(xCount, yCount, occupied, null, true);
-
-        return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
+        return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied);
     }
 
     static boolean findVacantCell(int[] vacant, int spanX, int spanY,
@@ -930,8 +1007,6 @@
         final int yCount = mCountY;
         final boolean[][] occupied = mOccupied;
 
-        findOccupiedCells(xCount, yCount, occupied, null, true);
-
         final boolean[] flat = new boolean[xCount * yCount];
         for (int y = 0; y < yCount; y++) {
             for (int x = 0; x < xCount; x++) {
@@ -942,32 +1017,34 @@
         return flat;
     }
 
-    /**
-     * Update the array of occupied cells.
-     * @param ignoreView If non-null, the space occupied by this View is treated as vacant
-     * @param ignoreFolders If true, a cell occupied by a Folder is treated as vacant
-     */
-    private void findOccupiedCells(
-            int xCount, int yCount, boolean[][] occupied, View ignoreView, boolean ignoreFolders) {
-
-        for (int x = 0; x < xCount; x++) {
-            for (int y = 0; y < yCount; y++) {
-                occupied[x][y] = false;
+    private void clearOccupiedCells() {
+        for (int x = 0; x < mCountX; x++) {
+            for (int y = 0; y < mCountY; y++) {
+                mOccupied[x][y] = false;
             }
         }
+    }
 
-        int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            if ((ignoreFolders && child instanceof Folder) || child.equals(ignoreView)) {
-                continue;
-            }
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+    public void onMove(View view, int newCellX, int newCellY) {
+        LayoutParams lp = (LayoutParams) view.getLayoutParams();
+        markCellsAsUnoccupiedForView(view);
+        markCellsForView(newCellX, newCellY, lp.cellHSpan, lp.cellVSpan, true);
+    }
 
-            for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
-                for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
-                    occupied[x][y] = true;
-                }
+    private void markCellsAsOccupiedForView(View view) {
+        LayoutParams lp = (LayoutParams) view.getLayoutParams();
+        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
+    }
+
+    private void markCellsAsUnoccupiedForView(View view) {
+        LayoutParams lp = (LayoutParams) view.getLayoutParams();
+        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
+    }
+
+    private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean value) {
+        for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
+            for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
+                mOccupied[x][y] = value;
             }
         }
     }
@@ -1087,117 +1164,25 @@
         }
     }
 
+    // This class stores info for two purposes:
+    // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY,
+    //    its spanX, spanY, and the screen it is on
+    // 2. When long clicking on an empty cell in a CellLayout, we save information about the
+    //    cellX and cellY coordinates and which page was clicked. We then set this as a tag on
+    //    the CellLayout that was long clicked
     static final class CellInfo implements ContextMenu.ContextMenuInfo {
-        private boolean[][] mOccupied;
-        private int mCountX;
-        private int mCountY;
         View cell;
         int cellX = -1;
         int cellY = -1;
-        // intersectX and intersectY constrain the results of findCellForSpan; any empty space
-        // it results must include this point (unless intersectX and intersectY are -1)
-        int intersectX = -1;
-        int intersectY = -1;
         int spanX;
         int spanY;
         int screen;
         boolean valid;
 
-        void updateOccupiedCells(boolean[][] occupied, int xCount, int yCount) {
-            mOccupied = occupied.clone();
-            mCountX = xCount;
-            mCountY = yCount;
-        }
-
-        void updateOccupiedCells(boolean[] occupied, int xCount, int yCount) {
-            if (mOccupied == null || mCountX != xCount || mCountY != yCount) {
-                mOccupied = new boolean[xCount][yCount];
-            }
-            mCountX = xCount;
-            mCountY = yCount;
-            for (int y = 0; y < yCount; y++) {
-                for (int x = 0; x < xCount; x++) {
-                    mOccupied[x][y] = occupied[y * xCount + x];
-                }
-            }
-        }
-
-        boolean existsEmptyCell() {
-            return findCellForSpan(null, 1, 1);
-        }
-        /**
-         * Finds the upper-left coordinate of the first rectangle in the grid that can
-         * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
-         * then this method will only return coordinates for rectangles that contain the cell
-         * (intersectX, intersectY)
-         *
-         * @param cellXY The array that will contain the position of a vacant cell if such a cell
-         *               can be found.
-         * @param spanX The horizontal span of the cell we want to find.
-         * @param spanY The vertical span of the cell we want to find.
-         *
-         * @return True if a vacant cell of the specified dimension was found, false otherwise.
-         */
-        boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
-            // return the span represented by the CellInfo only there is no view there
-            //   (this.cell == null) and there is enough space
-
-            if (this.cell == null && this.spanX >= spanX && this.spanY >= spanY) {
-                if (cellXY != null) {
-                    cellXY[0] = cellX;
-                    cellXY[1] = cellY;
-                }
-                return true;
-            }
-
-            int startX = 0;
-            if (intersectX >= 0) {
-                startX = Math.max(startX, intersectX - (spanX - 1));
-            }
-            int endX = mCountX - (spanX - 1);
-            if (intersectX >= 0) {
-                endX = Math.min(endX, intersectX + (spanX - 1));
-            }
-            int startY = 0;
-            if (intersectY >= 0) {
-                startY = Math.max(startY, intersectY - (spanY - 1));
-            }
-            int endY = mCountY - (spanY - 1);
-            if (intersectY >= 0) {
-                endY = Math.min(endY, intersectY + (spanY - 1));
-            }
-
-            for (int x = startX; x < endX; x++) {
-                inner:
-                for (int y = startY; y < endY; y++) {
-                    for (int i = 0; i < spanX; i++) {
-                        for (int j = 0; j < spanY; j++) {
-                            if (mOccupied[x + i][y + j]) {
-                                // small optimization: we can skip to below the row we just found
-                                // an occupied cell
-                                y += j;
-                                continue inner;
-                            }
-                        }
-                    }
-                    if (cellXY != null) {
-                        cellXY[0] = x;
-                        cellXY[1] = y;
-                    }
-                    return true;
-                }
-            }
-            return false;
-        }
-
         @Override
         public String toString() {
             return "Cell[view=" + (cell == null ? "null" : cell.getClass())
                     + ", x=" + cellX + ", y=" + cellY + "]";
         }
     }
-
-    public boolean lastDownOnOccupiedCell() {
-        return mLastDownOnOccupiedCell;
-    }
 }
diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
index ead258c..79b3e6f 100644
--- a/src/com/android/launcher2/CustomizePagedView.java
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -16,10 +16,7 @@
 
 package com.android.launcher2;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
+import com.android.launcher.R;
 
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -28,13 +25,12 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.graphics.Bitmap.Config;
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.provider.LiveFolders;
@@ -44,12 +40,14 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import com.android.launcher.R;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
 
 public class CustomizePagedView extends PagedView
     implements View.OnLongClickListener, View.OnClickListener,
@@ -354,6 +352,7 @@
         }
 
         final View animView = v;
+        PendingAddItemInfo createItemInfo = new PendingAddItemInfo();
         switch (mCustomizationType) {
         case WidgetCustomization:
             // Get the icon as the drag representation
@@ -365,58 +364,35 @@
             icon.draw(c);
 
             AppWidgetProviderInfo appWidgetInfo = (AppWidgetProviderInfo) v.getTag();
-            LauncherAppWidgetInfo dragInfo = new LauncherAppWidgetInfo(appWidgetInfo.provider);
-            dragInfo.minWidth = appWidgetInfo.minWidth;
-            dragInfo.minHeight = appWidgetInfo.minHeight;
-            mDragController.startDrag(v, b, this, dragInfo, DragController.DRAG_ACTION_COPY, null);
+            createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+            createItemInfo.componentName = appWidgetInfo.provider;
+            mDragController.startDrag(v, b, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
 
             // Cleanup the icon
             b.recycle();
             return true;
         case FolderCustomization:
-            // animate some feedback to the long press
-            animateClickFeedback(v, new Runnable() {
-                @Override
-                public void run() {
-                    // add the folder
-                    ResolveInfo resolveInfo = (ResolveInfo) animView.getTag();
-                    Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
-                    if (resolveInfo.labelRes == R.string.group_folder) {
-                        // Create app shortcuts is a special built-in case of shortcuts
-                        createFolderIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME,
-                            getContext().getString(R.string.group_folder));
-                    } else {
-                        ComponentName name = new ComponentName(resolveInfo.activityInfo.packageName,
-                                resolveInfo.activityInfo.name);
-                        createFolderIntent.setComponent(name);
-                    }
-                    mLauncher.prepareAddItemFromHomeCustomizationDrawer();
-                    mLauncher.addLiveFolder(createFolderIntent);
-                }
-            });
+            ResolveInfo resolveInfo = (ResolveInfo) animView.getTag();
+            if (resolveInfo.labelRes == R.string.group_folder) {
+                UserFolderInfo folderInfo = new UserFolderInfo();
+                folderInfo.title = getResources().getText(R.string.folder_name);
+                mDragController.startDrag(
+                        v, this, folderInfo, DragController.DRAG_ACTION_COPY, null);
+            } else {
+                createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER;
+                createItemInfo.componentName = new ComponentName(
+                        resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
+                mDragController.startDrag(
+                        v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
+            }
             return true;
         case ShortcutCustomization:
-            // animate some feedback to the long press
-            animateClickFeedback(v, new Runnable() {
-                @Override
-                public void run() {
-                    // add the shortcut
-                    ResolveInfo info = (ResolveInfo) animView.getTag();
-                    Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-                    if (info.labelRes == R.string.group_applications) {
-                        // Create app shortcuts is a special built-in case of shortcuts
-                        createShortcutIntent.putExtra(
-                                Intent.EXTRA_SHORTCUT_NAME,getContext().getString(
-                                        R.string.group_applications));
-                    } else {
-                        ComponentName name = new ComponentName(info.activityInfo.packageName, 
-                                info.activityInfo.name);
-                        createShortcutIntent.setComponent(name);
-                    }
-                    mLauncher.prepareAddItemFromHomeCustomizationDrawer();
-                    mLauncher.processShortcut(createShortcutIntent);
-                }
-            });
+            ResolveInfo info = (ResolveInfo) animView.getTag();
+            createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+            createItemInfo.componentName = new ComponentName(
+                    info.activityInfo.packageName, info.activityInfo.name);
+            mDragController.startDrag(
+                    v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
             return true;
         case ApplicationCustomization:
             // Pick up the application for dropping
diff --git a/src/com/android/launcher2/DeleteZone.java b/src/com/android/launcher2/DeleteZone.java
index aeaf5a3..1f54b36 100644
--- a/src/com/android/launcher2/DeleteZone.java
+++ b/src/com/android/launcher2/DeleteZone.java
@@ -190,6 +190,10 @@
         }
     }
 
+    public boolean isDropEnabled() {
+        return true;
+    }
+
     private void createAnimations() {
         if (mInAnimation == null) {
             mInAnimation = new FastAnimationSet();
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 7fc905b..87b3473 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -18,16 +18,17 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.IBinder;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Vibrator;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.View;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.View;
 import android.view.WindowManager;
 import android.view.inputmethod.InputMethodManager;
 
@@ -568,6 +569,9 @@
         final int count = dropTargets.size();
         for (int i=count-1; i>=0; i--) {
             DropTarget target = dropTargets.get(i);
+            if (!target.isDropEnabled())
+                continue;
+
             target.getHitRect(r);
 
             // Convert the hit rect to screen coordinates
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index 41e76f0..d14f5f7 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -76,8 +76,7 @@
         scale.setScale(scaleFactor, scaleFactor);
 
         mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
-        mDragRegionWidth = width;
-        mDragRegionHeight = height;
+        setDragRegion(0, 0, width, height);
 
         // The point in our scaled bitmap that the touch events are located
         mRegistrationX = registrationX + (DRAG_SCALE / 2);
@@ -91,6 +90,22 @@
         mDragRegionHeight = height;
     }
 
+    public int getScaledDragRegionXOffset() {
+        return -(int)((mScale - 1.0f) * mDragRegionWidth / 2);
+    }
+
+    public int getScaledDragRegionWidth() {
+        return (int)(mScale * mDragRegionWidth);
+    }
+
+    public int getScaledDragRegionYOffset() {
+        return -(int)((mScale - 1.0f) * mDragRegionHeight / 2);
+    }
+
+    public int getScaledDragRegionHeight() {
+        return (int)(mScale * mDragRegionWidth);
+    }
+
     public int getDragRegionLeft() {
         return mDragRegionLeft;
     }
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index d2e3ace..308dbbe 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -23,6 +23,12 @@
  *
  */
 public interface DropTarget {
+    /**
+     * Used to temporarily disable certain drop targets
+     *
+     * @return boolean specifying whether this drop target is currently enabled
+     */
+    boolean isDropEnabled();
 
     /**
      * Handle an object being dropped on the DropTarget
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index e2cef0e..e692d20 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -29,7 +29,7 @@
 /**
  * An icon that can appear on in the workspace representing an {@link UserFolder}.
  */
-public class FolderIcon extends BubbleTextView implements DropTarget {
+public class FolderIcon extends DimmableBubbleTextView implements DropTarget {
     private UserFolderInfo mInfo;
     private Launcher mLauncher;
     private Drawable mCloseIcon;
@@ -43,6 +43,10 @@
         super(context);
     }
 
+    public boolean isDropEnabled() {
+        return !((Workspace)getParent().getParent()).isSmall();
+    }
+
     static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
             UserFolderInfo folderInfo) {
 
diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java
index 992bab1..caeb12b 100644
--- a/src/com/android/launcher2/InstallShortcutReceiver.java
+++ b/src/com/android/launcher2/InstallShortcutReceiver.java
@@ -50,11 +50,6 @@
         String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
 
         if (findEmptyCell(context, mCoordinates, screen)) {
-            CellLayout.CellInfo cell = new CellLayout.CellInfo();
-            cell.cellX = mCoordinates[0];
-            cell.cellY = mCoordinates[1];
-            cell.screen = screen;
-
             Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
 
             if (intent.getAction() == null) {
@@ -66,7 +61,7 @@
             boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true);
             if (duplicate || !LauncherModel.shortcutExists(context, name, intent)) {
                 ((LauncherApplication)context.getApplicationContext()).getModel()
-                        .addShortcut(context, data, cell, true);
+                        .addShortcut(context, data, screen, mCoordinates[0], mCoordinates[1], true);
                 Toast.makeText(context, context.getString(R.string.shortcut_installed, name),
                         Toast.LENGTH_SHORT).show();
             } else {
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index c82f998..5be78f9 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -118,8 +118,6 @@
     static final boolean DEBUG_WIDGETS = false;
     static final boolean DEBUG_USER_INTERFACE = false;
 
-    private static final int WALLPAPER_SCREENS_SPAN = 2;
-
     private static final int MENU_GROUP_ADD = 1;
     private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1;
 
@@ -160,16 +158,6 @@
     private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX";
     // Type: int
     private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY";
-    // Type: int[]
-    private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells";
     // Type: boolean
     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
     // Type: long
@@ -204,10 +192,12 @@
     private AppWidgetManager mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
 
-    private CellLayout.CellInfo mAddItemCellInfo;
-    private int[] mAddItemCoordinates;
-    private CellLayout.CellInfo mMenuAddInfo;
-    private final int[] mCellCoordinates = new int[2];
+    private int mAddScreen = -1;
+    private int mAddIntersectCellX = -1;
+    private int mAddIntersectCellY = -1;
+    private int[] mAddDropPosition;
+    private int[] mTmpAddItemCellCoordinates = new int[2];
+
     private FolderInfo mFolderInfo;
 
     private DeleteZone mDeleteZone;
@@ -662,29 +652,29 @@
         // For example, the user would PICK_SHORTCUT for "Music playlist", and we
         // launch over to the Music app to actually CREATE_SHORTCUT.
 
-        if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
+        if (resultCode == RESULT_OK && mAddScreen != -1) {
             switch (requestCode) {
                 case REQUEST_PICK_APPLICATION:
-                    completeAddApplication(this, data, mAddItemCellInfo);
+                    completeAddApplication(this, data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
                     break;
                 case REQUEST_PICK_SHORTCUT:
                     processShortcut(data);
                     break;
                 case REQUEST_CREATE_SHORTCUT:
-                    completeAddShortcut(data, mAddItemCellInfo);
+                    completeAddShortcut(data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
                     break;
                 case REQUEST_PICK_LIVE_FOLDER:
                     addLiveFolder(data);
                     break;
                 case REQUEST_CREATE_LIVE_FOLDER:
-                    completeAddLiveFolder(data, mAddItemCellInfo);
+                    completeAddLiveFolder(data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
                     break;
                 case REQUEST_PICK_APPWIDGET:
                     addAppWidgetFromPick(data);
                     break;
                 case REQUEST_CREATE_APPWIDGET:
                     int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
-                    completeAddAppWidget(appWidgetId, mAddItemCellInfo);
+                    completeAddAppWidget(appWidgetId, mAddScreen);
                     break;
                 case REQUEST_PICK_WALLPAPER:
                     // We just wanted the activity result here so we can clear mWaitingForResult
@@ -821,20 +811,11 @@
         }
 
         final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
+
         if (addScreen > -1) {
-            mAddItemCoordinates = null;
-            mAddItemCellInfo = new CellLayout.CellInfo();
-            final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
-            addItemCellInfo.valid = true;
-            addItemCellInfo.screen = addScreen;
-            addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
-            addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
-            addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
-            addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
-            addItemCellInfo.updateOccupiedCells(
-                    savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS),
-                    savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X),
-                    savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y));
+            mAddScreen = addScreen;
+            mAddIntersectCellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
+            mAddIntersectCellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
             mRestoring = true;
         }
 
@@ -1021,9 +1002,15 @@
      * @param data The intent describing the application.
      * @param cellInfo The position on screen where to create the shortcut.
      */
-    void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) {
-        cellInfo.screen = mWorkspace.getCurrentPage();
-        if (!findSingleSlot(cellInfo)) return;
+    void completeAddApplication(Context context, Intent data, int screen,
+            int intersectCellX, int intersectCellY) {
+        final int[] cellXY = mTmpAddItemCellCoordinates;
+        final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
+
+        if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
+            showOutOfSpaceMessage();
+            return;
+        }
 
         final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(),
                 data, context);
@@ -1032,7 +1019,8 @@
             info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
                     Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
             info.container = ItemInfo.NO_ID;
-            mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked());
+            mWorkspace.addApplicationShortcut(info, screen, cellXY[0], cellXY[1],
+                    isWorkspaceLocked(), mAddIntersectCellX, mAddIntersectCellY);
         } else {
             Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
         }
@@ -1044,16 +1032,22 @@
      * @param data The intent describing the shortcut.
      * @param cellInfo The position on screen where to create the shortcut.
      */
-    private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) {
-        cellInfo.screen = mWorkspace.getCurrentPage();
-        if (!findSingleSlot(cellInfo)) return;
+    private void completeAddShortcut(Intent data, int screen,
+            int intersectCellX, int intersectCellY) {
+        final int[] cellXY = mTmpAddItemCellCoordinates;
+        final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
 
-        final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false);
+        if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
+            showOutOfSpaceMessage();
+            return;
+        }
+
+        final ShortcutInfo info = mModel.addShortcut(
+                this, data, screen, cellXY[0], cellXY[1], false);
 
         if (!mRestoring) {
             final View view = createShortcut(info);
-            mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
-                    isWorkspaceLocked());
+            mWorkspace.addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
         }
     }
 
@@ -1064,46 +1058,58 @@
      * @param appWidgetId The app widget id
      * @param cellInfo The position on screen where to create the widget.
      */
-    private void completeAddAppWidget(int appWidgetId, CellLayout.CellInfo cellInfo) {
+    private void completeAddAppWidget(int appWidgetId, int screen) {
         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
 
         // Calculate the grid spans needed to fit this widget
-        CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
-        int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight, null);
+        CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
+        int[] spanXY = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight, null);
 
         // Try finding open space on Launcher screen
         // We have saved the position to which the widget was dragged-- this really only matters
         // if we are placing widgets on a "spring-loaded" screen
-        final int[] xy = mCellCoordinates;
+        final int[] cellXY = mTmpAddItemCellCoordinates;
 
         // For now, we don't save the coordinate where we dropped the icon because we're not
         // supporting spring-loaded mini-screens; however, leaving the ability to directly place
         // a widget on the home screen in case we want to add it in the future
-        final int[] xyTouch = null;
-        //final int[] xyTouch = mAddItemCoordinates;
+        final int[] touchXY = null;
+        //final int[] touchXY = mAddDropPosition;
         boolean findNearestVacantAreaFailed = false;
-        if (xyTouch != null) {
-            CellLayout screen = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
-            int[] result = screen.findNearestVacantArea(
-                    mAddItemCoordinates[0], mAddItemCoordinates[1],
-                    spans[0], spans[1], cellInfo, xy);
+        boolean foundCellSpan = false;
+        if (touchXY != null) {
+            // when dragging and dropping, just find the closest free spot
+            CellLayout screenLayout = (CellLayout) mWorkspace.getChildAt(screen);
+            int[] result = screenLayout.findNearestVacantArea(
+                    touchXY[0], touchXY[1], spanXY[0], spanXY[1], cellXY);
             findNearestVacantAreaFailed = (result == null);
+            foundCellSpan = !findNearestVacantAreaFailed;
+        } else {
+            if (mAddIntersectCellX != -1 && mAddIntersectCellY != -1) {
+                // if we long pressed on an empty cell to bring up a menu,
+                // make sure we intersect the empty cell
+                foundCellSpan = layout.findCellForSpanThatIntersects(cellXY, spanXY[0], spanXY[1],
+                        mAddIntersectCellX, mAddIntersectCellY);
+            } else {
+                // if we went through the menu -> add, just find any spot
+                foundCellSpan = layout.findCellForSpan(cellXY, spanXY[0], spanXY[1]);
+            }
         }
 
-        if (findNearestVacantAreaFailed ||
-                (xyTouch == null && !findSlot(cellInfo, xy, spans[0], spans[1]))) {
+        if (!foundCellSpan) {
             if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+            showOutOfSpaceMessage();
             return;
         }
 
         // Build Launcher-specific widget info and save to database
         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
-        launcherInfo.spanX = spans[0];
-        launcherInfo.spanY = spans[1];
+        launcherInfo.spanX = spanXY[0];
+        launcherInfo.spanY = spanXY[1];
 
         LauncherModel.addItemToDatabase(this, launcherInfo,
                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                cellInfo.screen, xy[0], xy[1], false);
+                screen, cellXY[0], cellXY[1], false);
 
         if (!mRestoring) {
             mDesktopItems.add(launcherInfo);
@@ -1114,11 +1120,15 @@
             launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
             launcherInfo.hostView.setTag(launcherInfo);
 
-            mWorkspace.addInScreen(launcherInfo.hostView, cellInfo.screen, xy[0], xy[1],
+            mWorkspace.addInScreen(launcherInfo.hostView, screen, cellXY[0], cellXY[1],
                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
         }
     }
 
+    void showOutOfSpaceMessage() {
+        Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
+    }
+
     public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
         mDesktopItems.remove(launcherInfo);
         launcherInfo.hostView = null;
@@ -1219,19 +1229,10 @@
             outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true);
         }
 
-        if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) {
-            final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
-            final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen);
-
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX());
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY());
-            outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS,
-                   layout.getOccupiedCellsFlattened());
+        if (mAddScreen > -1 && mWaitingForResult) {
+            outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, mAddScreen);
+            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mAddIntersectCellX);
+            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mAddIntersectCellY);
         }
 
         if (mFolderInfo != null && mWaitingForResult) {
@@ -1353,20 +1354,13 @@
 
         // Disable add if the workspace is full.
         if (visible) {
-            mMenuAddInfo = mWorkspace.updateOccupiedCellsForCurrentScreen(null);
-            menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid);
+            CellLayout layout = (CellLayout) mWorkspace.getChildAt(mWorkspace.getCurrentPage());
+            menu.setGroupEnabled(MENU_GROUP_ADD, layout.existsEmptyCell());
         }
 
         return true;
     }
 
-    // we need to initialize mAddItemCellInfo before adding something to the homescreen -- when
-    // using the settings menu to add an item, something similar happens in showAddDialog
-    public void prepareAddItemFromHomeCustomizationDrawer() {
-        mMenuAddInfo = mWorkspace.updateOccupiedCellsForCurrentScreen(null);
-        mAddItemCellInfo = mMenuAddInfo;
-    }
-
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
@@ -1410,16 +1404,24 @@
             }
         } else {
             closeAllApps(true);
-            showAddDialog(mMenuAddInfo);
+            showAddDialog(-1, -1);
         }
     }
 
-    void addAppWidgetFromDrop(ComponentName appWidgetProvider, CellLayout.CellInfo cellInfo,
-            int[] position) {
-        mAddItemCellInfo = cellInfo;
+    private void resetAddInfo() {
+        mAddScreen = -1;
+        mAddIntersectCellX = -1;
+        mAddIntersectCellY = -1;
+        mAddDropPosition = null;
+    }
 
-        // only set mAddItemCoordinates if we dropped on home screen in "spring-loaded" manner
-        mAddItemCoordinates = position;
+    void addAppWidgetFromDrop(ComponentName appWidgetProvider, int screen, int[] position) {
+        resetAddInfo();
+        mAddScreen = screen;
+
+        // only set mAddDropPosition if we dropped on home screen in "spring-loaded" manner
+        mAddDropPosition = position;
+
         int appWidgetId = getAppWidgetHost().allocateAppWidgetId();
         AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, appWidgetProvider);
         addAppWidgetImpl(appWidgetId);
@@ -1445,10 +1447,20 @@
             startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
         } else {
             // Otherwise just add it
-            completeAddAppWidget(appWidgetId, mAddItemCellInfo);
+            completeAddAppWidget(appWidgetId, mAddScreen);
         }
     }
 
+    void processShortcutFromDrop(ComponentName componentName, int screen, int[] position) {
+        resetAddInfo();
+        mAddScreen = screen;
+        mAddDropPosition = position;
+
+        Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+        createShortcutIntent.setComponent(componentName);
+        processShortcut(createShortcutIntent);
+    }
+
     void processShortcut(Intent intent) {
         // Handle case where user selected "Applications"
         String applicationName = getResources().getString(R.string.group_applications);
@@ -1470,59 +1482,75 @@
         startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
     }
 
-    void addLiveFolder(Intent intent) {
+    void addLiveFolderFromDrop(ComponentName componentName, int screen, int[] position) {
+        resetAddInfo();
+        mAddScreen = screen;
+        mAddDropPosition = position;
+
+        Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
+        createFolderIntent.setComponent(componentName);
+
+        addLiveFolder(createFolderIntent);
+    }
+
+    void addLiveFolder(Intent intent) { // YYY add screen intersect etc. parameters here
         // Handle case where user selected "Folder"
         String folderName = getResources().getString(R.string.group_folder);
         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
 
         if (folderName != null && folderName.equals(shortcutName)) {
-            addFolder();
+            addFolder(mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
         } else {
             startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER);
         }
     }
 
-    void addFolder() {
+    void addFolder(int screen, int intersectCellX, int intersectCellY) {
         UserFolderInfo folderInfo = new UserFolderInfo();
         folderInfo.title = getText(R.string.folder_name);
 
-        CellLayout.CellInfo cellInfo = mAddItemCellInfo;
-        cellInfo.screen = mWorkspace.getCurrentPage();
-        if (!findSingleSlot(cellInfo)) return;
+        final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
+        final int[] cellXY = mTmpAddItemCellCoordinates;
+        if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
+            showOutOfSpaceMessage();
+            return;
+        }
 
         // Update the model
         LauncherModel.addItemToDatabase(this, folderInfo,
                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                mWorkspace.getCurrentPage(), cellInfo.cellX, cellInfo.cellY, false);
+                screen, cellXY[0], cellXY[1], false);
         sFolders.put(folderInfo.id, folderInfo);
 
         // Create the view
         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), folderInfo);
-        mWorkspace.addInCurrentScreen(newFolder,
-                cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked());
+        mWorkspace.addInScreen(newFolder, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
     }
 
     void removeFolder(FolderInfo folder) {
         sFolders.remove(folder.id);
     }
 
-    private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) {
-        cellInfo.screen = mWorkspace.getCurrentPage();
-        if (!findSingleSlot(cellInfo)) return;
+    private void completeAddLiveFolder(Intent data, int screen, int intersectCellX, int intersectCellY) {
+        final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
+        final int[] cellXY = mTmpAddItemCellCoordinates;
+        if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
+            showOutOfSpaceMessage();
+            return;
+        }
 
-        final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
+        final LiveFolderInfo info = addLiveFolder(this, data, screen, cellXY[0], cellXY[1], false);
 
         if (!mRestoring) {
             final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
-            mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
-                    isWorkspaceLocked());
+            mWorkspace.addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
         }
     }
 
     static LiveFolderInfo addLiveFolder(Context context, Intent data,
-            CellLayout.CellInfo cellInfo, boolean notify) {
+            int screen, int cellX, int cellY, boolean notify) {
 
         Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT);
         String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME);
@@ -1558,34 +1586,12 @@
                 LiveFolders.DISPLAY_MODE_GRID);
 
         LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
+                screen, cellX, cellY, notify);
         sFolders.put(info.id, info);
 
         return info;
     }
 
-    private boolean findSingleSlot(CellLayout.CellInfo cellInfo) {
-        final int[] xy = new int[2];
-        if (findSlot(cellInfo, xy, 1, 1)) {
-            cellInfo.cellX = xy[0];
-            cellInfo.cellY = xy[1];
-            return true;
-        }
-        return false;
-    }
-
-    private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
-        if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
-            CellLayout targetLayout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
-            cellInfo = targetLayout.updateOccupiedCells(null, null);
-            if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
-                Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
-                return false;
-            }
-        }
-        return true;
-    }
-
     private void showNotifications() {
         final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
         if (statusBar != null) {
@@ -1907,28 +1913,38 @@
             v = (View) v.getParent();
         }
 
-        CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
 
+        resetAddInfo();
+        CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag();
         // This happens when long clicking an item with the dpad/trackball
-        if (cellInfo == null) {
+        if (longClickCellInfo == null || !longClickCellInfo.valid) {
             return true;
         }
 
+        final View itemUnderLongClick = longClickCellInfo.cell;
+
         if (mWorkspace.allowLongPress()) {
-            if (cellInfo.cell == null) {
-                if (cellInfo.valid) {
-                    // User long pressed on empty space
-                    mWorkspace.setAllowLongPress(false);
-                    mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
-                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-                    showAddDialog(cellInfo);
+            if (itemUnderLongClick == null) {
+                // User long pressed on empty space
+                mWorkspace.setAllowLongPress(false);
+                mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+                if (LauncherApplication.isScreenXLarge()) {
+                    // Animate the widget chooser up from the bottom of the screen
+                    if (!isCustomizationDrawerVisible()) {
+                        showCustomizationDrawer(true);
+                    }
+                } else {
+                    showAddDialog(longClickCellInfo.cellX, longClickCellInfo.cellY);
                 }
             } else {
-                if (!(cellInfo.cell instanceof Folder)) {
+                if (!(itemUnderLongClick instanceof Folder)) {
                     // User long pressed on an item
                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-                    mWorkspace.startDrag(cellInfo);
+                    mAddIntersectCellX = longClickCellInfo.cellX;
+                    mAddIntersectCellY = longClickCellInfo.cellY;
+                    mWorkspace.startDrag(longClickCellInfo);
                 }
             }
         }
@@ -2106,9 +2122,11 @@
         showDialog(DIALOG_RENAME_FOLDER);
     }
 
-    private void showAddDialog(CellLayout.CellInfo cellInfo) {
-        mAddItemCellInfo = cellInfo;
-        mAddItemCoordinates = null;
+    private void showAddDialog(int intersectX, int intersectY) {
+        resetAddInfo();
+        mAddIntersectCellX = intersectX;
+        mAddIntersectCellY = intersectY;
+        mAddScreen = mWorkspace.getCurrentPage();
         mWaitingForResult = true;
         showDialog(DIALOG_CREATE_SHORTCUT);
     }
@@ -2634,7 +2652,7 @@
                 mAllAppsPagedView.endChoiceMode();
                 mCustomizePagedView.endChoiceMode();
             } else {
-                Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
+                showOutOfSpaceMessage();
             }
         }
     }
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 4ad31b1..7c1fa21 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -1478,11 +1478,11 @@
     }
 
     ShortcutInfo addShortcut(Context context, Intent data,
-            CellLayout.CellInfo cellInfo, boolean notify) {
+            int screen, int cellX, int cellY, boolean notify) {
 
         final ShortcutInfo info = infoFromShortcutIntent(context, data);
         addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
+                screen, cellX, cellY, notify);
 
         return info;
     }
diff --git a/src/com/android/launcher2/LiveFolderInfo.java b/src/com/android/launcher2/LiveFolderInfo.java
index 74b0217..7db321b 100644
--- a/src/com/android/launcher2/LiveFolderInfo.java
+++ b/src/com/android/launcher2/LiveFolderInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher2;
 
+import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -28,6 +29,8 @@
      */
     Intent baseIntent;
 
+    ComponentName componentName;
+
     /**
      * The live folder's content uri.
      */
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 9dbe61d..f9fcd02 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -334,7 +334,7 @@
     }
 
     @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    public void onLayout(boolean changed, int left, int top, int right, int bottom) {
         if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
             setHorizontalScrollBarEnabled(false);
             int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
diff --git a/src/com/android/launcher2/PendingAddItemInfo.java b/src/com/android/launcher2/PendingAddItemInfo.java
new file mode 100644
index 0000000..23e2330
--- /dev/null
+++ b/src/com/android/launcher2/PendingAddItemInfo.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.ComponentName;
+
+/**
+ * We pass this object with a drag from the customization tray
+ */
+class PendingAddItemInfo extends ItemInfo {
+    /**
+     * The component that will be created.
+     */
+    ComponentName componentName;
+}
\ No newline at end of file
diff --git a/src/com/android/launcher2/UserFolder.java b/src/com/android/launcher2/UserFolder.java
index c7466b8..d6799f7 100644
--- a/src/com/android/launcher2/UserFolder.java
+++ b/src/com/android/launcher2/UserFolder.java
@@ -72,6 +72,10 @@
         }
     }
 
+    public boolean isDropEnabled() {
+        return true;
+    }
+
     void bind(FolderInfo info) {
         super.bind(info);
         setContentAdapter(new ShortcutsAdapter(mContext, ((UserFolderInfo) info).contents));
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 599fbda..8f16300 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -16,12 +16,12 @@
 
 package com.android.launcher2;
 
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
 import com.android.launcher.R;
 
-import android.animation.PropertyValuesHolder;
+import android.animation.Animator;
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.animation.Animator.AnimatorListener;
 import android.app.WallpaperManager;
 import android.appwidget.AppWidgetManager;
@@ -46,7 +46,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -103,8 +102,10 @@
 
     private int[] mTempCell = new int[2];
     private int[] mTempEstimate = new int[2];
+    private float[] mTempOriginXY = new float[2];
     private float[] mTempDragCoordinates = new float[2];
     private float[] mTempDragBottomRightCoordinates = new float[2];
+    private Matrix mTempInverseMatrix = new Matrix();
 
     private static final int DEFAULT_CELL_COUNT_X = 4;
     private static final int DEFAULT_CELL_COUNT_Y = 4;
@@ -117,7 +118,6 @@
     private boolean mIsSmall;
     private AnimatorListener mUnshrinkAnimationListener;
 
-
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -274,35 +274,6 @@
     }
 
     /**
-     * Adds the specified child in the current 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 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 addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
-        addInScreen(child, mCurrentPage, x, y, spanX, spanY, false);
-    }
-
-    /**
-     * Adds the specified child in the current 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 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.
-     */
-    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
-        addInScreen(child, mCurrentPage, x, y, spanX, spanY, insert);
-    }
-
-    /**
      * Adds the specified child in the specified screen. The position and dimension of
      * the child are defined by x, y, spanX and spanY.
      *
@@ -369,14 +340,6 @@
         }
     }
 
-    CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) {
-        CellLayout group = (CellLayout) getChildAt(mCurrentPage);
-        if (group != null) {
-            return group.updateOccupiedCells(occupied, null);
-        }
-        return null;
-    }
-
     public boolean onTouch(View v, MotionEvent event) {
         // this is an intercepted event being forwarded from a cell layout
         if (mIsSmall) {
@@ -469,7 +432,7 @@
     }
 
     @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    public void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
         // if shrinkToBottom() is called on initialization, it has to be deferred
@@ -776,43 +739,40 @@
         invalidate();
     }
 
-    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
-        addApplicationShortcut(info, cellInfo, false);
+    void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY,
+            boolean insertAtFirst, int intersectX, int intersectY) {
+        final CellLayout cellLayout = (CellLayout) getChildAt(screen);
+        View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info);
+
+        final int[] cellXY = new int[2];
+        cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
+        addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
+        LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
+                LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
+                cellXY[0], cellXY[1]);
     }
 
-    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo,
-            boolean insertAtFirst) {
-        final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
-        final int[] result = new int[2];
-
-        layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
-        onDropExternal(result[0], result[1], info, layout, insertAtFirst);
-    }
 
     public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
-        CellLayout cellLayout = getCurrentDropLayout();
+        CellLayout cellLayout;
         int originX = x - xOffset;
         int originY = y - yOffset;
         if (mIsSmall) {
-            // find out which target layout is over
-            final float[] localXY = mTempDragCoordinates;
-            localXY[0] = originX;
-            localXY[1] = originY;
-            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
-            // we need to subtract left/top here because DragController already adds
-            // dragRegionLeft/Top to xOffset and yOffset
-            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
-            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
-            cellLayout =  findMatchingPageForDragOver(localXY, localBottomRightXY);
+            cellLayout = findMatchingPageForDragOver(dragView, originX, originY);
             if (cellLayout == null) {
                 // cancel the drag if we're not over a mini-screen at time of drop
                 // TODO: maybe add a nice fade here?
                 return;
             }
-            // localXY will be transformed into the local screen's coordinate space; save that info
-            originX = (int)localXY[0];
-            originY = (int)localXY[1];
+            // get originX and originY in the local coordinate system of the screen
+            mTempOriginXY[0] = originX;
+            mTempOriginXY[1] = originY;
+            mapPointGlobalToLocal(cellLayout, mTempOriginXY);
+            originX = (int)mTempOriginXY[0];
+            originY = (int)mTempOriginXY[1];
+        } else {
+            cellLayout = getCurrentDropLayout();
         }
         if (source != this) {
             onDropExternal(originX, originY, dragInfo, cellLayout);
@@ -835,8 +795,8 @@
 
                 // update the item's position after drop
                 final ItemInfo info = (ItemInfo) cell.getTag();
-                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell
-                        .getLayoutParams();
+                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+                cellLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
                 lp.cellX = mTargetCell[0];
                 lp.cellY = mTargetCell[1];
 
@@ -854,6 +814,10 @@
     public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
 
+        if (mIsSmall) {
+            // If we're shrunken, don't let anyone drag on folders/etc  that are on the mini-screens
+            return null;
+        }
         // We may need to delegate the drag to a child view. If a 1x1 item
         // would land in a cell occupied by a DragTarget (e.g. a Folder),
         // then drag events should be handled by that child.
@@ -871,12 +835,12 @@
             dragPointX = x;
             dragPointY = y;
         }
+        dragPointX += mScrollX - currentLayout.getLeft();
+        dragPointY += mScrollY - currentLayout.getTop();
 
         // If we are dragging over a cell that contains a DropTarget that will
         // accept the drop, delegate to that DropTarget.
         final int[] cellXY = mTempCell;
-        int localDragPointX = dragPointX - (currentLayout.getLeft() - mScrollX);
-        int localDragPointY = dragPointY - (currentLayout.getTop() - mScrollY);
         currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
         View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
         if (child instanceof DropTarget) {
@@ -888,29 +852,32 @@
         return null;
     }
 
+
+    private void mapPointGlobalToLocal(View v, float[] xy) {
+        xy[0] = xy[0] + mScrollX - v.getLeft();
+        xy[1] = xy[1] + mScrollY - v.getTop();
+        v.getMatrix().invert(mTempInverseMatrix);
+        mTempInverseMatrix.mapPoints(xy);
+    }
+
     // xy = upper left corner of item being dragged
     // bottomRightXy = lower right corner of item being dragged
     // This method will see which mini-screen is most overlapped by the item being dragged, and
     // return it. It will also transform the parameters xy and bottomRightXy into the local
     // coordinate space of the returned screen
-    private CellLayout findMatchingPageForDragOver(float[] xy, float[] bottomRightXy) {
-        float x = xy[0];
-        float y = xy[1];
-        float right = bottomRightXy[0];
-        float bottom = bottomRightXy[1];
-
-        float bestX = 0;
-        float bestY = 0;
-        float bestRight = 0;
-        float bestBottom = 0;
-
-        Matrix inverseMatrix = new Matrix();
+    private CellLayout findMatchingPageForDragOver(DragView dragView, int originX, int originY) {
+        float x = originX + dragView.getScaledDragRegionXOffset();
+        float y = originY + dragView.getScaledDragRegionYOffset();
+        float right = x + dragView.getScaledDragRegionWidth();
+        float bottom = y + dragView.getScaledDragRegionHeight();
 
         // We loop through all the screens (ie CellLayouts) and see which one overlaps the most
         // with the item being dragged.
         final int screenCount = getChildCount();
         CellLayout bestMatchingScreen = null;
-        float bestOverlapSoFar = 0;
+        float smallestDistSoFar = Float.MAX_VALUE;
+        final float[] xy = mTempDragCoordinates;
+        final float[] bottomRightXy = mTempDragBottomRightCoordinates;
         for (int i = 0; i < screenCount; i++) {
             CellLayout cl = (CellLayout)getChildAt(i);
             // Transform the coordinates of the item being dragged to the CellLayout's coordinates
@@ -918,57 +885,76 @@
             float top = cl.getTop();
             xy[0] = x + mScrollX - left;
             xy[1] = y + mScrollY - top;
-            cl.getMatrix().invert(inverseMatrix);
 
             bottomRightXy[0] = right + mScrollX - left;
             bottomRightXy[1] = bottom + mScrollY - top;
 
-            inverseMatrix.mapPoints(xy);
-            inverseMatrix.mapPoints(bottomRightXy);
+            cl.getMatrix().invert(mTempInverseMatrix);
+            mTempInverseMatrix.mapPoints(xy);
+            mTempInverseMatrix.mapPoints(bottomRightXy);
 
             float dragRegionX = xy[0];
             float dragRegionY = xy[1];
             float dragRegionRight = bottomRightXy[0];
             float dragRegionBottom = bottomRightXy[1];
+            float dragRegionCenterX = (dragRegionX + dragRegionRight) / 2.0f;
+            float dragRegionCenterY = (dragRegionY + dragRegionBottom) / 2.0f;
 
             // Find the overlapping region
             float overlapLeft = Math.max(0f, dragRegionX);
             float overlapTop = Math.max(0f, dragRegionY);
             float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom);
             float overlapRight = Math.min(cl.getWidth(), dragRegionRight);
-
             if (overlapRight >= 0 && overlapLeft <= cl.getWidth() &&
-                    overlapTop >= 0 && overlapBottom <= cl.getHeight()) {
-                // Calculate the size of the overlapping region
+                    (overlapTop >= 0 && overlapBottom <= cl.getHeight())) {
+                // Calculate the distance between the two centers
+                float distX = dragRegionCenterX - cl.getWidth()/2;
+                float distY = dragRegionCenterY - cl.getHeight()/2;
+                float dist = distX * distX + distY * distY;
+
                 float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
-                if (overlap > bestOverlapSoFar) {
-                    bestOverlapSoFar = overlap;
+
+                // Calculate the closest overlapping region
+                if (overlap > 0 && dist < smallestDistSoFar) {
+                    smallestDistSoFar = dist;
                     bestMatchingScreen = cl;
-                    bestX = xy[0];
-                    bestY = xy[1];
-                    bestRight = bottomRightXy[0];
-                    bestBottom = bottomRightXy[1];
                 }
              }
         }
-        if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) {
+
+        if (bestMatchingScreen != mDragTargetLayout) {
             if (mDragTargetLayout != null) {
-                mDragTargetLayout.onDragComplete();
+                mDragTargetLayout.onDragExit();
             }
             mDragTargetLayout = bestMatchingScreen;
         }
-        xy[0] = bestX;
-        xy[1] = bestY;
-        bottomRightXy[0] = bestRight;
-        bottomRightXy[1] = bestBottom;
         return bestMatchingScreen;
     }
 
     public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
+        CellLayout currentLayout;
+        int originX = x - xOffset;
+        int originY = y - yOffset;
+        if (mIsSmall) {
+            currentLayout = findMatchingPageForDragOver(dragView, originX, originY);
+
+            if (currentLayout == null) {
+                return;
+            }
+
+            currentLayout.setHover(true);
+            // get originX and originY in the local coordinate system of the screen
+            mTempOriginXY[0] = originX;
+            mTempOriginXY[1] = originY;
+            mapPointGlobalToLocal(currentLayout, mTempOriginXY);
+            originX = (int)mTempOriginXY[0];
+            originY = (int)mTempOriginXY[1];
+        } else {
+            currentLayout = getCurrentDropLayout();
+        }
 
         final ItemInfo item = (ItemInfo)dragInfo;
-        CellLayout currentLayout = getCurrentDropLayout();
 
         if (dragInfo instanceof LauncherAppWidgetInfo) {
             LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
@@ -980,25 +966,6 @@
                 item.spanY = spans[1];
             }
         }
-        int originX = x - xOffset;
-        int originY = y - yOffset;
-        if (mIsSmall) {
-            // find out which mini screen the dragged item is over
-            final float[] localXY = mTempDragCoordinates;
-            localXY[0] = originX;
-            localXY[1] = originY;
-            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
-
-            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
-            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
-            currentLayout = findMatchingPageForDragOver(localXY, localBottomRightXY);
-            if (currentLayout != null) {
-                currentLayout.setHover(true);
-            }
-
-            originX = (int)localXY[0];
-            originY = (int)localXY[1];
-        }
 
         if (source != this) {
             // This is a hack to fix the point used to determine which cell an icon from the all
@@ -1015,7 +982,7 @@
         }
         if (currentLayout != mDragTargetLayout) {
             if (mDragTargetLayout != null) {
-                mDragTargetLayout.onDragComplete();
+                mDragTargetLayout.onDragExit();
             }
             mDragTargetLayout = currentLayout;
         }
@@ -1028,14 +995,14 @@
             int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX);
             int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY);
             mDragTargetLayout.visualizeDropLocation(
-                    child, localOriginX, localOriginY, item.spanX, item.spanY);
+                    child, localOriginX, localOriginY, item.spanX, item.spanY, child);
         }
     }
 
     public void onDragExit(DragSource source, int x, int y, int xOffset,
             int yOffset, DragView dragView, Object dragInfo) {
         if (mDragTargetLayout != null) {
-            mDragTargetLayout.onDragComplete();
+            mDragTargetLayout.onDragExit();
             mDragTargetLayout = null;
         }
     }
@@ -1055,17 +1022,43 @@
         CellLayout cl = (CellLayout) layout;
         ItemInfo info = (ItemInfo) dragInfo;
 
-        final CellLayout.CellInfo cellInfo = cl.updateOccupiedCells(null, null);
-        if (cellInfo.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) {
+        if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) {
             onDropExternal(0, 0, dragInfo, cl, false);
             return true;
         }
+        mLauncher.showOutOfSpaceMessage();
         return false;
     }
 
+    // Drag from somewhere else
     private void onDropExternal(int x, int y, Object dragInfo,
             CellLayout cellLayout, boolean insertAtFirst) {
-        // Drag from somewhere else
+        int screen = indexOfChild(cellLayout);
+        if (dragInfo instanceof PendingAddItemInfo) {
+            PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
+            // When dragging and dropping from customization tray, we deal with creating
+            // widgets/shortcuts/folders in a slightly different way
+            int[] touchXY = new int[2];
+            touchXY[0] = x;
+            touchXY[1] = y;
+            switch (info.itemType) {
+                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                    mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY);
+                    break;
+                case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
+                    mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY);
+                    break;
+                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                    mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY);
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown item type: " + info.itemType);
+            }
+            cellLayout.onDragExit();
+            return;
+        }
+
+        // This is for other drag/drop cases, like dragging from All Apps
         ItemInfo info = (ItemInfo) dragInfo;
 
         View view = null;
@@ -1082,25 +1075,15 @@
             break;
         case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
             view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
-                    (ViewGroup) getChildAt(mCurrentPage),
-                    ((UserFolderInfo) info));
-            break;
-        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-            cellLayout.setTagToCellInfoForPoint(x, y);
-            int[] position = new int[2];
-            position[0] = x;
-            position[1] = y;
-            mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName,
-                    cellLayout.getTag(), position);
+                    cellLayout, ((UserFolderInfo) info));
             break;
         default:
-            throw new IllegalStateException("Unknown item type: "
-                    + info.itemType);
+            throw new IllegalStateException("Unknown item type: " + info.itemType);
         }
 
         // If the view is null, it has already been added.
         if (view == null) {
-            cellLayout.onDragComplete();
+            cellLayout.onDragExit();
         } else {
             mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
             addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
@@ -1109,7 +1092,7 @@
             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
 
             LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage,
+                    LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
                     lp.cellX, lp.cellY);
         }
     }
@@ -1124,22 +1107,40 @@
     }
 
     /**
+     * Return the current CellInfo describing our current drag; this method exists
+     * so that Launcher can sync this object with the correct info when the activity is created/
+     * destroyed
+     *
+     */
+    public CellLayout.CellInfo getDragInfo() {
+        return mDragInfo;
+    }
+
+    /**
      * {@inheritDoc}
      */
     public boolean acceptDrop(DragSource source, int x, int y,
             int xOffset, int yOffset, DragView dragView, Object dragInfo) {
-        final CellLayout layout = getCurrentDropLayout();
+        CellLayout layout;
+        if (mIsSmall) {
+            layout = findMatchingPageForDragOver(dragView, x - xOffset, y - yOffset);
+            if (layout == null) {
+                // cancel the drag if we're not over a mini-screen at time of drop
+                return false;
+            }
+        } else {
+            layout = getCurrentDropLayout();
+        }
         final CellLayout.CellInfo dragCellInfo = mDragInfo;
         final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
         final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
 
         final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
-        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
 
-        if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) {
+        if (layout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
             return true;
         } else {
-            Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
+            mLauncher.showOutOfSpaceMessage();
             return false;
         }
     }
@@ -1156,9 +1157,9 @@
         layout.estimateDropCell(localPixelX, localPixelY, spanX, spanY, cellXY);
         layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate);
 
-        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
         // Find the best target drop location
-        return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle);
+        return layout.findNearestVacantArea(
+                mTempEstimate[0], mTempEstimate[1], spanX, spanY, recycle);
     }
 
     /**
@@ -1196,6 +1197,10 @@
         mDragInfo = null;
     }
 
+    public boolean isDropEnabled() {
+        return true;
+    }
+
     @Override
     protected void onRestoreInstanceState(Parcelable state) {
         super.onRestoreInstanceState(state);
@@ -1366,8 +1371,6 @@
     }
 
     void updateShortcuts(ArrayList<ApplicationInfo> apps) {
-        final PackageManager pm = mLauncher.getPackageManager();
-
         final int screenCount = getChildCount();
         for (int i = 0; i < screenCount; i++) {
             final CellLayout layout = (CellLayout) getChildAt(i);