Implementing resizable widgets in launcher

Change-Id: I18598493dcf34fc6089fa330a4b66803f658b773
diff --git a/src/com/android/launcher2/AppWidgetResizeFrame.java b/src/com/android/launcher2/AppWidgetResizeFrame.java
new file mode 100644
index 0000000..6ddb5b8
--- /dev/null
+++ b/src/com/android/launcher2/AppWidgetResizeFrame.java
@@ -0,0 +1,298 @@
+package com.android.launcher2;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.view.Gravity;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher.R;
+
+public class AppWidgetResizeFrame extends FrameLayout {
+
+    private ItemInfo mItemInfo;
+    private LauncherAppWidgetHostView mWidgetView;
+    private CellLayout mCellLayout;
+    private ImageView mLeftHandle;
+    private ImageView mRightHandle;
+    private ImageView mTopHandle; 
+    private ImageView mBottomHandle;
+
+    private boolean mLeftBorderActive;
+    private boolean mRightBorderActive;
+    private boolean mTopBorderActive;
+    private boolean mBottomBorderActive;
+
+    private int mBaselineWidth;
+    private int mBaselineHeight;
+    private int mBaselineX;
+    private int mBaselineY;
+    private int mResizeMode;
+    
+    private int mRunningHInc;
+    private int mRunningVInc;
+    private int mMinHSpan;
+    private int mMinVSpan;
+    private int mDeltaX;
+    private int mDeltaY;
+
+    private int mExpandability[] = new int[4];
+
+    final int BORDER_WIDTH = 50;
+    final int FRAME_MARGIN = 15;
+    final int SNAP_DURATION = 150;
+
+    public AppWidgetResizeFrame(Context context, ItemInfo itemInfo, 
+            LauncherAppWidgetHostView widgetView, CellLayout cellLayout) {
+
+        super(context);
+        mContext = context;
+        mItemInfo = itemInfo;
+        mCellLayout = cellLayout;
+        mWidgetView = widgetView;
+        mResizeMode = widgetView.getAppWidgetInfo().resizableMode;
+        
+        final AppWidgetProviderInfo info = widgetView.getAppWidgetInfo();
+        int[] result = mCellLayout.rectToCell(info.minWidth, info.minHeight, null);
+        mMinHSpan = result[0];
+        mMinVSpan = result[1];
+
+        setBackgroundResource(R.drawable.resize_frame);
+        setPadding(0, 0, 0, 0);
+
+        LayoutParams lp;
+        mLeftHandle = new ImageView(context);
+        mLeftHandle.setImageResource(R.drawable.h_handle);
+        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 
+                Gravity.LEFT | Gravity.CENTER_VERTICAL);
+        addView(mLeftHandle, lp);
+
+        mRightHandle = new ImageView(context);
+        mRightHandle.setImageResource(R.drawable.h_handle);
+        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 
+                Gravity.RIGHT | Gravity.CENTER_VERTICAL);
+        addView(mRightHandle, lp);
+
+        mTopHandle = new ImageView(context);
+        mTopHandle.setImageResource(R.drawable.v_handle);
+        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 
+                Gravity.CENTER_HORIZONTAL | Gravity.TOP);
+        addView(mTopHandle, lp);
+
+        mBottomHandle = new ImageView(context);
+        mBottomHandle.setImageResource(R.drawable.v_handle);
+        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 
+                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+        addView(mBottomHandle, lp);
+
+        if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
+            mTopHandle.setVisibility(GONE);
+            mBottomHandle.setVisibility(GONE);
+        } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
+            mLeftHandle.setVisibility(GONE);
+            mRightHandle.setVisibility(GONE);
+        }        
+    }
+
+    public boolean beginResizeIfPointInRegion(int x, int y) {
+        boolean horizontalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0;
+        boolean verticalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0;
+        mLeftBorderActive = (x < BORDER_WIDTH) && horizontalActive;
+        mRightBorderActive = (x > getWidth() - BORDER_WIDTH) && horizontalActive;
+        mTopBorderActive = (y < BORDER_WIDTH) && verticalActive;
+        mBottomBorderActive = (y > getHeight() - BORDER_WIDTH) && verticalActive;
+
+        boolean anyBordersActive = mLeftBorderActive || mRightBorderActive
+                || mTopBorderActive || mBottomBorderActive;
+
+        mBaselineWidth = getMeasuredWidth();
+        mBaselineHeight = getMeasuredHeight();
+        mBaselineX = getLeft();
+        mBaselineY = getTop();
+        mRunningHInc = 0;
+        mRunningVInc = 0;
+
+        if (anyBordersActive) {
+            mLeftHandle.setAlpha(mLeftBorderActive ? 1.0f : 0.5f);
+            mRightHandle.setAlpha(mRightBorderActive ? 1.0f : 0.5f);
+            mTopHandle.setAlpha(mTopBorderActive ? 1.0f : 0.5f);
+            mBottomHandle.setAlpha(mBottomBorderActive ? 1.0f : 0.5f);
+        }
+        mCellLayout.getExpandabilityArrayForView(mWidgetView, mExpandability);
+        return anyBordersActive;
+    }
+
+    public void updateDeltas(int deltaX, int deltaY) {
+        if (mLeftBorderActive) {
+            mDeltaX = Math.max(-mBaselineX, deltaX); 
+            mDeltaX = Math.min(mBaselineWidth - 2*BORDER_WIDTH, mDeltaX);
+        } else if (mRightBorderActive) {
+            mDeltaX = Math.min(mCellLayout.getWidth() - (mBaselineX + mBaselineWidth), deltaX);
+            mDeltaX = Math.max(-mBaselineWidth + 2*BORDER_WIDTH, mDeltaX);
+        }
+
+        if (mTopBorderActive) {
+            mDeltaY = Math.max(-mBaselineY, deltaY);
+            mDeltaY = Math.min(mBaselineHeight - 2*BORDER_WIDTH, mDeltaY);
+        } else if (mBottomBorderActive) {
+            mDeltaY = Math.min(mCellLayout.getHeight() - (mBaselineY + mBaselineHeight), deltaY);
+            mDeltaY = Math.max(-mBaselineHeight + 2*BORDER_WIDTH, mDeltaY);
+        }
+    }
+
+    public void visualizeResizeForDelta(int deltaX, int deltaY) {
+        updateDeltas(deltaX, deltaY);
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+        if (mLeftBorderActive) {
+            lp.x = mBaselineX + mDeltaX;
+            lp.width = mBaselineWidth - mDeltaX;
+        } else if (mRightBorderActive) {
+            lp.width = mBaselineWidth + mDeltaX;
+        }
+
+        if (mTopBorderActive) {
+            lp.y = mBaselineY + mDeltaY;
+            lp.height = mBaselineHeight - mDeltaY;
+        } else if (mBottomBorderActive) {
+            lp.height = mBaselineHeight + mDeltaY;
+        }
+
+        resizeWidgetIfNeeded();
+        requestLayout();
+    }
+
+    private void resizeWidgetIfNeeded() {
+        // TODO: these computations probably aren't quite right... think about them
+
+        //System.out.println("runningIncX before: " + mRunningHInc);
+        //System.out.println("runningIncY before: " + mRunningVInc);
+        
+        int xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
+        int yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
+
+        int hSpanInc = (int) Math.round(1.0f * mDeltaX / xThreshold) - mRunningHInc;
+        int vSpanInc = (int) Math.round(1.0f * mDeltaY / yThreshold) - mRunningVInc;
+        int cellXInc = 0;
+        int cellYInc = 0;
+
+        if (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();
+        if (mLeftBorderActive) {
+            cellXInc = Math.max(-mExpandability[0], hSpanInc);
+            cellXInc = Math.min(lp.cellHSpan - mMinHSpan, cellXInc);
+            hSpanInc *= -1;
+            hSpanInc = Math.min(mExpandability[0], hSpanInc);
+            hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
+            mRunningHInc -= hSpanInc;
+        } else if (mRightBorderActive) {
+            hSpanInc = Math.min(mExpandability[2], hSpanInc);
+            hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
+            mRunningHInc += hSpanInc;
+        }
+
+        if (mTopBorderActive) {
+            cellYInc = Math.max(-mExpandability[1], vSpanInc);
+            cellYInc = Math.min(lp.cellVSpan - mMinVSpan, cellYInc);
+            vSpanInc *= -1;
+            vSpanInc = Math.min(mExpandability[1], vSpanInc);
+            vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
+            mRunningVInc -= vSpanInc;
+        } else if (mBottomBorderActive) {
+            vSpanInc = Math.min(mExpandability[3], vSpanInc);
+            vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
+            mRunningVInc += vSpanInc;
+        }
+
+        // Update the widget's dimensions and position according to the deltas computed above
+        if (mLeftBorderActive || mRightBorderActive) {
+            lp.cellHSpan += hSpanInc;
+            lp.cellX += cellXInc;
+        }
+
+        if (mTopBorderActive || mBottomBorderActive) {
+            lp.cellVSpan += vSpanInc;
+            lp.cellY += cellYInc;
+        }
+
+        try {
+            mCellLayout.getExpandabilityArrayForView(mWidgetView, mExpandability);
+        } catch (Exception e) {
+            System.out.println("Problem!");
+        }
+
+        // Update the cells occupied by this widget
+        mCellLayout.markCellsAsOccupiedForView(mWidgetView);
+    }
+
+    public void commitResizeForDelta(int deltaX, int deltaY) {
+        visualizeResizeForDelta(deltaX, deltaY);
+
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
+        LauncherModel.resizeItemInDatabase(getContext(), mItemInfo, lp.cellX, lp.cellY,
+                lp.cellHSpan, lp.cellVSpan);
+        mWidgetView.requestLayout();
+
+        // Once our widget resizes (hence the post), we want to snap the resize frame to it
+        post(new Runnable() {
+            public void run() {
+                snapToWidget(true);
+            }
+        });
+    }
+
+    public void snapToWidget(boolean animate) {
+        final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+
+        final int newWidth = mWidgetView.getWidth() + 2 * FRAME_MARGIN;
+        final int newHeight = mWidgetView.getHeight() + 2 * FRAME_MARGIN;
+        final int newX = mWidgetView.getLeft() - FRAME_MARGIN;
+        final int newY = mWidgetView.getTop() - FRAME_MARGIN;
+        if (!animate) {
+            lp.width = newWidth;
+            lp.height = newHeight;
+            lp.x = newX;
+            lp.y = newY;
+            mLeftHandle.setAlpha(1.0f);
+            mRightHandle.setAlpha(1.0f);
+            mTopHandle.setAlpha(1.0f);
+            mBottomHandle.setAlpha(1.0f);
+            requestLayout();
+        } else {
+            PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", lp.width, newWidth);
+            PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", lp.height, newHeight);
+            PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", lp.x, newX);
+            PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
+            ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y);
+            ObjectAnimator leftOa = ObjectAnimator.ofFloat(mLeftHandle, "alpha", 1.0f);
+            ObjectAnimator rightOa = ObjectAnimator.ofFloat(mRightHandle, "alpha", 1.0f);
+            ObjectAnimator topOa = ObjectAnimator.ofFloat(mTopHandle, "alpha", 1.0f);
+            ObjectAnimator bottomOa = ObjectAnimator.ofFloat(mBottomHandle, "alpha", 1.0f);
+            oa.addUpdateListener(new AnimatorUpdateListener() {
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    requestLayout();
+                }
+            });
+            AnimatorSet set = new AnimatorSet();
+            if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
+                set.playTogether(oa, topOa, bottomOa);
+            } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
+                set.playTogether(oa, leftOa, rightOa);
+            } else {
+                set.playTogether(oa, leftOa, rightOa, topOa, bottomOa);
+            }
+
+            set.setDuration(SNAP_DURATION);
+            set.start();
+        }
+    }
+}
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 6691e64..a2a539e 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -587,11 +587,11 @@
         boolean found = false;
         for (int i = count - 1; i >= 0; i--) {
             final View child = mChildren.getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
-            if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
+            if ((child.getVisibility() == VISIBLE || child.getAnimation() != null) && lp.isLockedToGrid) {
                 child.getHitRect(frame);
                 if (frame.contains(x, y)) {
-                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                     cellInfo.cell = child;
                     cellInfo.cellX = lp.cellX;
                     cellInfo.cellY = lp.cellY;
@@ -703,6 +703,14 @@
         return mCellHeight;
     }
 
+    int getWidthGap() {
+        return mWidthGap;
+    }
+
+    int getHeightGap() {
+        return mHeightGap;
+    }
+
     int getLeftPadding() {
         return mLeftPadding;
     }
@@ -1332,19 +1340,68 @@
         }
     }
 
