merge in jb-release history after reset to master
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index 2ce563d..7a07898 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -11,9 +11,10 @@
     <!-- Whether or not to fade the side pages -->
     <bool name="config_workspaceFadeAdjacentScreens">true</bool>
 
-    <!--  When dragging an item on the workspace, how much bigger (scale factor) the dragged view
-          should be, as compared to the original view. If 0, it will not be scaled at all. -->
-    <integer name="config_dragViewScaleFactor">115</integer>
+    <!-- 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/colors.xml b/res/values/colors.xml
index 1efeb9e..ece0fd8 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -23,6 +23,9 @@
     <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 691fb07..7ab9870 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -54,9 +54,10 @@
 
     <integer name="config_crosshairsFadeInTime">600</integer>
 
-    <!--  When dragging an item on the workspace, how much bigger (scale factor) the dragged view
-          should be, as compared to the original view. If 0, it will not be scaled at all. -->
-    <integer name="config_dragViewScaleFactor">115</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. -->
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 8db1ab6..91205d0 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -19,6 +19,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -39,6 +40,8 @@
 import android.graphics.TableMaskFilter;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Process;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -167,7 +170,7 @@
  */
 public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
         AllAppsView, View.OnClickListener, View.OnKeyListener, DragSource,
-        PagedViewIcon.PressedCallback {
+        PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener {
     static final String LOG_TAG = "AppsCustomizePagedView";
 
     /**
@@ -201,6 +204,7 @@
     private Canvas mCanvas;
     private Drawable mDefaultWidgetBackground;
     private IconCache mIconCache;
+    private int mDragViewMultiplyColor;
 
     // Dimens
     private int mContentWidth;
@@ -228,6 +232,15 @@
     ArrayList<AppsCustomizeAsyncTask> mRunningTasks;
     private static final int sPageSleepDelay = 200;
 
+    private Runnable mInflateWidgetRunnable = null;
+    private Runnable mBindWidgetRunnable = null;
+    static final int WIDGET_NO_CLEANUP_REQUIRED = -1;
+    static final int WIDGET_BOUND = 0;
+    static final int WIDGET_INFLATED = 1;
+    int mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED;
+    int mWidgetLoadingId = -1;
+    PendingAddWidgetInfo mCreateWidgetInfo = null;
+
     public AppsCustomizePagedView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mLayoutInflater = LayoutInflater.from(context);
@@ -242,6 +255,7 @@
         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);
@@ -534,7 +548,65 @@
         mLauncher.getWorkspace().beginDragShared(v, this);
     }
 
+    private void loadWidgetInBackground(final PendingAddWidgetInfo info) {
+        final AppWidgetProviderInfo pInfo = info.info;
+        if (pInfo.configure != null) {
+            return;
+        }
+
+        mBindWidgetRunnable = new Runnable() {
+            @Override
+            public void run() {
+                mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
+                AppWidgetManager.getInstance(mLauncher).bindAppWidgetId(mWidgetLoadingId,
+                        info.componentName);
+                mWidgetCleanupState = WIDGET_BOUND;
+            }
+        };
+        post(mBindWidgetRunnable);
+
+        mInflateWidgetRunnable = new Runnable() {
+            @Override
+            public void run() {
+                AppWidgetHostView hostView =
+                        mLauncher.getAppWidgetHost().createView(mContext, mWidgetLoadingId, pInfo);
+                info.boundWidget = hostView;
+                mWidgetCleanupState = WIDGET_INFLATED;
+            }
+        };
+        post(mInflateWidgetRunnable);
+    }
+
+    @Override
+    public void onShortPress(View v) {
+        // We are anticipating a long press, and we use this time to load bind and instantiate
+        // the widget. This will need to be cleaned up if it turns out no long press occurs.
+        mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag());
+        loadWidgetInBackground(mCreateWidgetInfo);
+    }
+
+    @Override
+    public void cleanUpShortPress(View v) {
+        PendingAddWidgetInfo info = mCreateWidgetInfo;
+        mCreateWidgetInfo = null;
+        if (mWidgetCleanupState >= 0 && mWidgetLoadingId != -1) {
+            mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
+        }
+        if (mWidgetCleanupState == WIDGET_BOUND) {
+            removeCallbacks(mInflateWidgetRunnable);
+        } else if (mWidgetCleanupState == WIDGET_INFLATED) {
+            AppWidgetHostView widget = info.boundWidget;
+            int widgetId = widget.getAppWidgetId();
+            mLauncher.getAppWidgetHost().deleteAppWidgetId(widgetId);
+        }
+        mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED;
+        mWidgetLoadingId = -1;
+    }
+
     private void beginDraggingWidget(View v) {
+        mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED;
+        mWidgetLoadingId = -1;
+
         // Get the widget preview as the drag representation
         ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
         PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
@@ -543,15 +615,17 @@
         Bitmap preview;
         Bitmap outline;
         if (createItemInfo instanceof PendingAddWidgetInfo) {
-            PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo;
+            PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo;
+            createItemInfo = createWidgetInfo;
+            mCreateWidgetInfo = null;
             int[] spanXY = mLauncher.getSpanForWidget(createWidgetInfo, null);
+            int[] size = mLauncher.getWorkspace().estimateItemSize(spanXY[0],
+                    spanXY[1], createWidgetInfo, true);
             createItemInfo.spanX = spanXY[0];
             createItemInfo.spanY = spanXY[1];
 
-            int[] maxSize = mLauncher.getWorkspace().estimateItemSize(spanXY[0], spanXY[1],
-                    createWidgetInfo, true);
             preview = getWidgetPreview(createWidgetInfo.componentName, createWidgetInfo.previewImage,
-                    createWidgetInfo.icon, spanXY[0], spanXY[1], maxSize[0], maxSize[1]);
+                    createWidgetInfo.icon, spanXY[0], spanXY[1], size[0], size[1]);
         } else {
             // Workaround for the fact that we don't keep the original ResolveInfo associated with
             // the shortcut around.  To get the icon, we just render the preview image (which has
@@ -578,6 +652,9 @@
         // 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;
@@ -588,26 +665,39 @@
         outline.recycle();
         preview.recycle();
     }
+
     @Override
-    protected boolean beginDragging(View v) {
-        // Dismiss the cling
-        mLauncher.dismissAllAppsCling(null);
-
+    protected boolean beginDragging(final View v) {
         if (!super.beginDragging(v)) return false;
 
-        // Reset the alpha on the dragged icon before we drag
-        resetDrawableState();
-
-        // Go into spring loaded mode (must happen before we startDrag())
-        mLauncher.enterSpringLoadedDragMode();
-
         if (v instanceof PagedViewIcon) {
             beginDraggingApplication(v);
         } else if (v instanceof PagedViewWidget) {
             beginDraggingWidget(v);
         }
+
+        // We delay entering spring-loaded mode slightly to make sure the UI
+        // thready is free of any work.
+        postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                // We don't enter spring-loaded mode if the drag has been cancelled
+                if (mLauncher.getDragController().isDragging()) {
+                    // Dismiss the cling
+                    mLauncher.dismissAllAppsCling(null);
+
+                    // Reset the alpha on the dragged icon before we drag
+                    resetDrawableState();
+
+                    // Go into spring loaded mode (must happen before we startDrag())
+                    mLauncher.enterSpringLoadedDragMode();
+                }
+            }
+        },150);
+
         return true;
     }
+
     private void endDragging(View target, boolean success) {
         mLauncher.getWorkspace().onDragStopped(success);
         if (!success || (target != mLauncher.getWorkspace() &&
@@ -617,7 +707,6 @@
             mLauncher.exitSpringLoadedDragMode();
         }
         mLauncher.unlockScreenOrientationOnLargeUI();
-
     }
 
     @Override
@@ -903,6 +992,9 @@
             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);
         }
     }
@@ -1037,6 +1129,7 @@
                 int[] cellSpans = mLauncher.getSpanForWidget(info, null);
                 widget.applyFromAppWidgetProviderInfo(info, -1, cellSpans);
                 widget.setTag(createItemInfo);
+                widget.setShortPressListener(this);
             } else if (rawInfo instanceof ResolveInfo) {
                 // Fill in the shortcuts information
                 ResolveInfo info = (ResolveInfo) rawInfo;
diff --git a/src/com/android/launcher2/DeleteDropTarget.java b/src/com/android/launcher2/DeleteDropTarget.java
index 3b82f9e..a6b2b5c 100644
--- a/src/com/android/launcher2/DeleteDropTarget.java
+++ b/src/com/android/launcher2/DeleteDropTarget.java
@@ -165,23 +165,30 @@
         }
     }
 
-    private void animateToTrashAndCompleteDrop(final DragObject d) {
+    Rect getDeleteRect(int deleteItemWidth, int deleteItemHeight) {
         DragLayer dragLayer = mLauncher.getDragLayer();
-        Rect from = new Rect();
-        Rect to = new Rect();
-        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
-        dragLayer.getViewRectRelativeToSelf(this, to);
 
+        Rect to = new Rect();
+        dragLayer.getViewRectRelativeToSelf(this, to);
         int width = mCurrentDrawable.getIntrinsicWidth();
         int height = mCurrentDrawable.getIntrinsicHeight();
         to.set(to.left + getPaddingLeft(), to.top + getPaddingTop(),
                 to.left + getPaddingLeft() + width, to.bottom);
 
         // Center the destination rect about the trash icon
-        int xOffset = (int) -(d.dragView.getMeasuredWidth() - width) / 2;
-        int yOffset = (int) -(d.dragView.getMeasuredHeight() - height) / 2;
+        int xOffset = (int) -(deleteItemWidth - width) / 2;
+        int yOffset = (int) -(deleteItemHeight - height) / 2;
         to.offset(xOffset, yOffset);
 
+        return to;
+    }
+
+    private void animateToTrashAndCompleteDrop(final DragObject d) {
+        DragLayer dragLayer = mLauncher.getDragLayer();
+        Rect from = new Rect();
+        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
+        Rect to = getDeleteRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight());
+
         mSearchDropTargetBar.deferOnDragEnd();
         Runnable onAnimationEndRunnable = new Runnable() {
             @Override
@@ -191,9 +198,10 @@
                 completeDrop(d);
             }
         };
-        dragLayer.animateView(d.dragView, from, to, 0.1f, 0.1f,
+        dragLayer.animateView(d.dragView, from, to, 0.1f, 1, 1, 0.1f, 0.1f,
                 DELETE_ANIMATION_DURATION, new DecelerateInterpolator(2),
-                new DecelerateInterpolator(1.5f), onAnimationEndRunnable, false, null);
+                new DecelerateInterpolator(1.5f), onAnimationEndRunnable,
+                DragLayer.ANIMATION_END_DISAPPEAR, null);
     }
 
     private void completeDrop(DragObject d) {
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 425f301..8658eeb 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -394,9 +394,7 @@
                 listener.onDragEnd();
             }
             if (mDragObject.dragView != null) {
-                if (!mDragObject.deferDragViewCleanupPostAnimation) {
-                    mDragObject.dragView.remove();
-                }
+                mDragObject.dragView.remove();
                 mDragObject.dragView = null;
             }
         }
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index 05596ef..6f3bcd1 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -62,14 +62,21 @@
     private ValueAnimator mDropAnim = null;
     private ValueAnimator mFadeOutAnim = null;
     private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
-    private DragView mDropView = null;
+    private View 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;
     private int mQsbIndex = -1;
+    public static final int ANIMATION_END_DISAPPEAR = 0;
+    public static final int ANIMATION_END_FADE_OUT = 1;
+    public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
 
     /**
      * Used to create a new DragLayer from XML.
@@ -411,15 +418,23 @@
         animateViewIntoPosition(dragView, child, null);
     }
 
-    public void animateViewIntoPosition(DragView dragView, final int[] pos, float scale,
-            Runnable onFinishRunnable) {
+    public void animateViewIntoPosition(DragView dragView, final int[] pos, 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], scale,
-                onFinishRunnable, true, -1, null);
+        animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], 1, 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,
@@ -483,18 +498,19 @@
                 oa.start();
             }
         };
-        animateViewIntoPosition(dragView, fromX, fromY, toX, toY, scale,
-                onCompleteRunnable, true, duration, anchorView);
+        animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, scale, scale,
+                onCompleteRunnable, ANIMATION_END_FADE_OUT, duration, anchorView);
     }
 
-    private void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
-            final int toX, final int toY, float finalScale, Runnable onCompleteRunnable,
-            boolean fadeOut, int duration, View anchorView) {
+    private void animateViewIntoPosition(final View 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) {
         Rect from = new Rect(fromX, fromY, fromX +
                 view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
         Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
-        animateView(view, from, to, 1f, finalScale, duration, null, null,
-                onCompleteRunnable, true, anchorView);
+        animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration,
+                null, null, onCompleteRunnable, animationEndStyle, anchorView);
     }
 
     /**
@@ -518,10 +534,11 @@
      *        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 DragView view, final Rect from, final Rect to,
-            final float finalAlpha, final float finalScale, int duration,
-            final Interpolator motionInterpolator, final Interpolator alphaInterpolator,
-            final Runnable onCompleteRunnable, final boolean fadeOut, View anchorView) {
+    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) {
         // 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));
@@ -544,10 +561,8 @@
             mFadeOutAnim.cancel();
         }
 
-        // Show the drop view if it was previously hidden
         mDropView = view;
-        mDropView.cancelAnimation();
-        mDropView.resetLayoutParams();
+        final float initialAlpha = view.getAlpha();
         mDropAnim = new ValueAnimator();
         if (alphaInterpolator == null || motionInterpolator == null) {
             mDropAnim.setInterpolator(mCubicEaseOutInterpolator);
@@ -558,38 +573,28 @@
         }
         mAnchorView = anchorView;
 
-        final float initialAlpha = view.getAlpha();
-        final float initialScale = 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();
-                final int width = view.getMeasuredWidth();
-                final int height = view.getMeasuredHeight();
+                // Invalidate the old position
+                int width = view.getMeasuredWidth();
+                int height = view.getMeasuredHeight();
+                invalidate(mDropViewPos[0], mDropViewPos[1],
+                        mDropViewPos[0] + width, mDropViewPos[1] + height);
 
                 float alphaPercent = alphaInterpolator == null ? percent :
                         alphaInterpolator.getInterpolation(percent);
                 float motionPercent = motionInterpolator == null ? percent :
                         motionInterpolator.getInterpolation(percent);
-                float scale = finalScale * percent + initialScale * (1 - percent);
-                float alpha = finalAlpha * alphaPercent + initialAlpha * (1 - alphaPercent);
 
-                float fromLeft = from.left + (initialScale - 1f) * width / 2;
-                float fromTop = from.top + (initialScale - 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(scale);
-                mDropView.setScaleY(scale);
-                mDropView.setAlpha(alpha);
+                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();
             }
         });
@@ -598,17 +603,32 @@
                 if (onCompleteRunnable != null) {
                     onCompleteRunnable.run();
                 }
-                if (fadeOut) {
+                switch (animationEndStyle) {
+                case ANIMATION_END_DISAPPEAR:
+                    clearAnimatedView();
+                    break;
+                case ANIMATION_END_FADE_OUT:
                     fadeOutDragView();
-                } else {
-                    mDropView.remove();
-                    mDropView = null;
+                    break;
+                case ANIMATION_END_REMAIN_VISIBLE:
+                    break;
                 }
             }
         });
         mDropAnim.start();
     }
 
+    public void clearAnimatedView() {
+        mDropView = null;
+        mDropViewScaleX = 1;
+        mDropViewScaleY = 1;
+        invalidate();
+    }
+
+    public View getAnimatedView() {
+        return mDropView;
+    }
+
     private void fadeOutDragView() {
         mFadeOutAnim = new ValueAnimator();
         mFadeOutAnim.setDuration(150);
@@ -617,16 +637,17 @@
         mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
                 final float percent = (Float) animation.getAnimatedValue();
-
-                float alpha = 1 - percent;
-                mDropView.setAlpha(alpha);
-                invalidate();
+                mDropViewAlpha = 1 - percent;
+                int width = mDropView.getMeasuredWidth();
+                int height = mDropView.getMeasuredHeight();
+                invalidate(mDropViewPos[0], mDropViewPos[1],
+                        mDropViewPos[0] + width, mDropViewPos[1] + height);
             }
         });
         mFadeOutAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
-                mDropView.remove();
                 mDropView = null;
+                invalidate();
             }
         });
         mFadeOutAnim.start();
@@ -675,4 +696,25 @@
             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 7be70a2..15d9c54 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -32,9 +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;
     private int mRegistrationX;
     private int mRegistrationY;
@@ -43,6 +42,7 @@
     private Rect mDragRegion = null;
     private DragLayer mDragLayer = null;
     private boolean mHasDrawn = false;
+    private float mCrossFadeProgress = 0f;
 
     ValueAnimator mAnim;
     private float mOffsetX = 0.0f;
@@ -67,13 +67,20 @@
         mDragLayer = launcher.getDragLayer();
 
         final Resources res = getResources();
-        final float scale = res.getInteger(R.integer.config_dragViewScaleFactor) / 100f;
-        final float offsetX = res.getDimensionPixelSize(R.dimen.dragViewOffsetX);
-        final float offsetY = res.getDimensionPixelSize(R.dimen.dragViewOffsetY);
+        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);
 
         // Animate the view into the correct position
         mAnim = ValueAnimator.ofFloat(0.0f, 1.0f);
-        mAnim.setDuration(150);
+        mAnim.setDuration(110);
         mAnim.setInterpolator(new DecelerateInterpolator(2.5f));
         mAnim.addUpdateListener(new AnimatorUpdateListener() {
             @Override
@@ -85,9 +92,6 @@
 
                 mOffsetX += deltaX;
                 mOffsetY += deltaY;
-                setScaleX(1f + (value * (scale - 1f)));
-                setScaleY(1f + (value * (scale - 1f)));
-                setAlpha(sDragAlpha * value + (1f - value));
 
                 if (getParent() == null) {
                     animation.cancel();
@@ -95,14 +99,12 @@
                     DragLayer.LayoutParams lp = mLayoutParams;
                     lp.x += deltaX;
                     lp.y += deltaY;
-                    lp.width = mBitmap.getWidth();
-                    lp.height = mBitmap.getHeight();
                     mDragLayer.requestLayout();
                 }
             }
         });
 
-        mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height);
+        mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
         setDragRegion(new Rect(0, 0, width, height));
 
         // The point in our scaled bitmap that the touch events are located
@@ -164,9 +166,43 @@
             p.setColor(0xaaffffff);
             canvas.drawRect(0, 0, getWidth(), getHeight(), p);
         }
+        if (mPaint == null) {
+            mPaint = new Paint();
+        }
 
         mHasDrawn = true;
+        boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
+        if (crossFade) {
+            int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
+            mPaint.setAlpha(alpha);
+        }
         canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
+        if (crossFade) {
+            mPaint.setAlpha((int) (255 * mCrossFadeProgress));
+            canvas.save();
+            float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
+            float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
+            canvas.scale(sX, sY);
+            canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
+            canvas.restore();
+        }
+    }
+
+    public void setCrossFadeBitmap(Bitmap crossFadeBitmap) {
+        mCrossFadeBitmap = crossFadeBitmap;
+    }
+
+    public void crossFade(int duration) {
+        ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+        va.setDuration(duration);
+        va.setInterpolator(new DecelerateInterpolator(1.5f));
+        va.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mCrossFadeProgress = animation.getAnimatedFraction();
+            }
+        });
+        va.start();
     }
 
     public void setPaint(Paint paint) {
@@ -208,18 +244,6 @@
         mAnim.start();
     }
 
-    public void cancelAnimation() {
-        if (mAnim != null && mAnim.isRunning()) {
-            mAnim.cancel();
-        }
-    }
-
-    public void resetLayoutParams() {
-        DragLayer.LayoutParams lp = mLayoutParams;
-        lp.x = lp.y = 0;
-        mOffsetX = mOffsetY = 0;
-    }
-
     /**
      * Move the window containing this view.
      *
@@ -234,9 +258,7 @@
     }
 
     void remove() {
-        if (getParent() != null) {
-            mDragLayer.removeView(DragView.this);
-        }
+        mDragLayer.removeView(DragView.this);
     }
 
     int[] getPosition(int[] result) {
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index e49f782..4172da2 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -55,9 +55,6 @@
         /** 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 a023516..ca537d8 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 DragView srcView, Rect dstRect,
+            final ShortcutInfo srcInfo, final View 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, null);
+        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
 
         // This will animate the first item from it's position as an icon into its
         // position as the first item in the preview
@@ -320,9 +320,8 @@
         mFolderRingAnimator.animateToNaturalState();
     }
 
-    private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
-            float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable,
-            DragObject d) {
+    private void onDrop(final ShortcutInfo item, View animateView, Rect finalRect,
+            float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
         item.cellX = -1;
         item.cellY = -1;
 
@@ -360,10 +359,11 @@
 
             float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
 
+            float finalScale = scale * scaleRelativeToDragLayer;
             dragLayer.animateView(animateView, from, to, finalAlpha,
-                    scale * scaleRelativeToDragLayer, DROP_IN_ANIMATION_DURATION,
+                    1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
                     new DecelerateInterpolator(2), new AccelerateInterpolator(2),
-                    postAnimationRunnable, false, null);
+                    postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
             postDelayed(new Runnable() {
                 public void run() {
                     addItem(item);
@@ -383,7 +383,7 @@
             item = (ShortcutInfo) d.dragInfo;
         }
         mFolder.notifyDrop();
-        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
+        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
     }
 
     public DropTarget getDropTargetDelegate(DragObject d) {
diff --git a/src/com/android/launcher2/IconCache.java b/src/com/android/launcher2/IconCache.java
index 17d6e40..1e8379d 100644
--- a/src/com/android/launcher2/IconCache.java
+++ b/src/com/android/launcher2/IconCache.java
@@ -50,15 +50,13 @@
     private int mIconDpi;
 
     public IconCache(LauncherApplication context) {
+        ActivityManager activityManager =
+                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
         mContext = context;
         mPackageManager = context.getPackageManager();
-        if (LauncherApplication.isScreenLarge()) {
-            ActivityManager activityManager =
-                    (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-            mIconDpi = activityManager.getLauncherLargeIconDensity();
-        } else {
-            mIconDpi = context.getResources().getDisplayMetrics().densityDpi;
-        }
+        mIconDpi = activityManager.getLauncherLargeIconDensity();
+
         // need to set mIconDpi before getting default icon
         mDefaultIcon = makeDefaultIcon();
     }
diff --git a/src/com/android/launcher2/InfoDropTarget.java b/src/com/android/launcher2/InfoDropTarget.java
index 21fe8ba..dba845b 100644
--- a/src/com/android/launcher2/InfoDropTarget.java
+++ b/src/com/android/launcher2/InfoDropTarget.java
@@ -29,7 +29,6 @@
 import android.view.ViewGroup;
 
 import com.android.launcher.R;
-import com.android.launcher2.DropTarget.DragObject;
 
 public class InfoDropTarget extends ButtonDropTarget {
 
@@ -86,9 +85,6 @@
         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/Launcher.java b/src/com/android/launcher2/Launcher.java
index 7d974a5..4062145 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -49,6 +49,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -246,6 +247,7 @@
     private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2];
 
     static final ArrayList<String> sDumpLogs = new ArrayList<String>();
+    PendingAddWidgetInfo mWidgetBeingConfigured = null;
 
 
     private BubbleTextView mWaitingForResume;
@@ -498,7 +500,7 @@
                 break;
             case REQUEST_CREATE_APPWIDGET:
                 int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
-                completeAddAppWidget(appWidgetId, args.container, args.screen);
+                completeAddAppWidget(appWidgetId, args.container, args.screen, null, null);
                 result = true;
                 break;
             case REQUEST_PICK_WALLPAPER:
@@ -509,10 +511,20 @@
     }
 
     @Override
-    protected void onActivityResult(final int requestCode, int resultCode, final Intent data) {
+    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
         boolean delayExitSpringLoadedMode = false;
+        boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
+                requestCode == REQUEST_CREATE_APPWIDGET);
         mWaitingForResult = false;
 
+        // We have special handling for widgets
+        if (isWidgetDrop) {
+            int appWidgetId = data != null ?
+                    data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
+            completeTwoStageWidgetDrop(resultCode, appWidgetId);
+            return;
+        }
+
         // The pattern used here is that a user PICKs a specific application,
         // which, depending on the target, might need to CREATE the actual target.
 
@@ -526,26 +538,51 @@
             args.screen = mPendingAddInfo.screen;
             args.cellX = mPendingAddInfo.cellX;
             args.cellY = mPendingAddInfo.cellY;
-
-            // If the loader is still running, defer the add until it is done.
             if (isWorkspaceLocked()) {
                 sPendingAddList.add(args);
             } else {
                 delayExitSpringLoadedMode = completeAdd(args);
             }
-        } else if ((requestCode == REQUEST_PICK_APPWIDGET ||
-                requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED) {
-            if (data != null) {
-                // Clean up the appWidgetId if we canceled
-                int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
-                if (appWidgetId != -1) {
-                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
-                }
-            }
         }
-
+        mDragLayer.clearAnimatedView();
         // Exit spring loaded mode if necessary after cancelling the configuration of a widget
-        exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode);
+        exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode,
+                null);
+    }
+
+    private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
+        CellLayout cellLayout = (CellLayout) mWorkspace.getChildAt(mWidgetBeingConfigured.screen);
+        Runnable onCompleteRunnable = null;
+        int animationType = 0;
+
+        if (resultCode == RESULT_OK) {
+            animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
+            final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
+                    mWidgetBeingConfigured.info);
+            mWidgetBeingConfigured.boundWidget = layout;
+            onCompleteRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
+                            mPendingAddInfo.screen, layout, null);
+                    exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
+                            null);
+                }
+            };
+        } else if (resultCode == RESULT_CANCELED) {
+            animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
+            onCompleteRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
+                            null);
+                }
+            };
+        }
+        mWorkspace.animateExternalDrop(mWidgetBeingConfigured, cellLayout,
+                (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
+                animationType);
+        mWidgetBeingConfigured = null;
     }
 
     @Override
@@ -934,8 +971,11 @@
      * @param appWidgetId The app widget id
      * @param cellInfo The position on screen where to create the widget.
      */
-    private void completeAddAppWidget(final int appWidgetId, long container, int screen) {
-        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+    private void completeAddAppWidget(final int appWidgetId, long container, int screen,
+            AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) {
+        if (appWidgetInfo == null) {
+            appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+        }
 
         // Calculate the grid spans needed to fit this widget
         CellLayout layout = getCellLayout(container, screen);
@@ -984,12 +1024,16 @@
                 container, screen, cellXY[0], cellXY[1], false);
 
         if (!mRestoring) {
-            // Perform actual inflation because we're live
-            launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+            if (hostView == null) {
+                // Perform actual inflation because we're live
+                launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+                launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
+            } else {
+                // The AppWidgetHostView has already been inflated and instantiated
+                launcherInfo.hostView = hostView;
+            }
 
-            launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
             launcherInfo.hostView.setTag(launcherInfo);
-
             mWorkspace.addInScreen(launcherInfo.hostView, container, screen, cellXY[0], cellXY[1],
                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
 
@@ -1427,9 +1471,9 @@
         addAppWidgetImpl(appWidgetId, null);
     }
 
-    void addAppWidgetImpl(int appWidgetId, PendingAddWidgetInfo info) {
-        AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
-
+    void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info) {
+        final AppWidgetProviderInfo appWidget = info.info;
+        Runnable configurationActivity = null;
         if (appWidget.configure != null) {
             // Launch over to configure widget, if needed
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
@@ -1437,9 +1481,8 @@
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
             if (info != null) {
                 if (info.mimeType != null && !info.mimeType.isEmpty()) {
-                    intent.putExtra(
-                            InstallWidgetReceiver.EXTRA_APPWIDGET_CONFIGURATION_DATA_MIME_TYPE,
-                            info.mimeType);
+                    intent.putExtra(InstallWidgetReceiver.
+                            EXTRA_APPWIDGET_CONFIGURATION_DATA_MIME_TYPE, info.mimeType);
 
                     final String mimeType = info.mimeType;
                     final ClipData clipData = (ClipData) info.configurationData;
@@ -1450,8 +1493,8 @@
                             final CharSequence stringData = item.getText();
                             final Uri uriData = item.getUri();
                             final Intent intentData = item.getIntent();
-                            final String key =
-                                InstallWidgetReceiver.EXTRA_APPWIDGET_CONFIGURATION_DATA;
+                            final String key = InstallWidgetReceiver.
+                                    EXTRA_APPWIDGET_CONFIGURATION_DATA;
                             if (uriData != null) {
                                 intent.putExtra(key, uriData);
                             } else if (intentData != null) {
@@ -1464,14 +1507,13 @@
                     }
                 }
             }
-
             startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
+            mWidgetBeingConfigured = info;
         } else {
             // Otherwise just add it
-            completeAddAppWidget(appWidgetId, info.container, info.screen);
-
+            completeAddAppWidget(appWidgetId, info.container, info.screen, info.boundWidget, appWidget);
             // Exit spring loaded mode if necessary after adding the widget
-            exitSpringLoadedDragModeDelayed(true, false);
+            exitSpringLoadedDragModeDelayed(true, false, null);
         }
     }
 
@@ -1519,9 +1561,16 @@
             mPendingAddInfo.cellY = cell[1];
         }
 
-        int appWidgetId = getAppWidgetHost().allocateAppWidgetId();
-        AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, info.componentName);
+        AppWidgetHostView hostView = info.boundWidget;
+        int appWidgetId;
+        if (hostView != null) {
+            appWidgetId = hostView.getAppWidgetId();
+        } else {
+            appWidgetId = getAppWidgetHost().allocateAppWidgetId();
+            AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, info.componentName);
+        }
         addAppWidgetImpl(appWidgetId, info);
+
     }
 
     void processShortcut(Intent intent) {
@@ -2368,8 +2417,9 @@
      * This is the opposite of showAppsCustomizeHelper.
      * @param animated If true, the transition will be animated.
      */
-    private void hideAppsCustomizeHelper(
-            State toState, final boolean animated, final boolean springLoaded) {
+    private void hideAppsCustomizeHelper(State toState, final boolean animated,
+            final boolean springLoaded, final Runnable onCompleteRunnable) {
+
         if (mStateAnimation != null) {
             mStateAnimation.cancel();
             mStateAnimation = null;
@@ -2426,6 +2476,9 @@
                     dispatchOnLauncherTransitionEnd(fromView, animated, true);
                     dispatchOnLauncherTransitionEnd(toView, animated, true);
                     mWorkspace.hideScrollingIndicator(false);
+                    if (onCompleteRunnable != null) {
+                        onCompleteRunnable.run();
+                    }
                 }
             });
 
@@ -2453,9 +2506,13 @@
     }
 
     void showWorkspace(boolean animated) {
+        showWorkspace(animated, null);
+    }
+
+    void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
         if (mState != State.WORKSPACE) {
             mWorkspace.setVisibility(View.VISIBLE);
-            hideAppsCustomizeHelper(State.WORKSPACE, animated, false);
+            hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);
 
             // Show the search bar and hotseat
             mSearchDropTargetBar.showSearchBar(animated);
