When adding or moving a resizable widget, the widget may resize

-> If a widget is resizable, and there is not enough room to add it
   in its current (or default) size, but can be scaled down to fit
   a certain area, it will be resized to fit the available space
-> The resizing is animated using a crossfade and scale between
   the original dragView and the widget rendered in the final size

Change-Id: I75db9dcabecce11598b3ae55f20b96b2ec6b7e87
diff --git a/src/com/android/launcher2/AppWidgetResizeFrame.java b/src/com/android/launcher2/AppWidgetResizeFrame.java
index 6d132eb..c01a882 100644
--- a/src/com/android/launcher2/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher2/AppWidgetResizeFrame.java
@@ -80,7 +80,7 @@
         mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
 
         final AppWidgetProviderInfo info = widgetView.getAppWidgetInfo();
-        int[] result = mLauncher.getMinResizeSpanForWidget(info, null);
+        int[] result = mLauncher.getMinSpanForWidget(info, null);
         mMinHSpan = result[0];
         mMinVSpan = result[1];
 
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 2001ad9..3fcff72 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -629,6 +629,9 @@
                     spanXY[1], createWidgetInfo, true);
             createItemInfo.spanX = spanXY[0];
             createItemInfo.spanY = spanXY[1];
+            int[] minSpanXY = mLauncher.getMinSpanForWidget(createWidgetInfo, null);
+            createWidgetInfo.minSpanX = minSpanXY[0];
+            createWidgetInfo.minSpanY = minSpanXY[1];
 
             FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
             float minScale = 1.25f;
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 5df271e..37328ef 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -34,7 +34,6 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.NinePatchDrawable;
 import android.util.AttributeSet;
@@ -53,6 +52,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.Stack;
 
 public class CellLayout extends ViewGroup {
     static final String TAG = "CellLayout";
@@ -109,7 +109,7 @@
 
     // These arrays are used to implement the drag visualization on x-large screens.
     // They are used as circular arrays, indexed by mDragOutlineCurrent.
-    private Point[] mDragOutlines = new Point[4];
+    private Rect[] mDragOutlines = new Rect[4];
     private float[] mDragOutlineAlphas = new float[mDragOutlines.length];
     private InterruptibleInOutAnimator[] mDragOutlineAnims =
             new InterruptibleInOutAnimator[mDragOutlines.length];
@@ -198,7 +198,7 @@
 
         mDragCell[0] = mDragCell[1] = -1;
         for (int i = 0; i < mDragOutlines.length; i++) {
-            mDragOutlines[i] = new Point(-1, -1);
+            mDragOutlines[i] = new Rect(-1, -1, -1, -1);
         }
 
         // When dragging things around the home screens, we show a green outline of
@@ -232,10 +232,7 @@
                         animation.cancel();
                     } else {
                         mDragOutlineAlphas[thisIndex] = (Float) animation.getAnimatedValue();
-                        final int left = mDragOutlines[thisIndex].x;
-                        final int top = mDragOutlines[thisIndex].y;
-                        CellLayout.this.invalidate(left, top,
-                                left + outline.getWidth(), top + outline.getHeight());
+                        CellLayout.this.invalidate(mDragOutlines[thisIndex]);
                     }
                 }
             });
@@ -419,10 +416,10 @@
         for (int i = 0; i < mDragOutlines.length; i++) {
             final float alpha = mDragOutlineAlphas[i];
             if (alpha > 0) {
-                final Point p = mDragOutlines[i];
+                final Rect r = mDragOutlines[i];
                 final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag();
                 paint.setAlpha((int)(alpha + .5f));
-                canvas.drawBitmap(b, p.x, p.y, paint);
+                canvas.drawBitmap(b, null, r, paint);
             }
         }
 
@@ -1038,11 +1035,16 @@
     }
 
     void visualizeDropLocation(View v, Bitmap dragOutline, int originX, int originY,
-            int spanX, int spanY, Point dragOffset, Rect dragRegion) {
+            int minSpanX, int minSpanY, int spanX, int spanY, Point dragOffset, Rect dragRegion) {
 
         final int oldDragCellX = mDragCell[0];
         final int oldDragCellY = mDragCell[1];
-        final int[] nearest = findNearestVacantArea(originX, originY, spanX, spanY, v, mDragCell);
+        int[] resultSpan = new int[2];
+        final int[] nearest = findNearestVacantArea(originX, originY, minSpanX, minSpanY,
+                spanX, spanY, v, mDragCell, resultSpan);
+        boolean resize = spanX > resultSpan[0] || spanY > resultSpan[1];
+        spanX = resultSpan[0];
+        spanY = resultSpan[1];
         if (v != null && dragOffset == null) {
             mDragCenter.set(originX + (v.getWidth() / 2), originY + (v.getHeight() / 2));
         } else {
@@ -1093,12 +1095,15 @@
                             - dragOutline.getHeight()) / 2;
                 }
             }
-
             final int oldIndex = mDragOutlineCurrent;
             mDragOutlineAnims[oldIndex].animateOut();
             mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
+            Rect r = mDragOutlines[mDragOutlineCurrent];
+            r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
+            if (resize) {
+                cellToRect(nearest[0], nearest[1], spanX, spanY, r);
+            }
 
-            mDragOutlines[mDragOutlineCurrent].set(left, top);
             mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
             mDragOutlineAnims[mDragOutlineCurrent].animateIn();
         }
