Merge "Fix more cases where blips show up in All Apps"
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index f4cece0..1481540 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -91,7 +91,7 @@
     <string name="workspace_cling_title" msgid="738396473989890567">"Fühlen Sie sich wie zu Hause"</string>
     <string name="workspace_cling_move_item" msgid="791013895761065070">"Hier können Sie Ihre Lieblings-Apps ablegen."</string>
     <string name="workspace_cling_open_all_apps" msgid="2459977609848572588">"Berühren Sie den Kreis für eine Übersicht aller Apps."</string>
-    <string name="all_apps_cling_title" msgid="2559734712581447107">"Einige Apps auswählen"</string>
+    <string name="all_apps_cling_title" msgid="2559734712581447107">"Apps auswählen"</string>
     <string name="all_apps_cling_add_item" msgid="5665035103260318891">"Berühren und halten Sie eine App, um sie zum Startbildschirm hinzuzufügen."</string>
     <string name="folder_cling_title" msgid="4308949882377840953">"Apps mit Ordnern organisieren"</string>
     <string name="folder_cling_move_item" msgid="270598675060435169">"Berühren und halten Sie eine App, um sie zu verschieben."</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 8eeb9d5..f00820d 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -42,9 +42,9 @@
     <string name="group_wallpapers" msgid="1568191644272224858">"Wallpaper"</string>
     <string name="out_of_space" msgid="8365249326091984698">"No more room on this Home screen."</string>
     <string name="invalid_hotseat_item" msgid="6545340627805449250">"This widget is too large for the hot-seat."</string>
-    <string name="shortcut_installed" msgid="7071557296331322355">"<xliff:g id="NAME">%s</xliff:g>Shortcut \"\" created."</string>
-    <string name="shortcut_uninstalled" msgid="2129499669449749995">"<xliff:g id="NAME">%s</xliff:g>Shortcut \"\" was removed."</string>
-    <string name="shortcut_duplicate" msgid="4757756326465060694">"<xliff:g id="NAME">%s</xliff:g>Shortcut \"\" already exists."</string>
+    <string name="shortcut_installed" msgid="7071557296331322355">"Shortcut \"<xliff:g id="NAME">%s</xliff:g>\" created."</string>
+    <string name="shortcut_uninstalled" msgid="2129499669449749995">"Shortcut \"<xliff:g id="NAME">%s</xliff:g>\" was removed."</string>
+    <string name="shortcut_duplicate" msgid="4757756326465060694">"Shortcut \"<xliff:g id="NAME">%s</xliff:g>\" already exists."</string>
     <string name="title_select_shortcut" msgid="1873670208166882222">"Choose shortcut"</string>
     <string name="title_select_application" msgid="1793455815754848652">"Choose app"</string>
     <string name="all_apps_button_label" msgid="2578400570124163469">"Apps"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 51a7019..dd93a57 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -27,12 +27,12 @@
     <string name="pick_wallpaper" msgid="5630222540525626723">"Wallpaper"</string>
     <string name="activity_not_found" msgid="217823393239365967">"Apl tidak dipasang."</string>
     <string name="widgets_tab_label" msgid="9145860100000983599">"Widget"</string>
-    <string name="long_press_widget_to_add" msgid="7395697462851217506">"Sentuh &amp; tahan untuk memilih gawit."</string>
+    <string name="long_press_widget_to_add" msgid="7395697462851217506">"Sentuh &amp; tahan untuk memilih widget."</string>
     <string name="market" msgid="2652226429823445833">"Belanja"</string>
     <string name="external_drop_widget_error" msgid="2285187188524172774">"Tidak dapat melepas item ke layar Utama ini."</string>
-    <string name="external_drop_widget_pick_title" msgid="7040647073452295370">"Pilih gawit untuk membuat"</string>
+    <string name="external_drop_widget_pick_title" msgid="7040647073452295370">"Pilih widget untuk membuat"</string>
     <string name="rename_folder_label" msgid="5646236631298452787">"Nama folder"</string>
-    <string name="rename_folder_title" msgid="4544573104191526550">"Ubah nama folder"</string>
+    <string name="rename_folder_title" msgid="4544573104191526550">"Ganti nama folder"</string>
     <string name="rename_action" msgid="6016003384693240896">"OK"</string>
     <string name="cancel_action" msgid="3811860427489435048">"Batal"</string>
     <string name="menu_item_add_item" msgid="6233177331075781114">"Tambahkan ke Layar Utama"</string>
@@ -41,7 +41,7 @@
     <string name="group_widgets" msgid="6704978494073105844">"Widget"</string>
     <string name="group_wallpapers" msgid="1568191644272224858">"Wallpaper"</string>
     <string name="out_of_space" msgid="8365249326091984698">"Tidak ada ruang lagi pada layar Utama ini."</string>
-    <string name="invalid_hotseat_item" msgid="6545340627805449250">"Gawit ini terlalu besar untuk hotseat tersebut."</string>
+    <string name="invalid_hotseat_item" msgid="6545340627805449250">"Widget ini terlalu besar untuk hotseat tersebut."</string>
     <string name="shortcut_installed" msgid="7071557296331322355">"Pintasan \"<xliff:g id="NAME">%s</xliff:g>\" sudah dibuat."</string>
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"Pintasan \"<xliff:g id="NAME">%s</xliff:g>\" telah dihapus."</string>
     <string name="shortcut_duplicate" msgid="4757756326465060694">"Pintasan \"<xliff:g id="NAME">%s</xliff:g>\" sudah ada."</string>
@@ -87,7 +87,7 @@
     <string name="default_scroll_format" msgid="4057140866420001240">"Laman %1$d dari %2$d"</string>
     <string name="workspace_scroll_format" msgid="7911126267695001437">"Ruang kerja %1$d dari %2$d"</string>
     <string name="apps_customize_apps_scroll_format" msgid="5494241912377704885">"Laman apl %1$d dari %2$d"</string>
-    <string name="apps_customize_widgets_scroll_format" msgid="5383009742241717437">"Laman gawit %1$d dari %2$d"</string>
+    <string name="apps_customize_widgets_scroll_format" msgid="5383009742241717437">"Laman widget %1$d dari %2$d"</string>
     <string name="workspace_cling_title" msgid="738396473989890567">"Anggap rumah sendiri"</string>
     <string name="workspace_cling_move_item" msgid="791013895761065070">"Anda dapat meletakkan apl favorit Anda di sini."</string>
     <string name="workspace_cling_open_all_apps" msgid="2459977609848572588">"Untuk melihat semua apl Anda, sentuh lingkaran."</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 31ef18a..2a75af8 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -39,7 +39,7 @@
     <bool name="config_workspaceFadeAdjacentScreens">false</bool>
 
     <!-- The transition duration for the background of the drop targets -->
-    <integer name="config_dropTargetBgTransitionDuration">100</integer>
+    <integer name="config_dropTargetBgTransitionDuration">0</integer>
 
     <integer name="config_crosshairsFadeInTime">600</integer>
 
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index a44765a..1a9958c 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -791,6 +791,10 @@
         mDraggingWidget = false;
     }
 
+    public boolean supportsFlingToDelete() {
+        return false;
+    }
+
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
diff --git a/src/com/android/launcher2/BubbleTextView.java b/src/com/android/launcher2/BubbleTextView.java
index 4a3a7a4..a775ed5 100644
--- a/src/com/android/launcher2/BubbleTextView.java
+++ b/src/com/android/launcher2/BubbleTextView.java
@@ -247,8 +247,8 @@
     }
 
     void setCellLayoutPressedOrFocusedIcon() {
-        if (getParent() instanceof CellLayoutChildren) {
-            CellLayoutChildren parent = (CellLayoutChildren) getParent();
+        if (getParent() instanceof ShortcutAndWidgetContainer) {
+            ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) getParent();
             if (parent != null) {
                 CellLayout layout = (CellLayout) parent.getParent();
                 layout.setPressedOrFocusedIcon((mPressedOrFocusedBackground != null) ? this : null);
diff --git a/src/com/android/launcher2/ButtonDropTarget.java b/src/com/android/launcher2/ButtonDropTarget.java
index dc34d88..e9f8ce8 100644
--- a/src/com/android/launcher2/ButtonDropTarget.java
+++ b/src/com/android/launcher2/ButtonDropTarget.java
@@ -18,7 +18,7 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Paint;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.widget.TextView;
@@ -71,6 +71,10 @@
     public void onDrop(DragObject d) {
     }
 
+    public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
+        // Do nothing
+    }
+
     public void onDragEnter(DragObject d) {
         d.dragView.setColor(mHoverColor);
     }
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 528984a..5969f98 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -35,6 +35,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.NinePatchDrawable;
 import android.util.AttributeSet;
@@ -84,6 +85,7 @@
     int[] mTempLocation = new int[2];
 
     boolean[][] mOccupied;
+    boolean[][] mTmpOccupied;
     private boolean mLastDownOnOccupiedCell = false;
 
     private OnTouchListener mInterceptTouchListener;
@@ -125,21 +127,33 @@
     private InterruptibleInOutAnimator mCrosshairsAnimator = null;
     private float mCrosshairsVisibility = 0.0f;
 
-    private HashMap<CellLayout.LayoutParams, ObjectAnimator> mReorderAnimators = new
-            HashMap<CellLayout.LayoutParams, ObjectAnimator>();
+    private HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new
+            HashMap<CellLayout.LayoutParams, Animator>();
 
     // When a drag operation is in progress, holds the nearest cell to the touch point
     private final int[] mDragCell = new int[2];
 
     private boolean mDragging = false;
+    private boolean mItemLocationsDirty = false;
 
     private TimeInterpolator mEaseOutInterpolator;
-    private CellLayoutChildren mChildren;
+    private ShortcutAndWidgetContainer mShortcutsAndWidgets;
 
     private boolean mIsHotseat = false;
     private float mChildScale = 1f;
     private float mHotseatChildScale = 1f;
 
+    public static final int MODE_DRAG_OVER = 0;
+    public static final int MODE_ON_DROP = 1;
+    public static final int MODE_ON_DROP_EXTERNAL = 2;
+    public static final int MODE_ACCEPT_DROP = 3;
+    private static final boolean DESTRUCTIVE_REORDER = true;
+    private static final boolean DEBUG_VISUALIZE_OCCUPIED = false;
+
+    private ArrayList<View> mIntersectingViews = new ArrayList<View>();
+    private Rect mOccupiedRect = new Rect();
+    private int[] mDirectionVector = new int[2];
+
     public CellLayout(Context context) {
         this(context, null);
     }
@@ -167,6 +181,7 @@
         mCountX = LauncherModel.getCellCountX();
         mCountY = LauncherModel.getCellCountY();
         mOccupied = new boolean[mCountX][mCountY];
+        mTmpOccupied = new boolean[mCountX][mCountY];
 
         a.recycle();
 
@@ -266,9 +281,9 @@
         mBackgroundRect = new Rect();
         mForegroundRect = new Rect();
 
-        mChildren = new CellLayoutChildren(context);
-        mChildren.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap);
-        addView(mChildren);
+        mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context);
+        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap);
+        addView(mShortcutsAndWidgets);
     }
 
     static int widthInPortrait(Resources r, int numCells) {
@@ -294,13 +309,14 @@
     }
 
     public void enableHardwareLayers() {
-        mChildren.enableHardwareLayers();
+        mShortcutsAndWidgets.enableHardwareLayers();
     }
 
     public void setGridSize(int x, int y) {
         mCountX = x;
         mCountY = y;
         mOccupied = new boolean[mCountX][mCountY];
+        mTmpOccupied = new boolean[mCountX][mCountY];
         requestLayout();
     }
 
@@ -337,13 +353,6 @@
         }
     }
 
-    public CellLayoutChildren getChildrenLayout() {
-        if (getChildCount() > 0) {
-            return (CellLayoutChildren) getChildAt(0);
-        }
-        return null;
-    }
-
     void setIsDragOverlapping(boolean isDragOverlapping) {
         if (mIsDragOverlapping != isDragOverlapping) {
             mIsDragOverlapping = isDragOverlapping;
@@ -450,6 +459,23 @@
             }
         }
 
