blob: 8e2ffe923d00f67459a0e0d0bb66d707876151e2 [file] [log] [blame]
Daniel Sandler325dc232013-06-05 22:57:57 -04001package com.android.launcher3;
Adam Cohend4844c32011-02-18 19:25:06 -08002
Sunny Goyal55bdeed2018-09-18 16:17:22 -07003import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
4import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
5import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X;
6import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y;
7
Adam Cohend4844c32011-02-18 19:25:06 -08008import android.animation.AnimatorSet;
9import android.animation.ObjectAnimator;
10import android.animation.PropertyValuesHolder;
Adam Cohen0cf2a7c2011-11-08 15:07:01 -080011import android.appwidget.AppWidgetHostView;
Adam Cohend4844c32011-02-18 19:25:06 -080012import android.appwidget.AppWidgetProviderInfo;
13import android.content.Context;
Sunny Goyal756cd262015-08-20 12:33:21 -070014import android.graphics.Point;
Adam Cohen0cf2a7c2011-11-08 15:07:01 -080015import android.graphics.Rect;
Sunny Goyal08ca40f2016-09-13 21:07:31 -070016import android.util.AttributeSet;
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;
Sunny Goyal37920962017-09-28 13:43:24 -070020import android.view.ViewGroup;
Adam Cohend4844c32011-02-18 19:25:06 -080021
Sunny Goyale78e3d72015-09-24 11:23:31 -070022import com.android.launcher3.accessibility.DragViewStateAnnouncer;
Sunny Goyal08ca40f2016-09-13 21:07:31 -070023import com.android.launcher3.dragndrop.DragLayer;
Tony Wickham71255bb2016-02-10 16:18:15 -080024import com.android.launcher3.util.FocusLogic;
Sunny Goyal29947f02017-12-18 13:49:44 -080025import com.android.launcher3.widget.LauncherAppWidgetHostView;
Sunny Goyale78e3d72015-09-24 11:23:31 -070026
Sunny Goyal37920962017-09-28 13:43:24 -070027public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
Sunny Goyalba776d52015-05-18 20:52:57 -070028 private static final int SNAP_DURATION = 150;
29 private static final float DIMMED_HANDLE_ALPHA = 0f;
30 private static final float RESIZE_THRESHOLD = 0.66f;
31
Sunny Goyal756cd262015-08-20 12:33:21 -070032 private static final Rect sTmpRect = new Rect();
33
34 // Represents the cell size on the grid in the two orientations.
35 private static Point[] sCellSize;
Sunny Goyalba776d52015-05-18 20:52:57 -070036
Sunny Goyal08ca40f2016-09-13 21:07:31 -070037 private static final int HANDLE_COUNT = 4;
38 private static final int INDEX_LEFT = 0;
39 private static final int INDEX_TOP = 1;
40 private static final int INDEX_RIGHT = 2;
41 private static final int INDEX_BOTTOM = 3;
42
Sunny Goyalba776d52015-05-18 20:52:57 -070043 private final Launcher mLauncher;
Sunny Goyal08ca40f2016-09-13 21:07:31 -070044 private final DragViewStateAnnouncer mStateAnnouncer;
Sunny Goyalf0b6db72018-08-13 16:10:14 -070045 private final FirstFrameAnimatorHelper mFirstFrameAnimatorHelper;
Sunny Goyalba776d52015-05-18 20:52:57 -070046
Sunny Goyal08ca40f2016-09-13 21:07:31 -070047 private final View[] mDragHandles = new View[HANDLE_COUNT];
Sunny Goyalba776d52015-05-18 20:52:57 -070048
Sunny Goyal08ca40f2016-09-13 21:07:31 -070049 private LauncherAppWidgetHostView mWidgetView;
50 private CellLayout mCellLayout;
51 private DragLayer mDragLayer;
52
53 private Rect mWidgetPadding;
Sunny Goyalba776d52015-05-18 20:52:57 -070054
55 private final int mBackgroundPadding;
56 private final int mTouchTargetWidth;
57
58 private final int[] mDirectionVector = new int[2];
59 private final int[] mLastDirectionVector = new int[2];
Adam Cohend4844c32011-02-18 19:25:06 -080060
Sunny Goyal08ca40f2016-09-13 21:07:31 -070061 private final IntRange mTempRange1 = new IntRange();
62 private final IntRange mTempRange2 = new IntRange();
63
64 private final IntRange mDeltaXRange = new IntRange();
65 private final IntRange mBaselineX = new IntRange();
66
67 private final IntRange mDeltaYRange = new IntRange();
68 private final IntRange mBaselineY = new IntRange();
Sunny Goyale78e3d72015-09-24 11:23:31 -070069
Adam Cohend4844c32011-02-18 19:25:06 -080070 private boolean mLeftBorderActive;
71 private boolean mRightBorderActive;
72 private boolean mTopBorderActive;
73 private boolean mBottomBorderActive;
74
Adam Cohend4844c32011-02-18 19:25:06 -080075 private int mResizeMode;
Adam Cohen3cba7222011-03-02 19:03:11 -080076
Adam Cohend4844c32011-02-18 19:25:06 -080077 private int mRunningHInc;
78 private int mRunningVInc;
79 private int mMinHSpan;
80 private int mMinVSpan;
81 private int mDeltaX;
82 private int mDeltaY;
Adam Cohenbebf0422012-04-11 18:06:28 -070083 private int mDeltaXAddOn;
84 private int mDeltaYAddOn;
Adam Cohen1b607ed2011-03-03 17:26:50 -080085
Adam Cohen4459d6b2012-07-13 15:59:15 -070086 private int mTopTouchRegionAdjustment = 0;
87 private int mBottomTouchRegionAdjustment = 0;
88
Sunny Goyal6ad72f02016-09-23 11:01:10 -070089 private int mXDown, mYDown;
90
Sunny Goyal08ca40f2016-09-13 21:07:31 -070091 public AppWidgetResizeFrame(Context context) {
92 this(context, null);
93 }
Adam Cohend4844c32011-02-18 19:25:06 -080094
Sunny Goyal08ca40f2016-09-13 21:07:31 -070095 public AppWidgetResizeFrame(Context context, AttributeSet attrs) {
96 this(context, attrs, 0);
97 }
98
99 public AppWidgetResizeFrame(Context context, AttributeSet attrs, int defStyleAttr) {
100 super(context, attrs, defStyleAttr);
101
102 mLauncher = Launcher.getLauncher(context);
103 mStateAnnouncer = DragViewStateAnnouncer.createFor(this);
104
105 mBackgroundPadding = getResources()
106 .getDimensionPixelSize(R.dimen.resize_frame_background_padding);
107 mTouchTargetWidth = 2 * mBackgroundPadding;
Sunny Goyalf0b6db72018-08-13 16:10:14 -0700108 mFirstFrameAnimatorHelper = new FirstFrameAnimatorHelper(this);
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700109 }
110
111 @Override
112 protected void onFinishInflate() {
113 super.onFinishInflate();
114
Sunny Goyal37920962017-09-28 13:43:24 -0700115 ViewGroup content = (ViewGroup) getChildAt(0);
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700116 for (int i = 0; i < HANDLE_COUNT; i ++) {
Sunny Goyal37920962017-09-28 13:43:24 -0700117 mDragHandles[i] = content.getChildAt(i);
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700118 }
119 }
120
Sunny Goyal37920962017-09-28 13:43:24 -0700121 public static void showForWidget(LauncherAppWidgetHostView widget, CellLayout cellLayout) {
122 Launcher launcher = Launcher.getLauncher(cellLayout.getContext());
123 AbstractFloatingView.closeAllOpenViews(launcher);
124
125 DragLayer dl = launcher.getDragLayer();
126 AppWidgetResizeFrame frame = (AppWidgetResizeFrame) launcher.getLayoutInflater()
127 .inflate(R.layout.app_widget_resize_frame, dl, false);
128 frame.setupForWidget(widget, cellLayout, dl);
129 ((DragLayer.LayoutParams) frame.getLayoutParams()).customPosition = true;
130
131 dl.addView(frame);
132 frame.mIsOpen = true;
133 frame.snapToWidget(false);
134 }
135
136 private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700137 DragLayer dragLayer) {
Adam Cohend4844c32011-02-18 19:25:06 -0800138 mCellLayout = cellLayout;
139 mWidgetView = widgetView;
Adam Cohen59400422014-03-05 18:07:04 -0800140 LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
141 widgetView.getAppWidgetInfo();
142 mResizeMode = info.resizeMode;
Adam Cohen67882692011-03-11 15:29:03 -0800143 mDragLayer = dragLayer;
Adam Cohen3cba7222011-03-02 19:03:11 -0800144
Sunny Goyal233ee962015-08-03 13:05:01 -0700145 mMinHSpan = info.minSpanX;
146 mMinVSpan = info.minSpanY;
Adam Cohend4844c32011-02-18 19:25:06 -0800147
Sunny Goyal952e63d2017-08-16 04:59:08 -0700148 mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(),
149 widgetView.getAppWidgetInfo().provider, null);
Adam Cohen59400422014-03-05 18:07:04 -0800150
Adam Cohend4844c32011-02-18 19:25:06 -0800151 if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700152 mDragHandles[INDEX_TOP].setVisibility(GONE);
153 mDragHandles[INDEX_BOTTOM].setVisibility(GONE);
Adam Cohend4844c32011-02-18 19:25:06 -0800154 } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700155 mDragHandles[INDEX_LEFT].setVisibility(GONE);
156 mDragHandles[INDEX_RIGHT].setVisibility(GONE);
Adam Cohen3cba7222011-03-02 19:03:11 -0800157 }
158
Adam Cohenbebf0422012-04-11 18:06:28 -0700159 // When we create the resize frame, we first mark all cells as unoccupied. The appropriate
160 // cells (same if not resized, or different) will be marked as occupied when the resize
161 // frame is dismissed.
162 mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
Tony Wickham71255bb2016-02-10 16:18:15 -0800163
164 setOnKeyListener(this);
Adam Cohend4844c32011-02-18 19:25:06 -0800165 }
166
167 public boolean beginResizeIfPointInRegion(int x, int y) {
168 boolean horizontalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0;
169 boolean verticalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0;
Adam Cohen4459d6b2012-07-13 15:59:15 -0700170
Adam Cohen3cba7222011-03-02 19:03:11 -0800171 mLeftBorderActive = (x < mTouchTargetWidth) && horizontalActive;
172 mRightBorderActive = (x > getWidth() - mTouchTargetWidth) && horizontalActive;
Adam Cohen4459d6b2012-07-13 15:59:15 -0700173 mTopBorderActive = (y < mTouchTargetWidth + mTopTouchRegionAdjustment) && verticalActive;
174 mBottomBorderActive = (y > getHeight() - mTouchTargetWidth + mBottomTouchRegionAdjustment)
175 && verticalActive;
Adam Cohend4844c32011-02-18 19:25:06 -0800176
177 boolean anyBordersActive = mLeftBorderActive || mRightBorderActive
178 || mTopBorderActive || mBottomBorderActive;
179
Adam Cohend4844c32011-02-18 19:25:06 -0800180 if (anyBordersActive) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700181 mDragHandles[INDEX_LEFT].setAlpha(mLeftBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
182 mDragHandles[INDEX_RIGHT].setAlpha(mRightBorderActive ? 1.0f :DIMMED_HANDLE_ALPHA);
183 mDragHandles[INDEX_TOP].setAlpha(mTopBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
184 mDragHandles[INDEX_BOTTOM].setAlpha(mBottomBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
Adam Cohend4844c32011-02-18 19:25:06 -0800185 }
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700186
187 if (mLeftBorderActive) {
188 mDeltaXRange.set(-getLeft(), getWidth() - 2 * mTouchTargetWidth);
189 } else if (mRightBorderActive) {
190 mDeltaXRange.set(2 * mTouchTargetWidth - getWidth(), mDragLayer.getWidth() - getRight());
191 } else {
192 mDeltaXRange.set(0, 0);
193 }
194 mBaselineX.set(getLeft(), getRight());
195
196 if (mTopBorderActive) {
197 mDeltaYRange.set(-getTop(), getHeight() - 2 * mTouchTargetWidth);
198 } else if (mBottomBorderActive) {
199 mDeltaYRange.set(2 * mTouchTargetWidth - getHeight(), mDragLayer.getHeight() - getBottom());
200 } else {
201 mDeltaYRange.set(0, 0);
202 }
203 mBaselineY.set(getTop(), getBottom());
204
Adam Cohend4844c32011-02-18 19:25:06 -0800205 return anyBordersActive;
206 }
207
Adam Cohen1b607ed2011-03-03 17:26:50 -0800208 /**
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700209 * Based on the deltas, we resize the frame.
Adam Cohen1b607ed2011-03-03 17:26:50 -0800210 */
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700211 public void visualizeResizeForDelta(int deltaX, int deltaY) {
212 mDeltaX = mDeltaXRange.clamp(deltaX);
213 mDeltaY = mDeltaYRange.clamp(deltaY);
Adam Cohend4844c32011-02-18 19:25:06 -0800214
Adam Cohen67882692011-03-11 15:29:03 -0800215 DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700216 mDeltaX = mDeltaXRange.clamp(deltaX);
217 mBaselineX.applyDelta(mLeftBorderActive, mRightBorderActive, mDeltaX, mTempRange1);
218 lp.x = mTempRange1.start;
219 lp.width = mTempRange1.size();
Adam Cohen67882692011-03-11 15:29:03 -0800220
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700221 mDeltaY = mDeltaYRange.clamp(deltaY);
222 mBaselineY.applyDelta(mTopBorderActive, mBottomBorderActive, mDeltaY, mTempRange1);
223 lp.y = mTempRange1.start;
224 lp.height = mTempRange1.size();
Adam Cohend4844c32011-02-18 19:25:06 -0800225
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700226 resizeWidgetIfNeeded(false);
Jon Miranda097a7252016-12-28 10:49:55 -0800227
228 // When the widget resizes in multi-window mode, the translation value changes to maintain
229 // a center fit. These overrides ensure the resize frame always aligns with the widget view.
230 getSnappedRectRelativeToDragLayer(sTmpRect);
231 if (mLeftBorderActive) {
232 lp.width = sTmpRect.width() + sTmpRect.left - lp.x;
233 }
234 if (mTopBorderActive) {
235 lp.height = sTmpRect.height() + sTmpRect.top - lp.y;
236 }
237 if (mRightBorderActive) {
238 lp.x = sTmpRect.left;
239 }
240 if (mBottomBorderActive) {
241 lp.y = sTmpRect.top;
242 }
243
Adam Cohend4844c32011-02-18 19:25:06 -0800244 requestLayout();
245 }
246
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700247 private static int getSpanIncrement(float deltaFrac) {
248 return Math.abs(deltaFrac) > RESIZE_THRESHOLD ? Math.round(deltaFrac) : 0;
249 }
250
Adam Cohen1b607ed2011-03-03 17:26:50 -0800251 /**
252 * Based on the current deltas, we determine if and how to resize the widget.
253 */
Adam Cohenbebf0422012-04-11 18:06:28 -0700254 private void resizeWidgetIfNeeded(boolean onDismiss) {
Sunny Goyalaa8a8712016-11-20 15:26:01 +0530255 float xThreshold = mCellLayout.getCellWidth();
256 float yThreshold = mCellLayout.getCellHeight();
Adam Cohend4844c32011-02-18 19:25:06 -0800257
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700258 int hSpanInc = getSpanIncrement((mDeltaX + mDeltaXAddOn) / xThreshold - mRunningHInc);
259 int vSpanInc = getSpanIncrement((mDeltaY + mDeltaYAddOn) / yThreshold - mRunningVInc);
Adam Cohene4b77292011-03-08 18:35:52 -0800260
Adam Cohenbebf0422012-04-11 18:06:28 -0700261 if (!onDismiss && (hSpanInc == 0 && vSpanInc == 0)) return;
Adam Cohend4844c32011-02-18 19:25:06 -0800262
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700263 mDirectionVector[0] = 0;
264 mDirectionVector[1] = 0;
Adam Cohend4844c32011-02-18 19:25:06 -0800265
266 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
Adam Cohen1b607ed2011-03-03 17:26:50 -0800267
Adam Cohenbebf0422012-04-11 18:06:28 -0700268 int spanX = lp.cellHSpan;
269 int spanY = lp.cellVSpan;
270 int cellX = lp.useTmpCoords ? lp.tmpCellX : lp.cellX;
271 int cellY = lp.useTmpCoords ? lp.tmpCellY : lp.cellY;
272
Adam Cohen1b607ed2011-03-03 17:26:50 -0800273 // For each border, we bound the resizing based on the minimum width, and the maximum
274 // expandability.
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700275 mTempRange1.set(cellX, spanX + cellX);
276 int hSpanDelta = mTempRange1.applyDeltaAndBound(mLeftBorderActive, mRightBorderActive,
277 hSpanInc, mMinHSpan, mCellLayout.getCountX(), mTempRange2);
278 cellX = mTempRange2.start;
279 spanX = mTempRange2.size();
280 if (hSpanDelta != 0) {
281 mDirectionVector[0] = mLeftBorderActive ? -1 : 1;
Adam Cohend4844c32011-02-18 19:25:06 -0800282 }
283
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700284 mTempRange1.set(cellY, spanY + cellY);
285 int vSpanDelta = mTempRange1.applyDeltaAndBound(mTopBorderActive, mBottomBorderActive,
286 vSpanInc, mMinVSpan, mCellLayout.getCountY(), mTempRange2);
287 cellY = mTempRange2.start;
288 spanY = mTempRange2.size();
289 if (vSpanDelta != 0) {
290 mDirectionVector[1] = mTopBorderActive ? -1 : 1;
Adam Cohend4844c32011-02-18 19:25:06 -0800291 }
292
Adam Cohenbebf0422012-04-11 18:06:28 -0700293 if (!onDismiss && vSpanDelta == 0 && hSpanDelta == 0) return;
Adam Cohend4844c32011-02-18 19:25:06 -0800294
Adam Cohene0489502012-08-27 15:18:53 -0700295 // We always want the final commit to match the feedback, so we make sure to use the
296 // last used direction vector when committing the resize / reorder.
297 if (onDismiss) {
298 mDirectionVector[0] = mLastDirectionVector[0];
299 mDirectionVector[1] = mLastDirectionVector[1];
300 } else {
301 mLastDirectionVector[0] = mDirectionVector[0];
302 mLastDirectionVector[1] = mDirectionVector[1];
303 }
304
Adam Cohenbebf0422012-04-11 18:06:28 -0700305 if (mCellLayout.createAreaForResize(cellX, cellY, spanX, spanY, mWidgetView,
306 mDirectionVector, onDismiss)) {
Sunny Goyale78e3d72015-09-24 11:23:31 -0700307 if (mStateAnnouncer != null && (lp.cellHSpan != spanX || lp.cellVSpan != spanY) ) {
308 mStateAnnouncer.announce(
309 mLauncher.getString(R.string.widget_resized, spanX, spanY));
310 }
311
Adam Cohenbebf0422012-04-11 18:06:28 -0700312 lp.tmpCellX = cellX;
313 lp.tmpCellY = cellY;
314 lp.cellHSpan = spanX;
315 lp.cellVSpan = spanY;
316 mRunningVInc += vSpanDelta;
317 mRunningHInc += hSpanDelta;
Sunny Goyale78e3d72015-09-24 11:23:31 -0700318
Adam Cohena897f392012-04-27 18:12:05 -0700319 if (!onDismiss) {
320 updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY);
321 }
Adam Cohenbebf0422012-04-11 18:06:28 -0700322 }
Adam Cohen67882692011-03-11 15:29:03 -0800323 mWidgetView.requestLayout();
Adam Cohend4844c32011-02-18 19:25:06 -0800324 }
325
Adam Cohena897f392012-04-27 18:12:05 -0700326 static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
327 int spanX, int spanY) {
Sunny Goyalba776d52015-05-18 20:52:57 -0700328 getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect);
329 widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top,
330 sTmpRect.right, sTmpRect.bottom);
Adam Cohen9e05a5e2012-09-10 15:53:09 -0700331 }
332
Sunny Goyal16466f12016-03-10 05:34:30 -0800333 public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY, Rect rect) {
Sunny Goyal756cd262015-08-20 12:33:21 -0700334 if (sCellSize == null) {
Sunny Goyal87f784c2017-01-11 10:48:34 -0800335 InvariantDeviceProfile inv = LauncherAppState.getIDP(context);
Sunny Goyal756cd262015-08-20 12:33:21 -0700336
337 // Initiate cell sizes.
338 sCellSize = new Point[2];
339 sCellSize[0] = inv.landscapeProfile.getCellSize();
340 sCellSize[1] = inv.portraitProfile.getCellSize();
341 }
342
Adam Cohen9e05a5e2012-09-10 15:53:09 -0700343 if (rect == null) {
344 rect = new Rect();
345 }
Sunny Goyal16466f12016-03-10 05:34:30 -0800346 final float density = context.getResources().getDisplayMetrics().density;
Adam Cohena897f392012-04-27 18:12:05 -0700347
348 // Compute landscape size
Sunny Goyal756cd262015-08-20 12:33:21 -0700349 int landWidth = (int) ((spanX * sCellSize[0].x) / density);
350 int landHeight = (int) ((spanY * sCellSize[0].y) / density);
Adam Cohena897f392012-04-27 18:12:05 -0700351
352 // Compute portrait size
Sunny Goyal756cd262015-08-20 12:33:21 -0700353 int portWidth = (int) ((spanX * sCellSize[1].x) / density);
354 int portHeight = (int) ((spanY * sCellSize[1].y) / density);
Adam Cohen9e05a5e2012-09-10 15:53:09 -0700355 rect.set(portWidth, landHeight, landWidth, portHeight);
356 return rect;
Adam Cohena897f392012-04-27 18:12:05 -0700357 }
358
Sunny Goyal988ad272017-08-18 02:05:01 -0700359 @Override
360 protected void onDetachedFromWindow() {
361 super.onDetachedFromWindow();
362
363 // We are done with resizing the widget. Save the widget size & position to LauncherModel
Adam Cohenbebf0422012-04-11 18:06:28 -0700364 resizeWidgetIfNeeded(true);
Adam Cohenbebf0422012-04-11 18:06:28 -0700365 }
Adam Cohend4844c32011-02-18 19:25:06 -0800366
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700367 private void onTouchUp() {
Sunny Goyalaa8a8712016-11-20 15:26:01 +0530368 int xThreshold = mCellLayout.getCellWidth();
369 int yThreshold = mCellLayout.getCellHeight();
Adam Cohend4844c32011-02-18 19:25:06 -0800370
Jon Miranda097a7252016-12-28 10:49:55 -0800371 mDeltaXAddOn = mRunningHInc * xThreshold;
372 mDeltaYAddOn = mRunningVInc * yThreshold;
Adam Cohenbebf0422012-04-11 18:06:28 -0700373 mDeltaX = 0;
374 mDeltaY = 0;
375
Sunny Goyalf0b6db72018-08-13 16:10:14 -0700376 post(() -> snapToWidget(true));
Adam Cohend4844c32011-02-18 19:25:06 -0800377 }
378
Jon Miranda097a7252016-12-28 10:49:55 -0800379 /**
380 * Returns the rect of this view when the frame is snapped around the widget, with the bounds
381 * relative to the {@link DragLayer}.
382 */
383 private void getSnappedRectRelativeToDragLayer(Rect out) {
Jon Miranda21266912016-12-19 14:12:05 -0800384 float scale = mWidgetView.getScaleToFit();
Adam Cohen37b59ff2011-06-13 17:13:42 -0700385
Jon Miranda097a7252016-12-28 10:49:55 -0800386 mDragLayer.getViewRectRelativeToSelf(mWidgetView, out);
Adam Cohend6e7aa32013-07-09 15:32:37 -0700387
Jon Miranda097a7252016-12-28 10:49:55 -0800388 int width = 2 * mBackgroundPadding
389 + (int) (scale * (out.width() - mWidgetPadding.left - mWidgetPadding.right));
390 int height = 2 * mBackgroundPadding
391 + (int) (scale * (out.height() - mWidgetPadding.top - mWidgetPadding.bottom));
Jon Miranda26e4d172016-12-12 15:43:18 -0800392
Jon Miranda097a7252016-12-28 10:49:55 -0800393 int x = (int) (out.left - mBackgroundPadding + scale * mWidgetPadding.left);
394 int y = (int) (out.top - mBackgroundPadding + scale * mWidgetPadding.top);
395
396 out.left = x;
397 out.top = y;
398 out.right = out.left + width;
399 out.bottom = out.top + height;
400 }
401
Sunny Goyal37920962017-09-28 13:43:24 -0700402 private void snapToWidget(boolean animate) {
Jon Miranda097a7252016-12-28 10:49:55 -0800403 getSnappedRectRelativeToDragLayer(sTmpRect);
404 int newWidth = sTmpRect.width();
405 int newHeight = sTmpRect.height();
406 int newX = sTmpRect.left;
407 int newY = sTmpRect.top;
Adam Cohen3cba7222011-03-02 19:03:11 -0800408
Sunny Goyalba776d52015-05-18 20:52:57 -0700409 // We need to make sure the frame's touchable regions lie fully within the bounds of the
Adam Cohen4459d6b2012-07-13 15:59:15 -0700410 // DragLayer. We allow the actual handles to be clipped, but we shift the touch regions
411 // down accordingly to provide a proper touch target.
Adam Cohen3cba7222011-03-02 19:03:11 -0800412 if (newY < 0) {
Adam Cohen4459d6b2012-07-13 15:59:15 -0700413 // In this case we shift the touch region down to start at the top of the DragLayer
414 mTopTouchRegionAdjustment = -newY;
415 } else {
416 mTopTouchRegionAdjustment = 0;
Adam Cohen3cba7222011-03-02 19:03:11 -0800417 }
Adam Cohen67882692011-03-11 15:29:03 -0800418 if (newY + newHeight > mDragLayer.getHeight()) {
Adam Cohen4459d6b2012-07-13 15:59:15 -0700419 // In this case we shift the touch region up to end at the bottom of the DragLayer
420 mBottomTouchRegionAdjustment = -(newY + newHeight - mDragLayer.getHeight());
421 } else {
422 mBottomTouchRegionAdjustment = 0;
Adam Cohen3cba7222011-03-02 19:03:11 -0800423 }
424
Jon Miranda26e4d172016-12-12 15:43:18 -0800425 final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
Adam Cohend4844c32011-02-18 19:25:06 -0800426 if (!animate) {
427 lp.width = newWidth;
428 lp.height = newHeight;
429 lp.x = newX;
430 lp.y = newY;
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700431 for (int i = 0; i < HANDLE_COUNT; i++) {
432 mDragHandles[i].setAlpha(1.0f);
433 }
Adam Cohend4844c32011-02-18 19:25:06 -0800434 requestLayout();
435 } else {
Sunny Goyalf0b6db72018-08-13 16:10:14 -0700436 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp,
Sunny Goyal55bdeed2018-09-18 16:17:22 -0700437 PropertyValuesHolder.ofInt(LAYOUT_WIDTH, lp.width, newWidth),
438 PropertyValuesHolder.ofInt(LAYOUT_HEIGHT, lp.height, newHeight),
439 PropertyValuesHolder.ofInt(LAYOUT_X, lp.x, newX),
440 PropertyValuesHolder.ofInt(LAYOUT_Y, lp.y, newY));
Sunny Goyalf0b6db72018-08-13 16:10:14 -0700441 mFirstFrameAnimatorHelper.addTo(oa).addUpdateListener(a -> requestLayout());
Sunny Goyal849c6a22018-08-08 16:33:46 -0700442
443 AnimatorSet set = new AnimatorSet();
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700444 set.play(oa);
445 for (int i = 0; i < HANDLE_COUNT; i++) {
Sunny Goyalf0b6db72018-08-13 16:10:14 -0700446 set.play(mFirstFrameAnimatorHelper.addTo(
447 ObjectAnimator.ofFloat(mDragHandles[i], ALPHA, 1f)));
Adam Cohend4844c32011-02-18 19:25:06 -0800448 }
Adam Cohend4844c32011-02-18 19:25:06 -0800449 set.setDuration(SNAP_DURATION);
450 set.start();
451 }
Tony Wickham71255bb2016-02-10 16:18:15 -0800452
453 setFocusableInTouchMode(true);
454 requestFocus();
455 }
456
457 @Override
458 public boolean onKey(View v, int keyCode, KeyEvent event) {
459 // Clear the frame and give focus to the widget host view when a directional key is pressed.
460 if (FocusLogic.shouldConsume(keyCode)) {
Sunny Goyal37920962017-09-28 13:43:24 -0700461 close(false);
Tony Wickham71255bb2016-02-10 16:18:15 -0800462 mWidgetView.requestFocus();
463 return true;
464 }
465 return false;
Adam Cohend4844c32011-02-18 19:25:06 -0800466 }
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700467
468 private boolean handleTouchDown(MotionEvent ev) {
469 Rect hitRect = new Rect();
470 int x = (int) ev.getX();
471 int y = (int) ev.getY();
472
473 getHitRect(hitRect);
474 if (hitRect.contains(x, y)) {
475 if (beginResizeIfPointInRegion(x - getLeft(), y - getTop())) {
476 mXDown = x;
477 mYDown = y;
478 return true;
479 }
480 }
481 return false;
482 }
483
484 @Override
485 public boolean onControllerTouchEvent(MotionEvent ev) {
486 int action = ev.getAction();
487 int x = (int) ev.getX();
488 int y = (int) ev.getY();
489
490 switch (action) {
491 case MotionEvent.ACTION_DOWN:
492 return handleTouchDown(ev);
493 case MotionEvent.ACTION_MOVE:
494 visualizeResizeForDelta(x - mXDown, y - mYDown);
495 break;
496 case MotionEvent.ACTION_CANCEL:
497 case MotionEvent.ACTION_UP:
498 visualizeResizeForDelta(x - mXDown, y - mYDown);
499 onTouchUp();
500 mXDown = mYDown = 0;
501 break;
502 }
503 return true;
504 }
505
506 @Override
507 public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
508 if (ev.getAction() == MotionEvent.ACTION_DOWN && handleTouchDown(ev)) {
509 return true;
510 }
Sunny Goyal37920962017-09-28 13:43:24 -0700511 close(false);
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700512 return false;
513 }
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700514
Sunny Goyal37920962017-09-28 13:43:24 -0700515 @Override
516 protected void handleClose(boolean animate) {
517 mDragLayer.removeView(this);
518 }
519
520 @Override
521 public void logActionCommand(int command) {
522 // TODO: Log this case.
523 }
524
525 @Override
526 protected boolean isOfType(int type) {
527 return (type & TYPE_WIDGET_RESIZE_FRAME) != 0;
528 }
529
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700530 /**
531 * A mutable class for describing the range of two int values.
532 */
533 private static class IntRange {
534
535 public int start, end;
536
537 public int clamp(int value) {
538 return Utilities.boundToRange(value, start, end);
539 }
540
541 public void set(int s, int e) {
542 start = s;
543 end = e;
544 }
545
546 public int size() {
547 return end - start;
548 }
549
550 /**
551 * Moves either the start or end edge (but never both) by {@param delta} and sets the
552 * result in {@param out}
553 */
554 public void applyDelta(boolean moveStart, boolean moveEnd, int delta, IntRange out) {
555 out.start = moveStart ? start + delta : start;
556 out.end = moveEnd ? end + delta : end;
557 }
558
559 /**
560 * Applies delta similar to {@link #applyDelta(boolean, boolean, int, IntRange)},
561 * with extra conditions.
562 * @param minSize minimum size after with the moving edge should not be shifted any further.
563 * For eg, if delta = -3 when moving the endEdge brings the size to less than
564 * minSize, only delta = -2 will applied
565 * @param maxEnd The maximum value to the end edge (start edge is always restricted to 0)
566 * @return the amount of increase when endEdge was moves and the amount of decrease when
567 * the start edge was moved.
568 */
569 public int applyDeltaAndBound(boolean moveStart, boolean moveEnd, int delta,
570 int minSize, int maxEnd, IntRange out) {
571 applyDelta(moveStart, moveEnd, delta, out);
Jon Miranda9f33cb22017-01-03 09:30:00 -0800572 if (out.start < 0) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700573 out.start = 0;
574 }
Jon Miranda9f33cb22017-01-03 09:30:00 -0800575 if (out.end > maxEnd) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700576 out.end = maxEnd;
577 }
578 if (out.size() < minSize) {
579 if (moveStart) {
580 out.start = out.end - minSize;
581 } else if (moveEnd) {
582 out.end = out.start + minSize;
583 }
584 }
585 return moveEnd ? out.size() - size() : size() - out.size();
586 }
587 }
Adam Cohend4844c32011-02-18 19:25:06 -0800588}