blob: 6d132ebf03dca109fee9b0b98bf356c111abe835 [file] [log] [blame]
Adam Cohend4844c32011-02-18 19:25:06 -08001package com.android.launcher2;
2
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 Cohen0cf2a7c2011-11-08 15:07:01 -080011import android.graphics.Rect;
Adam Cohend4844c32011-02-18 19:25:06 -080012import android.view.Gravity;
13import android.widget.FrameLayout;
14import android.widget.ImageView;
15
16import com.android.launcher.R;
17
18public class AppWidgetResizeFrame extends FrameLayout {
19
20 private ItemInfo mItemInfo;
21 private LauncherAppWidgetHostView mWidgetView;
22 private CellLayout mCellLayout;
Adam Cohen67882692011-03-11 15:29:03 -080023 private DragLayer mDragLayer;
24 private Workspace mWorkspace;
Adam Cohend4844c32011-02-18 19:25:06 -080025 private ImageView mLeftHandle;
26 private ImageView mRightHandle;
Adam Cohen1b607ed2011-03-03 17:26:50 -080027 private ImageView mTopHandle;
Adam Cohend4844c32011-02-18 19:25:06 -080028 private ImageView mBottomHandle;
29
30 private boolean mLeftBorderActive;
31 private boolean mRightBorderActive;
32 private boolean mTopBorderActive;
33 private boolean mBottomBorderActive;
34
Adam Cohen37b59ff2011-06-13 17:13:42 -070035 private int mWidgetPaddingLeft;
36 private int mWidgetPaddingRight;
37 private int mWidgetPaddingTop;
38 private int mWidgetPaddingBottom;
39
Adam Cohend4844c32011-02-18 19:25:06 -080040 private int mBaselineWidth;
41 private int mBaselineHeight;
42 private int mBaselineX;
43 private int mBaselineY;
44 private int mResizeMode;
Adam Cohen3cba7222011-03-02 19:03:11 -080045
Adam Cohend4844c32011-02-18 19:25:06 -080046 private int mRunningHInc;
47 private int mRunningVInc;
48 private int mMinHSpan;
49 private int mMinVSpan;
50 private int mDeltaX;
51 private int mDeltaY;
Adam Cohen1b607ed2011-03-03 17:26:50 -080052
Adam Cohen3cba7222011-03-02 19:03:11 -080053 private int mBackgroundPadding;
54 private int mTouchTargetWidth;
Adam Cohend4844c32011-02-18 19:25:06 -080055
56 private int mExpandability[] = new int[4];
57
Adam Cohend4844c32011-02-18 19:25:06 -080058 final int SNAP_DURATION = 150;
Adam Cohen3cba7222011-03-02 19:03:11 -080059 final int BACKGROUND_PADDING = 24;
Adam Cohene4b77292011-03-08 18:35:52 -080060 final float DIMMED_HANDLE_ALPHA = 0f;
61 final float RESIZE_THRESHOLD = 0.66f;
Adam Cohend4844c32011-02-18 19:25:06 -080062
Adam Cohen1b607ed2011-03-03 17:26:50 -080063 public static final int LEFT = 0;
64 public static final int TOP = 1;
65 public static final int RIGHT = 2;
66 public static final int BOTTOM = 3;
67
Adam Cohencbf47e32011-09-16 17:32:37 -070068 private Launcher mLauncher;
69
Adam Cohend4844c32011-02-18 19:25:06 -080070 public AppWidgetResizeFrame(Context context, ItemInfo itemInfo,
Adam Cohen67882692011-03-11 15:29:03 -080071 LauncherAppWidgetHostView widgetView, CellLayout cellLayout, DragLayer dragLayer) {
Adam Cohend4844c32011-02-18 19:25:06 -080072
73 super(context);
Adam Cohencbf47e32011-09-16 17:32:37 -070074 mLauncher = (Launcher) context;
Adam Cohend4844c32011-02-18 19:25:06 -080075 mItemInfo = itemInfo;
76 mCellLayout = cellLayout;
77 mWidgetView = widgetView;
Adam Cohen27c09b02011-02-28 14:45:11 -080078 mResizeMode = widgetView.getAppWidgetInfo().resizeMode;
Adam Cohen67882692011-03-11 15:29:03 -080079 mDragLayer = dragLayer;
80 mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
Adam Cohen3cba7222011-03-02 19:03:11 -080081
Adam Cohend4844c32011-02-18 19:25:06 -080082 final AppWidgetProviderInfo info = widgetView.getAppWidgetInfo();
Adam Cohencbf47e32011-09-16 17:32:37 -070083 int[] result = mLauncher.getMinResizeSpanForWidget(info, null);
Adam Cohend4844c32011-02-18 19:25:06 -080084 mMinHSpan = result[0];
85 mMinVSpan = result[1];
86
Adam Cohen3cba7222011-03-02 19:03:11 -080087 setBackgroundResource(R.drawable.widget_resize_frame_holo);
Adam Cohend4844c32011-02-18 19:25:06 -080088 setPadding(0, 0, 0, 0);
89
90 LayoutParams lp;
91 mLeftHandle = new ImageView(context);
Adam Cohen3cba7222011-03-02 19:03:11 -080092 mLeftHandle.setImageResource(R.drawable.widget_resize_handle_left);
Adam Cohend4844c32011-02-18 19:25:06 -080093 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
94 Gravity.LEFT | Gravity.CENTER_VERTICAL);
95 addView(mLeftHandle, lp);
96
97 mRightHandle = new ImageView(context);
Adam Cohen3cba7222011-03-02 19:03:11 -080098 mRightHandle.setImageResource(R.drawable.widget_resize_handle_right);
Adam Cohend4844c32011-02-18 19:25:06 -080099 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
100 Gravity.RIGHT | Gravity.CENTER_VERTICAL);
101 addView(mRightHandle, lp);
102
103 mTopHandle = new ImageView(context);
Adam Cohen3cba7222011-03-02 19:03:11 -0800104 mTopHandle.setImageResource(R.drawable.widget_resize_handle_top);
Adam Cohend4844c32011-02-18 19:25:06 -0800105 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
106 Gravity.CENTER_HORIZONTAL | Gravity.TOP);
107 addView(mTopHandle, lp);
108
109 mBottomHandle = new ImageView(context);
Adam Cohen3cba7222011-03-02 19:03:11 -0800110 mBottomHandle.setImageResource(R.drawable.widget_resize_handle_bottom);
Adam Cohend4844c32011-02-18 19:25:06 -0800111 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
112 Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
113 addView(mBottomHandle, lp);
114
Adam Cohen0cf2a7c2011-11-08 15:07:01 -0800115 Rect p = AppWidgetHostView.getDefaultPaddingForWidget(context,
116 widgetView.getAppWidgetInfo().provider, null);
Adam Cohen82ebbd22011-09-30 15:05:40 -0700117 mWidgetPaddingLeft = p.left;
118 mWidgetPaddingTop = p.top;
119 mWidgetPaddingRight = p.right;
120 mWidgetPaddingBottom = p.bottom;
Adam Cohen37b59ff2011-06-13 17:13:42 -0700121
Adam Cohend4844c32011-02-18 19:25:06 -0800122 if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
123 mTopHandle.setVisibility(GONE);
124 mBottomHandle.setVisibility(GONE);
125 } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
126 mLeftHandle.setVisibility(GONE);
127 mRightHandle.setVisibility(GONE);
Adam Cohen3cba7222011-03-02 19:03:11 -0800128 }
129
Adam Cohencbf47e32011-09-16 17:32:37 -0700130 final float density = mLauncher.getResources().getDisplayMetrics().density;
Adam Cohen3cba7222011-03-02 19:03:11 -0800131 mBackgroundPadding = (int) Math.ceil(density * BACKGROUND_PADDING);
132 mTouchTargetWidth = 2 * mBackgroundPadding;
Adam Cohend4844c32011-02-18 19:25:06 -0800133 }
134
135 public boolean beginResizeIfPointInRegion(int x, int y) {
136 boolean horizontalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0;
137 boolean verticalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0;
Adam Cohen3cba7222011-03-02 19:03:11 -0800138 mLeftBorderActive = (x < mTouchTargetWidth) && horizontalActive;
139 mRightBorderActive = (x > getWidth() - mTouchTargetWidth) && horizontalActive;
140 mTopBorderActive = (y < mTouchTargetWidth) && verticalActive;
141 mBottomBorderActive = (y > getHeight() - mTouchTargetWidth) && verticalActive;
Adam Cohend4844c32011-02-18 19:25:06 -0800142
143 boolean anyBordersActive = mLeftBorderActive || mRightBorderActive
144 || mTopBorderActive || mBottomBorderActive;
145
146 mBaselineWidth = getMeasuredWidth();
147 mBaselineHeight = getMeasuredHeight();
148 mBaselineX = getLeft();
149 mBaselineY = getTop();
150 mRunningHInc = 0;
151 mRunningVInc = 0;
152
153 if (anyBordersActive) {
Adam Cohen3cba7222011-03-02 19:03:11 -0800154 mLeftHandle.setAlpha(mLeftBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
155 mRightHandle.setAlpha(mRightBorderActive ? 1.0f :DIMMED_HANDLE_ALPHA);
156 mTopHandle.setAlpha(mTopBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
157 mBottomHandle.setAlpha(mBottomBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
Adam Cohend4844c32011-02-18 19:25:06 -0800158 }
159 mCellLayout.getExpandabilityArrayForView(mWidgetView, mExpandability);
Adam Cohen3cba7222011-03-02 19:03:11 -0800160
Adam Cohend4844c32011-02-18 19:25:06 -0800161 return anyBordersActive;
162 }
163
Adam Cohen1b607ed2011-03-03 17:26:50 -0800164 /**
165 * Here we bound the deltas such that the frame cannot be stretched beyond the extents
166 * of the CellLayout, and such that the frame's borders can't cross.
167 */
Adam Cohend4844c32011-02-18 19:25:06 -0800168 public void updateDeltas(int deltaX, int deltaY) {
169 if (mLeftBorderActive) {
170 mDeltaX = Math.max(-mBaselineX, deltaX);
Adam Cohen3cba7222011-03-02 19:03:11 -0800171 mDeltaX = Math.min(mBaselineWidth - 2 * mTouchTargetWidth, mDeltaX);
Adam Cohend4844c32011-02-18 19:25:06 -0800172 } else if (mRightBorderActive) {
Adam Cohen67882692011-03-11 15:29:03 -0800173 mDeltaX = Math.min(mDragLayer.getWidth() - (mBaselineX + mBaselineWidth), deltaX);
Adam Cohen3cba7222011-03-02 19:03:11 -0800174 mDeltaX = Math.max(-mBaselineWidth + 2 * mTouchTargetWidth, mDeltaX);
Adam Cohend4844c32011-02-18 19:25:06 -0800175 }
176
177 if (mTopBorderActive) {
178 mDeltaY = Math.max(-mBaselineY, deltaY);
Adam Cohen3cba7222011-03-02 19:03:11 -0800179 mDeltaY = Math.min(mBaselineHeight - 2 * mTouchTargetWidth, mDeltaY);
Adam Cohend4844c32011-02-18 19:25:06 -0800180 } else if (mBottomBorderActive) {
Adam Cohen67882692011-03-11 15:29:03 -0800181 mDeltaY = Math.min(mDragLayer.getHeight() - (mBaselineY + mBaselineHeight), deltaY);
Adam Cohen3cba7222011-03-02 19:03:11 -0800182 mDeltaY = Math.max(-mBaselineHeight + 2 * mTouchTargetWidth, mDeltaY);
Adam Cohend4844c32011-02-18 19:25:06 -0800183 }
184 }
185
Adam Cohen1b607ed2011-03-03 17:26:50 -0800186 /**
187 * Based on the deltas, we resize the frame, and, if needed, we resize the widget.
188 */
Adam Cohend4844c32011-02-18 19:25:06 -0800189 public void visualizeResizeForDelta(int deltaX, int deltaY) {
190 updateDeltas(deltaX, deltaY);
Adam Cohen67882692011-03-11 15:29:03 -0800191 DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
192
Adam Cohend4844c32011-02-18 19:25:06 -0800193 if (mLeftBorderActive) {
194 lp.x = mBaselineX + mDeltaX;
195 lp.width = mBaselineWidth - mDeltaX;
196 } else if (mRightBorderActive) {
197 lp.width = mBaselineWidth + mDeltaX;
198 }
199
200 if (mTopBorderActive) {
201 lp.y = mBaselineY + mDeltaY;
202 lp.height = mBaselineHeight - mDeltaY;
203 } else if (mBottomBorderActive) {
204 lp.height = mBaselineHeight + mDeltaY;
205 }
206
207 resizeWidgetIfNeeded();
208 requestLayout();
209 }
210
Adam Cohen1b607ed2011-03-03 17:26:50 -0800211 /**
212 * Based on the current deltas, we determine if and how to resize the widget.
213 */
Adam Cohend4844c32011-02-18 19:25:06 -0800214 private void resizeWidgetIfNeeded() {
Adam Cohend4844c32011-02-18 19:25:06 -0800215 int xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
216 int yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
217
Adam Cohene4b77292011-03-08 18:35:52 -0800218 float hSpanIncF = 1.0f * mDeltaX / xThreshold - mRunningHInc;
219 float vSpanIncF = 1.0f * mDeltaY / yThreshold - mRunningVInc;
220
221 int hSpanInc = 0;
222 int vSpanInc = 0;
Adam Cohend4844c32011-02-18 19:25:06 -0800223 int cellXInc = 0;
224 int cellYInc = 0;
225
Adam Cohene4b77292011-03-08 18:35:52 -0800226 if (Math.abs(hSpanIncF) > RESIZE_THRESHOLD) {
227 hSpanInc = Math.round(hSpanIncF);
228 }
229 if (Math.abs(vSpanIncF) > RESIZE_THRESHOLD) {
230 vSpanInc = Math.round(vSpanIncF);
231 }
232
Adam Cohend4844c32011-02-18 19:25:06 -0800233 if (hSpanInc == 0 && vSpanInc == 0) return;
234
235 // Before we change the widget, we clear the occupied cells associated with it.
236 // The new set of occupied cells is marked below, once the layout params are updated.
237 mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
238
239 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
Adam Cohen1b607ed2011-03-03 17:26:50 -0800240
241 // For each border, we bound the resizing based on the minimum width, and the maximum
242 // expandability.
Adam Cohend4844c32011-02-18 19:25:06 -0800243 if (mLeftBorderActive) {
Adam Cohen1b607ed2011-03-03 17:26:50 -0800244 cellXInc = Math.max(-mExpandability[LEFT], hSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800245 cellXInc = Math.min(lp.cellHSpan - mMinHSpan, cellXInc);
246 hSpanInc *= -1;
Adam Cohen1b607ed2011-03-03 17:26:50 -0800247 hSpanInc = Math.min(mExpandability[LEFT], hSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800248 hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
249 mRunningHInc -= hSpanInc;
250 } else if (mRightBorderActive) {
Adam Cohen1b607ed2011-03-03 17:26:50 -0800251 hSpanInc = Math.min(mExpandability[RIGHT], hSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800252 hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
253 mRunningHInc += hSpanInc;
254 }
255
256 if (mTopBorderActive) {
Adam Cohen1b607ed2011-03-03 17:26:50 -0800257 cellYInc = Math.max(-mExpandability[TOP], vSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800258 cellYInc = Math.min(lp.cellVSpan - mMinVSpan, cellYInc);
259 vSpanInc *= -1;
Adam Cohen1b607ed2011-03-03 17:26:50 -0800260 vSpanInc = Math.min(mExpandability[TOP], vSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800261 vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
262 mRunningVInc -= vSpanInc;
263 } else if (mBottomBorderActive) {
Adam Cohen1b607ed2011-03-03 17:26:50 -0800264 vSpanInc = Math.min(mExpandability[BOTTOM], vSpanInc);
Adam Cohend4844c32011-02-18 19:25:06 -0800265 vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
266 mRunningVInc += vSpanInc;
267 }
268
269 // Update the widget's dimensions and position according to the deltas computed above
270 if (mLeftBorderActive || mRightBorderActive) {
271 lp.cellHSpan += hSpanInc;
272 lp.cellX += cellXInc;
273 }
274
275 if (mTopBorderActive || mBottomBorderActive) {
276 lp.cellVSpan += vSpanInc;
277 lp.cellY += cellYInc;
278 }
279
Adam Cohen1b607ed2011-03-03 17:26:50 -0800280 // Update the expandability array, as we have changed the widget's size.
Adam Cohen3cba7222011-03-02 19:03:11 -0800281 mCellLayout.getExpandabilityArrayForView(mWidgetView, mExpandability);
Adam Cohend4844c32011-02-18 19:25:06 -0800282
283 // Update the cells occupied by this widget
284 mCellLayout.markCellsAsOccupiedForView(mWidgetView);
Adam Cohen67882692011-03-11 15:29:03 -0800285 mWidgetView.requestLayout();
Adam Cohend4844c32011-02-18 19:25:06 -0800286 }
287
Adam Cohen1b607ed2011-03-03 17:26:50 -0800288 /**
289 * This is the final step of the resize. Here we save the new widget size and position
290 * to LauncherModel and animate the resize frame.
291 */
Adam Cohend4844c32011-02-18 19:25:06 -0800292 public void commitResizeForDelta(int deltaX, int deltaY) {
293 visualizeResizeForDelta(deltaX, deltaY);
294
295 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
296 LauncherModel.resizeItemInDatabase(getContext(), mItemInfo, lp.cellX, lp.cellY,
297 lp.cellHSpan, lp.cellVSpan);
298 mWidgetView.requestLayout();
299
300 // Once our widget resizes (hence the post), we want to snap the resize frame to it
301 post(new Runnable() {
302 public void run() {
303 snapToWidget(true);
304 }
305 });
306 }
307
308 public void snapToWidget(boolean animate) {
Adam Cohen67882692011-03-11 15:29:03 -0800309 final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
Winson Chung4b825dcd2011-06-19 12:41:22 -0700310 int xOffset = mCellLayout.getLeft() + mCellLayout.getPaddingLeft() - mWorkspace.getScrollX();
311 int yOffset = mCellLayout.getTop() + mCellLayout.getPaddingTop() - mWorkspace.getScrollY();
Adam Cohend4844c32011-02-18 19:25:06 -0800312
Adam Cohen37b59ff2011-06-13 17:13:42 -0700313 int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding - mWidgetPaddingLeft -
314 mWidgetPaddingRight;
315 int newHeight = mWidgetView.getHeight() + 2 * mBackgroundPadding - mWidgetPaddingTop -
316 mWidgetPaddingBottom;
317
318 int newX = mWidgetView.getLeft() - mBackgroundPadding + xOffset + mWidgetPaddingLeft;
319 int newY = mWidgetView.getTop() - mBackgroundPadding + yOffset + mWidgetPaddingTop;
Adam Cohen3cba7222011-03-02 19:03:11 -0800320
321 // We need to make sure the frame stays within the bounds of the CellLayout
322 if (newY < 0) {
323 newHeight -= -newY;
324 newY = 0;
325 }
Adam Cohen67882692011-03-11 15:29:03 -0800326 if (newY + newHeight > mDragLayer.getHeight()) {
327 newHeight -= newY + newHeight - mDragLayer.getHeight();
Adam Cohen3cba7222011-03-02 19:03:11 -0800328 }
329
Adam Cohend4844c32011-02-18 19:25:06 -0800330 if (!animate) {
331 lp.width = newWidth;
332 lp.height = newHeight;
333 lp.x = newX;
334 lp.y = newY;
335 mLeftHandle.setAlpha(1.0f);
336 mRightHandle.setAlpha(1.0f);
337 mTopHandle.setAlpha(1.0f);
338 mBottomHandle.setAlpha(1.0f);
339 requestLayout();
340 } else {
341 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", lp.width, newWidth);
Adam Cohen1b607ed2011-03-03 17:26:50 -0800342 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", lp.height,
343 newHeight);
Adam Cohend4844c32011-02-18 19:25:06 -0800344 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", lp.x, newX);
345 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
346 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y);
347 ObjectAnimator leftOa = ObjectAnimator.ofFloat(mLeftHandle, "alpha", 1.0f);
348 ObjectAnimator rightOa = ObjectAnimator.ofFloat(mRightHandle, "alpha", 1.0f);
349 ObjectAnimator topOa = ObjectAnimator.ofFloat(mTopHandle, "alpha", 1.0f);
350 ObjectAnimator bottomOa = ObjectAnimator.ofFloat(mBottomHandle, "alpha", 1.0f);
351 oa.addUpdateListener(new AnimatorUpdateListener() {
352 public void onAnimationUpdate(ValueAnimator animation) {
353 requestLayout();
354 }
355 });
356 AnimatorSet set = new AnimatorSet();
357 if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
358 set.playTogether(oa, topOa, bottomOa);
359 } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
360 set.playTogether(oa, leftOa, rightOa);
361 } else {
362 set.playTogether(oa, leftOa, rightOa, topOa, bottomOa);
363 }
364
365 set.setDuration(SNAP_DURATION);
366 set.start();
367 }
368 }
369}