+        if (DEBUG_VISUALIZE_OCCUPIED) {
+            int[] pt = new int[2];
+            ColorDrawable cd = new ColorDrawable(Color.RED);
+            cd.setBounds(0, 0, 80, 80);
+            for (int i = 0; i < mCountX; i++) {
+                for (int j = 0; j < mCountY; j++) {
+                    if (mOccupied[i][j]) {
+                        cellToPoint(i, j, pt);
+                        canvas.save();
+                        canvas.translate(pt[0], pt[1]);
+                        cd.draw(canvas);
+                        canvas.restore();
+                    }
+                }
+            }
+        }
+
         // The folder outer / inner ring image(s)
         for (int i = 0; i < mFolderOuterRings.size(); i++) {
             FolderRingAnimator fra = mFolderOuterRings.get(i);
@@ -583,7 +609,7 @@
         // (this happens if we're being dropped from all-apps
         if (bubbleChild.getLayoutParams() instanceof LayoutParams &&
                 (bubbleChild.getMeasuredWidth() | bubbleChild.getMeasuredHeight()) == 0) {
-            getChildrenLayout().measureChild(bubbleChild);
+            getShortcutsAndWidgets().measureChild(bubbleChild);
         }
         int measuredWidth = bubbleChild.getMeasuredWidth();
         int measuredHeight = bubbleChild.getMeasuredHeight();
@@ -634,7 +660,7 @@
 
             child.setId(childId);
 
-            mChildren.addView(child, index, lp);
+            mShortcutsAndWidgets.addView(child, index, lp);
 
             if (markCells) markCellsAsOccupiedForView(child);
 
@@ -646,61 +672,53 @@
     @Override
     public void removeAllViews() {
         clearOccupiedCells();
-        mChildren.removeAllViews();
+        mShortcutsAndWidgets.removeAllViews();
     }
 
     @Override
     public void removeAllViewsInLayout() {
-        if (mChildren.getChildCount() > 0) {
+        if (mShortcutsAndWidgets.getChildCount() > 0) {
             clearOccupiedCells();
-            mChildren.removeAllViewsInLayout();
+            mShortcutsAndWidgets.removeAllViewsInLayout();
         }
     }
 
     public void removeViewWithoutMarkingCells(View view) {
-        mChildren.removeView(view);
+        mShortcutsAndWidgets.removeView(view);
     }
 
     @Override
     public void removeView(View view) {
         markCellsAsUnoccupiedForView(view);
-        mChildren.removeView(view);
+        mShortcutsAndWidgets.removeView(view);
     }
 
     @Override
     public void removeViewAt(int index) {
-        markCellsAsUnoccupiedForView(mChildren.getChildAt(index));
-        mChildren.removeViewAt(index);
+        markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(index));
+        mShortcutsAndWidgets.removeViewAt(index);
     }
 
     @Override
     public void removeViewInLayout(View view) {
         markCellsAsUnoccupiedForView(view);
-        mChildren.removeViewInLayout(view);
+        mShortcutsAndWidgets.removeViewInLayout(view);
     }
 
     @Override
     public void removeViews(int start, int count) {
         for (int i = start; i < start + count; i++) {
-            markCellsAsUnoccupiedForView(mChildren.getChildAt(i));
+            markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(i));
         }
-        mChildren.removeViews(start, count);
+        mShortcutsAndWidgets.removeViews(start, count);
     }
 
     @Override
     public void removeViewsInLayout(int start, int count) {
         for (int i = start; i < start + count; i++) {
-            markCellsAsUnoccupiedForView(mChildren.getChildAt(i));
+            markCellsAsUnoccupiedForView(mShortcutsAndWidgets.getChildAt(i));
         }
-        mChildren.removeViewsInLayout(start, count);
-    }
-
-    public void drawChildren(Canvas canvas) {
-        mChildren.draw(canvas);
-    }
-
-    void buildChildrenLayer() {
-        mChildren.buildLayer();
+        mShortcutsAndWidgets.removeViewsInLayout(start, count);
     }
 
     @Override
@@ -714,11 +732,11 @@
         Rect frame = mRect;
         final int x = touchX + mScrollX;
         final int y = touchY + mScrollY;
-        final int count = mChildren.getChildCount();
+        final int count = mShortcutsAndWidgets.getChildCount();
 
         boolean found = false;
         for (int i = count - 1; i >= 0; i--) {
-            final View child = mChildren.getChildAt(i);
+            final View child = mShortcutsAndWidgets.getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
             if ((child.getVisibility() == VISIBLE || child.getAnimation() != null) &&
@@ -847,7 +865,7 @@
     }
 
     /**
-     * Given a cell coordinate, return the point that represents the upper left corner of that cell
+     * Given a cell coordinate, return the point that represents the center of the cell
      *
      * @param cellX X coordinate of the cell
      * @param cellY Y coordinate of the cell
@@ -855,11 +873,32 @@
      * @param result Array of 2 ints to hold the x and y coordinate of the point
      */
     void cellToCenterPoint(int cellX, int cellY, int[] result) {
+        regionToCenterPoint(cellX, cellY, 1, 1, result);
+    }
+
+    /**
+     * Given a cell coordinate and span return the point that represents the center of the regio
+     *
+     * @param cellX X coordinate of the cell
+     * @param cellY Y coordinate of the cell
+     *
+     * @param result Array of 2 ints to hold the x and y coordinate of the point
+     */
+    void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
         final int hStartPadding = getPaddingLeft();
         final int vStartPadding = getPaddingTop();
 
-        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) + mCellWidth / 2;
-        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) + mCellHeight / 2;
+        result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap) +
+                (spanX * mCellWidth + (spanX - 1) * mWidthGap) / 2;
+        result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap) +
+                (spanY * mCellHeight + (spanY - 1) * mHeightGap) / 2;
+    }
+
+    public float getDistanceFromCell(float x, float y, int[] cell) {
+        cellToCenterPoint(cell[0], cell[1], mTmpPoint);
+        float distance = (float) Math.sqrt( Math.pow(x - mTmpPoint[0], 2) +
+                Math.pow(y - mTmpPoint[1], 2));
+        return distance;
     }
 
     int getCellWidth() {
@@ -914,7 +953,7 @@
             int vFreeSpace = vSpace - (mCountY * mOriginalCellHeight);
             mWidthGap = Math.min(mMaxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
             mHeightGap = Math.min(mMaxGap,numHeightGaps > 0 ? (vFreeSpace / numHeightGaps) : 0);
-            mChildren.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap);
+            mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap);
         } else {
             mWidthGap = mOriginalWidthGap;
             mHeightGap = mOriginalHeightGap;
@@ -963,12 +1002,12 @@
 
     @Override
     protected void setChildrenDrawingCacheEnabled(boolean enabled) {
-        mChildren.setChildrenDrawingCacheEnabled(enabled);
+        mShortcutsAndWidgets.setChildrenDrawingCacheEnabled(enabled);
     }
 
     @Override
     protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
-        mChildren.setChildrenDrawnWithCacheEnabled(enabled);
+        mShortcutsAndWidgets.setChildrenDrawnWithCacheEnabled(enabled);
     }
 
     public float getBackgroundAlpha() {
@@ -990,35 +1029,33 @@
         }
     }
 
-    // Need to return true to let the view system know we know how to handle alpha-- this is
-    // because when our children have an alpha of 0.0f, they are still rendering their "dimmed"
-    // versions
-    @Override
-    protected boolean onSetAlpha(int alpha) {
-        return true;
-    }
-
-    @Override
-    public void setAlpha(float alpha) {
-        setChildrenAlpha(alpha);
-        super.setAlpha(alpha);
-    }
-
-    private void setChildrenAlpha(float alpha) {
+    public void setShortcutAndWidgetAlpha(float alpha) {
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             getChildAt(i).setAlpha(alpha);
         }
     }
 
+    public ShortcutAndWidgetContainer getShortcutsAndWidgets() {
+        if (getChildCount() > 0) {
+            return (ShortcutAndWidgetContainer) getChildAt(0);
+        }
+        return null;
+    }
+
     public View getChildAt(int x, int y) {
-        return mChildren.getChildAt(x, y);
+        return mShortcutsAndWidgets.getChildAt(x, y);
     }
 
     public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration,
-            int delay) {
-        CellLayoutChildren clc = getChildrenLayout();
-        if (clc.indexOfChild(child) != -1 && !mOccupied[cellX][cellY]) {
+            int delay, boolean permanent, boolean adjustOccupied) {
+        ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
+        boolean[][] occupied = mOccupied;
+        if (!permanent) {
+            occupied = mTmpOccupied;
+        }
+
+        if (clc.indexOfChild(child) != -1 && !occupied[cellX][cellY]) {
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             final ItemInfo info = (ItemInfo) child.getTag();
 
@@ -1028,41 +1065,57 @@
                 mReorderAnimators.remove(lp);
             }
 
-            int oldX = lp.x;
-            int oldY = lp.y;
-            mOccupied[lp.cellX][lp.cellY] = false;
-            mOccupied[cellX][cellY] = true;
-
+            final int oldX = lp.x;
+            final int oldY = lp.y;
+            if (adjustOccupied) {
+                occupied[lp.cellX][lp.cellY] = false;
+                occupied[cellX][cellY] = true;
+            }
             lp.isLockedToGrid = true;
-            lp.cellX = info.cellX = cellX;
-            lp.cellY = info.cellY = cellY;
+            if (permanent) {
+                lp.cellX = info.cellX = cellX;
+                lp.cellY = info.cellY = cellY;
+            } else {
+                lp.tmpCellX = cellX;
+                lp.tmpCellY = cellY;
+            }
             clc.setupLp(lp);
             lp.isLockedToGrid = false;
-            int newX = lp.x;
-            int newY = lp.y;
+            final int newX = lp.x;
+            final int newY = lp.y;
 
             lp.x = oldX;
             lp.y = oldY;
-            child.requestLayout();
 
-            PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", oldX, newX);
-            PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", oldY, newY);
-            ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, x, y);
-            oa.setDuration(duration);
-            mReorderAnimators.put(lp, oa);
-            oa.addUpdateListener(new AnimatorUpdateListener() {
+            // Exit early if we're not actually moving the view
+            if (oldX == newX && oldY == newY) {
+                lp.isLockedToGrid = true;
+                return true;
+            }
+
+            ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+            va.setDuration(duration);
+            mReorderAnimators.put(lp, va);
+
+            va.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
-                    child.requestLayout();
+                    float r = ((Float) animation.getAnimatedValue()).floatValue();
+                    child.setTranslationX(r * (newX - oldX));
+                    child.setTranslationY(r * (newY - oldY));
                 }
             });
-            oa.addListener(new AnimatorListenerAdapter() {
+            va.addListener(new AnimatorListenerAdapter() {
                 boolean cancelled = false;
                 public void onAnimationEnd(Animator animation) {
                     // If the animation was cancelled, it means that another animation
                     // has interrupted this one, and we don't want to lock the item into
                     // place just yet.
                     if (!cancelled) {
+                        child.setTranslationX(0);
+                        child.setTranslationY(0);
                         lp.isLockedToGrid = true;
+                        child.requestLayout();
                     }
                     if (mReorderAnimators.containsKey(lp)) {
                         mReorderAnimators.remove(lp);
@@ -1072,8 +1125,8 @@
                     cancelled = true;
                 }
             });
-            oa.setStartDelay(delay);
-            oa.start();
+            va.setStartDelay(delay);
+            va.start();
             return true;
         }
         return false;
@@ -1109,17 +1162,11 @@
         result[1] = Math.max(0, result[1]); // Snap to top
     }
 
-    void visualizeDropLocation(View v, Bitmap dragOutline, int originX, int originY,
-            int minSpanX, int minSpanY, int spanX, int spanY, Point dragOffset, Rect dragRegion) {
-
+    void visualizeDropLocation(View v, Bitmap dragOutline, int originX, int originY, int cellX,
+            int cellY, int spanX, int spanY, boolean resize, Point dragOffset, Rect dragRegion) {
         final int oldDragCellX = mDragCell[0];
         final int oldDragCellY = mDragCell[1];
-        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 {
@@ -1133,10 +1180,12 @@
             return;
         }
 
-        if (nearest != null && (nearest[0] != oldDragCellX || nearest[1] != oldDragCellY)) {
+        if (cellX != oldDragCellX || cellY != oldDragCellY) {
+            mDragCell[0] = cellX;
+            mDragCell[1] = cellY;
             // Find the top left corner of the rect the object will occupy
             final int[] topLeft = mTmpPoint;
-            cellToPoint(nearest[0], nearest[1], topLeft);
+            cellToPoint(cellX, cellY, topLeft);
 
             int left = topLeft[0];
             int top = topLeft[1];
@@ -1176,7 +1225,7 @@
             Rect r = mDragOutlines[mDragOutlineCurrent];
             r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
             if (resize) {
-                cellToRect(nearest[0], nearest[1], spanX, spanY, r);
+                cellToRect(cellX, cellY, spanX, spanY, r);
             }
 
             mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
@@ -1251,7 +1300,7 @@
     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);
+                spanX, spanY, ignoreView, ignoreOccupied, result, null, mOccupied);
     }
 
     private final Stack<Rect> mTempRectStack = new Stack<Rect>();
@@ -1262,6 +1311,7 @@
             }
         }
     }