@@ -1112,8 +1117,7 @@
     public void clearDragOutlines() {
         final int oldIndex = mDragOutlineCurrent;
         mDragOutlineAnims[oldIndex].animateOut();
-        mDragCell[0] = -1;
-        mDragCell[1] = -1;
+        mDragCell[0] = mDragCell[1] = -1;
     }
 
     /**
@@ -1129,8 +1133,8 @@
      * @return The X, Y cell of a vacant area that can contain this object,
      *         nearest the requested location.
      */
-    int[] findNearestVacantArea(
-            int pixelX, int pixelY, int spanX, int spanY, int[] result) {
+    int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
+            int[] result) {
         return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, result);
     }
 
@@ -1140,6 +1144,27 @@
      *
      * @param pixelX The X location at which you want to search for a vacant area.
      * @param pixelY The Y location at which you want to search for a vacant area.
+     * @param minSpanX The minimum horizontal span required
+     * @param minSpanY The minimum vertical span required
+     * @param spanX Horizontal span of the object.
+     * @param spanY Vertical span of the object.
+     * @param result Array in which to place the result, or null (in which case a new array will
+     *        be allocated)
+     * @return The X, Y cell of a vacant area that can contain this object,
+     *         nearest the requested location.
+     */
+    int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
+            int spanY, int[] result, int[] resultSpan) {
+        return findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null,
+                result, resultSpan);
+    }
+
+    /**
+     * Find a vacant area that will fit the given bounds nearest the requested
+     * cell location. Uses Euclidean distance to score multiple vacant areas.
+     *
+     * @param pixelX The X location at which you want to search for a vacant area.
+     * @param pixelY The Y location at which you want to search for a vacant area.
      * @param spanX Horizontal span of the object.
      * @param spanY Vertical span of the object.
      * @param ignoreOccupied If true, the result can be an occupied cell
@@ -1150,6 +1175,43 @@
      */
     int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, View ignoreView,
             boolean ignoreOccupied, int[] result) {
+        return findNearestArea(pixelX, pixelY, spanX, spanY,
+                spanX, spanY, ignoreView, ignoreOccupied, result, null);
+    }
+
+    private final Stack<Rect> mTempRectStack = new Stack<Rect>();
+    private void lazyInitTempRectStack() {
+        if (mTempRectStack.isEmpty()) {
+            for (int i = 0; i < mCountX * mCountY; i++) {
+                mTempRectStack.push(new Rect());
+            }
+        }
+    }
+    private void recycleTempRects(Stack<Rect> used) {
+        while (!used.isEmpty()) {
+            mTempRectStack.push(used.pop());
+        }
+    }
+
+    /**
+     * Find a vacant area that will fit the given bounds nearest the requested
+     * cell location. Uses Euclidean distance to score multiple vacant areas.
+     *
+     * @param pixelX The X location at which you want to search for a vacant area.
+     * @param pixelY The Y location at which you want to search for a vacant area.
+     * @param minSpanX The minimum horizontal span required
+     * @param minSpanY The minimum vertical span required
+     * @param spanX Horizontal span of the object.
+     * @param spanY Vertical span of the object.
+     * @param ignoreOccupied If true, the result can be an occupied cell
+     * @param result Array in which to place the result, or null (in which case a new array will
+     *        be allocated)
+     * @return The X, Y cell of a vacant area that can contain this object,
+     *         nearest the requested location.
+     */
+    int[] findNearestArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
+            View ignoreView, boolean ignoreOccupied, int[] result, int[] resultSpan) {
+        lazyInitTempRectStack();
         // mark space take by ignoreView as available (method checks if ignoreView is null)
         markCellsAsUnoccupiedForView(ignoreView);
 
@@ -1162,35 +1224,99 @@
         // Keep track of best-scoring drop area
         final int[] bestXY = result != null ? result : new int[2];
         double bestDistance = Double.MAX_VALUE;
+        final Rect bestRect = new Rect(-1, -1, -1, -1);
+        final Stack<Rect> validRegions = new Stack<Rect>();
 
         final int countX = mCountX;
         final int countY = mCountY;
         final boolean[][] occupied = mOccupied;
 
-        for (int y = 0; y < countY - (spanY - 1); y++) {
+        if (minSpanX <= 0 || minSpanY <= 0 || spanX <= 0 || spanY <= 0 ||
+                spanX < minSpanX || spanY < minSpanY) {
+            return bestXY;
+        }
+
+        for (int y = 0; y < countY - (minSpanY - 1); y++) {
             inner:
-            for (int x = 0; x < countX - (spanX - 1); x++) {
+            for (int x = 0; x < countX - (minSpanX - 1); x++) {
+                int ySize = -1;
+                int xSize = -1;
                 if (ignoreOccupied) {
-                    for (int i = 0; i < spanX; i++) {
-                        for (int j = 0; j < spanY; j++) {
+                    // First, let's see if this thing fits anywhere
+                    for (int i = 0; i < minSpanX; i++) {
+                        for (int j = 0; j < minSpanY; j++) {
                             if (occupied[x + i][y + j]) {
-                                // small optimization: we can skip to after the column we
-                                // just found an occupied cell
-                                x += i;
                                 continue inner;
                             }
                         }
                     }
+                    xSize = minSpanX;
+                    ySize = minSpanY;
+
+                    // We know that the item will fit at _some_ acceptable size, now let's see
+                    // how big we can make it. We'll alternate between incrementing x and y spans
+                    // until we hit a limit.
+                    boolean incX = true;
+                    boolean hitMaxX = xSize >= spanX;
+                    boolean hitMaxY = ySize >= spanY;
+                    while (!(hitMaxX && hitMaxY)) {
+                        if (incX && !hitMaxX) {
+                            for (int j = 0; j < ySize; j++) {
+                                if (x + xSize > countX -1 || occupied[x + xSize][y + j]) {
+                                    // We can't move out horizontally
+                                    hitMaxX = true;
+                                }
+                            }
+                            if (!hitMaxX) {
+                                xSize++;
+                            }
+                        } else if (!hitMaxY) {
+                            for (int i = 0; i < xSize; i++) {
+                                if (y + ySize > countY - 1 || occupied[x + i][y + ySize]) {
+                                    // We can't move out vertically
+                                    hitMaxY = true;
+                                }
+                            }
+                            if (!hitMaxY) {
+                                ySize++;
+                            }
+                        }
+                        hitMaxX |= xSize >= spanX;
+                        hitMaxY |= ySize >= spanY;
+                        incX = !incX;
+                    }
+                    incX = true;
+                    hitMaxX = xSize >= spanX;
+                    hitMaxY = ySize >= spanY;
                 }
                 final int[] cellXY = mTmpXY;
                 cellToCenterPoint(x, y, cellXY);
 
+                // We verify that the current rect is not a sub-rect of any of our previous
+                // candidates. In this case, the current rect is disqualified in favour of the
+                // containing rect.
+                Rect currentRect = mTempRectStack.pop();
+                currentRect.set(x, y, x + xSize, y + ySize);
+                boolean contained = false;
+                for (Rect r : validRegions) {
+                    if (r.contains(currentRect)) {
+                        contained = true;
+                        break;
+                    }
+                }
+                validRegions.push(currentRect);
                 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
                         + Math.pow(cellXY[1] - pixelY, 2));
-                if (distance <= bestDistance) {
+                if ((distance <= bestDistance && !contained) ||
+                        currentRect.contains(bestRect)) {
                     bestDistance = distance;
                     bestXY[0] = x;
                     bestXY[1] = y;
+                    if (resultSpan != null) {
+                        resultSpan[0] = xSize;
+                        resultSpan[1] = ySize;
+                    }
+                    bestRect.set(currentRect);
                 }
             }
         }
@@ -1202,6 +1328,7 @@
             bestXY[0] = -1;
             bestXY[1] = -1;
         }
+        recycleTempRects(validRegions);
         return bestXY;
     }
 
@@ -1224,6 +1351,27 @@
     }
 
     /**
+     * Find a vacant area that will fit the given bounds nearest the requested
+     * cell location. Uses Euclidean distance to score multiple vacant areas.
+     *
+     * @param pixelX The X location at which you want to search for a vacant area.
+     * @param pixelY The Y location at which you want to search for a vacant area.
+     * @param minSpanX The minimum horizontal span required
+     * @param minSpanY The minimum vertical span required
+     * @param spanX Horizontal span of the object.
+     * @param spanY Vertical span of the object.
+     * @param ignoreView Considers space occupied by this view as unoccupied
+     * @param result Previously returned value to possibly recycle.
+     * @return The X, Y cell of a vacant area that can contain this object,
+     *         nearest the requested location.
+     */
+    int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY,
+            int spanX, int spanY, View ignoreView, int[] result, int[] resultSpan) {
+        return findNearestArea(pixelX, pixelY, minSpanX, minSpanY,
+                spanX, spanY, ignoreView, true, result, resultSpan);
+    }
+
+    /**
      * Find a starting cell position that will fit the given bounds nearest the requested
      * cell location. Uses Euclidean distance to score multiple vacant areas.
      *
@@ -1390,8 +1538,7 @@
         }
 
         // Invalidate the drag data
-        mDragCell[0] = -1;
-        mDragCell[1] = -1;
+        mDragCell[0] = mDragCell[1] = -1;
         mDragOutlineAnims[mDragOutlineCurrent].animateOut();
         mDragOutlineCurrent = (mDragOutlineCurrent + 1) % mDragOutlineAnims.length;
 
@@ -1422,7 +1569,7 @@
      * @param cellVSpan Height in cells
      * @param resultRect Rect into which to put the results
      */
-    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
+    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, Rect resultRect) {
         final int cellWidth = mCellWidth;
         final int cellHeight = mCellHeight;
         final int widthGap = mWidthGap;
@@ -1597,10 +1744,10 @@
         }
     }
 
