When dragging items on the home screen, visualize where they will drop.

Draw a rectangle around the area where an item will land if it is dropped.
If the area is vacant, the rectangle is green; otherwise, it is red.

Change-Id: I859b52514566fa55f8c7a04493b8088d12baa476
diff --git a/res/drawable/rounded_rect_green.9.png b/res/drawable/rounded_rect_green.9.png
new file mode 100644
index 0000000..5787c3d
--- /dev/null
+++ b/res/drawable/rounded_rect_green.9.png
Binary files differ
diff --git a/res/drawable/rounded_rect_red.9.png b/res/drawable/rounded_rect_red.9.png
new file mode 100644
index 0000000..7a5fdee
--- /dev/null
+++ b/res/drawable/rounded_rect_red.9.png
Binary files differ
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 1d45565..9cc370f 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher2;
 
-import java.util.ArrayList;
+import com.android.launcher.R;
 
 import android.app.WallpaperManager;
 import android.content.Context;
@@ -25,6 +25,7 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.ContextMenu;
 import android.view.MotionEvent;
@@ -34,7 +35,8 @@
 import android.view.animation.Animation;
 import android.view.animation.LayoutAnimationController;
 
-import com.android.launcher.R;
+import java.util.ArrayList;
+import java.util.Arrays;
 
 public class CellLayout extends ViewGroup {
     static final String TAG = "CellLayout";
@@ -63,10 +65,25 @@
     private final Rect mRect = new Rect();
     private final CellInfo mCellInfo = new CellInfo();
 
-    int[] mCellXY = new int[2];
+    // This is a temporary variable to prevent having to allocate a new object just to
+    // return an (x, y) value from helper functions. Do NOT use it to maintain other state.
+    private final int[] mTmpCellXY = new int[2];
+
     boolean[][] mOccupied;
 
-    private RectF mDragRect = new RectF();
+    private final RectF mDragRect = new RectF();
+
+    // When dragging, used to indicate a vacant drop location
+    private Drawable mVacantDrawable;
+
+    // When dragging, used to indicate an occupied drop location
+    private Drawable mOccupiedDrawable;
+
+    // Updated to point to mVacantDrawable or mOccupiedDrawable, as appropriate
+    private Drawable mDragRectDrawable;
+
+    // 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;
@@ -83,6 +100,13 @@
 
     public CellLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+
+        // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
+        // the user where a dragged item will land when dropped.
+        setWillNotDraw(false);
+        mVacantDrawable = getResources().getDrawable(R.drawable.rounded_rect_green);
+        mOccupiedDrawable = getResources().getDrawable(R.drawable.rounded_rect_red);
+
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
 
         mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
@@ -113,6 +137,18 @@
     }
 
     @Override
+    protected void onDraw(Canvas canvas) {
+        if (!mDragRect.isEmpty()) {
+            mDragRectDrawable.setBounds(
+                    (int)mDragRect.left,
+                    (int)mDragRect.top,
+                    (int)mDragRect.right,
+                    (int)mDragRect.bottom);
+            mDragRectDrawable.draw(canvas);
+        }
+    }
+
+    @Override
     public void cancelLongPress() {
         super.cancelLongPress();
 
@@ -199,7 +235,7 @@
         mLastDownOnOccupiedCell = found;
 
         if (!found) {
-            int cellXY[] = mCellXY;
+            final int cellXY[] = mTmpCellXY;
             pointToCellExact(x, y, cellXY);
 
             final boolean portrait = mPortrait;
@@ -207,7 +243,7 @@
             final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
 
             final boolean[][] occupied = mOccupied;
-            findOccupiedCells(xCount, yCount, occupied, null);
+            findOccupiedCells(xCount, yCount, occupied, null, true);
 
             cellInfo.cell = null;
             cellInfo.cellX = cellXY[0];
@@ -258,7 +294,7 @@
             final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
 
             final boolean[][] occupied = mOccupied;
-            findOccupiedCells(xCount, yCount, occupied, null);
+            findOccupiedCells(xCount, yCount, occupied, null, true);
 
             findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
 
@@ -340,6 +376,9 @@
         cellInfo.vacantCells.add(cell);
     }
 
+    /**
+     * 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]) {
@@ -349,6 +388,9 @@
         return true;
     }
 
+    /**
+     * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
+     */
     private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
         for (int x = left; x <= right; x++) {
             if (occupied[x][y]) {
@@ -372,7 +414,7 @@
                 }
             }
         } else {
-            findOccupiedCells(xCount, yCount, occupied, ignoreView);
+            findOccupiedCells(xCount, yCount, occupied, ignoreView, true);
         }
 
         CellInfo cellInfo = new CellInfo();
