Refactoring AppWidgetResizeFrame

> Defining the layout in xml
> Simplifying the touch handling calculations

Change-Id: Iccfd82161d1e678d77ad6ff63f76e04ad905f9d8
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
new file mode 100644
index 0000000..91a1e45
--- /dev/null
+++ b/res/layout/app_widget_resize_frame.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher3.AppWidgetResizeFrame
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/widget_resize_shadow"
+    android:foreground="@drawable/widget_resize_frame"
+    android:padding="0dp" >
+
+    <!-- Left -->
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_widget_resize_handle"
+        android:layout_gravity="left|center_vertical"
+        android:layout_marginLeft="@dimen/widget_handle_margin" />
+
+    <!-- Top -->
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_widget_resize_handle"
+        android:layout_gravity="top|center_horizontal"
+        android:layout_marginTop="@dimen/widget_handle_margin" />
+
+    <!-- Right -->
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_widget_resize_handle"
+        android:layout_gravity="right|center_vertical"
+        android:layout_marginRight="@dimen/widget_handle_margin" />
+
+    <!-- Bottom -->
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_widget_resize_handle"
+        android:layout_gravity="bottom|center_horizontal"
+        android:layout_marginBottom="@dimen/widget_handle_margin" />
+
+</com.android.launcher3.AppWidgetResizeFrame>
\ No newline at end of file
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index f0ec503..d00d5dd 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -1,7 +1,5 @@
 package com.android.launcher3;
 
-import com.android.launcher3.dragndrop.DragLayer;
-
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
@@ -13,14 +11,14 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.view.Gravity;
+import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.FocusLogic;
 import com.android.launcher3.util.TouchController;
 
@@ -35,17 +33,22 @@
     // Represents the cell size on the grid in the two orientations.
     private static Point[] sCellSize;
 
+    private static final int HANDLE_COUNT = 4;
+    private static final int INDEX_LEFT = 0;
+    private static final int INDEX_TOP = 1;
+    private static final int INDEX_RIGHT = 2;
+    private static final int INDEX_BOTTOM = 3;
+
     private final Launcher mLauncher;
-    private final LauncherAppWidgetHostView mWidgetView;
-    private final CellLayout mCellLayout;
-    private final DragLayer mDragLayer;
+    private final DragViewStateAnnouncer mStateAnnouncer;
 
-    private final ImageView mLeftHandle;
-    private final ImageView mRightHandle;
-    private final ImageView mTopHandle;
-    private final ImageView mBottomHandle;
+    private final View[] mDragHandles = new View[HANDLE_COUNT];
 
-    private final Rect mWidgetPadding;
+    private LauncherAppWidgetHostView mWidgetView;
+    private CellLayout mCellLayout;
+    private DragLayer mDragLayer;
+
+    private Rect mWidgetPadding;
 
     private final int mBackgroundPadding;
     private final int mTouchTargetWidth;
@@ -54,17 +57,20 @@
     private final int[] mLastDirectionVector = new int[2];
     private final int[] mTmpPt = new int[2];
 
-    private final DragViewStateAnnouncer mStateAnnouncer;
+    private final IntRange mTempRange1 = new IntRange();
+    private final IntRange mTempRange2 = new IntRange();
+
+    private final IntRange mDeltaXRange = new IntRange();
+    private final IntRange mBaselineX = new IntRange();
+
+    private final IntRange mDeltaYRange = new IntRange();
+    private final IntRange mBaselineY = new IntRange();
 
     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;
@@ -81,11 +87,36 @@
 
     private int mXDown, mYDown;
 