-    public void onMove(View view, int newCellX, int newCellY) {
+    public void onMove(View view, int newCellX, int newCellY, int newSpanX, int newSpanY) {
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
         markCellsAsUnoccupiedForView(view);
-        markCellsForView(newCellX, newCellY, lp.cellHSpan, lp.cellVSpan, true);
+        markCellsForView(newCellX, newCellY, newSpanX, newSpanY, true);
     }
 
     public void markCellsAsOccupiedForView(View view) {
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index 7bc9bc8..a3b389d 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -458,7 +458,7 @@
             toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
         } else if (child instanceof FolderIcon) {
             // Account for holographic blur padding on the drag view
-            toY -= HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS / 2;
+            toY -= Workspace.DRAG_BITMAP_PADDING / 2;
             // Center in the x coordinate about the target's drawable
             toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
         } else {
@@ -470,28 +470,19 @@
         final int fromX = r.left;
         final int fromY = r.top;
         child.setVisibility(INVISIBLE);
-        child.setAlpha(0);
         Runnable onCompleteRunnable = new Runnable() {
             public void run() {
                 child.setVisibility(VISIBLE);
-                ObjectAnimator oa = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
-                oa.setDuration(60);
-                oa.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(android.animation.Animator animation) {
-                        if (onFinishAnimationRunnable != null) {
-                            onFinishAnimationRunnable.run();
-                        }
-                    }
-                });
-                oa.start();
+                if (onFinishAnimationRunnable != null) {
+                    onFinishAnimationRunnable.run();
+                }
             }
         };
         animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, scale, scale,