@@ -600,7 +642,7 @@
                 if (lp.dropped) {
                     lp.dropped = false;
 
-                    final int[] cellXY = mCellXY;
+                    final int[] cellXY = mTmpCellXY;
                     getLocationOnScreen(cellXY);
                     mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
                             cellXY[0] + childLeft + lp.width / 2,
@@ -626,6 +668,79 @@
         super.setChildrenDrawnWithCacheEnabled(enabled);
     }
 
+    private boolean isVacant(int originX, int originY, int spanX, int spanY) {
+        for (int i = 0; i < spanY; i++) {
+            if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Estimate where the top left cell of the dragged item will land if it is dropped.
+     *
+     * @param originX The X value of the top left corner of the item
+     * @param originY The Y value of the top left corner of the item
+     * @param spanX The number of horizontal cells that the item spans
+     * @param spanY The number of vertical cells that the item spans
+     * @param result The estimated drop cell X and Y.
+     */
+    void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
+        final int countX = getCountX();
+        final int countY = getCountY();
+
+        pointToCellRounded(originX, originY, result);
+
+        // If the item isn't fully on this screen, snap to the edges
+        int rightOverhang = result[0] + spanX - countX;
+        if (rightOverhang > 0) {
+            result[0] -= rightOverhang; // Snap to right
+        }
+        result[0] = Math.max(0, result[0]); // Snap to left
+        int bottomOverhang = result[1] + spanY - countY;
+        if (bottomOverhang > 0) {
+            result[1] -= bottomOverhang; // Snap to bottom
+        }
+        result[1] = Math.max(0, result[1]); // Snap to top
+    }
+
+    void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) {
+        final int[] originCell = mDragCell;
+        final int[] cellXY = mTmpCellXY;
+        estimateDropCell(originX, originY, spanX, spanY, cellXY);
+
+        // Only recalculate the bounding rect when necessary
+        if (!Arrays.equals(cellXY, originCell)) {
+            originCell[0] = cellXY[0];
+            originCell[1] = cellXY[1];
+
+            // Find the top left corner of the rect the object will occupy
+            final int[] topLeft = mTmpCellXY;
+            cellToPoint(originCell[0], originCell[1], topLeft);
+            final int left = topLeft[0];
+            final int top = topLeft[1];
+
+            // Now find the bottom right
+            final int[] bottomRight = mTmpCellXY;
+            cellToPoint(originCell[0] + spanX - 1, originCell[1] + spanY - 1, bottomRight);
+            bottomRight[0] += mCellWidth;
+            bottomRight[1] += mCellHeight;
+
+            final int countX = mPortrait ? mShortAxisCells : mLongAxisCells;
+            final int countY = mPortrait ? mLongAxisCells : mShortAxisCells;
+            // 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);
+            mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable;
+
+            // mDragRect will be rendered in onDraw()
+            mDragRect.set(left, top, bottomRight[0], bottomRight[1]);
+            invalidate();
+        }
+    }
+
     /**
      * Find a vacant area that will fit the given bounds nearest the requested
      * cell location. Uses Euclidean distance to score multiple vacant areas.
@@ -644,7 +759,6 @@
 
         // Keep track of best-scoring drop area
         final int[] bestXY = recycle != null ? recycle : new int[2];
-        final int[] cellXY = mCellXY;
         double bestDistance = Double.MAX_VALUE;
 
         // Bail early if vacant cells aren't valid
@@ -662,7 +776,8 @@
                 continue;
             }
 
-            // Score is center distance from requested pixel
+            // Score is distance from requested pixel to the top left of each cell
+            final int[] cellXY = mTmpCellXY;
             cellToPoint(cell.cellX, cell.cellY, cellXY);
 
             double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
@@ -683,6 +798,18 @@
     }
 
     /**
+     * Called when a drag and drop operation has finished (successfully or not).
+     */
+    void onDragComplete() {
+        // Invalidate the drag data
+        mDragCell[0] = -1;
+        mDragCell[1] = -1;
+
+        mDragRect.setEmpty();
+        invalidate();
+    }
+
+    /**
      * Mark a child as having been dropped.
      *
      * @param child The child that is being dropped
@@ -694,16 +821,15 @@
             lp.dropped = true;
             mDragRect.setEmpty();
             child.requestLayout();
-            invalidate();
         }
+        onDragComplete();
     }
 
     void onDropAborted(View child) {
         if (child != null) {
             ((LayoutParams) child.getLayoutParams()).isDragging = false;
-            invalidate();
         }
-        mDragRect.setEmpty();
+        onDragComplete();
     }
 
     /**
@@ -718,30 +844,16 @@
     }
 
     /**
-     * Drag a child over the specified position
-     *
-     * @param child The child that is being dropped
-     * @param cellX The child's new x cell location
-     * @param cellY The child's new y cell location
-     */
-    void onDragOverChild(View child, int cellX, int cellY) {
-        int[] cellXY = mCellXY;
-        pointToCellRounded(cellX, cellY, cellXY);
-        LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        cellToRect(cellXY[0], cellXY[1], lp.cellHSpan, lp.cellVSpan, mDragRect);
-        invalidate();
-    }
-
-    /**
      * Computes a bounding rectangle for a range of cells
      *
      * @param cellX X coordinate of upper left corner expressed as a cell position
      * @param cellY Y coordinate of upper left corner expressed as a cell position
      * @param cellHSpan Width in cells
      * @param cellVSpan Height in cells
-     * @param dragRect Rectnagle into which to put the results
+     * @param resultRect Rect into which to put the results
      */
