Merge "Cleaning up drag and drop visulization and drop location determination"
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index c05fe75..3d3c1ff 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -24,6 +24,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -290,6 +291,16 @@
         c.translate((v.getWidth() - icon.getIntrinsicWidth()) / 2, v.getPaddingTop());
         icon.draw(c);
 
+        Rect dragRect = null;
+        if (v instanceof TextView) {
+            int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
+            int top = v.getPaddingTop();
+            int left = (b.getWidth() - iconSize) / 2;
+            int right = left + iconSize;
+            int bottom = top + iconSize;
+            dragRect = new Rect(left, top, right, bottom);
+        }
+
         // We toggle the checked state _after_ we create the view for the drag in case toggling the
         // checked state changes the view's look
         if (v instanceof Checkable) {
@@ -309,7 +320,7 @@
         // Start the drag
         mLauncher.lockScreenOrientation();
         mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1, b);
-        mDragController.startDrag(v, b, this, app, DragController.DRAG_ACTION_COPY, null);
+        mDragController.startDrag(v, b, this, app, DragController.DRAG_ACTION_COPY, dragRect);
         b.recycle();
         return true;
     }
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index dd0c58a..ac1a4bd 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -736,6 +736,22 @@
         result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
     }
 
+    /**
+     * Given a cell coordinate, return the point that represents the upper left corner of that cell
+     *
+     * @param cellX X coordinate of the cell
+     * @param cellY Y coordinate of the cell
+     *
+     * @param result Array of 2 ints to hold the x and y coordinate of the point
+     */
+    void cellToCenterPoint(int cellX, int cellY, int[] result) {
+        final int hStartPadding = getLeftPadding();
+        final int vStartPadding = getTopPadding();
+
+        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) + mCellWidth / 2;
+        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) + mCellHeight / 2;
+    }
+
     int getCellWidth() {
         return mCellWidth;
     }
@@ -1021,6 +1037,12 @@
         // mark space take by ignoreView as available (method checks if ignoreView is null)
         markCellsAsUnoccupiedForView(ignoreView);
 
+        // For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds
+        // to the center of the item, but we are searching based on the top-left cell, so
+        // we translate the point over to correspond to the top-left.
+        pixelX -= (mCellWidth + mWidthGap) * (spanX - 1) / 2f;
+        pixelY -= (mCellHeight + mHeightGap) * (spanY - 1) / 2f;
+
         // Keep track of best-scoring drop area
         final int[] bestXY = result != null ? result : new int[2];
         double bestDistance = Double.MAX_VALUE;
@@ -1045,7 +1067,7 @@
                     }
                 }
                 final int[] cellXY = mTmpCellXY;
-                cellToPoint(x, y, cellXY);
+                cellToCenterPoint(x, y, cellXY);
 
                 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
                         + Math.pow(cellXY[1] - pixelY, 2));
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index cb4509b..5ca1e1c 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -73,10 +73,10 @@
     private boolean mDragging;
 
     /** X coordinate of the down event. */
-    private float mMotionDownX;
+    private int mMotionDownX;
 
     /** Y coordinate of the down event. */
-    private float mMotionDownY;
+    private int mMotionDownY;
 
     /** Info about the screen for clamping. */
     private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -85,10 +85,10 @@
     private View mOriginator;
 
     /** X offset from the upper-left corner of the cell to where we touched.  */
-    private float mTouchOffsetX;
+    private int mTouchOffsetX;
 
     /** Y offset from the upper-left corner of the cell to where we touched.  */
