Merge "Minimizing number of db calls when installing shortcut externally."
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index 2ce563d..d97e3c2 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -11,10 +11,6 @@
     <!-- 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 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/config.xml b/res/values/config.xml
index 691fb07..70e65c9 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -54,10 +54,6 @@
 
     <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>
-
     <!-- 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 8db1ab6..2f8d128 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";
 
     /**
@@ -228,6 +231,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);
@@ -534,7 +546,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 +613,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
@@ -588,26 +660,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 +702,6 @@
             mLauncher.exitSpringLoadedDragMode();
         }
         mLauncher.unlockScreenOrientationOnLargeUI();
-
     }
 
     @Override
@@ -642,6 +726,7 @@
             if (showOutOfSpaceMessage) {
                 mLauncher.showOutOfSpaceMessage();
             }
+            d.deferDragViewCleanupPostAnimation = false;
         }
     }
 
@@ -1037,6 +1122,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/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/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/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index 05596ef..5e5740e 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -70,6 +70,9 @@
     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 +414,16 @@
         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 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], scale,
-                onFinishRunnable, true, -1, null);
+        animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY,
+                onFinishRunnable, animationEndStyle, duration, null);
     }
 
     public void animateViewIntoPosition(DragView dragView, final View child,
@@ -483,18 +487,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) {
+            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);
     }
 
     /**
@@ -519,9 +524,11 @@
      *        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 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 boolean fadeOut, View anchorView) {
+            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));
@@ -558,8 +565,8 @@
         }
         mAnchorView = anchorView;
 
-        final float initialAlpha = view.getAlpha();
-        final float initialScale = mDropView.getScaleX();
+        final float initAlpha = view.getAlpha();
+        final float dropViewScale = mDropView.getScaleX();
 
         mDropAnim.setDuration(duration);
         mDropAnim.setFloatValues(0.0f, 1.0f);
@@ -574,23 +581,28 @@
                         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;
+                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(scale);
-                mDropView.setScaleY(scale);
+                mDropView.setScaleX(scaleX);
+                mDropView.setScaleY(scaleY);
                 mDropView.setAlpha(alpha);
-                invalidate();
             }
         });
         mDropAnim.addListener(new AnimatorListenerAdapter() {
@@ -598,17 +610,36 @@
                 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() {
+        if (mDropAnim != null) {
+            mDropAnim.cancel();
+        }
+        if (mDropView != null) {
+            mDropView.remove();
+        }
+        mDropView = null;
+        invalidate();
+    }
+
+    public View getAnimatedView() {
+        return mDropView;
+    }
+
     private void fadeOutDragView() {
         mFadeOutAnim = new ValueAnimator();
         mFadeOutAnim.setDuration(150);
@@ -620,13 +651,15 @@
 
                 float alpha = 1 - percent;
                 mDropView.setAlpha(alpha);
-                invalidate();
             }
         });
         mFadeOutAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
-                mDropView.remove();
+                if (mDropView != null) {
+                    mDropView.remove();
+                }
                 mDropView = null;
+                invalidate();
             }
         });
         mFadeOutAnim.start();
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index 7be70a2..90b2083 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -35,6 +35,7 @@
     private static float sDragAlpha = 0.8f;
 
     private Bitmap mBitmap;
+    private Bitmap mCrossFadeBitmap;
     private Paint mPaint;
     private int mRegistrationX;
     private int mRegistrationY;
@@ -43,13 +44,12 @@
     private Rect mDragRegion = null;
     private DragLayer mDragLayer = null;
     private boolean mHasDrawn = false;
+    private float mCrossFadeProgress = 0f;
 
     ValueAnimator mAnim;
     private float mOffsetX = 0.0f;
     private float mOffsetY = 0.0f;
 
-    private DragLayer.LayoutParams mLayoutParams;
-
     /**
      * Construct the drag view.
      * <p>
@@ -67,9 +67,10 @@
         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 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);
@@ -92,12 +93,8 @@
                 if (getParent() == null) {
                     animation.cancel();
                 } else {
-                    DragLayer.LayoutParams lp = mLayoutParams;
-                    lp.x += deltaX;
-                    lp.y += deltaY;
-                    lp.width = mBitmap.getWidth();
-                    lp.height = mBitmap.getHeight();
-                    mDragLayer.requestLayout();
+                    setTranslationX(getTranslationX() + deltaX);
+                    setTranslationY(getTranslationY() + deltaY);
                 }
             }
         });
@@ -164,9 +161,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) {
@@ -200,11 +231,10 @@
         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();
     }
 
@@ -215,9 +245,8 @@
     }
 
     public void resetLayoutParams() {
-        DragLayer.LayoutParams lp = mLayoutParams;
-        lp.x = lp.y = 0;
         mOffsetX = mOffsetY = 0;
+        requestLayout();
     }
 
     /**
@@ -227,10 +256,8 @@
      * @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() {
@@ -238,13 +265,5 @@
             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;
-    }
 }
 
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index a023516..ff7e10f 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -360,10 +360,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);
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/Launcher.java b/src/com/android/launcher2/Launcher.java
index 7d974a5..8850cf8 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) {
@@ -2164,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
@@ -2221,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;
@@ -2232,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);
@@ -2279,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
@@ -2303,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();
@@ -2349,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);
         }
     }
@@ -2368,13 +2406,14 @@
      * 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;
         }
         Resources res = getResources();
+        final Launcher instance = this;
 
         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
         final int fadeOutDuration =
@@ -2382,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) {
@@ -2413,22 +2451,27 @@
                 .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();
+                    }
                 }
             });
 
+            mStateAnimation = new AnimatorSet();
             mStateAnimation.playTogether(scaleAnim, alphaAnim);
             if (workspaceAnim != null) {
                 mStateAnimation.play(workspaceAnim);
@@ -2436,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);
         }
     }
@@ -2453,9 +2496,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 +2551,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 +2570,7 @@
                     // clean up our state transition functions
                     mAppsCustomizeTabHost.setVisibility(View.GONE);
                     mSearchDropTargetBar.showSearchBar(true);
-                    showWorkspace(true);
+                    showWorkspace(true, onCompleteRunnable);
                 } else {
                     exitSpringLoadedDragMode();
                 }
@@ -3381,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/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/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 41a2bfb..704138e 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;
@@ -76,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";
 
@@ -158,6 +159,8 @@
     private State mState = State.NORMAL;
     private boolean mIsSwitchingState = false;
 
+    private AnimatorListener mChangeStateAnimationListener;
+
     boolean mAnimatingViewIntoPlace = false;
     boolean mIsDragOccuring = false;
     boolean mChildrenLayersEnabled = true;
@@ -203,6 +206,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;
@@ -272,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) {
@@ -332,15 +342,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;
     }
 
@@ -386,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();
@@ -500,10 +515,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;
@@ -1623,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);
@@ -1682,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) {
@@ -1697,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.
      *
@@ -1948,7 +1957,7 @@
     }
 
     public boolean transitionStateShouldAllowDrop() {
-        return (!isSwitchingState() || mTransitionProgress > 0.5f);
+        return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL);
     }
 
     /**
@@ -2867,7 +2876,7 @@
         final Runnable exitSpringLoadedRunnable = new Runnable() {
             @Override
             public void run() {
-                mLauncher.exitSpringLoadedDragModeDelayed(true, false);
+                mLauncher.exitSpringLoadedDragModeDelayed(true, false, null);
             }
         };
 
@@ -2928,27 +2937,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 +3004,89 @@
         }
     }
 
+    // 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 == 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, 1f, scaleX, scaleY,
+                animationEnd, onCompleteRunnable, duration);
+        }
+    }
+
     public void setFinalTransitionTransform(CellLayout layout) {
         if (isSwitchingState()) {
             int index = indexOfChild(layout);