Merge "Fixing annoying bug where widget would drop to side page"
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index 7a07898..d97e3c2 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -11,11 +11,6 @@
     <!-- Whether or not to fade the side pages -->
     <bool name="config_workspaceFadeAdjacentScreens">true</bool>
 
-    <!-- When dragging items on the workspace, how much bigger (in pixels) the dragged view
-         should be, as compared to the original view. If 0, it will not be scaled at all.
-         Should be an even number, for pixel alignment. -->
-    <integer name="config_dragViewExtraPixels">0</integer>
-
     <!-- When shrinking the workspace, this is the percentage of its original size. -->
     <integer name="config_workspaceShrinkPercent">17</integer>
 
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index d265c36..2d72e10 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -72,6 +72,9 @@
      the drag view should be offset from the position of the original view. -->
     <dimen name="dragViewOffsetX">0dp</dimen>
     <dimen name="dragViewOffsetY">-12dp</dimen>
+    <!--  When dragging an item, how much bigger (fixed dps) the dragged view
+          should be. If 0, it will not be scaled at all. -->
+    <dimen name="dragViewScale">12dp</dimen>
 
 <!-- Folders -->
     <!-- The size of the image which sits behind the preview of the folder contents -->
diff --git a/res/values/colors.xml b/res/values/colors.xml
index ece0fd8..1efeb9e 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -23,9 +23,6 @@
     <color name="delete_target_hover_tint">#DAFF0000</color>
     <color name="info_target_hover_tint">#DA0099CC</color>
 
-    <!-- The alpha/color to apply to the drag image -->
-    <color name="drag_view_multiply_color">#CCFFFFFF</color>
-
     <color name="bubble_dark_background">#20000000</color>
 
     <color name="appwidget_error_color">#FCCC</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 7ab9870..70e65c9 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -54,11 +54,6 @@
 
     <integer name="config_crosshairsFadeInTime">600</integer>
 
-    <!--  When dragging an item on the workspace, how much bigger (in pixels) the dragged view
-          should be, as compared to the original view. If 0, it will not be scaled at all.
-          Should be an even number, for pixel alignment. -->
-    <integer name="config_dragViewExtraPixels">40</integer>
-
     <!-- The duration (in ms) of the fade animation on the object outlines, used when
          we are dragging objects around on the home screen. -->
     <integer name="config_dragOutlineFadeTime">900</integer>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index fe0cb00..c4a66ac 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -110,6 +110,9 @@
      the drag view should be offset from the position of the original view. -->
     <dimen name="dragViewOffsetX">0dp</dimen>
     <dimen name="dragViewOffsetY">-8dp</dimen>
+    <!--  When dragging an item, how much bigger (fixed dps) the dragged view
+          should be. If 0, it will not be scaled at all. -->
+    <dimen name="dragViewScale">12dp</dimen>
 
     <!-- Padding applied to AppWidgets -->
     <dimen name="app_widget_preview_padding_left">8dp</dimen>
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 91205d0..2f8d128 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -204,7 +204,6 @@
     private Canvas mCanvas;
     private Drawable mDefaultWidgetBackground;
     private IconCache mIconCache;
-    private int mDragViewMultiplyColor;
 
     // Dimens
     private int mContentWidth;
@@ -255,7 +254,6 @@
         Resources resources = context.getResources();
         mDefaultWidgetBackground = resources.getDrawable(R.drawable.default_widget_preview_holo);
         mAppIconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size);
-        mDragViewMultiplyColor = resources.getColor(R.color.drag_view_multiply_color);
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0);
         mMaxAppCellCountX = a.getInt(R.styleable.AppsCustomizePagedView_maxAppCellCountX, -1);
@@ -652,9 +650,6 @@
         // Save the preview for the outline generation, then dim the preview
         outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(),
                 false);
-        mCanvas.setBitmap(preview);
-        mCanvas.drawColor(mDragViewMultiplyColor, PorterDuff.Mode.MULTIPLY);
-        mCanvas.setBitmap(null);
 
         // Start the drag
         alphaClipPaint = null;
@@ -731,6 +726,7 @@
             if (showOutOfSpaceMessage) {
                 mLauncher.showOutOfSpaceMessage();
             }
+            d.deferDragViewCleanupPostAnimation = false;
         }
     }
 
@@ -992,9 +988,6 @@
             d.setBounds(x, y, x + w, y + h);
             d.draw(c);
             d.setBounds(oldBounds); // Restore the bounds
-            if (multiplyColor != 0xFFFFFFFF) {
-                c.drawColor(mDragViewMultiplyColor, PorterDuff.Mode.MULTIPLY);
-            }
             c.setBitmap(null);
         }
     }