-    private float mTouchOffsetY;
+    private int mTouchOffsetY;
 
     /** the area at the edge of the screen that makes the workspace go left
      *   or right while you're dragging.
@@ -289,13 +289,14 @@
             listener.onDragStart(source, dragInfo, dragAction);
         }
 
-        int registrationX = ((int)mMotionDownX) - screenX;
-        int registrationY = ((int)mMotionDownY) - screenY;
+        final int registrationX = ((int)mMotionDownX) - screenX;
+        final int registrationY = ((int)mMotionDownY) - screenY;
 
         final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
         final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
-        mTouchOffsetX = mMotionDownX - screenX - dragRegionLeft;
-        mTouchOffsetY = mMotionDownY - screenY - dragRegionTop;
+
+        mTouchOffsetX = mMotionDownX - (screenX + dragRegionLeft);
+        mTouchOffsetY = mMotionDownY - (screenY + dragRegionTop);
 
         mDragging = true;
         mDragSource = source;
@@ -314,8 +315,7 @@
         });
 
         if (dragRegion != null) {
-            dragView.setDragRegion(dragRegionLeft, dragRegion.top,
-                    dragRegion.right - dragRegionLeft, dragRegion.bottom - dragRegionTop);
+            dragView.setDragRegion(new Rect(dragRegion));
         }
 
         dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index ab99744..b02e22b 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -26,6 +26,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.view.Gravity;
 import android.view.View;
@@ -42,10 +43,7 @@
     private int mRegistrationX;
     private int mRegistrationY;
 
-    private int mDragRegionLeft = 0;
-    private int mDragRegionTop = 0;
-    private int mDragRegionWidth;
-    private int mDragRegionHeight;
+    private Rect mDragRegion = null;
 
     ValueAnimator mAnim;
     private float mOffsetX = 0.0f;
@@ -117,7 +115,7 @@
         });
 
         mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
-        setDragRegion(0, 0, width, height);
+        setDragRegion(new Rect(0, 0, width, height));
 
         // The point in our scaled bitmap that the touch events are located
         mRegistrationX = registrationX;
@@ -132,31 +130,32 @@
         return mOffsetY;
     }
 
-    public void setDragRegion(int left, int top, int width, int height) {
-        mDragRegionLeft = left;
-        mDragRegionTop = top;
-        mDragRegionWidth = width;
-        mDragRegionHeight = height;
-    }
-
     public void setOnDrawRunnable(Runnable r) {
         mOnDrawRunnable = r;
     }
 
     public int getDragRegionLeft() {
-        return mDragRegionLeft;
+        return mDragRegion.left;
     }
 
     public int getDragRegionTop() {
-        return mDragRegionTop;
+        return mDragRegion.top;
     }
 
     public int getDragRegionWidth() {
-        return mDragRegionWidth;
+        return mDragRegion.width();
     }
 
     public int getDragRegionHeight() {
-        return mDragRegionHeight;
+        return mDragRegion.height();
+    }
+
+    public void setDragRegion(Rect r) {
+        mDragRegion = r;
+    }
+
+    public Rect getDragRegion() {
+        return mDragRegion;
     }
 
     @Override
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 4e5169a..66f1ceb 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -155,6 +155,7 @@
     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
     private int[] mTempCell = new int[2];
     private int[] mTempEstimate = new int[2];
+    private float[] mDragViewVisualCenter = new float[2];
     private float[] mTempOriginXY = new float[2];
     private float[] mTempDragCoordinates = new float[2];
     private float[] mTempTouchCoordinates = new float[2];
@@ -2132,12 +2133,23 @@
 
         final int bmpWidth = b.getWidth();
         final int bmpHeight = b.getHeight();
+
         child.getLocationOnScreen(mTempXY);
         final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
         final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2;
-        mLauncher.lockScreenOrientation();
-        mDragController.startDrag(
-                b, screenX, screenY, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
+
+        Rect dragRect = null;
+        if (child instanceof BubbleTextView) {
+            int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
+            int top = child.getPaddingTop();
+            int left = (bmpWidth - iconSize) / 2;
+            int right = left + iconSize;
+            int bottom = top + iconSize;
+            dragRect = new Rect(left, top, right, bottom);
+        }
+
+        mDragController.startDrag(b, screenX, screenY, this, child.getTag(),
+                DragController.DRAG_ACTION_MOVE, dragRect);
         b.recycle();
     }
 
@@ -2161,8 +2173,6 @@
         // Based on the position of the drag view, find the top left of the original view
         int viewX = dragViewX + (dragView.getWidth() - child.getMeasuredWidth()) / 2;
         int viewY = dragViewY + (dragView.getHeight() - child.getMeasuredHeight()) / 2;
-        viewX += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
-        viewY += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
 
         // Set its old pos (in the new parent's coordinates); it will be animated
         // in animateViewIntoPosition after the next layout pass
@@ -2265,22 +2275,12 @@
 
     public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
-        boolean largeOrSpringLoaded = !mIsSmall || mWasSpringLoadedOnDragExit;
-        int originX = largeOrSpringLoaded ? x - xOffset : x - xOffset + dragView.getWidth() / 2;
-        int originY = largeOrSpringLoaded ? y - yOffset : y - yOffset + dragView.getHeight() / 2;
 
-        if (mIsSmall || mIsInUnshrinkAnimation) {
-            // get originX and originY in the local coordinate system of the screen
-            mTempOriginXY[0] = originX;
-            mTempOriginXY[1] = originY;
-            mapPointFromSelfToChild(mDragTargetLayout, mTempOriginXY);
-            originX = (int)mTempOriginXY[0];
-            originY = (int)mTempOriginXY[1];
-            if (!largeOrSpringLoaded) {
-               originX -= mDragTargetLayout.getCellWidth() / 2;
-               originY -= mDragTargetLayout.getCellHeight() / 2;
-            }
-        }
+        mDragViewVisualCenter = getDragViewVisualCenter(x, y, xOffset, yOffset, dragView,
+                mDragViewVisualCenter);
+
+        // We want the point to be mapped to the dragTarget.
+        mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
 
         // When you are in customization mode and drag to a particular screen, make that the
         // new current/default screen, so any subsequent taps add items to that screen
@@ -2292,14 +2292,15 @@
         }
 
         if (source != this) {
-            final int[] touchXY = new int[] { originX, originY };
+            final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
+                    (int) mDragViewVisualCenter[1] };
             if ((mIsSmall || mIsInUnshrinkAnimation) && !mLauncher.isAllAppsVisible()) {
                 // When the workspace is shrunk and the drop comes from customize, don't actually
                 // add the item to the screen -- customize will do this itself
                 ((ItemInfo) dragInfo).dropPos = touchXY;
                 return;
             }
-            onDropExternal(touchXY, dragInfo, mDragTargetLayout, false, dragView, originX, originY);
+            onDropExternal(touchXY, dragInfo, mDragTargetLayout, false, dragView);
         } else if (mDragInfo != null) {
             final View cell = mDragInfo.cell;
             CellLayout dropTargetLayout = mDragTargetLayout;
@@ -2319,9 +2320,9 @@
 
                 // First we find the cell nearest to point at which the item is dropped, without
                 // any consideration to whether there is an item there.
-                mTargetCell = findNearestArea(originX, originY,
-                        mDragInfo.spanX, mDragInfo.spanY, dropTargetLayout,
-                        mTargetCell);
+                mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
+                        (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY,
+                        dropTargetLayout, mTargetCell);
 
                 final int screen = (mTargetCell == null) ?
                         mDragInfo.screen : indexOfChild(dropTargetLayout);
@@ -2345,9 +2346,9 @@
 
                 // Aside from the special case where we're dropping a shortcut onto a shortcut,
                 // we need to find the nearest cell location that is vacant
-                mTargetCell = findNearestVacantArea(originX, originY,
-                        mDragInfo.spanX, mDragInfo.spanY, cell, dropTargetLayout,
-                        mTargetCell);
+                mTargetCell = findNearestVacantArea((int) mDragViewVisualCenter[0],
+                        (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY, cell,
+                        dropTargetLayout, mTargetCell);
 
                 if (screen != mCurrentPage) {
                     snapToPage(screen);
@@ -2406,14 +2407,30 @@
 
             final CellLayout parent = (CellLayout) cell.getParent().getParent();
 
+            int loc[] = new int[2];
+            getViewLocationRelativeToSelf(dragView, loc);
+
             // Prepare it to be animated into its new position
             // This must be called after the view has been re-parented
-            setPositionForDropAnimation(dragView, originX, originY, parent, cell);
+            setPositionForDropAnimation(dragView, loc[0], loc[1], parent, cell);
             boolean animateDrop = !mWasSpringLoadedOnDragExit;
             parent.onDropChild(cell, animateDrop);
         }
     }
 
+    private void getViewLocationRelativeToSelf(View v, int[] location) {
+        getLocationOnScreen(location);
+        int x = location[0];
+        int y = location[1];
+
+        v.getLocationOnScreen(location);
+        int vX = location[0];
+        int vY = location[1];
+
+        location[0] = vX - x;
+        location[1] = vY - y;
+    }
+
     public void onDragEnter(DragSource source, int x, int y, int xOffset,
             int yOffset, DragView dragView, Object dragInfo) {
         mDragTargetLayout = null; // Reset the drag state
@@ -2642,11 +2659,11 @@
        xy[1] -= (mScrollY - v.getTop());
    }
 
-    static private float squaredDistance(float[] point1, float[] point2) {
+   static private float squaredDistance(float[] point1, float[] point2) {
         float distanceX = point1[0] - point2[0];
         float distanceY = point2[1] - point2[1];
         return distanceX * distanceX + distanceY * distanceY;
-    }
+   }
 
     /*
      *
@@ -2744,21 +2761,57 @@
         return bestMatchingScreen;
     }
 
+    // This is used to compute the visual center of the dragView. This point is then
+    // used to visualize drop locations and determine where to drop an item. The idea is that
+    // the visual center represents the user's interpretation of where the item is, and hence
+    // is the appropriate point to use when determining drop location.
+    private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
+            DragView dragView, float[] recycle) {
+        float res[];
+        if (recycle == null) {
+            res = new float[2];
+        } else {
+            res = recycle;
+        }
+
+        // First off, the drag view has been shifted in a way that is not represented in the
+        // x and y values or the x/yOffsets. Here we account for that shift.
+        x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
+        y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
+
+        // These represent the visual top and left of drag view if a dragRect was provided.
+        // If a dragRect was not provided, then they correspond to the actual view left and
+        // top, as the dragRect is in that case taken to be the entire dragView.
+        // R.dimen.dragViewOffsetY.
+        int left = x - xOffset;
+        int top = y - yOffset;
+
+        // In order to find the visual center, we shift by half the dragRect
+        res[0] = left + dragView.getDragRegion().width() / 2;
+        res[1] = top + dragView.getDragRegion().height() / 2;
+
+        return res;
+    }
+
     public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
         // When touch is inside the scroll area, skip dragOver actions for the current screen
         if (!mInScrollArea) {
             CellLayout layout;
-            int originX = x - xOffset;
-            int originY = y - yOffset;
+            int left = x - xOffset;
+            int top = y - yOffset;
+
+            mDragViewVisualCenter = getDragViewVisualCenter(x, y, xOffset, yOffset, dragView,
+                    mDragViewVisualCenter);
+
             boolean shrunken = mIsSmall || mIsInUnshrinkAnimation;
             if (shrunken) {
                 mLastDragView = dragView;
-                mLastDragOriginX = originX;
-                mLastDragOriginY = originY;
+                mLastDragOriginX = left;
+                mLastDragOriginY = top;
                 mLastDragXOffset = xOffset;
                 mLastDragYOffset = yOffset;
-                layout = findMatchingPageForDragOver(dragView, originX, originY, xOffset, yOffset);
+                layout = findMatchingPageForDragOver(dragView, left, top, xOffset, yOffset);
 
                 if (layout != mDragTargetLayout) {
                     if (mDragTargetLayout != null) {
@@ -2804,35 +2857,14 @@
                     }
                 }
 
-                if (source instanceof AllAppsPagedView) {
-                    // This is a hack to fix the point used to determine which cell an icon from
-                    // the all apps screen is over
-                    if (item != null && item.spanX == 1 && layout != null) {
-                        int dragRegionLeft = (dragView.getWidth() - layout.getCellWidth()) / 2;
-
-                        originX += dragRegionLeft - dragView.getDragRegionLeft();
-                        if (dragView.getDragRegionWidth() != layout.getCellWidth()) {
-                            dragView.setDragRegion(dragView.getDragRegionLeft(),
-                                    dragView.getDragRegionTop(),
-                                    layout.getCellWidth(),
-                                    dragView.getDragRegionHeight());
-                        }
-                    }
-                } else if (source == this) {
-                    // When dragging from the workspace, the drag view is slightly bigger than
-                    // the original view, and offset vertically. Adjust to account for this.
-                    final View origView = mDragInfo.cell;
-                    originX += (dragView.getMeasuredWidth() - origView.getWidth()) / 2;
-                    originY += (dragView.getMeasuredHeight() - origView.getHeight()) / 2
-                            + dragView.getOffsetY();
-                }
-
                 if (mDragTargetLayout != null) {
                     final View child = (mDragInfo == null) ? null : mDragInfo.cell;
-                    float[] localOrigin = { originX, originY };
-                    mapPointFromSelfToChild(mDragTargetLayout, localOrigin, null);
+                    // We want the point to be mapped to the dragTarget.
+                    mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
                     mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
-                            (int) localOrigin[0], (int) localOrigin[1], item.spanX, item.spanY);
+                            (int) mDragViewVisualCenter[0],
+                            (int) mDragViewVisualCenter[1],
+                            item.spanX, item.spanY);
                 }
             }
         }
@@ -2880,7 +2912,7 @@
 
     private void onDropExternal(int[] touchXY, Object dragInfo,
             CellLayout cellLayout, boolean insertAtFirst) {
-        onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null, 0, 0);
+        onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
     }
 
     /**
@@ -2892,8 +2924,7 @@
      * to add an item to one of the workspace screens.
      */
     private void onDropExternal(int[] touchXY, Object dragInfo,
-            CellLayout cellLayout, boolean insertAtFirst, DragView dragView,
-            int dragViewX, int dragViewY) {
+            CellLayout cellLayout, boolean insertAtFirst, DragView dragView) {
         int screen = indexOfChild(cellLayout);
         if (dragInfo instanceof PendingAddItemInfo) {
             PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
@@ -2939,7 +2970,8 @@
             mTargetCell = new int[2];
             if (touchXY != null) {
                 // when dragging and dropping, just find the closest free spot
-                cellLayout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, mTargetCell);
+                mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, null, cellLayout,
+                        mTargetCell);
             } else {
                 cellLayout.findCellForSpan(mTargetCell, 1, 1);
             }