+    public void getExpandabilityArrayForView(View view, int[] expandability) {
+        final LayoutParams lp = (LayoutParams) view.getLayoutParams();        
+        boolean flag;
+
+        // Left
+        expandability[0] = 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[0]++;
+        }
+
+        // Top
+        expandability[1] = 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[1]++;
+        } 
+
+        // Right   
+        expandability[2] = 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[2]++;
+        } 
+
+        // Bottom
+        expandability[3] = 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[3]++;
+        } 
+    }
+
     public void onMove(View view, int newCellX, int newCellY) {
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
         markCellsAsUnoccupiedForView(view);
         markCellsForView(newCellX, newCellY, lp.cellHSpan, lp.cellVSpan, true);
     }
 
-    private void markCellsAsOccupiedForView(View view) {
+    public void markCellsAsOccupiedForView(View view) {
         if (view == null || view.getParent() != mChildren) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
         markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
     }
 
-    private void markCellsAsUnoccupiedForView(View view) {
+    public void markCellsAsUnoccupiedForView(View view) {
         if (view == null || view.getParent() != mChildren) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
         markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
@@ -1409,6 +1466,8 @@
         @ViewDebug.ExportedProperty
         public int cellVSpan;
 
+        public boolean isLockedToGrid = true;
+
         /**
          * Is this item currently being dragged
          */
@@ -1467,19 +1526,52 @@
 
         public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
                 int hStartPadding, int vStartPadding) {
+            if (isLockedToGrid) {
+                final int myCellHSpan = cellHSpan;
+                final int myCellVSpan = cellVSpan;
+                final int myCellX = cellX;
+                final int myCellY = cellY;
+    
+                width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
+                        leftMargin - rightMargin;
+                height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
+                        topMargin - bottomMargin;
+    
+                x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
+                y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
+            }
+        }
 
-            final int myCellHSpan = cellHSpan;
-            final int myCellVSpan = cellVSpan;
-            final int myCellX = cellX;
-            final int myCellY = cellY;
+        public void setWidth(int width) {
+            this.width = width;
+        }
 
-            width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
-                    leftMargin - rightMargin;
-            height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
-                    topMargin - bottomMargin;
+        public int getWidth() {
+            return width;
+        }
 
-            x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
-            y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
+        public void setHeight(int height) {
+            this.height = height;
+        }
+
+        public int getHeight() {
+            return height;
+        }
+
+        public void setX(int x) {
+            this.x = x;
+        }
+
+        public int getX() {
+            return x;
+        }
+
+        public void setY(int y) {
+            this.y = y;
+        }
+
+        public int getY() {
+            return y;
         }
 
         public String toString() {
diff --git a/src/com/android/launcher2/CellLayoutChildren.java b/src/com/android/launcher2/CellLayoutChildren.java
index 0d0a339..2a08fe3 100644
--- a/src/com/android/launcher2/CellLayoutChildren.java
+++ b/src/com/android/launcher2/CellLayoutChildren.java
@@ -16,12 +16,14 @@
 
 package com.android.launcher2;
 
+import java.util.ArrayList;
+
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Rect;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.View.MeasureSpec;
 
 public class CellLayoutChildren extends ViewGroup {
     static final String TAG = "CellLayoutChildren";
@@ -32,12 +34,12 @@
 
     private final WallpaperManager mWallpaperManager;
 
-    private int mCellWidth;
-    private int mCellHeight;
-
     private int mLeftPadding;
     private int mTopPadding;
 
+    private int mCellWidth;
+    private int mCellHeight;
+
     private int mWidthGap;
     private int mHeightGap;
 
@@ -171,4 +173,102 @@
     protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
         super.setChildrenDrawnWithCacheEnabled(enabled);
     }
-}
\ No newline at end of file
+
+    private final ArrayList<AppWidgetResizeFrame> mResizeFrames = new  ArrayList<AppWidgetResizeFrame>();
+    private AppWidgetResizeFrame mCurrentResizeFrame;
+    private int mXDown, mYDown;
+    private boolean mIsWidgetBeingResized;
+
+    public void clearAllResizeFrames() {
+        for (AppWidgetResizeFrame frame: mResizeFrames) {
+            removeView(frame);
+        }
+        mResizeFrames.clear();
+    }
+
+    public boolean isWidgetBeingResized() {
+        return mIsWidgetBeingResized;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        Rect hitRect = new Rect();
+
+        int x = (int) ev.getX();
+        int y = (int) ev.getY();
+
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            for (AppWidgetResizeFrame child: mResizeFrames) {
+                child.getHitRect(hitRect);
+                if (hitRect.contains(x, y)) {
+                    if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) {
+                        mCurrentResizeFrame = child;
+                        mIsWidgetBeingResized = true;
+                        mXDown = x;
+                        mYDown = y;
+                        requestDisallowInterceptTouchEvent(true);
+                        return true;
+                    }
+                }
+            }
+            mCurrentResizeFrame = null;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean handled = false;
+        Rect hitRect = new Rect();
+        int action = ev.getAction();
+
+        int x = (int) ev.getX();
+        int y = (int) ev.getY();
+
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            for (AppWidgetResizeFrame child: mResizeFrames) {
+                child.getHitRect(hitRect);
+                if (hitRect.contains(x, y)) {
+                    if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) {
+                        mCurrentResizeFrame = child;
+                        mIsWidgetBeingResized = true;
+                        mXDown = x;
+                        mYDown = y;
+                        requestDisallowInterceptTouchEvent(true);
+                        return true;
+                    }
+                }
+            }
+            mCurrentResizeFrame = null;
+        }
+
+        // TODO: Need to handle ACTION_POINTER_UP / multi-touch
+        if (mCurrentResizeFrame != null) {
+            switch (action) {
+                case MotionEvent.ACTION_MOVE:
+                    mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
+                    break;
+                case MotionEvent.ACTION_CANCEL:
+                case MotionEvent.ACTION_UP: {
+                    mCurrentResizeFrame.commitResizeForDelta(x - mXDown, y - mYDown);
+                    mIsWidgetBeingResized = false;
+                    handled = true;
+                }
+            }
+        }
+        return handled;
+    }
+
+    public void addResizeFrame(ItemInfo itemInfo, LauncherAppWidgetHostView widget, CellLayout cellLayout) {
+        AppWidgetResizeFrame resizeFrame = new AppWidgetResizeFrame(getContext(),
+                itemInfo, widget, cellLayout);
+
+        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(-1, -1, -1, -1);
+        lp.isLockedToGrid = false;
+
+        addView(resizeFrame, lp);
+        mResizeFrames.add(resizeFrame);
+
+        resizeFrame.snapToWidget(false);
+    }
+}
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index a9dd7e3..1912f81 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher2;
 
