Move drop anim into the Workspace to prevent clipping issues
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index b7491ba..95c976b 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -125,7 +125,6 @@
 
     private boolean mDragging = false;
 
-    private ValueAnimator mDropAnim;
     private TimeInterpolator mEaseOutInterpolator;
 
     public CellLayout(Context context) {
@@ -255,9 +254,6 @@
             mDragOutlineAnims[i] = anim;
         }
 
-        mDropAnim = ValueAnimator.ofFloat(1.0f, 0.0f);
-        mDropAnim.setInterpolator(mEaseOutInterpolator);
-
         mBackgroundRect = new Rect();
         mHoverRect = new Rect();
         setHoverScale(1.0f);
@@ -776,37 +772,6 @@
         }
     }
 
-    /**
-     * Animate a child of this CellLayout into its current layout position.
-     * The position to animate from is given by the oldX and oldY values in its LayoutParams.
-     */
-    private void animateChildIntoPosition(final View child) {
-        final Resources res = getResources();
-        final ValueAnimator anim = mDropAnim;
-        final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-        final float startX = lp.oldX - lp.x;
-        final float startY = lp.oldY - lp.y;
-
-        // Calculate the duration of the animation based on the object's distance
-        final float dist = (float) Math.sqrt(startX*startX + startY*startY);
-        final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
-        final int duration = (int) (res.getInteger(R.integer.config_dropAnimMaxDuration)
-                * mEaseOutInterpolator.getInterpolation(dist / maxDist));
-
-        anim.end(); // Make sure it's not already running
-        anim.setDuration(duration);
-        anim.setFloatValues(1.0f, 0.0f);
-        anim.removeAllUpdateListeners();
-        anim.addUpdateListener(new AnimatorUpdateListener() {
-            public void onAnimationUpdate(ValueAnimator animation) {
-                final float value = (Float) anim.getAnimatedValue();
-                child.setTranslationX(startX * value);
-                child.setTranslationY(startY * value);
-            }
-        });
-        anim.start();
-    }
-
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         int count = getChildCount();
@@ -830,7 +795,7 @@
                             cellXY[0] + childLeft + lp.width / 2,
                             cellXY[1] + childTop + lp.height / 2, 0, null);
 
-                    animateChildIntoPosition(child);
+                    ((Workspace) mParent).animateViewIntoPosition(child);
                 }
             }
         }
@@ -1259,15 +1224,6 @@
         }
     }
 
