Widget resizing can now displace items

Change-Id: I005c9aebf64b2d01debe59f86a208075d19476ea
diff --git a/src/com/android/launcher2/AppWidgetResizeFrame.java b/src/com/android/launcher2/AppWidgetResizeFrame.java
index c01a882..2cc1240 100644
--- a/src/com/android/launcher2/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher2/AppWidgetResizeFrame.java
@@ -49,11 +49,13 @@
     private int mMinVSpan;
     private int mDeltaX;
     private int mDeltaY;
+    private int mDeltaXAddOn;
+    private int mDeltaYAddOn;
 
     private int mBackgroundPadding;
     private int mTouchTargetWidth;
 
-    private int mExpandability[] = new int[4];
+    int[] mDirectionVector = new int[2];
 
     final int SNAP_DURATION = 150;
     final int BACKGROUND_PADDING = 24;
@@ -130,6 +132,11 @@
         final float density = mLauncher.getResources().getDisplayMetrics().density;
         mBackgroundPadding = (int) Math.ceil(density * BACKGROUND_PADDING);
         mTouchTargetWidth = 2 * mBackgroundPadding;
+
+        // When we create the resize frame, we first mark all cells as unoccupied. The appropriate
+        // cells (same if not resized, or different) will be marked as occupied when the resize
+        // frame is dismissed.
+        mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
     }
 
     public boolean beginResizeIfPointInRegion(int x, int y) {
@@ -147,8 +154,6 @@
         mBaselineHeight = getMeasuredHeight();
         mBaselineX = getLeft();
         mBaselineY = getTop();
-        mRunningHInc = 0;
-        mRunningVInc = 0;
 
         if (anyBordersActive) {
             mLeftHandle.setAlpha(mLeftBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
@@ -156,8 +161,6 @@
             mTopHandle.setAlpha(mTopBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
             mBottomHandle.setAlpha(mBottomBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
         }
-        mCellLayout.getExpandabilityArrayForView(mWidgetView, mExpandability);
-
         return anyBordersActive;
     }
 
@@ -183,10 +186,14 @@
         }
     }
 
+    public void visualizeResizeForDelta(int deltaX, int deltaY) {
+        visualizeResizeForDelta(deltaX, deltaY, false);
+    }
+
     /**
      *  Based on the deltas, we resize the frame, and, if needed, we resize the widget.
      */
-    public void visualizeResizeForDelta(int deltaX, int deltaY) {
+    private void visualizeResizeForDelta(int deltaX, int deltaY, boolean onDismiss) {
         updateDeltas(deltaX, deltaY);
         DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
 
@@ -204,25 +211,31 @@
             lp.height = mBaselineHeight + mDeltaY;
         }
 
-        resizeWidgetIfNeeded();
+        resizeWidgetIfNeeded(onDismiss);
         requestLayout();
     }
 
     /**
      *  Based on the current deltas, we determine if and how to resize the widget.
      */
-    private void resizeWidgetIfNeeded() {
+    private void resizeWidgetIfNeeded(boolean onDismiss) {
         int xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
         int yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
 
-        float hSpanIncF = 1.0f * mDeltaX / xThreshold - mRunningHInc;
-        float vSpanIncF = 1.0f * mDeltaY / yThreshold - mRunningVInc;
+        int deltaX = mDeltaX + mDeltaXAddOn;
+        int deltaY = mDeltaY + mDeltaYAddOn;
+
+        float hSpanIncF = 1.0f * deltaX / xThreshold - mRunningHInc;
+        float vSpanIncF = 1.0f * deltaY / yThreshold - mRunningVInc;
 
         int hSpanInc = 0;
         int vSpanInc = 0;
         int cellXInc = 0;
         int cellYInc = 0;
 
+        int countX = mCellLayout.getCountX();
+        int countY = mCellLayout.getCountY();
+
         if (Math.abs(hSpanIncF) > RESIZE_THRESHOLD) {
             hSpanInc = Math.round(hSpanIncF);
         }
@@ -230,58 +243,75 @@
             vSpanInc = Math.round(vSpanIncF);
         }
 
-        if (hSpanInc == 0 && vSpanInc == 0) return;
+        if (!onDismiss && (hSpanInc == 0 && vSpanInc == 0)) return;
 
-        // Before we change the widget, we clear the occupied cells associated with it.
-        // The new set of occupied cells is marked below, once the layout params are updated.
-        mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
 
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
 
+        int spanX = lp.cellHSpan;
+        int spanY = lp.cellVSpan;
+        int cellX = lp.useTmpCoords ? lp.tmpCellX : lp.cellX;
+        int cellY = lp.useTmpCoords ? lp.tmpCellY : lp.cellY;
+
+        int hSpanDelta = 0;
+        int vSpanDelta = 0;
+
         // For each border, we bound the resizing based on the minimum width, and the maximum
         // expandability.
         if (mLeftBorderActive) {
-            cellXInc = Math.max(-mExpandability[LEFT], hSpanInc);
+            cellXInc = Math.max(-cellX, hSpanInc);
             cellXInc = Math.min(lp.cellHSpan - mMinHSpan, cellXInc);
             hSpanInc *= -1;
-            hSpanInc = Math.min(mExpandability[LEFT], hSpanInc);
+            hSpanInc = Math.min(cellX, hSpanInc);
             hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
-            mRunningHInc -= hSpanInc;
+            hSpanDelta = -hSpanInc;
+
         } else if (mRightBorderActive) {
-            hSpanInc = Math.min(mExpandability[RIGHT], hSpanInc);
+            hSpanInc = Math.min(countX - (cellX + spanX), hSpanInc);
             hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
-            mRunningHInc += hSpanInc;
+            hSpanDelta = hSpanInc;
         }
 
         if (mTopBorderActive) {
-            cellYInc = Math.max(-mExpandability[TOP], vSpanInc);
+            cellYInc = Math.max(-cellY, vSpanInc);
             cellYInc = Math.min(lp.cellVSpan - mMinVSpan, cellYInc);
             vSpanInc *= -1;
-            vSpanInc = Math.min(mExpandability[TOP], vSpanInc);
+            vSpanInc = Math.min(cellY, vSpanInc);
             vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
-            mRunningVInc -= vSpanInc;
+            vSpanDelta = -vSpanInc;
         } else if (mBottomBorderActive) {
-            vSpanInc = Math.min(mExpandability[BOTTOM], vSpanInc);
+            vSpanInc = Math.min(countY - (cellY + spanY), vSpanInc);
             vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
-            mRunningVInc += vSpanInc;
+            vSpanDelta = vSpanInc;
         }
 
+        mDirectionVector[0] = 0;
+        mDirectionVector[1] = 0;
         // Update the widget's dimensions and position according to the deltas computed above
         if (mLeftBorderActive || mRightBorderActive) {
-            lp.cellHSpan += hSpanInc;
-            lp.cellX += cellXInc;
+            spanX += hSpanInc;
+            cellX += cellXInc;
+            mDirectionVector[0] = mLeftBorderActive ? -1 : 1;
         }
 
         if (mTopBorderActive || mBottomBorderActive) {
-            lp.cellVSpan += vSpanInc;
-            lp.cellY += cellYInc;
+            spanY += vSpanInc;
+            cellY += cellYInc;
+            mDirectionVector[1] = mTopBorderActive ? -1 : 1;
         }
 
-        // Update the expandability array, as we have changed the widget's size.
-        mCellLayout.getExpandabilityArrayForView(mWidgetView, mExpandability);
+        if (!onDismiss && vSpanDelta == 0 && hSpanDelta == 0) return;
 
-        // Update the cells occupied by this widget
-        mCellLayout.markCellsAsOccupiedForView(mWidgetView);
+        if (mCellLayout.createAreaForResize(cellX, cellY, spanX, spanY, mWidgetView,
+                mDirectionVector, onDismiss)) {
+            lp.tmpCellX = cellX;
+            lp.tmpCellY = cellY;
+            lp.cellHSpan = spanX;
+            lp.cellVSpan = spanY;
+            mRunningVInc += vSpanDelta;
+            mRunningHInc += hSpanDelta;
+        }
+
         mWidgetView.requestLayout();
     }
 
@@ -289,16 +319,22 @@
      * This is the final step of the resize. Here we save the new widget size and position
      * to LauncherModel and animate the resize frame.
      */
-    public void commitResizeForDelta(int deltaX, int deltaY) {
-        visualizeResizeForDelta(deltaX, deltaY);
+    public void commitResize() {
+        resizeWidgetIfNeeded(true);
+        requestLayout();
+    }
 
-        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
-        LauncherModel.resizeItemInDatabase(getContext(), mItemInfo, lp.cellX, lp.cellY,
-                lp.cellHSpan, lp.cellVSpan);
-        mWidgetView.requestLayout();
+    public void onTouchUp() {
+        int xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
+        int yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
 
-        // Once our widget resizes (hence the post), we want to snap the resize frame to it
+        mDeltaXAddOn = mRunningHInc * xThreshold; 
+        mDeltaYAddOn = mRunningVInc * yThreshold; 
+        mDeltaX = 0;
+        mDeltaY = 0;
+
         post(new Runnable() {
+            @Override
             public void run() {
                 snapToWidget(true);
             }
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index da7c2b0..26a3ecf 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -2083,6 +2083,8 @@
             if (info != null) {
                 info.cellX = lp.cellX = lp.tmpCellX;
                 info.cellY = lp.cellY = lp.tmpCellY;
+                info.spanX = lp.cellHSpan;
+                info.spanY = lp.cellVSpan;
             }
         }
         mLauncher.getWorkspace().updateItemLocationsInDatabase(this);
@@ -2214,6 +2216,37 @@
         setItemPlacementDirty(false);
     }
 
+    boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY,
+            View dragView, int[] direction, boolean commit) {
+        int[] pixelXY = new int[2];
+        regionToCenterPoint(cellX, cellY, spanX, spanY, pixelXY);
+
+        // First we determine if things have moved enough to cause a different layout
+        ItemConfiguration swapSolution = simpleSwap(pixelXY[0], pixelXY[1], spanX, spanY,
+                 spanX,  spanY, direction, dragView,  true,  new ItemConfiguration());
+
+        setUseTempCoords(true);
+        if (swapSolution != null && swapSolution.isSolution) {
+            // 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
+            copySolutionToTempState(swapSolution, dragView);
+            setItemPlacementDirty(true);
+            animateItemsToSolution(swapSolution, dragView, commit);
+
+            if (commit) {
+                commitTempPlacement();
+                completeAndClearReorderHintAnimations();
+                setItemPlacementDirty(false);
+            } else {
+                beginOrAdjustHintAnimations(swapSolution, dragView,
+                        REORDER_ANIMATION_DURATION);
+            }
+            mShortcutsAndWidgets.requestLayout();
+        }
+        return swapSolution.isSolution;
+    }
+
     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
@@ -2235,7 +2268,6 @@
                 mPreviousReorderDirection[0] = INVALID_DIRECTION;
                 mPreviousReorderDirection[1] = INVALID_DIRECTION;
             }
-            
         } else {
             getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView, mDirectionVector);
             mPreviousReorderDirection[0] = mDirectionVector[0];
