blob: 0caaf2e2cb4a45537a95d4115ac3496a672ce1ed [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Joe Onoratoa5902522009-07-30 13:37:37 -070017package com.android.launcher2;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070019import com.android.launcher.R;
Winson Chungaafa03c2010-06-11 17:34:16 -070020
21import android.app.WallpaperManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080022import android.content.Context;
Joe Onorato79e56262009-09-21 15:23:04 -040023import android.content.res.Resources;
Winson Chungaafa03c2010-06-11 17:34:16 -070024import android.content.res.TypedArray;
25import android.graphics.Canvas;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080026import android.graphics.Rect;
27import android.graphics.RectF;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070028import android.graphics.drawable.Drawable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080029import android.util.AttributeSet;
30import android.view.ContextMenu;
31import android.view.MotionEvent;
32import android.view.View;
33import android.view.ViewDebug;
34import android.view.ViewGroup;
Winson Chungaafa03c2010-06-11 17:34:16 -070035import android.view.animation.Animation;
36import android.view.animation.LayoutAnimationController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070038import java.util.Arrays;
Romain Guyedcce092010-03-04 13:03:17 -080039
Adam Cohen9415d872010-09-13 14:49:43 -070040public class CellLayout extends ViewGroup implements Dimmable {
Winson Chungaafa03c2010-06-11 17:34:16 -070041 static final String TAG = "CellLayout";
42
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043 private int mCellWidth;
44 private int mCellHeight;
Winson Chungaafa03c2010-06-11 17:34:16 -070045
Winson Chungaafa03c2010-06-11 17:34:16 -070046 private int mLeftPadding;
47 private int mRightPadding;
48 private int mTopPadding;
49 private int mBottomPadding;
50
Adam Cohend22015c2010-07-26 22:02:18 -070051 private int mCountX;
52 private int mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080053
54 private int mWidthGap;
55 private int mHeightGap;
56
57 private final Rect mRect = new Rect();
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -070058 private final RectF mRectF = new RectF();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059 private final CellInfo mCellInfo = new CellInfo();
Winson Chungaafa03c2010-06-11 17:34:16 -070060
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070061 // This is a temporary variable to prevent having to allocate a new object just to
62 // return an (x, y) value from helper functions. Do NOT use it to maintain other state.
63 private final int[] mTmpCellXY = new int[2];
64
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065 boolean[][] mOccupied;
66
Michael Jurkadee05892010-07-27 10:01:56 -070067 private OnTouchListener mInterceptTouchListener;
68
Michael Jurka5f1c5092010-09-03 14:15:02 -070069 private float mBackgroundAlpha;
70 private final Rect mBackgroundLayoutRect = new Rect();
Adam Cohen9415d872010-09-13 14:49:43 -070071
Michael Jurka5f1c5092010-09-03 14:15:02 -070072 private Drawable mBackground;
Adam Cohen9415d872010-09-13 14:49:43 -070073 private Drawable mBackgroundMini;
74 private Drawable mBackgroundMiniHover;
Michael Jurkaa63c4522010-08-19 13:52:27 -070075 // If we're actively dragging something over this screen and it's small,
76 // mHover is true
77 private boolean mHover = false;
Michael Jurkadee05892010-07-27 10:01:56 -070078
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070079 private final RectF mDragRect = new RectF();
80
81 // When dragging, used to indicate a vacant drop location
82 private Drawable mVacantDrawable;
83
84 // When dragging, used to indicate an occupied drop location
85 private Drawable mOccupiedDrawable;
86
87 // Updated to point to mVacantDrawable or mOccupiedDrawable, as appropriate
88 private Drawable mDragRectDrawable;
89
90 // When a drag operation is in progress, holds the nearest cell to the touch point
91 private final int[] mDragCell = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -080092
Winson Chungaafa03c2010-06-11 17:34:16 -070093 private final WallpaperManager mWallpaperManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080094
95 public CellLayout(Context context) {
96 this(context, null);
97 }
98
99 public CellLayout(Context context, AttributeSet attrs) {
100 this(context, attrs, 0);
101 }
102
103 public CellLayout(Context context, AttributeSet attrs, int defStyle) {
104 super(context, attrs, defStyle);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700105
106 // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
107 // the user where a dragged item will land when dropped.
108 setWillNotDraw(false);
109 mVacantDrawable = getResources().getDrawable(R.drawable.rounded_rect_green);
110 mOccupiedDrawable = getResources().getDrawable(R.drawable.rounded_rect_red);
111
Michael Jurkaa63c4522010-08-19 13:52:27 -0700112 if (LauncherApplication.isScreenXLarge()) {
Adam Cohen9415d872010-09-13 14:49:43 -0700113 mBackgroundMini = getResources().getDrawable(R.drawable.mini_home_screen_bg);
114 mBackgroundMini.setFilterBitmap(true);
115 mBackground = getResources().getDrawable(R.drawable.home_screen_bg);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700116 mBackground.setFilterBitmap(true);
Adam Cohen9415d872010-09-13 14:49:43 -0700117 mBackgroundMiniHover = getResources().getDrawable(R.drawable.mini_home_screen_bg_hover);
118 mBackgroundMiniHover.setFilterBitmap(true);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700119 }
120
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800121 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
122
123 mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
124 mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700125
Adam Cohend22015c2010-07-26 22:02:18 -0700126 mLeftPadding =
127 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisStartPadding, 10);
128 mRightPadding =
129 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisEndPadding, 10);
130 mTopPadding =
131 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisStartPadding, 10);
132 mBottomPadding =
133 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisEndPadding, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700134
Adam Cohend22015c2010-07-26 22:02:18 -0700135 mCountX = LauncherModel.getCellCountX();
136 mCountY = LauncherModel.getCellCountY();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700137 mOccupied = new boolean[mCountX][mCountY];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800138
139 a.recycle();
140
141 setAlwaysDrawnWithCacheEnabled(false);
142
Romain Guy84f296c2009-11-04 15:00:44 -0800143 mWallpaperManager = WallpaperManager.getInstance(getContext());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800144 }
145
Michael Jurkaa63c4522010-08-19 13:52:27 -0700146 public void setHover(boolean value) {
147 if (mHover != value) {
148 invalidate();
149 }
150 mHover = value;
151 }
152
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700153 @Override
Romain Guya6abce82009-11-10 02:54:41 -0800154 public void dispatchDraw(Canvas canvas) {
Michael Jurka5f1c5092010-09-03 14:15:02 -0700155 if (mBackgroundAlpha > 0.0f) {
Adam Cohen9415d872010-09-13 14:49:43 -0700156 Drawable bg;
157 if (mHover && getScaleX() < 0.5f) {
158 bg = mBackgroundMiniHover;
159 } else if (getScaleX() < 0.5f) {
160 bg = mBackgroundMini;
161 } else {
162 bg = mBackground;
163 }
Michael Jurka5f1c5092010-09-03 14:15:02 -0700164 bg.setAlpha((int) (mBackgroundAlpha * 255));
Michael Jurkaa63c4522010-08-19 13:52:27 -0700165 bg.draw(canvas);
166 }
Romain Guya6abce82009-11-10 02:54:41 -0800167 super.dispatchDraw(canvas);
168 }
169
170 @Override
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700171 protected void onDraw(Canvas canvas) {
172 if (!mDragRect.isEmpty()) {
173 mDragRectDrawable.setBounds(
174 (int)mDragRect.left,
175 (int)mDragRect.top,
176 (int)mDragRect.right,
177 (int)mDragRect.bottom);
178 mDragRectDrawable.draw(canvas);
179 }
Michael Jurka5f1c5092010-09-03 14:15:02 -0700180 super.onDraw(canvas);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700181 }
182
Adam Cohen9415d872010-09-13 14:49:43 -0700183 public void setDimmableProgress(float progress) {
184 for (int i = 0; i < getChildCount(); i++) {
185 Dimmable d = (Dimmable) getChildAt(i);
186 d.setDimmableProgress(progress);
187 }
188 }
189
190 public float getDimmableProgress() {
191 if (getChildCount() > 0) {
192 return ((Dimmable) getChildAt(0)).getDimmableProgress();
193 }
194 return 0.0f;
195 }
196
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700197 @Override
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700198 public void cancelLongPress() {
199 super.cancelLongPress();
200
201 // Cancel long press for all children
202 final int count = getChildCount();
203 for (int i = 0; i < count; i++) {
204 final View child = getChildAt(i);
205 child.cancelLongPress();
206 }
207 }
208
Michael Jurkadee05892010-07-27 10:01:56 -0700209 public void setOnInterceptTouchListener(View.OnTouchListener listener) {
210 mInterceptTouchListener = listener;
211 }
212
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800213 int getCountX() {
Adam Cohend22015c2010-07-26 22:02:18 -0700214 return mCountX;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800215 }
216
217 int getCountY() {
Adam Cohend22015c2010-07-26 22:02:18 -0700218 return mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800219 }
220
Winson Chungaafa03c2010-06-11 17:34:16 -0700221 // Takes canonical layout parameters
222 public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
223 final LayoutParams lp = params;
224
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800225 // Generate an id for each view, this assumes we have at most 256x256 cells
226 // per workspace screen
Adam Cohend22015c2010-07-26 22:02:18 -0700227 if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700228 // If the horizontal or vertical span is set to -1, it is taken to
229 // mean that it spans the extent of the CellLayout
Adam Cohend22015c2010-07-26 22:02:18 -0700230 if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
231 if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800232
Winson Chungaafa03c2010-06-11 17:34:16 -0700233 child.setId(childId);
234
Michael Jurkadee05892010-07-27 10:01:56 -0700235 // We might be in the middle or end of shrinking/fading to a dimmed view
236 // Make sure this view's alpha is set the same as all the rest of the views
Michael Jurka5f1c5092010-09-03 14:15:02 -0700237 child.setAlpha(getAlpha());
Winson Chungaafa03c2010-06-11 17:34:16 -0700238 addView(child, index, lp);
Michael Jurkadee05892010-07-27 10:01:56 -0700239
Michael Jurka0280c3b2010-09-17 15:00:07 -0700240 markCellsAsOccupiedForView(child);
241
Winson Chungaafa03c2010-06-11 17:34:16 -0700242 return true;
243 }
244 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800245 }
246
247 @Override
Michael Jurka0280c3b2010-09-17 15:00:07 -0700248 public void removeAllViews() {
249 clearOccupiedCells();
250 }
251
252 @Override
253 public void removeAllViewsInLayout() {
254 clearOccupiedCells();
255 }
256
257 @Override
258 public void removeView(View view) {
259 markCellsAsUnoccupiedForView(view);
260 super.removeView(view);
261 }
262
263 @Override
264 public void removeViewAt(int index) {
265 markCellsAsUnoccupiedForView(getChildAt(index));
266 super.removeViewAt(index);
267 }
268
269 @Override
270 public void removeViewInLayout(View view) {
271 markCellsAsUnoccupiedForView(view);
272 super.removeViewInLayout(view);
273 }
274
275 @Override
276 public void removeViews(int start, int count) {
277 for (int i = start; i < start + count; i++) {
278 markCellsAsUnoccupiedForView(getChildAt(i));
279 }
280 super.removeViews(start, count);
281 }
282
283 @Override
284 public void removeViewsInLayout(int start, int count) {
285 for (int i = start; i < start + count; i++) {
286 markCellsAsUnoccupiedForView(getChildAt(i));
287 }
288 super.removeViewsInLayout(start, count);
289 }
290
291 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800292 public void requestChildFocus(View child, View focused) {
293 super.requestChildFocus(child, focused);
294 if (child != null) {
295 Rect r = new Rect();
296 child.getDrawingRect(r);
297 requestRectangleOnScreen(r);
298 }
299 }
300
301 @Override
302 protected void onAttachedToWindow() {
303 super.onAttachedToWindow();
304 mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
305 }
306
Michael Jurkaaf442092010-06-10 17:01:57 -0700307 public void setTagToCellInfoForPoint(int touchX, int touchY) {
308 final CellInfo cellInfo = mCellInfo;
309 final Rect frame = mRect;
310 final int x = touchX + mScrollX;
311 final int y = touchY + mScrollY;
312 final int count = getChildCount();
313
314 boolean found = false;
315 for (int i = count - 1; i >= 0; i--) {
316 final View child = getChildAt(i);
317
318 if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
319 child.getHitRect(frame);
320 if (frame.contains(x, y)) {
321 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
322 cellInfo.cell = child;
323 cellInfo.cellX = lp.cellX;
324 cellInfo.cellY = lp.cellY;
325 cellInfo.spanX = lp.cellHSpan;
326 cellInfo.spanY = lp.cellVSpan;
327 cellInfo.valid = true;
328 found = true;
Michael Jurkaaf442092010-06-10 17:01:57 -0700329 break;
330 }
331 }
332 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700333
Michael Jurkaaf442092010-06-10 17:01:57 -0700334 if (!found) {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700335 final int cellXY[] = mTmpCellXY;
Michael Jurkaaf442092010-06-10 17:01:57 -0700336 pointToCellExact(x, y, cellXY);
337
Michael Jurkaaf442092010-06-10 17:01:57 -0700338 cellInfo.cell = null;
339 cellInfo.cellX = cellXY[0];
340 cellInfo.cellY = cellXY[1];
341 cellInfo.spanX = 1;
342 cellInfo.spanY = 1;
Michael Jurka0280c3b2010-09-17 15:00:07 -0700343 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < mCountX &&
344 cellXY[1] < mCountY && !mOccupied[cellXY[0]][cellXY[1]];
Michael Jurkaaf442092010-06-10 17:01:57 -0700345 }
346 setTag(cellInfo);
347 }
348
Winson Chungaafa03c2010-06-11 17:34:16 -0700349
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800350 @Override
351 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkadee05892010-07-27 10:01:56 -0700352 if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
353 return true;
354 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800355 final int action = ev.getAction();
356 final CellInfo cellInfo = mCellInfo;
357
358 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkaaf442092010-06-10 17:01:57 -0700359 setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800360 } else if (action == MotionEvent.ACTION_UP) {
361 cellInfo.cell = null;
362 cellInfo.cellX = -1;
363 cellInfo.cellY = -1;
364 cellInfo.spanX = 0;
365 cellInfo.spanY = 0;
366 cellInfo.valid = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800367 setTag(cellInfo);
368 }
369
370 return false;
371 }
372
373 @Override
374 public CellInfo getTag() {
Michael Jurka0280c3b2010-09-17 15:00:07 -0700375 return (CellInfo) super.getTag();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800376 }
377
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700378 /**
379 * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
380 */
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800381 private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
382 for (int x = left; x <= right; x++) {
383 if (occupied[x][y]) {
384 return false;
385 }
386 }
387 return true;
388 }
389
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800390 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700391 * Given a point, return the cell that strictly encloses that point
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800392 * @param x X coordinate of the point
393 * @param y Y coordinate of the point
394 * @param result Array of 2 ints to hold the x and y coordinate of the cell
395 */
396 void pointToCellExact(int x, int y, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700397 final int hStartPadding = getLeftPadding();
398 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800399
400 result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
401 result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
402
Adam Cohend22015c2010-07-26 22:02:18 -0700403 final int xAxis = mCountX;
404 final int yAxis = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800405
406 if (result[0] < 0) result[0] = 0;
407 if (result[0] >= xAxis) result[0] = xAxis - 1;
408 if (result[1] < 0) result[1] = 0;
409 if (result[1] >= yAxis) result[1] = yAxis - 1;
410 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700411
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800412 /**
413 * Given a point, return the cell that most closely encloses that point
414 * @param x X coordinate of the point
415 * @param y Y coordinate of the point
416 * @param result Array of 2 ints to hold the x and y coordinate of the cell
417 */
418 void pointToCellRounded(int x, int y, int[] result) {
419 pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
420 }
421
422 /**
423 * Given a cell coordinate, return the point that represents the upper left corner of that cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700424 *
425 * @param cellX X coordinate of the cell
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800426 * @param cellY Y coordinate of the cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700427 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800428 * @param result Array of 2 ints to hold the x and y coordinate of the point
429 */
430 void cellToPoint(int cellX, int cellY, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700431 final int hStartPadding = getLeftPadding();
432 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800433
434 result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
435 result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
436 }
437
Romain Guy84f296c2009-11-04 15:00:44 -0800438 int getCellWidth() {
439 return mCellWidth;
440 }
441
442 int getCellHeight() {
443 return mCellHeight;
444 }
445
Romain Guy1a304a12009-11-10 00:02:32 -0800446 int getLeftPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700447 return mLeftPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800448 }
449
450 int getTopPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700451 return mTopPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800452 }
453
454 int getRightPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700455 return mRightPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800456 }
457
458 int getBottomPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700459 return mBottomPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800460 }
461
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800462 @Override
463 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
464 // TODO: currently ignoring padding
Winson Chungaafa03c2010-06-11 17:34:16 -0700465
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800466 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700467 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
468
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800469 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
470 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700471
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800472 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
473 throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
474 }
475
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800476 final int cellWidth = mCellWidth;
477 final int cellHeight = mCellHeight;
478
Adam Cohend22015c2010-07-26 22:02:18 -0700479 int numWidthGaps = mCountX - 1;
480 int numHeightGaps = mCountY - 1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800481
Michael Jurka0280c3b2010-09-17 15:00:07 -0700482 int vSpaceLeft = heightSpecSize - mTopPadding - mBottomPadding - (cellHeight * mCountY);
Adam Cohend22015c2010-07-26 22:02:18 -0700483 mHeightGap = vSpaceLeft / numHeightGaps;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800484
Michael Jurka0280c3b2010-09-17 15:00:07 -0700485 int hSpaceLeft = widthSpecSize - mLeftPadding - mRightPadding - (cellWidth * mCountX);
Adam Cohend22015c2010-07-26 22:02:18 -0700486 mWidthGap = hSpaceLeft / numWidthGaps;
Winson Chungaafa03c2010-06-11 17:34:16 -0700487
Michael Jurka5f1c5092010-09-03 14:15:02 -0700488 // center it around the min gaps
489 int minGap = Math.min(mWidthGap, mHeightGap);
490 mWidthGap = mHeightGap = minGap;
491
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800492 int count = getChildCount();
493
494 for (int i = 0; i < count; i++) {
495 View child = getChildAt(i);
496 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Winson Chungaafa03c2010-06-11 17:34:16 -0700497 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
498 mLeftPadding, mTopPadding);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800499
Michael Jurka0280c3b2010-09-17 15:00:07 -0700500 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
Winson Chungaafa03c2010-06-11 17:34:16 -0700501 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
502 MeasureSpec.EXACTLY);
503
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800504 child.measure(childWidthMeasureSpec, childheightMeasureSpec);
505 }
Michael Jurka5f1c5092010-09-03 14:15:02 -0700506 if (widthSpecMode == MeasureSpec.AT_MOST) {
507 int newWidth = mLeftPadding + mRightPadding + (mCountX * cellWidth) +
508 ((mCountX - 1) * minGap);
509 int newHeight = mTopPadding + mBottomPadding + (mCountY * cellHeight) +
510 ((mCountY - 1) * minGap);
511 setMeasuredDimension(newWidth, newHeight);
512 } else if (widthSpecMode == MeasureSpec.EXACTLY) {
513 setMeasuredDimension(widthSpecSize, heightSpecSize);
514 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800515 }
516
517 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -0700518 protected void onLayout(boolean changed, int l, int t, int r, int b) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800519 int count = getChildCount();
520
521 for (int i = 0; i < count; i++) {
522 View child = getChildAt(i);
523 if (child.getVisibility() != GONE) {
524
525 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
526
527 int childLeft = lp.x;
528 int childTop = lp.y;
529 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
Romain Guy84f296c2009-11-04 15:00:44 -0800530
531 if (lp.dropped) {
532 lp.dropped = false;
533
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700534 final int[] cellXY = mTmpCellXY;
Romain Guy06762ab2010-01-25 16:51:08 -0800535 getLocationOnScreen(cellXY);
Romain Guy84f296c2009-11-04 15:00:44 -0800536 mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
Romain Guy06762ab2010-01-25 16:51:08 -0800537 cellXY[0] + childLeft + lp.width / 2,
538 cellXY[1] + childTop + lp.height / 2, 0, null);
Romain Guy84f296c2009-11-04 15:00:44 -0800539 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800540 }
541 }
542 }
543
544 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700545 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
546 super.onSizeChanged(w, h, oldw, oldh);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700547 mBackgroundLayoutRect.set(0, 0, w, h);
548 if (mBackground != null) {
549 mBackground.setBounds(mBackgroundLayoutRect);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700550 }
Adam Cohen9415d872010-09-13 14:49:43 -0700551 if (mBackgroundMiniHover != null) {
552 mBackgroundMiniHover.setBounds(mBackgroundLayoutRect);
553 }
554 if (mBackgroundMini != null) {
555 mBackgroundMini.setBounds(mBackgroundLayoutRect);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700556 }
Michael Jurkadee05892010-07-27 10:01:56 -0700557 }
558
559 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800560 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
561 final int count = getChildCount();
562 for (int i = 0; i < count; i++) {
563 final View view = getChildAt(i);
564 view.setDrawingCacheEnabled(enabled);
565 // Update the drawing caches
Adam Powellfefa0ce2010-05-03 10:23:50 -0700566 view.buildDrawingCache(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800567 }
568 }
569
570 @Override
571 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
572 super.setChildrenDrawnWithCacheEnabled(enabled);
573 }
574
Michael Jurka5f1c5092010-09-03 14:15:02 -0700575 public float getBackgroundAlpha() {
576 return mBackgroundAlpha;
Michael Jurkadee05892010-07-27 10:01:56 -0700577 }
578
Michael Jurka5f1c5092010-09-03 14:15:02 -0700579 public void setBackgroundAlpha(float alpha) {
580 mBackgroundAlpha = alpha;
Michael Jurka0142d492010-08-25 17:46:15 -0700581 invalidate();
Michael Jurkadee05892010-07-27 10:01:56 -0700582 }
583
Michael Jurka5f1c5092010-09-03 14:15:02 -0700584 // Need to return true to let the view system know we know how to handle alpha-- this is
585 // because when our children have an alpha of 0.0f, they are still rendering their "dimmed"
586 // versions
587 @Override
588 protected boolean onSetAlpha(int alpha) {
589 return true;
590 }
591
592 public void setAlpha(float alpha) {
593 setChildrenAlpha(alpha);
594 super.setAlpha(alpha);
595 }
596
Michael Jurkadee05892010-07-27 10:01:56 -0700597 private void setChildrenAlpha(float alpha) {
Michael Jurka0142d492010-08-25 17:46:15 -0700598 final int childCount = getChildCount();
599 for (int i = 0; i < childCount; i++) {
Michael Jurkadee05892010-07-27 10:01:56 -0700600 getChildAt(i).setAlpha(alpha);
601 }
602 }
603
Michael Jurka0280c3b2010-09-17 15:00:07 -0700604 private boolean isVacantIgnoring(
605 int originX, int originY, int spanX, int spanY, View ignoreView) {
606 if (ignoreView != null) {
607 markCellsAsUnoccupiedForView(ignoreView);
608 }
Michael Jurka28750fb2010-09-24 17:43:49 -0700609 boolean isVacant = true;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700610 for (int i = 0; i < spanY; i++) {
611 if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
Michael Jurka28750fb2010-09-24 17:43:49 -0700612 isVacant = false;
613 break;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700614 }
615 }
Michael Jurka0280c3b2010-09-17 15:00:07 -0700616 if (ignoreView != null) {
617 markCellsAsOccupiedForView(ignoreView);
618 }
Michael Jurka28750fb2010-09-24 17:43:49 -0700619 return isVacant;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700620 }
621
Michael Jurka0280c3b2010-09-17 15:00:07 -0700622 private boolean isVacant(int originX, int originY, int spanX, int spanY) {
623 return isVacantIgnoring(originX, originY, spanX, spanY, null);
624 }
625
Patrick Dubroy440c3602010-07-13 17:50:32 -0700626 public View getChildAt(int x, int y) {
627 final int count = getChildCount();
628 for (int i = 0; i < count; i++) {
629 View child = getChildAt(i);
630 LayoutParams lp = (LayoutParams) child.getLayoutParams();
631
632 if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
633 (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
634 return child;
635 }
636 }
637 return null;
638 }
639
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700640 /**
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700641 * Estimate the size that a child with the given dimensions will take in the layout.
642 */
643 void estimateChildSize(int minWidth, int minHeight, int[] result) {
644 // Assuming it's placed at 0, 0, find where the bottom right cell will land
645 rectToCell(minWidth, minHeight, result);
646
647 // Then figure out the rect it will occupy
648 cellToRect(0, 0, result[0], result[1], mRectF);
649 result[0] = (int)mRectF.width();
650 result[1] = (int)mRectF.height();
651 }
652
653 /**
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700654 * Estimate where the top left cell of the dragged item will land if it is dropped.
655 *
656 * @param originX The X value of the top left corner of the item
657 * @param originY The Y value of the top left corner of the item
658 * @param spanX The number of horizontal cells that the item spans
659 * @param spanY The number of vertical cells that the item spans
660 * @param result The estimated drop cell X and Y.
661 */
662 void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
Adam Cohend22015c2010-07-26 22:02:18 -0700663 final int countX = mCountX;
664 final int countY = mCountY;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700665
Michael Jurkaa63c4522010-08-19 13:52:27 -0700666 // pointToCellRounded takes the top left of a cell but will pad that with
667 // cellWidth/2 and cellHeight/2 when finding the matching cell
668 pointToCellRounded(originX, originY, result);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700669
670 // If the item isn't fully on this screen, snap to the edges
671 int rightOverhang = result[0] + spanX - countX;
672 if (rightOverhang > 0) {
673 result[0] -= rightOverhang; // Snap to right
674 }
675 result[0] = Math.max(0, result[0]); // Snap to left
676 int bottomOverhang = result[1] + spanY - countY;
677 if (bottomOverhang > 0) {
678 result[1] -= bottomOverhang; // Snap to bottom
679 }
680 result[1] = Math.max(0, result[1]); // Snap to top
681 }
682
Michael Jurka0280c3b2010-09-17 15:00:07 -0700683 void visualizeDropLocation(
684 View view, int originX, int originY, int spanX, int spanY, View draggedItem) {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700685 final int[] originCell = mDragCell;
686 final int[] cellXY = mTmpCellXY;
687 estimateDropCell(originX, originY, spanX, spanY, cellXY);
688
689 // Only recalculate the bounding rect when necessary
690 if (!Arrays.equals(cellXY, originCell)) {
691 originCell[0] = cellXY[0];
692 originCell[1] = cellXY[1];
693
694 // Find the top left corner of the rect the object will occupy
695 final int[] topLeft = mTmpCellXY;
696 cellToPoint(originCell[0], originCell[1], topLeft);
697 final int left = topLeft[0];
698 final int top = topLeft[1];
699
700 // Now find the bottom right
701 final int[] bottomRight = mTmpCellXY;
702 cellToPoint(originCell[0] + spanX - 1, originCell[1] + spanY - 1, bottomRight);
703 bottomRight[0] += mCellWidth;
704 bottomRight[1] += mCellHeight;
705
Michael Jurka0280c3b2010-09-17 15:00:07 -0700706 boolean vacant =
707 isVacantIgnoring(originCell[0], originCell[1], spanX, spanY, draggedItem);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700708 mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable;
709
710 // mDragRect will be rendered in onDraw()
711 mDragRect.set(left, top, bottomRight[0], bottomRight[1]);
712 invalidate();
713 }
714 }
715
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800716 /**
Jeff Sharkey70864282009-04-07 21:08:40 -0700717 * Find a vacant area that will fit the given bounds nearest the requested
718 * cell location. Uses Euclidean distance to score multiple vacant areas.
Winson Chungaafa03c2010-06-11 17:34:16 -0700719 *
Romain Guy51afc022009-05-04 18:03:43 -0700720 * @param pixelX The X location at which you want to search for a vacant area.
721 * @param pixelY The Y location at which you want to search for a vacant area.
Jeff Sharkey70864282009-04-07 21:08:40 -0700722 * @param spanX Horizontal span of the object.
723 * @param spanY Vertical span of the object.
724 * @param vacantCells Pre-computed set of vacant cells to search.
725 * @param recycle Previously returned value to possibly recycle.
726 * @return The X, Y cell of a vacant area that can contain this object,
727 * nearest the requested location.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800728 */
Michael Jurka6a1435d2010-09-27 17:35:12 -0700729 int[] findNearestVacantArea(
730 int pixelX, int pixelY, int spanX, int spanY, int[] recycle) {
731 return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, recycle);
732 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700733
Michael Jurka6a1435d2010-09-27 17:35:12 -0700734 /**
735 * Find a vacant area that will fit the given bounds nearest the requested
736 * cell location. Uses Euclidean distance to score multiple vacant areas.
737 *
738 * @param pixelX The X location at which you want to search for a vacant area.
739 * @param pixelY The Y location at which you want to search for a vacant area.
740 * @param spanX Horizontal span of the object.
741 * @param spanY Vertical span of the object.
742 * @param vacantCells Pre-computed set of vacant cells to search.
743 * @param recycle Previously returned value to possibly recycle.
744 * @param ignoreView Considers space occupied by this view as unoccupied
745 * @return The X, Y cell of a vacant area that can contain this object,
746 * nearest the requested location.
747 */
748 int[] findNearestVacantArea(
749 int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] recycle) {
750 if (ignoreView != null) {
751 markCellsAsUnoccupiedForView(ignoreView);
752 }
Jeff Sharkey70864282009-04-07 21:08:40 -0700753 // Keep track of best-scoring drop area
754 final int[] bestXY = recycle != null ? recycle : new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700755 double bestDistance = Double.MAX_VALUE;
Winson Chungaafa03c2010-06-11 17:34:16 -0700756
Michael Jurkac28de512010-08-13 11:27:44 -0700757 for (int x = 0; x < mCountX - (spanX - 1); x++) {
758 inner:
759 for (int y = 0; y < mCountY - (spanY - 1); y++) {
760 for (int i = 0; i < spanX; i++) {
761 for (int j = 0; j < spanY; j++) {
762 if (mOccupied[x + i][y + j]) {
763 // small optimization: we can skip to below the row we just found
764 // an occupied cell
765 y += j;
766 continue inner;
767 }
768 }
769 }
770 final int[] cellXY = mTmpCellXY;
771 cellToPoint(x, y, cellXY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800772
Michael Jurkac28de512010-08-13 11:27:44 -0700773 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
774 + Math.pow(cellXY[1] - pixelY, 2));
775 if (distance <= bestDistance) {
776 bestDistance = distance;
777 bestXY[0] = x;
778 bestXY[1] = y;
779 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800780 }
781 }
Michael Jurka6a1435d2010-09-27 17:35:12 -0700782 if (ignoreView != null) {
783 markCellsAsOccupiedForView(ignoreView);
784 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800785
Winson Chungaafa03c2010-06-11 17:34:16 -0700786 // Return null if no suitable location found
Jeff Sharkey70864282009-04-07 21:08:40 -0700787 if (bestDistance < Double.MAX_VALUE) {
788 return bestXY;
789 } else {
790 return null;
791 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800792 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700793
Michael Jurka0280c3b2010-09-17 15:00:07 -0700794 boolean existsEmptyCell() {
795 return findCellForSpan(null, 1, 1);
796 }
797
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800798 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -0700799 * Finds the upper-left coordinate of the first rectangle in the grid that can
800 * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
801 * then this method will only return coordinates for rectangles that contain the cell
802 * (intersectX, intersectY)
803 *
804 * @param cellXY The array that will contain the position of a vacant cell if such a cell
805 * can be found.
806 * @param spanX The horizontal span of the cell we want to find.
807 * @param spanY The vertical span of the cell we want to find.
808 *
809 * @return True if a vacant cell of the specified dimension was found, false otherwise.
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700810 */
Michael Jurka0280c3b2010-09-17 15:00:07 -0700811 boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
812 return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null);
813 }
814
815 /**
816 * Like above, but ignores any cells occupied by the item "ignoreView"
817 *
818 * @param cellXY The array that will contain the position of a vacant cell if such a cell
819 * can be found.
820 * @param spanX The horizontal span of the cell we want to find.
821 * @param spanY The vertical span of the cell we want to find.
822 * @param ignoreView The home screen item we should treat as not occupying any space
823 * @return
824 */
825 boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
826 return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, ignoreView);
827 }
828
829 /**
830 * Like above, but if intersectX and intersectY are not -1, then this method will try to
831 * return coordinates for rectangles that contain the cell [intersectX, intersectY]
832 *
833 * @param spanX The horizontal span of the cell we want to find.
834 * @param spanY The vertical span of the cell we want to find.
835 * @param ignoreView The home screen item we should treat as not occupying any space
836 * @param intersectX The X coordinate of the cell that we should try to overlap
837 * @param intersectX The Y coordinate of the cell that we should try to overlap
838 *
839 * @return True if a vacant cell of the specified dimension was found, false otherwise.
840 */
841 boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
842 int intersectX, int intersectY) {
843 return findCellForSpanThatIntersectsIgnoring(
844 cellXY, spanX, spanY, intersectX, intersectY, null);
845 }
846
847 /**
848 * The superset of the above two methods
849 */
850 boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
851 int intersectX, int intersectY, View ignoreView) {
852 if (ignoreView != null) {
853 markCellsAsUnoccupiedForView(ignoreView);
854 }
855
Michael Jurka28750fb2010-09-24 17:43:49 -0700856 boolean foundCell = false;
Michael Jurka0280c3b2010-09-17 15:00:07 -0700857 while (true) {
858 int startX = 0;
859 if (intersectX >= 0) {
860 startX = Math.max(startX, intersectX - (spanX - 1));
861 }
862 int endX = mCountX - (spanX - 1);
863 if (intersectX >= 0) {
864 endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0));
865 }
866 int startY = 0;
867 if (intersectY >= 0) {
868 startY = Math.max(startY, intersectY - (spanY - 1));
869 }
870 int endY = mCountY - (spanY - 1);
871 if (intersectY >= 0) {
872 endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0));
873 }
874
875 for (int x = startX; x < endX; x++) {
876 inner:
877 for (int y = startY; y < endY; y++) {
878 for (int i = 0; i < spanX; i++) {
879 for (int j = 0; j < spanY; j++) {
880 if (mOccupied[x + i][y + j]) {
881 // small optimization: we can skip to below the row we just found
882 // an occupied cell
883 y += j;
884 continue inner;
885 }
886 }
887 }
888 if (cellXY != null) {
889 cellXY[0] = x;
890 cellXY[1] = y;
891 }
Michael Jurka28750fb2010-09-24 17:43:49 -0700892 foundCell = true;
893 break;
Michael Jurka0280c3b2010-09-17 15:00:07 -0700894 }
895 }
896 if (intersectX == -1 && intersectY == -1) {
897 break;
898 } else {
899 // if we failed to find anything, try again but without any requirements of
900 // intersecting
901 intersectX = -1;
902 intersectY = -1;
903 continue;
904 }
905 }
906
907 if (ignoreView != null) {
908 markCellsAsOccupiedForView(ignoreView);
909 }
Michael Jurka28750fb2010-09-24 17:43:49 -0700910 return foundCell;
Michael Jurka0280c3b2010-09-17 15:00:07 -0700911 }
912
913 /**
914 * Called when drag has left this CellLayout or has been completed (successfully or not)
915 */
916 void onDragExit() {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700917 // Invalidate the drag data
918 mDragCell[0] = -1;
919 mDragCell[1] = -1;
920
Michael Jurkaa63c4522010-08-19 13:52:27 -0700921 setHover(false);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700922 mDragRect.setEmpty();
923 invalidate();
924 }
925
926 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700927 * Mark a child as having been dropped.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800928 *
929 * @param child The child that is being dropped
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800930 */
Winson Chungaafa03c2010-06-11 17:34:16 -0700931 void onDropChild(View child) {
Romain Guyd94533d2009-08-17 10:01:15 -0700932 if (child != null) {
933 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Romain Guyd94533d2009-08-17 10:01:15 -0700934 lp.isDragging = false;
Romain Guy84f296c2009-11-04 15:00:44 -0800935 lp.dropped = true;
Romain Guyd94533d2009-08-17 10:01:15 -0700936 mDragRect.setEmpty();
937 child.requestLayout();
Romain Guyd94533d2009-08-17 10:01:15 -0700938 }
Michael Jurka0280c3b2010-09-17 15:00:07 -0700939 onDragExit();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800940 }
941
942 void onDropAborted(View child) {
943 if (child != null) {
944 ((LayoutParams) child.getLayoutParams()).isDragging = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800945 }
Michael Jurka0280c3b2010-09-17 15:00:07 -0700946 onDragExit();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800947 }
948
949 /**
950 * Start dragging the specified child
Winson Chungaafa03c2010-06-11 17:34:16 -0700951 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800952 * @param child The child that is being dragged
953 */
954 void onDragChild(View child) {
955 LayoutParams lp = (LayoutParams) child.getLayoutParams();
956 lp.isDragging = true;
957 mDragRect.setEmpty();
958 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700959
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800960 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800961 * Computes a bounding rectangle for a range of cells
Winson Chungaafa03c2010-06-11 17:34:16 -0700962 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800963 * @param cellX X coordinate of upper left corner expressed as a cell position
964 * @param cellY Y coordinate of upper left corner expressed as a cell position
Winson Chungaafa03c2010-06-11 17:34:16 -0700965 * @param cellHSpan Width in cells
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800966 * @param cellVSpan Height in cells
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700967 * @param resultRect Rect into which to put the results
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800968 */
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700969 public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800970 final int cellWidth = mCellWidth;
971 final int cellHeight = mCellHeight;
972 final int widthGap = mWidthGap;
973 final int heightGap = mHeightGap;
Winson Chungaafa03c2010-06-11 17:34:16 -0700974
975 final int hStartPadding = getLeftPadding();
976 final int vStartPadding = getTopPadding();
977
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800978 int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
979 int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
980
981 int x = hStartPadding + cellX * (cellWidth + widthGap);
982 int y = vStartPadding + cellY * (cellHeight + heightGap);
Winson Chungaafa03c2010-06-11 17:34:16 -0700983
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700984 resultRect.set(x, y, x + width, y + height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800985 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700986
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800987 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700988 * Computes the required horizontal and vertical cell spans to always
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800989 * fit the given rectangle.
Winson Chungaafa03c2010-06-11 17:34:16 -0700990 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800991 * @param width Width in pixels
992 * @param height Height in pixels
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700993 * @param result An array of length 2 in which to store the result (may be null).
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800994 */
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700995 public int[] rectToCell(int width, int height, int[] result) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800996 // Always assume we're working with the smallest span to make sure we
997 // reserve enough space in both orientations.
Joe Onorato79e56262009-09-21 15:23:04 -0400998 final Resources resources = getResources();
999 int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
1000 int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001001 int smallerSize = Math.min(actualWidth, actualHeight);
Joe Onorato79e56262009-09-21 15:23:04 -04001002
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001003 // Always round up to next largest cell
1004 int spanX = (width + smallerSize) / smallerSize;
1005 int spanY = (height + smallerSize) / smallerSize;
Joe Onorato79e56262009-09-21 15:23:04 -04001006
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -07001007 if (result == null) {
1008 return new int[] { spanX, spanY };
1009 }
1010 result[0] = spanX;
1011 result[1] = spanY;
1012 return result;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001013 }
1014
1015 /**
1016 * Find the first vacant cell, if there is one.
1017 *
1018 * @param vacant Holds the x and y coordinate of the vacant cell
1019 * @param spanX Horizontal cell span.
1020 * @param spanY Vertical cell span.
Winson Chungaafa03c2010-06-11 17:34:16 -07001021 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001022 * @return True if a vacant cell was found
1023 */
1024 public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001025
Michael Jurka0280c3b2010-09-17 15:00:07 -07001026 return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001027 }
1028
1029 static boolean findVacantCell(int[] vacant, int spanX, int spanY,
1030 int xCount, int yCount, boolean[][] occupied) {
1031
1032 for (int x = 0; x < xCount; x++) {
1033 for (int y = 0; y < yCount; y++) {
1034 boolean available = !occupied[x][y];
1035out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
1036 for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
1037 available = available && !occupied[i][j];
1038 if (!available) break out;
1039 }
1040 }
1041
1042 if (available) {
1043 vacant[0] = x;
1044 vacant[1] = y;
1045 return true;
1046 }
1047 }
1048 }
1049
1050 return false;
1051 }
1052
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001053 /**
1054 * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
1055 */
1056 boolean[] getOccupiedCellsFlattened() {
Adam Cohend22015c2010-07-26 22:02:18 -07001057 final int xCount = mCountX;
1058 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001059 final boolean[][] occupied = mOccupied;
1060
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001061 final boolean[] flat = new boolean[xCount * yCount];
1062 for (int y = 0; y < yCount; y++) {
1063 for (int x = 0; x < xCount; x++) {
1064 flat[y * xCount + x] = occupied[x][y];
1065 }
1066 }
1067
1068 return flat;
1069 }
1070
Michael Jurka0280c3b2010-09-17 15:00:07 -07001071 private void clearOccupiedCells() {
1072 for (int x = 0; x < mCountX; x++) {
1073 for (int y = 0; y < mCountY; y++) {
1074 mOccupied[x][y] = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001075 }
1076 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07001077 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001078
Michael Jurka0280c3b2010-09-17 15:00:07 -07001079 public void onMove(View view, int newCellX, int newCellY) {
1080 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1081 markCellsAsUnoccupiedForView(view);
1082 markCellsForView(newCellX, newCellY, lp.cellHSpan, lp.cellVSpan, true);
1083 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001084
Michael Jurka0280c3b2010-09-17 15:00:07 -07001085 private void markCellsAsOccupiedForView(View view) {
1086 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1087 markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
1088 }
1089
1090 private void markCellsAsUnoccupiedForView(View view) {
1091 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1092 markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
1093 }
1094
1095 private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean value) {
1096 for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
1097 for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
1098 mOccupied[x][y] = value;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001099 }
1100 }
1101 }
1102
1103 @Override
1104 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
1105 return new CellLayout.LayoutParams(getContext(), attrs);
1106 }
1107
1108 @Override
1109 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1110 return p instanceof CellLayout.LayoutParams;
1111 }
1112
1113 @Override
1114 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1115 return new CellLayout.LayoutParams(p);
1116 }
1117
Winson Chungaafa03c2010-06-11 17:34:16 -07001118 public static class CellLayoutAnimationController extends LayoutAnimationController {
1119 public CellLayoutAnimationController(Animation animation, float delay) {
1120 super(animation, delay);
1121 }
1122
1123 @Override
1124 protected long getDelayForView(View view) {
1125 return (int) (Math.random() * 150);
1126 }
1127 }
1128
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001129 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1130 /**
1131 * Horizontal location of the item in the grid.
1132 */
1133 @ViewDebug.ExportedProperty
1134 public int cellX;
1135
1136 /**
1137 * Vertical location of the item in the grid.
1138 */
1139 @ViewDebug.ExportedProperty
1140 public int cellY;
1141
1142 /**
1143 * Number of cells spanned horizontally by the item.
1144 */
1145 @ViewDebug.ExportedProperty
1146 public int cellHSpan;
1147
1148 /**
1149 * Number of cells spanned vertically by the item.
1150 */
1151 @ViewDebug.ExportedProperty
1152 public int cellVSpan;
Winson Chungaafa03c2010-06-11 17:34:16 -07001153
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001154 /**
1155 * Is this item currently being dragged
1156 */
1157 public boolean isDragging;
1158
1159 // X coordinate of the view in the layout.
1160 @ViewDebug.ExportedProperty
1161 int x;
1162 // Y coordinate of the view in the layout.
1163 @ViewDebug.ExportedProperty
1164 int y;
1165
Romain Guy84f296c2009-11-04 15:00:44 -08001166 boolean dropped;
Romain Guyfcb9e712009-10-02 16:06:52 -07001167
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001168 public LayoutParams(Context c, AttributeSet attrs) {
1169 super(c, attrs);
1170 cellHSpan = 1;
1171 cellVSpan = 1;
1172 }
1173
1174 public LayoutParams(ViewGroup.LayoutParams source) {
1175 super(source);
1176 cellHSpan = 1;
1177 cellVSpan = 1;
1178 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001179
1180 public LayoutParams(LayoutParams source) {
1181 super(source);
1182 this.cellX = source.cellX;
1183 this.cellY = source.cellY;
1184 this.cellHSpan = source.cellHSpan;
1185 this.cellVSpan = source.cellVSpan;
1186 }
1187
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001188 public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
Romain Guy8f19cdd2010-01-08 15:07:00 -08001189 super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001190 this.cellX = cellX;
1191 this.cellY = cellY;
1192 this.cellHSpan = cellHSpan;
1193 this.cellVSpan = cellVSpan;
1194 }
1195
1196 public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
1197 int hStartPadding, int vStartPadding) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001198
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001199 final int myCellHSpan = cellHSpan;
1200 final int myCellVSpan = cellVSpan;
1201 final int myCellX = cellX;
1202 final int myCellY = cellY;
Winson Chungaafa03c2010-06-11 17:34:16 -07001203
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001204 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
1205 leftMargin - rightMargin;
1206 height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
1207 topMargin - bottomMargin;
1208
1209 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
1210 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
1211 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001212
1213 public String toString() {
1214 return "(" + this.cellX + ", " + this.cellY + ")";
1215 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001216 }
1217
Michael Jurka0280c3b2010-09-17 15:00:07 -07001218 // This class stores info for two purposes:
1219 // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY,
1220 // its spanX, spanY, and the screen it is on
1221 // 2. When long clicking on an empty cell in a CellLayout, we save information about the
1222 // cellX and cellY coordinates and which page was clicked. We then set this as a tag on
1223 // the CellLayout that was long clicked
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001224 static final class CellInfo implements ContextMenu.ContextMenuInfo {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001225 View cell;
Michael Jurkaa63c4522010-08-19 13:52:27 -07001226 int cellX = -1;
1227 int cellY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001228 int spanX;
1229 int spanY;
1230 int screen;
1231 boolean valid;
1232
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001233 @Override
1234 public String toString() {
Winson Chungaafa03c2010-06-11 17:34:16 -07001235 return "Cell[view=" + (cell == null ? "null" : cell.getClass())
1236 + ", x=" + cellX + ", y=" + cellY + "]";
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001237 }
1238 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001239}