diff --git a/src/com/android/launcher2/AppsCustomizeTabHost.java b/src/com/android/launcher2/AppsCustomizeTabHost.java
index caababa..023946b 100644
--- a/src/com/android/launcher2/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher2/AppsCustomizeTabHost.java
@@ -347,8 +347,9 @@
 
     /* LauncherTransitionable overrides */
     @Override
-    public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
+    public void onLauncherTransitionStart(Launcher l, Animator animation, boolean toWorkspace) {
         mInTransition = true;
+        boolean animated = (animation != null);
 
         mContent.setVisibility(VISIBLE);
 
@@ -371,9 +372,9 @@
     }
 
     @Override
-    public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
+    public void onLauncherTransitionEnd(Launcher l, Animator animation, boolean toWorkspace) {
         mInTransition = false;
-        if (animated) {
+        if (animation != null) {
             setLayerType(LAYER_TYPE_NONE, null);
         }
 
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 8658eeb..425f301 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -394,7 +394,9 @@
                 listener.onDragEnd();
             }
             if (mDragObject.dragView != null) {
-                mDragObject.dragView.remove();
+                if (!mDragObject.deferDragViewCleanupPostAnimation) {
+                    mDragObject.dragView.remove();
+                }
                 mDragObject.dragView = null;
             }
         }
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index 6f3bcd1..5e5740e 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -62,14 +62,10 @@
     private ValueAnimator mDropAnim = null;
     private ValueAnimator mFadeOutAnim = null;
     private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
-    private View mDropView = null;
+    private DragView mDropView = null;
     private int mAnchorViewInitialScrollX = 0;
     private View mAnchorView = null;
 
-    private int[] mDropViewPos = new int[2];
-    private float mDropViewScaleX;
-    private float mDropViewScaleY;
-    private float mDropViewAlpha;
     private boolean mHoverPointClosesFolder = false;
     private Rect mHitRect = new Rect();
     private int mWorkspaceIndex = -1;
@@ -418,25 +414,18 @@
         animateViewIntoPosition(dragView, child, null);
     }
 
-    public void animateViewIntoPosition(DragView dragView, final int[] pos, float scaleX, float
-            scaleY, int animationEndStyle, Runnable onFinishRunnable, int duration) {
+    public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
+            float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
+            int duration) {
         Rect r = new Rect();
         getViewRectRelativeToSelf(dragView, r);
         final int fromX = r.left;
         final int fromY = r.top;
 
-        animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], 1, 1, 1, scaleX, scaleY,
+        animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY,
                 onFinishRunnable, animationEndStyle, duration, null);
     }
 
-    public void scaleViewIntoPosition(DragView dragView, final int[] pos, float finalAlpha,
-            float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
-            int duration) {
-        animateViewIntoPosition(dragView, pos[0], pos[1], pos[0], pos[1], finalAlpha,
-                mDropViewScaleX, mDropViewScaleY, scaleX, scaleY, onFinishRunnable,
-                animationEndStyle, duration, null);
-    }
-
     public void animateViewIntoPosition(DragView dragView, final View child,
             final Runnable onFinishAnimationRunnable) {
         animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, null);
@@ -502,7 +491,7 @@
                 onCompleteRunnable, ANIMATION_END_FADE_OUT, duration, anchorView);
     }
 
-    private void animateViewIntoPosition(final View view, final int fromX, final int fromY,
+    private void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
             final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY,
             float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
             int animationEndStyle, int duration, View anchorView) {
@@ -534,11 +523,12 @@
      *        anchored to in case scrolling is currently taking place. Note: currently this is
      *        only used for the X dimension for the case of the workspace.
      */
-    public void animateView(final View view, final Rect from, final Rect to, final float finalAlpha,
-            final float initScaleX, final float initScaleY, final float finalScaleX,
-            final float finalScaleY, int duration, final Interpolator motionInterpolator,
-            final Interpolator alphaInterpolator, final Runnable onCompleteRunnable,
-            final int animationEndStyle, View anchorView) {
+    public void animateView(final DragView view, final Rect from, final Rect to,
+            final float finalAlpha, final float initScaleX, final float initScaleY,
+            final float finalScaleX, final float finalScaleY, int duration,
+            final Interpolator motionInterpolator, final Interpolator alphaInterpolator,
+            final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) {
+
         // Calculate the duration of the animation based on the object's distance
         final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) +
                 Math.pow(to.top - from.top, 2));
@@ -561,8 +551,10 @@
             mFadeOutAnim.cancel();
         }
 
+        // Show the drop view if it was previously hidden
         mDropView = view;
-        final float initialAlpha = view.getAlpha();
+        mDropView.cancelAnimation();
+        mDropView.resetLayoutParams();
         mDropAnim = new ValueAnimator();
         if (alphaInterpolator == null || motionInterpolator == null) {
             mDropAnim.setInterpolator(mCubicEaseOutInterpolator);
@@ -573,29 +565,44 @@
         }
         mAnchorView = anchorView;
 