@@ -2504,13 +2561,14 @@
 
     void enterSpringLoadedDragMode() {
         if (mState == State.APPS_CUSTOMIZE) {
-            hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true);
+            hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true, null);
             hideDockDivider();
             mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
         }
     }
 
-    void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay) {
+    void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay,
+            final Runnable onCompleteRunnable) {
         if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
 
         mHandler.postDelayed(new Runnable() {
@@ -2522,7 +2580,7 @@
                     // clean up our state transition functions
                     mAppsCustomizeTabHost.setVisibility(View.GONE);
                     mSearchDropTargetBar.showSearchBar(true);
-                    showWorkspace(true);
+                    showWorkspace(true, onCompleteRunnable);
                 } else {
                     exitSpringLoadedDragMode();
                 }
diff --git a/src/com/android/launcher2/PagedViewWidget.java b/src/com/android/launcher2/PagedViewWidget.java
index 12e9c46..5ba8691 100644
--- a/src/com/android/launcher2/PagedViewWidget.java
+++ b/src/com/android/launcher2/PagedViewWidget.java
@@ -23,6 +23,7 @@
 import android.content.res.Resources;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -40,6 +41,9 @@
     private ImageView mPreviewImageView;
 
     private String mDimensionsFormatString;
+    CheckForShortPress mPendingCheckForShortPress = null;
+    ShortPressListener mShortPressListener = null;
+    boolean mShortPressTriggered = false;
 
     public PagedViewWidget(Context context) {
         this(context, null);
@@ -127,8 +131,67 @@
         }
     }
 
+    void setShortPressListener(ShortPressListener listener) {
+        mShortPressListener = listener;
+    }
+
+    interface ShortPressListener {
+        void onShortPress(View v);
+        void cleanUpShortPress(View v);
+    }
+
+    class CheckForShortPress implements Runnable {
+        public void run() {
+            if (mShortPressListener != null) {
+                mShortPressListener.onShortPress(PagedViewWidget.this);
+            }
+            mShortPressTriggered = true;
+        }
+    }
+
+    private void checkForShortPress() {
+        if (mPendingCheckForShortPress == null) {
+            mPendingCheckForShortPress = new CheckForShortPress();
+        }
+        postDelayed(mPendingCheckForShortPress, 120);
+    }
+
+    /**
+     * Remove the longpress detection timer.
+     */
+    private void removeShortPressCallback() {
+        if (mPendingCheckForShortPress != null) {
+          removeCallbacks(mPendingCheckForShortPress);
+        }
+    }
+
+    private void cleanUpShortPress() {
+        removeShortPressCallback();
+        if (mShortPressTriggered) {
+            if (mShortPressListener != null) {
+                mShortPressListener.cleanUpShortPress(PagedViewWidget.this);
+            }
+            mShortPressTriggered = false;
+        }
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        super.onTouchEvent(event);
+
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_UP:
+                cleanUpShortPress();
+                break;
+            case MotionEvent.ACTION_DOWN:
+                checkForShortPress();
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                cleanUpShortPress();
+                break;
+            case MotionEvent.ACTION_MOVE:
+                break;
+        }
         // We eat up the touch events here, since the PagedView (which uses the same swiping
         // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when
         // the user is scrolling between pages.  This means that if the pages themselves don't
@@ -136,6 +199,6 @@
         // onTouchEvent() handling will prevent further intercept touch events from being called
         // (it's the same view in that case).  This is not ideal, but to prevent more changes,
         // we just always mark the touch event as handled.
-        return super.onTouchEvent(event) || true;
+        return true;
     }
 }
