blob: e86b305f4a0ec87424d4382364d91f02d610920e [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;
Michael Jurkadee05892010-07-27 10:01:56 -070025import android.graphics.Bitmap;
Winson Chungaafa03c2010-06-11 17:34:16 -070026import android.graphics.Canvas;
Michael Jurkadee05892010-07-27 10:01:56 -070027import android.graphics.Matrix;
28import android.graphics.Paint;
29import android.graphics.PorterDuff;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.graphics.Rect;
31import android.graphics.RectF;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070032import android.graphics.drawable.Drawable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.util.AttributeSet;
34import android.view.ContextMenu;
35import android.view.MotionEvent;
36import android.view.View;
37import android.view.ViewDebug;
38import android.view.ViewGroup;
Michael Jurkadee05892010-07-27 10:01:56 -070039import android.view.View.OnTouchListener;
Winson Chungaafa03c2010-06-11 17:34:16 -070040import android.view.animation.Animation;
41import android.view.animation.LayoutAnimationController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080042
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070043import java.util.ArrayList;
44import java.util.Arrays;
Romain Guyedcce092010-03-04 13:03:17 -080045
The Android Open Source Project31dd5032009-03-03 19:32:27 -080046public class CellLayout extends ViewGroup {
Winson Chungaafa03c2010-06-11 17:34:16 -070047 static final String TAG = "CellLayout";
Michael Jurkadee05892010-07-27 10:01:56 -070048 // we make the dimmed bitmap smaller than the screen itself for memory + perf reasons
49 static final float DIMMED_BITMAP_SCALE = 0.25f;
Winson Chungaafa03c2010-06-11 17:34:16 -070050
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051 private boolean mPortrait;
52
53 private int mCellWidth;
54 private int mCellHeight;
Winson Chungaafa03c2010-06-11 17:34:16 -070055
Winson Chungaafa03c2010-06-11 17:34:16 -070056 private int mLeftPadding;
57 private int mRightPadding;
58 private int mTopPadding;
59 private int mBottomPadding;
60
Adam Cohend22015c2010-07-26 22:02:18 -070061 private int mCountX;
62 private int mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080063
64 private int mWidthGap;
65 private int mHeightGap;
66
67 private final Rect mRect = new Rect();
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -070068 private final RectF mRectF = new RectF();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080069 private final CellInfo mCellInfo = new CellInfo();
Winson Chungaafa03c2010-06-11 17:34:16 -070070
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070071 // This is a temporary variable to prevent having to allocate a new object just to
72 // return an (x, y) value from helper functions. Do NOT use it to maintain other state.
73 private final int[] mTmpCellXY = new int[2];
74
The Android Open Source Project31dd5032009-03-03 19:32:27 -080075 boolean[][] mOccupied;
76
Michael Jurkadee05892010-07-27 10:01:56 -070077 private OnTouchListener mInterceptTouchListener;
78
79 // this is what the home screen fades to when it shrinks
80 // (ie in all apps and in home screen customize mode)
81 private Bitmap mDimmedBitmap;
82 private Canvas mDimmedBitmapCanvas;
83 private float mDimmedBitmapAlpha;
84 private boolean mDimmedBitmapDirty;
85 private final Paint mDimmedBitmapPaint = new Paint();
86 private final Rect mLayoutRect = new Rect();
87 private final Rect mDimmedBitmapRect = new Rect();
88
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070089 private final RectF mDragRect = new RectF();
90
91 // When dragging, used to indicate a vacant drop location
92 private Drawable mVacantDrawable;
93
94 // When dragging, used to indicate an occupied drop location
95 private Drawable mOccupiedDrawable;
96
97 // Updated to point to mVacantDrawable or mOccupiedDrawable, as appropriate
98 private Drawable mDragRectDrawable;
99
100 // When a drag operation is in progress, holds the nearest cell to the touch point
101 private final int[] mDragCell = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800102
103 private boolean mDirtyTag;
Mike Cleronf8bbd342009-10-23 16:15:16 -0700104 private boolean mLastDownOnOccupiedCell = false;
Winson Chungaafa03c2010-06-11 17:34:16 -0700105
106 private final WallpaperManager mWallpaperManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800107
108 public CellLayout(Context context) {
109 this(context, null);
110 }
111
112 public CellLayout(Context context, AttributeSet attrs) {
113 this(context, attrs, 0);
114 }
115
116 public CellLayout(Context context, AttributeSet attrs, int defStyle) {
117 super(context, attrs, defStyle);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700118
119 // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
120 // the user where a dragged item will land when dropped.
121 setWillNotDraw(false);
122 mVacantDrawable = getResources().getDrawable(R.drawable.rounded_rect_green);
123 mOccupiedDrawable = getResources().getDrawable(R.drawable.rounded_rect_red);
124
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800125 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
126
127 mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
128 mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700129
Adam Cohend22015c2010-07-26 22:02:18 -0700130 mLeftPadding =
131 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisStartPadding, 10);
132 mRightPadding =
133 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisEndPadding, 10);
134 mTopPadding =
135 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisStartPadding, 10);
136 mBottomPadding =
137 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisEndPadding, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700138
Adam Cohend22015c2010-07-26 22:02:18 -0700139 mCountX = LauncherModel.getCellCountX();
140 mCountY = LauncherModel.getCellCountY();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800141
142 a.recycle();
143
144 setAlwaysDrawnWithCacheEnabled(false);
145
Romain Guy84f296c2009-11-04 15:00:44 -0800146 mWallpaperManager = WallpaperManager.getInstance(getContext());
Michael Jurkadee05892010-07-27 10:01:56 -0700147
148 mDimmedBitmapPaint.setFilterBitmap(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800149 }
150
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700151 @Override
Romain Guya6abce82009-11-10 02:54:41 -0800152 public void dispatchDraw(Canvas canvas) {
153 super.dispatchDraw(canvas);
154 }
155
156 @Override
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700157 protected void onDraw(Canvas canvas) {
158 if (!mDragRect.isEmpty()) {
159 mDragRectDrawable.setBounds(
160 (int)mDragRect.left,
161 (int)mDragRect.top,
162 (int)mDragRect.right,
163 (int)mDragRect.bottom);
164 mDragRectDrawable.draw(canvas);
165 }
Michael Jurkadee05892010-07-27 10:01:56 -0700166 if (mDimmedBitmap != null && mDimmedBitmapAlpha > 0.0f) {
167 if (mDimmedBitmapDirty) {
168 updateDimmedBitmap();
169 mDimmedBitmapDirty = false;
170 }
171 mDimmedBitmapPaint.setAlpha((int) (mDimmedBitmapAlpha * 255));
172
173 canvas.drawBitmap(mDimmedBitmap, mDimmedBitmapRect, mLayoutRect, mDimmedBitmapPaint);
174 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700175 }
176
177 @Override
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700178 public void cancelLongPress() {
179 super.cancelLongPress();
180
181 // Cancel long press for all children
182 final int count = getChildCount();
183 for (int i = 0; i < count; i++) {
184 final View child = getChildAt(i);
185 child.cancelLongPress();
186 }
187 }
188
Michael Jurkadee05892010-07-27 10:01:56 -0700189 public void setOnInterceptTouchListener(View.OnTouchListener listener) {
190 mInterceptTouchListener = listener;
191 }
192
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800193 int getCountX() {
Adam Cohend22015c2010-07-26 22:02:18 -0700194 return mCountX;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800195 }
196
197 int getCountY() {
Adam Cohend22015c2010-07-26 22:02:18 -0700198 return mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800199 }
200
Winson Chungaafa03c2010-06-11 17:34:16 -0700201 // Takes canonical layout parameters
202 public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
203 final LayoutParams lp = params;
204
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800205 // Generate an id for each view, this assumes we have at most 256x256 cells
206 // per workspace screen
Adam Cohend22015c2010-07-26 22:02:18 -0700207 if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700208 // If the horizontal or vertical span is set to -1, it is taken to
209 // mean that it spans the extent of the CellLayout
Adam Cohend22015c2010-07-26 22:02:18 -0700210 if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
211 if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800212
Winson Chungaafa03c2010-06-11 17:34:16 -0700213 child.setId(childId);
214
Michael Jurkadee05892010-07-27 10:01:56 -0700215 // We might be in the middle or end of shrinking/fading to a dimmed view
216 // Make sure this view's alpha is set the same as all the rest of the views
217 child.setAlpha(1.0f - mDimmedBitmapAlpha);
218
Winson Chungaafa03c2010-06-11 17:34:16 -0700219 addView(child, index, lp);
Michael Jurkadee05892010-07-27 10:01:56 -0700220
221 // next time we draw the dimmed bitmap we need to update it
222 mDimmedBitmapDirty = true;
Winson Chungaafa03c2010-06-11 17:34:16 -0700223 return true;
224 }
225 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800226 }
227
228 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700229 public void removeView(View view) {
230 super.removeView(view);
231 mDimmedBitmapDirty = true;
232 }
233
234 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800235 public void requestChildFocus(View child, View focused) {
236 super.requestChildFocus(child, focused);
237 if (child != null) {
238 Rect r = new Rect();
239 child.getDrawingRect(r);
240 requestRectangleOnScreen(r);
241 }
242 }
243
244 @Override
245 protected void onAttachedToWindow() {
246 super.onAttachedToWindow();
247 mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
248 }
249
Michael Jurkaaf442092010-06-10 17:01:57 -0700250 public void setTagToCellInfoForPoint(int touchX, int touchY) {
251 final CellInfo cellInfo = mCellInfo;
252 final Rect frame = mRect;
253 final int x = touchX + mScrollX;
254 final int y = touchY + mScrollY;
255 final int count = getChildCount();
256
257 boolean found = false;
258 for (int i = count - 1; i >= 0; i--) {
259 final View child = getChildAt(i);
260
261 if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
262 child.getHitRect(frame);
263 if (frame.contains(x, y)) {
264 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
265 cellInfo.cell = child;
266 cellInfo.cellX = lp.cellX;
267 cellInfo.cellY = lp.cellY;
268 cellInfo.spanX = lp.cellHSpan;
269 cellInfo.spanY = lp.cellVSpan;
Michael Jurkac28de512010-08-13 11:27:44 -0700270 cellInfo.intersectX = lp.cellX;
271 cellInfo.intersectY = lp.cellY;
Michael Jurkaaf442092010-06-10 17:01:57 -0700272 cellInfo.valid = true;
273 found = true;
274 mDirtyTag = false;
275 break;
276 }
277 }
278 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700279
Michael Jurkaaf442092010-06-10 17:01:57 -0700280 mLastDownOnOccupiedCell = found;
281
282 if (!found) {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700283 final int cellXY[] = mTmpCellXY;
Michael Jurkaaf442092010-06-10 17:01:57 -0700284 pointToCellExact(x, y, cellXY);
285
286 final boolean portrait = mPortrait;
Adam Cohend22015c2010-07-26 22:02:18 -0700287 final int xCount = mCountX;
288 final int yCount = mCountY;
Michael Jurkaaf442092010-06-10 17:01:57 -0700289
290 final boolean[][] occupied = mOccupied;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700291 findOccupiedCells(xCount, yCount, occupied, null, true);
Michael Jurkaaf442092010-06-10 17:01:57 -0700292
293 cellInfo.cell = null;
294 cellInfo.cellX = cellXY[0];
295 cellInfo.cellY = cellXY[1];
296 cellInfo.spanX = 1;
297 cellInfo.spanY = 1;
Michael Jurkac28de512010-08-13 11:27:44 -0700298 cellInfo.intersectX = cellXY[0];
299 cellInfo.intersectY = cellXY[1];
Michael Jurkaaf442092010-06-10 17:01:57 -0700300 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
301 cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
302
303 // Instead of finding the interesting vacant cells here, wait until a
304 // caller invokes getTag() to retrieve the result. Finding the vacant
305 // cells is a bit expensive and can generate many new objects, it's
306 // therefore better to defer it until we know we actually need it.
307
308 mDirtyTag = true;
309 }
310 setTag(cellInfo);
311 }
312
Winson Chungaafa03c2010-06-11 17:34:16 -0700313
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800314 @Override
315 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkadee05892010-07-27 10:01:56 -0700316 if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
317 return true;
318 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800319 final int action = ev.getAction();
320 final CellInfo cellInfo = mCellInfo;
321
322 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkaaf442092010-06-10 17:01:57 -0700323 setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800324 } else if (action == MotionEvent.ACTION_UP) {
325 cellInfo.cell = null;
326 cellInfo.cellX = -1;
327 cellInfo.cellY = -1;
328 cellInfo.spanX = 0;
329 cellInfo.spanY = 0;
330 cellInfo.valid = false;
331 mDirtyTag = false;
332 setTag(cellInfo);
333 }
334
335 return false;
336 }
337
338 @Override
339 public CellInfo getTag() {
340 final CellInfo info = (CellInfo) super.getTag();
341 if (mDirtyTag && info.valid) {
Adam Cohend22015c2010-07-26 22:02:18 -0700342 final int xCount = mCountX;
343 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800344
345 final boolean[][] occupied = mOccupied;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700346 findOccupiedCells(xCount, yCount, occupied, null, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800347
Michael Jurkac28de512010-08-13 11:27:44 -0700348 info.updateOccupiedCells(occupied, mCountX, mCountY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800349
350 mDirtyTag = false;
351 }
352 return info;
353 }
354
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700355 /**
356 * Check if the column 'x' is empty from rows 'top' to 'bottom', inclusive.
357 */
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800358 private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
359 for (int y = top; y <= bottom; y++) {
360 if (occupied[x][y]) {
361 return false;
362 }
363 }
364 return true;
365 }
366
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700367 /**
368 * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
369 */
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800370 private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
371 for (int x = left; x <= right; x++) {
372 if (occupied[x][y]) {
373 return false;
374 }
375 }
376 return true;
377 }
378
Michael Jurkac28de512010-08-13 11:27:44 -0700379 CellInfo updateOccupiedCells(boolean[] occupiedCells, View ignoreView) {
Adam Cohend22015c2010-07-26 22:02:18 -0700380 final int xCount = mCountX;
381 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800382
383 boolean[][] occupied = mOccupied;
384
385 if (occupiedCells != null) {
386 for (int y = 0; y < yCount; y++) {
387 for (int x = 0; x < xCount; x++) {
388 occupied[x][y] = occupiedCells[y * xCount + x];
389 }
390 }
391 } else {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700392 findOccupiedCells(xCount, yCount, occupied, ignoreView, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800393 }
394
395 CellInfo cellInfo = new CellInfo();
396
397 cellInfo.cellX = -1;
398 cellInfo.cellY = -1;
Michael Jurkac28de512010-08-13 11:27:44 -0700399 cellInfo.intersectX = -1;
400 cellInfo.intersectY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800401 cellInfo.spanY = 0;
402 cellInfo.spanX = 0;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800403 cellInfo.screen = mCellInfo.screen;
404
Michael Jurkac28de512010-08-13 11:27:44 -0700405 cellInfo.updateOccupiedCells(occupied, mCountX, mCountY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800406
Michael Jurkac28de512010-08-13 11:27:44 -0700407 cellInfo.valid = cellInfo.existsEmptyCell();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800408
409 // Assume the caller will perform their own cell searching, otherwise we
410 // risk causing an unnecessary rebuild after findCellForSpan()
Winson Chungaafa03c2010-06-11 17:34:16 -0700411
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800412 return cellInfo;
413 }
414
415 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700416 * Given a point, return the cell that strictly encloses that point
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800417 * @param x X coordinate of the point
418 * @param y Y coordinate of the point
419 * @param result Array of 2 ints to hold the x and y coordinate of the cell
420 */
421 void pointToCellExact(int x, int y, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700422 final int hStartPadding = getLeftPadding();
423 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800424
425 result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
426 result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
427
Adam Cohend22015c2010-07-26 22:02:18 -0700428 final int xAxis = mCountX;
429 final int yAxis = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800430
431 if (result[0] < 0) result[0] = 0;
432 if (result[0] >= xAxis) result[0] = xAxis - 1;
433 if (result[1] < 0) result[1] = 0;
434 if (result[1] >= yAxis) result[1] = yAxis - 1;
435 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700436
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800437 /**
438 * Given a point, return the cell that most closely encloses that point
439 * @param x X coordinate of the point
440 * @param y Y coordinate of the point
441 * @param result Array of 2 ints to hold the x and y coordinate of the cell
442 */
443 void pointToCellRounded(int x, int y, int[] result) {
444 pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
445 }
446
447 /**
448 * Given a cell coordinate, return the point that represents the upper left corner of that cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700449 *
450 * @param cellX X coordinate of the cell
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800451 * @param cellY Y coordinate of the cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700452 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800453 * @param result Array of 2 ints to hold the x and y coordinate of the point
454 */
455 void cellToPoint(int cellX, int cellY, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700456 final int hStartPadding = getLeftPadding();
457 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800458
459 result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
460 result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
461 }
462
Romain Guy84f296c2009-11-04 15:00:44 -0800463 int getCellWidth() {
464 return mCellWidth;
465 }
466
467 int getCellHeight() {
468 return mCellHeight;
469 }
470
Romain Guy1a304a12009-11-10 00:02:32 -0800471 int getLeftPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700472 return mLeftPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800473 }
474
475 int getTopPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700476 return mTopPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800477 }
478
479 int getRightPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700480 return mRightPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800481 }
482
483 int getBottomPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700484 return mBottomPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800485 }
486
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800487 @Override
488 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
489 // TODO: currently ignoring padding
Winson Chungaafa03c2010-06-11 17:34:16 -0700490
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800491 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700492 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
493
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800494 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
495 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700496
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800497 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
498 throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
499 }
500
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800501 final int cellWidth = mCellWidth;
502 final int cellHeight = mCellHeight;
503
Adam Cohend22015c2010-07-26 22:02:18 -0700504 if (mOccupied == null) {
505 mOccupied = new boolean[mCountX][mCountY];
Winson Chungaafa03c2010-06-11 17:34:16 -0700506 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800507
Adam Cohend22015c2010-07-26 22:02:18 -0700508 int numWidthGaps = mCountX - 1;
509 int numHeightGaps = mCountY - 1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800510
Adam Cohend22015c2010-07-26 22:02:18 -0700511 int vSpaceLeft = heightSpecSize - mTopPadding
512 - mBottomPadding - (cellHeight * mCountY);
513 mHeightGap = vSpaceLeft / numHeightGaps;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800514
Adam Cohend22015c2010-07-26 22:02:18 -0700515 int hSpaceLeft = widthSpecSize - mLeftPadding
516 - mRightPadding - (cellWidth * mCountX);
517 mWidthGap = hSpaceLeft / numWidthGaps;
Winson Chungaafa03c2010-06-11 17:34:16 -0700518
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 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Winson Chungaafa03c2010-06-11 17:34:16 -0700524 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
525 mLeftPadding, mTopPadding);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800526
Winson Chungaafa03c2010-06-11 17:34:16 -0700527 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
528 MeasureSpec.EXACTLY);
529 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
530 MeasureSpec.EXACTLY);
531
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800532 child.measure(childWidthMeasureSpec, childheightMeasureSpec);
533 }
534
535 setMeasuredDimension(widthSpecSize, heightSpecSize);
536 }
537
538 @Override
539 protected void onLayout(boolean changed, int l, int t, int r, int b) {
540 int count = getChildCount();
541
542 for (int i = 0; i < count; i++) {
543 View child = getChildAt(i);
544 if (child.getVisibility() != GONE) {
545
546 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
547
548 int childLeft = lp.x;
549 int childTop = lp.y;
550 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
Romain Guy84f296c2009-11-04 15:00:44 -0800551
552 if (lp.dropped) {
553 lp.dropped = false;
554
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700555 final int[] cellXY = mTmpCellXY;
Romain Guy06762ab2010-01-25 16:51:08 -0800556 getLocationOnScreen(cellXY);
Romain Guy84f296c2009-11-04 15:00:44 -0800557 mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
Romain Guy06762ab2010-01-25 16:51:08 -0800558 cellXY[0] + childLeft + lp.width / 2,
559 cellXY[1] + childTop + lp.height / 2, 0, null);
Romain Guy84f296c2009-11-04 15:00:44 -0800560 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800561 }
562 }
563 }
564
565 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700566 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
567 super.onSizeChanged(w, h, oldw, oldh);
568 mLayoutRect.set(0, 0, w, h);
569 mDimmedBitmapRect.set(0, 0, (int) (DIMMED_BITMAP_SCALE * w), (int) (DIMMED_BITMAP_SCALE * h));
570 }
571
572 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800573 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
574 final int count = getChildCount();
575 for (int i = 0; i < count; i++) {
576 final View view = getChildAt(i);
577 view.setDrawingCacheEnabled(enabled);
578 // Update the drawing caches
Adam Powellfefa0ce2010-05-03 10:23:50 -0700579 view.buildDrawingCache(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800580 }
581 }
582
583 @Override
584 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
585 super.setChildrenDrawnWithCacheEnabled(enabled);
586 }
587
Michael Jurkadee05892010-07-27 10:01:56 -0700588 public float getDimmedBitmapAlpha() {
589 return mDimmedBitmapAlpha;
590 }
591
592 public void setDimmedBitmapAlpha(float alpha) {
593 // If we're dimming the screen after it was not dimmed, refresh
594 // to allow for updated widgets. We don't continually refresh it
595 // after this point, however, as an optimization
596 if (mDimmedBitmapAlpha == 0.0f && alpha > 0.0f) {
597 updateDimmedBitmap();
598 }
599 mDimmedBitmapAlpha = alpha;
600 setChildrenAlpha(1.0f - mDimmedBitmapAlpha);
601 }
602
603 private void setChildrenAlpha(float alpha) {
604 for (int i = 0; i < getChildCount(); i++) {
605 getChildAt(i).setAlpha(alpha);
606 }
607 }
608
609 public void updateDimmedBitmap() {
610 if (mDimmedBitmap == null) {
611 mDimmedBitmap = Bitmap.createBitmap((int) (getWidth() * DIMMED_BITMAP_SCALE),
612 (int) (getHeight() * DIMMED_BITMAP_SCALE), Bitmap.Config.ARGB_8888);
613 mDimmedBitmapCanvas = new Canvas(mDimmedBitmap);
614 mDimmedBitmapCanvas.scale(DIMMED_BITMAP_SCALE, DIMMED_BITMAP_SCALE);
615 }
616 // clear the canvas
617 mDimmedBitmapCanvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
618
619 // draw the screen into the bitmap
620 // just for drawing to the bitmap, make all the items on the screen opaque
621 setChildrenAlpha(1.0f);
622 dispatchDraw(mDimmedBitmapCanvas);
623 setChildrenAlpha(1.0f - mDimmedBitmapAlpha);
624
625 // make the bitmap 'dimmed' ie colored regions are dark grey,
626 // the rest is light grey
627 // We draw grey to the whole bitmap, but filter where we draw based on
628 // what regions are transparent or not (SRC_OUT), causing the intended effect
629
630 // First, draw light grey everywhere in the background (currently transparent) regions
631 // This will leave the regions with the widgets as mostly transparent
632 mDimmedBitmapCanvas.drawColor(0xCCCCCCCC, PorterDuff.Mode.SRC_OUT);
633 // Now, fill the the remaining transparent regions with dark grey
634 mDimmedBitmapCanvas.drawColor(0xCC333333, PorterDuff.Mode.SRC_OUT);
635 }
636
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700637 private boolean isVacant(int originX, int originY, int spanX, int spanY) {
638 for (int i = 0; i < spanY; i++) {
639 if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
640 return false;
641 }
642 }
643 return true;
644 }
645
Patrick Dubroy440c3602010-07-13 17:50:32 -0700646 public View getChildAt(int x, int y) {
647 final int count = getChildCount();
648 for (int i = 0; i < count; i++) {
649 View child = getChildAt(i);
650 LayoutParams lp = (LayoutParams) child.getLayoutParams();
651
652 if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
653 (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
654 return child;
655 }
656 }
657 return null;
658 }
659
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700660 /**
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700661 * Estimate the size that a child with the given dimensions will take in the layout.
662 */
663 void estimateChildSize(int minWidth, int minHeight, int[] result) {
664 // Assuming it's placed at 0, 0, find where the bottom right cell will land
665 rectToCell(minWidth, minHeight, result);
666
667 // Then figure out the rect it will occupy
668 cellToRect(0, 0, result[0], result[1], mRectF);
669 result[0] = (int)mRectF.width();
670 result[1] = (int)mRectF.height();
671 }
672
673 /**
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700674 * Estimate where the top left cell of the dragged item will land if it is dropped.
675 *
676 * @param originX The X value of the top left corner of the item
677 * @param originY The Y value of the top left corner of the item
678 * @param spanX The number of horizontal cells that the item spans
679 * @param spanY The number of vertical cells that the item spans
680 * @param result The estimated drop cell X and Y.
681 */
682 void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
Adam Cohend22015c2010-07-26 22:02:18 -0700683 final int countX = mCountX;
684 final int countY = mCountY;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700685
Patrick Dubroy976ebec2010-08-04 20:03:37 -0700686 // originX and originY give the top left of the cell, but pointToCellRounded
687 // compares center-to-center, so pass in the middle coordinates
688 pointToCellRounded(originX + (mCellWidth / 2), originY + (mCellHeight / 2), result);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700689
690 // If the item isn't fully on this screen, snap to the edges
691 int rightOverhang = result[0] + spanX - countX;
692 if (rightOverhang > 0) {
693 result[0] -= rightOverhang; // Snap to right
694 }
695 result[0] = Math.max(0, result[0]); // Snap to left
696 int bottomOverhang = result[1] + spanY - countY;
697 if (bottomOverhang > 0) {
698 result[1] -= bottomOverhang; // Snap to bottom
699 }
700 result[1] = Math.max(0, result[1]); // Snap to top
701 }
702
703 void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) {
704 final int[] originCell = mDragCell;
705 final int[] cellXY = mTmpCellXY;
706 estimateDropCell(originX, originY, spanX, spanY, cellXY);
707
708 // Only recalculate the bounding rect when necessary
709 if (!Arrays.equals(cellXY, originCell)) {
710 originCell[0] = cellXY[0];
711 originCell[1] = cellXY[1];
712
713 // Find the top left corner of the rect the object will occupy
714 final int[] topLeft = mTmpCellXY;
715 cellToPoint(originCell[0], originCell[1], topLeft);
716 final int left = topLeft[0];
717 final int top = topLeft[1];
718
719 // Now find the bottom right
720 final int[] bottomRight = mTmpCellXY;
721 cellToPoint(originCell[0] + spanX - 1, originCell[1] + spanY - 1, bottomRight);
722 bottomRight[0] += mCellWidth;
723 bottomRight[1] += mCellHeight;
724
Adam Cohend22015c2010-07-26 22:02:18 -0700725 final int countX = mCountX;
726 final int countY = mCountY;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700727 // TODO: It's not necessary to do this every time, but it's not especially expensive
728 findOccupiedCells(countX, countY, mOccupied, view, false);
729
730 boolean vacant = isVacant(originCell[0], originCell[1], spanX, spanY);
731 mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable;
732
733 // mDragRect will be rendered in onDraw()
734 mDragRect.set(left, top, bottomRight[0], bottomRight[1]);
735 invalidate();
736 }
737 }
738
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800739 /**
Jeff Sharkey70864282009-04-07 21:08:40 -0700740 * Find a vacant area that will fit the given bounds nearest the requested
741 * cell location. Uses Euclidean distance to score multiple vacant areas.
Winson Chungaafa03c2010-06-11 17:34:16 -0700742 *
Romain Guy51afc022009-05-04 18:03:43 -0700743 * @param pixelX The X location at which you want to search for a vacant area.
744 * @param pixelY The Y location at which you want to search for a vacant area.
Jeff Sharkey70864282009-04-07 21:08:40 -0700745 * @param spanX Horizontal span of the object.
746 * @param spanY Vertical span of the object.
747 * @param vacantCells Pre-computed set of vacant cells to search.
748 * @param recycle Previously returned value to possibly recycle.
749 * @return The X, Y cell of a vacant area that can contain this object,
750 * nearest the requested location.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800751 */
Jeff Sharkey70864282009-04-07 21:08:40 -0700752 int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
753 CellInfo vacantCells, int[] recycle) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700754
Jeff Sharkey70864282009-04-07 21:08:40 -0700755 // Keep track of best-scoring drop area
756 final int[] bestXY = recycle != null ? recycle : new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700757 double bestDistance = Double.MAX_VALUE;
Winson Chungaafa03c2010-06-11 17:34:16 -0700758
Michael Jurkac28de512010-08-13 11:27:44 -0700759 for (int x = 0; x < mCountX - (spanX - 1); x++) {
760 inner:
761 for (int y = 0; y < mCountY - (spanY - 1); y++) {
762 for (int i = 0; i < spanX; i++) {
763 for (int j = 0; j < spanY; j++) {
764 if (mOccupied[x + i][y + j]) {
765 // small optimization: we can skip to below the row we just found
766 // an occupied cell
767 y += j;
768 continue inner;
769 }
770 }
771 }
772 final int[] cellXY = mTmpCellXY;
773 cellToPoint(x, y, cellXY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800774
Michael Jurkac28de512010-08-13 11:27:44 -0700775 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
776 + Math.pow(cellXY[1] - pixelY, 2));
777 if (distance <= bestDistance) {
778 bestDistance = distance;
779 bestXY[0] = x;
780 bestXY[1] = y;
781 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800782 }
783 }
784
Winson Chungaafa03c2010-06-11 17:34:16 -0700785 // Return null if no suitable location found
Jeff Sharkey70864282009-04-07 21:08:40 -0700786 if (bestDistance < Double.MAX_VALUE) {
787 return bestXY;
788 } else {
789 return null;
790 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800791 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700792
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800793 /**
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700794 * Called when a drag and drop operation has finished (successfully or not).
795 */
796 void onDragComplete() {
797 // Invalidate the drag data
798 mDragCell[0] = -1;
799 mDragCell[1] = -1;
800
801 mDragRect.setEmpty();
802 invalidate();
803 }
804
805 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700806 * Mark a child as having been dropped.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800807 *
808 * @param child The child that is being dropped
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800809 */
Winson Chungaafa03c2010-06-11 17:34:16 -0700810 void onDropChild(View child) {
Romain Guyd94533d2009-08-17 10:01:15 -0700811 if (child != null) {
812 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Romain Guyd94533d2009-08-17 10:01:15 -0700813 lp.isDragging = false;
Romain Guy84f296c2009-11-04 15:00:44 -0800814 lp.dropped = true;
Romain Guyd94533d2009-08-17 10:01:15 -0700815 mDragRect.setEmpty();
816 child.requestLayout();
Romain Guyd94533d2009-08-17 10:01:15 -0700817 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700818 onDragComplete();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800819 }
820
821 void onDropAborted(View child) {
822 if (child != null) {
823 ((LayoutParams) child.getLayoutParams()).isDragging = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800824 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700825 onDragComplete();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800826 }
827
828 /**
829 * Start dragging the specified child
Winson Chungaafa03c2010-06-11 17:34:16 -0700830 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800831 * @param child The child that is being dragged
832 */
833 void onDragChild(View child) {
834 LayoutParams lp = (LayoutParams) child.getLayoutParams();
835 lp.isDragging = true;
836 mDragRect.setEmpty();
837 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700838
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800839 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800840 * Computes a bounding rectangle for a range of cells
Winson Chungaafa03c2010-06-11 17:34:16 -0700841 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800842 * @param cellX X coordinate of upper left corner expressed as a cell position
843 * @param cellY Y coordinate of upper left corner expressed as a cell position
Winson Chungaafa03c2010-06-11 17:34:16 -0700844 * @param cellHSpan Width in cells
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800845 * @param cellVSpan Height in cells
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700846 * @param resultRect Rect into which to put the results
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800847 */
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700848 public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800849 final int cellWidth = mCellWidth;
850 final int cellHeight = mCellHeight;
851 final int widthGap = mWidthGap;
852 final int heightGap = mHeightGap;
Winson Chungaafa03c2010-06-11 17:34:16 -0700853
854 final int hStartPadding = getLeftPadding();
855 final int vStartPadding = getTopPadding();
856
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800857 int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
858 int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
859
860 int x = hStartPadding + cellX * (cellWidth + widthGap);
861 int y = vStartPadding + cellY * (cellHeight + heightGap);
Winson Chungaafa03c2010-06-11 17:34:16 -0700862
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700863 resultRect.set(x, y, x + width, y + height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800864 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700865
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800866 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700867 * Computes the required horizontal and vertical cell spans to always
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800868 * fit the given rectangle.
Winson Chungaafa03c2010-06-11 17:34:16 -0700869 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800870 * @param width Width in pixels
871 * @param height Height in pixels
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700872 * @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 -0800873 */
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700874 public int[] rectToCell(int width, int height, int[] result) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800875 // Always assume we're working with the smallest span to make sure we
876 // reserve enough space in both orientations.
Joe Onorato79e56262009-09-21 15:23:04 -0400877 final Resources resources = getResources();
878 int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
879 int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800880 int smallerSize = Math.min(actualWidth, actualHeight);
Joe Onorato79e56262009-09-21 15:23:04 -0400881
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800882 // Always round up to next largest cell
883 int spanX = (width + smallerSize) / smallerSize;
884 int spanY = (height + smallerSize) / smallerSize;
Joe Onorato79e56262009-09-21 15:23:04 -0400885
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700886 if (result == null) {
887 return new int[] { spanX, spanY };
888 }
889 result[0] = spanX;
890 result[1] = spanY;
891 return result;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800892 }
893
894 /**
895 * Find the first vacant cell, if there is one.
896 *
897 * @param vacant Holds the x and y coordinate of the vacant cell
898 * @param spanX Horizontal cell span.
899 * @param spanY Vertical cell span.
Winson Chungaafa03c2010-06-11 17:34:16 -0700900 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800901 * @return True if a vacant cell was found
902 */
903 public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
Adam Cohend22015c2010-07-26 22:02:18 -0700904 final int xCount = mCountX;
905 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800906 final boolean[][] occupied = mOccupied;
907
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700908 findOccupiedCells(xCount, yCount, occupied, null, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800909
910 return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
911 }
912
913 static boolean findVacantCell(int[] vacant, int spanX, int spanY,
914 int xCount, int yCount, boolean[][] occupied) {
915
916 for (int x = 0; x < xCount; x++) {
917 for (int y = 0; y < yCount; y++) {
918 boolean available = !occupied[x][y];
919out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
920 for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
921 available = available && !occupied[i][j];
922 if (!available) break out;
923 }
924 }
925
926 if (available) {
927 vacant[0] = x;
928 vacant[1] = y;
929 return true;
930 }
931 }
932 }
933
934 return false;
935 }
936
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700937 /**
938 * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
939 */
940 boolean[] getOccupiedCellsFlattened() {
Adam Cohend22015c2010-07-26 22:02:18 -0700941 final int xCount = mCountX;
942 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800943 final boolean[][] occupied = mOccupied;
944
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700945 findOccupiedCells(xCount, yCount, occupied, null, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800946
947 final boolean[] flat = new boolean[xCount * yCount];
948 for (int y = 0; y < yCount; y++) {
949 for (int x = 0; x < xCount; x++) {
950 flat[y * xCount + x] = occupied[x][y];
951 }
952 }
953
954 return flat;
955 }
956
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700957 /**
958 * Update the array of occupied cells.
959 * @param ignoreView If non-null, the space occupied by this View is treated as vacant
960 * @param ignoreFolders If true, a cell occupied by a Folder is treated as vacant
961 */
962 private void findOccupiedCells(
963 int xCount, int yCount, boolean[][] occupied, View ignoreView, boolean ignoreFolders) {
964
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800965 for (int x = 0; x < xCount; x++) {
966 for (int y = 0; y < yCount; y++) {
967 occupied[x][y] = false;
968 }
969 }
970
971 int count = getChildCount();
972 for (int i = 0; i < count; i++) {
973 View child = getChildAt(i);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700974 if ((ignoreFolders && child instanceof Folder) || child.equals(ignoreView)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800975 continue;
976 }
977 LayoutParams lp = (LayoutParams) child.getLayoutParams();
978
979 for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
980 for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
981 occupied[x][y] = true;
982 }
983 }
984 }
985 }
986
987 @Override
988 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
989 return new CellLayout.LayoutParams(getContext(), attrs);
990 }
991
992 @Override
993 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
994 return p instanceof CellLayout.LayoutParams;
995 }
996
997 @Override
998 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
999 return new CellLayout.LayoutParams(p);
1000 }
1001
Winson Chungaafa03c2010-06-11 17:34:16 -07001002 public static class CellLayoutAnimationController extends LayoutAnimationController {
1003 public CellLayoutAnimationController(Animation animation, float delay) {
1004 super(animation, delay);
1005 }
1006
1007 @Override
1008 protected long getDelayForView(View view) {
1009 return (int) (Math.random() * 150);
1010 }
1011 }
1012
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001013 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1014 /**
1015 * Horizontal location of the item in the grid.
1016 */
1017 @ViewDebug.ExportedProperty
1018 public int cellX;
1019
1020 /**
1021 * Vertical location of the item in the grid.
1022 */
1023 @ViewDebug.ExportedProperty
1024 public int cellY;
1025
1026 /**
1027 * Number of cells spanned horizontally by the item.
1028 */
1029 @ViewDebug.ExportedProperty
1030 public int cellHSpan;
1031
1032 /**
1033 * Number of cells spanned vertically by the item.
1034 */
1035 @ViewDebug.ExportedProperty
1036 public int cellVSpan;
Winson Chungaafa03c2010-06-11 17:34:16 -07001037
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001038 /**
1039 * Is this item currently being dragged
1040 */
1041 public boolean isDragging;
1042
1043 // X coordinate of the view in the layout.
1044 @ViewDebug.ExportedProperty
1045 int x;
1046 // Y coordinate of the view in the layout.
1047 @ViewDebug.ExportedProperty
1048 int y;
1049
Romain Guy84f296c2009-11-04 15:00:44 -08001050 boolean dropped;
Romain Guyfcb9e712009-10-02 16:06:52 -07001051
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001052 public LayoutParams(Context c, AttributeSet attrs) {
1053 super(c, attrs);
1054 cellHSpan = 1;
1055 cellVSpan = 1;
1056 }
1057
1058 public LayoutParams(ViewGroup.LayoutParams source) {
1059 super(source);
1060 cellHSpan = 1;
1061 cellVSpan = 1;
1062 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001063
1064 public LayoutParams(LayoutParams source) {
1065 super(source);
1066 this.cellX = source.cellX;
1067 this.cellY = source.cellY;
1068 this.cellHSpan = source.cellHSpan;
1069 this.cellVSpan = source.cellVSpan;
1070 }
1071
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001072 public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
Romain Guy8f19cdd2010-01-08 15:07:00 -08001073 super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001074 this.cellX = cellX;
1075 this.cellY = cellY;
1076 this.cellHSpan = cellHSpan;
1077 this.cellVSpan = cellVSpan;
1078 }
1079
1080 public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
1081 int hStartPadding, int vStartPadding) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001082
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001083 final int myCellHSpan = cellHSpan;
1084 final int myCellVSpan = cellVSpan;
1085 final int myCellX = cellX;
1086 final int myCellY = cellY;
Winson Chungaafa03c2010-06-11 17:34:16 -07001087
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001088 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
1089 leftMargin - rightMargin;
1090 height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
1091 topMargin - bottomMargin;
1092
1093 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
1094 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
1095 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001096
1097 public String toString() {
1098 return "(" + this.cellX + ", " + this.cellY + ")";
1099 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001100 }
1101
1102 static final class CellInfo implements ContextMenu.ContextMenuInfo {
Michael Jurkac28de512010-08-13 11:27:44 -07001103 private boolean[][] mOccupied;
1104 private int mCountX;
1105 private int mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001106 View cell;
1107 int cellX;
1108 int cellY;
Michael Jurkac28de512010-08-13 11:27:44 -07001109 // intersectX and intersectY constrain the results of findCellForSpan; any empty space
1110 // it results must include this point (unless intersectX and intersectY are -1)
1111 int intersectX;
1112 int intersectY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001113 int spanX;
1114 int spanY;
1115 int screen;
1116 boolean valid;
1117
Michael Jurkac28de512010-08-13 11:27:44 -07001118 void updateOccupiedCells(boolean[][] occupied, int xCount, int yCount) {
1119 mOccupied = occupied.clone();
1120 mCountX = xCount;
1121 mCountY = yCount;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001122 }
1123
Michael Jurkac28de512010-08-13 11:27:44 -07001124 void updateOccupiedCells(boolean[] occupied, int xCount, int yCount) {
1125 if (mOccupied == null || mCountX != xCount || mCountY != yCount) {
1126 mOccupied = new boolean[xCount][yCount];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001127 }
Michael Jurkac28de512010-08-13 11:27:44 -07001128 mCountX = xCount;
1129 mCountY = yCount;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001130 for (int y = 0; y < yCount; y++) {
1131 for (int x = 0; x < xCount; x++) {
Michael Jurkac28de512010-08-13 11:27:44 -07001132 mOccupied[x][y] = occupied[y * xCount + x];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001133 }
1134 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001135 }
1136
Michael Jurkac28de512010-08-13 11:27:44 -07001137 boolean existsEmptyCell() {
1138 return findCellForSpan(null, 1, 1);
1139 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001140 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001141 * Finds the upper-left coordinate of the first rectangle in the grid that can
Michael Jurkac28de512010-08-13 11:27:44 -07001142 * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
1143 * then this method will only return coordinates for rectangles that contain the cell
1144 * (intersectX, intersectY)
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001145 *
1146 * @param cellXY The array that will contain the position of a vacant cell if such a cell
1147 * can be found.
1148 * @param spanX The horizontal span of the cell we want to find.
1149 * @param spanY The vertical span of the cell we want to find.
1150 *
1151 * @return True if a vacant cell of the specified dimension was found, false otherwise.
1152 */
1153 boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
Michael Jurka0e260592010-06-30 17:07:39 -07001154 // return the span represented by the CellInfo only there is no view there
1155 // (this.cell == null) and there is enough space
Michael Jurkac28de512010-08-13 11:27:44 -07001156
Michael Jurka0e260592010-06-30 17:07:39 -07001157 if (this.cell == null && this.spanX >= spanX && this.spanY >= spanY) {
Michael Jurkac28de512010-08-13 11:27:44 -07001158 if (cellXY != null) {
1159 cellXY[0] = cellX;
1160 cellXY[1] = cellY;
1161 }
1162 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001163 }
1164
Michael Jurkac28de512010-08-13 11:27:44 -07001165 int startX = 0;
1166 if (intersectX >= 0) {
1167 startX = Math.max(startX, intersectX - (spanX - 1));
1168 }
1169 int endX = mCountX - (spanX - 1);
1170 if (intersectX >= 0) {
1171 endX = Math.min(endX, intersectX + (spanX - 1));
1172 }
1173 int startY = 0;
1174 if (intersectY >= 0) {
1175 startY = Math.max(startY, intersectY - (spanY - 1));
1176 }
1177 int endY = mCountY - (spanY - 1);
1178 if (intersectY >= 0) {
1179 endY = Math.min(endY, intersectY + (spanY - 1));
1180 }
1181
1182 for (int x = startX; x < endX + 1; x++) {
1183 inner:
1184 for (int y = startY; y < endY; y++) {
1185 for (int i = 0; i < spanX; i++) {
1186 for (int j = 0; j < spanY; j++) {
1187 if (mOccupied[x + i][y + j]) {
1188 // small optimization: we can skip to below the row we just found
1189 // an occupied cell
1190 y += j;
1191 continue inner;
1192 }
1193 }
1194 }
1195 if (cellXY != null) {
1196 cellXY[0] = x;
1197 cellXY[1] = y;
1198 }
1199 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001200 }
1201 }
Michael Jurkac28de512010-08-13 11:27:44 -07001202 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001203 }
1204
1205 @Override
1206 public String toString() {
Winson Chungaafa03c2010-06-11 17:34:16 -07001207 return "Cell[view=" + (cell == null ? "null" : cell.getClass())
1208 + ", x=" + cellX + ", y=" + cellY + "]";
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001209 }
1210 }
Mike Cleronf8bbd342009-10-23 16:15:16 -07001211
1212 public boolean lastDownOnOccupiedCell() {
1213 return mLastDownOnOccupiedCell;
1214 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001215}