@@ -2694,56 +2726,6 @@
         }
     }
 
-    /**
-     * Given a view, determines how much that view can be expanded in all directions, in terms of
-     * whether or not there are other items occupying adjacent cells. Used by the
-     * AppWidgetResizeFrame to determine how the widget can be resized.
-     */
-    public void getExpandabilityArrayForView(View view, int[] expandability) {
-        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        boolean flag;
-
-        expandability[AppWidgetResizeFrame.LEFT] = 0;
-        for (int x = lp.cellX - 1; x >= 0; x--) {
-            flag = false;
-            for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan; y++) {
-                if (mOccupied[x][y]) flag = true;
-            }
-            if (flag) break;
-            expandability[AppWidgetResizeFrame.LEFT]++;
-        }
-
-        expandability[AppWidgetResizeFrame.TOP] = 0;
-        for (int y = lp.cellY - 1; y >= 0; y--) {
-            flag = false;
-            for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan; x++) {
-                if (mOccupied[x][y]) flag = true;
-            }
-            if (flag) break;
-            expandability[AppWidgetResizeFrame.TOP]++;
-        }
-
-        expandability[AppWidgetResizeFrame.RIGHT] = 0;
-        for (int x = lp.cellX + lp.cellHSpan; x < mCountX; x++) {
-            flag = false;
-            for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan; y++) {
-                if (mOccupied[x][y]) flag = true;
-            }
-            if (flag) break;
-            expandability[AppWidgetResizeFrame.RIGHT]++;
-        }
-
-        expandability[AppWidgetResizeFrame.BOTTOM] = 0;
-        for (int y = lp.cellY + lp.cellVSpan; y < mCountY; y++) {
-            flag = false;
-            for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan; x++) {
-                if (mOccupied[x][y]) flag = true;
-            }
-            if (flag) break;
-            expandability[AppWidgetResizeFrame.BOTTOM]++;
-        }
-    }
-
     public void onMove(View view, int newCellX, int newCellY, int newSpanX, int newSpanY) {
         markCellsAsUnoccupiedForView(view);
         markCellsForView(newCellX, newCellY, newSpanX, newSpanY, mOccupied, true);
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index 9452857..e4e48d0 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -242,7 +242,8 @@
                     break;
                 case MotionEvent.ACTION_CANCEL:
                 case MotionEvent.ACTION_UP:
-                    mCurrentResizeFrame.commitResizeForDelta(x - mXDown, y - mYDown);
+                    mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
+                    mCurrentResizeFrame.onTouchUp();
                     mCurrentResizeFrame = null;
             }
         }