+
     private void recycleTempRects(Stack<Rect> used) {
         while (!used.isEmpty()) {
             mTempRectStack.push(used.pop());
@@ -1285,10 +1335,11 @@
      *         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) {
+            View ignoreView, boolean ignoreOccupied, int[] result, int[] resultSpan,
+            boolean[][] occupied) {
         lazyInitTempRectStack();
         // mark space take by ignoreView as available (method checks if ignoreView is null)
-        markCellsAsUnoccupiedForView(ignoreView);
+        markCellsAsUnoccupiedForView(ignoreView, occupied);
 
         // For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds
         // to the center of the item, but we are searching based on the top-left cell, so
@@ -1304,7 +1355,6 @@
 
         final int countX = mCountX;
         final int countY = mCountY;
-        final boolean[][] occupied = mOccupied;
 
         if (minSpanX <= 0 || minSpanY <= 0 || spanX <= 0 || spanY <= 0 ||
                 spanX < minSpanX || spanY < minSpanY) {
@@ -1382,6 +1432,7 @@
                 validRegions.push(currentRect);
                 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
                         + Math.pow(cellXY[1] - pixelY, 2));
+
                 if ((distance <= bestDistance && !contained) ||
                         currentRect.contains(bestRect)) {
                     bestDistance = distance;
@@ -1396,7 +1447,7 @@
             }
         }
         // re-mark space taken by ignoreView as occupied
-        markCellsAsOccupiedForView(ignoreView);
+        markCellsAsOccupiedForView(ignoreView, occupied);
 
         // Return -1, -1 if no suitable location found
         if (bestDistance == Double.MAX_VALUE) {
@@ -1407,6 +1458,646 @@
         return bestXY;
     }
 
+     /**
+     * Find a vacant area that will fit the given bounds nearest the requested
+     * cell location, and will also weigh in a suggested direction vector of the
+     * desired location. This method computers distance based on unit grid distances,
+     * not pixel distances.
+     *
+     * @param cellX The X cell nearest to which you want to search for a vacant area.
+     * @param cellY The Y cell nearest which you want to search for a vacant area.
+     * @param spanX Horizontal span of the object.
+     * @param spanY Vertical span of the object.
+     * @param direction The favored direction in which the views should move from x, y
+     * @param exactDirectionOnly If this parameter is true, then only solutions where the direction
+     *        matches exactly. Otherwise we find the best matching direction.
+     * @param occoupied The array which represents which cells in the CellLayout are occupied
+     * @param blockOccupied The array which represents which cells in the specified block (cellX,
+     *        cellY, spanX, spanY) are occupied. This is used when try to move a group of views. 
+     * @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.
+     */
+    private int[] findNearestArea(int cellX, int cellY, int spanX, int spanY, int[] direction,
+            boolean[][] occupied, boolean blockOccupied[][], int[] result) {
+        // Keep track of best-scoring drop area
+        final int[] bestXY = result != null ? result : new int[2];
+        float bestDistance = Float.MAX_VALUE;
+        int bestDirectionScore = Integer.MIN_VALUE;
+
+        final int countX = mCountX;
+        final int countY = mCountY;
+
+        for (int y = 0; y < countY - (spanY - 1); y++) {
+            inner:
+            for (int x = 0; x < countX - (spanX - 1); x++) {
+                // First, let's see if this thing fits anywhere
+                for (int i = 0; i < spanX; i++) {
+                    for (int j = 0; j < spanY; j++) {
+                        if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
+                            continue inner;
+                        }
+                    }
+                }
+
+                float distance = (float)
+                        Math.sqrt((x - cellX) * (x - cellX) + (y - cellY) * (y - cellY));
+                int[] curDirection = mTmpPoint;
+                computeDirectionVector(x - cellX, y - cellY, curDirection);
+                // The direction score is just the dot product of the two candidate direction
+                // and that passed in.
+                int curDirectionScore = direction[0] * curDirection[0] +
+                        direction[1] * curDirection[1];
+                boolean exactDirectionOnly = false;
+                boolean directionMatches = direction[0] == curDirection[0] &&
+                        direction[0] == curDirection[0];
+                if ((directionMatches || !exactDirectionOnly) &&
+                        Float.compare(distance,  bestDistance) < 0 || (Float.compare(distance,
+                        bestDistance) == 0 && curDirectionScore > bestDirectionScore)) {
+                    bestDistance = distance;
+                    bestDirectionScore = curDirectionScore;
+                    bestXY[0] = x;
+                    bestXY[1] = y;
+                }
+            }
+        }
+
+        // Return -1, -1 if no suitable location found
+        if (bestDistance == Float.MAX_VALUE) {
+            bestXY[0] = -1;
+            bestXY[1] = -1;
+        }
+        return bestXY;
+    }
+
+    private int[] findNearestAreaInDirection(int cellX, int cellY, int spanX, int spanY, 
+            int[] direction,boolean[][] occupied,
+            boolean blockOccupied[][], int[] result) {
+        // Keep track of best-scoring drop area
+        final int[] bestXY = result != null ? result : new int[2];
+        bestXY[0] = -1;
+        bestXY[1] = -1;
+        float bestDistance = Float.MAX_VALUE;
+
+        // We use this to march in a single direction
+        if (direction[0] != 0 && direction[1] != 0) { 
+            return bestXY;
+        }
+
+        // This will only incrememnet one of x or y based on the assertion above
+        int x = cellX + direction[0];
+        int y = cellY + direction[1];
+        while (x >= 0 && x + spanX <= mCountX && y >= 0 && y + spanY <= mCountY) {
+
+            boolean fail = false;
+            for (int i = 0; i < spanX; i++) {
+                for (int j = 0; j < spanY; j++) {
+                    if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
+                        fail = true;                    
+                    }
+                }
+            }
+            if (!fail) {
+                float distance = (float)
+                        Math.sqrt((x - cellX) * (x - cellX) + (y - cellY) * (y - cellY));
+                if (Float.compare(distance,  bestDistance) < 0) {
+                    bestDistance = distance;
+                    bestXY[0] = x;
+                    bestXY[1] = y;
+                }
+            }
+            x += direction[0];
+            y += direction[1];
+        }
+        return bestXY;
+    }
+
+    private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
+            int[] direction) {
+        LayoutParams lp = (LayoutParams) v.getLayoutParams();
+        boolean success = false;
+        markCellsForView(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan,
+                lp.cellVSpan, mTmpOccupied, false);
+        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
+
+        findNearestArea(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan,
+                direction, mTmpOccupied, null, mTempLocation);
+
+        if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
+            lp.tmpCellX = mTempLocation[0];
+            lp.tmpCellY = mTempLocation[1];
+            success = true;
+
+        }
+        markCellsForView(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan,
+                lp.cellVSpan, mTmpOccupied, true);
+        return success;
+    }
+
+    // This method looks in the specified direction to see if there is an additional view
+    // immediately adjecent in that direction
+    private boolean addViewInDirection(ArrayList<View> views, Rect boundingRect, int[] direction,
+            boolean[][] occupied) {
+        boolean found = false;
+
+        int childCount = mShortcutsAndWidgets.getChildCount();
+        Rect r0 = new Rect(boundingRect);
+        Rect r1 = new Rect();
+
+        int deltaX = 0;
+        int deltaY = 0;
+        if (direction[1] < 0) {
+            r0.set(r0.left, r0.top - 1, r0.right, r0.bottom);
+            deltaY = -1;
+        } else if (direction[1] > 0) {
+            r0.set(r0.left, r0.top, r0.right, r0.bottom + 1);
+            deltaY = 1;
+        } else if (direction[0] < 0) {
+            r0.set(r0.left - 1, r0.top, r0.right, r0.bottom);
+            deltaX = -1;
+        } else if (direction[0] > 0) {
+            r0.set(r0.left, r0.top, r0.right + 1, r0.bottom);
+            deltaX = 1;
+        }
+
+        for (int i = 0; i < childCount; i++) {
+            View child = mShortcutsAndWidgets.getChildAt(i);
+            if (views.contains(child)) continue;
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            r1.set(lp.tmpCellX, lp.tmpCellY, lp.tmpCellX + lp.cellHSpan, lp.tmpCellY + lp.cellVSpan);
+            if (Rect.intersects(r0, r1)) {
+                if (!lp.canReorder) {
+                    return false;
+                }
+                boolean pushed = false;
+                for (int x = lp.tmpCellX; x < lp.tmpCellX + lp.cellHSpan; x++) {
+                    for (int y = lp.tmpCellY; y < lp.tmpCellY + lp.cellVSpan; y++) {
+                        boolean inBounds = x - deltaX >= 0 && x -deltaX < mCountX
+                                && y - deltaY >= 0 && y - deltaY < mCountY;
+                        if (inBounds && occupied[x - deltaX][y - deltaY]) {
+                            pushed = true;
+                        }
+                    }
+                }
+                if (pushed) {
+                    views.add(child);
+                    boundingRect.union(lp.tmpCellX, lp.tmpCellY, lp.tmpCellX + lp.cellHSpan,
+                            lp.tmpCellY + lp.cellVSpan);
+                    found = true;
+                }
+            }
+        }
+        return found;
+    }
+
+    private boolean pushViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
+            int[] direction) {
+        if (views.size() == 0) return true;
+
+
+        boolean success = false;
+
+        // We construct a rect which represents the entire group of views
+        Rect boundingRect = null;
+        for (View v: views) {
+            LayoutParams lp = (LayoutParams) v.getLayoutParams();
+            if (boundingRect == null) {
+                boundingRect = new Rect(lp.tmpCellX, lp.tmpCellY, lp.tmpCellX + lp.cellHSpan,
+                        lp.tmpCellY + lp.cellVSpan);
+            } else {
+                boundingRect.union(lp.tmpCellX, lp.tmpCellY, lp.tmpCellX + lp.cellHSpan,
+                        lp.tmpCellY + lp.cellVSpan);
+            }
+        }
+
+        ArrayList<View> dup = (ArrayList<View>) views.clone();
+        while (addViewInDirection(dup, boundingRect, direction, mTmpOccupied)) {
+        }
+        for (View v: dup) {
+            LayoutParams lp = (LayoutParams) v.getLayoutParams();
+            markCellsForView(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan,
+                    lp.cellVSpan, mTmpOccupied, false); 
+        }
+
+        boolean[][] blockOccupied = new boolean[boundingRect.width()][boundingRect.height()];
+        int top = boundingRect.top;
+        int left = boundingRect.left;
+        for (View v: dup) {
+            LayoutParams lp = (LayoutParams) v.getLayoutParams();
+            markCellsForView(lp.tmpCellX - left, lp.tmpCellY - top, lp.cellHSpan,
+                    lp.cellVSpan, blockOccupied, true); 
+        }
+
+        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
+
+        findNearestAreaInDirection(boundingRect.left, boundingRect.top, boundingRect.width(),
+                boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
+
+        int deltaX = mTempLocation[0] - boundingRect.left;
+        int deltaY = mTempLocation[1] - boundingRect.top;
+        if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
+            for (View v: dup) {
+                LayoutParams lp = (LayoutParams) v.getLayoutParams();
+                lp.tmpCellX += deltaX;
+                lp.tmpCellY += deltaY;
+            }
+            success = true;
+        }
+        for (View v: dup) {
+            LayoutParams lp = (LayoutParams) v.getLayoutParams();
+            markCellsForView(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan,
+                    lp.cellVSpan, mTmpOccupied, true);
+        }
+        return success;
+    }
+
+    private boolean addViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
+            int[] direction) {
+        if (views.size() == 0) return true;
+        boolean success = false;
+
+        // We construct a rect which represents the entire group of views
+        Rect boundingRect = null;
+        for (View v: views) {
+            LayoutParams lp = (LayoutParams) v.getLayoutParams();
+            markCellsForView(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan,
+                    lp.cellVSpan, mTmpOccupied, false);
+            if (boundingRect == null) {
+                boundingRect = new Rect(lp.tmpCellX, lp.tmpCellY, lp.tmpCellX + lp.cellHSpan,
+                        lp.tmpCellY + lp.cellVSpan);
+            } else {
+                boundingRect.union(lp.tmpCellX, lp.tmpCellY, lp.tmpCellX + lp.cellHSpan,
+                        lp.tmpCellY + lp.cellVSpan);
+            }
+        }
+        boolean[][] blockOccupied = new boolean[boundingRect.width()][boundingRect.height()];
+        int top = boundingRect.top;
+        int left = boundingRect.left;
+        for (View v: views) {
+            LayoutParams lp = (LayoutParams) v.getLayoutParams();
+            markCellsForView(lp.tmpCellX - left, lp.tmpCellY - top, lp.cellHSpan,
+                    lp.cellVSpan, blockOccupied, true); 
+        }
+
+        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
+
+        // TODO: this bounding rect may not be completely filled, lets be more precise about this
+        // check.
+        findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
+                boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
+
+        int deltaX = mTempLocation[0] - boundingRect.left;
+        int deltaY = mTempLocation[1] - boundingRect.top;
+        if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
+            for (View v: views) {
+                LayoutParams lp = (LayoutParams) v.getLayoutParams();
+                lp.tmpCellX += deltaX;
+                lp.tmpCellY += deltaY;
+            }
+            success = true;
+        }
+        for (View v: views) {
+            LayoutParams lp = (LayoutParams) v.getLayoutParams();
+            markCellsForView(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan,
+                    lp.cellVSpan, mTmpOccupied, true);
+        }
+        return success;
+    }
+
+    private void markCellsForRect(Rect r, boolean[][] occupied, boolean value) {
+        markCellsForView(r.left, r.top, r.width(), r.height(), occupied, value);
+    }
+
+    private boolean rearrangementExists(int cellX, int cellY, int spanX, int spanY, int[] direction,
+            View ignoreView) {
+        mIntersectingViews.clear();
+
+        mOccupiedRect.set(cellX, cellY, cellX + spanX, cellY + spanY);
+
+        if (ignoreView != null) {
+            LayoutParams lp = (LayoutParams) ignoreView.getLayoutParams();
+            lp.tmpCellX = cellX;
+            lp.tmpCellY = cellY;
+        }
+
+        int childCount = mShortcutsAndWidgets.getChildCount();
+        Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
+        Rect r1 = new Rect();
+        for (int i = 0; i < childCount; i++) {
+            View child = mShortcutsAndWidgets.getChildAt(i);
+            if (child == ignoreView) continue;
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan);
+            if (Rect.intersects(r0, r1)) {
+                if (!lp.canReorder) {
+                    return false;
+                }
+                mIntersectingViews.add(child);
+            }
+        }
+
+        if (pushViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction)) {
+            return true;
+        }
+        // Try the opposite direction
+        direction[0] *= -1;
+        direction[1] *= -1;
+        if (pushViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction)) {
+            return true;
+        }
+        // Switch the direction back
+        direction[0] *= -1;
+        direction[1] *= -1;
+
+        // First we try moving the views as a block
+        if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction)) {
+            return true;
+        }
+
+        // Ok, they couldn't move as a block, let's move them individually
+        for (View v : mIntersectingViews) {
+            if (!addViewToTempLocation(v, mOccupiedRect, direction)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /*
+     * Returns a pair (x, y), where x,y are in {-1, 0, 1} corresponding to vector between
+     * the provided point and the provided cell
+     */
+    private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
+        double angle = Math.atan(((float) deltaY) / deltaX);
+
+        result[0] = 0;
+        result[1] = 0;
+        if (Math.abs(Math.cos(angle)) > 0.5f) {
+            result[0] = (int) Math.signum(deltaX);
+        }
+        if (Math.abs(Math.sin(angle)) > 0.5f) {
+            result[1] = (int) Math.signum(deltaY);
+        }
+    }
+
+    ItemConfiguration simpleSwap(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
+            int spanY, int[] direction, View dragView, boolean decX, ItemConfiguration solution) {
+        // This creates a copy of the current occupied array, omitting the current view being
+        // dragged
+        resetTempLayoutToCurrent(dragView);
+
+        // We find the nearest cell into which we would place the dragged item, assuming there's
+        // nothing in its way.
+        int result[] = new int[2];
+        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
+
+        boolean success = false;
+        // First we try the exact nearest position of the item being dragged,
+        // we will then want to try to move this around to other neighbouring positions
+        success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView);
+
+        if (!success) {
+            // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
+            // x, then 1 in y etc.
+            if (spanX > minSpanX && (minSpanY == spanY || decX)) {
+                return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX - 1, spanY, direction,
+                        dragView, false, solution);
+            } else if (spanY > minSpanY) {
+                return simpleSwap(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY - 1, direction,
+                        dragView, true, solution);
+            }
+            solution.isSolution = false;
+        } else {
+            solution.isSolution = true;
+            solution.dragViewX = result[0];
+            solution.dragViewY = result[1];
+            solution.dragViewSpanX = spanX;
+            solution.dragViewSpanY = spanY;
+            copyCurrentStateToSolution(solution, true);
+        }
+        return solution;
+    }
+
+    private void copyCurrentStateToSolution(ItemConfiguration solution, boolean temp) {
+        int childCount = mShortcutsAndWidgets.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = mShortcutsAndWidgets.getChildAt(i);
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            Point p;
+            if (temp) {
+                p = new Point(lp.tmpCellX, lp.tmpCellY);
+            } else {
+                p = new Point(lp.cellX, lp.cellY);
+            }
+            solution.map.put(child, p);
+        }
+    }
+
+    private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
+        for (int i = 0; i < mCountX; i++) {
+            for (int j = 0; j < mCountY; j++) {
+                mTmpOccupied[i][j] = false;
+            }
+        }
+
+        int childCount = mShortcutsAndWidgets.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = mShortcutsAndWidgets.getChildAt(i);
+            if (child == dragView) continue;
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            Point p = solution.map.get(child);
+            if (p != null) {
+                lp.tmpCellX = p.x;
+                lp.tmpCellY = p.y;
+                markCellsForView(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan,
+                        mTmpOccupied, true);
+            }
+        }
+        markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
+                solution.dragViewSpanY, mTmpOccupied, true);
+    }
+
+    private void animateItemsToSolution(ItemConfiguration solution, View dragView, boolean
+            commitDragView) {
+
+        boolean[][] occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied;
+        for (int i = 0; i < mCountX; i++) {
+            for (int j = 0; j < mCountY; j++) {
+                occupied[i][j] = false;
+            }
+        }
+
+        int childCount = mShortcutsAndWidgets.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = mShortcutsAndWidgets.getChildAt(i);
+            if (child == dragView) continue;
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            Point p = solution.map.get(child);
+            if (p != null) {
+                if (lp.cellX != p.x || lp.cellY != p.y) {
+                    animateChildToPosition(child, p.x, p.y, 150, 0, DESTRUCTIVE_REORDER, false);
+                }
+                markCellsForView(p.x, p.y, lp.cellHSpan, lp.cellVSpan, occupied, true);
+            }
+        }
+        if (commitDragView) {
+            markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
+                    solution.dragViewSpanY, occupied, true);
+        }
+    }
+
+    private void commitTempPlacement() {
+        for (int i = 0; i < mCountX; i++) {
+            for (int j = 0; j < mCountY; j++) {
+                mOccupied[i][j] = mTmpOccupied[i][j];
+            }
+        }
+        int childCount = mShortcutsAndWidgets.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
+            lp.cellX = lp.tmpCellX;
+            lp.cellY = lp.tmpCellY;
+        }
+    }
+
+    public void setUseTempCoords(boolean useTempCoords) {
+        int childCount = mShortcutsAndWidgets.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
+            lp.useTmpCoords = useTempCoords;
+        }
+    }
+
+    private void resetTempLayoutToCurrent(View ignoreView) {
+        for (int i = 0; i < mCountX; i++) {
+            for (int j = 0; j < mCountY; j++) {
+                mTmpOccupied[i][j] = mOccupied[i][j];
+            }
+        }
+        int childCount = mShortcutsAndWidgets.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = mShortcutsAndWidgets.getChildAt(i);
+            if (child == ignoreView) continue;
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            lp.tmpCellX = lp.cellX;
+            lp.tmpCellY = lp.cellY;
+        }
+    }
+
+    ItemConfiguration findConfigurationNoShuffle(int pixelX, int pixelY, int minSpanX, int minSpanY,
+            int spanX, int spanY, View dragView, ItemConfiguration solution) {
+        int[] result = new int[2];
+        int[] resultSpan = new int[2];
+        findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, null, result,
+                resultSpan);
+        if (result[0] >= 0 && result[1] >= 0) {
+            copyCurrentStateToSolution(solution, false);
+            solution.dragViewX = result[0];
+            solution.dragViewY = result[1];
+            solution.dragViewSpanX = resultSpan[0];
+            solution.dragViewSpanY = resultSpan[1];
+            solution.isSolution = true;
+        } else {
+            solution.isSolution = false;
+        }
+        return solution;
+    }
+
+    public void prepareChildForDrag(View child) {
+        markCellsAsUnoccupiedForView(child);
+    }
+
+    int[] createArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
+            View dragView, int[] result, int resultSpan[], int mode) {
+
+        // First we determine if things have moved enough to cause a different layout
+        result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
+
+        if (resultSpan == null) {
+            resultSpan = new int[2];
+        }
+
+        // We attempt the first algorithm
+        regionToCenterPoint(result[0], result[1], spanX, spanY, mTmpPoint);
+        computeDirectionVector((mTmpPoint[0] - pixelX) / spanX, (mTmpPoint[1] - pixelY) / spanY,
+                mDirectionVector);
+        ItemConfiguration swapSolution = simpleSwap(pixelX, pixelY, minSpanX, minSpanY,
+                 spanX,  spanY, mDirectionVector, dragView,  true,  new ItemConfiguration());
+
+        // We attempt the approach which doesn't shuffle views at all
+        ItemConfiguration noShuffleSolution = findConfigurationNoShuffle(pixelX, pixelY, minSpanX,
+                minSpanY, spanX, spanY, dragView, new ItemConfiguration());
+
+        ItemConfiguration finalSolution = null;
+        if (swapSolution.isSolution && swapSolution.area() >= noShuffleSolution.area()) {
+            finalSolution = swapSolution;
+        } else if (noShuffleSolution.isSolution) {
+            finalSolution = noShuffleSolution;
+        }
+
+        boolean foundSolution = true;
+        if (!DESTRUCTIVE_REORDER) {
+            setUseTempCoords(true);
+        }
+
+        if (finalSolution != null) {
+            result[0] = finalSolution.dragViewX;
+            result[1] = finalSolution.dragViewY;
+            resultSpan[0] = finalSolution.dragViewSpanX;
+            resultSpan[1] = finalSolution.dragViewSpanY;
+
+            // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
+            // committing anything or animating anything as we just want to determine if a solution
+            // exists
+            if (mode == MODE_DRAG_OVER || mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
+                if (!DESTRUCTIVE_REORDER) {
+                    copySolutionToTempState(finalSolution, dragView);
+                }
+                setItemPlacementDirty(true);
+                animateItemsToSolution(finalSolution, dragView, mode == MODE_ON_DROP);
+
+                if (!DESTRUCTIVE_REORDER && mode == MODE_ON_DROP) {
+                    commitTempPlacement();
+                }
+            }
+        } else {
+            foundSolution = false;
+            result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
+        }
+
+        if ((mode == MODE_ON_DROP || !foundSolution) && !DESTRUCTIVE_REORDER) {
+            setUseTempCoords(false);
+        }
+        boolean[][] occupied = mOccupied;
+
+        mShortcutsAndWidgets.requestLayout();
+        return result;
+    }
+
+    public boolean isItemPlacementDirty() {
+        return mItemLocationsDirty;
+    }
+
+    public void setItemPlacementDirty(boolean dirty) {
+        mItemLocationsDirty = dirty;
+    }
+
+    private class ItemConfiguration {
+        HashMap<View, Point> map = new HashMap<View, Point>();
+        boolean isSolution = false;
+        int dragViewX, dragViewY, dragViewSpanX, dragViewSpanY;
+
+        int area() {
+            return dragViewSpanX * dragViewSpanY;
+        }
+        void clear() {
+            map.clear();
+            isSolution = false;
+        }
+    }
+
     /**
      * Find a vacant area that will fit the given bounds nearest the requested
      * cell location. Uses Euclidean distance to score multiple vacant areas.
@@ -1442,8 +2133,8 @@
      */
     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);
