blob: f8792163c7216e15117fdfd4044c4a4e73ce68c8 [file] [log] [blame]
Daniel Sandler325dc232013-06-05 22:57:57 -04001package com.android.launcher3;
Adam Cohend4844c32011-02-18 19:25:06 -08002
3import android.animation.AnimatorSet;
4import android.animation.ObjectAnimator;
5import android.animation.PropertyValuesHolder;
6import android.animation.ValueAnimator;
7import android.animation.ValueAnimator.AnimatorUpdateListener;
Adam Cohen0cf2a7c2011-11-08 15:07:01 -08008import android.appwidget.AppWidgetHostView;
Adam Cohend4844c32011-02-18 19:25:06 -08009import android.appwidget.AppWidgetProviderInfo;
10import android.content.Context;
Adam Cohen59400422014-03-05 18:07:04 -080011import android.content.res.Resources;
Sunny Goyal756cd262015-08-20 12:33:21 -070012import android.graphics.Point;
Adam Cohen0cf2a7c2011-11-08 15:07:01 -080013import android.graphics.Rect;
Sunny Goyal08ca40f2016-09-13 21:07:31 -070014import android.util.AttributeSet;
Tony Wickham71255bb2016-02-10 16:18:15 -080015import android.view.KeyEvent;
Sunny Goyal6ad72f02016-09-23 11:01:10 -070016import android.view.MotionEvent;
Tony Wickham71255bb2016-02-10 16:18:15 -080017import android.view.View;
Adam Cohend4844c32011-02-18 19:25:06 -080018import android.widget.FrameLayout;
Adam Cohend4844c32011-02-18 19:25:06 -080019
Sunny Goyale78e3d72015-09-24 11:23:31 -070020import com.android.launcher3.accessibility.DragViewStateAnnouncer;
Sunny Goyal08ca40f2016-09-13 21:07:31 -070021import com.android.launcher3.dragndrop.DragLayer;
Tony Wickham71255bb2016-02-10 16:18:15 -080022import com.android.launcher3.util.FocusLogic;
Sunny Goyal6ad72f02016-09-23 11:01:10 -070023import com.android.launcher3.util.TouchController;
Sunny Goyale78e3d72015-09-24 11:23:31 -070024
Sunny Goyal6ad72f02016-09-23 11:01:10 -070025public class AppWidgetResizeFrame extends FrameLayout
26 implements View.OnKeyListener, TouchController {
Sunny Goyalba776d52015-05-18 20:52:57 -070027 private static final int SNAP_DURATION = 150;
28 private static final float DIMMED_HANDLE_ALPHA = 0f;
29 private static final float RESIZE_THRESHOLD = 0.66f;
30
Sunny Goyal756cd262015-08-20 12:33:21 -070031 private static final Rect sTmpRect = new Rect();
32
33 // Represents the cell size on the grid in the two orientations.
34 private static Point[] sCellSize;
Sunny Goyalba776d52015-05-18 20:52:57 -070035
Sunny Goyal08ca40f2016-09-13 21:07:31 -070036 private static final int HANDLE_COUNT = 4;
37 private static final int INDEX_LEFT = 0;
38 private static final int INDEX_TOP = 1;
39 private static final int INDEX_RIGHT = 2;
40 private static final int INDEX_BOTTOM = 3;
41
Sunny Goyalba776d52015-05-18 20:52:57 -070042 private final Launcher mLauncher;
Sunny Goyal08ca40f2016-09-13 21:07:31 -070043 private final DragViewStateAnnouncer mStateAnnouncer;
Sunny Goyalba776d52015-05-18 20:52:57 -070044
Sunny Goyal08ca40f2016-09-13 21:07:31 -070045 private final View[] mDragHandles = new View[HANDLE_COUNT];
Sunny Goyalba776d52015-05-18 20:52:57 -070046
Sunny Goyal08ca40f2016-09-13 21:07:31 -070047 private LauncherAppWidgetHostView mWidgetView;
48 private CellLayout mCellLayout;
49 private DragLayer mDragLayer;
50
51 private Rect mWidgetPadding;
Sunny Goyalba776d52015-05-18 20:52:57 -070052
53 private final int mBackgroundPadding;
54 private final int mTouchTargetWidth;
55
56 private final int[] mDirectionVector = new int[2];
57 private final int[] mLastDirectionVector = new int[2];
58 private final int[] mTmpPt = new int[2];
Adam Cohend4844c32011-02-18 19:25:06 -080059
Sunny Goyal08ca40f2016-09-13 21:07:31 -070060 private final IntRange mTempRange1 = new IntRange();
61 private final IntRange mTempRange2 = new IntRange();
62
63 private final IntRange mDeltaXRange = new IntRange();
64 private final IntRange mBaselineX = new IntRange();
65
66 private final IntRange mDeltaYRange = new IntRange();
67 private final IntRange mBaselineY = new IntRange();
Sunny Goyale78e3d72015-09-24 11:23:31 -070068
Adam Cohend4844c32011-02-18 19:25:06 -080069 private boolean mLeftBorderActive;
70 private boolean mRightBorderActive;
71 private boolean mTopBorderActive;
72 private boolean mBottomBorderActive;
73
Adam Cohend4844c32011-02-18 19:25:06 -080074 private int mResizeMode;
Adam Cohen3cba7222011-03-02 19:03:11 -080075
Adam Cohend4844c32011-02-18 19:25:06 -080076 private int mRunningHInc;
77 private int mRunningVInc;
78 private int mMinHSpan;
79 private int mMinVSpan;
80 private int mDeltaX;
81 private int mDeltaY;
Adam Cohenbebf0422012-04-11 18:06:28 -070082 private int mDeltaXAddOn;
83 private int mDeltaYAddOn;
Adam Cohen1b607ed2011-03-03 17:26:50 -080084
Adam Cohen4459d6b2012-07-13 15:59:15 -070085 private int mTopTouchRegionAdjustment = 0;
86 private int mBottomTouchRegionAdjustment = 0;
87
Sunny Goyal6ad72f02016-09-23 11:01:10 -070088 private int mXDown, mYDown;
89
Sunny Goyal08ca40f2016-09-13 21:07:31 -070090 public AppWidgetResizeFrame(Context context) {
91 this(context, null);
92 }
Adam Cohend4844c32011-02-18 19:25:06 -080093
Sunny Goyal08ca40f2016-09-13 21:07:31 -070094 public AppWidgetResizeFrame(Context context, AttributeSet attrs) {
95 this(context, attrs, 0);
96 }
97
98 public AppWidgetResizeFrame(Context context, AttributeSet attrs, int defStyleAttr) {
99 super(context, attrs, defStyleAttr);
100
101 mLauncher = Launcher.getLauncher(context);
102 mStateAnnouncer = DragViewStateAnnouncer.createFor(this);
103
104 mBackgroundPadding = getResources()
105 .getDimensionPixelSize(R.dimen.resize_frame_background_padding);
106 mTouchTargetWidth = 2 * mBackgroundPadding;
107 }
108
109 @Override
110 protected void onFinishInflate() {
111 super.onFinishInflate();
112
113 for (int i = 0; i < HANDLE_COUNT; i ++) {
114 mDragHandles[i] = getChildAt(i);
115 }
116 }
117
118 public void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
119 DragLayer dragLayer) {
Adam Cohend4844c32011-02-18 19:25:06 -0800120 mCellLayout = cellLayout;
121 mWidgetView = widgetView;
Adam Cohen59400422014-03-05 18:07:04 -0800122 LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
123 widgetView.getAppWidgetInfo();
124 mResizeMode = info.resizeMode;
Adam Cohen67882692011-03-11 15:29:03 -0800125 mDragLayer = dragLayer;
Adam Cohen3cba7222011-03-02 19:03:11 -0800126
Sunny Goyal233ee962015-08-03 13:05:01 -0700127 mMinHSpan = info.minSpanX;
128 mMinVSpan = info.minSpanY;
Adam Cohend4844c32011-02-18 19:25:06 -0800129
Adam Cohen59400422014-03-05 18:07:04 -0800130 if (!info.isCustomWidget) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700131 mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(),
Adam Cohen59400422014-03-05 18:07:04 -0800132 widgetView.getAppWidgetInfo().provider, null);
133 } else {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700134 Resources r = getContext().getResources();
Adam Cohen59400422014-03-05 18:07:04 -0800135 int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding);
Sunny Goyalba776d52015-05-18 20:52:57 -0700136 mWidgetPadding = new Rect(padding, padding, padding, padding);
Adam Cohen59400422014-03-05 18:07:04 -0800137 }
138
Adam Cohend4844c32011-02-18 19:25:06 -0800139 if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700140 mDragHandles[INDEX_TOP].setVisibility(GONE);
141 mDragHandles[INDEX_BOTTOM].setVisibility(GONE);
Adam Cohend4844c32011-02-18 19:25:06 -0800142 } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700143 mDragHandles[INDEX_LEFT].setVisibility(GONE);
144 mDragHandles[INDEX_RIGHT].setVisibility(GONE);
Adam Cohen3cba7222011-03-02 19:03:11 -0800145 }
146
Adam Cohenbebf0422012-04-11 18:06:28 -0700147 // When we create the resize frame, we first mark all cells as unoccupied. The appropriate
148 // cells (same if not resized, or different) will be marked as occupied when the resize
149 // frame is dismissed.
150 mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
Tony Wickham71255bb2016-02-10 16:18:15 -0800151
152 setOnKeyListener(this);
Adam Cohend4844c32011-02-18 19:25:06 -0800153 }
154
155 public boolean beginResizeIfPointInRegion(int x, int y) {
156 boolean horizontalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0;
157 boolean verticalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0;
Adam Cohen4459d6b2012-07-13 15:59:15 -0700158
Adam Cohen3cba7222011-03-02 19:03:11 -0800159 mLeftBorderActive = (x < mTouchTargetWidth) && horizontalActive;
160 mRightBorderActive = (x > getWidth() - mTouchTargetWidth) && horizontalActive;
Adam Cohen4459d6b2012-07-13 15:59:15 -0700161 mTopBorderActive = (y < mTouchTargetWidth + mTopTouchRegionAdjustment) && verticalActive;
162 mBottomBorderActive = (y > getHeight() - mTouchTargetWidth + mBottomTouchRegionAdjustment)
163 && verticalActive;
Adam Cohend4844c32011-02-18 19:25:06 -0800164
165 boolean anyBordersActive = mLeftBorderActive || mRightBorderActive
166 || mTopBorderActive || mBottomBorderActive;
167
Adam Cohend4844c32011-02-18 19:25:06 -0800168 if (anyBordersActive) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700169 mDragHandles[INDEX_LEFT].setAlpha(mLeftBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
170 mDragHandles[INDEX_RIGHT].setAlpha(mRightBorderActive ? 1.0f :DIMMED_HANDLE_ALPHA);
171 mDragHandles[INDEX_TOP].setAlpha(mTopBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
172 mDragHandles[INDEX_BOTTOM].setAlpha(mBottomBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
Adam Cohend4844c32011-02-18 19:25:06 -0800173 }
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700174
175 if (mLeftBorderActive) {
176 mDeltaXRange.set(-getLeft(), getWidth() - 2 * mTouchTargetWidth);
177 } else if (mRightBorderActive) {
178 mDeltaXRange.set(2 * mTouchTargetWidth - getWidth(), mDragLayer.getWidth() - getRight());
179 } else {
180 mDeltaXRange.set(0, 0);
181 }
182 mBaselineX.set(getLeft(), getRight());
183
184 if (mTopBorderActive) {
185 mDeltaYRange.set(-getTop(), getHeight() - 2 * mTouchTargetWidth);
186 } else if (mBottomBorderActive) {
187 mDeltaYRange.set(2 * mTouchTargetWidth - getHeight(), mDragLayer.getHeight() - getBottom());
188 } else {
189 mDeltaYRange.set(0, 0);
190 }
191 mBaselineY.set(getTop(), getBottom());
192
Adam Cohend4844c32011-02-18 19:25:06 -0800193 return anyBordersActive;
194 }
195
Adam Cohen1b607ed2011-03-03 17:26:50 -0800196 /**
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700197 * Based on the deltas, we resize the frame.
Adam Cohen1b607ed2011-03-03 17:26:50 -0800198 */
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700199 public void visualizeResizeForDelta(int deltaX, int deltaY) {
200 mDeltaX = mDeltaXRange.clamp(deltaX);
201 mDeltaY = mDeltaYRange.clamp(deltaY);
Adam Cohend4844c32011-02-18 19:25:06 -0800202
Adam Cohen67882692011-03-11 15:29:03 -0800203 DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700204 mDeltaX = mDeltaXRange.clamp(deltaX);
205 mBaselineX.applyDelta(mLeftBorderActive, mRightBorderActive, mDeltaX, mTempRange1);
206 lp.x = mTempRange1.start;
207 lp.width = mTempRange1.size();
Adam Cohen67882692011-03-11 15:29:03 -0800208
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700209 mDeltaY = mDeltaYRange.clamp(deltaY);
210 mBaselineY.applyDelta(mTopBorderActive, mBottomBorderActive, mDeltaY, mTempRange1);
211 lp.y = mTempRange1.start;
212 lp.height = mTempRange1.size();
Adam Cohend4844c32011-02-18 19:25:06 -0800213
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700214 resizeWidgetIfNeeded(false);
Adam Cohend4844c32011-02-18 19:25:06 -0800215 requestLayout();
216 }
217
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700218 private static int getSpanIncrement(float deltaFrac) {
219 return Math.abs(deltaFrac) > RESIZE_THRESHOLD ? Math.round(deltaFrac) : 0;
220 }
221
Adam Cohen1b607ed2011-03-03 17:26:50 -0800222 /**
223 * Based on the current deltas, we determine if and how to resize the widget.
224 */
Adam Cohenbebf0422012-04-11 18:06:28 -0700225 private void resizeWidgetIfNeeded(boolean onDismiss) {
Sunny Goyalaa8a8712016-11-20 15:26:01 +0530226 float xThreshold = mCellLayout.getCellWidth();
227 float yThreshold = mCellLayout.getCellHeight();
Adam Cohend4844c32011-02-18 19:25:06 -0800228
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700229 int hSpanInc = getSpanIncrement((mDeltaX + mDeltaXAddOn) / xThreshold - mRunningHInc);
230 int vSpanInc = getSpanIncrement((mDeltaY + mDeltaYAddOn) / yThreshold - mRunningVInc);
Adam Cohene4b77292011-03-08 18:35:52 -0800231
Adam Cohenbebf0422012-04-11 18:06:28 -0700232 if (!onDismiss && (hSpanInc == 0 && vSpanInc == 0)) return;
Adam Cohend4844c32011-02-18 19:25:06 -0800233
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700234 mDirectionVector[0] = 0;
235 mDirectionVector[1] = 0;
Adam Cohend4844c32011-02-18 19:25:06 -0800236
237 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
Adam Cohen1b607ed2011-03-03 17:26:50 -0800238
Adam Cohenbebf0422012-04-11 18:06:28 -0700239 int spanX = lp.cellHSpan;
240 int spanY = lp.cellVSpan;
241 int cellX = lp.useTmpCoords ? lp.tmpCellX : lp.cellX;
242 int cellY = lp.useTmpCoords ? lp.tmpCellY : lp.cellY;
243
Adam Cohen1b607ed2011-03-03 17:26:50 -0800244 // For each border, we bound the resizing based on the minimum width, and the maximum
245 // expandability.
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700246 mTempRange1.set(cellX, spanX + cellX);
247 int hSpanDelta = mTempRange1.applyDeltaAndBound(mLeftBorderActive, mRightBorderActive,
248 hSpanInc, mMinHSpan, mCellLayout.getCountX(), mTempRange2);
249 cellX = mTempRange2.start;
250 spanX = mTempRange2.size();
251 if (hSpanDelta != 0) {
252 mDirectionVector[0] = mLeftBorderActive ? -1 : 1;
Adam Cohend4844c32011-02-18 19:25:06 -0800253 }
254
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700255 mTempRange1.set(cellY, spanY + cellY);
256 int vSpanDelta = mTempRange1.applyDeltaAndBound(mTopBorderActive, mBottomBorderActive,
257 vSpanInc, mMinVSpan, mCellLayout.getCountY(), mTempRange2);
258 cellY = mTempRange2.start;
259 spanY = mTempRange2.size();
260 if (vSpanDelta != 0) {
261 mDirectionVector[1] = mTopBorderActive ? -1 : 1;
Adam Cohend4844c32011-02-18 19:25:06 -0800262 }
263
Adam Cohenbebf0422012-04-11 18:06:28 -0700264 if (!onDismiss && vSpanDelta == 0 && hSpanDelta == 0) return;
Adam Cohend4844c32011-02-18 19:25:06 -0800265
Adam Cohene0489502012-08-27 15:18:53 -0700266 // We always want the final commit to match the feedback, so we make sure to use the
267 // last used direction vector when committing the resize / reorder.
268 if (onDismiss) {
269 mDirectionVector[0] = mLastDirectionVector[0];
270 mDirectionVector[1] = mLastDirectionVector[1];
271 } else {
272 mLastDirectionVector[0] = mDirectionVector[0];
273 mLastDirectionVector[1] = mDirectionVector[1];
274 }
275
Adam Cohenbebf0422012-04-11 18:06:28 -0700276 if (mCellLayout.createAreaForResize(cellX, cellY, spanX, spanY, mWidgetView,
277 mDirectionVector, onDismiss)) {
Sunny Goyale78e3d72015-09-24 11:23:31 -0700278 if (mStateAnnouncer != null && (lp.cellHSpan != spanX || lp.cellVSpan != spanY) ) {
279 mStateAnnouncer.announce(
280 mLauncher.getString(R.string.widget_resized, spanX, spanY));
281 }
282
Adam Cohenbebf0422012-04-11 18:06:28 -0700283 lp.tmpCellX = cellX;
284 lp.tmpCellY = cellY;
285 lp.cellHSpan = spanX;
286 lp.cellVSpan = spanY;
287 mRunningVInc += vSpanDelta;
288 mRunningHInc += hSpanDelta;
Sunny Goyale78e3d72015-09-24 11:23:31 -0700289
Adam Cohena897f392012-04-27 18:12:05 -0700290 if (!onDismiss) {
291 updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY);
292 }
Adam Cohenbebf0422012-04-11 18:06:28 -0700293 }
Adam Cohen67882692011-03-11 15:29:03 -0800294 mWidgetView.requestLayout();
Adam Cohend4844c32011-02-18 19:25:06 -0800295 }
296
Adam Cohena897f392012-04-27 18:12:05 -0700297 static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
298 int spanX, int spanY) {
Sunny Goyalba776d52015-05-18 20:52:57 -0700299 getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect);
300 widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top,
301 sTmpRect.right, sTmpRect.bottom);
Adam Cohen9e05a5e2012-09-10 15:53:09 -0700302 }
303
Sunny Goyal16466f12016-03-10 05:34:30 -0800304 public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY, Rect rect) {
Sunny Goyal756cd262015-08-20 12:33:21 -0700305 if (sCellSize == null) {
306 InvariantDeviceProfile inv = LauncherAppState.getInstance().getInvariantDeviceProfile();
307
308 // Initiate cell sizes.
309 sCellSize = new Point[2];
310 sCellSize[0] = inv.landscapeProfile.getCellSize();
311 sCellSize[1] = inv.portraitProfile.getCellSize();
312 }
313
Adam Cohen9e05a5e2012-09-10 15:53:09 -0700314 if (rect == null) {
315 rect = new Rect();
316 }
Sunny Goyal16466f12016-03-10 05:34:30 -0800317 final float density = context.getResources().getDisplayMetrics().density;
Adam Cohena897f392012-04-27 18:12:05 -0700318
319 // Compute landscape size
Sunny Goyal756cd262015-08-20 12:33:21 -0700320 int landWidth = (int) ((spanX * sCellSize[0].x) / density);
321 int landHeight = (int) ((spanY * sCellSize[0].y) / density);
Adam Cohena897f392012-04-27 18:12:05 -0700322
323 // Compute portrait size
Sunny Goyal756cd262015-08-20 12:33:21 -0700324 int portWidth = (int) ((spanX * sCellSize[1].x) / density);
325 int portHeight = (int) ((spanY * sCellSize[1].y) / density);
Adam Cohen9e05a5e2012-09-10 15:53:09 -0700326 rect.set(portWidth, landHeight, landWidth, portHeight);
327 return rect;
Adam Cohena897f392012-04-27 18:12:05 -0700328 }
329
Adam Cohen1b607ed2011-03-03 17:26:50 -0800330 /**
331 * This is the final step of the resize. Here we save the new widget size and position
332 * to LauncherModel and animate the resize frame.
333 */
Adam Cohenbebf0422012-04-11 18:06:28 -0700334 public void commitResize() {
335 resizeWidgetIfNeeded(true);
336 requestLayout();
337 }
Adam Cohend4844c32011-02-18 19:25:06 -0800338
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700339 private void onTouchUp() {
Sunny Goyalaa8a8712016-11-20 15:26:01 +0530340 int xThreshold = mCellLayout.getCellWidth();
341 int yThreshold = mCellLayout.getCellHeight();
Adam Cohend4844c32011-02-18 19:25:06 -0800342
Adam Cohenbebf0422012-04-11 18:06:28 -0700343 mDeltaXAddOn = mRunningHInc * xThreshold;
344 mDeltaYAddOn = mRunningVInc * yThreshold;
345 mDeltaX = 0;
346 mDeltaY = 0;
347
Adam Cohend4844c32011-02-18 19:25:06 -0800348 post(new Runnable() {
Adam Cohenbebf0422012-04-11 18:06:28 -0700349 @Override
Adam Cohend4844c32011-02-18 19:25:06 -0800350 public void run() {
351 snapToWidget(true);
352 }
353 });
354 }
355
356 public void snapToWidget(boolean animate) {
Jon Miranda21266912016-12-19 14:12:05 -0800357 float scale = mWidgetView.getScaleToFit();
Adam Cohen37b59ff2011-06-13 17:13:42 -0700358
Jon Miranda26e4d172016-12-12 15:43:18 -0800359 mDragLayer.getViewRectRelativeToSelf(mWidgetView, sTmpRect);
Adam Cohend6e7aa32013-07-09 15:32:37 -0700360
Jon Miranda26e4d172016-12-12 15:43:18 -0800361 int newWidth = 2 * mBackgroundPadding
362 + (int) (scale * (sTmpRect.width() - mWidgetPadding.left - mWidgetPadding.right));
363 int newHeight = 2 * mBackgroundPadding
364 + (int) (scale * (sTmpRect.height() - mWidgetPadding.top - mWidgetPadding.bottom));
365
366 int newX = (int) (sTmpRect.left - mBackgroundPadding + scale * mWidgetPadding.left);
367 int newY = (int) (sTmpRect.top - mBackgroundPadding + scale * mWidgetPadding.top);
Adam Cohen3cba7222011-03-02 19:03:11 -0800368
Sunny Goyalba776d52015-05-18 20:52:57 -0700369 // We need to make sure the frame's touchable regions lie fully within the bounds of the
Adam Cohen4459d6b2012-07-13 15:59:15 -0700370 // DragLayer. We allow the actual handles to be clipped, but we shift the touch regions
371 // down accordingly to provide a proper touch target.
Adam Cohen3cba7222011-03-02 19:03:11 -0800372 if (newY < 0) {
Adam Cohen4459d6b2012-07-13 15:59:15 -0700373 // In this case we shift the touch region down to start at the top of the DragLayer
374 mTopTouchRegionAdjustment = -newY;
375 } else {
376 mTopTouchRegionAdjustment = 0;
Adam Cohen3cba7222011-03-02 19:03:11 -0800377 }
Adam Cohen67882692011-03-11 15:29:03 -0800378 if (newY + newHeight > mDragLayer.getHeight()) {
Adam Cohen4459d6b2012-07-13 15:59:15 -0700379 // In this case we shift the touch region up to end at the bottom of the DragLayer
380 mBottomTouchRegionAdjustment = -(newY + newHeight - mDragLayer.getHeight());
381 } else {
382 mBottomTouchRegionAdjustment = 0;
Adam Cohen3cba7222011-03-02 19:03:11 -0800383 }
384
Jon Miranda26e4d172016-12-12 15:43:18 -0800385 final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
Adam Cohend4844c32011-02-18 19:25:06 -0800386 if (!animate) {
387 lp.width = newWidth;
388 lp.height = newHeight;
389 lp.x = newX;
390 lp.y = newY;
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700391 for (int i = 0; i < HANDLE_COUNT; i++) {
392 mDragHandles[i].setAlpha(1.0f);
393 }
Adam Cohend4844c32011-02-18 19:25:06 -0800394 requestLayout();
395 } else {
396 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", lp.width, newWidth);
Adam Cohen1b607ed2011-03-03 17:26:50 -0800397 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", lp.height,
398 newHeight);
Adam Cohend4844c32011-02-18 19:25:06 -0800399 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", lp.x, newX);
400 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
Michael Jurkaf1ad6082013-03-13 12:55:46 +0100401 ObjectAnimator oa =
402 LauncherAnimUtils.ofPropertyValuesHolder(lp, this, width, height, x, y);
Adam Cohend4844c32011-02-18 19:25:06 -0800403 oa.addUpdateListener(new AnimatorUpdateListener() {
404 public void onAnimationUpdate(ValueAnimator animation) {
405 requestLayout();
406 }
407 });
Michael Jurka2ecf9952012-06-18 12:52:28 -0700408 AnimatorSet set = LauncherAnimUtils.createAnimatorSet();
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700409 set.play(oa);
410 for (int i = 0; i < HANDLE_COUNT; i++) {
411 set.play(LauncherAnimUtils.ofFloat(mDragHandles[i], ALPHA, 1.0f));
Adam Cohend4844c32011-02-18 19:25:06 -0800412 }
413
414 set.setDuration(SNAP_DURATION);
415 set.start();
416 }
Tony Wickham71255bb2016-02-10 16:18:15 -0800417
418 setFocusableInTouchMode(true);
419 requestFocus();
420 }
421
422 @Override
423 public boolean onKey(View v, int keyCode, KeyEvent event) {
424 // Clear the frame and give focus to the widget host view when a directional key is pressed.
425 if (FocusLogic.shouldConsume(keyCode)) {
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700426 mDragLayer.clearResizeFrame();
Tony Wickham71255bb2016-02-10 16:18:15 -0800427 mWidgetView.requestFocus();
428 return true;
429 }
430 return false;
Adam Cohend4844c32011-02-18 19:25:06 -0800431 }
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700432
433 private boolean handleTouchDown(MotionEvent ev) {
434 Rect hitRect = new Rect();
435 int x = (int) ev.getX();
436 int y = (int) ev.getY();
437
438 getHitRect(hitRect);
439 if (hitRect.contains(x, y)) {
440 if (beginResizeIfPointInRegion(x - getLeft(), y - getTop())) {
441 mXDown = x;
442 mYDown = y;
443 return true;
444 }
445 }
446 return false;
447 }
448
449 @Override
450 public boolean onControllerTouchEvent(MotionEvent ev) {
451 int action = ev.getAction();
452 int x = (int) ev.getX();
453 int y = (int) ev.getY();
454
455 switch (action) {
456 case MotionEvent.ACTION_DOWN:
457 return handleTouchDown(ev);
458 case MotionEvent.ACTION_MOVE:
459 visualizeResizeForDelta(x - mXDown, y - mYDown);
460 break;
461 case MotionEvent.ACTION_CANCEL:
462 case MotionEvent.ACTION_UP:
463 visualizeResizeForDelta(x - mXDown, y - mYDown);
464 onTouchUp();
465 mXDown = mYDown = 0;
466 break;
467 }
468 return true;
469 }
470
471 @Override
472 public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
473 if (ev.getAction() == MotionEvent.ACTION_DOWN && handleTouchDown(ev)) {
474 return true;
475 }
476 return false;
477 }
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700478
479 /**
480 * A mutable class for describing the range of two int values.
481 */
482 private static class IntRange {
483
484 public int start, end;
485
486 public int clamp(int value) {
487 return Utilities.boundToRange(value, start, end);
488 }
489
490 public void set(int s, int e) {
491 start = s;
492 end = e;
493 }
494
495 public int size() {
496 return end - start;
497 }
498
499 /**
500 * Moves either the start or end edge (but never both) by {@param delta} and sets the
501 * result in {@param out}
502 */
503 public void applyDelta(boolean moveStart, boolean moveEnd, int delta, IntRange out) {
504 out.start = moveStart ? start + delta : start;
505 out.end = moveEnd ? end + delta : end;
506 }
507
508 /**
509 * Applies delta similar to {@link #applyDelta(boolean, boolean, int, IntRange)},
510 * with extra conditions.
511 * @param minSize minimum size after with the moving edge should not be shifted any further.
512 * For eg, if delta = -3 when moving the endEdge brings the size to less than
513 * minSize, only delta = -2 will applied
514 * @param maxEnd The maximum value to the end edge (start edge is always restricted to 0)
515 * @return the amount of increase when endEdge was moves and the amount of decrease when
516 * the start edge was moved.
517 */
518 public int applyDeltaAndBound(boolean moveStart, boolean moveEnd, int delta,
519 int minSize, int maxEnd, IntRange out) {
520 applyDelta(moveStart, moveEnd, delta, out);
Jon Miranda9f33cb22017-01-03 09:30:00 -0800521 if (out.start < 0) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700522 out.start = 0;
523 }
Jon Miranda9f33cb22017-01-03 09:30:00 -0800524 if (out.end > maxEnd) {
Sunny Goyal08ca40f2016-09-13 21:07:31 -0700525 out.end = maxEnd;
526 }
527 if (out.size() < minSize) {
528 if (moveStart) {
529 out.start = out.end - minSize;
530 } else if (moveEnd) {
531 out.end = out.start + minSize;
532 }
533 }
534 return moveEnd ? out.size() - size() : size() - out.size();
535 }
536 }
Adam Cohend4844c32011-02-18 19:25:06 -0800537}