+        final float initAlpha = view.getAlpha();
+        final float dropViewScale = mDropView.getScaleX();
+
         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
-                int width = view.getMeasuredWidth();
-                int height = view.getMeasuredHeight();
-                invalidate(mDropViewPos[0], mDropViewPos[1],
-                        mDropViewPos[0] + width, mDropViewPos[1] + height);
+                final int width = view.getMeasuredWidth();
+                final int height = view.getMeasuredHeight();
 
                 float alphaPercent = alphaInterpolator == null ? percent :
                         alphaInterpolator.getInterpolation(percent);
                 float motionPercent = motionInterpolator == null ? percent :
                         motionInterpolator.getInterpolation(percent);
 
-                mDropViewPos[0] = from.left + (int) Math.round(((to.left - from.left) * motionPercent));
-                mDropViewPos[1] = from.top + (int) Math.round(((to.top - from.top) * motionPercent));
-                mDropViewScaleX = percent * finalScaleX + (1 - percent) * initScaleX;
-                mDropViewScaleY = percent * finalScaleY + (1 - percent) * initScaleY;
-                mDropViewAlpha = alphaPercent * finalAlpha + (1 - alphaPercent) * initialAlpha;
-                invalidate();
+                float initialScaleX = initScaleX * dropViewScale;
+                float initialScaleY = initScaleY * dropViewScale;
+                float scaleX = finalScaleX * percent + initialScaleX * (1 - percent);
+                float scaleY = finalScaleY * percent + initialScaleY * (1 - percent);
+                float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent);
+
+                float fromLeft = from.left + (initialScaleX - 1f) * width / 2;
+                float fromTop = from.top + (initialScaleY - 1f) * height / 2;
+
+                int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent)));
+                int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent)));
+
+                int xPos = x - mDropView.getScrollX() + (mAnchorView != null
+                        ? (mAnchorViewInitialScrollX - mAnchorView.getScrollX()) : 0);
+                int yPos = y - mDropView.getScrollY();
+
+                mDropView.setTranslationX(xPos);
+                mDropView.setTranslationY(yPos);
+                mDropView.setScaleX(scaleX);
+                mDropView.setScaleY(scaleY);
+                mDropView.setAlpha(alpha);
             }
         });
         mDropAnim.addListener(new AnimatorListenerAdapter() {
@@ -619,9 +626,13 @@
     }
 
     public void clearAnimatedView() {
+        if (mDropAnim != null) {
+            mDropAnim.cancel();
+        }
+        if (mDropView != null) {
+            mDropView.remove();
+        }
         mDropView = null;
-        mDropViewScaleX = 1;
-        mDropViewScaleY = 1;
         invalidate();
     }
 
@@ -637,15 +648,16 @@
         mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
                 final float percent = (Float) animation.getAnimatedValue();
-                mDropViewAlpha = 1 - percent;
-                int width = mDropView.getMeasuredWidth();
-                int height = mDropView.getMeasuredHeight();
-                invalidate(mDropViewPos[0], mDropViewPos[1],
-                        mDropViewPos[0] + width, mDropViewPos[1] + height);
+
+                float alpha = 1 - percent;
+                mDropView.setAlpha(alpha);
             }
         });
         mFadeOutAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
+                if (mDropView != null) {
+                    mDropView.remove();
+                }
                 mDropView = null;
                 invalidate();
             }
@@ -696,25 +708,4 @@
             return i;
         }
     }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        super.dispatchDraw(canvas);