+        return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, ignoreView, true,
+                result, resultSpan, mOccupied);
     }
 
     /**
@@ -1482,7 +2173,7 @@
      * @return True if a vacant cell of the specified dimension was found, false otherwise.
      */
     boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
-        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null);
+        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null, mOccupied);
     }
 
     /**
@@ -1496,7 +2187,8 @@
      * @return
      */
     boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
-        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, ignoreView);
+        return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1,
+                ignoreView, mOccupied);
     }
 
     /**
@@ -1514,16 +2206,16 @@
     boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
             int intersectX, int intersectY) {
         return findCellForSpanThatIntersectsIgnoring(
-                cellXY, spanX, spanY, intersectX, intersectY, null);
+                cellXY, spanX, spanY, intersectX, intersectY, null, mOccupied);
     }
 
     /**
      * The superset of the above two methods
      */
     boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
-            int intersectX, int intersectY, View ignoreView) {
+            int intersectX, int intersectY, View ignoreView, boolean occupied[][]) {
         // mark space take by ignoreView as available (method checks if ignoreView is null)
-        markCellsAsUnoccupiedForView(ignoreView);
+        markCellsAsUnoccupiedForView(ignoreView, occupied);
 
         boolean foundCell = false;
         while (true) {
@@ -1549,7 +2241,7 @@
                 for (int x = startX; x < endX; x++) {
                     for (int i = 0; i < spanX; i++) {
                         for (int j = 0; j < spanY; j++) {
-                            if (mOccupied[x + i][y + j]) {
+                            if (occupied[x + i][y + j]) {
                                 // small optimization: we can skip to after the column we just found
                                 // an occupied cell
                                 x += i;
@@ -1577,7 +2269,7 @@
         }
 
         // re-mark space taken by ignoreView as occupied
-        markCellsAsOccupiedForView(ignoreView);
+        markCellsAsOccupiedForView(ignoreView, occupied);
         return foundCell;
     }
 
@@ -1820,27 +2512,34 @@
     }
 
     public void onMove(View view, int newCellX, int newCellY, int newSpanX, int newSpanY) {
-        LayoutParams lp = (LayoutParams) view.getLayoutParams();
         markCellsAsUnoccupiedForView(view);
-        markCellsForView(newCellX, newCellY, newSpanX, newSpanY, true);
+        markCellsForView(newCellX, newCellY, newSpanX, newSpanY, mOccupied, true);
     }
 
     public void markCellsAsOccupiedForView(View view) {
-        if (view == null || view.getParent() != mChildren) return;
+        markCellsAsOccupiedForView(view, mOccupied);
+    }
+    public void markCellsAsOccupiedForView(View view, boolean[][] occupied) {
+        if (view == null || view.getParent() != mShortcutsAndWidgets) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
+        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, true);
     }
 
     public void markCellsAsUnoccupiedForView(View view) {
-        if (view == null || view.getParent() != mChildren) return;
+        markCellsAsUnoccupiedForView(view, mOccupied);
+    }
+    public void markCellsAsUnoccupiedForView(View view, boolean occupied[][]) {
+        if (view == null || view.getParent() != mShortcutsAndWidgets) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
+        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, occupied, false);
     }
 
-    private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean value) {
+    private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean[][] occupied,
+            boolean value) {
+        if (cellX < 0 || cellY < 0) return;
         for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
             for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
-                mOccupied[x][y] = value;
+                occupied[x][y] = value;
             }
         }
     }
@@ -1903,6 +2602,21 @@
         public int cellY;
 
         /**
+         * Temporary horizontal location of the item in the grid during reorder
+         */
+        public int tmpCellX;
+
+        /**
+         * Temporary vertical location of the item in the grid during reorder
+         */
+        public int tmpCellY;
+
+        /**
+         * Indicates that the temporary coordinates should be used to layout the items
+         */
+        public boolean useTmpCoords;
+
+        /**
          * Number of cells spanned horizontally by the item.
          */
         @ViewDebug.ExportedProperty
@@ -1920,6 +2634,12 @@
          */
         public boolean isLockedToGrid = true;
 
+        /**
+         * Indicates whether this item can be reordered. Always true except in the case of the
+         * the AllApps button.
+         */
+        public boolean canReorder = true;
+
         // X coordinate of the view in the layout.
         @ViewDebug.ExportedProperty
         int x;