-                onCompleteRunnable, ANIMATION_END_FADE_OUT, duration, anchorView);
+                onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
     }
 
-    private void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
+    public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
             final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY,
             float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
             int animationEndStyle, int duration, View anchorView) {
diff --git a/src/com/android/launcher2/InstallWidgetReceiver.java b/src/com/android/launcher2/InstallWidgetReceiver.java
index 6b3763c..a1e9b11 100644
--- a/src/com/android/launcher2/InstallWidgetReceiver.java
+++ b/src/com/android/launcher2/InstallWidgetReceiver.java
@@ -189,7 +189,7 @@
             final PendingAddWidgetInfo createInfo = new PendingAddWidgetInfo(widgetInfo, mMimeType,
                     mClipData);
             mLauncher.addAppWidgetFromDrop(createInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                    mTargetLayoutScreen, null, mTargetLayoutPos);
+                    mTargetLayoutScreen, null, null, mTargetLayoutPos);
         }
     }
 }
diff --git a/src/com/android/launcher2/ItemInfo.java b/src/com/android/launcher2/ItemInfo.java
index 8d46624..11a6c0d 100644
--- a/src/com/android/launcher2/ItemInfo.java
+++ b/src/com/android/launcher2/ItemInfo.java
@@ -77,6 +77,15 @@
     int spanY = 1;
 
     /**
+     * Indicates the minimum X cell span.
+     */
+    int minSpanX = 1;
+
+    /**
+     * Indicates the minimum Y cell span.
+     */
+    int minSpanY = 1;
+    /**
      * Indicates whether the item is a gesture.
      */
     boolean isGesture = false;
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 83ca675..d670463 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -584,9 +584,9 @@
                 }
             };
         }
-        mWorkspace.animateExternalDrop(mWidgetBeingConfigured, cellLayout,
+        mWorkspace.animateWidgetDrop(mWidgetBeingConfigured, cellLayout,
                 (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
-                animationType);
+                animationType, mWidgetBeingConfigured.boundWidget, true);
         mWidgetBeingConfigured = null;
     }
 
@@ -959,7 +959,7 @@
         return getSpanForWidget(info.provider, info.minWidth, info.minHeight, spanXY);
     }
 
-    int[] getMinResizeSpanForWidget(AppWidgetProviderInfo info, int[] spanXY) {
+    int[] getMinSpanForWidget(AppWidgetProviderInfo info, int[] spanXY) {
         return getSpanForWidget(info.provider, info.minResizeWidth, info.minResizeHeight, spanXY);
     }
 
@@ -967,6 +967,11 @@
         return getSpanForWidget(info.componentName, info.minWidth, info.minHeight, spanXY);
     }
 
+    int[] getMinSpanForWidget(PendingAddWidgetInfo info, int[] spanXY) {
+        return getSpanForWidget(info.componentName, info.minResizeWidth,
+                info.minResizeHeight, spanXY);
+    }
+
     /**
      * Add a widget to the workspace.
      *
@@ -982,6 +987,7 @@
         // Calculate the grid spans needed to fit this widget
         CellLayout layout = getCellLayout(container, screen);
 
+        int[] minSpanXY = getMinSpanForWidget(appWidgetInfo, null);
         int[] spanXY = getSpanForWidget(appWidgetInfo, null);
 
         // Try finding open space on Launcher screen
@@ -989,18 +995,24 @@
         // if we are placing widgets on a "spring-loaded" screen
         int[] cellXY = mTmpAddItemCellCoordinates;
         int[] touchXY = mPendingAddInfo.dropPos;
+        int[] finalSpan = new int[2];
         boolean foundCellSpan = false;
         if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) {
             cellXY[0] = mPendingAddInfo.cellX;
             cellXY[1] = mPendingAddInfo.cellY;
+            spanXY[0] = mPendingAddInfo.spanX;
+            spanXY[1] = mPendingAddInfo.spanY;
             foundCellSpan = true;
         } else if (touchXY != null) {
             // when dragging and dropping, just find the closest free spot
             int[] result = layout.findNearestVacantArea(
-                    touchXY[0], touchXY[1], spanXY[0], spanXY[1], cellXY);
+                    touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0],
+                    spanXY[1], cellXY, finalSpan);
+            spanXY[0] = finalSpan[0];
+            spanXY[1] = finalSpan[1];
             foundCellSpan = (result != null);
         } else {
-            foundCellSpan = layout.findCellForSpan(cellXY, spanXY[0], spanXY[1]);
+            foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]);
         }
 
         if (!foundCellSpan) {
@@ -1021,6 +1033,8 @@
         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
         launcherInfo.spanX = spanXY[0];
         launcherInfo.spanY = spanXY[1];
+        launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
+        launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
 
         LauncherModel.addItemToDatabase(this, launcherInfo,
                 container, screen, cellXY[0], cellXY[1], false);
@@ -1036,6 +1050,7 @@
             }
 
             launcherInfo.hostView.setTag(launcherInfo);
+            launcherInfo.hostView.setVisibility(View.VISIBLE);
             mWorkspace.addInScreen(launcherInfo.hostView, container, screen, cellXY[0], cellXY[1],
                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
 
@@ -1465,6 +1480,7 @@
         mPendingAddInfo.screen = -1;
         mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
         mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
+        mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
         mPendingAddInfo.dropPos = null;
     }
 
@@ -1556,15 +1572,22 @@
      * @param position The location on the screen where it was dropped, optional
      */
     void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, int screen,
