blob: 1d45565a090a9a88133dad9f7a9271a9f1991473 [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
Winson Chungaafa03c2010-06-11 17:34:16 -070019import java.util.ArrayList;
20
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;
28import android.util.AttributeSet;
29import android.view.ContextMenu;
30import android.view.MotionEvent;
31import android.view.View;
32import android.view.ViewDebug;
33import android.view.ViewGroup;
Winson Chungaafa03c2010-06-11 17:34:16 -070034import android.view.animation.Animation;
35import android.view.animation.LayoutAnimationController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080036
Romain Guyedcce092010-03-04 13:03:17 -080037import com.android.launcher.R;
38
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039public class CellLayout extends ViewGroup {
Winson Chungaafa03c2010-06-11 17:34:16 -070040 static final String TAG = "CellLayout";
41
The Android Open Source Project31dd5032009-03-03 19:32:27 -080042 private boolean mPortrait;
43
44 private int mCellWidth;
45 private int mCellHeight;
Winson Chungaafa03c2010-06-11 17:34:16 -070046
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047 private int mLongAxisStartPadding;
48 private int mLongAxisEndPadding;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049 private int mShortAxisStartPadding;
50 private int mShortAxisEndPadding;
51
Winson Chungaafa03c2010-06-11 17:34:16 -070052 private int mLeftPadding;
53 private int mRightPadding;
54 private int mTopPadding;
55 private int mBottomPadding;
56
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057 private int mShortAxisCells;
58 private int mLongAxisCells;
59
60 private int mWidthGap;
61 private int mHeightGap;
62
63 private final Rect mRect = new Rect();
64 private final CellInfo mCellInfo = new CellInfo();
Winson Chungaafa03c2010-06-11 17:34:16 -070065
The Android Open Source Project31dd5032009-03-03 19:32:27 -080066 int[] mCellXY = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -080067 boolean[][] mOccupied;
68
69 private RectF mDragRect = new RectF();
70
71 private boolean mDirtyTag;
Mike Cleronf8bbd342009-10-23 16:15:16 -070072 private boolean mLastDownOnOccupiedCell = false;
Winson Chungaafa03c2010-06-11 17:34:16 -070073
74 private final WallpaperManager mWallpaperManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080075
76 public CellLayout(Context context) {
77 this(context, null);
78 }
79
80 public CellLayout(Context context, AttributeSet attrs) {
81 this(context, attrs, 0);
82 }
83
84 public CellLayout(Context context, AttributeSet attrs, int defStyle) {
85 super(context, attrs, defStyle);
86 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
87
88 mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
89 mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -070090
91 mLongAxisStartPadding =
The Android Open Source Project31dd5032009-03-03 19:32:27 -080092 a.getDimensionPixelSize(R.styleable.CellLayout_longAxisStartPadding, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -070093 mLongAxisEndPadding =
The Android Open Source Project31dd5032009-03-03 19:32:27 -080094 a.getDimensionPixelSize(R.styleable.CellLayout_longAxisEndPadding, 10);
95 mShortAxisStartPadding =
96 a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisStartPadding, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -070097 mShortAxisEndPadding =
The Android Open Source Project31dd5032009-03-03 19:32:27 -080098 a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisEndPadding, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -070099
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800100 mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4);
101 mLongAxisCells = a.getInt(R.styleable.CellLayout_longAxisCells, 4);
102
103 a.recycle();
104
105 setAlwaysDrawnWithCacheEnabled(false);
106
Romain Guy84f296c2009-11-04 15:00:44 -0800107 mWallpaperManager = WallpaperManager.getInstance(getContext());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108 }
109
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700110 @Override
Romain Guya6abce82009-11-10 02:54:41 -0800111 public void dispatchDraw(Canvas canvas) {
112 super.dispatchDraw(canvas);
113 }
114
115 @Override
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700116 public void cancelLongPress() {
117 super.cancelLongPress();
118
119 // Cancel long press for all children
120 final int count = getChildCount();
121 for (int i = 0; i < count; i++) {
122 final View child = getChildAt(i);
123 child.cancelLongPress();
124 }
125 }
126
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800127 int getCountX() {
128 return mPortrait ? mShortAxisCells : mLongAxisCells;
129 }
130
131 int getCountY() {
132 return mPortrait ? mLongAxisCells : mShortAxisCells;
133 }
134
Winson Chungaafa03c2010-06-11 17:34:16 -0700135 // Takes canonical layout parameters
136 public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
137 final LayoutParams lp = params;
138
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800139 // Generate an id for each view, this assumes we have at most 256x256 cells
140 // per workspace screen
Winson Chungaafa03c2010-06-11 17:34:16 -0700141 if (lp.cellX >= 0 && lp.cellX <= getCountX() - 1 && lp.cellY >= 0 && lp.cellY <= getCountY() - 1) {
142 // If the horizontal or vertical span is set to -1, it is taken to
143 // mean that it spans the extent of the CellLayout
144 if (lp.cellHSpan < 0) lp.cellHSpan = getCountX();
145 if (lp.cellVSpan < 0) lp.cellVSpan = getCountY();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800146
Winson Chungaafa03c2010-06-11 17:34:16 -0700147 child.setId(childId);
148
149 addView(child, index, lp);
150 return true;
151 }
152 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800153 }
154
155 @Override
156 public void requestChildFocus(View child, View focused) {
157 super.requestChildFocus(child, focused);
158 if (child != null) {
159 Rect r = new Rect();
160 child.getDrawingRect(r);
161 requestRectangleOnScreen(r);
162 }
163 }
164
165 @Override
166 protected void onAttachedToWindow() {
167 super.onAttachedToWindow();
168 mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
169 }
170
Michael Jurkaaf442092010-06-10 17:01:57 -0700171 public void setTagToCellInfoForPoint(int touchX, int touchY) {
172 final CellInfo cellInfo = mCellInfo;
173 final Rect frame = mRect;
174 final int x = touchX + mScrollX;
175 final int y = touchY + mScrollY;
176 final int count = getChildCount();
177
178 boolean found = false;
179 for (int i = count - 1; i >= 0; i--) {
180 final View child = getChildAt(i);
181
182 if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
183 child.getHitRect(frame);
184 if (frame.contains(x, y)) {
185 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
186 cellInfo.cell = child;
187 cellInfo.cellX = lp.cellX;
188 cellInfo.cellY = lp.cellY;
189 cellInfo.spanX = lp.cellHSpan;
190 cellInfo.spanY = lp.cellVSpan;
191 cellInfo.valid = true;
192 found = true;
193 mDirtyTag = false;
194 break;
195 }
196 }
197 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700198
Michael Jurkaaf442092010-06-10 17:01:57 -0700199 mLastDownOnOccupiedCell = found;
200
201 if (!found) {
202 int cellXY[] = mCellXY;
203 pointToCellExact(x, y, cellXY);
204
205 final boolean portrait = mPortrait;
206 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
207 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
208
209 final boolean[][] occupied = mOccupied;
210 findOccupiedCells(xCount, yCount, occupied, null);
211
212 cellInfo.cell = null;
213 cellInfo.cellX = cellXY[0];
214 cellInfo.cellY = cellXY[1];
215 cellInfo.spanX = 1;
216 cellInfo.spanY = 1;
217 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
218 cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
219
220 // Instead of finding the interesting vacant cells here, wait until a
221 // caller invokes getTag() to retrieve the result. Finding the vacant
222 // cells is a bit expensive and can generate many new objects, it's
223 // therefore better to defer it until we know we actually need it.
224
225 mDirtyTag = true;
226 }
227 setTag(cellInfo);
228 }
229
Winson Chungaafa03c2010-06-11 17:34:16 -0700230
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800231 @Override
232 public boolean onInterceptTouchEvent(MotionEvent ev) {
233 final int action = ev.getAction();
234 final CellInfo cellInfo = mCellInfo;
235
236 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkaaf442092010-06-10 17:01:57 -0700237 setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800238 } else if (action == MotionEvent.ACTION_UP) {
239 cellInfo.cell = null;
240 cellInfo.cellX = -1;
241 cellInfo.cellY = -1;
242 cellInfo.spanX = 0;
243 cellInfo.spanY = 0;
244 cellInfo.valid = false;
245 mDirtyTag = false;
246 setTag(cellInfo);
247 }
248
249 return false;
250 }
251
252 @Override
253 public CellInfo getTag() {
254 final CellInfo info = (CellInfo) super.getTag();
255 if (mDirtyTag && info.valid) {
256 final boolean portrait = mPortrait;
257 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
258 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
259
260 final boolean[][] occupied = mOccupied;
Jeff Sharkey70864282009-04-07 21:08:40 -0700261 findOccupiedCells(xCount, yCount, occupied, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800262
263 findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
264
265 mDirtyTag = false;
266 }
267 return info;
268 }
269
Winson Chungaafa03c2010-06-11 17:34:16 -0700270 private static void findIntersectingVacantCells(CellInfo cellInfo, int x,
271 int y, int xCount, int yCount, boolean[][] occupied) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800272
273 cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
274 cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
275 cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
276 cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
277 cellInfo.clearVacantCells();
278
279 if (occupied[x][y]) {
280 return;
281 }
282
283 cellInfo.current.set(x, y, x, y);
284
285 findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
286 }
287
288 private static void findVacantCell(Rect current, int xCount, int yCount, boolean[][] occupied,
289 CellInfo cellInfo) {
290
291 addVacantCell(current, cellInfo);
292
293 if (current.left > 0) {
294 if (isColumnEmpty(current.left - 1, current.top, current.bottom, occupied)) {
295 current.left--;
296 findVacantCell(current, xCount, yCount, occupied, cellInfo);
297 current.left++;
298 }
299 }
300
301 if (current.right < xCount - 1) {
302 if (isColumnEmpty(current.right + 1, current.top, current.bottom, occupied)) {
303 current.right++;
304 findVacantCell(current, xCount, yCount, occupied, cellInfo);
305 current.right--;
306 }
307 }
308
309 if (current.top > 0) {
310 if (isRowEmpty(current.top - 1, current.left, current.right, occupied)) {
311 current.top--;
312 findVacantCell(current, xCount, yCount, occupied, cellInfo);
313 current.top++;
314 }
315 }
316
317 if (current.bottom < yCount - 1) {
318 if (isRowEmpty(current.bottom + 1, current.left, current.right, occupied)) {
319 current.bottom++;
320 findVacantCell(current, xCount, yCount, occupied, cellInfo);
321 current.bottom--;
322 }
323 }
324 }
325
326 private static void addVacantCell(Rect current, CellInfo cellInfo) {
327 CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
328 cell.cellX = current.left;
329 cell.cellY = current.top;
330 cell.spanX = current.right - current.left + 1;
331 cell.spanY = current.bottom - current.top + 1;
332 if (cell.spanX > cellInfo.maxVacantSpanX) {
333 cellInfo.maxVacantSpanX = cell.spanX;
334 cellInfo.maxVacantSpanXSpanY = cell.spanY;
335 }
336 if (cell.spanY > cellInfo.maxVacantSpanY) {
337 cellInfo.maxVacantSpanY = cell.spanY;
338 cellInfo.maxVacantSpanYSpanX = cell.spanX;
339 }
340 cellInfo.vacantCells.add(cell);
341 }
342
343 private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
344 for (int y = top; y <= bottom; y++) {
345 if (occupied[x][y]) {
346 return false;
347 }
348 }
349 return true;
350 }
351
352 private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
353 for (int x = left; x <= right; x++) {
354 if (occupied[x][y]) {
355 return false;
356 }
357 }
358 return true;
359 }
360
Jeff Sharkey70864282009-04-07 21:08:40 -0700361 CellInfo findAllVacantCells(boolean[] occupiedCells, View ignoreView) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800362 final boolean portrait = mPortrait;
363 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
364 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
365
366 boolean[][] occupied = mOccupied;
367
368 if (occupiedCells != null) {
369 for (int y = 0; y < yCount; y++) {
370 for (int x = 0; x < xCount; x++) {
371 occupied[x][y] = occupiedCells[y * xCount + x];
372 }
373 }
374 } else {
Jeff Sharkey70864282009-04-07 21:08:40 -0700375 findOccupiedCells(xCount, yCount, occupied, ignoreView);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800376 }
377
378 CellInfo cellInfo = new CellInfo();
379
380 cellInfo.cellX = -1;
381 cellInfo.cellY = -1;
382 cellInfo.spanY = 0;
383 cellInfo.spanX = 0;
384 cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
385 cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
386 cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
387 cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
388 cellInfo.screen = mCellInfo.screen;
389
390 Rect current = cellInfo.current;
391
392 for (int x = 0; x < xCount; x++) {
393 for (int y = 0; y < yCount; y++) {
394 if (!occupied[x][y]) {
395 current.set(x, y, x, y);
396 findVacantCell(current, xCount, yCount, occupied, cellInfo);
397 occupied[x][y] = true;
398 }
399 }
400 }
401
402 cellInfo.valid = cellInfo.vacantCells.size() > 0;
403
404 // Assume the caller will perform their own cell searching, otherwise we
405 // risk causing an unnecessary rebuild after findCellForSpan()
Winson Chungaafa03c2010-06-11 17:34:16 -0700406
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800407 return cellInfo;
408 }
409
410 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700411 * Given a point, return the cell that strictly encloses that point
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800412 * @param x X coordinate of the point
413 * @param y Y coordinate of the point
414 * @param result Array of 2 ints to hold the x and y coordinate of the cell
415 */
416 void pointToCellExact(int x, int y, int[] result) {
417 final boolean portrait = mPortrait;
Winson Chungaafa03c2010-06-11 17:34:16 -0700418
419 final int hStartPadding = getLeftPadding();
420 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800421
422 result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
423 result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
424
425 final int xAxis = portrait ? mShortAxisCells : mLongAxisCells;
426 final int yAxis = portrait ? mLongAxisCells : mShortAxisCells;
427
428 if (result[0] < 0) result[0] = 0;
429 if (result[0] >= xAxis) result[0] = xAxis - 1;
430 if (result[1] < 0) result[1] = 0;
431 if (result[1] >= yAxis) result[1] = yAxis - 1;
432 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700433
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800434 /**
435 * Given a point, return the cell that most closely encloses that point
436 * @param x X coordinate of the point
437 * @param y Y coordinate of the point
438 * @param result Array of 2 ints to hold the x and y coordinate of the cell
439 */
440 void pointToCellRounded(int x, int y, int[] result) {
441 pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
442 }
443
444 /**
445 * Given a cell coordinate, return the point that represents the upper left corner of that cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700446 *
447 * @param cellX X coordinate of the cell
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800448 * @param cellY Y coordinate of the cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700449 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800450 * @param result Array of 2 ints to hold the x and y coordinate of the point
451 */
452 void cellToPoint(int cellX, int cellY, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700453 final int hStartPadding = getLeftPadding();
454 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800455
456 result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
457 result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
458 }
459
Romain Guy84f296c2009-11-04 15:00:44 -0800460 int getCellWidth() {
461 return mCellWidth;
462 }
463
464 int getCellHeight() {
465 return mCellHeight;
466 }
467
Romain Guy1a304a12009-11-10 00:02:32 -0800468 int getLeftPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700469 return mLeftPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800470 }
471
472 int getTopPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700473 return mTopPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800474 }
475
476 int getRightPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700477 return mRightPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800478 }
479
480 int getBottomPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700481 return mBottomPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800482 }
483
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800484 @Override
485 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
486 // TODO: currently ignoring padding
Winson Chungaafa03c2010-06-11 17:34:16 -0700487
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800488 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700489 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
490
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800491 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
492 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700493
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800494 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
495 throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
496 }
497
498 final int shortAxisCells = mShortAxisCells;
499 final int longAxisCells = mLongAxisCells;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800500 final int cellWidth = mCellWidth;
501 final int cellHeight = mCellHeight;
502
Winson Chungaafa03c2010-06-11 17:34:16 -0700503 boolean portrait = heightSpecSize > widthSpecSize;
504 if (portrait != mPortrait || mOccupied == null) {
505 if (portrait) {
506 mOccupied = new boolean[mShortAxisCells][mLongAxisCells];
507 } else {
508 mOccupied = new boolean[mLongAxisCells][mShortAxisCells];
509 }
510 }
511 mPortrait = portrait;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800512
513 int numShortGaps = shortAxisCells - 1;
514 int numLongGaps = longAxisCells - 1;
515
516 if (mPortrait) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700517 int vSpaceLeft = heightSpecSize - mLongAxisStartPadding
518 - mLongAxisEndPadding - (cellHeight * longAxisCells);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800519 mHeightGap = vSpaceLeft / numLongGaps;
520
Winson Chungaafa03c2010-06-11 17:34:16 -0700521 int hSpaceLeft = widthSpecSize - mShortAxisStartPadding
522 - mShortAxisEndPadding - (cellWidth * shortAxisCells);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800523 if (numShortGaps > 0) {
524 mWidthGap = hSpaceLeft / numShortGaps;
525 } else {
526 mWidthGap = 0;
527 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700528
529 if (LauncherApplication.isInPlaceRotationEnabled()) {
530 mWidthGap = mHeightGap = Math.min(mHeightGap, mWidthGap);
531 mLeftPadding = mRightPadding = (widthSpecSize - cellWidth
532 * shortAxisCells - (shortAxisCells - 1) * mWidthGap) / 2;
533 mTopPadding = mBottomPadding = (heightSpecSize - cellHeight
534 * longAxisCells - (longAxisCells - 1) * mHeightGap) / 2;
535 } else {
536 mLeftPadding = mShortAxisStartPadding;
537 mRightPadding = mShortAxisEndPadding;
538 mTopPadding = mLongAxisStartPadding;
539 mBottomPadding = mLongAxisEndPadding;
540 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800541 } else {
Winson Chungaafa03c2010-06-11 17:34:16 -0700542 int hSpaceLeft = widthSpecSize - mLongAxisStartPadding
543 - mLongAxisEndPadding - (cellWidth * longAxisCells);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800544 mWidthGap = hSpaceLeft / numLongGaps;
545
Winson Chungaafa03c2010-06-11 17:34:16 -0700546 int vSpaceLeft = heightSpecSize - mShortAxisStartPadding
547 - mShortAxisEndPadding - (cellHeight * shortAxisCells);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800548 if (numShortGaps > 0) {
549 mHeightGap = vSpaceLeft / numShortGaps;
550 } else {
551 mHeightGap = 0;
552 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700553
554 if (LauncherApplication.isScreenXLarge()) {
555 mWidthGap = mHeightGap = Math.min(mHeightGap, mWidthGap);
556 mLeftPadding = mRightPadding = (widthSpecSize - cellWidth
557 * longAxisCells - (longAxisCells - 1) * mWidthGap) / 2 ;
558 mTopPadding = mBottomPadding = (heightSpecSize - cellHeight
559 * shortAxisCells - (shortAxisCells - 1) * mHeightGap) / 2;
560 } else {
561 mLeftPadding = mLongAxisStartPadding;
562 mRightPadding = mLongAxisEndPadding;
563 mTopPadding = mShortAxisStartPadding;
564 mBottomPadding = mShortAxisEndPadding;
565 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800566 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800567 int count = getChildCount();
568
569 for (int i = 0; i < count; i++) {
570 View child = getChildAt(i);
571 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Winson Chungaafa03c2010-06-11 17:34:16 -0700572 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
573 mLeftPadding, mTopPadding);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800574
Winson Chungaafa03c2010-06-11 17:34:16 -0700575 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
576 MeasureSpec.EXACTLY);
577 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
578 MeasureSpec.EXACTLY);
579
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800580 child.measure(childWidthMeasureSpec, childheightMeasureSpec);
581 }
582
583 setMeasuredDimension(widthSpecSize, heightSpecSize);
584 }
585
586 @Override
587 protected void onLayout(boolean changed, int l, int t, int r, int b) {
588 int count = getChildCount();
589
590 for (int i = 0; i < count; i++) {
591 View child = getChildAt(i);
592 if (child.getVisibility() != GONE) {
593
594 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
595
596 int childLeft = lp.x;
597 int childTop = lp.y;
598 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
Romain Guy84f296c2009-11-04 15:00:44 -0800599
600 if (lp.dropped) {
601 lp.dropped = false;
602
Romain Guy06762ab2010-01-25 16:51:08 -0800603 final int[] cellXY = mCellXY;
604 getLocationOnScreen(cellXY);
Romain Guy84f296c2009-11-04 15:00:44 -0800605 mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
Romain Guy06762ab2010-01-25 16:51:08 -0800606 cellXY[0] + childLeft + lp.width / 2,
607 cellXY[1] + childTop + lp.height / 2, 0, null);
Romain Guy84f296c2009-11-04 15:00:44 -0800608 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800609 }
610 }
611 }
612
613 @Override
614 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
615 final int count = getChildCount();
616 for (int i = 0; i < count; i++) {
617 final View view = getChildAt(i);
618 view.setDrawingCacheEnabled(enabled);
619 // Update the drawing caches
Adam Powellfefa0ce2010-05-03 10:23:50 -0700620 view.buildDrawingCache(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800621 }
622 }
623
624 @Override
625 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
626 super.setChildrenDrawnWithCacheEnabled(enabled);
627 }
628
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800629 /**
Jeff Sharkey70864282009-04-07 21:08:40 -0700630 * Find a vacant area that will fit the given bounds nearest the requested
631 * cell location. Uses Euclidean distance to score multiple vacant areas.
Winson Chungaafa03c2010-06-11 17:34:16 -0700632 *
Romain Guy51afc022009-05-04 18:03:43 -0700633 * @param pixelX The X location at which you want to search for a vacant area.
634 * @param pixelY The Y location at which you want to search for a vacant area.
Jeff Sharkey70864282009-04-07 21:08:40 -0700635 * @param spanX Horizontal span of the object.
636 * @param spanY Vertical span of the object.
637 * @param vacantCells Pre-computed set of vacant cells to search.
638 * @param recycle Previously returned value to possibly recycle.
639 * @return The X, Y cell of a vacant area that can contain this object,
640 * nearest the requested location.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800641 */
Jeff Sharkey70864282009-04-07 21:08:40 -0700642 int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
643 CellInfo vacantCells, int[] recycle) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700644
Jeff Sharkey70864282009-04-07 21:08:40 -0700645 // Keep track of best-scoring drop area
646 final int[] bestXY = recycle != null ? recycle : new int[2];
647 final int[] cellXY = mCellXY;
648 double bestDistance = Double.MAX_VALUE;
Winson Chungaafa03c2010-06-11 17:34:16 -0700649
Jeff Sharkey70864282009-04-07 21:08:40 -0700650 // Bail early if vacant cells aren't valid
651 if (!vacantCells.valid) {
652 return null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800653 }
654
Jeff Sharkey70864282009-04-07 21:08:40 -0700655 // Look across all vacant cells for best fit
656 final int size = vacantCells.vacantCells.size();
657 for (int i = 0; i < size; i++) {
658 final CellInfo.VacantCell cell = vacantCells.vacantCells.get(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700659
Jeff Sharkey70864282009-04-07 21:08:40 -0700660 // Reject if vacant cell isn't our exact size
661 if (cell.spanX != spanX || cell.spanY != spanY) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800662 continue;
663 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700664
Jeff Sharkey70864282009-04-07 21:08:40 -0700665 // Score is center distance from requested pixel
666 cellToPoint(cell.cellX, cell.cellY, cellXY);
Winson Chungaafa03c2010-06-11 17:34:16 -0700667
668 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
669 + Math.pow(cellXY[1] - pixelY, 2));
Jeff Sharkey70864282009-04-07 21:08:40 -0700670 if (distance <= bestDistance) {
671 bestDistance = distance;
672 bestXY[0] = cell.cellX;
673 bestXY[1] = cell.cellY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800674 }
675 }
676
Winson Chungaafa03c2010-06-11 17:34:16 -0700677 // Return null if no suitable location found
Jeff Sharkey70864282009-04-07 21:08:40 -0700678 if (bestDistance < Double.MAX_VALUE) {
679 return bestXY;
680 } else {
681 return null;
682 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800683 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700684
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800685 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700686 * Mark a child as having been dropped.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800687 *
688 * @param child The child that is being dropped
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800689 */
Winson Chungaafa03c2010-06-11 17:34:16 -0700690 void onDropChild(View child) {
Romain Guyd94533d2009-08-17 10:01:15 -0700691 if (child != null) {
692 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Romain Guyd94533d2009-08-17 10:01:15 -0700693 lp.isDragging = false;
Romain Guy84f296c2009-11-04 15:00:44 -0800694 lp.dropped = true;
Romain Guyd94533d2009-08-17 10:01:15 -0700695 mDragRect.setEmpty();
696 child.requestLayout();
697 invalidate();
698 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800699 }
700
701 void onDropAborted(View child) {
702 if (child != null) {
703 ((LayoutParams) child.getLayoutParams()).isDragging = false;
704 invalidate();
705 }
706 mDragRect.setEmpty();
707 }
708
709 /**
710 * Start dragging the specified child
Winson Chungaafa03c2010-06-11 17:34:16 -0700711 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800712 * @param child The child that is being dragged
713 */
714 void onDragChild(View child) {
715 LayoutParams lp = (LayoutParams) child.getLayoutParams();
716 lp.isDragging = true;
717 mDragRect.setEmpty();
718 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700719
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800720 /**
721 * Drag a child over the specified position
Winson Chungaafa03c2010-06-11 17:34:16 -0700722 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800723 * @param child The child that is being dropped
724 * @param cellX The child's new x cell location
Winson Chungaafa03c2010-06-11 17:34:16 -0700725 * @param cellY The child's new y cell location
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800726 */
727 void onDragOverChild(View child, int cellX, int cellY) {
728 int[] cellXY = mCellXY;
729 pointToCellRounded(cellX, cellY, cellXY);
730 LayoutParams lp = (LayoutParams) child.getLayoutParams();
731 cellToRect(cellXY[0], cellXY[1], lp.cellHSpan, lp.cellVSpan, mDragRect);
732 invalidate();
733 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700734
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800735 /**
736 * Computes a bounding rectangle for a range of cells
Winson Chungaafa03c2010-06-11 17:34:16 -0700737 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800738 * @param cellX X coordinate of upper left corner expressed as a cell position
739 * @param cellY Y coordinate of upper left corner expressed as a cell position
Winson Chungaafa03c2010-06-11 17:34:16 -0700740 * @param cellHSpan Width in cells
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800741 * @param cellVSpan Height in cells
742 * @param dragRect Rectnagle into which to put the results
743 */
744 public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF dragRect) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800745 final int cellWidth = mCellWidth;
746 final int cellHeight = mCellHeight;
747 final int widthGap = mWidthGap;
748 final int heightGap = mHeightGap;
Winson Chungaafa03c2010-06-11 17:34:16 -0700749
750 final int hStartPadding = getLeftPadding();
751 final int vStartPadding = getTopPadding();
752
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800753 int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
754 int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
755
756 int x = hStartPadding + cellX * (cellWidth + widthGap);
757 int y = vStartPadding + cellY * (cellHeight + heightGap);
Winson Chungaafa03c2010-06-11 17:34:16 -0700758
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800759 dragRect.set(x, y, x + width, y + height);
760 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700761
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800762 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700763 * Computes the required horizontal and vertical cell spans to always
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800764 * fit the given rectangle.
Winson Chungaafa03c2010-06-11 17:34:16 -0700765 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800766 * @param width Width in pixels
767 * @param height Height in pixels
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800768 */
769 public int[] rectToCell(int width, int height) {
770 // Always assume we're working with the smallest span to make sure we
771 // reserve enough space in both orientations.
Joe Onorato79e56262009-09-21 15:23:04 -0400772 final Resources resources = getResources();
773 int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
774 int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800775 int smallerSize = Math.min(actualWidth, actualHeight);
Joe Onorato79e56262009-09-21 15:23:04 -0400776
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800777 // Always round up to next largest cell
778 int spanX = (width + smallerSize) / smallerSize;
779 int spanY = (height + smallerSize) / smallerSize;
Joe Onorato79e56262009-09-21 15:23:04 -0400780
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800781 return new int[] { spanX, spanY };
782 }
783
784 /**
785 * Find the first vacant cell, if there is one.
786 *
787 * @param vacant Holds the x and y coordinate of the vacant cell
788 * @param spanX Horizontal cell span.
789 * @param spanY Vertical cell span.
Winson Chungaafa03c2010-06-11 17:34:16 -0700790 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800791 * @return True if a vacant cell was found
792 */
793 public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
794 final boolean portrait = mPortrait;
795 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
796 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
797 final boolean[][] occupied = mOccupied;
798
Jeff Sharkey70864282009-04-07 21:08:40 -0700799 findOccupiedCells(xCount, yCount, occupied, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800800
801 return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
802 }
803
804 static boolean findVacantCell(int[] vacant, int spanX, int spanY,
805 int xCount, int yCount, boolean[][] occupied) {
806
807 for (int x = 0; x < xCount; x++) {
808 for (int y = 0; y < yCount; y++) {
809 boolean available = !occupied[x][y];
810out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
811 for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
812 available = available && !occupied[i][j];
813 if (!available) break out;
814 }
815 }
816
817 if (available) {
818 vacant[0] = x;
819 vacant[1] = y;
820 return true;
821 }
822 }
823 }
824
825 return false;
826 }
827
828 boolean[] getOccupiedCells() {
829 final boolean portrait = mPortrait;
830 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
831 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
832 final boolean[][] occupied = mOccupied;
833
Jeff Sharkey70864282009-04-07 21:08:40 -0700834 findOccupiedCells(xCount, yCount, occupied, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800835
836 final boolean[] flat = new boolean[xCount * yCount];
837 for (int y = 0; y < yCount; y++) {
838 for (int x = 0; x < xCount; x++) {
839 flat[y * xCount + x] = occupied[x][y];
840 }
841 }
842
843 return flat;
844 }
845
Jeff Sharkey70864282009-04-07 21:08:40 -0700846 private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied, View ignoreView) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800847 for (int x = 0; x < xCount; x++) {
848 for (int y = 0; y < yCount; y++) {
849 occupied[x][y] = false;
850 }
851 }
852
853 int count = getChildCount();
854 for (int i = 0; i < count; i++) {
855 View child = getChildAt(i);
Jeff Sharkey70864282009-04-07 21:08:40 -0700856 if (child instanceof Folder || child.equals(ignoreView)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800857 continue;
858 }
859 LayoutParams lp = (LayoutParams) child.getLayoutParams();
860
861 for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
862 for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
863 occupied[x][y] = true;
864 }
865 }
866 }
867 }
868
869 @Override
870 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
871 return new CellLayout.LayoutParams(getContext(), attrs);
872 }
873
874 @Override
875 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
876 return p instanceof CellLayout.LayoutParams;
877 }
878
879 @Override
880 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
881 return new CellLayout.LayoutParams(p);
882 }
883
Winson Chungaafa03c2010-06-11 17:34:16 -0700884 public static class CellLayoutAnimationController extends LayoutAnimationController {
885 public CellLayoutAnimationController(Animation animation, float delay) {
886 super(animation, delay);
887 }
888
889 @Override
890 protected long getDelayForView(View view) {
891 return (int) (Math.random() * 150);
892 }
893 }
894
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800895 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
896 /**
897 * Horizontal location of the item in the grid.
898 */
899 @ViewDebug.ExportedProperty
900 public int cellX;
901
902 /**
903 * Vertical location of the item in the grid.
904 */
905 @ViewDebug.ExportedProperty
906 public int cellY;
907
908 /**
909 * Number of cells spanned horizontally by the item.
910 */
911 @ViewDebug.ExportedProperty
912 public int cellHSpan;
913
914 /**
915 * Number of cells spanned vertically by the item.
916 */
917 @ViewDebug.ExportedProperty
918 public int cellVSpan;
Winson Chungaafa03c2010-06-11 17:34:16 -0700919
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800920 /**
921 * Is this item currently being dragged
922 */
923 public boolean isDragging;
924
925 // X coordinate of the view in the layout.
926 @ViewDebug.ExportedProperty
927 int x;
928 // Y coordinate of the view in the layout.
929 @ViewDebug.ExportedProperty
930 int y;
931
Romain Guy84f296c2009-11-04 15:00:44 -0800932 boolean dropped;
Romain Guyfcb9e712009-10-02 16:06:52 -0700933
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800934 public LayoutParams(Context c, AttributeSet attrs) {
935 super(c, attrs);
936 cellHSpan = 1;
937 cellVSpan = 1;
938 }
939
940 public LayoutParams(ViewGroup.LayoutParams source) {
941 super(source);
942 cellHSpan = 1;
943 cellVSpan = 1;
944 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700945
946 public LayoutParams(LayoutParams source) {
947 super(source);
948 this.cellX = source.cellX;
949 this.cellY = source.cellY;
950 this.cellHSpan = source.cellHSpan;
951 this.cellVSpan = source.cellVSpan;
952 }
953
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800954 public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
Romain Guy8f19cdd2010-01-08 15:07:00 -0800955 super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800956 this.cellX = cellX;
957 this.cellY = cellY;
958 this.cellHSpan = cellHSpan;
959 this.cellVSpan = cellVSpan;
960 }
961
962 public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
963 int hStartPadding, int vStartPadding) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700964
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800965 final int myCellHSpan = cellHSpan;
966 final int myCellVSpan = cellVSpan;
967 final int myCellX = cellX;
968 final int myCellY = cellY;
Winson Chungaafa03c2010-06-11 17:34:16 -0700969
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800970 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
971 leftMargin - rightMargin;
972 height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
973 topMargin - bottomMargin;
974
975 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
976 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
977 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700978
979 public String toString() {
980 return "(" + this.cellX + ", " + this.cellY + ")";
981 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800982 }
983
984 static final class CellInfo implements ContextMenu.ContextMenuInfo {
985 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700986 * See View.AttachInfo.InvalidateInfo for futher explanations about the
987 * recycling mechanism. In this case, we recycle the vacant cells
988 * instances because up to several hundreds can be instanciated when the
989 * user long presses an empty cell.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800990 */
991 static final class VacantCell {
992 int cellX;
993 int cellY;
994 int spanX;
995 int spanY;
996
997 // We can create up to 523 vacant cells on a 4x4 grid, 100 seems
998 // like a reasonable compromise given the size of a VacantCell and
999 // the fact that the user is not likely to touch an empty 4x4 grid
Winson Chungaafa03c2010-06-11 17:34:16 -07001000 // very often
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001001 private static final int POOL_LIMIT = 100;
1002 private static final Object sLock = new Object();
1003
1004 private static int sAcquiredCount = 0;
1005 private static VacantCell sRoot;
1006
1007 private VacantCell next;
1008
1009 static VacantCell acquire() {
1010 synchronized (sLock) {
1011 if (sRoot == null) {
1012 return new VacantCell();
1013 }
1014
1015 VacantCell info = sRoot;
1016 sRoot = info.next;
1017 sAcquiredCount--;
1018
1019 return info;
1020 }
1021 }
1022
1023 void release() {
1024 synchronized (sLock) {
1025 if (sAcquiredCount < POOL_LIMIT) {
1026 sAcquiredCount++;
1027 next = sRoot;
1028 sRoot = this;
1029 }
1030 }
1031 }
1032
1033 @Override
1034 public String toString() {
Winson Chungaafa03c2010-06-11 17:34:16 -07001035 return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX="
1036 + spanX + ", spanY=" + spanY + "]";
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001037 }
1038 }
1039
1040 View cell;
1041 int cellX;
1042 int cellY;
1043 int spanX;
1044 int spanY;
1045 int screen;
1046 boolean valid;
1047
1048 final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
1049 int maxVacantSpanX;
1050 int maxVacantSpanXSpanY;
1051 int maxVacantSpanY;
1052 int maxVacantSpanYSpanX;
1053 final Rect current = new Rect();
1054
Romain Guy4c58c482009-05-12 17:35:41 -07001055 void clearVacantCells() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001056 final ArrayList<VacantCell> list = vacantCells;
1057 final int count = list.size();
1058
Winson Chungaafa03c2010-06-11 17:34:16 -07001059 for (int i = 0; i < count; i++) {
1060 list.get(i).release();
1061 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001062
1063 list.clear();
1064 }
1065
1066 void findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
1067 if (cellX < 0 || cellY < 0) {
1068 maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
1069 maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
1070 clearVacantCells();
1071 return;
1072 }
1073
1074 final boolean[][] unflattened = new boolean[xCount][yCount];
1075 for (int y = 0; y < yCount; y++) {
1076 for (int x = 0; x < xCount; x++) {
1077 unflattened[x][y] = occupied[y * xCount + x];
1078 }
1079 }
1080 CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
1081 }
1082
1083 /**
1084 * This method can be called only once! Calling #findVacantCellsFromOccupied will
1085 * restore the ability to call this method.
1086 *
1087 * Finds the upper-left coordinate of the first rectangle in the grid that can
1088 * hold a cell of the specified dimensions.
1089 *
1090 * @param cellXY The array that will contain the position of a vacant cell if such a cell
1091 * can be found.
1092 * @param spanX The horizontal span of the cell we want to find.
1093 * @param spanY The vertical span of the cell we want to find.
1094 *
1095 * @return True if a vacant cell of the specified dimension was found, false otherwise.
1096 */
1097 boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
Romain Guy4c58c482009-05-12 17:35:41 -07001098 return findCellForSpan(cellXY, spanX, spanY, true);
1099 }
1100
1101 boolean findCellForSpan(int[] cellXY, int spanX, int spanY, boolean clear) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001102 final ArrayList<VacantCell> list = vacantCells;
1103 final int count = list.size();
1104
1105 boolean found = false;
1106
Michael Jurka0e260592010-06-30 17:07:39 -07001107 // return the span represented by the CellInfo only there is no view there
1108 // (this.cell == null) and there is enough space
1109 if (this.cell == null && this.spanX >= spanX && this.spanY >= spanY) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001110 cellXY[0] = cellX;
1111 cellXY[1] = cellY;
1112 found = true;
1113 }
1114
1115 // Look for an exact match first
1116 for (int i = 0; i < count; i++) {
1117 VacantCell cell = list.get(i);
1118 if (cell.spanX == spanX && cell.spanY == spanY) {
1119 cellXY[0] = cell.cellX;
1120 cellXY[1] = cell.cellY;
1121 found = true;
1122 break;
1123 }
1124 }
1125
1126 // Look for the first cell large enough
1127 for (int i = 0; i < count; i++) {
1128 VacantCell cell = list.get(i);
1129 if (cell.spanX >= spanX && cell.spanY >= spanY) {
1130 cellXY[0] = cell.cellX;
1131 cellXY[1] = cell.cellY;
1132 found = true;
1133 break;
1134 }
1135 }
1136
Winson Chungaafa03c2010-06-11 17:34:16 -07001137 if (clear) {
1138 clearVacantCells();
1139 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001140
1141 return found;
1142 }
1143
1144 @Override
1145 public String toString() {
Winson Chungaafa03c2010-06-11 17:34:16 -07001146 return "Cell[view=" + (cell == null ? "null" : cell.getClass())
1147 + ", x=" + cellX + ", y=" + cellY + "]";
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001148 }
1149 }
Mike Cleronf8bbd342009-10-23 16:15:16 -07001150
1151 public boolean lastDownOnOccupiedCell() {
1152 return mLastDownOnOccupiedCell;
1153 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001154}