-        if (mDropView != null) {
-            // We are animating an item that was just dropped on the home screen.
-            // Render its View in the current animation position.
-            canvas.save(Canvas.MATRIX_SAVE_FLAG);
-            final int xPos = mDropViewPos[0] - mDropView.getScrollX() + (mAnchorView != null
-                    ? (mAnchorViewInitialScrollX - mAnchorView.getScrollX()) : 0);
-            final int yPos = mDropViewPos[1] - mDropView.getScrollY();
-            int width = mDropView.getMeasuredWidth();
-            int height = mDropView.getMeasuredHeight();
-            canvas.translate(xPos, yPos);
-            canvas.translate((1 - mDropViewScaleX) * width / 2, (1 - mDropViewScaleY) * height / 2);
-            canvas.scale(mDropViewScaleX, mDropViewScaleY);
-            mDropView.setAlpha(mDropViewAlpha);
-            mDropView.draw(canvas);
-            canvas.restore();
-        }
-    }
 }
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index 15d9c54..90b2083 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -32,6 +32,8 @@
 import com.android.launcher.R;
 
 public class DragView extends View {
+    private static float sDragAlpha = 0.8f;
+
     private Bitmap mBitmap;
     private Bitmap mCrossFadeBitmap;
     private Paint mPaint;
@@ -48,8 +50,6 @@
     private float mOffsetX = 0.0f;
     private float mOffsetY = 0.0f;
 
-    private DragLayer.LayoutParams mLayoutParams;
-
     /**
      * Construct the drag view.
      * <p>
@@ -67,20 +67,14 @@
         mDragLayer = launcher.getDragLayer();
 
         final Resources res = getResources();
-        final int dragScale = res.getInteger(R.integer.config_dragViewExtraPixels);
-
-        Matrix scale = new Matrix();
-        final float scaleFactor = (width + dragScale) / width;
-        if (scaleFactor != 1.0f) {
-            scale.setScale(scaleFactor, scaleFactor);
-        }
-
-        final int offsetX = res.getDimensionPixelSize(R.dimen.dragViewOffsetX);
-        final int offsetY = res.getDimensionPixelSize(R.dimen.dragViewOffsetY);
+        final float offsetX = res.getDimensionPixelSize(R.dimen.dragViewOffsetX);
+        final float offsetY = res.getDimensionPixelSize(R.dimen.dragViewOffsetY);
+        final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
+        final float scale = (width + scaleDps) / width;
 
         // Animate the view into the correct position
         mAnim = ValueAnimator.ofFloat(0.0f, 1.0f);
-        mAnim.setDuration(110);
+        mAnim.setDuration(150);
         mAnim.setInterpolator(new DecelerateInterpolator(2.5f));
         mAnim.addUpdateListener(new AnimatorUpdateListener() {
             @Override
@@ -92,19 +86,20 @@
 
                 mOffsetX += deltaX;
                 mOffsetY += deltaY;
+                setScaleX(1f + (value * (scale - 1f)));
+                setScaleY(1f + (value * (scale - 1f)));
+                setAlpha(sDragAlpha * value + (1f - value));
 
                 if (getParent() == null) {
                     animation.cancel();
                 } else {
-                    DragLayer.LayoutParams lp = mLayoutParams;
-                    lp.x += deltaX;
-                    lp.y += deltaY;
-                    mDragLayer.requestLayout();
+                    setTranslationX(getTranslationX() + deltaX);
+                    setTranslationY(getTranslationY() + deltaY);
                 }
             }
         });
 
-        mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
+        mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height);
         setDragRegion(new Rect(0, 0, width, height));
 
         // The point in our scaled bitmap that the touch events are located
@@ -236,14 +231,24 @@
         DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0);
         lp.width = mBitmap.getWidth();
         lp.height = mBitmap.getHeight();
-        lp.x = touchX - mRegistrationX;
-        lp.y = touchY - mRegistrationY;
         lp.customPosition = true;
         setLayoutParams(lp);
-        mLayoutParams = lp;
+        setTranslationX(touchX - mRegistrationX);
+        setTranslationY(touchY - mRegistrationY);
         mAnim.start();
     }
 
+    public void cancelAnimation() {
+        if (mAnim != null && mAnim.isRunning()) {
+            mAnim.cancel();
+        }
+    }
+
+    public void resetLayoutParams() {
+        mOffsetX = mOffsetY = 0;
+        requestLayout();
+    }
+
     /**
      * Move the window containing this view.
      *
@@ -251,22 +256,14 @@
      * @param touchY the y coordinate the user touched in DragLayer coordinates
      */
     void move(int touchX, int touchY) {
-        DragLayer.LayoutParams lp = mLayoutParams;
-        lp.x = touchX - mRegistrationX + (int) mOffsetX;
-        lp.y = touchY - mRegistrationY + (int) mOffsetY;
-        mDragLayer.requestLayout();
+        setTranslationX(touchX - mRegistrationX + (int) mOffsetX);
+        setTranslationY(touchY - mRegistrationY + (int) mOffsetY);
     }
 
     void remove() {
-        mDragLayer.removeView(DragView.this);
-    }
-
-    int[] getPosition(int[] result) {
-        DragLayer.LayoutParams lp = mLayoutParams;
-        if (result == null) result = new int[2];
-        result[0] = lp.x;
-        result[1] = lp.y;
-        return result;
+        if (getParent() != null) {
+            mDragLayer.removeView(DragView.this);
+        }
     }
 }
 
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index 4172da2..e49f782 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -55,6 +55,9 @@
         /** Indicates that the drag operation was cancelled */
         public boolean cancelled = false;
 
+        /** Defers removing the DragView from the DragLayer until after the drop animation. */
+        public boolean deferDragViewCleanupPostAnimation = true;
+
         public DragObject() {
         }
     }
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index ca537d8..ff7e10f 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -295,14 +295,14 @@
     }
 
     public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
-            final ShortcutInfo srcInfo, final View srcView, Rect dstRect,
+            final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
             float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
 
         Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
         computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth());
 
         // This will animate the dragView (srcView) into the new folder
-        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
+        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null);
 
         // This will animate the first item from it's position as an icon into its
         // position as the first item in the preview