-            int[] cell, int[] loc) {
+            int[] cell, int[] span, int[] loc) {
         resetAddInfo();
         mPendingAddInfo.container = info.container = container;
         mPendingAddInfo.screen = info.screen = screen;
         mPendingAddInfo.dropPos = loc;
+        mPendingAddInfo.minSpanX = info.minSpanX;
+        mPendingAddInfo.minSpanY = info.minSpanY;
+
         if (cell != null) {
             mPendingAddInfo.cellX = cell[0];
             mPendingAddInfo.cellY = cell[1];
         }
+        if (span != null) {
+            mPendingAddInfo.spanX = span[0];
+            mPendingAddInfo.spanY = span[1];
+        }
 
         AppWidgetHostView hostView = info.boundWidget;
         int appWidgetId;
@@ -1575,7 +1598,6 @@
             AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, info.componentName);
         }
         addAppWidgetImpl(appWidgetId, info);
-
     }
 
     void processShortcut(Intent intent) {
diff --git a/src/com/android/launcher2/PendingAddItemInfo.java b/src/com/android/launcher2/PendingAddItemInfo.java
index 09a8a9e..d36e217 100644
--- a/src/com/android/launcher2/PendingAddItemInfo.java
+++ b/src/com/android/launcher2/PendingAddItemInfo.java
@@ -34,6 +34,8 @@
 class PendingAddWidgetInfo extends PendingAddItemInfo {
     int minWidth;
     int minHeight;
+    int minResizeWidth;
+    int minResizeHeight;
     int previewImage;
     int icon;
     AppWidgetProviderInfo info;
@@ -50,6 +52,8 @@
         componentName = i.provider;
         minWidth = i.minWidth;
         minHeight = i.minHeight;
+        minResizeWidth = i.minResizeWidth;
+        minResizeHeight = i.minResizeHeight;
         previewImage = i.previewImage;
         icon = i.icon;
         if (dataMimeType != null && data != null) {
@@ -62,6 +66,8 @@
     public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
         minWidth = copy.minWidth;
         minHeight = copy.minHeight;
+        minResizeWidth = copy.minResizeWidth;
+        minResizeHeight = copy.minResizeHeight;
         previewImage = copy.previewImage;
         icon = copy.icon;
         info = copy.info;
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index c9898c8..e461a85 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -17,6 +17,7 @@
 package com.android.launcher2;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -40,7 +41,6 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Point;
-import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region.Op;
@@ -173,6 +173,7 @@
     private final Rect mTempRect = new Rect();
     private final int[] mTempXY = new int[2];
     private float mOverscrollFade = 0;
+    public static final int DRAG_BITMAP_PADDING = 0;
 
     // Paint used to draw external drop outline
     private final Paint mExternalDragOutlinePaint = new Paint();
@@ -207,9 +208,11 @@
     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;
+    public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
+    public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
+    public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
+    public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
+    public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
 
     // Relating to workspace drag fade out
     private float mDragFadeOutAlpha;
@@ -325,13 +328,13 @@
     // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
     // dimension if unsuccessful
     public int[] estimateItemSize(int hSpan, int vSpan,
-            PendingAddItemInfo pendingItemInfo, boolean springLoaded) {
+            ItemInfo itemInfo, boolean springLoaded) {
         int[] size = new int[2];
         if (getChildCount() > 0) {
             CellLayout cl = (CellLayout) mLauncher.getWorkspace().getChildAt(0);
-            RectF r = estimateItemPosition(cl, pendingItemInfo, 0, 0, hSpan, vSpan);
-            size[0] = (int) r.width();
-            size[1] = (int) r.height();
+            Rect r = estimateItemPosition(cl, itemInfo, 0, 0, hSpan, vSpan);
+            size[0] = r.width();
+            size[1] = r.height();
             if (springLoaded) {
                 size[0] *= mSpringLoadedShrinkFactor;
                 size[1] *= mSpringLoadedShrinkFactor;
@@ -343,9 +346,9 @@
             return size;
         }
     }
-    public RectF estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
+    public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
             int hCell, int vCell, int hSpan, int vSpan) {
-        RectF r = new RectF();
+        Rect r = new Rect();
         cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
         return r;
     }
@@ -1492,23 +1495,18 @@
     public void onDragStartedWithItem(View v) {
         final Canvas canvas = new Canvas();
 
-        // We need to add extra padding to the bitmap to make room for the glow effect
-        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
-
         // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(v, canvas, bitmapPadding);
+        mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING);
     }
 
     public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, Paint alphaClipPaint) {
         final Canvas canvas = new Canvas();
 
-        // We need to add extra padding to the bitmap to make room for the glow effect
-        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
-
         int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
 
         // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(b, canvas, bitmapPadding, size[0], size[1], alphaClipPaint);
+        mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0],
+                size[1], alphaClipPaint);
     }
 
     // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was
