blob: f0ec50343a1f9280d391817a76178f290fa3e1a9 [file] [log] [blame]
Daniel Sandler325dc232013-06-05 22:57:57 -04001package com.android.launcher3;
Adam Cohend4844c32011-02-18 19:25:06 -08002
Vadim Tryshevfedca432015-08-19 17:55:02 -07003import com.android.launcher3.dragndrop.DragLayer;
4
Adam Cohend4844c32011-02-18 19:25:06 -08005import android.animation.AnimatorSet;
6import android.animation.ObjectAnimator;
7import android.animation.PropertyValuesHolder;
8import android.animation.ValueAnimator;
9import android.animation.ValueAnimator.AnimatorUpdateListener;
Adam Cohen0cf2a7c2011-11-08 15:07:01 -080010import android.appwidget.AppWidgetHostView;
Adam Cohend4844c32011-02-18 19:25:06 -080011import android.appwidget.AppWidgetProviderInfo;
12import android.content.Context;
Adam Cohen59400422014-03-05 18:07:04 -080013import android.content.res.Resources;
Sunny Goyal756cd262015-08-20 12:33:21 -070014import android.graphics.Point;
Adam Cohen0cf2a7c2011-11-08 15:07:01 -080015import android.graphics.Rect;
Adam Cohend4844c32011-02-18 19:25:06 -080016import android.view.Gravity;
Tony Wickham71255bb2016-02-10 16:18:15 -080017import android.view.KeyEvent;
Sunny Goyal6ad72f02016-09-23 11:01:10 -070018import android.view.MotionEvent;
Tony Wickham71255bb2016-02-10 16:18:15 -080019import android.view.View;
Adam Cohend4844c32011-02-18 19:25:06 -080020import android.widget.FrameLayout;
21import android.widget.ImageView;
22
Sunny Goyale78e3d72015-09-24 11:23:31 -070023import com.android.launcher3.accessibility.DragViewStateAnnouncer;
Tony Wickham71255bb2016-02-10 16:18:15 -080024import com.android.launcher3.util.FocusLogic;
Sunny Goyal6ad72f02016-09-23 11:01:10 -070025import com.android.launcher3.util.TouchController;
Sunny Goyale78e3d72015-09-24 11:23:31 -070026
Sunny Goyal6ad72f02016-09-23 11:01:10 -070027public class AppWidgetResizeFrame extends FrameLayout
28 implements View.OnKeyListener, TouchController {
Sunny Goyalba776d52015-05-18 20:52:57 -070029 private static final int SNAP_DURATION = 150;
30 private static final float DIMMED_HANDLE_ALPHA = 0f;
31 private static final float RESIZE_THRESHOLD = 0.66f;
32
Sunny Goyal756cd262015-08-20 12:33:21 -070033 private static final Rect sTmpRect = new Rect();
34
35 // Represents the cell size on the grid in the two orientations.
36 private static Point[] sCellSize;
Sunny Goyalba776d52015-05-18 20:52:57 -070037
38 private final Launcher mLauncher;
39 private final LauncherAppWidgetHostView mWidgetView;
40 private final CellLayout mCellLayout;
41 private final DragLayer mDragLayer;
42
43 private final ImageView mLeftHandle;
44 private final ImageView mRightHandle;
45 private final ImageView mTopHandle;
46 private final ImageView mBottomHandle;
47
48 private final Rect mWidgetPadding;
49
50 private final int mBackgroundPadding;
51 private final int mTouchTargetWidth;
52
53 private final int[] mDirectionVector = new int[2];
54 private final int[] mLastDirectionVector = new int[2];
55 private final int[] mTmpPt = new int[2];
Adam Cohend4844c32011-02-18 19:25:06 -080056
Sunny Goyale78e3d72015-09-24 11:23:31 -070057 private final DragViewStateAnnouncer mStateAnnouncer;
58
Adam Cohend4844c32011-02-18 19:25:06 -080059 private boolean mLeftBorderActive;
60 private boolean mRightBorderActive;
61 private boolean mTopBorderActive;
62 private boolean mBottomBorderActive;
63
64 private int mBaselineWidth;
65 private int mBaselineHeight;
66 private int mBaselineX;
67 private int mBaselineY;
68 private int mResizeMode;
Adam Cohen3cba7222011-03-02 19:03:11 -080069
Adam Cohend4844c32011-02-18 19:25:06 -080070 private int mRunningHInc;
71 private int mRunningVInc;
72 private int mMinHSpan;
73 private int mMinVSpan;
74 private int mDeltaX;
75 private int mDeltaY;
Adam Cohenbebf0422012-04-11 18:06:28 -070076 private int mDeltaXAddOn;
77 private int mDeltaYAddOn;
Adam Cohen1b607ed2011-03-03 17:26:50 -080078
Adam Cohen4459d6b2012-07-13 15:59:15 -070079 private int mTopTouchRegionAdjustment = 0;
80 private int mBottomTouchRegionAdjustment = 0;
81
Sunny Goyal6ad72f02016-09-23 11:01:10 -070082 private int mXDown, mYDown;
83
Michael Jurka3a9fced2012-04-13 14:44:29 -070084 public AppWidgetResizeFrame(Context context,
Adam Cohen67882692011-03-11 15:29:03 -080085 LauncherAppWidgetHostView widgetView, CellLayout cellLayout, DragLayer dragLayer) {
Adam Cohend4844c32011-02-18 19:25:06 -080086
87 super(context);
Adam Cohencbf47e32011-09-16 17:32:37 -070088 mLauncher = (Launcher) context;
Adam Cohend4844c32011-02-18 19:25:06 -080089 mCellLayout = cellLayout;
90 mWidgetView = widgetView;
Adam Cohen59400422014-03-05 18:07:04 -080091 LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
92 widgetView.getAppWidgetInfo();
93 mResizeMode = info.resizeMode;
Adam Cohen67882692011-03-11 15:29:03 -080094 mDragLayer = dragLayer;
Adam Cohen3cba7222011-03-02 19:03:11 -080095
Sunny Goyal233ee962015-08-03 13:05:01 -070096 mMinHSpan = info.minSpanX;
97 mMinVSpan = info.minSpanY;
Adam Cohend4844c32011-02-18 19:25:06 -080098
Sunny Goyale78e3d72015-09-24 11:23:31 -070099 mStateAnnouncer = DragViewStateAnnouncer.createFor(this);
100
Sunny Goyalba776d52015-05-18 20:52:57 -0700101 setBackgroundResource(R.drawable.widget_resize_shadow);
102 setForeground(getResources().getDrawable(R.drawable.widget_resize_frame));
Adam Cohend4844c32011-02-18 19:25:06 -0800103 setPadding(0, 0, 0, 0);
104
Sunny Goyalba776d52015-05-18 20:52:57 -0700105 final int handleMargin = getResources().getDimensionPixelSize(R.dimen.widget_handle_margin);
Adam Cohend4844c32011-02-18 19:25:06 -0800106 LayoutParams lp;
107 mLeftHandle = new ImageView(context);
Sunny Goyalba776d52015-05-18 20:52:57 -0700108 mLeftHandle.setImageResource(R.drawable.ic_widget_resize_handle);
109 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
Adam Cohen283dcbe2013-09-26 16:35:40 -0700110 Gravity.LEFT | Gravity.CENTER_VERTICAL);
Sunny Goyalba776d52015-05-18 20:52:57 -0700111 lp.leftMargin = handleMargin;
Adam Cohend4844c32011-02-18 19:25:06 -0800112 addView(mLeftHandle, lp);
113
114 mRightHandle = new ImageView(context);
Sunny Goyalba776d52015-05-18 20:52:57 -0700115 mRightHandle.setImageResource(R.drawable.ic_widget_resize_handle);
116 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
Adam Cohen283dcbe2013-09-26 16:35:40 -0700117 Gravity.RIGHT | Gravity.CENTER_VERTICAL);
Sunny Goyalba776d52015-05-18 20:52:57 -0700118 lp.rightMargin = handleMargin;
Adam Cohend4844c32011-02-18 19:25:06 -0800119 addView(mRightHandle, lp);
120
121 mTopHandle = new ImageView(context);
Sunny Goyalba776d52015-05-18 20:52:57 -0700122 mTopHandle.setImageResource(R.drawable.ic_widget_resize_handle);
123 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
Adam Cohend4844c32011-02-18 19:25:06 -0800124 Gravity.CENTER_HORIZONTAL | Gravity.TOP);
Sunny Goyalba776d52015-05-18 20:52:57 -0700125 lp.topMargin = handleMargin;
Adam Cohend4844c32011-02-18 19:25:06 -0800126 addView(mTopHandle, lp);
127
128 mBottomHandle = new ImageView(context);
Sunny Goyalba776d52015-05-18 20:52:57 -0700129 mBottomHandle.setImageResource(R.drawable.ic_widget_resize_handle);
130 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
Adam Cohend4844c32011-02-18 19:25:06 -0800131 Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
Sunny Goyalba776d52015-05-18 20:52:57 -0700132 lp.bottomMargin = handleMargin;
Adam Cohend4844c32011-02-18 19:25:06 -0800133 addView(mBottomHandle, lp);
134
Adam Cohen59400422014-03-05 18:07:04 -0800135 if (!info.isCustomWidget) {
Sunny Goyalba776d52015-05-18 20:52:57 -0700136 mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context,
Adam Cohen59400422014-03-05 18:07:04 -0800137 widgetView.getAppWidgetInfo().provider, null);
138 } else {
139 Resources r = context.getResources();
140 int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding);
Sunny Goyalba776d52015-05-18 20:52:57 -0700141 mWidgetPadding = new Rect(padding, padding, padding, padding);
Adam Cohen59400422014-03-05 18:07:04 -0800142 }
143
Adam Cohend4844c32011-02-18 19:25:06 -0800144 if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
145 mTopHandle.setVisibility(GONE);
146 mBottomHandle.setVisibility(GONE);
147 } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
148 mLeftHandle.setVisibility(GONE);
149 mRightHandle.setVisibility(GONE);
Adam Cohen3cba7222011-03-02 19:03:11 -0800150 }
151
Sunny Goyalba776d52015-05-18 20:52:57 -0700152 mBackgroundPadding = getResources()
153 .getDimensionPixelSize(R.dimen.resize_frame_background_padding);
Adam Cohen3cba7222011-03-02 19:03:11 -0800154 mTouchTargetWidth = 2 * mBackgroundPadding;
Adam Cohenbebf0422012-04-11 18:06:28 -0700155
156 // When we create the resize frame, we first mark all cells as unoccupied. The appropriate
157 // cells (same if not resized, or different) will be marked as occupied when the resize
158 // frame is dismissed.
159 mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
Tony Wickham71255bb2016-02-10 16:18:15 -0800160
161 setOnKeyListener(this);
Adam Cohend4844c32011-02-18 19:25:06 -0800162 }
163
164 public boolean beginResizeIfPointInRegion(int x, int y) {
165 boolean horizontalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0;
166 boolean verticalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0;
Adam Cohen4459d6b2012-07-13 15:59:15 -0700167
Adam Cohen3cba7222011-03-02 19:03:11 -0800168 mLeftBorderActive = (x < mTouchTargetWidth) && horizontalActive;
169 mRightBorderActive = (x > getWidth() - mTouchTargetWidth) && horizontalActive;
Adam Cohen4459d6b2012-07-13 15:59:15 -0700170 mTopBorderActive = (y < mTouchTargetWidth + mTopTouchRegionAdjustment) && verticalActive;
171 mBottomBorderActive = (y > getHeight() - mTouchTargetWidth + mBottomTouchRegionAdjustment)
172 && verticalActive;
Adam Cohend4844c32011-02-18 19:25:06 -0800173
174 boolean anyBordersActive = mLeftBorderActive || mRightBorderActive
175 || mTopBorderActive || mBottomBorderActive;
176
177 mBaselineWidth = getMeasuredWidth();
178 mBaselineHeight = getMeasuredHeight();
179 mBaselineX = getLeft();
180 mBaselineY = getTop();
Adam Cohend4844c32011-02-18 19:25:06 -0800181
182 if (anyBordersActive) {
Adam Cohen3cba7222011-03-02 19:03:11 -0800183 mLeftHandle.setAlpha(mLeftBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
184 mRightHandle.setAlpha(mRightBorderActive ? 1.0f :DIMMED_HANDLE_ALPHA);
185 mTopHandle.setAlpha(mTopBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
186 mBottomHandle.setAlpha(mBottomBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
Adam Cohend4844c32011-02-18 19:25:06 -0800187 }
Adam Cohend4844c32011-02-18 19:25:06 -0800188 return anyBordersActive;
189 }
190
Adam Cohen1b607ed2011-03-03 17:26:50 -0800191 /**
192 * Here we bound the deltas such that the frame cannot be stretched beyond the extents
193 * of the CellLayout, and such that the frame's borders can't cross.
194 */
Adam Cohend4844c32011-02-18 19:25:06 -0800195 public void updateDeltas(int deltaX, int deltaY) {
196 if (mLeftBorderActive) {
197 mDeltaX = Math.max(-mBaselineX, deltaX);
Adam Cohen3cba7222011-03-02 19:03:11 -0800198 mDeltaX = Math.min(mBaselineWidth - 2 * mTouchTargetWidth, mDeltaX);
Adam Cohend4844c32011-02-18 19:25:06 -0800199 } else if (mRightBorderActive) {
Adam Cohen67882692011-03-11 15:29:03 -0800200 mDeltaX = Math.min(mDragLayer.getWidth() - (mBaselineX + mBaselineWidth), deltaX);
Adam Cohen3cba7222011-03-02 19:03:11 -0800201 mDeltaX = Math.max(-mBaselineWidth + 2 * mTouchTargetWidth, mDeltaX);
Adam Cohend4844c32011-02-18 19:25:06 -0800202 }
203
204 if (mTopBorderActive) {
205 mDeltaY = Math.max(-mBaselineY, deltaY);
Adam Cohen3cba7222011-03-02 19:03:11 -0800206 mDeltaY = Math.min(mBaselineHeight - 2 * mTouchTargetWidth, mDeltaY);
Adam Cohend4844c32011-02-18 19:25:06 -0800207 } else if (mBottomBorderActive) {
Adam Cohen67882692011-03-11 15:29:03 -0800208 mDeltaY = Math.min(mDragLayer.getHeight() - (mBaselineY + mBaselineHeight), deltaY);
Adam Cohen3cba7222011-03-02 19:03:11 -0800209 mDeltaY = Math.max(-mBaselineHeight + 2 * mTouchTargetWidth, mDeltaY);
Adam Cohend4844c32011-02-18 19:25:06 -0800210 }
211 }
212
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700213 private void visualizeResizeForDelta(int deltaX, int deltaY) {
Adam Cohenbebf0422012-04-11 18:06:28 -0700214 visualizeResizeForDelta(deltaX, deltaY, false);
215 }
216
Adam Cohen1b607ed2011-03-03 17:26:50 -0800217 /**
218 * Based on the deltas, we resize the frame, and, if needed, we resize the widget.
219 */
Adam Cohenbebf0422012-04-11 18:06:28 -0700220 private void visualizeResizeForDelta(int deltaX, int deltaY, boolean onDismiss) {
Adam Cohend4844c32011-02-18 19:25:06 -0800221 updateDeltas(deltaX, deltaY);
Adam Cohen67882692011-03-11 15:29:03 -0800222 DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
223
Adam Cohend4844c32011-02-18 19:25:06 -0800224 if (mLeftBorderActive) {
225 lp.x = mBaselineX + mDeltaX;
226 lp.width = mBaselineWidth - mDeltaX;
227 } else if (mRightBorderActive) {
228 lp.width = mBaselineWidth + mDeltaX;
229 }
230
231 if (mTopBorderActive) {
232 lp.y = mBaselineY + mDeltaY;
233 lp.height = mBaselineHeight - mDeltaY;
234 } else if (mBottomBorderActive) {
235 lp.height = mBaselineHeight + mDeltaY;
236 }
237
Adam Cohenbebf0422012-04-11 18:06:28 -0700238 resizeWidgetIfNeeded(onDismiss);
Adam Cohend4844c32011-02-18 19:25:06 -0800239 requestLayout();
240 }
241
Adam Cohen1b607ed2011-03-03 17:26:50 -0800242 /**
243 * Based on the current deltas, we determine if and how to resize the widget.
244 */
Adam Cohenbebf0422012-04-11 18:06:28 -0700245 private void resizeWidgetIfNeeded(boolean onDismiss) {
Adam Cohend4844c32011-02-18 19:25:06 -0800246 int xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
247 int yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
248
Adam Cohenbebf0422012-04-11 18:06:28 -0700249 int deltaX = mDeltaX + mDeltaXAddOn;
250 int deltaY = mDeltaY + mDeltaYAddOn;
251
252 float hSpanIncF = 1.0f * deltaX / xThreshold - mRunningHInc;
253 float vSpanIncF = 1.0f * deltaY / yThreshold - mRunningVInc;
Adam Cohene4b77292011-03-08 18:35:52 -0800254
255 int hSpanInc = 0;
256 int vSpanInc = 0;
Adam Cohend4844c32011-02-18 19:25:06 -0800257 int cellXInc = 0;
258 int cellYInc = 0;
259
Adam Cohenbebf0422012-04-11 18:06:28 -0700260 int countX = mCellLayout.getCountX();
261 int countY = mCellLayout.getCountY();
262
Adam Cohene4b77292011-03-08 18:35:52 -0800263 if (Math.abs(hSpanIncF) > RESIZE_THRESHOLD) {
264 hSpanInc = Math.round(hSpanIncF);
265 }
266 if (Math.abs(vSpanIncF) > RESIZE_THRESHOLD) {
267 vSpanInc = Math.round(vSpanIncF);
268 }
269
Adam Cohenbebf0422012-04-11 18:06:28 -0700270 if (!onDismiss && (hSpanInc == 0 && vSpanInc == 0)) return;
Adam Cohend4844c32011-02-18 19:25:06 -0800271
Adam Cohend4844c32011-02-18 19:25:06 -0800272
273 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
Adam Cohen1b607ed2011-03-03 17:26:50 -0800274
Adam Cohenbebf0422012-04-11 18:06:28 -0700275 int spanX = lp.cellHSpan;
276 int spanY = lp.cellVSpan;
277 int cellX = lp.useTmpCoords ? lp.tmpCellX : lp.cellX;
278 int cellY = lp.useTmpCoords ? lp.tmpCellY : lp.cellY;
279
280 int hSpanDelta = 0;
281 int vSpanDelta = 0;
282
Adam Cohen1b607ed2011-03-03 17:26:50 -0800283 // For each border, we bound the resizing based on the minimum width, and the maximum
284 // expandability.
Adam Cohend4844c32011-02-18 19:25:06 -0800285 if (mLeftBorderActive) {
Adam Cohenbebf0422012-04-11 18:06:28 -0700286 cellXInc = Math.max(-cellX, hSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800287 cellXInc = Math.min(lp.cellHSpan - mMinHSpan, cellXInc);
288 hSpanInc *= -1;
Adam Cohenbebf0422012-04-11 18:06:28 -0700289 hSpanInc = Math.min(cellX, hSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800290 hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
Adam Cohenbebf0422012-04-11 18:06:28 -0700291 hSpanDelta = -hSpanInc;
292
Adam Cohend4844c32011-02-18 19:25:06 -0800293 } else if (mRightBorderActive) {
Adam Cohenbebf0422012-04-11 18:06:28 -0700294 hSpanInc = Math.min(countX - (cellX + spanX), hSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800295 hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
Adam Cohenbebf0422012-04-11 18:06:28 -0700296 hSpanDelta = hSpanInc;
Adam Cohend4844c32011-02-18 19:25:06 -0800297 }
298
299 if (mTopBorderActive) {
Adam Cohenbebf0422012-04-11 18:06:28 -0700300 cellYInc = Math.max(-cellY, vSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800301 cellYInc = Math.min(lp.cellVSpan - mMinVSpan, cellYInc);
302 vSpanInc *= -1;
Adam Cohenbebf0422012-04-11 18:06:28 -0700303 vSpanInc = Math.min(cellY, vSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800304 vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
Adam Cohenbebf0422012-04-11 18:06:28 -0700305 vSpanDelta = -vSpanInc;
Adam Cohend4844c32011-02-18 19:25:06 -0800306 } else if (mBottomBorderActive) {
Adam Cohenbebf0422012-04-11 18:06:28 -0700307 vSpanInc = Math.min(countY - (cellY + spanY), vSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800308 vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
Adam Cohenbebf0422012-04-11 18:06:28 -0700309 vSpanDelta = vSpanInc;
Adam Cohend4844c32011-02-18 19:25:06 -0800310 }
311
Adam Cohenbebf0422012-04-11 18:06:28 -0700312 mDirectionVector[0] = 0;
313 mDirectionVector[1] = 0;
Adam Cohend4844c32011-02-18 19:25:06 -0800314 // Update the widget's dimensions and position according to the deltas computed above
315 if (mLeftBorderActive || mRightBorderActive) {
Adam Cohenbebf0422012-04-11 18:06:28 -0700316 spanX += hSpanInc;
317 cellX += cellXInc;
Adam Cohene0489502012-08-27 15:18:53 -0700318 if (hSpanDelta != 0) {
319 mDirectionVector[0] = mLeftBorderActive ? -1 : 1;
320 }
Adam Cohend4844c32011-02-18 19:25:06 -0800321 }
322
323 if (mTopBorderActive || mBottomBorderActive) {
Adam Cohenbebf0422012-04-11 18:06:28 -0700324 spanY += vSpanInc;
325 cellY += cellYInc;
Adam Cohene0489502012-08-27 15:18:53 -0700326 if (vSpanDelta != 0) {
327 mDirectionVector[1] = mTopBorderActive ? -1 : 1;
328 }
Adam Cohend4844c32011-02-18 19:25:06 -0800329 }
330
Adam Cohenbebf0422012-04-11 18:06:28 -0700331 if (!onDismiss && vSpanDelta == 0 && hSpanDelta == 0) return;
Adam Cohend4844c32011-02-18 19:25:06 -0800332
Adam Cohene0489502012-08-27 15:18:53 -0700333 // We always want the final commit to match the feedback, so we make sure to use the
334 // last used direction vector when committing the resize / reorder.
335 if (onDismiss) {
336 mDirectionVector[0] = mLastDirectionVector[0];
337 mDirectionVector[1] = mLastDirectionVector[1];
338 } else {
339 mLastDirectionVector[0] = mDirectionVector[0];
340 mLastDirectionVector[1] = mDirectionVector[1];
341 }
342
Adam Cohenbebf0422012-04-11 18:06:28 -0700343 if (mCellLayout.createAreaForResize(cellX, cellY, spanX, spanY, mWidgetView,
344 mDirectionVector, onDismiss)) {
Sunny Goyale78e3d72015-09-24 11:23:31 -0700345 if (mStateAnnouncer != null && (lp.cellHSpan != spanX || lp.cellVSpan != spanY) ) {
346 mStateAnnouncer.announce(
347 mLauncher.getString(R.string.widget_resized, spanX, spanY));
348 }
349
Adam Cohenbebf0422012-04-11 18:06:28 -0700350 lp.tmpCellX = cellX;
351 lp.tmpCellY = cellY;
352 lp.cellHSpan = spanX;
353 lp.cellVSpan = spanY;
354 mRunningVInc += vSpanDelta;
355 mRunningHInc += hSpanDelta;
Sunny Goyale78e3d72015-09-24 11:23:31 -0700356
Adam Cohena897f392012-04-27 18:12:05 -0700357 if (!onDismiss) {
358 updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY);
359 }
Adam Cohenbebf0422012-04-11 18:06:28 -0700360 }
Adam Cohen67882692011-03-11 15:29:03 -0800361 mWidgetView.requestLayout();
Adam Cohend4844c32011-02-18 19:25:06 -0800362 }
363
Adam Cohena897f392012-04-27 18:12:05 -0700364 static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
365 int spanX, int spanY) {
Sunny Goyalba776d52015-05-18 20:52:57 -0700366 getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect);
367 widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top,
368 sTmpRect.right, sTmpRect.bottom);
Adam Cohen9e05a5e2012-09-10 15:53:09 -0700369 }
370
Sunny Goyal16466f12016-03-10 05:34:30 -0800371 public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY, Rect rect) {
Sunny Goyal756cd262015-08-20 12:33:21 -0700372 if (sCellSize == null) {
373 InvariantDeviceProfile inv = LauncherAppState.getInstance().getInvariantDeviceProfile();
374
375 // Initiate cell sizes.
376 sCellSize = new Point[2];
377 sCellSize[0] = inv.landscapeProfile.getCellSize();
378 sCellSize[1] = inv.portraitProfile.getCellSize();
379 }
380
Adam Cohen9e05a5e2012-09-10 15:53:09 -0700381 if (rect == null) {
382 rect = new Rect();
383 }
Sunny Goyal16466f12016-03-10 05:34:30 -0800384 final float density = context.getResources().getDisplayMetrics().density;
Adam Cohena897f392012-04-27 18:12:05 -0700385
386 // Compute landscape size
Sunny Goyal756cd262015-08-20 12:33:21 -0700387 int landWidth = (int) ((spanX * sCellSize[0].x) / density);
388 int landHeight = (int) ((spanY * sCellSize[0].y) / density);
Adam Cohena897f392012-04-27 18:12:05 -0700389
390 // Compute portrait size
Sunny Goyal756cd262015-08-20 12:33:21 -0700391 int portWidth = (int) ((spanX * sCellSize[1].x) / density);
392 int portHeight = (int) ((spanY * sCellSize[1].y) / density);
Adam Cohen9e05a5e2012-09-10 15:53:09 -0700393 rect.set(portWidth, landHeight, landWidth, portHeight);
394 return rect;
Adam Cohena897f392012-04-27 18:12:05 -0700395 }
396
Adam Cohen1b607ed2011-03-03 17:26:50 -0800397 /**
398 * This is the final step of the resize. Here we save the new widget size and position
399 * to LauncherModel and animate the resize frame.
400 */
Adam Cohenbebf0422012-04-11 18:06:28 -0700401 public void commitResize() {
402 resizeWidgetIfNeeded(true);
403 requestLayout();
404 }
Adam Cohend4844c32011-02-18 19:25:06 -0800405
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700406 private void onTouchUp() {
Adam Cohenbebf0422012-04-11 18:06:28 -0700407 int xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
408 int yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
Adam Cohend4844c32011-02-18 19:25:06 -0800409
Adam Cohenbebf0422012-04-11 18:06:28 -0700410 mDeltaXAddOn = mRunningHInc * xThreshold;
411 mDeltaYAddOn = mRunningVInc * yThreshold;
412 mDeltaX = 0;
413 mDeltaY = 0;
414
Adam Cohend4844c32011-02-18 19:25:06 -0800415 post(new Runnable() {
Adam Cohenbebf0422012-04-11 18:06:28 -0700416 @Override
Adam Cohend4844c32011-02-18 19:25:06 -0800417 public void run() {
418 snapToWidget(true);
419 }
420 });
421 }
422
423 public void snapToWidget(boolean animate) {
Adam Cohen67882692011-03-11 15:29:03 -0800424 final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
Sunny Goyalba776d52015-05-18 20:52:57 -0700425 int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding
426 - mWidgetPadding.left - mWidgetPadding.right;
427 int newHeight = mWidgetView.getHeight() + 2 * mBackgroundPadding
428 - mWidgetPadding.top - mWidgetPadding.bottom;
Adam Cohen37b59ff2011-06-13 17:13:42 -0700429
Adam Cohend6e7aa32013-07-09 15:32:37 -0700430 mTmpPt[0] = mWidgetView.getLeft();
431 mTmpPt[1] = mWidgetView.getTop();
432 mDragLayer.getDescendantCoordRelativeToSelf(mCellLayout.getShortcutsAndWidgets(), mTmpPt);
433
Sunny Goyalba776d52015-05-18 20:52:57 -0700434 int newX = mTmpPt[0] - mBackgroundPadding + mWidgetPadding.left;
435 int newY = mTmpPt[1] - mBackgroundPadding + mWidgetPadding.top;
Adam Cohen3cba7222011-03-02 19:03:11 -0800436
Sunny Goyalba776d52015-05-18 20:52:57 -0700437 // We need to make sure the frame's touchable regions lie fully within the bounds of the
Adam Cohen4459d6b2012-07-13 15:59:15 -0700438 // DragLayer. We allow the actual handles to be clipped, but we shift the touch regions
439 // down accordingly to provide a proper touch target.
Adam Cohen3cba7222011-03-02 19:03:11 -0800440 if (newY < 0) {
Adam Cohen4459d6b2012-07-13 15:59:15 -0700441 // In this case we shift the touch region down to start at the top of the DragLayer
442 mTopTouchRegionAdjustment = -newY;
443 } else {
444 mTopTouchRegionAdjustment = 0;
Adam Cohen3cba7222011-03-02 19:03:11 -0800445 }
Adam Cohen67882692011-03-11 15:29:03 -0800446 if (newY + newHeight > mDragLayer.getHeight()) {
Adam Cohen4459d6b2012-07-13 15:59:15 -0700447 // In this case we shift the touch region up to end at the bottom of the DragLayer
448 mBottomTouchRegionAdjustment = -(newY + newHeight - mDragLayer.getHeight());
449 } else {
450 mBottomTouchRegionAdjustment = 0;
Adam Cohen3cba7222011-03-02 19:03:11 -0800451 }
452
Adam Cohend4844c32011-02-18 19:25:06 -0800453 if (!animate) {
454 lp.width = newWidth;
455 lp.height = newHeight;
456 lp.x = newX;
457 lp.y = newY;
458 mLeftHandle.setAlpha(1.0f);
459 mRightHandle.setAlpha(1.0f);
460 mTopHandle.setAlpha(1.0f);
461 mBottomHandle.setAlpha(1.0f);
462 requestLayout();
463 } else {
464 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", lp.width, newWidth);
Adam Cohen1b607ed2011-03-03 17:26:50 -0800465 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", lp.height,
466 newHeight);
Adam Cohend4844c32011-02-18 19:25:06 -0800467 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", lp.x, newX);
468 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
Michael Jurkaf1ad6082013-03-13 12:55:46 +0100469 ObjectAnimator oa =
470 LauncherAnimUtils.ofPropertyValuesHolder(lp, this, width, height, x, y);
Sunny Goyal5d2fc322015-07-06 22:52:49 -0700471 ObjectAnimator leftOa = LauncherAnimUtils.ofFloat(mLeftHandle, ALPHA, 1.0f);
472 ObjectAnimator rightOa = LauncherAnimUtils.ofFloat(mRightHandle, ALPHA, 1.0f);
473 ObjectAnimator topOa = LauncherAnimUtils.ofFloat(mTopHandle, ALPHA, 1.0f);
474 ObjectAnimator bottomOa = LauncherAnimUtils.ofFloat(mBottomHandle, ALPHA, 1.0f);
Adam Cohend4844c32011-02-18 19:25:06 -0800475 oa.addUpdateListener(new AnimatorUpdateListener() {
476 public void onAnimationUpdate(ValueAnimator animation) {
477 requestLayout();
478 }
479 });
Michael Jurka2ecf9952012-06-18 12:52:28 -0700480 AnimatorSet set = LauncherAnimUtils.createAnimatorSet();
Adam Cohend4844c32011-02-18 19:25:06 -0800481 if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
482 set.playTogether(oa, topOa, bottomOa);
483 } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
484 set.playTogether(oa, leftOa, rightOa);
485 } else {
486 set.playTogether(oa, leftOa, rightOa, topOa, bottomOa);
487 }
488
489 set.setDuration(SNAP_DURATION);
490 set.start();
491 }
Tony Wickham71255bb2016-02-10 16:18:15 -0800492
493 setFocusableInTouchMode(true);
494 requestFocus();
495 }
496
497 @Override
498 public boolean onKey(View v, int keyCode, KeyEvent event) {
499 // Clear the frame and give focus to the widget host view when a directional key is pressed.
500 if (FocusLogic.shouldConsume(keyCode)) {
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700501 mDragLayer.clearResizeFrame();
Tony Wickham71255bb2016-02-10 16:18:15 -0800502 mWidgetView.requestFocus();
503 return true;
504 }
505 return false;
Adam Cohend4844c32011-02-18 19:25:06 -0800506 }
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700507
508 private boolean handleTouchDown(MotionEvent ev) {
509 Rect hitRect = new Rect();
510 int x = (int) ev.getX();
511 int y = (int) ev.getY();
512
513 getHitRect(hitRect);
514 if (hitRect.contains(x, y)) {
515 if (beginResizeIfPointInRegion(x - getLeft(), y - getTop())) {
516 mXDown = x;
517 mYDown = y;
518 return true;
519 }
520 }
521 return false;
522 }
523
524 @Override
525 public boolean onControllerTouchEvent(MotionEvent ev) {
526 int action = ev.getAction();
527 int x = (int) ev.getX();
528 int y = (int) ev.getY();
529
530 switch (action) {
531 case MotionEvent.ACTION_DOWN:
532 return handleTouchDown(ev);
533 case MotionEvent.ACTION_MOVE:
534 visualizeResizeForDelta(x - mXDown, y - mYDown);
535 break;
536 case MotionEvent.ACTION_CANCEL:
537 case MotionEvent.ACTION_UP:
538 visualizeResizeForDelta(x - mXDown, y - mYDown);
539 onTouchUp();
540 mXDown = mYDown = 0;
541 break;
542 }
543 return true;
544 }
545
546 @Override
547 public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
548 if (ev.getAction() == MotionEvent.ACTION_DOWN && handleTouchDown(ev)) {
549 return true;
550 }
551 return false;
552 }
Adam Cohend4844c32011-02-18 19:25:06 -0800553}