+import com.android.launcher.R;
+
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.util.AttributeSet;
@@ -56,6 +58,17 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        // Here we need to detect if any touch event has occured which doesn't result
+        // in resizing a widget. In this case, we dismiss any visible resize frames.
+        post(new Runnable() {
+            public void run() {
+                Workspace w = (Workspace) findViewById(R.id.workspace);
+                CellLayout currentPage = (CellLayout) w.getChildAt(w.getCurrentPage());
+                if (!currentPage.getChildrenLayout().isWidgetBeingResized()) {
+                    currentPage.getChildrenLayout().clearAllResizeFrames();
+                }                
+            }
+        });
         return mDragController.onInterceptTouchEvent(ev);
     }
 
diff --git a/src/com/android/launcher2/LauncherAppWidgetHostView.java b/src/com/android/launcher2/LauncherAppWidgetHostView.java
index 85a80f9..7c5de85 100644
--- a/src/com/android/launcher2/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher2/LauncherAppWidgetHostView.java
@@ -72,7 +72,7 @@
         // Otherwise continue letting touch events fall through to children
         return false;
     }
-    
+
     class CheckForLongPress implements Runnable {
         private int mOriginalWindowAttachCount;
 
@@ -122,4 +122,8 @@
         }
         super.onVisibilityChanged(changedView, visibility);
     }