@@ -383,6 +384,7 @@
     public void clearAllResizeFrames() {
         if (mResizeFrames.size() > 0) {
             for (AppWidgetResizeFrame frame: mResizeFrames) {
+                frame.commitResize();
                 removeView(frame);
             }
             mResizeFrames.clear();
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index a9c4970..d29e2e6 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -248,6 +248,7 @@
             sWorker.post(r);
         }
     }
+
     /**
      * Move an item in the DB to a new <container, screen, cellX, cellY>
      */
@@ -276,24 +277,35 @@
     }
 
     /**
-     * Resize an item in the DB to a new <spanX, spanY, cellX, cellY>
+     * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
      */
-    static void resizeItemInDatabase(Context context, final ItemInfo item, final int cellX,
-            final int cellY, final int spanX, final int spanY) {
-        item.spanX = spanX;
-        item.spanY = spanY;
+    static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
+            final int screen, final int cellX, final int cellY, final int spanX, final int spanY) {
+        item.container = container;
         item.cellX = cellX;
         item.cellY = cellY;
+        item.spanX = spanX;
+        item.spanY = spanY;
+
+        // We store hotseat items in canonical form which is this orientation invariant position
+        // in the hotseat
+        if (context instanceof Launcher && screen < 0 &&
+                container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+            item.screen = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
+        } else {
+            item.screen = screen;
+        }
 
         final ContentValues values = new ContentValues();
         values.put(LauncherSettings.Favorites.CONTAINER, item.container);
-        values.put(LauncherSettings.Favorites.SPANX, spanX);
-        values.put(LauncherSettings.Favorites.SPANY, spanY);
-        values.put(LauncherSettings.Favorites.CELLX, cellX);
-        values.put(LauncherSettings.Favorites.CELLY, cellY);
-        updateItemInDatabaseHelper(context, values, item, "resizeItemInDatabase");
-    }
+        values.put(LauncherSettings.Favorites.CELLX, item.cellX);
+        values.put(LauncherSettings.Favorites.CELLY, item.cellY);
+        values.put(LauncherSettings.Favorites.SPANX, item.spanX);
+        values.put(LauncherSettings.Favorites.SPANY, item.spanY);
+        values.put(LauncherSettings.Favorites.SCREEN, item.screen);
 
+        updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
+    }
 
     /**
      * Update an item to the database in a specified container.
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 8c3b465..aa343b0 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -3403,8 +3403,8 @@
             ItemInfo info = (ItemInfo) v.getTag();
             // Null check required as the AllApps button doesn't have an item info
             if (info != null) {
-                LauncherModel.moveItemInDatabase(mLauncher, info, container, screen, info.cellX,
-                        info.cellY);
+                LauncherModel.modifyItemInDatabase(mLauncher, info, container, screen, info.cellX,
+                        info.cellY, info.spanX, info.spanY);
             }
         }
     }