-    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF dragRect) {
+    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
+        final boolean portrait = mPortrait;
         final int cellWidth = mCellWidth;
         final int cellHeight = mCellHeight;
         final int widthGap = mWidthGap;
@@ -756,7 +868,7 @@
         int x = hStartPadding + cellX * (cellWidth + widthGap);
         int y = vStartPadding + cellY * (cellHeight + heightGap);
 
-        dragRect.set(x, y, x + width, y + height);
+        resultRect.set(x, y, x + width, y + height);
     }
 
     /**
@@ -796,7 +908,7 @@
         final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
         final boolean[][] occupied = mOccupied;
 
-        findOccupiedCells(xCount, yCount, occupied, null);
+        findOccupiedCells(xCount, yCount, occupied, null, true);
 
         return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
     }
@@ -825,13 +937,16 @@
         return false;
     }
 
-    boolean[] getOccupiedCells() {
+    /**
+     * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
+     */
+    boolean[] getOccupiedCellsFlattened() {
         final boolean portrait = mPortrait;
         final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
         final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
         final boolean[][] occupied = mOccupied;
 
-        findOccupiedCells(xCount, yCount, occupied, null);
+        findOccupiedCells(xCount, yCount, occupied, null, true);
 
         final boolean[] flat = new boolean[xCount * yCount];
         for (int y = 0; y < yCount; y++) {
@@ -843,7 +958,14 @@
         return flat;
     }
 
-    private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied, View ignoreView) {
+    /**
+     * 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;
@@ -853,7 +975,7 @@
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
-            if (child instanceof Folder || child.equals(ignoreView)) {
+            if ((ignoreFolders && child instanceof Folder) || child.equals(ignoreView)) {
                 continue;
             }
             LayoutParams lp = (LayoutParams) child.getLayoutParams();
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 03066c1..94556be 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -1088,7 +1088,7 @@
             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.getOccupiedCells());
+                   layout.getOccupiedCellsFlattened());
         }
 
         if (mFolderInfo != null && mWaitingForResult) {
diff --git a/src/com/android/launcher2/LauncherAppWidgetInfo.java b/src/com/android/launcher2/LauncherAppWidgetInfo.java
index 3c81bac..32c92aa 100644
--- a/src/com/android/launcher2/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher2/LauncherAppWidgetInfo.java
@@ -21,23 +21,46 @@
 import android.content.ContentValues;
 
 /**
- * Represents a widget, which just contains an identifier.
+ * Represents a widget (either instantiated or about to be) in the Launcher.
  */
 class LauncherAppWidgetInfo extends ItemInfo {
 
     /**
+     * Indicates that the widget hasn't been instantiated yet.
+     */
+    static final int NO_ID = -1;
+
+    /**
      * Identifier for this widget when talking with
      * {@link android.appwidget.AppWidgetManager} for updates.
      */
-    int appWidgetId;
+    int appWidgetId = NO_ID;
+
     ComponentName providerName;
     
+    // TODO: Are these necessary here?
+    int minWidth = -1;
+    int minHeight = -1;
+
     /**
      * View that holds this widget after it's been created.  This view isn't created
      * until Launcher knows it's needed.
      */
     AppWidgetHostView hostView = null;
 
+    /**
+     * Constructor for use with AppWidgets that haven't been instantiated yet.
+     */
+    LauncherAppWidgetInfo(ComponentName providerName) {
+        itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+        this.providerName = providerName;
+
+        // Since the widget isn't instantiated yet, we don't know these values. Set them to -1
+        // to indicate that they should be calculated based on the layout and minWidth/minHeight
+        spanX = -1;
+        spanY = -1;
+    }
+
     LauncherAppWidgetInfo(int appWidgetId) {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
         this.appWidgetId = appWidgetId;
@@ -54,7 +77,6 @@
         return "AppWidget(id=" + Integer.toString(appWidgetId) + ")";
     }
 
-
     @Override
     void unbind() {
         super.unbind();
diff --git a/src/com/android/launcher2/WidgetChooser.java b/src/com/android/launcher2/WidgetChooser.java
index 4718c6c..101b671 100644
--- a/src/com/android/launcher2/WidgetChooser.java
+++ b/src/com/android/launcher2/WidgetChooser.java
@@ -34,8 +34,11 @@
             int screenX = mMotionDownRawX - (w / 2);
             int screenY = mMotionDownRawY - h;
 
-            LauncherAppWidgetInfo dragInfo = new LauncherAppWidgetInfo(-1);
-            dragInfo.providerName = info.provider;
+            AppWidgetProviderInfo appWidgetInfo = (AppWidgetProviderInfo)view.getTag();
+            LauncherAppWidgetInfo dragInfo = new LauncherAppWidgetInfo(info.provider);
+            // TODO: Is this really the best place to do this?
+            dragInfo.minWidth = appWidgetInfo.minWidth;
+            dragInfo.minHeight = appWidgetInfo.minHeight;
             mDragController.startDrag(bmp, screenX, screenY,
                     0, 0, w, h, this, dragInfo, DragController.DRAG_ACTION_COPY);
             return true;
diff --git a/src/com/android/launcher2/WidgetListAdapter.java b/src/com/android/launcher2/WidgetListAdapter.java
index 5a569ea..5d5d86a 100644
--- a/src/com/android/launcher2/WidgetListAdapter.java
+++ b/src/com/android/launcher2/WidgetListAdapter.java
@@ -85,6 +85,8 @@
         image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
         textView.setCompoundDrawables(null, image, null, null);
         textView.setText(info.label);
+        // Store the widget info on the associated view so we can easily fetch it later
+        textView.setTag(info);
 
         return textView;
     }
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 57195ad..67b543e 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -28,6 +28,7 @@
 import android.content.pm.ProviderInfo;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -43,9 +44,9 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
 import android.view.animation.Interpolator;
 import android.view.animation.RotateAnimation;
-import android.view.animation.Animation.AnimationListener;
 import android.widget.Scroller;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -90,6 +91,11 @@
      */
     private int[] mTargetCell = null;
 
+    /**
+     * The CellLayout that is currently being dragged over
+     */
+    private CellLayout mDragTargetLayout = null;
+
     private float mLastMotionX;
     private float mLastMotionY;
 
@@ -136,6 +142,8 @@
     private static final float BASELINE_FLING_VELOCITY = 2500.f;
     private static final float FLING_VELOCITY_INFLUENCE = 0.4f;
 
+    private Paint mDropIndicatorPaint;
+
     private static class WorkspaceOvershootInterpolator implements Interpolator {
         private static final float DEFAULT_TENSION = 1.3f;
         private float mTension;
@@ -480,13 +488,12 @@
             lp.cellVSpan = spanY;
         }
 
-        // get the canonical child id to uniquely represent this view in this
-        // screen
+        // Get the canonical child id to uniquely represent this view in this screen
         int childId = LauncherModel.getCanonicalCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY);
         if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) {
             // TODO: This branch occurs when the workspace is adding views
             // outside of the defined grid
-            // maybe we should be deleting these items from the LauncherMode?
+            // maybe we should be deleting these items from the LauncherModel?
             Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
         }
 