diff --git a/src/com/android/launcher2/PendingAddItemInfo.java b/src/com/android/launcher2/PendingAddItemInfo.java
index 9c52ecf..09a8a9e 100644
--- a/src/com/android/launcher2/PendingAddItemInfo.java
+++ b/src/com/android/launcher2/PendingAddItemInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher2;
 
+import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.os.Parcelable;
@@ -35,6 +36,8 @@
     int minHeight;
     int previewImage;
     int icon;
+    AppWidgetProviderInfo info;
+    AppWidgetHostView boundWidget;
 
     // Any configuration data that we want to pass to a configuration activity when
     // starting up a widget
@@ -43,6 +46,7 @@
 
     public PendingAddWidgetInfo(AppWidgetProviderInfo i, String dataMimeType, Parcelable data) {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+        this.info = i;
         componentName = i.provider;
         minWidth = i.minWidth;
         minHeight = i.minHeight;
@@ -53,4 +57,18 @@
             configurationData = data;
         }
     }
-}
\ No newline at end of file
+
+    // Copy constructor
+    public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
+        minWidth = copy.minWidth;
+        minHeight = copy.minHeight;
+        previewImage = copy.previewImage;
+        icon = copy.icon;
+        info = copy.info;
+        boundWidget = copy.boundWidget;
+        mimeType = copy.mimeType;
+        configurationData = copy.configurationData;
+        componentName = copy.componentName;
+        itemType = copy.itemType;
+    }
+}
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 41a2bfb..309fda8 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -56,6 +56,7 @@
 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;