@@ -320,8 +320,9 @@
         mFolderRingAnimator.animateToNaturalState();
     }
 
-    private void onDrop(final ShortcutInfo item, View animateView, Rect finalRect,
-            float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
+    private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
+            float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable,
+            DragObject d) {
         item.cellX = -1;
         item.cellY = -1;
 
@@ -383,7 +384,7 @@
             item = (ShortcutInfo) d.dragInfo;
         }
         mFolder.notifyDrop();
-        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
+        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
     }
 
     public DropTarget getDropTargetDelegate(DragObject d) {
diff --git a/src/com/android/launcher2/InfoDropTarget.java b/src/com/android/launcher2/InfoDropTarget.java
index dba845b..21fe8ba 100644
--- a/src/com/android/launcher2/InfoDropTarget.java
+++ b/src/com/android/launcher2/InfoDropTarget.java
@@ -29,6 +29,7 @@
 import android.view.ViewGroup;
 
 import com.android.launcher.R;
+import com.android.launcher2.DropTarget.DragObject;
 
 public class InfoDropTarget extends ButtonDropTarget {
 
@@ -85,6 +86,9 @@
         if (componentName != null) {
             mLauncher.startApplicationDetailsActivity(componentName);
         }
+
+        // There is no post-drop animation, so clean up the DragView now
+        d.deferDragViewCleanupPostAnimation = false;
         return false;
     }
 
diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java
index 3cb37a7..9e2067d 100644
--- a/src/com/android/launcher2/InstallShortcutReceiver.java
+++ b/src/com/android/launcher2/InstallShortcutReceiver.java
@@ -40,12 +40,21 @@
             return;
         }
 
-        int screen = Launcher.getScreen();
-        String[] errorMsgs = {""};
-        if (!installShortcut(context, data, screen, errorMsgs)) {
+        final int screen = Launcher.getScreen();
+        final String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
+        final Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+
+        final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
+        final boolean shortcutExists = LauncherModel.shortcutExists(context, name, intent);
+
+        final String[] errorMsgs = {""};
+
+        if (!installShortcut(context, data, items, name, intent, screen, shortcutExists,
+                errorMsgs)) {
             // The target screen is full, let's try the other screens
             for (int i = 0; i < Launcher.SCREEN_COUNT; i++) {
-                if (i != screen && installShortcut(context, data, i, errorMsgs)) break;
+                if (i != screen && installShortcut(context, data, items, name, intent, i,
+                        shortcutExists, errorMsgs)) break;
             }
         }
 
@@ -55,11 +64,9 @@
         }
     }
 
