blob: 40347adc2b7333d366355372c6df73ebdb2d67e7 [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;
8import android.appwidget.AppWidgetProviderInfo;
9import android.content.Context;
10import android.view.Gravity;
11import android.widget.FrameLayout;
12import android.widget.ImageView;
13
14import com.android.launcher.R;
15
16public class AppWidgetResizeFrame extends FrameLayout {
17
18 private ItemInfo mItemInfo;
19 private LauncherAppWidgetHostView mWidgetView;
20 private CellLayout mCellLayout;
21 private ImageView mLeftHandle;
22 private ImageView mRightHandle;
23 private ImageView mTopHandle;
24 private ImageView mBottomHandle;
25
26 private boolean mLeftBorderActive;
27 private boolean mRightBorderActive;
28 private boolean mTopBorderActive;
29 private boolean mBottomBorderActive;
30
31 private int mBaselineWidth;
32 private int mBaselineHeight;
33 private int mBaselineX;
34 private int mBaselineY;
35 private int mResizeMode;
Adam Cohen3cba7222011-03-02 19:03:11 -080036
Adam Cohend4844c32011-02-18 19:25:06 -080037 private int mRunningHInc;
38 private int mRunningVInc;
39 private int mMinHSpan;
40 private int mMinVSpan;
41 private int mDeltaX;
42 private int mDeltaY;
Adam Cohen3cba7222011-03-02 19:03:11 -080043 private int mBackgroundPadding;
44 private int mTouchTargetWidth;
Adam Cohend4844c32011-02-18 19:25:06 -080045
46 private int mExpandability[] = new int[4];
47
Adam Cohend4844c32011-02-18 19:25:06 -080048 final int SNAP_DURATION = 150;
Adam Cohen3cba7222011-03-02 19:03:11 -080049 final int BACKGROUND_PADDING = 24;
50 final float DIMMED_HANDLE_ALPHA = 0.3f;
Adam Cohend4844c32011-02-18 19:25:06 -080051
52 public AppWidgetResizeFrame(Context context, ItemInfo itemInfo,
53 LauncherAppWidgetHostView widgetView, CellLayout cellLayout) {
54
55 super(context);
56 mContext = context;
57 mItemInfo = itemInfo;
58 mCellLayout = cellLayout;
59 mWidgetView = widgetView;
Adam Cohen27c09b02011-02-28 14:45:11 -080060 mResizeMode = widgetView.getAppWidgetInfo().resizeMode;
Adam Cohen3cba7222011-03-02 19:03:11 -080061
Adam Cohend4844c32011-02-18 19:25:06 -080062 final AppWidgetProviderInfo info = widgetView.getAppWidgetInfo();
63 int[] result = mCellLayout.rectToCell(info.minWidth, info.minHeight, null);
64 mMinHSpan = result[0];
65 mMinVSpan = result[1];
66
Adam Cohen3cba7222011-03-02 19:03:11 -080067 setBackgroundResource(R.drawable.widget_resize_frame_holo);
Adam Cohend4844c32011-02-18 19:25:06 -080068 setPadding(0, 0, 0, 0);
69
70 LayoutParams lp;
71 mLeftHandle = new ImageView(context);
Adam Cohen3cba7222011-03-02 19:03:11 -080072 mLeftHandle.setImageResource(R.drawable.widget_resize_handle_left);
Adam Cohend4844c32011-02-18 19:25:06 -080073 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
74 Gravity.LEFT | Gravity.CENTER_VERTICAL);
75 addView(mLeftHandle, lp);
76
77 mRightHandle = new ImageView(context);
Adam Cohen3cba7222011-03-02 19:03:11 -080078 mRightHandle.setImageResource(R.drawable.widget_resize_handle_right);
Adam Cohend4844c32011-02-18 19:25:06 -080079 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
80 Gravity.RIGHT | Gravity.CENTER_VERTICAL);
81 addView(mRightHandle, lp);
82
83 mTopHandle = new ImageView(context);
Adam Cohen3cba7222011-03-02 19:03:11 -080084 mTopHandle.setImageResource(R.drawable.widget_resize_handle_top);
Adam Cohend4844c32011-02-18 19:25:06 -080085 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
86 Gravity.CENTER_HORIZONTAL | Gravity.TOP);
87 addView(mTopHandle, lp);
88
89 mBottomHandle = new ImageView(context);
Adam Cohen3cba7222011-03-02 19:03:11 -080090 mBottomHandle.setImageResource(R.drawable.widget_resize_handle_bottom);
Adam Cohend4844c32011-02-18 19:25:06 -080091 lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
92 Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
93 addView(mBottomHandle, lp);
94
95 if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
96 mTopHandle.setVisibility(GONE);
97 mBottomHandle.setVisibility(GONE);
98 } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
99 mLeftHandle.setVisibility(GONE);
100 mRightHandle.setVisibility(GONE);
Adam Cohen3cba7222011-03-02 19:03:11 -0800101 }
102
103 final float density = mContext.getResources().getDisplayMetrics().density;
104 mBackgroundPadding = (int) Math.ceil(density * BACKGROUND_PADDING);
105 mTouchTargetWidth = 2 * mBackgroundPadding;
Adam Cohend4844c32011-02-18 19:25:06 -0800106 }
107
108 public boolean beginResizeIfPointInRegion(int x, int y) {
109 boolean horizontalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0;
110 boolean verticalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0;
Adam Cohen3cba7222011-03-02 19:03:11 -0800111 mLeftBorderActive = (x < mTouchTargetWidth) && horizontalActive;
112 mRightBorderActive = (x > getWidth() - mTouchTargetWidth) && horizontalActive;
113 mTopBorderActive = (y < mTouchTargetWidth) && verticalActive;
114 mBottomBorderActive = (y > getHeight() - mTouchTargetWidth) && verticalActive;
Adam Cohend4844c32011-02-18 19:25:06 -0800115
116 boolean anyBordersActive = mLeftBorderActive || mRightBorderActive
117 || mTopBorderActive || mBottomBorderActive;
118
119 mBaselineWidth = getMeasuredWidth();
120 mBaselineHeight = getMeasuredHeight();
121 mBaselineX = getLeft();
122 mBaselineY = getTop();
123 mRunningHInc = 0;
124 mRunningVInc = 0;
125
126 if (anyBordersActive) {
Adam Cohen3cba7222011-03-02 19:03:11 -0800127 mLeftHandle.setAlpha(mLeftBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
128 mRightHandle.setAlpha(mRightBorderActive ? 1.0f :DIMMED_HANDLE_ALPHA);
129 mTopHandle.setAlpha(mTopBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
130 mBottomHandle.setAlpha(mBottomBorderActive ? 1.0f : DIMMED_HANDLE_ALPHA);
Adam Cohend4844c32011-02-18 19:25:06 -0800131 }
132 mCellLayout.getExpandabilityArrayForView(mWidgetView, mExpandability);
Adam Cohen3cba7222011-03-02 19:03:11 -0800133
Adam Cohend4844c32011-02-18 19:25:06 -0800134 return anyBordersActive;
135 }
136
137 public void updateDeltas(int deltaX, int deltaY) {
138 if (mLeftBorderActive) {
139 mDeltaX = Math.max(-mBaselineX, deltaX);
Adam Cohen3cba7222011-03-02 19:03:11 -0800140 mDeltaX = Math.min(mBaselineWidth - 2 * mTouchTargetWidth, mDeltaX);
Adam Cohend4844c32011-02-18 19:25:06 -0800141 } else if (mRightBorderActive) {
142 mDeltaX = Math.min(mCellLayout.getWidth() - (mBaselineX + mBaselineWidth), deltaX);
Adam Cohen3cba7222011-03-02 19:03:11 -0800143 mDeltaX = Math.max(-mBaselineWidth + 2 * mTouchTargetWidth, mDeltaX);
Adam Cohend4844c32011-02-18 19:25:06 -0800144 }
145
146 if (mTopBorderActive) {
147 mDeltaY = Math.max(-mBaselineY, deltaY);
Adam Cohen3cba7222011-03-02 19:03:11 -0800148 mDeltaY = Math.min(mBaselineHeight - 2 * mTouchTargetWidth, mDeltaY);
Adam Cohend4844c32011-02-18 19:25:06 -0800149 } else if (mBottomBorderActive) {
150 mDeltaY = Math.min(mCellLayout.getHeight() - (mBaselineY + mBaselineHeight), deltaY);
Adam Cohen3cba7222011-03-02 19:03:11 -0800151 mDeltaY = Math.max(-mBaselineHeight + 2 * mTouchTargetWidth, mDeltaY);
Adam Cohend4844c32011-02-18 19:25:06 -0800152 }
153 }
154
155 public void visualizeResizeForDelta(int deltaX, int deltaY) {
156 updateDeltas(deltaX, deltaY);
157 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
158 if (mLeftBorderActive) {
159 lp.x = mBaselineX + mDeltaX;
160 lp.width = mBaselineWidth - mDeltaX;
161 } else if (mRightBorderActive) {
162 lp.width = mBaselineWidth + mDeltaX;
163 }
164
165 if (mTopBorderActive) {
166 lp.y = mBaselineY + mDeltaY;
167 lp.height = mBaselineHeight - mDeltaY;
168 } else if (mBottomBorderActive) {
169 lp.height = mBaselineHeight + mDeltaY;
170 }
171
172 resizeWidgetIfNeeded();
173 requestLayout();
174 }
175
176 private void resizeWidgetIfNeeded() {
Adam Cohend4844c32011-02-18 19:25:06 -0800177 int xThreshold = mCellLayout.getCellWidth() + mCellLayout.getWidthGap();
178 int yThreshold = mCellLayout.getCellHeight() + mCellLayout.getHeightGap();
179
180 int hSpanInc = (int) Math.round(1.0f * mDeltaX / xThreshold) - mRunningHInc;
181 int vSpanInc = (int) Math.round(1.0f * mDeltaY / yThreshold) - mRunningVInc;
182 int cellXInc = 0;
183 int cellYInc = 0;
184
185 if (hSpanInc == 0 && vSpanInc == 0) return;
186
187 // Before we change the widget, we clear the occupied cells associated with it.
188 // The new set of occupied cells is marked below, once the layout params are updated.
189 mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
190
191 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
192 if (mLeftBorderActive) {
193 cellXInc = Math.max(-mExpandability[0], hSpanInc);
194 cellXInc = Math.min(lp.cellHSpan - mMinHSpan, cellXInc);
195 hSpanInc *= -1;
196 hSpanInc = Math.min(mExpandability[0], hSpanInc);
197 hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
198 mRunningHInc -= hSpanInc;
199 } else if (mRightBorderActive) {
200 hSpanInc = Math.min(mExpandability[2], hSpanInc);
201 hSpanInc = Math.max(-(lp.cellHSpan - mMinHSpan), hSpanInc);
202 mRunningHInc += hSpanInc;
203 }
204
205 if (mTopBorderActive) {
206 cellYInc = Math.max(-mExpandability[1], vSpanInc);
207 cellYInc = Math.min(lp.cellVSpan - mMinVSpan, cellYInc);
208 vSpanInc *= -1;
209 vSpanInc = Math.min(mExpandability[1], vSpanInc);
210 vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
211 mRunningVInc -= vSpanInc;
212 } else if (mBottomBorderActive) {
213 vSpanInc = Math.min(mExpandability[3], vSpanInc);
214 vSpanInc = Math.max(-(lp.cellVSpan - mMinVSpan), vSpanInc);
215 mRunningVInc += vSpanInc;
216 }
217
218 // Update the widget's dimensions and position according to the deltas computed above
219 if (mLeftBorderActive || mRightBorderActive) {
220 lp.cellHSpan += hSpanInc;
221 lp.cellX += cellXInc;
222 }
223
224 if (mTopBorderActive || mBottomBorderActive) {
225 lp.cellVSpan += vSpanInc;
226 lp.cellY += cellYInc;
227 }
228
Adam Cohen3cba7222011-03-02 19:03:11 -0800229 mCellLayout.getExpandabilityArrayForView(mWidgetView, mExpandability);
Adam Cohend4844c32011-02-18 19:25:06 -0800230
231 // Update the cells occupied by this widget
232 mCellLayout.markCellsAsOccupiedForView(mWidgetView);
233 }
234
235 public void commitResizeForDelta(int deltaX, int deltaY) {
236 visualizeResizeForDelta(deltaX, deltaY);
237
238 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
239 LauncherModel.resizeItemInDatabase(getContext(), mItemInfo, lp.cellX, lp.cellY,
240 lp.cellHSpan, lp.cellVSpan);
241 mWidgetView.requestLayout();
242
243 // Once our widget resizes (hence the post), we want to snap the resize frame to it
244 post(new Runnable() {
245 public void run() {
246 snapToWidget(true);
247 }
248 });
249 }
250
251 public void snapToWidget(boolean animate) {
252 final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
253
Adam Cohen3cba7222011-03-02 19:03:11 -0800254 int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding;
255 int newHeight = mWidgetView.getHeight() + 2 * mBackgroundPadding;
256 int newX = mWidgetView.getLeft() - mBackgroundPadding;
257 int newY = mWidgetView.getTop() - mBackgroundPadding;
258
259 // We need to make sure the frame stays within the bounds of the CellLayout
260 if (newY < 0) {
261 newHeight -= -newY;
262 newY = 0;
263 }
264 if (newY + newHeight > mCellLayout.getHeight()) {
265 newHeight -= newY + newHeight - mCellLayout.getHeight();
266 }
267
Adam Cohend4844c32011-02-18 19:25:06 -0800268 if (!animate) {
269 lp.width = newWidth;
270 lp.height = newHeight;
271 lp.x = newX;
272 lp.y = newY;
273 mLeftHandle.setAlpha(1.0f);
274 mRightHandle.setAlpha(1.0f);
275 mTopHandle.setAlpha(1.0f);
276 mBottomHandle.setAlpha(1.0f);
277 requestLayout();
278 } else {
279 PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", lp.width, newWidth);
280 PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", lp.height, newHeight);
281 PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", lp.x, newX);
282 PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
283 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp, width, height, x, y);
284 ObjectAnimator leftOa = ObjectAnimator.ofFloat(mLeftHandle, "alpha", 1.0f);
285 ObjectAnimator rightOa = ObjectAnimator.ofFloat(mRightHandle, "alpha", 1.0f);
286 ObjectAnimator topOa = ObjectAnimator.ofFloat(mTopHandle, "alpha", 1.0f);
287 ObjectAnimator bottomOa = ObjectAnimator.ofFloat(mBottomHandle, "alpha", 1.0f);
288 oa.addUpdateListener(new AnimatorUpdateListener() {
289 public void onAnimationUpdate(ValueAnimator animation) {
290 requestLayout();
291 }
292 });
293 AnimatorSet set = new AnimatorSet();
294 if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
295 set.playTogether(oa, topOa, bottomOa);
296 } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
297 set.playTogether(oa, leftOa, rightOa);
298 } else {
299 set.playTogether(oa, leftOa, rightOa, topOa, bottomOa);
300 }
301
302 set.setDuration(SNAP_DURATION);
303 set.start();
304 }
305 }
306}