-    public AppWidgetResizeFrame(Context context,
-            LauncherAppWidgetHostView widgetView, CellLayout cellLayout, DragLayer dragLayer) {
+    public AppWidgetResizeFrame(Context context) {
+        this(context, null);
+    }
 
-        super(context);
-        mLauncher = (Launcher) context;
+    public AppWidgetResizeFrame(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppWidgetResizeFrame(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mLauncher = Launcher.getLauncher(context);
+        mStateAnnouncer = DragViewStateAnnouncer.createFor(this);
+
+        mBackgroundPadding = getResources()
+                .getDimensionPixelSize(R.dimen.resize_frame_background_padding);
+        mTouchTargetWidth = 2 * mBackgroundPadding;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        for (int i = 0; i < HANDLE_COUNT; i ++) {
+            mDragHandles[i] = getChildAt(i);
+        }
+    }
+
+    public void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
+            DragLayer dragLayer) {
         mCellLayout = cellLayout;
         mWidgetView = widgetView;
         LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
@@ -96,63 +127,23 @@
         mMinHSpan = info.minSpanX;
         mMinVSpan = info.minSpanY;
 
-        mStateAnnouncer = DragViewStateAnnouncer.createFor(this);
-
-        setBackgroundResource(R.drawable.widget_resize_shadow);
-        setForeground(getResources().getDrawable(R.drawable.widget_resize_frame));
-        setPadding(0, 0, 0, 0);
-
-        final int handleMargin = getResources().getDimensionPixelSize(R.dimen.widget_handle_margin);
-        LayoutParams lp;
-        mLeftHandle = new ImageView(context);
-        mLeftHandle.setImageResource(R.drawable.ic_widget_resize_handle);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
-                Gravity.LEFT | Gravity.CENTER_VERTICAL);
-        lp.leftMargin = handleMargin;
-        addView(mLeftHandle, lp);
-
-        mRightHandle = new ImageView(context);
-        mRightHandle.setImageResource(R.drawable.ic_widget_resize_handle);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
-                Gravity.RIGHT | Gravity.CENTER_VERTICAL);
-        lp.rightMargin = handleMargin;
-        addView(mRightHandle, lp);
-
-        mTopHandle = new ImageView(context);
-        mTopHandle.setImageResource(R.drawable.ic_widget_resize_handle);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
-                Gravity.CENTER_HORIZONTAL | Gravity.TOP);
-        lp.topMargin = handleMargin;
-        addView(mTopHandle, lp);
-
-        mBottomHandle = new ImageView(context);
-        mBottomHandle.setImageResource(R.drawable.ic_widget_resize_handle);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
-                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-        lp.bottomMargin = handleMargin;
-        addView(mBottomHandle, lp);
-
         if (!info.isCustomWidget) {
-            mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context,
+            mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(),
                     widgetView.getAppWidgetInfo().provider, null);
         } else {
-            Resources r = context.getResources();
+            Resources r = getContext().getResources();
             int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding);
             mWidgetPadding = new Rect(padding, padding, padding, padding);
         }
 
         if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
-            mTopHandle.setVisibility(GONE);
-            mBottomHandle.setVisibility(GONE);
+            mDragHandles[INDEX_TOP].setVisibility(GONE);
+            mDragHandles[INDEX_BOTTOM].setVisibility(GONE);
         } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
-            mLeftHandle.setVisibility(GONE);
-            mRightHandle.setVisibility(GONE);
+            mDragHandles[INDEX_LEFT].setVisibility(GONE);
+            mDragHandles[INDEX_RIGHT].setVisibility(GONE);
         }
 