-    private boolean installShortcut(Context context, Intent data, int screen, String[] errorMsgs) {
-        String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-
-        if (findEmptyCell(context, mCoordinates, screen)) {
-            Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+    private boolean installShortcut(Context context, Intent data, ArrayList<ItemInfo> items,
+            String name, Intent intent, int screen, boolean shortcutExists, String[] errorMsgs) {
+        if (findEmptyCell(context, items, mCoordinates, screen)) {
             if (intent != null) {
                 if (intent.getAction() == null) {
                     intent.setAction(Intent.ACTION_VIEW);
@@ -68,7 +75,7 @@
                 // By default, we allow for duplicate entries (located in
                 // different places)
                 boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true);
-                if (duplicate || !LauncherModel.shortcutExists(context, name, intent)) {
+                if (duplicate || !shortcutExists) {
                     LauncherApplication app = (LauncherApplication) context.getApplicationContext();
                     ShortcutInfo info = app.getModel().addShortcut(context, data,
                             LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, mCoordinates[0],
@@ -91,12 +98,12 @@
         return false;
     }
 
-    private static boolean findEmptyCell(Context context, int[] xy, int screen) {
+    private static boolean findEmptyCell(Context context, ArrayList<ItemInfo> items, int[] xy,
+            int screen) {
         final int xCount = LauncherModel.getCellCountX();
         final int yCount = LauncherModel.getCellCountY();
         boolean[][] occupied = new boolean[xCount][yCount];
 
-        ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
         ItemInfo item = null;
         int cellX, cellY, spanX, spanY;
         for (int i = 0; i < items.size(); ++i) {
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 4062145..8850cf8 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -2213,18 +2213,6 @@
         }
     }
 
-    private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
-        if (v instanceof LauncherTransitionable) {
-            ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace);
-        }
-    }
-
-    private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
-        if (v instanceof LauncherTransitionable) {
-            ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace);
-        }
-    }
-
     /**
      * Things to test when changing the following seven functions.
      *   - Home from workspace
@@ -2270,7 +2258,7 @@
      * Assumes that the view to show is anchored at either the very top or very bottom
      * of the screen.
      */
-    private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) {
+    private void showAppsCustomizeHelper(boolean animated, final boolean springLoaded) {
         if (mStateAnimation != null) {
             mStateAnimation.cancel();
             mStateAnimation = null;
@@ -2281,7 +2269,6 @@
         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
         final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
         final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
-        final View fromView = mWorkspace;
         final View toView = mAppsCustomizeTabHost;
         final int startDelay =
                 res.getInteger(R.integer.config_workspaceAppsCustomizeAnimationStagger);
@@ -2328,8 +2315,10 @@
                 }
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    dispatchOnLauncherTransitionEnd(fromView, animated, false);
-                    dispatchOnLauncherTransitionEnd(toView, animated, false);
+                    if (toView instanceof LauncherTransitionable) {
+                        ((LauncherTransitionable) toView).onLauncherTransitionEnd(instance,
+                                scaleAnim, false);
+                    }
 
                     if (!springLoaded && !LauncherApplication.isScreenLarge()) {
                         // Hide the workspace scrollbar
@@ -2352,14 +2341,14 @@
             }
 
             boolean delayAnim = false;
+            LauncherTransitionable lt = (LauncherTransitionable) toView;
             final ViewTreeObserver observer;
 
-            dispatchOnLauncherTransitionStart(fromView, animated, false);
-            dispatchOnLauncherTransitionStart(toView, animated, false);
+            lt.onLauncherTransitionStart(instance, mStateAnimation, false);
 
             // If any of the objects being animated haven't been measured/laid out
             // yet, delay the animation until we get a layout pass
-            if ((((LauncherTransitionable) toView).getContent().getMeasuredWidth() == 0) ||
+            if ((lt.getContent().getMeasuredWidth() == 0) ||
                     (mWorkspace.getMeasuredWidth() == 0) ||
                     (toView.getMeasuredWidth() == 0)) {
                 observer = mWorkspace.getViewTreeObserver();
@@ -2398,16 +2387,16 @@
             toView.setScaleY(1.0f);
             toView.setVisibility(View.VISIBLE);
             toView.bringToFront();
+            if (toView instanceof LauncherTransitionable) {
+                ((LauncherTransitionable) toView).onLauncherTransitionStart(instance, null, false);
+                ((LauncherTransitionable) toView).onLauncherTransitionEnd(instance, null, false);
 
-            if (!springLoaded && !LauncherApplication.isScreenLarge()) {
-                // Hide the workspace scrollbar
-                mWorkspace.hideScrollingIndicator(true);
-                hideDockDivider();
+                if (!springLoaded && !LauncherApplication.isScreenLarge()) {
+                    // Hide the workspace scrollbar
+                    mWorkspace.hideScrollingIndicator(true);
+                    hideDockDivider();
+                }
             }
-            dispatchOnLauncherTransitionStart(fromView, animated, false);
-            dispatchOnLauncherTransitionEnd(fromView, animated, false);
-            dispatchOnLauncherTransitionStart(toView, animated, false);
-            dispatchOnLauncherTransitionEnd(toView, animated, false);
             updateWallpaperVisibility(false);
         }
     }
@@ -2419,12 +2408,12 @@
      */
     private void hideAppsCustomizeHelper(State toState, final boolean animated,
             final boolean springLoaded, final Runnable onCompleteRunnable) {
-
         if (mStateAnimation != null) {
             mStateAnimation.cancel();
             mStateAnimation = null;
         }
         Resources res = getResources();
+        final Launcher instance = this;
 
         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
         final int fadeOutDuration =
@@ -2432,7 +2421,6 @@
         final float scaleFactor = (float)
                 res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
         final View fromView = mAppsCustomizeTabHost;
-        final View toView = mWorkspace;
         Animator workspaceAnim = null;
 
         if (toState == State.WORKSPACE) {
@@ -2463,18 +2451,19 @@
                 .setDuration(fadeOutDuration);
             alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
 
-            mStateAnimation = new AnimatorSet();
-
-            dispatchOnLauncherTransitionStart(fromView, animated, true);
-            dispatchOnLauncherTransitionStart(toView, animated, true);
-
-            mStateAnimation.addListener(new AnimatorListenerAdapter() {
+            if (fromView instanceof LauncherTransitionable) {
+                ((LauncherTransitionable) fromView).onLauncherTransitionStart(instance, alphaAnim,
+                        true);
+            }
+            alphaAnim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     updateWallpaperVisibility(true);
                     fromView.setVisibility(View.GONE);
-                    dispatchOnLauncherTransitionEnd(fromView, animated, true);
-                    dispatchOnLauncherTransitionEnd(toView, animated, true);
+                    if (fromView instanceof LauncherTransitionable) {
+                        ((LauncherTransitionable) fromView).onLauncherTransitionEnd(instance,
+                                alphaAnim, true);
+                    }
                     mWorkspace.hideScrollingIndicator(false);
                     if (onCompleteRunnable != null) {
                         onCompleteRunnable.run();
@@ -2482,6 +2471,7 @@
                 }
             });
 
+            mStateAnimation = new AnimatorSet();
             mStateAnimation.playTogether(scaleAnim, alphaAnim);
             if (workspaceAnim != null) {
                 mStateAnimation.play(workspaceAnim);
@@ -2489,10 +2479,10 @@
             mStateAnimation.start();
         } else {
             fromView.setVisibility(View.GONE);
-            dispatchOnLauncherTransitionStart(fromView, animated, false);
-            dispatchOnLauncherTransitionEnd(fromView, animated, false);
-            dispatchOnLauncherTransitionStart(toView, animated, false);
-            dispatchOnLauncherTransitionEnd(toView, animated, false);
+            if (fromView instanceof LauncherTransitionable) {
+                ((LauncherTransitionable) fromView).onLauncherTransitionStart(instance, null, true);
+                ((LauncherTransitionable) fromView).onLauncherTransitionEnd(instance, null, true);
+            }
             mWorkspace.hideScrollingIndicator(false);
         }
     }
@@ -3439,6 +3429,6 @@
 
 interface LauncherTransitionable {
     View getContent();
-    void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
-    void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
+    void onLauncherTransitionStart(Launcher l, Animator animation, boolean toWorkspace);
+    void onLauncherTransitionEnd(Launcher l, Animator animation, boolean toWorkspace);
 }
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 7abe6e1..9a140d2 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -1680,7 +1680,6 @@
         if (mHasScrollIndicator && mScrollIndicator == null) {
             ViewGroup parent = (ViewGroup) getParent();
             mScrollIndicator = (View) (parent.findViewById(R.id.paged_view_indicator));
-            mScrollIndicator.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             mHasScrollIndicator = mScrollIndicator != null;
             if (mHasScrollIndicator) {
                 mScrollIndicator.setVisibility(View.VISIBLE);
@@ -1806,6 +1805,7 @@
             indicatorPos += indicatorCenterOffset;
         }
         mScrollIndicator.setTranslationX(indicatorPos);
+        mScrollIndicator.invalidate();
     }
 
     public void showScrollIndicatorTrack() {
diff --git a/src/com/android/launcher2/SearchDropTargetBar.java b/src/com/android/launcher2/SearchDropTargetBar.java
index 03ca38f..3a7f24b 100644
--- a/src/com/android/launcher2/SearchDropTargetBar.java
+++ b/src/com/android/launcher2/SearchDropTargetBar.java
@@ -129,26 +129,14 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 mQSBSearchBar.setVisibility(View.VISIBLE);
-                mQSBSearchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mQSBSearchBar.setLayerType(View.LAYER_TYPE_NONE, null);
             }
         });
         mQSBSearchBarFadeOutAnim = ObjectAnimator.ofFloat(mQSBSearchBar, "alpha", 0f);
         mQSBSearchBarFadeOutAnim.setDuration(sTransitionOutDuration);
         mQSBSearchBarFadeOutAnim.addListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationStart(Animator animation) {
-                mQSBSearchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            }
-
-            @Override
             public void onAnimationEnd(Animator animation) {
                 mQSBSearchBar.setVisibility(View.INVISIBLE);
-                mQSBSearchBar.setLayerType(View.LAYER_TYPE_NONE, null);
             }
         });
     }
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 6bd597d..9274f81 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -17,6 +17,8 @@
 package com.android.launcher2;
 
 import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -24,7 +26,6 @@
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.AlertDialog;
 import android.app.WallpaperManager;
-import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ClipData;
@@ -56,7 +57,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.View.MeasureSpec;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -77,7 +77,7 @@
  */
 public class Workspace extends SmoothPagedView
         implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
-        DragController.DragListener, LauncherTransitionable {
+        DragController.DragListener {
     @SuppressWarnings({"UnusedDeclaration"})
     private static final String TAG = "Launcher.Workspace";
 
@@ -159,6 +159,8 @@
     private State mState = State.NORMAL;
     private boolean mIsSwitchingState = false;
 
+    private AnimatorListener mChangeStateAnimationListener;
+
     boolean mAnimatingViewIntoPlace = false;
     boolean mIsDragOccuring = false;
     boolean mChildrenLayersEnabled = true;
@@ -170,7 +172,6 @@
     private Bitmap mDragOutline = null;
     private final Rect mTempRect = new Rect();
     private final int[] mTempXY = new int[2];
-    private int mDragViewMultiplyColor;
     private float mOverscrollFade = 0;
 
     // Paint used to draw external drop outline
@@ -279,9 +280,11 @@
             // landscape
             TypedArray actionBarSizeTypedArray =
                 context.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
+            DisplayMetrics displayMetrics = res.getDisplayMetrics();
             final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f);
             final float systemBarHeight = res.getDimension(R.dimen.status_bar_height);
-            final float smallestScreenDim = res.getConfiguration().smallestScreenWidthDp;
+            final float smallestScreenDim = res.getConfiguration().smallestScreenWidthDp *
+                    displayMetrics.density;
 
             cellCountX = 1;
             while (CellLayout.widthInPortrait(res, cellCountX + 1) <= smallestScreenDim) {
@@ -297,7 +300,6 @@
 
         mSpringLoadedShrinkFactor =
             res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
-        mDragViewMultiplyColor = res.getColor(R.color.drag_view_multiply_color);
 
         // if the value is manually specified, use that instead
         cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
@@ -385,6 +387,20 @@
             // In this case, we will skip drawing background protection
         }
 
+        mChangeStateAnimationListener = new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mIsSwitchingState = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mIsSwitchingState = false;
+                mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
+                updateChildrenLayersEnabled();
+            }
+        };
+
         mWallpaperOffset = new WallpaperOffsetInterpolator();
         Display display = mLauncher.getWindowManager().getDefaultDisplay();
         mDisplayWidth = display.getWidth();
@@ -1624,10 +1640,26 @@
                 cl.setBackgroundAlphaMultiplier(finalAlphaMultiplierValue);
                 cl.setAlpha(finalAlpha);
                 cl.setRotationY(rotation);
+                mChangeStateAnimationListener.onAnimationEnd(null);
             }
         }
 
         if (animated) {
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(android.animation.Animator animation) {
+                    // The above code to determine initialAlpha and finalAlpha will ensure that only
+                    // the current page is visible during (and subsequently, after) the transition
+                    // animation.  If fade adjacent pages is disabled, then re-enable the page
+                    // visibility after the transition animation.
+                    if (!mFadeInAdjacentScreens) {
+                        for (int i = 0; i < getChildCount(); i++) {
+                            final CellLayout cl = (CellLayout) getChildAt(i);
+                            cl.setAlpha(1f);
+                        }
+                    }
+                }
+            });
             for (int index = 0; index < getChildCount(); index++) {
                 final int i = index;
                 final CellLayout cl = (CellLayout) getChildAt(i);
@@ -1683,6 +1715,9 @@
                 }
             }
             anim.setStartDelay(delay);