@@ -1961,8 +2681,8 @@
             if (isLockedToGrid) {
                 final int myCellHSpan = cellHSpan;
                 final int myCellVSpan = cellVSpan;
-                final int myCellX = cellX;
-                final int myCellY = cellY;
+                final int myCellX = useTmpCoords ? tmpCellX : cellX;
+                final int myCellY = useTmpCoords ? tmpCellY : cellY;
 
                 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
                         leftMargin - rightMargin;
diff --git a/src/com/android/launcher2/DeleteDropTarget.java b/src/com/android/launcher2/DeleteDropTarget.java
index 4621dea..6f45590 100644
--- a/src/com/android/launcher2/DeleteDropTarget.java
+++ b/src/com/android/launcher2/DeleteDropTarget.java
@@ -16,24 +16,33 @@
 
 package com.android.launcher2;
 
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.TransitionDrawable;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.LinearInterpolator;
 
 import com.android.launcher.R;
 
 public class DeleteDropTarget extends ButtonDropTarget {
+    private static int DELETE_ANIMATION_DURATION = 285;
+    private static int MODE_FLING_DELETE_TO_TRASH = 0;
+    private static int MODE_FLING_DELETE_ALONG_VECTOR = 1;
 
-    private static int DELETE_ANIMATION_DURATION = 300;
+    private final int mFlingDeleteMode = MODE_FLING_DELETE_ALONG_VECTOR;
+
     private ColorStateList mOriginalTextColor;
     private TransitionDrawable mUninstallDrawable;
     private TransitionDrawable mRemoveDrawable;
@@ -223,4 +232,175 @@
     public void onDrop(DragObject d) {
         animateToTrashAndCompleteDrop(d);
     }
+
+    /**
+     * Creates an animation from the current drag view to the delete trash icon.
+     */
+    private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer,
+            DragObject d, PointF vel, ViewConfiguration config) {
+        final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
+                mCurrentDrawable.getIntrinsicWidth(), mCurrentDrawable.getIntrinsicHeight());
+        final Rect from = new Rect();
+        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
+
+        // Calculate how far along the velocity vector we should put the intermediate point on
+        // the bezier curve
+        float velocity = Math.abs(vel.length());
+        float vp = Math.min(1f, velocity / (config.getScaledMaximumFlingVelocity() / 2f));
+        int offsetY = (int) (-from.top * vp);
+        int offsetX = (int) (offsetY / (vel.y / vel.x));
+        final float y2 = from.top + offsetY;                        // intermediate t/l
+        final float x2 = from.left + offsetX;
+        final float x1 = from.left;                                 // drag view t/l
+        final float y1 = from.top;
+        final float x3 = to.left;                                   // delete target t/l
+        final float y3 = to.top;
+
+        final TimeInterpolator scaleAlphaInterpolator = new TimeInterpolator() {
+            @Override
+            public float getInterpolation(float t) {
+                return t * t * t * t * t * t * t * t;
+            }
+        };
+        return new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                final DragView dragView = (DragView) dragLayer.getAnimatedView();
+                float t = ((Float) animation.getAnimatedValue()).floatValue();
+                float tp = scaleAlphaInterpolator.getInterpolation(t);
+                float initialScale = dragView.getInitialScale();
+                float finalAlpha = 0.5f;
+                float scale = dragView.getScaleX();
+                float x1o = ((1f - scale) * dragView.getMeasuredWidth()) / 2f;
+                float y1o = ((1f - scale) * dragView.getMeasuredHeight()) / 2f;
+                float x = (1f - t) * (1f - t) * (x1 - x1o) + 2 * (1f - t) * t * (x2 - x1o) +
+                        (t * t) * x3;
+                float y = (1f - t) * (1f - t) * (y1 - y1o) + 2 * (1f - t) * t * (y2 - x1o) +
+                        (t * t) * y3;
+
+                dragView.setTranslationX(x);
+                dragView.setTranslationY(y);
+                dragView.setScaleX(initialScale * (1f - tp));
+                dragView.setScaleY(initialScale * (1f - tp));
+                dragView.setAlpha(finalAlpha + (1f - finalAlpha) * (1f - tp));
+            }
+        };
+    }
+
+    /**
+     * Creates an animation from the current drag view along its current velocity vector.
+     * For this animation, the alpha runs for a fixed duration and we update the position
+     * progressively.
+     */
+    private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener {
+        private static float FRICTION = 0.93f;
+
+        private DragLayer mDragLayer;
+        private PointF mVelocity;
+        private Rect mFrom;
+        private long mPrevTime;
+        private boolean mHasOffsetForScale;
+
+        private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(1.5f);
+
+        public FlingAlongVectorAnimatorUpdateListener(DragLayer dragLayer, PointF vel, Rect from,
+                long startTime) {
+            mDragLayer = dragLayer;
+            mVelocity = vel;
+            mFrom = from;
+            mPrevTime = startTime;
+        }
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            final DragView dragView = (DragView) mDragLayer.getAnimatedView();
+            float t = ((Float) animation.getAnimatedValue()).floatValue();
+            long curTime = AnimationUtils.currentAnimationTimeMillis();
+
+            if (!mHasOffsetForScale) {
+                mHasOffsetForScale = true;
+                float scale = dragView.getScaleX();
+                float xOffset = ((scale - 1f) * dragView.getMeasuredWidth()) / 2f;
+                float yOffset = ((scale - 1f) * dragView.getMeasuredHeight()) / 2f;
+
+                mFrom.left += xOffset;
+                mFrom.top += yOffset;
+            }
+
+            mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f);
+            mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f);
+
+            dragView.setTranslationX(mFrom.left);
+            dragView.setTranslationY(mFrom.top);
+            dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
+
+            mVelocity.x *= FRICTION;
+            mVelocity.y *= FRICTION;
+            mPrevTime = curTime;
+        }
+    };
+    private AnimatorUpdateListener createFlingAlongVectorAnimatorListener(final DragLayer dragLayer,
+            DragObject d, PointF vel, final long startTime, final int duration,
+            ViewConfiguration config) {
+        final Rect from = new Rect();
+        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
+
+        return new FlingAlongVectorAnimatorUpdateListener(dragLayer, vel, from, startTime);
+    }
+
+    public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) {
+        // Don't highlight the icon as it's animating
+        d.dragView.setColor(0);
+        d.dragView.updateInitialScaleToCurrentScale();
+
+        if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) {
+            // Defer animating out the drop target if we are animating to it
+            mSearchDropTargetBar.deferOnDragEnd();
+            mSearchDropTargetBar.finishAnimations();
+        }
+
+        final ViewConfiguration config = ViewConfiguration.get(mLauncher);
+        final DragLayer dragLayer = mLauncher.getDragLayer();
+        final int duration = DELETE_ANIMATION_DURATION;
+        final long startTime = AnimationUtils.currentAnimationTimeMillis();
+
+        // NOTE: Because it takes time for the first frame of animation to actually be
+        // called and we expect the animation to be a continuation of the fling, we have
+        // to account for the time that has elapsed since the fling finished.  And since
+        // we don't have a startDelay, we will always get call to update when we call
+        // start() (which we want to ignore).
+        final TimeInterpolator tInterpolator = new TimeInterpolator() {
+            private int mCount = -1;
+            private float mOffset = 0f;
+
+            @Override
+            public float getInterpolation(float t) {
+                if (mCount < 0) {
+                    mCount++;
+                } else if (mCount == 0) {
+                    mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
+                            startTime) / duration);
+                    mCount++;
+                }
+                return Math.min(1f, mOffset + t);
+            }
+        };
+        AnimatorUpdateListener updateCb = null;
+        if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) {
+            updateCb = createFlingToTrashAnimatorListener(dragLayer, d, vel, config);
+        } else if (mFlingDeleteMode == MODE_FLING_DELETE_ALONG_VECTOR) {
+            updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime,
+                    duration, config);
+        }
+        Runnable onAnimationEndRunnable = new Runnable() {
+            @Override
+            public void run() {
+                mSearchDropTargetBar.onDragEnd();
+                mLauncher.exitSpringLoadedDragMode();
+                completeDrop(d);
+            }
+        };
+        dragLayer.animateView(d.dragView, updateCb, duration, tInterpolator, onAnimationEndRunnable,
+                DragLayer.ANIMATION_END_DISAPPEAR, null);
+    }
 }
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 019a749..2a88925 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
@@ -26,6 +27,7 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.inputmethod.InputMethodManager;
@@ -60,6 +62,9 @@
     static final int SCROLL_LEFT = 0;
     static final int SCROLL_RIGHT = 1;
 
+    private static final float MAX_FLING_DEGREES = 35f;
+    private static final int FLING_TO_DELETE_THRESHOLD_Y_VELOCITY = -1400;
+
     private Launcher mLauncher;
     private Handler mHandler;
     private final Vibrator mVibrator = new Vibrator();
@@ -86,8 +91,8 @@
 
     /** Who can receive drop events */
     private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();
-
     private ArrayList<DragListener> mListeners = new ArrayList<DragListener>();
+    private DropTarget mFlingToDeleteDropTarget;
 
     /** The window token used as the parent for the DragView. */
     private IBinder mWindowToken;
@@ -111,6 +116,9 @@
     private int mTmpPoint[] = new int[2];
     private Rect mDragLayerRect = new Rect();
 
+    protected int mFlingToDeleteThresholdVelocity;
+    private VelocityTracker mVelocityTracker;
+
     /**
      * Interface to receive notifications when a drag starts or stops
      */
@@ -141,6 +149,10 @@
         mLauncher = launcher;
         mHandler = new Handler();
         mScrollZone = launcher.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
+        mVelocityTracker = VelocityTracker.obtain();
+
+        float density = launcher.getResources().getDisplayMetrics().density;
+        mFlingToDeleteThresholdVelocity = (int) (FLING_TO_DELETE_THRESHOLD_Y_VELOCITY * density);
     }
 
     public boolean dragging() {
@@ -378,15 +390,35 @@
         if (mDragging) {
             mDragging = false;
             clearScrollRunnable();
-            for (DragListener listener : mListeners) {
-                listener.onDragEnd();
-            }
+            boolean isDeferred = false;
             if (mDragObject.dragView != null) {
-                if (!mDragObject.deferDragViewCleanupPostAnimation) {
+                isDeferred = mDragObject.deferDragViewCleanupPostAnimation;
+                if (!isDeferred) {
                     mDragObject.dragView.remove();
                 }
                 mDragObject.dragView = null;
             }
+
+            // Only end the drag if we are not deferred
+            if (!isDeferred) {
+                for (DragListener listener : mListeners) {
+                    listener.onDragEnd();
+                }
+            }
+        }
+
+        releaseVelocityTracker();
+    }
+
+    /**
+     * This only gets called as a result of drag view cleanup being deferred in endDrag();
+     */
+    void onDeferredEndDrag(DragView dragView) {
+        dragView.remove();
+
+        // If we skipped calling onDragEnd() before, do it now
+        for (DragListener listener : mListeners) {
+            listener.onDragEnd();
         }
     }
 
@@ -408,8 +440,11 @@
             Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
                     + mDragging);
         }
-        final int action = ev.getAction();
 
+        // Update the velocity tracker
+        acquireVelocityTrackerAndAddMovement(ev);
+
+        final int action = ev.getAction();
         final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
         final int dragLayerX = dragLayerPos[0];
         final int dragLayerY = dragLayerPos[1];
@@ -425,7 +460,12 @@
                 break;
             case MotionEvent.ACTION_UP:
                 if (mDragging) {
-                    drop(dragLayerX, dragLayerY);
+                    PointF vec = isFlingingToDelete(mDragObject.dragSource);
+                    if (vec != null) {
+                        dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
+                    } else {
+                        drop(dragLayerX, dragLayerY);
+                    }
                 }
                 endDrag();
                 break;
@@ -529,6 +569,9 @@
             return false;
         }
 
+        // Update the velocity tracker
+        acquireVelocityTrackerAndAddMovement(ev);
+
         final int action = ev.getAction();
         final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
         final int dragLayerX = dragLayerPos[0];
@@ -553,10 +596,15 @@
         case MotionEvent.ACTION_UP:
             // Ensure that we've processed a move event at the current pointer location.
             handleMoveEvent(dragLayerX, dragLayerY);
-
             mHandler.removeCallbacks(mScrollRunnable);
+
             if (mDragging) {
-                drop(dragLayerX, dragLayerY);
+                PointF vec = isFlingingToDelete(mDragObject.dragSource);
+                if (vec != null) {
+                    dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
+                } else {
+                    drop(dragLayerX, dragLayerY);
+                }
             }
             endDrag();
             break;
@@ -569,6 +617,58 @@
         return true;
     }
 