@@ -1912,35 +1910,29 @@
         }
 
         mDragInfo = cellInfo;
-        child.setVisibility(GONE);
+        child.setVisibility(INVISIBLE);
 
         child.clearFocus();
         child.setPressed(false);
 
         final Canvas canvas = new Canvas();
 
-        // We need to add extra padding to the bitmap to make room for the glow effect
-        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
-
         // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(child, canvas, bitmapPadding);
+        mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING);
         beginDragShared(child, this);
     }
 
     public void beginDragShared(View child, DragSource source) {
         Resources r = getResources();
 
-        // We need to add extra padding to the bitmap to make room for the glow effect
-        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
-
         // The drag bitmap follows the touch point around on the screen
-        final Bitmap b = createDragBitmap(child, new Canvas(), bitmapPadding);
+        final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
 
         final int bmpWidth = b.getWidth();
 
         mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
         final int dragLayerX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
-        int dragLayerY = mTempXY[1] - bitmapPadding / 2;
+        int dragLayerY = mTempXY[1] - DRAG_BITMAP_PADDING / 2;
 
         Point dragVisualizeOffset = null;
         Rect dragRect = null;
@@ -1954,7 +1946,8 @@
             dragLayerY += top;
             // Note: The drag region is used to calculate drag layer offsets, but the
             // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
-            dragVisualizeOffset = new Point(-bitmapPadding / 2, iconPaddingTop - bitmapPadding / 2);
+            dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2,
+                    iconPaddingTop - DRAG_BITMAP_PADDING / 2);
             dragRect = new Rect(left, top, right, bottom);
         } else if (child instanceof FolderIcon) {
             int previewSize = r.getDimensionPixelSize(R.dimen.folder_preview_size);
@@ -2026,8 +2019,14 @@
                 spanY = dragInfo.spanY;
             }
 
+            int minSpanX = spanX;
+            int minSpanY = spanY;
+            if (d.dragInfo instanceof PendingAddWidgetInfo) {
+                minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
+                minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
+            }
             mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
-                    (int) mDragViewVisualCenter[1], spanX, spanY, mDragTargetLayout, mTargetCell);
+                    (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout, mTargetCell);
             if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell, true)) {
                 return true;
             }
@@ -2037,7 +2036,7 @@
             }
 
             // Don't accept the drop if there's no room for the item