-        mBackgroundPadding = getResources()
-                .getDimensionPixelSize(R.dimen.resize_frame_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.
@@ -174,101 +165,74 @@
         boolean anyBordersActive = mLeftBorderActive || mRightBorderActive
                 || mTopBorderActive || mBottomBorderActive;
 
-        mBaselineWidth = getMeasuredWidth();
-        mBaselineHeight = getMeasuredHeight();
-        mBaselineX = getLeft();
-        mBaselineY = getTop();
-
         if (anyBordersActive) {
-            mLeftHandle.setAlpha(mLeftBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
-            mRightHandle.setAlpha(mRightBorderActive ? 1.0f :DIMMED_HANDLE_ALPHA);
-            mTopHandle.setAlpha(mTopBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
-            mBottomHandle.setAlpha(mBottomBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
+            mDragHandles[INDEX_LEFT].setAlpha(mLeftBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
+            mDragHandles[INDEX_RIGHT].setAlpha(mRightBorderActive ? 1.0f :DIMMED_HANDLE_ALPHA);
+            mDragHandles[INDEX_TOP].setAlpha(mTopBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
+            mDragHandles[INDEX_BOTTOM].setAlpha(mBottomBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
         }
+
+        if (mLeftBorderActive) {
+            mDeltaXRange.set(-getLeft(), getWidth() - 2 * mTouchTargetWidth);
+        } else if (mRightBorderActive) {
+            mDeltaXRange.set(2 * mTouchTargetWidth - getWidth(), mDragLayer.getWidth() - getRight());
+        } else {
+            mDeltaXRange.set(0, 0);
+        }
+        mBaselineX.set(getLeft(), getRight());
+
+        if (mTopBorderActive) {
+            mDeltaYRange.set(-getTop(), getHeight() - 2 * mTouchTargetWidth);
+        } else if (mBottomBorderActive) {
+            mDeltaYRange.set(2 * mTouchTargetWidth - getHeight(), mDragLayer.getHeight() - getBottom());
+        } else {
+            mDeltaYRange.set(0, 0);
+        }
+        mBaselineY.set(getTop(), getBottom());
+
         return anyBordersActive;
     }
 
     /**
-     *  Here we bound the deltas such that the frame cannot be stretched beyond the extents
-     *  of the CellLayout, and such that the frame's borders can't cross.
+     *  Based on the deltas, we resize the frame.
      */
-    public void updateDeltas(int deltaX, int deltaY) {
-        if (mLeftBorderActive) {
-            mDeltaX = Math.max(-mBaselineX, deltaX); 
-            mDeltaX = Math.min(mBaselineWidth - 2 * mTouchTargetWidth, mDeltaX);
-        } else if (mRightBorderActive) {
-            mDeltaX = Math.min(mDragLayer.getWidth() - (mBaselineX + mBaselineWidth), deltaX);
-            mDeltaX = Math.max(-mBaselineWidth + 2 * mTouchTargetWidth, mDeltaX);
-        }
+    public void visualizeResizeForDelta(int deltaX, int deltaY) {
+        mDeltaX = mDeltaXRange.clamp(deltaX);
+        mDeltaY = mDeltaYRange.clamp(deltaY);
 
-        if (mTopBorderActive) {
-            mDeltaY = Math.max(-mBaselineY, deltaY);
-            mDeltaY = Math.min(mBaselineHeight - 2 * mTouchTargetWidth, mDeltaY);
-        } else if (mBottomBorderActive) {
-            mDeltaY = Math.min(mDragLayer.getHeight() - (mBaselineY + mBaselineHeight), deltaY);
-            mDeltaY = Math.max(-mBaselineHeight + 2 * mTouchTargetWidth, mDeltaY);
-        }
-    }
-
-    private 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.
-     */
-    private void visualizeResizeForDelta(int deltaX, int deltaY, boolean onDismiss) {
-        updateDeltas(deltaX, deltaY);
         DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+        mDeltaX = mDeltaXRange.clamp(deltaX);
+        mBaselineX.applyDelta(mLeftBorderActive, mRightBorderActive, mDeltaX, mTempRange1);
+        lp.x = mTempRange1.start;
+        lp.width = mTempRange1.size();
 
-        if (mLeftBorderActive) {
-            lp.x = mBaselineX + mDeltaX;
-            lp.width = mBaselineWidth - mDeltaX;
-        } else if (mRightBorderActive) {
-            lp.width = mBaselineWidth + mDeltaX;
-        }
+        mDeltaY = mDeltaYRange.clamp(deltaY);
+        mBaselineY.applyDelta(mTopBorderActive, mBottomBorderActive, mDeltaY, mTempRange1);
+        lp.y = mTempRange1.start;
+        lp.height = mTempRange1.size();
 
-        if (mTopBorderActive) {
-            lp.y = mBaselineY + mDeltaY;
-            lp.height = mBaselineHeight - mDeltaY;
-        } else if (mBottomBorderActive) {
-            lp.height = mBaselineHeight + mDeltaY;
-        }
-
-        resizeWidgetIfNeeded(onDismiss);
+        resizeWidgetIfNeeded(false);
         requestLayout();
     }
 
+    private static int getSpanIncrement(float deltaFrac) {
+        return Math.abs(deltaFrac) > RESIZE_THRESHOLD ? Math.round(deltaFrac) : 0;
+    }
+
     /**
      *  Based on the current deltas, we determine if and how to resize the widget.
      */
     private void resizeWidgetIfNeeded(boolean onDismiss) {
-        int xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
-        int yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
+        float xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
+        float yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
 
-        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);
-        }
-        if (Math.abs(vSpanIncF) > RESIZE_THRESHOLD) {
-            vSpanInc = Math.round(vSpanIncF);
-        }
+        int hSpanInc = getSpanIncrement((mDeltaX + mDeltaXAddOn) / xThreshold - mRunningHInc);
+        int vSpanInc = getSpanIncrement((mDeltaY + mDeltaYAddOn) / yThreshold - mRunningVInc);
 
         if (!onDismiss && (hSpanInc == 0 && vSpanInc == 0)) return;
 
+        mDirectionVector[0] = 0;
+        mDirectionVector[1] = 0;
 
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
 
@@ -277,55 +241,24 @@
         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(-cellX, hSpanInc);
-            cellXInc = Math.min(lp.cellHSpan - mMinHSpan, cellXInc);
-            hSpanInc *= -1;
-            hSpanInc = Math.min(cellX, hSpanInc);
-            hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
-            hSpanDelta = -hSpanInc;
-
-        } else if (mRightBorderActive) {
-            hSpanInc = Math.min(countX - (cellX + spanX), hSpanInc);
-            hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
-            hSpanDelta = hSpanInc;
+        mTempRange1.set(cellX, spanX + cellX);
+        int hSpanDelta = mTempRange1.applyDeltaAndBound(mLeftBorderActive, mRightBorderActive,
+                hSpanInc, mMinHSpan, mCellLayout.getCountX(), mTempRange2);
+        cellX = mTempRange2.start;
+        spanX = mTempRange2.size();
+        if (hSpanDelta != 0) {
+            mDirectionVector[0] = mLeftBorderActive ? -1 : 1;
         }
 
-        if (mTopBorderActive) {
-            cellYInc = Math.max(-cellY, vSpanInc);
-            cellYInc = Math.min(lp.cellVSpan - mMinVSpan, cellYInc);
-            vSpanInc *= -1;
-            vSpanInc = Math.min(cellY, vSpanInc);
-            vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
-            vSpanDelta = -vSpanInc;
-        } else if (mBottomBorderActive) {
-            vSpanInc = Math.min(countY - (cellY + spanY), vSpanInc);
-            vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), 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) {
-            spanX += hSpanInc;
-            cellX += cellXInc;
-            if (hSpanDelta != 0) {
-                mDirectionVector[0] = mLeftBorderActive ? -1 : 1;
-            }
-        }
-
-        if (mTopBorderActive || mBottomBorderActive) {
-            spanY += vSpanInc;
-            cellY += cellYInc;
-            if (vSpanDelta != 0) {
-                mDirectionVector[1] = mTopBorderActive ? -1 : 1;
-            }
+        mTempRange1.set(cellY, spanY + cellY);
+        int vSpanDelta = mTempRange1.applyDeltaAndBound(mTopBorderActive, mBottomBorderActive,
+                vSpanInc, mMinVSpan, mCellLayout.getCountY(), mTempRange2);
+        cellY = mTempRange2.start;
+        spanY = mTempRange2.size();
+        if (vSpanDelta != 0) {
+            mDirectionVector[1] = mTopBorderActive ? -1 : 1;
         }
 
         if (!onDismiss && vSpanDelta == 0 && hSpanDelta == 0) return;
@@ -455,10 +388,9 @@
             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);
+            for (int i = 0; i < HANDLE_COUNT; i++) {
+                mDragHandles[i].setAlpha(1.0f);
+            }
             requestLayout();
         } else {
             PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", lp.width, newWidth);
@@ -468,22 +400,15 @@
             PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
             ObjectAnimator oa =
                     LauncherAnimUtils.ofPropertyValuesHolder(lp, this, width, height, x, y);
-            ObjectAnimator leftOa = LauncherAnimUtils.ofFloat(mLeftHandle, ALPHA, 1.0f);
-            ObjectAnimator rightOa = LauncherAnimUtils.ofFloat(mRightHandle, ALPHA, 1.0f);
-            ObjectAnimator topOa = LauncherAnimUtils.ofFloat(mTopHandle, ALPHA, 1.0f);
-            ObjectAnimator bottomOa = LauncherAnimUtils.ofFloat(mBottomHandle, ALPHA, 1.0f);
             oa.addUpdateListener(new AnimatorUpdateListener() {
                 public void onAnimationUpdate(ValueAnimator animation) {
                     requestLayout();
                 }
             });
             AnimatorSet set = LauncherAnimUtils.createAnimatorSet();
-            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.play(oa);
+            for (int i = 0; i < HANDLE_COUNT; i++) {
+                set.play(LauncherAnimUtils.ofFloat(mDragHandles[i], ALPHA, 1.0f));
             }
 
             set.setDuration(SNAP_DURATION);
@@ -550,4 +475,63 @@
         }
         return false;
     }
+
+    /**
+     * A mutable class for describing the range of two int values.
+     */
+    private static class IntRange {
+
+        public int start, end;
+
+        public int clamp(int value) {
+            return Utilities.boundToRange(value, start, end);
+        }
+
+        public void set(int s, int e) {
+            start = s;
+            end = e;
+        }
+
+        public int size() {
+            return end - start;
+        }
+
+        /**
+         * Moves either the start or end edge (but never both) by {@param delta} and  sets the
+         * result in {@param out}
+         */
+        public void applyDelta(boolean moveStart, boolean moveEnd, int delta, IntRange out) {
+            out.start = moveStart ? start + delta : start;
+            out.end = moveEnd ? end + delta : end;
+        }
+
+        /**
+         * Applies delta similar to {@link #applyDelta(boolean, boolean, int, IntRange)},
+         * with extra conditions.
+         * @param minSize minimum size after with the moving edge should not be shifted any further.
+         *                For eg, if delta = -3 when moving the endEdge brings the size to less than
+         *                minSize, only delta = -2 will applied
+         * @param maxEnd The maximum value to the end edge (start edge is always restricted to 0)
+         * @return the amount of increase when endEdge was moves and the amount of decrease when
+         * the start edge was moved.
+         */
+        public int applyDeltaAndBound(boolean moveStart, boolean moveEnd, int delta,
+                int minSize, int maxEnd, IntRange out) {
+            applyDelta(moveStart, moveEnd, delta, out);
+            if (start < 0) {
+                out.start = 0;
+            }
+            if (end > maxEnd) {
+                out.end = maxEnd;
+            }
+            if (out.size() < minSize) {
+                if (moveStart) {
+                    out.start = out.end - minSize;
+                } else if (moveEnd) {
+                    out.end = out.start + minSize;
+                }
+            }
+            return moveEnd ? out.size() - size() : size() - out.size();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index ef9c965..1bbbd46 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -38,6 +38,7 @@
 import android.util.AttributeSet;
 import android.view.DragEvent;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -621,12 +622,12 @@
     public void addResizeFrame(LauncherAppWidgetHostView widget, CellLayout cellLayout) {
         clearResizeFrame();
 
-        mCurrentResizeFrame = new AppWidgetResizeFrame(getContext(), widget, cellLayout, this);
+        mCurrentResizeFrame = (AppWidgetResizeFrame) LayoutInflater.from(mLauncher)
+                .inflate(R.layout.app_widget_resize_frame, this, false);
+        mCurrentResizeFrame.setupForWidget(widget, cellLayout, this);
+        ((LayoutParams) mCurrentResizeFrame.getLayoutParams()).customPosition = true;
 
-        LayoutParams lp = new LayoutParams(-1, -1);
-        lp.customPosition = true;
-
-        addView(mCurrentResizeFrame, lp);
+        addView(mCurrentResizeFrame);
         mCurrentResizeFrame.snapToWidget(false);
     }