@@ -1212,13 +1219,45 @@
         clearVacantCache();
     }
 
-    public void onDragOver(DragSource source, int x, int y, int xOffset,
-            int yOffset, DragView dragView, Object dragInfo) {
+    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
+            DragView dragView, Object dragInfo) {
+
+        ItemInfo item = (ItemInfo)dragInfo;
+
+        CellLayout currentLayout = getCurrentDropLayout();
+
+        if (dragInfo instanceof LauncherAppWidgetInfo) {
+            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
+
+            if (widgetInfo.spanX == -1) {
+                // Calculate the grid spans needed to fit this widget
+                int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight);
+                item.spanX = spans[0];
+                item.spanY = spans[1];
+            }
+        }
+        if (currentLayout != mDragTargetLayout) {
+            if (mDragTargetLayout != null) {
+                mDragTargetLayout.onDragComplete();
+            }
+            mDragTargetLayout = currentLayout;
+        }
+
+        // Find the top left corner of the item
+        int originX = x - xOffset;
+        int originY = y - yOffset;
+
+        final View child = (mDragInfo == null) ? null : mDragInfo.cell;
+        currentLayout.visualizeDropLocation(child, originX, originY, item.spanX, item.spanY);
     }
 
     public void onDragExit(DragSource source, int x, int y, int xOffset,
             int yOffset, DragView dragView, Object dragInfo) {
         clearVacantCache();
+        if (mDragTargetLayout != null) {
+            mDragTargetLayout.onDragComplete();
+            mDragTargetLayout = null;
+        }
     }
 
     private void onDropExternal(int x, int y, Object dragInfo,
@@ -1257,16 +1296,15 @@
                     + info.itemType);
         }
 
-        // addAppWidgetFromDrop already took care of attaching the widget view to the appropriate cell
-        // TODO why aren't we calling addInScreen here?
-        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
-            mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout,
-                    mTargetCell);
+        // If the view is null, it has already been added.
+        if (view == null) {
+            cellLayout.onDragComplete();
+        } else {
+            mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
             addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
                     mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
             cellLayout.onDropChild(view);
-            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view
-                    .getLayoutParams();
+            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
 
             LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
                     LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen,
@@ -1344,14 +1382,18 @@
      */
     private int[] estimateDropCell(int pixelX, int pixelY,
             int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
+
+        final int[] cellXY = mTempCell;
+        layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY);
+        layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate);
+
         // Create vacant cell cache if none exists
         if (mVacantCache == null) {
             mVacantCache = layout.findAllVacantCells(null, ignoreView);
         }
 
         // Find the best target drop location
-        return layout.findNearestVacantArea(pixelX, pixelY,
-                spanX, spanY, mVacantCache, recycle);
+        return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, mVacantCache, recycle);
     }
 
     void setLauncher(Launcher launcher) {