@@ -169,6 +170,7 @@
     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
@@ -203,6 +205,11 @@
     final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
     final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
 
+    // Relating to the animation of items being dropped externally
+    public static final int ANIMATE_INTO_POSITION = 0;
+    public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 1;
+    public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 2;
+
     // These variables are used for storing the initial and final values during workspace animations
     private int mSavedScrollX;
     private float mSavedRotationY;
@@ -290,6 +297,7 @@
 
         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);
@@ -332,15 +340,6 @@
             int hCell, int vCell, int hSpan, int vSpan) {
         RectF r = new RectF();
         cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
-        if (pendingInfo instanceof PendingAddWidgetInfo) {
-            PendingAddWidgetInfo widgetInfo = (PendingAddWidgetInfo) pendingInfo;
-            Rect p = AppWidgetHostView.getDefaultPaddingForWidget(mContext,
-                    widgetInfo.componentName, null);
-            r.top += p.top;
-            r.left += p.left;
-            r.right -= p.right;
-            r.bottom -= p.bottom;
-        }
         return r;
     }
 
@@ -500,10 +499,12 @@
             child.setOnKeyListener(new IconKeyEventListener());
         }
 
-        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-        if (lp == null) {
+        LayoutParams genericLp = child.getLayoutParams();
+        CellLayout.LayoutParams lp;
+        if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
             lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
         } else {
+            lp = (CellLayout.LayoutParams) genericLp;
             lp.cellX = x;
             lp.cellY = y;
             lp.cellHSpan = spanX;
@@ -1792,6 +1793,7 @@
         canvas.setBitmap(b);
         drawDragView(v, canvas, padding, true);
         mOutlineHelper.applyOuterBlur(b, canvas, outlineColor);
+        canvas.drawColor(mDragViewMultiplyColor, PorterDuff.Mode.MULTIPLY);
         canvas.setBitmap(null);
 
         return b;
@@ -1948,7 +1950,7 @@
     }
 
     public boolean transitionStateShouldAllowDrop() {
-        return (!isSwitchingState() || mTransitionProgress > 0.5f);
+        return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL);
     }
 
     /**
@@ -2244,7 +2246,6 @@
                 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
                         disableHardwareLayersRunnable, this);
             } else {
-                d.deferDragViewCleanupPostAnimation = false;
                 cell.setVisibility(VISIBLE);
             }
             parent.onDropChild(cell);
@@ -2867,7 +2868,7 @@
         final Runnable exitSpringLoadedRunnable = new Runnable() {
             @Override
             public void run() {
-                mLauncher.exitSpringLoadedDragModeDelayed(true, false);
+                mLauncher.exitSpringLoadedDragModeDelayed(true, false, null);
             }
         };
 
@@ -2928,27 +2929,8 @@
                 }
             };
 
-            // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
-            // location and size on the home screen.
-            RectF r = estimateItemPosition(cellLayout, pendingInfo,
-                    mTargetCell[0], mTargetCell[1], spanX, spanY);
-            int loc[] = new int[2];
-            loc[0] = (int) r.left;
-            loc[1] = (int) r.top;
-            setFinalTransitionTransform(cellLayout);
-            float cellLayoutScale =
-                    mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(cellLayout, loc);
-            resetTransitionTransform(cellLayout);
-
-            float dragViewScale =  Math.min(r.width() / d.dragView.getMeasuredWidth(),
-                    r.height() / d.dragView.getMeasuredHeight());
-            // The animation will scale the dragView about its center, so we need to center about
-            // the final location.
-            loc[0] -= (d.dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
-            loc[1] -= (d.dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
-
-            mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, loc,
-                    dragViewScale * cellLayoutScale, onAnimationCompleteRunnable);
+            animateExternalDrop((PendingAddItemInfo) info, cellLayout, d.dragView,
+                    onAnimationCompleteRunnable, ANIMATE_INTO_POSITION);
         } else {
             // This is for other drag/drop cases, like dragging from All Apps
             View view = null;
@@ -3014,6 +2996,92 @@
         }
     }
 
+    // The following methods deal with animating an item from external drop
+    void onPreDraw(View v) {
+        if (v instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) v;
+            for (int i = 0; i < vg.getChildCount(); i++) {
+                View child = vg.getChildAt(i);
+                onPreDraw(child);
+            }
+        } else if (v instanceof TextView) {
+            ((TextView) v).onPreDraw();
+        }
+    }
+
+    public Bitmap createWidgetBitmap(PendingAddWidgetInfo widgetInfo) {
+        int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX,
+                widgetInfo.spanY, widgetInfo, false);
+        View layout = widgetInfo.boundWidget;
+        layout.setVisibility(VISIBLE);
+
+        int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
+        int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
+        Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
+                Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas(b);
+
+        layout.measure(width, height);
+        layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
+        onPreDraw(layout);
+        layout.draw(c);
+        c.setBitmap(null);
+        return b;
+    }
+
+    public void animateExternalDrop(PendingAddItemInfo pendingInfo, CellLayout cellLayout,
+            DragView dragView, Runnable onCompleteRunnable, int animationType) {
+        // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
+        // location and size on the home screen.
+        int spanX = pendingInfo.spanX;
+        int spanY = pendingInfo.spanY;
+        RectF r = estimateItemPosition(cellLayout, pendingInfo,
+                mTargetCell[0], mTargetCell[1], spanX, spanY);
+        int loc[] = new int[2];
+        loc[0] = (int) r.left;
+        loc[1] = (int) r.top;
+        setFinalTransitionTransform(cellLayout);
+        float cellLayoutScale =
+                mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(cellLayout, loc);
+        resetTransitionTransform(cellLayout);
+
+        float dragViewScaleX = r.width() / dragView.getMeasuredWidth();
+        float dragViewScaleY = r.height() / dragView.getMeasuredHeight();
+        // The animation will scale the dragView about its center, so we need to center about
+        // the final location.
+        loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
+        loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
+
+        float scaleX = dragViewScaleX * cellLayoutScale;
+        float scaleY = dragViewScaleY * cellLayoutScale;
+
+        Resources res = mLauncher.getResources();
+        int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
+
+        int animationEnd = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
+        if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
+                (((PendingAddWidgetInfo) pendingInfo).info.configure == null ||
+                animationType == COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION)) {
+            Bitmap crossFadeBitmap = createWidgetBitmap((PendingAddWidgetInfo) pendingInfo);
+            dragView.setCrossFadeBitmap(crossFadeBitmap);
+            dragView.crossFade((int) (duration * 0.8f));
+            animationEnd = DragLayer.ANIMATION_END_DISAPPEAR;
+        } else {
+            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,
+                    DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
+        } else {
+            mLauncher.getDragLayer().animateViewIntoPosition(dragView, loc, scaleX, scaleY,
+                animationEnd, onCompleteRunnable, duration);
+        }
+    }
+
     public void setFinalTransitionTransform(CellLayout layout) {
         if (isSwitchingState()) {
             int index = indexOfChild(layout);