+    /**
+     * Determines whether the user flung the current item to delete it.
+     *
+     * @return the vector at which the item was flung, or null if no fling was detected.
+     */
+    private PointF isFlingingToDelete(DragSource source) {
+        if (mFlingToDeleteDropTarget == null) return null;
+        if (!source.supportsFlingToDelete()) return null;
+
+        ViewConfiguration config = ViewConfiguration.get(mLauncher);
+        mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
+
+        if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
+            // Do a quick dot product test to ensure that we are flinging upwards
+            PointF vel = new PointF(mVelocityTracker.getXVelocity(),
+                    mVelocityTracker.getYVelocity());
+            PointF upVec = new PointF(0f, -1f);
+            float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
+                    (vel.length() * upVec.length()));
+            if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
+                return vel;
+            }
+        }
+        return null;
+    }
+
+    private void dropOnFlingToDeleteTarget(float x, float y, PointF vel) {
+        final int[] coordinates = mCoordinatesTemp;
+
+        mDragObject.x = coordinates[0];
+        mDragObject.y = coordinates[1];
+        mDragObject.dragComplete = true;
+
+        // Clean up dragging on the target if it's not the current fling delete target otherwise,
+        // start dragging to it.
+        if (mLastDropTarget != null && mFlingToDeleteDropTarget != mLastDropTarget) {
+            mLastDropTarget.onDragExit(mDragObject);
+        }
+
+        // Drop onto the fling-to-delete target
+        boolean accepted = false;
+        mFlingToDeleteDropTarget.onDragEnter(mDragObject);
+        mFlingToDeleteDropTarget.onDragExit(mDragObject);
+        if (mFlingToDeleteDropTarget.acceptDrop(mDragObject)) {
+            mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, mDragObject.x, mDragObject.y,
+                    vel);
+            accepted = true;
+        }
+        mDragObject.dragSource.onDropCompleted((View) mFlingToDeleteDropTarget, mDragObject,
+                accepted);
+    }
+
     private void drop(float x, float y) {
         final int[] coordinates = mCoordinatesTemp;
         final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
@@ -659,6 +759,27 @@
     }
 
     /**
+     * Sets the current fling-to-delete drop target.
+     */
+    public void setFlingToDeleteDropTarget(DropTarget target) {
+        mFlingToDeleteDropTarget = target;
+    }
+
+    private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+    }
+
+    private void releaseVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    /**
      * Set which view scrolls for touch events near the edge of the screen.
      */
     public void setScrollView(View v) {
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index ce5c8c5..9452857 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -18,15 +18,15 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -34,6 +34,7 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
@@ -433,7 +434,7 @@
 
     public void animateViewIntoPosition(DragView dragView, final View child, int duration,
             final Runnable onFinishAnimationRunnable, View anchorView) {
-        CellLayoutChildren parentChildren = (CellLayoutChildren) child.getParent();
+        ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
         CellLayout parent = (CellLayout) (CellLayout) parentChildren.getParent();
         CellLayout.LayoutParams lp =  (CellLayout.LayoutParams) child.getLayoutParams();
         parentChildren.measureChild(child);
@@ -542,35 +543,17 @@
             duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
         }
 
-        if (mDropAnim != null) {
-            mDropAnim.cancel();
-        }
-
-        if (mFadeOutAnim != null) {
-            mFadeOutAnim.cancel();
-        }
-
-        // Show the drop view if it was previously hidden
-        mDropView = view;
-        mDropView.cancelAnimation();
-        mDropView.resetLayoutParams();
-        mDropAnim = new ValueAnimator();
+        // Fall back to cubic ease out interpolator for the animation if none is specified
+        TimeInterpolator interpolator = null;
         if (alphaInterpolator == null || motionInterpolator == null) {
-            mDropAnim.setInterpolator(mCubicEaseOutInterpolator);
+            interpolator = mCubicEaseOutInterpolator;
         }
 
-        if (anchorView != null) {
-            mAnchorViewInitialScrollX = anchorView.getScrollX();
-        }
-        mAnchorView = anchorView;
-
+        // Animate the view
         final float initAlpha = view.getAlpha();
-        final float dropViewScale = mDropView.getScaleX();
-
-        mDropAnim.setDuration(duration);
-        mDropAnim.setFloatValues(0.0f, 1.0f);
-        mDropAnim.removeAllUpdateListeners();
-        mDropAnim.addUpdateListener(new AnimatorUpdateListener() {
+        final float dropViewScale = view.getScaleX();
+        AnimatorUpdateListener updateCb = new AnimatorUpdateListener() {
+            @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 final float percent = (Float) animation.getAnimatedValue();
                 final int width = view.getMeasuredWidth();
@@ -603,7 +586,35 @@
                 mDropView.setScaleY(scaleY);
                 mDropView.setAlpha(alpha);
             }
-        });
+        };
+        animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle,
+                anchorView);
+    }
+
+    public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration,
+            TimeInterpolator interpolator, final Runnable onCompleteRunnable,
+            final int animationEndStyle, View anchorView) {
+        // Clean up the previous animations
+        if (mDropAnim != null) mDropAnim.cancel();
+        if (mFadeOutAnim != null) mFadeOutAnim.cancel();
+
+        // Show the drop view if it was previously hidden
+        mDropView = view;
+        mDropView.cancelAnimation();
+        mDropView.resetLayoutParams();
+
+        // Set the anchor view if the page is scrolling
+        if (anchorView != null) {
+            mAnchorViewInitialScrollX = anchorView.getScrollX();
+        }
+        mAnchorView = anchorView;
+
+        // Create and start the animation
+        mDropAnim = new ValueAnimator();
+        mDropAnim.setInterpolator(interpolator);
+        mDropAnim.setDuration(duration);
+        mDropAnim.setFloatValues(0f, 1f);
+        mDropAnim.addUpdateListener(updateCb);
         mDropAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
                 if (onCompleteRunnable != null) {
@@ -629,7 +640,7 @@
             mDropAnim.cancel();
         }
         if (mDropView != null) {
-            mDropView.remove();
+            mDragController.onDeferredEndDrag(mDropView);
         }
         mDropView = null;
         invalidate();
@@ -655,7 +666,7 @@
         mFadeOutAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
                 if (mDropView != null) {
-                    mDropView.remove();
+                    mDragController.onDeferredEndDrag(mDropView);
                 }
                 mDropView = null;
                 invalidate();
diff --git a/src/com/android/launcher2/DragSource.java b/src/com/android/launcher2/DragSource.java
index 06f5ee1..a654b93 100644
--- a/src/com/android/launcher2/DragSource.java
+++ b/src/com/android/launcher2/DragSource.java
@@ -25,5 +25,6 @@
  *
  */
 public interface DragSource {
+    boolean supportsFlingToDelete();
     void onDropCompleted(View target, DragObject d, boolean success);
 }
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index 3090e8f..5636f99 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -51,6 +51,7 @@
     ValueAnimator mAnim;
     private float mOffsetX = 0.0f;
     private float mOffsetY = 0.0f;
+    private float mInitialScale = 1f;
 
     /**
      * Construct the drag view.
@@ -67,6 +68,7 @@
             int left, int top, int width, int height, final float initialScale) {
         super(launcher);
         mDragLayer = launcher.getDragLayer();
+        mInitialScale = initialScale;
 
         final Resources res = getResources();
         final float offsetX = res.getDimensionPixelSize(R.dimen.dragViewOffsetX);
@@ -151,6 +153,14 @@
         return mDragRegion;
     }
 
+    public float getInitialScale() {
+        return mInitialScale;
+    }
+
+    public void updateInitialScaleToCurrentScale() {
+        mInitialScale = getScaleX();
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
@@ -233,6 +243,11 @@
      */
     public void show(int touchX, int touchY) {
         mDragLayer.addView(this);
+
+        // Enable hw-layers on this view
+        setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+        // Start the pick-up animation
         DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0);
         lp.width = mBitmap.getWidth();
         lp.height = mBitmap.getHeight();
@@ -267,6 +282,9 @@
 
     void remove() {
         if (getParent() != null) {
+            // Disable hw-layers on this view
+            setLayerType(View.LAYER_TYPE_NONE, null);
+
             mDragLayer.removeView(DragView.this);
         }
     }
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index e49f782..fb714c6 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher2;
 
+import android.graphics.PointF;
 import android.graphics.Rect;
 
 /**
@@ -92,6 +93,13 @@
     void onDragExit(DragObject dragObject);
 
     /**
+     * Handle an object being dropped as a result of flinging to delete and will be called in place
+     * of onDrop().  (This is only called on objects that are set as the DragController's
+     * fling-to-delete target.
+     */
+    void onFlingToDelete(DragObject dragObject, int x, int y, PointF vec);
+
+    /**
      * Allows a DropTarget to delegate drag and drop events to another object.
      *
      * Most subclasses will should just return null from this method.
diff --git a/src/com/android/launcher2/FocusHelper.java b/src/com/android/launcher2/FocusHelper.java
index 7807b5d..ecc9d9c 100644
--- a/src/com/android/launcher2/FocusHelper.java
+++ b/src/com/android/launcher2/FocusHelper.java
@@ -534,7 +534,7 @@
                 if (handleKeyEvent) {
                     // Select the first bubble text view in the current page of the workspace
                     final CellLayout layout = (CellLayout) workspace.getChildAt(pageIndex);
-                    final CellLayoutChildren children = layout.getChildrenLayout();
+                    final ShortcutAndWidgetContainer children = layout.getShortcutsAndWidgets();
                     final View newIcon = getIconInDirection(layout, children, -1, 1);
                     if (newIcon != null) {
                         newIcon.requestFocus();
@@ -556,9 +556,10 @@
     /**
      * Private helper method to get the CellLayoutChildren given a CellLayout index.
      */
-    private static CellLayoutChildren getCellLayoutChildrenForIndex(ViewGroup container, int i) {
+    private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex(
+            ViewGroup container, int i) {
         ViewGroup parent = (ViewGroup) container.getChildAt(i);
-        return (CellLayoutChildren) parent.getChildAt(0);
+        return (ShortcutAndWidgetContainer) parent.getChildAt(0);
     }
 
     /**
@@ -664,7 +665,7 @@
      * Handles key events in a Workspace containing.
      */
     static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) {
-        CellLayoutChildren parent = (CellLayoutChildren) v.getParent();
+        ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
         final CellLayout layout = (CellLayout) parent.getParent();
         final Workspace workspace = (Workspace) layout.getParent();
         final ViewGroup launcher = (ViewGroup) workspace.getParent();
@@ -819,7 +820,7 @@
      * Handles key events for items in a Folder.
      */
     static boolean handleFolderKeyEvent(View v, int keyCode, KeyEvent e) {
-        CellLayoutChildren parent = (CellLayoutChildren) v.getParent();
+        ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
         final CellLayout layout = (CellLayout) parent.getParent();
         final Folder folder = (Folder) layout.getParent();
         View title = folder.mFolderName;
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 08d3315..6856a09 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -22,6 +22,7 @@
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.text.InputType;
@@ -35,7 +36,6 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.inputmethod.EditorInfo;
@@ -148,7 +148,7 @@
         super.onFinishInflate();
         mContent = (CellLayout) findViewById(R.id.folder_content);
         mContent.setGridSize(0, 0);
-        mContent.getChildrenLayout().setMotionEventSplittingEnabled(false);
+        mContent.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
         mFolderName = (FolderEditText) findViewById(R.id.folder_name);
         mFolderName.setFolder(this);
         mFolderName.setOnFocusChangeListener(this);
@@ -528,7 +528,7 @@
                 for (int x = startX; x <= endX; x++) {
                     View v = mContent.getChildAt(x,y);
                     if (mContent.animateChildToPosition(v, empty[0], empty[1],
-                            REORDER_ANIMATION_DURATION, delay)) {
+                            REORDER_ANIMATION_DURATION, delay, true, true)) {
                         empty[0] = x;
                         empty[1] = y;
                         delay += delayAmount;
@@ -545,7 +545,7 @@
                 for (int x = startX; x >= endX; x--) {
                     View v = mContent.getChildAt(x,y);
                     if (mContent.animateChildToPosition(v, empty[0], empty[1],
-                            REORDER_ANIMATION_DURATION, delay)) {
+                            REORDER_ANIMATION_DURATION, delay, true, true)) {
                         empty[0] = x;
                         empty[1] = y;
                         delay += delayAmount;
@@ -653,6 +653,10 @@
         updateItemLocationsInDatabase();
     }
 
+    public boolean supportsFlingToDelete() {
+        return true;
+    }
+
     private void updateItemLocationsInDatabase() {
         ArrayList<View> list = getItemsInReadingOrder();
         for (int i = 0; i < list.size(); i++) {
@@ -731,7 +735,7 @@
         mLauncher.getWorkspace().setFinalScrollForPageChange(currentPage);
         // We first fetch the currently visible CellLayoutChildren
         CellLayout currentLayout = (CellLayout) mLauncher.getWorkspace().getChildAt(currentPage);
-        CellLayoutChildren boundingLayout = currentLayout.getChildrenLayout();
+        ShortcutAndWidgetContainer boundingLayout = currentLayout.getShortcutsAndWidgets();
         Rect bounds = new Rect();
         parent.getDescendantRectRelativeToSelf(boundingLayout, bounds);
         // We reset the workspaces scroll
@@ -822,11 +826,11 @@
     }
 
     public int getItemCount() {
-        return mContent.getChildrenLayout().getChildCount();
+        return mContent.getShortcutsAndWidgets().getChildCount();
     }
 
     public View getItemAt(int index) {
-        return mContent.getChildrenLayout().getChildAt(index);
+        return mContent.getShortcutsAndWidgets().getChildAt(index);
     }
 
     private void onCloseComplete() {
@@ -924,6 +928,10 @@
         mInfo.add(item);
     }
 
+    public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
+        // Do nothing
+    }
+
     public void onAdd(ShortcutInfo item) {
         mItemsInvalidated = true;
         // If the item was dropped onto this open folder, we have done the work associated
diff --git a/src/com/android/launcher2/Hotseat.java b/src/com/android/launcher2/Hotseat.java
index add62c0..9e525bd 100644
--- a/src/com/android/launcher2/Hotseat.java
+++ b/src/com/android/launcher2/Hotseat.java
@@ -130,7 +130,8 @@
         // the hotseat in order regardless of which orientation they were added
         int x = getCellXFromOrder(mAllAppsButtonRank);
         int y = getCellYFromOrder(mAllAppsButtonRank);
-        mContent.addViewToCellLayout(allAppsButton, -1, 0, new CellLayout.LayoutParams(x,y,1,1),
-                true, true);
+        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x,y,1,1);
+        lp.canReorder = false;
+        mContent.addViewToCellLayout(allAppsButton, -1, 0, lp, true, true);
     }
 }
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index b7d4c8b..0c1b76f 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -912,13 +912,14 @@
             foundCellSpan = true;
 
             // If appropriate, either create a folder or add to an existing folder
-            if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY,
+            if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
                     true, null,null)) {
                 return;
             }
             DragObject dragObject = new DragObject();
             dragObject.dragInfo = info;