+            // 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
+            anim.addListener(mChangeStateAnimationListener);
         }
 
         if (stateIsSpringLoaded) {
@@ -1698,33 +1733,6 @@
         return anim;
     }
 
-    @Override
-    public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
-        mIsSwitchingState = true;
-    }
-
-    @Override
-    public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
-        mIsSwitchingState = false;
-        mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
-        updateChildrenLayersEnabled();
-        // The code in getChangeStateAnimation to determine initialAlpha and finalAlpha will ensure
-        // ensure that only the current page is visible during (and subsequently, after) the
-        // transition animation.  If fade adjacent pages is disabled, then re-enable the page
-        // visibility after the transition animation.
-        if (!mFadeInAdjacentScreens) {
-            for (int i = 0; i < getChildCount(); i++) {
-                final CellLayout cl = (CellLayout) getChildAt(i);
-                cl.setAlpha(1f);
-            }
-        }
-    }
-
-    @Override
-    public View getContent() {
-        return this;
-    }
-
     /**
      * Draw the View v into the given Canvas.
      *
@@ -1793,7 +1801,6 @@
         canvas.setBitmap(b);
         drawDragView(v, canvas, padding, true);
         mOutlineHelper.applyOuterBlur(b, canvas, outlineColor);
-        canvas.drawColor(mDragViewMultiplyColor, PorterDuff.Mode.MULTIPLY);
         canvas.setBitmap(null);
 
         return b;
@@ -2246,6 +2253,7 @@
                 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
                         disableHardwareLayersRunnable, this);
             } else {
+                d.deferDragViewCleanupPostAnimation = false;
                 cell.setVisibility(VISIBLE);
             }
             parent.onDropChild(cell);
@@ -3069,14 +3077,11 @@
             scaleX = scaleY = Math.min(scaleX,  scaleY);
         }
 
-        if (animationType == COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION) {
-            mLauncher.getDragLayer().scaleViewIntoPosition(dragView, loc, 1, scaleX, scaleY,
-                    animationEnd, onCompleteRunnable, duration);
-        } else if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
-            mLauncher.getDragLayer().scaleViewIntoPosition(dragView, loc, 0, 0.1f, 0.1f,
+        if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
+            mLauncher.getDragLayer().animateViewIntoPosition(dragView, loc, 0f, 0.1f, 0.1f,
                     DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
         } else {
-            mLauncher.getDragLayer().animateViewIntoPosition(dragView, loc, scaleX, scaleY,
+            mLauncher.getDragLayer().animateViewIntoPosition(dragView, loc, 1f, scaleX, scaleY,
                 animationEnd, onCompleteRunnable, duration);
         }
     }