-    void onDropAborted(View child) {
-        if (child != null) {
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            lp.isDragging = false;
-            child.setVisibility(View.VISIBLE);
-            animateChildIntoPosition(child);
-        }
-    }
-
     /**
      * Start dragging the specified child
      *
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 5a31e9b..dfc664d 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -25,6 +25,9 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.WallpaperManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -53,6 +56,7 @@
 import android.view.DragEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.animation.DecelerateInterpolator;
 import android.widget.TextView;
 import android.widget.Toast;
 
@@ -151,6 +155,11 @@
     private final Rect mTempRect = new Rect();
     private final int[] mTempXY = new int[2];
 
+    private ValueAnimator mDropAnim = null;
+    private TimeInterpolator mQuintEaseOutInterpolator = new DecelerateInterpolator(2.5f);
+    private View mDropView = null;
+    private int[] mDropViewPos = new int[] { -1, -1 };
+
     // Paint used to draw external drop outline
     private final Paint mExternalDragOutlinePaint = new Paint();
 
@@ -204,6 +213,7 @@
         LauncherApplication app = (LauncherApplication)context.getApplicationContext();
         mIconCache = app.getIconCache();
         mExternalDragOutlinePaint.setAntiAlias(true);
+        setWillNotDraw(false);
 
         mUnshrinkAnimationListener = new AnimatorListenerAdapter() {
             public void onAnimationStart(Animator animation) {
@@ -579,6 +589,15 @@
             }
         } else {
             super.dispatchDraw(canvas);
+
+            if (mDropView != null) {
+                canvas.save(Canvas.MATRIX_SAVE_FLAG);
+                final int xPos = mDropViewPos[0] - mDropView.getScrollX();
+                final int yPos = mDropViewPos[1] - mDropView.getScrollY();
+                canvas.translate(xPos, yPos);
+                mDropView.draw(canvas);
+                canvas.restore();
+            }
         }
     }
 
@@ -1052,6 +1071,7 @@
         CellLayout current = getCurrentDropLayout();
 
         current.onDragChild(child);
+        child.setVisibility(View.GONE);
 
         child.clearFocus();
         child.setPressed(false);
@@ -1100,28 +1120,106 @@
         viewX -= getResources().getInteger(R.integer.config_dragViewOffsetX);
         viewY -= getResources().getInteger(R.integer.config_dragViewOffsetY);
 
-        // Set its old pos (in the new parent's coordinates); the CellLayout will
-        // animate it from this position during the next layout pass
+        // Set its old pos (in the new parent's coordinates); it will be animated
+        // in animateViewIntoPosition after the next layout pass
         lp.oldX = viewX - (parent.getLeft() - mScrollX);
         lp.oldY = viewY - (parent.getTop() - mScrollY);
     }
 
+    public void animateViewIntoPosition(final View view) {
+        final CellLayout parent = (CellLayout) view.getParent();
+        final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
+
+        mDropView = view;
+
+        // Convert the animation params to be relative to the Workspace, not the CellLayout
+        final int fromX = lp.oldX + parent.getLeft();
+        final int fromY = lp.oldY + parent.getTop();
+
+        final int dx = lp.x - lp.oldX;
+        final int dy = lp.y - lp.oldY;
+
+        // Calculate the duration of the animation based on the object's distance
+        final float dist = (float) Math.sqrt(dx*dx + dy*dy);
+        final Resources res = getResources();
+        final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
+        final int duration = (int) (res.getInteger(R.integer.config_dropAnimMaxDuration)
+                * mQuintEaseOutInterpolator.getInterpolation(dist / maxDist));
+
+        // Lazy initialize the animation
+        if (mDropAnim == null) {
+            mDropAnim = new ValueAnimator();
+            mDropAnim.setInterpolator(mQuintEaseOutInterpolator);
+
+            // Make the view invisible during the animation; we'll render it manually.
+            mDropAnim.addListener(new AnimatorListenerAdapter() {
+                public void onAnimationStart(Animator animation) {
+                    mDropView.setVisibility(View.INVISIBLE);
+                }
+
+                public void onAnimationEnd(Animator animation) {
+                    if (mDropView != null) {
+                        mDropView.setVisibility(View.VISIBLE);
+                        mDropView = null;
+                    }
+                }
+            });
+        } else {
+            mDropAnim.end(); // Make sure it's not already running
+        }
+        mDropAnim.setDuration(duration);
+        mDropAnim.setFloatValues(0.0f, 1.0f);
+        mDropAnim.removeAllUpdateListeners();
+        mDropAnim.addUpdateListener(new AnimatorUpdateListener() {
+            public void onAnimationUpdate(ValueAnimator animation) {
+                final float percent = (Float) animation.getAnimatedValue();
+                // Invalidate the old position
+                invalidate(mDropViewPos[0], mDropViewPos[1],
+                        mDropViewPos[0] + view.getWidth(), mDropViewPos[1] + view.getHeight());
+
+                mDropViewPos[0] = fromX + (int) (percent * dx + 0.5f);
+                mDropViewPos[1] = fromY + (int) (percent * dy + 0.5f);
+                invalidate(mDropViewPos[0], mDropViewPos[1],
+                        mDropViewPos[0] + view.getWidth(), mDropViewPos[1] + view.getHeight());
+            }
+        });
+        mDropAnim.start();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean acceptDrop(DragSource source, int x, int y,
+            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
+
+        // If it's an external drop (e.g. from All Apps), check if it should be accepted
+        if (source != this) {
+            // Don't accept the drop if we're not over a screen at time of drop
+            if (mDragTargetLayout == null) {
+                return false;
+            }
+
+            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;
+
+            // Don't accept the drop if there's no room for the item
+            if (!mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
+                mLauncher.showOutOfSpaceMessage();
+                return false;
+            }
+        }
+        return true;
+    }
+
     public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
 
         int originX = x - xOffset;
         int originY = y - yOffset;
 
-        if (mDragTargetLayout == null) {
-            // Cancel the drag if we're not over a screen at time of drop
-            if (mDragInfo != null) {
-                // Set its position so the parent can animate it back
-                final View parent = getChildAt(mDragInfo.screen);
-                setPositionForDropAnimation(dragView, originX, originY, parent, mDragInfo.cell);
-            }
-            return;
-        }
-
         if (mIsSmall || mIsInUnshrinkAnimation) {
             // get originX and originY in the local coordinate system of the screen
             mTempOriginXY[0] = originX;
@@ -1134,37 +1232,41 @@
         if (source != this) {
             onDropExternal(originX, originY, dragInfo, mDragTargetLayout);
         } else if (mDragInfo != null) {
-            // Move internally
             final View cell = mDragInfo.cell;
-            mTargetCell = findNearestVacantArea(originX, originY,
-                    mDragInfo.spanX, mDragInfo.spanY, cell, mDragTargetLayout,
-                    mTargetCell);
+            if (mDragTargetLayout != null) {
+                // Move internally
+                mTargetCell = findNearestVacantArea(originX, originY,
+                        mDragInfo.spanX, mDragInfo.spanY, cell, mDragTargetLayout,
+                        mTargetCell);
 
-            int screen = indexOfChild(mDragTargetLayout);
-            if (screen != mDragInfo.screen) {
-                final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
-                originalCellLayout.removeView(cell);
-                addInScreen(cell, screen, mTargetCell[0], mTargetCell[1],
-                        mDragInfo.spanX, mDragInfo.spanY);
+                int screen = indexOfChild(mDragTargetLayout);
+                if (screen != mDragInfo.screen) {
+                    final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
+                    originalCellLayout.removeView(cell);
+                    addInScreen(cell, screen, mTargetCell[0], mTargetCell[1],
+                            mDragInfo.spanX, mDragInfo.spanY);
+                }
+
+                // update the item's position after drop
+                final ItemInfo info = (ItemInfo) cell.getTag();
+                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+                mDragTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
+                lp.cellX = mTargetCell[0];
+                lp.cellY = mTargetCell[1];
+                cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen,
+                        mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
+
+                LauncherModel.moveItemInDatabase(mLauncher, info,
+                        LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
+                        lp.cellX, lp.cellY);
             }
-            mDragTargetLayout.onDropChild(cell);
 
-            // update the item's position after drop
-            final ItemInfo info = (ItemInfo) cell.getTag();
-            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
-            mDragTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
-            lp.cellX = mTargetCell[0];
-            lp.cellY = mTargetCell[1];
-            cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen,
-                    mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
-
-            LauncherModel.moveItemInDatabase(mLauncher, info,
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
-                    lp.cellX, lp.cellY);
+            final CellLayout parent = (CellLayout) cell.getParent();
 
             // Prepare it to be animated into its new position
             // This must be called after the view has been re-parented
-            setPositionForDropAnimation(dragView, originX, originY, mDragTargetLayout, cell);
+            setPositionForDropAnimation(dragView, originX, originY, parent, cell);
+            parent.onDropChild(cell);
         }
     }
 
@@ -1670,30 +1772,6 @@
     }
 
     /**
-     * {@inheritDoc}
-     */
-    public boolean acceptDrop(DragSource source, int x, int y,
-            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
-        if (mDragTargetLayout == null) {
-            // cancel the drag if we're not over a screen at time of drop
-            return false;
-        }
-
-        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;
-
-        if (mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
-            return true;
-        } else {
-            mLauncher.showOutOfSpaceMessage();
-            return false;
-        }
-    }
-
-    /**
      * Calculate the nearest cell where the given object would be dropped.
      */
     private int[] findNearestVacantArea(int pixelX, int pixelY,
@@ -1733,7 +1811,7 @@
                 // final Object tag = mDragInfo.cell.getTag();
             }
         } else if (mDragInfo != null) {
-            ((CellLayout) getChildAt(mDragInfo.screen)).onDropAborted(mDragInfo.cell);
+            ((CellLayout) getChildAt(mDragInfo.screen)).onDropChild(mDragInfo.cell);
         }
 
         mDragOutline = null;