-            if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, dragObject, true)) {
+            if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
+                    true)) {
                 return;
             }
         } else if (touchXY != null) {
diff --git a/src/com/android/launcher2/SearchDropTargetBar.java b/src/com/android/launcher2/SearchDropTargetBar.java
index 03ca38f..76d7076 100644
--- a/src/com/android/launcher2/SearchDropTargetBar.java
+++ b/src/com/android/launcher2/SearchDropTargetBar.java
@@ -69,6 +69,7 @@
         dragController.addDragListener(mDeleteDropTarget);
         dragController.addDropTarget(mInfoDropTarget);
         dragController.addDropTarget(mDeleteDropTarget);
+        dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
         mInfoDropTarget.setLauncher(launcher);
         mDeleteDropTarget.setLauncher(launcher);
     }
@@ -153,6 +154,13 @@
         });
     }
 
+    public void finishAnimations() {
+        mDropTargetBarFadeInAnim.end();
+        mDropTargetBarFadeOutAnim.end();
+        mQSBSearchBarFadeInAnim.end();
+        mQSBSearchBarFadeOutAnim.end();
+    }
+
     private void cancelAnimations() {
         mDropTargetBarFadeInAnim.cancel();
         mDropTargetBarFadeOutAnim.cancel();
diff --git a/src/com/android/launcher2/CellLayoutChildren.java b/src/com/android/launcher2/ShortcutAndWidgetContainer.java
similarity index 97%
rename from src/com/android/launcher2/CellLayoutChildren.java
rename to src/com/android/launcher2/ShortcutAndWidgetContainer.java
index 4ca44ed..7e5e940 100644
--- a/src/com/android/launcher2/CellLayoutChildren.java
+++ b/src/com/android/launcher2/ShortcutAndWidgetContainer.java
@@ -26,7 +26,7 @@
 
 import com.android.launcher2.CellLayout.LayoutParams;
 
-public class CellLayoutChildren extends ViewGroup {
+public class ShortcutAndWidgetContainer extends ViewGroup {
     static final String TAG = "CellLayoutChildren";
 
     // These are temporary variables to prevent having to allocate a new object just to
@@ -41,7 +41,7 @@
     private int mWidthGap;
     private int mHeightGap;
 
-    public CellLayoutChildren(Context context) {
+    public ShortcutAndWidgetContainer(Context context) {
         super(context);
         mWallpaperManager = WallpaperManager.getInstance(context);
     }
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index cfe0df4..8f11612 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -17,18 +17,15 @@
 package com.android.launcher2;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 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;
 import android.content.ClipDescription;
 import android.content.ComponentName;
 import android.content.Context;
@@ -41,8 +38,8 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.os.IBinder;
@@ -55,18 +52,16 @@
 import android.view.DragEvent;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
-import android.view.View.MeasureSpec;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.launcher.R;
 import com.android.launcher2.FolderIcon.FolderRingAnimator;
 import com.android.launcher2.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
+import com.android.launcher2.LauncherSettings.Favorites;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -96,6 +91,8 @@
     private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
     private static final int FLING_THRESHOLD_VELOCITY = 500;
 
+    private float mMaxDistanceForFolderCreation = 50.0f;
+
     // These animators are used to fade the children's outlines
     private ObjectAnimator mChildrenOutlineFadeInAnimation;
     private ObjectAnimator mChildrenOutlineFadeOutAnimation;
@@ -192,8 +189,10 @@
     private int mWallpaperTravelWidth;
 
     // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
-    private static final int FOLDER_CREATION_TIMEOUT = 250;
+    private static final int FOLDER_CREATION_TIMEOUT = 0;
+    private static final int REORDER_TIMEOUT = 250;
     private final Alarm mFolderCreationAlarm = new Alarm();
+    private final Alarm mReorderAlarm = new Alarm();
     private FolderRingAnimator mDragFolderRingAnimator = null;
     private View mLastDragOverView = null;
     private boolean mCreateUserFolderOnDrop = false;
@@ -212,6 +211,15 @@
     public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
     public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
 
+    // Related to dragging, folder creation and reordering
+    private static final int DRAG_MODE_NONE = 0;
+    private static final int DRAG_MODE_CREATE_FOLDER = 1;
+    private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
+    private static final int DRAG_MODE_REORDER = 3;
+    private int mDragMode = DRAG_MODE_NONE;
+    private int mLastReorderX = -1;
+    private int mLastReorderY = -1;
+
     // Relating to workspace drag fade out
     private float mDragFadeOutAlpha;
     private int mDragFadeOutDuration;
@@ -356,7 +364,7 @@
             final int childCount = getChildCount();
             for (int i = 0; i < childCount; i++) {
                 CellLayout cl = (CellLayout) getChildAt(i);
-                cl.buildChildrenLayer();
+                cl.getShortcutsAndWidgets().buildLayer();
             }
         }
     }
@@ -370,7 +378,7 @@
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             CellLayout cl = (CellLayout) getPageAt(i);
-            cl.getChildrenLayout().animate().alpha(mDragFadeOutAlpha)
+            cl.getShortcutsAndWidgets().animate().alpha(mDragFadeOutAlpha)
                 .setInterpolator(new AccelerateInterpolator(1.5f))
                 .setDuration(mDragFadeOutDuration)
                 .start();
@@ -386,7 +394,7 @@
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             CellLayout cl = (CellLayout) getPageAt(i);
-            cl.getChildrenLayout().animate().alpha(1f)
+            cl.getShortcutsAndWidgets().animate().alpha(1f)
                 .setInterpolator(new DecelerateInterpolator(1.5f))
                 .setDuration(mDragFadeOutDuration)
                 .start();
@@ -405,8 +413,8 @@
         setWillNotDraw(false);
         setChildrenDrawnWithCacheEnabled(true);
 
+        final Resources res = getResources();
         try {
-            final Resources res = getResources();
             mBackground = res.getDrawable(R.drawable.apps_customize_bg);
         } catch (Resources.NotFoundException e) {
             // In this case, we will skip drawing background protection
@@ -419,8 +427,8 @@
         mWallpaperTravelWidth = (int) (mDisplayWidth *
                 wallpaperTravelToScreenWidthRatio(mDisplayWidth, mDisplayHeight));
 
+        mMaxDistanceForFolderCreation = (0.5f * res.getDimension(R.dimen.app_icon_size));
         mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
-
     }
 
     @Override
@@ -715,7 +723,7 @@
         // user scrolls while we are transitioning (should not affect dispatchDraw optimizations)
         if (!mFadeInAdjacentScreens) {
             for (int i = 0; i < getChildCount(); ++i) {
-                getPageAt(i).setAlpha(1f);
+                ((CellLayout) getPageAt(i)).setShortcutAndWidgetAlpha(1f);
             }
         }
 
@@ -1198,7 +1206,7 @@
                 cl.setRotationY(rotation);
                 if (mFadeInAdjacentScreens && !isSmall()) {
                     float alpha = 1 - Math.abs(scrollProgress);
-                    cl.setAlpha(alpha);
+                    cl.setShortcutAndWidgetAlpha(alpha);
                 }
             }
         }
@@ -1650,7 +1658,7 @@
                 cl.setScaleY(finalScaleFactor);
                 cl.setBackgroundAlpha(finalBackgroundAlpha);
                 cl.setBackgroundAlphaMultiplier(finalAlphaMultiplierValue);
-                cl.setAlpha(finalAlpha);
+                cl.setShortcutAndWidgetAlpha(finalAlpha);
                 cl.setRotationY(rotation);
             }
         }
@@ -1666,7 +1674,7 @@
                     cl.setScaleY(mNewScaleYs[i]);
                     cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
                     cl.setBackgroundAlphaMultiplier(mNewBackgroundAlphaMultipliers[i]);
-                    cl.setAlpha(mNewAlphas[i]);
+                    cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
                     cl.setRotationY(mNewRotationYs[i]);
                 } else {
                     LauncherViewPropertyAnimator a = new LauncherViewPropertyAnimator(cl);
@@ -1676,10 +1684,16 @@
                         .scaleY(mNewScaleYs[i])
                         .setDuration(duration)
                         .setInterpolator(mZoomInInterpolator);
-                    if (mOldAlphas[i] != mNewAlphas[i]) {
-                        a.alpha(mNewAlphas[i]);
-                    }
                     anim.play(a);
+
+                    LauncherViewPropertyAnimator alphaAnim =
+                        new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
+                    if (mOldAlphas[i] != mNewAlphas[i]) {
+                        alphaAnim.alpha(mNewAlphas[i])
+                            .setDuration(duration)
+                            .setInterpolator(mZoomInInterpolator);
+                        anim.play(alphaAnim);
+                    }
                     if (mOldRotationYs[i] != 0 || mNewRotationYs[i] != 0) {
                         ValueAnimator rotate = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
                         rotate.setInterpolator(new DecelerateInterpolator(2.0f));
@@ -1748,7 +1762,7 @@
         if (!mFadeInAdjacentScreens) {
             for (int i = 0; i < getChildCount(); i++) {
                 final CellLayout cl = (CellLayout) getChildAt(i);
-                cl.setAlpha(1f);
+                cl.setShortcutAndWidgetAlpha(1f);
             }
         }
     }
@@ -1884,6 +1898,8 @@
 
         mDragInfo = cellInfo;
         child.setVisibility(INVISIBLE);
+        CellLayout layout = (CellLayout) child.getParent().getParent();
+        layout.prepareChildForDrag(child);
 
         child.clearFocus();
         child.setPressed(false);
@@ -2002,19 +2018,29 @@
                 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
                 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
             }
+
             mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
                     (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
                     mTargetCell);
-            if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell, true)) {
+            float distance = mDragTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
+                    mDragViewVisualCenter[1], mTargetCell);
+            if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout,
+                    mTargetCell, distance, true)) {
                 return true;
             }
             if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout,
-                    mTargetCell)) {
+                    mTargetCell, distance)) {
                 return true;
             }
 
+            int[] resultSpan = new int[2];
+            mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+                    (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
+                    null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
+            boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
+
             // Don't accept the drop if there's no room for the item
-            if (!mDragTargetLayout.findCellForSpanIgnoring(null, minSpanX, minSpanY, ignoreView)) {
+            if (!foundCell) {
                 // Don't show the message if we are dropping on the AllApps button and the hotseat
                 // is full
                 if (mTargetCell != null && mLauncher.isHotseatLayout(mDragTargetLayout)) {
@@ -2032,15 +2058,15 @@
         return true;
     }
 
-    boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell,
-            boolean considerTimeout) {
+    boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float
+            distance, boolean considerTimeout) {
+        if (distance > mMaxDistanceForFolderCreation) return false;
+
         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
 
         boolean hasntMoved = false;
         if (mDragInfo != null) {
-            CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
-            hasntMoved = (mDragInfo.cellX == targetCell[0] &&
-                    mDragInfo.cellY == targetCell[1]) && (cellParent == target);
+            hasntMoved = dropOverView == mDragInfo.cell;
         }
 
         if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
@@ -2055,7 +2081,10 @@
         return (aboveShortcut && willBecomeShortcut);
     }
 
-    boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell) {
+    boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
+            float distance) {
+        if (distance > mMaxDistanceForFolderCreation) return false;
+
         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
         if (dropOverView instanceof FolderIcon) {
             FolderIcon fi = (FolderIcon) dropOverView;
@@ -2067,7 +2096,9 @@
     }
 
     boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
-            int[] targetCell, boolean external, DragView dragView, Runnable postAnimationRunnable) {
+            int[] targetCell, float distance, boolean external, DragView dragView,
+            Runnable postAnimationRunnable) {
+        if (distance > mMaxDistanceForFolderCreation) return false;
         View v = target.getChildAt(targetCell[0], targetCell[1]);
         boolean hasntMoved = false;
         if (mDragInfo != null) {
@@ -2117,7 +2148,9 @@
     }
 
     boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
-            DragObject d, boolean external) {
+            float distance, DragObject d, boolean external) {
+        if (distance > mMaxDistanceForFolderCreation) return false;
+
         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
         if (dropOverView instanceof FolderIcon) {
             FolderIcon fi = (FolderIcon) dropOverView;
@@ -2172,16 +2205,21 @@
                 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
                 // First we find the cell nearest to point at which the item is
                 // dropped, without any consideration to whether there is an item there.
+
                 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
                         mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
+                float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
+                        mDragViewVisualCenter[1], mTargetCell);
+
                 // If the item being dropped is a shortcut and the nearest drop
                 // cell also contains a shortcut, then create a folder with the two shortcuts.
                 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
-                        dropTargetLayout, mTargetCell, false, d.dragView, null)) {
+                        dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
                     return;
                 }
 
-                if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, d, false)) {
+                if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
+                        distance, d, false)) {
                     return;
                 }
 
@@ -2194,10 +2232,12 @@
                     minSpanX = item.minSpanX;
                     minSpanY = item.minSpanY;
                 }
+
                 int[] resultSpan = new int[2];
-                mTargetCell = findNearestVacantArea((int) mDragViewVisualCenter[0],
-                        (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragInfo.spanX,
-                        mDragInfo.spanY, cell, dropTargetLayout, mTargetCell, resultSpan);
+                mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+                        (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
+                        mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
+
                 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
                 if (foundCell && (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
                     resizeOnDrop = true;
@@ -2221,8 +2261,6 @@
 
                     // update the item's position after drop
                     CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
-                    dropTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1],
-                            item.spanX, item.spanY);
                     lp.cellX = mTargetCell[0];
                     lp.cellY = mTargetCell[1];
                     lp.cellHSpan = item.spanX;
@@ -2302,6 +2340,10 @@
         }
     }
 