+
+    public int getResizableMode() {
+        return getAppWidgetInfo().resizableMode;
+    }
 }
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 2364174..12f5737 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -177,6 +177,33 @@
     }
 
     /**
+     * Resize an item in the DB to a new <spanX, spanY>
+     */
+    static void resizeItemInDatabase(Context context, ItemInfo item, int cellX, int cellY, 
+            int spanX, int spanY) {
+        item.spanX = spanX;
+        item.spanY = spanY;
+        item.cellX = cellX;
+        item.cellY = cellY;
+
+        final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
+        final ContentValues values = new ContentValues();
+        final ContentResolver cr = context.getContentResolver();
+
+        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);
+
+        sWorker.post(new Runnable() {
+                public void run() {
+                    cr.update(uri, values, null, null);
+                }
+            });
+    }
+
+    /**
      * Returns true if the shortcuts already exists in the database.
      * we identify a shortcut by its title and intent.
      */
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index e7735be..bd4b502 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -2271,6 +2271,24 @@
                     cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen,
                             mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
 
+                    if (cell instanceof LauncherAppWidgetHostView) {
+                        final CellLayoutChildren children = dropTargetLayout.getChildrenLayout();
+                        final CellLayout cellLayout = dropTargetLayout;
+                        // We post this call so that the widget has a chance to be placed
+                        // in its final location
+
+                        final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
+                        AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
+                        if (pinfo.resizableMode != AppWidgetProviderInfo.RESIZE_NONE) {
+                            post(new Runnable() {
+                                public void run() {
+                                    children.addResizeFrame(info, hostView, 
+                                            cellLayout);
+                                }
+                            });
+                        }
+                    }
+
                     LauncherModel.moveItemInDatabase(mLauncher, info,
                             LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
                             lp.cellX, lp.cellY);
@@ -2309,7 +2327,7 @@
         // would land in a cell occupied by a DragTarget (e.g. a Folder),
         // then drag events should be handled by that child.
 
-        ItemInfo item = (ItemInfo)dragInfo;
+        ItemInfo item = (ItemInfo) dragInfo;
         CellLayout currentLayout = getCurrentDropLayout();
 
         int dragPointX, dragPointY;