@@ -2952,7 +2984,11 @@
             cellLayout.getChildrenLayout().measureChild(view);
 
             if (dragView != null) {
-                setPositionForDropAnimation(dragView, dragViewX, dragViewY, cellLayout, view);
+                // we have the visual center of the drag view, we need to find the actual
+                // left and top of the dragView.
+                int loc[] = new int[2];
+                getViewLocationRelativeToSelf(dragView, loc);
+                setPositionForDropAnimation(dragView, loc[0], loc[1], cellLayout, view);
             }
 
             LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
@@ -2981,30 +3017,24 @@
 
     /**
      * Calculate the nearest cell where the given object would be dropped.
+     *
+     * pixelX and pixelY should be in the coordinate system of layout
      */
     private int[] findNearestVacantArea(int pixelX, int pixelY,
             int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
-
-        int localPixelX = pixelX - (layout.getLeft() - mScrollX);
-        int localPixelY = pixelY - (layout.getTop() - mScrollY);
-
-        // Find the best target drop location
         return layout.findNearestVacantArea(
-                localPixelX, localPixelY, spanX, spanY, ignoreView, recycle);
+                pixelX, pixelY, spanX, spanY, ignoreView, recycle);
     }
 
     /**
      * Calculate the nearest cell where the given object would be dropped.
+     *
+     * pixelX and pixelY should be in the coordinate system of layout
      */
     private int[] findNearestArea(int pixelX, int pixelY,
-            int spanX, int spanY,CellLayout layout, int[] recycle) {
-
-        int localPixelX = pixelX - (layout.getLeft() - mScrollX);
-        int localPixelY = pixelY - (layout.getTop() - mScrollY);
-
-        // Find the best target drop location
+            int spanX, int spanY, CellLayout layout, int[] recycle) {
         return layout.findNearestArea(
-                localPixelX, localPixelY, spanX, spanY, recycle);
+                pixelX, pixelY, spanX, spanY, recycle);
     }
 
     void setLauncher(Launcher launcher) {