+    public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
+        // Do nothing
+    }
+
     public void setFinalScrollForPageChange(int screen) {
         if (screen >= 0) {
             mSavedScrollX = getScrollX();
@@ -2358,6 +2400,13 @@
         // Clean up folders
         cleanupFolderCreation(d);
 
+        // Clean up reorder
+        if (mReorderAlarm != null) {
+            mReorderAlarm.cancelAlarm();
+            mLastReorderX = -1;
+            mLastReorderY = -1;
+        }
+
         // Reset the scroll area and previous drag target
         onResetScrollArea();
 
@@ -2366,6 +2415,7 @@
             mDragTargetLayout.onDragExit();
         }
         mLastDragOverView = null;
+        mDragMode = DRAG_MODE_NONE;
         mSpringLoadedDragController.cancel();
 
         if (!mIsPageMoving) {
@@ -2617,6 +2667,7 @@
         mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
             d.dragView, mDragViewVisualCenter);
 
+        final View child = (mDragInfo == null) ? null : mDragInfo.cell;
         // Identify whether we have dragged over a side page
         if (isSmall()) {
             if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
@@ -2642,6 +2693,7 @@
                     mDragTargetLayout.onDragEnter();
                 } else {
                     mLastDragOverView = null;
+                    mDragMode = DRAG_MODE_NONE;
                 }
 
                 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
@@ -2677,8 +2729,6 @@
 
         // Handle the drag over
         if (mDragTargetLayout != null) {
-            final View child = (mDragInfo == null) ? null : mDragInfo.cell;
-
             // We want the point to be mapped to the dragTarget.
             if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
                 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
@@ -2689,46 +2739,89 @@
 
             mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
                     (int) mDragViewVisualCenter[1], 1, 1, mDragTargetLayout, mTargetCell);
+            float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
+                    mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
+
             final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
                     mTargetCell[1]);
 
-            boolean userFolderPending = willCreateUserFolder(info, mDragTargetLayout,
-                    mTargetCell, false);
-            boolean isOverFolder = dragOverView instanceof FolderIcon;
-            if (dragOverView != mLastDragOverView) {
+            final View lastDragOverView = mLastDragOverView;
+            if (mLastDragOverView != dragOverView) {
+                mDragMode = DRAG_MODE_NONE;
+                mLastDragOverView = dragOverView;
+                if (mReorderAlarm != null) {
+                    mReorderAlarm.cancelAlarm();
+                }
+            }
+
+            boolean folder = willCreateOrAddToFolder(info, mDragTargetLayout, mTargetCell,
+                    targetCellDistance, dragOverView, lastDragOverView);
+
+            int minSpanX = item.spanX;
+            int minSpanY = item.spanY;
+            if (item.minSpanX > 0 && item.minSpanY > 0) {
+                minSpanX = item.minSpanX;
+                minSpanY = item.minSpanY;
+            }
+
+            if (!folder && !mReorderAlarm.alarmPending() && (mLastReorderX != mTargetCell[0] ||
+                    mLastReorderY != mTargetCell[1])) {
                 cancelFolderCreation();
-                if (mLastDragOverView != null && mLastDragOverView instanceof FolderIcon) {
-                    ((FolderIcon) mLastDragOverView).onDragExit(d.dragInfo);
+                ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
+                        minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
+                mReorderAlarm.setOnAlarmListener(listener);
+                mReorderAlarm.setAlarm(REORDER_TIMEOUT);
+            } else if (folder) {
+                if (mReorderAlarm != null) {
+                    mReorderAlarm.cancelAlarm();
                 }
             }
-
-            if (userFolderPending && dragOverView != mLastDragOverView) {
-                mFolderCreationAlarm.setOnAlarmListener(new
-                        FolderCreationAlarmListener(mDragTargetLayout, mTargetCell[0], mTargetCell[1]));
-                mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
-            }
-
-            if (dragOverView != mLastDragOverView && isOverFolder) {
-                ((FolderIcon) dragOverView).onDragEnter(d.dragInfo);
-                if (mDragTargetLayout != null) {
-                    mDragTargetLayout.clearDragOutlines();
-                }
-            }
-            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;
-                }
+            // TODO: need to determine what we're going to about visualizing drop locations
+            /*
+            boolean resize = resultSpan[0] != info.spanX || resultSpan[1] != info.spanY;
+            if (!folder) {
                 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
                         (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
-                        minSpanX, minSpanY, item.spanX, item.spanY,
+                        mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
                         d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
+
+            }
+            */
+        }
+    }
+
+    private boolean willCreateOrAddToFolder(ItemInfo info, CellLayout targetLayout,
+            int[] targetCell, float distance, View dragOverView, View lastDragOverView) {
+        boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
+                false);
+
+        if (userFolderPending && mDragMode == DRAG_MODE_NONE) {
+            mFolderCreationAlarm.setOnAlarmListener(new
+                    FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]));
+            mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
+        }
+
+        boolean willAddToFolder =
+                willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
+
+        if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
+            FolderIcon fi = ((FolderIcon) dragOverView);
+            mDragMode = DRAG_MODE_ADD_TO_FOLDER;
+            fi.onDragEnter(info);
+            if (targetLayout != null) {
+                targetLayout.clearDragOutlines();
             }
         }
+
+        if (dragOverView != lastDragOverView || (mCreateUserFolderOnDrop && !userFolderPending)
+                || (!willAddToFolder && mDragMode == DRAG_MODE_ADD_TO_FOLDER)) {
+            cancelFolderCreation();
+            if (lastDragOverView != null && lastDragOverView instanceof FolderIcon) {
+                ((FolderIcon) lastDragOverView).onDragExit(info);
+            }
+        }
+
+        return willAddToFolder || userFolderPending;
     }
 
     private void cleanupFolderCreation(DragObject d) {
@@ -2772,6 +2865,48 @@
             layout.showFolderAccept(mDragFolderRingAnimator);
             layout.clearDragOutlines();
             mCreateUserFolderOnDrop = true;
+            mDragMode = DRAG_MODE_CREATE_FOLDER;
+        }
+    }
+
+    class ReorderAlarmListener implements OnAlarmListener {
+        float[] dragViewCenter;
+        int minSpanX, minSpanY, spanX, spanY;
+        DragView dragView;
+        View child;
+
+        public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
+                int spanY, DragView dragView, View child) {
+            this.dragViewCenter = dragViewCenter;
+            this.minSpanX = minSpanX;
+            this.minSpanY = minSpanY;
+            this.spanX = spanX;
+            this.spanY = spanY;
+            this.child = child;
+            this.dragView = dragView;
+        }
+
+        public void onAlarm(Alarm alarm) {
+            int[] resultSpan = new int[2];
+            mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+                (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
+                child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
+
+            mLastReorderX = mTargetCell[0];
+            mLastReorderY = mTargetCell[1];
+
+            if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
+            }
+            mDragMode = DRAG_MODE_REORDER;
+
+            // TODO: need to determine what we're going to about visualizing drop locations
+            /*
+            boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
+            mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
+                (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
+                mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
+                dragView.getDragVisualizeOffset(), dragView.getDragRegion());
+            */
         }
     }
 
@@ -2841,23 +2976,27 @@
             if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
                 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
                         cellLayout, mTargetCell);
+                float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
+                        mDragViewVisualCenter[1], mTargetCell);
                 if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell,
-                        true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
-                                mDragTargetLayout, mTargetCell)) {
+                        distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
+                                mDragTargetLayout, mTargetCell, distance)) {
                     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) {
+                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(touchXY[0], touchXY[1], minSpanX, minSpanY,
-                        spanX, spanY, null, cellLayout, mTargetCell, resultSpan);
+                mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+                        (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
+                        null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
                 item.spanX = resultSpan[0];
                 item.spanY = resultSpan[1];
             }
@@ -2922,20 +3061,24 @@
             if (touchXY != null) {
                 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
                         cellLayout, mTargetCell);
+                float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
+                        mDragViewVisualCenter[1], mTargetCell);
                 d.postAnimationRunnable = exitSpringLoadedRunnable;
-                if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, true,
-                        d.dragView, d.postAnimationRunnable)) {
+                if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
+                        true, d.dragView, d.postAnimationRunnable)) {
                     return;
                 }
-                if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, d, true)) {
+                if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
+                        true)) {
                     return;
                 }
             }
 
             if (touchXY != null) {
                 // when dragging and dropping, just find the closest free spot
-                mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, null,
-                        cellLayout, mTargetCell);
+                mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+                        (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
+                        null, mTargetCell, null, CellLayout.MODE_ON_DROP);
             } else {
                 cellLayout.findCellForSpan(mTargetCell, 1, 1);
             }
@@ -2943,7 +3086,7 @@
                     info.spanY, insertAtFirst);
             cellLayout.onDropChild(view);
             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
-            cellLayout.getChildrenLayout().measureChild(view);
+            cellLayout.getShortcutsAndWidgets().measureChild(view);
 
 
             LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen,
@@ -3117,29 +3260,6 @@
      *
      * pixelX and pixelY should be in the coordinate system of layout
      */
-    private int[] findNearestVacantArea(int pixelX, int pixelY,
-            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
-        return layout.findNearestVacantArea(
-                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);
-    }
-
-    /**
-     * Calculate the nearest cell where the given object would be dropped.
-     *
-     * pixelX and pixelY should be in the coordinate system of layout
-     */
     private int[] findNearestArea(int pixelX, int pixelY,
             int spanX, int spanY, CellLayout layout, int[] recycle) {
         return layout.findNearestArea(
@@ -3188,10 +3308,38 @@
         mDragOutline = null;
         mDragInfo = null;
 
+        saveWorkspaceStateToDb();
         // Hide the scrolling indicator after you pick up an item
         hideScrollingIndicator(false);
     }
 
+    public void saveWorkspaceStateToDb() {
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            CellLayout cl = (CellLayout) getChildAt(i);
+            if (cl.isItemPlacementDirty()) {
+                updateItemLocationsInDatabase(cl);
+                cl.setItemPlacementDirty(false);
+            }
+        }
+    }
+
+    private void updateItemLocationsInDatabase(CellLayout cl) {
+        int count = cl.getShortcutsAndWidgets().getChildCount();
+        int screen = indexOfChild(cl);
+        for (int i = 0; i < count; i++) {
+            View v = cl.getShortcutsAndWidgets().getChildAt(i);
+            ItemInfo info = (ItemInfo) v.getTag();
+
+            LauncherModel.moveItemInDatabase(mLauncher, info, Favorites.CONTAINER_DESKTOP, screen,
+                        info.cellX, info.cellY);
+        }
+    }
+
+    public boolean supportsFlingToDelete() {
+        return true;
+    }
+
     public boolean isDropEnabled() {
         return true;
     }
@@ -3301,7 +3449,7 @@
     CellLayout getParentCellLayoutForView(View v) {
         ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
         for (CellLayout layout : layouts) {
-            if (layout.getChildrenLayout().indexOfChild(v) > -1) {
+            if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
                 return layout;
             }
         }
@@ -3325,23 +3473,26 @@
 
     /**
      * We should only use this to search for specific children.  Do not use this method to modify
-     * CellLayoutChildren directly.
+     * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
+     * the hotseat and workspace pages
      */
-    ArrayList<CellLayoutChildren> getWorkspaceAndHotseatCellLayoutChildren() {
-        ArrayList<CellLayoutChildren> childrenLayouts = new ArrayList<CellLayoutChildren>();
+    ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
+        ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
+                new ArrayList<ShortcutAndWidgetContainer>();
         int screenCount = getChildCount();
         for (int screen = 0; screen < screenCount; screen++) {
-            childrenLayouts.add(((CellLayout) getChildAt(screen)).getChildrenLayout());
+            childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
         }
         if (mLauncher.getHotseat() != null) {
-            childrenLayouts.add(mLauncher.getHotseat().getLayout().getChildrenLayout());
+            childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
         }
         return childrenLayouts;
     }
 
     public Folder getFolderForTag(Object tag) {
-        ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
-        for (CellLayoutChildren layout: childrenLayouts) {
+        ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
+                getAllShortcutAndWidgetContainers();
+        for (ShortcutAndWidgetContainer layout: childrenLayouts) {
             int count = layout.getChildCount();
             for (int i = 0; i < count; i++) {
                 View child = layout.getChildAt(i);
@@ -3357,8 +3508,9 @@
     }
 
     public View getViewForTag(Object tag) {
-        ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
-        for (CellLayoutChildren layout: childrenLayouts) {
+        ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
+                getAllShortcutAndWidgetContainers();
+        for (ShortcutAndWidgetContainer layout: childrenLayouts) {
             int count = layout.getChildCount();
             for (int i = 0; i < count; i++) {
                 View child = layout.getChildAt(i);
@@ -3371,8 +3523,9 @@
     }
 
     void clearDropTargets() {
-        ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
-        for (CellLayoutChildren layout: childrenLayouts) {
+        ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
+                getAllShortcutAndWidgetContainers();
+        for (ShortcutAndWidgetContainer layout: childrenLayouts) {
             int childCount = layout.getChildCount();
             for (int j = 0; j < childCount; j++) {
                 View v = layout.getChildAt(j);
@@ -3394,7 +3547,7 @@
 
         ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
         for (final CellLayout layoutParent: cellLayouts) {
-            final ViewGroup layout = layoutParent.getChildrenLayout();
+            final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
 
             // Avoid ANRs by treating each screen separately
             post(new Runnable() {
@@ -3480,8 +3633,8 @@
     }
 
     void updateShortcuts(ArrayList<ApplicationInfo> apps) {
-        ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
-        for (CellLayoutChildren layout: childrenLayouts) {
+        ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
+        for (ShortcutAndWidgetContainer layout: childrenLayouts) {
             int childCount = layout.getChildCount();
             for (int j = 0; j < childCount; j++) {
                 final View view = layout.getChildAt(j);