Merge "When the locale changes, flush the all apps list."
diff --git a/src/com/android/launcher2/AllAppsTabbed.java b/src/com/android/launcher2/AllAppsTabbed.java
index eaeb80f..e0ff1a8 100644
--- a/src/com/android/launcher2/AllAppsTabbed.java
+++ b/src/com/android/launcher2/AllAppsTabbed.java
@@ -19,7 +19,6 @@
 import com.android.launcher.R;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -81,9 +80,9 @@
                 final float alpha = mAllApps.getAlpha();
                 ValueAnimator alphaAnim = ObjectAnimator.ofFloat(mAllApps, "alpha", alpha, 0.0f).
                         setDuration(duration);
-                alphaAnim.addListener(new AnimatorListenerAdapter() {
+                alphaAnim.addListener(new LauncherAnimatorListenerAdapter() {
                     @Override
-                    public void onAnimationEnd(Animator animation) {
+                    public void onAnimationEndOrCancel(Animator animation) {
                         String tag = getCurrentTabTag();
                         if (tag == TAG_ALL) {
                             mAllApps.setAppFilter(AllAppsPagedView.ALL_APPS_FLAG);
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index b7491ba..66d5cb5 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.Arrays;
+import com.android.launcher.R;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -26,7 +26,6 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.WallpaperManager;
-import android.content.ClipDescription;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -42,7 +41,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.ContextMenu;
-import android.view.DragEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
@@ -51,7 +49,7 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.LayoutAnimationController;
 
-import com.android.launcher.R;
+import java.util.Arrays;
 
 public class CellLayout extends ViewGroup implements Dimmable {
     static final String TAG = "CellLayout";
@@ -125,7 +123,6 @@
 
     private boolean mDragging = false;
 
-    private ValueAnimator mDropAnim;
     private TimeInterpolator mEaseOutInterpolator;
 
     public CellLayout(Context context) {
@@ -246,6 +243,7 @@
             // The animation holds a reference to the drag outline bitmap as long is it's
             // running. This way the bitmap can be GCed when the animations are complete.
             anim.getAnimator().addListener(new AnimatorListenerAdapter() {
+                @Override
                 public void onAnimationEnd(Animator animation) {
                     if ((Float) ((ValueAnimator) animation).getAnimatedValue() == 0f) {
                         anim.setTag(null);
@@ -255,9 +253,6 @@
             mDragOutlineAnims[i] = anim;
         }
 
-        mDropAnim = ValueAnimator.ofFloat(1.0f, 0.0f);
-        mDropAnim.setInterpolator(mEaseOutInterpolator);
-
         mBackgroundRect = new Rect();
         mHoverRect = new Rect();
         setHoverScale(1.0f);
@@ -309,11 +304,13 @@
             AnimatorSet bouncer = new AnimatorSet();
             bouncer.play(scaleUp).before(scaleDown);
             bouncer.play(scaleUp).with(alphaFadeOut);
-            bouncer.addListener(new AnimatorListenerAdapter() {
+            bouncer.addListener(new LauncherAnimatorListenerAdapter() {
+                @Override
                 public void onAnimationStart(Animator animation) {
                     setHover(true);
                 }
-                public void onAnimationEnd(Animator animation) {
+                @Override
+                public void onAnimationEndOrCancel(Animator animation) {
                     setHover(false);
                     setHoverScale(1.0f);
                     setHoverAlpha(1.0f);
@@ -776,37 +773,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 +796,7 @@
                             cellXY[0] + childLeft + lp.width / 2,
                             cellXY[1] + childTop + lp.height / 2, 0, null);
 
-                    animateChildIntoPosition(child);
+                    ((Workspace) mParent).animateViewIntoPosition(child);
                 }
             }
         }
@@ -1259,15 +1225,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/InterruptibleInOutAnimator.java b/src/com/android/launcher2/InterruptibleInOutAnimator.java
index 5ebe605..570b9e7 100644
--- a/src/com/android/launcher2/InterruptibleInOutAnimator.java
+++ b/src/com/android/launcher2/InterruptibleInOutAnimator.java
@@ -52,8 +52,9 @@
         mOriginalFromValue = fromValue;
         mOriginalToValue = toValue;
 
-        mAnimator.addListener(new AnimatorListenerAdapter() {
-            public void onAnimationEnd(Animator animation) {
+        mAnimator.addListener(new LauncherAnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEndOrCancel(Animator animation) {
                 mDirection = STOPPED;
             }
         });
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 71978fa..c14c22c 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -21,7 +21,6 @@
 import com.android.launcher.R;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
@@ -41,12 +40,12 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.Intent.ShortcutIconResource;
 import android.content.IntentFilter;
+import android.content.Intent.ShortcutIconResource;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -79,9 +78,9 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.View.OnLongClickListener;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.inputmethod.InputMethodManager;
@@ -90,10 +89,10 @@
 import android.widget.LinearLayout;
 import android.widget.PopupWindow;
 import android.widget.TabHost;
-import android.widget.TabHost.OnTabChangeListener;
-import android.widget.TabHost.TabContentFactory;
 import android.widget.TextView;
 import android.widget.Toast;
+import android.widget.TabHost.OnTabChangeListener;
+import android.widget.TabHost.TabContentFactory;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -303,9 +302,9 @@
                     ValueAnimator alphaAnim = ObjectAnimator.ofFloat(mCustomizePagedView,
                             "alpha", alpha, 0.0f);
                     alphaAnim.setDuration(duration);
-                    alphaAnim.addListener(new AnimatorListenerAdapter() {
+                    alphaAnim.addListener(new LauncherAnimatorListenerAdapter() {
                         @Override
-                        public void onAnimationEnd(Animator animation) {
+                        public void onAnimationEndOrCancel(Animator animation) {
                             String tag = mHomeCustomizationDrawer.getCurrentTabTag();
                             if (tag == WIDGETS_TAG) {
                                 mCustomizePagedView.setCustomizationFilter(
@@ -2295,13 +2294,13 @@
         if (seq != null) {
             Animator anim = ObjectAnimator.ofFloat(view, "alpha", show ? 1.0f : 0.0f);
             anim.setDuration(duration);
-            anim.addListener(new AnimatorListenerAdapter() {
+            anim.addListener(new LauncherAnimatorListenerAdapter() {
                 @Override
                 public void onAnimationStart(Animator animation) {
                     if (showing) showToolbarButton(view);
                 }
                 @Override
-                public void onAnimationEnd(Animator animation) {
+                public void onAnimationEndOrCancel(Animator animation) {
                     if (hiding) hideToolbarButton(view);
                 }
             });
@@ -2388,7 +2387,7 @@
 
         setPivotsForZoom(toView, toState, scale);
 
-        if (toState == State.ALL_APPS) {
+        if (toAllApps) {
             mWorkspace.shrinkToBottom(animated);
         } else {
             mWorkspace.shrinkToTop(animated);
@@ -2400,7 +2399,7 @@
                     PropertyValuesHolder.ofFloat("scaleY", scale, 1.0f));
             scaleAnim.setDuration(duration);
             scaleAnim.setInterpolator(new DecelerateInterpolator());
-            scaleAnim.addListener(new AnimatorListenerAdapter() {
+            scaleAnim.addListener(new LauncherAnimatorListenerAdapter() {
                 @Override
                 public void onAnimationStart(Animator animation) {
                     // Prepare the position
@@ -2408,6 +2407,14 @@
                     toView.setTranslationY(0.0f);
                     toView.setVisibility(View.VISIBLE);
                 }
+                @Override
+                public void onAnimationEndOrCancel(Animator animation) {
+                    // If we don't set the final scale values here, if this animation is cancelled
+                    // it will have the wrong scale value and subsequent cameraPan animations will
+                    // not fix that
+                    toView.setScaleX(1.0f);
+                    toView.setScaleY(1.0f);
+                }
             });
 
             AnimatorSet toolbarHideAnim = new AnimatorSet();
@@ -2464,12 +2471,10 @@
                     PropertyValuesHolder.ofFloat("scaleY", scaleFactor));
             scaleAnim.setDuration(duration);
             scaleAnim.setInterpolator(new AccelerateInterpolator());
-            mStateAnimation.addListener(new AnimatorListenerAdapter() {
+            mStateAnimation.addListener(new LauncherAnimatorListenerAdapter() {
                 @Override
-                public void onAnimationEnd(Animator animation) {
+                public void onAnimationEndOrCancel(Animator animation) {
                     fromView.setVisibility(View.GONE);
-                    fromView.setScaleX(1.0f);
-                    fromView.setScaleY(1.0f);
                 }
             });
 
@@ -2524,14 +2529,14 @@
         if (animated) {
             if (mStateAnimation != null) mStateAnimation.cancel();
             mStateAnimation = new AnimatorSet();
-            mStateAnimation.addListener(new AnimatorListenerAdapter() {
+            mStateAnimation.addListener(new LauncherAnimatorListenerAdapter() {
                 @Override
                 public void onAnimationStart(Animator animation) {
                     toView.setVisibility(View.VISIBLE);
                     toView.setY(toViewStartY);
                 }
                 @Override
-                public void onAnimationEnd(Animator animation) {
+                public void onAnimationEndOrCancel(Animator animation) {
                     fromView.setVisibility(View.GONE);
                 }
             });
@@ -2543,8 +2548,11 @@
             ObjectAnimator fromAnim = ObjectAnimator.ofFloat(fromView, "y",
                     fromViewStartY, fromViewEndY);
             fromAnim.setDuration(duration);
-            ObjectAnimator toAnim = ObjectAnimator.ofFloat(toView, "y",
-                    toViewStartY, toViewEndY);
+            ObjectAnimator toAnim = ObjectAnimator.ofPropertyValuesHolder(toView,
+                    PropertyValuesHolder.ofFloat("y", toViewStartY, toViewEndY),
+                    PropertyValuesHolder.ofFloat("scaleX", toView.getScaleX(), 1.0f),
+                    PropertyValuesHolder.ofFloat("scaleY", toView.getScaleY(), 1.0f)
+                    );
             fromAnim.setDuration(duration);
             mStateAnimation.playTogether(toolbarHideAnim, fromAnim, toAnim);
 
@@ -2556,6 +2564,8 @@
             fromView.setY(fromViewEndY);
             fromView.setVisibility(View.GONE);
             toView.setY(toViewEndY);
+            toView.setScaleX(1.0f);
+            toView.setScaleY(1.0f);
             toView.setVisibility(View.VISIBLE);
             hideAndShowToolbarButtons(toState, null, null);
         }
diff --git a/src/com/android/launcher2/LauncherAnimatorListenerAdapter.java b/src/com/android/launcher2/LauncherAnimatorListenerAdapter.java
new file mode 100644
index 0000000..3ab4868
--- /dev/null
+++ b/src/com/android/launcher2/LauncherAnimatorListenerAdapter.java
@@ -0,0 +1,70 @@
+/*
+ * 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.animation.Animator;
+
+import java.util.HashSet;
+
+/**
+ * This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}.
+ * Any custom listener that cares only about a subset of the methods of this listener can
+ * simply subclass this adapter class instead of implementing the interface directly.
+ */
+public abstract class LauncherAnimatorListenerAdapter implements Animator.AnimatorListener {
+    HashSet<Animator> cancelled = new HashSet<Animator>();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final void onAnimationCancel(Animator animation) {
+        onAnimationEndOrCancel(animation);
+        cancelled.add(animation);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final void onAnimationEnd(Animator animation) {
+        if (!cancelled.contains(animation)) onAnimationEndOrCancel(animation);
+        cancelled.remove(animation);
+    }
+
+    /**
+     * Like onAnimationEnd, except it's called immediately in the case on onAnimationCancel, and
+     * it's only called once in that case
+     */
+    public void onAnimationEndOrCancel(Animator animation) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAnimationRepeat(Animator animation) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAnimationStart(Animator animation) {
+    }
+
+}
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 5a31e9b..2ddd044 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -16,15 +16,17 @@
 
 package com.android.launcher2;
 
-import java.util.ArrayList;
-import java.util.HashSet;
+import com.android.launcher.R;
 
 import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.WallpaperManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -53,10 +55,12 @@
 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;
 
-import com.android.launcher.R;
+import java.util.ArrayList;
+import java.util.HashSet;
 
 /**
  * The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -142,6 +146,7 @@
     private ShrinkPosition mShrunkenState;
     private boolean mWaitingToShrink = false;
     private ShrinkPosition mWaitingToShrinkPosition;
+    private AnimatorSet mAnimator;
 
     private boolean mInScrollArea = false;
     private boolean mInDragMode = false;
@@ -151,6 +156,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,12 +214,15 @@
         LauncherApplication app = (LauncherApplication)context.getApplicationContext();
         mIconCache = app.getIconCache();
         mExternalDragOutlinePaint.setAntiAlias(true);
+        setWillNotDraw(false);
 
-        mUnshrinkAnimationListener = new AnimatorListenerAdapter() {
+        mUnshrinkAnimationListener = new LauncherAnimatorListenerAdapter() {
+            @Override
             public void onAnimationStart(Animator animation) {
                 mIsInUnshrinkAnimation = true;
             }
-            public void onAnimationEnd(Animator animation) {
+            @Override
+            public void onAnimationEndOrCancel(Animator animation) {
                 mIsInUnshrinkAnimation = false;
             }
         };
@@ -579,6 +592,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();
+            }
         }
     }
 
@@ -766,6 +788,11 @@
         // of the views accordingly
         newX -= (pageWidth - scaledPageWidth) / 2.0f;
         newY -= (pageHeight - scaledPageHeight) / 2.0f;
+
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
+        mAnimator = new AnimatorSet();
         for (int i = 0; i < screenCount; i++) {
             CellLayout cl = (CellLayout) getChildAt(i);
 
@@ -786,7 +813,7 @@
                         PropertyValuesHolder.ofFloat("alpha", finalAlpha),
                         PropertyValuesHolder.ofFloat("rotationY", rotation));
                 anim.setDuration(duration);
-                anim.start();
+                mAnimator.playTogether(anim);
             } else {
                 cl.setX((int)newX);
                 cl.setY((int)newY);
@@ -799,6 +826,9 @@
             // increment newX for the next screen
             newX += scaledPageWidth + extraScaledSpacing;
         }
+        if (animated) {
+            mAnimator.start();
+        }
         setChildrenDrawnWithCacheEnabled(true);
     }
 
@@ -910,7 +940,10 @@
     void unshrink(boolean animated) {
         if (mIsSmall) {
             mIsSmall = false;
-            AnimatorSet s = new AnimatorSet();
+            if (mAnimator != null) {
+                mAnimator.cancel();
+            }
+            mAnimator = new AnimatorSet();
             final int screenCount = getChildCount();
 
             final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
@@ -926,8 +959,7 @@
                 }
 
                 if (animated) {
-
-                    s.playTogether(
+                    mAnimator.playTogether(
                             ObjectAnimator.ofFloat(cl, "translationX", 0.0f).setDuration(duration),
                             ObjectAnimator.ofFloat(cl, "translationY", 0.0f).setDuration(duration),
                             ObjectAnimator.ofFloat(cl, "scaleX", 1.0f).setDuration(duration),
@@ -948,8 +980,8 @@
             if (animated) {
                 // If we call this when we're not animated, onAnimationEnd is never called on
                 // the listener; make sure we only use the listener when we're actually animating
-                s.addListener(mUnshrinkAnimationListener);
-                s.start();
+                mAnimator.addListener(mUnshrinkAnimationListener);
+                mAnimator.start();
             }
         }
     }
@@ -1052,6 +1084,7 @@
         CellLayout current = getCurrentDropLayout();
 
         current.onDragChild(child);
+        child.setVisibility(View.GONE);
 
         child.clearFocus();
         child.setPressed(false);
@@ -1100,28 +1133,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 +1245,45 @@
         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);
+                if (mTargetCell == null) {
+                    mLauncher.showOutOfSpaceMessage();
+                } else {
+                    int screen = indexOfChild(mDragTargetLayout);
+                    if (screen != mDragInfo.screen) {
+                        // Reparent the view
+                        ((CellLayout) getChildAt(mDragInfo.screen)).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 +1789,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 +1828,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;