-            if (!mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
+            if (!mDragTargetLayout.findCellForSpanIgnoring(null, minSpanX, minSpanY, ignoreView)) {
                 // Don't show the message if we are dropping on the AllApps button and the hotseat
                 // is full
                 if (mTargetCell != null && mLauncher.isHotseatLayout(mDragTargetLayout)) {
@@ -2157,7 +2156,7 @@
         return false;
     }
 
-    public void onDrop(DragObject d) {
+    public void onDrop(final DragObject d) {
         mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
                 mDragViewVisualCenter);
 
@@ -2173,6 +2172,7 @@
         CellLayout dropTargetLayout = mDragTargetLayout;
 
         int snapScreen = -1;
+        boolean resizeOnDrop = false;
         if (d.dragSource != this) {
             final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
                     (int) mDragViewVisualCenter[1] };
@@ -2180,6 +2180,7 @@
         } else if (mDragInfo != null) {
             final View cell = mDragInfo.cell;
 
+            Runnable resizeRunnable = null;
             if (dropTargetLayout != null) {
                 // Move internally
                 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
@@ -2208,29 +2209,46 @@
 
                 // Aside from the special case where we're dropping a shortcut onto a shortcut,
                 // we need to find the nearest cell location that is vacant
+                ItemInfo item = (ItemInfo) d.dragInfo;
+                int minSpanX = item.spanX;
+                int minSpanY = item.spanY;
+                if (item.minSpanX > 0 && item.minSpanY > 0) {
+                    minSpanX = item.minSpanX;
+                    minSpanY = item.minSpanY;
+                }
+                int[] resultSpan = new int[2];
                 mTargetCell = findNearestVacantArea((int) mDragViewVisualCenter[0],
-                        (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY, cell,
-                        dropTargetLayout, mTargetCell);
+                        (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragInfo.spanX,
+                        mDragInfo.spanY, cell, dropTargetLayout, mTargetCell, resultSpan);
+                boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
+                if (foundCell && (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
+                    resizeOnDrop = true;
+                    item.spanX = resultSpan[0];
+                    item.spanY = resultSpan[1];
+                }
 
                 if (mCurrentPage != screen && !hasMovedIntoHotseat) {
                     snapScreen = screen;
                     snapToPage(screen);
                 }
 
-                if (mTargetCell[0] >= 0 && mTargetCell[1] >= 0) {
+                if (foundCell) {
+                    final ItemInfo info = (ItemInfo) cell.getTag();
                     if (hasMovedLayouts) {
                         // Reparent the view
                         getParentCellLayoutForView(cell).removeView(cell);
                         addInScreen(cell, container, screen, mTargetCell[0], mTargetCell[1],
-                                mDragInfo.spanX, mDragInfo.spanY);
+                                info.spanX, info.spanY);
                     }
 
                     // update the item's position after drop
-                    final ItemInfo info = (ItemInfo) cell.getTag();
                     CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
-                    dropTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
+                    dropTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1],
+                            item.spanX, item.spanY);
                     lp.cellX = mTargetCell[0];
                     lp.cellY = mTargetCell[1];
+                    lp.cellHSpan = item.spanX;
+                    lp.cellVSpan = item.spanY;
                     cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screen,
                             mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
 
@@ -2243,18 +2261,18 @@
                         final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
                         AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
                         if (pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
-                            final Runnable resizeRunnable = new Runnable() {
+                            final Runnable addResizeFrame = new Runnable() {
                                 public void run() {
                                     DragLayer dragLayer = mLauncher.getDragLayer();
                                     dragLayer.addResizeFrame(info, hostView, cellLayout);
                                 }
                             };
-                            post(new Runnable() {
+                            resizeRunnable = (new Runnable() {
                                 public void run() {
                                     if (!isPageMoving()) {
-                                        resizeRunnable.run();
+                                        addResizeFrame.run();
                                     } else {
-                                        mDelayedResizeRunnable = resizeRunnable;
+                                        mDelayedResizeRunnable = addResizeFrame;
                                     }
                                 }
                             });
@@ -2263,25 +2281,40 @@
 
                     LauncherModel.moveItemInDatabase(mLauncher, info, container, screen, lp.cellX,
                             lp.cellY);
+                } else {
+                    // If we can't find a drop location, we return the item to its original position
+                    CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+                    mTargetCell[0] = lp.cellX;
+                    mTargetCell[1] = lp.cellY;
                 }
             }
 
             final CellLayout parent = (CellLayout) cell.getParent().getParent();
-
+            final Runnable finalResizeRunnable = resizeRunnable;
             // Prepare it to be animated into its new position
             // This must be called after the view has been re-parented
-            final Runnable disableHardwareLayersRunnable = new Runnable() {
+            final Runnable onCompleteRunnable = new Runnable() {
                 @Override
                 public void run() {
                     mAnimatingViewIntoPlace = false;
                     updateChildrenLayersEnabled();
+                    if (finalResizeRunnable != null) {
+                        finalResizeRunnable.run();
+                    }
                 }
             };
             mAnimatingViewIntoPlace = true;
             if (d.dragView.hasDrawn()) {
-                int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
-                mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
-                        disableHardwareLayersRunnable, this);
+                final ItemInfo info = (ItemInfo) cell.getTag();
+                if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
+                    int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
+                            ANIMATE_INTO_POSITION_AND_DISAPPEAR;
+                    animateWidgetDrop(info, parent, d.dragView,
+                            onCompleteRunnable, animationType, cell, false);
+                } else {
+                    mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell,
+                            onCompleteRunnable);
+                }
             } else {
                 d.deferDragViewCleanupPostAnimation = false;
                 cell.setVisibility(VISIBLE);
@@ -2430,19 +2463,18 @@
             // Create the drag outline
             // We need to add extra padding to the bitmap to make room for the glow effect
             final Canvas canvas = new Canvas();
-            final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
-            mDragOutline = createExternalDragOutline(canvas, bitmapPadding);
+            mDragOutline = createExternalDragOutline(canvas, DRAG_BITMAP_PADDING);
 
             // Show the current page outlines to indicate that we can accept this drop
             showOutlines();
             layout.onDragEnter();
-            layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1, null, null);
+            layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1, 1, 1, null, null);
 
             return true;
         }
         case DragEvent.ACTION_DRAG_LOCATION:
             // Visualize the drop location
-            layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1, null, null);
+            layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1, 1, 1, null, null);
             return true;
         case DragEvent.ACTION_DROP: {
             // Try and add any shortcuts
@@ -2475,7 +2507,7 @@
                         final PendingAddWidgetInfo createInfo =
                                 new PendingAddWidgetInfo(widgetInfo, mimeType, data);
                         mLauncher.addAppWidgetFromDrop(createInfo,
-                            LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage, null, pos);
+                            LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage, null, null, pos);
                     } else {
                         // Show the widget picker dialog if there is more than one widget
                         // that can handle this data type
@@ -2815,10 +2847,16 @@
             mLastDragOverView = dragOverView;
 
             if (!mCreateUserFolderOnDrop && !isOverFolder) {
+                int minSpanX = item.spanX;
+                int minSpanY = item.spanY;
+                if (item.minSpanX > 0 && item.minSpanY > 0) {
+                    minSpanX = item.minSpanX;
+                    minSpanY = item.minSpanY;
+                }
                 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
                         (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
-                        item.spanX, item.spanY, d.dragView.getDragVisualizeOffset(),
-                        d.dragView.getDragRegion());
+                        minSpanX, minSpanY, item.spanX, item.spanY,
+                        d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
             }
         }
     }
@@ -2939,9 +2977,19 @@
                     findNearestVacantCell = false;
                 }
             }
+            final ItemInfo item = (ItemInfo) d.dragInfo;
+            int minSpanX = item.spanX;
+            int minSpanY = item.spanY;
+            if (item.minSpanX > 0 && item.minSpanY > 0) {
+                minSpanX = item.minSpanX;
+                minSpanY = item.minSpanY;
+            }
             if (findNearestVacantCell) {
-                    mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], spanX, spanY, null,
-                        cellLayout, mTargetCell);
+                int[] resultSpan = new int[2];
+                mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], minSpanX, minSpanY,
+                        spanX, spanY, null, cellLayout, mTargetCell, resultSpan);
+                item.spanX = resultSpan[0];
+                item.spanY = resultSpan[1];
             }
 
             Runnable onAnimationCompleteRunnable = new Runnable() {
@@ -2951,8 +2999,11 @@
                     // widgets/shortcuts/folders in a slightly different way
                     switch (pendingInfo.itemType) {
                     case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                        int span[] = new int[2];
+                        span[0] = item.spanX;
+                        span[1] = item.spanY;
                         mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
-                                container, screen, mTargetCell, null);
+                                container, screen, mTargetCell, span, null);
                         break;
                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                         mLauncher.processShortcutFromDrop(pendingInfo.componentName,
@@ -2965,9 +3016,15 @@
                     cellLayout.onDragExit();
                 }
             };
-
-            animateExternalDrop((PendingAddItemInfo) info, cellLayout, d.dragView,
-                    onAnimationCompleteRunnable, ANIMATE_INTO_POSITION);
+            View finalView = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+                    ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
+            int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
+            if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
+                    ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
+                animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
+            }
+            animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
+                    animationStyle, finalView, true);
         } else {
             // This is for other drag/drop cases, like dragging from All Apps
             View view = null;
@@ -3033,11 +3090,10 @@
         }
     }
 
-    public Bitmap createWidgetBitmap(PendingAddWidgetInfo widgetInfo) {
+    public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
         int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX,
                 widgetInfo.spanY, widgetInfo, false);
-        View layout = widgetInfo.boundWidget;
-        mLauncher.getDragLayer().removeView(layout);
+        int visibility = layout.getVisibility();
         layout.setVisibility(VISIBLE);
 
         int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
@@ -3050,56 +3106,90 @@
         layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
         layout.draw(c);
         c.setBitmap(null);
+        layout.setVisibility(visibility);
         return b;
     }
 
-    public void animateExternalDrop(PendingAddItemInfo pendingInfo, CellLayout cellLayout,
-            DragView dragView, Runnable onCompleteRunnable, int animationType) {
+    private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
+            DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell, View finalView,
+            boolean external) {
         // 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);
+        int spanX = info.spanX;
+        int spanY = info.spanY;
 
-        float dragViewScaleX = r.width() / dragView.getMeasuredWidth();
-        float dragViewScaleY = r.height() / dragView.getMeasuredHeight();
+        Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY);
+        loc[0] = r.left;
+        loc[1] = r.top;
+
+        setFinalTransitionTransform(layout);
+        float cellLayoutScale =
+                mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc);
+        resetTransitionTransform(layout);
+        float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
+        float dragViewScaleY = (1.0f * 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;
+        scaleXY[0] = dragViewScaleX * cellLayoutScale;
+        scaleXY[1] = dragViewScaleY * cellLayoutScale;
+    }
+
+    public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView,
+            final Runnable onCompleteRunnable, int animationType, final View finalView,
+            boolean external) {
+        Rect from = new Rect();
+        mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
+
+        int[] finalPos = new int[2];
+        float scaleXY[] = new float[2];
+        getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
+                finalView, external);
 
         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);
+        // In the case where we've prebound the widget, we remove it from the DragLayer
+        if (finalView instanceof AppWidgetHostView && external) {
+            mLauncher.getDragLayer().removeView(finalView);
+        }
+        if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
+            Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
             dragView.setCrossFadeBitmap(crossFadeBitmap);
             dragView.crossFade((int) (duration * 0.8f));
-            animationEnd = DragLayer.ANIMATION_END_DISAPPEAR;
-        } else {
-            scaleX = scaleY = Math.min(scaleX,  scaleY);
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && external) {
+            scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0],  scaleXY[1]);
         }
 
+        DragLayer dragLayer = mLauncher.getDragLayer();
         if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
-            mLauncher.getDragLayer().animateViewIntoPosition(dragView, loc, 0f, 0.1f, 0.1f,
+            mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
                     DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
         } else {
-            mLauncher.getDragLayer().animateViewIntoPosition(dragView, loc, 1f, scaleX, scaleY,
-                animationEnd, onCompleteRunnable, duration);
+            int endStyle;
+            if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
+                endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
+            } else {
+                endStyle = DragLayer.ANIMATION_END_DISAPPEAR;;
+            }
+
+            Runnable onComplete = new Runnable() {
+                @Override
+                public void run() {
+                    if (finalView != null) {
+                        finalView.setVisibility(VISIBLE);
+                    }
+                    if (onCompleteRunnable != null) {
+                        onCompleteRunnable.run();
+                    }
+                }
+            };
+            dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
+                    finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
+                    duration, this);
         }
     }
 
@@ -3159,7 +3249,19 @@
     private int[] findNearestVacantArea(int pixelX, int pixelY,
             int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
         return layout.findNearestVacantArea(
-                pixelX, pixelY, spanX, spanY, ignoreView, recycle);
+                pixelX, pixelY, spanX, spanY, spanX, spanY, ignoreView, recycle, null);
+    }
+
+    /**
+     * Calculate the nearest cell where the given object would be dropped.
+     *
+     * pixelX and pixelY should be in the coordinate system of layout
+     */
+    private int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY,
+            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle,
+            int[] returnSpan) {
+        return layout.findNearestVacantArea(
+                pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, ignoreView, recycle, returnSpan);
     }
 
     /**