blob: 7ae26bb8f94623b5bd7309e468e8ea915850a244 [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 Jurkaa63c4522010-08-19 13:52:27 -070027import android.graphics.Color;
Michael Jurkadee05892010-07-27 10:01:56 -070028import android.graphics.Matrix;
29import android.graphics.Paint;
30import android.graphics.PorterDuff;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080031import android.graphics.Rect;
32import android.graphics.RectF;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070033import android.graphics.drawable.Drawable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.util.AttributeSet;
35import android.view.ContextMenu;
36import android.view.MotionEvent;
37import android.view.View;
38import android.view.ViewDebug;
39import android.view.ViewGroup;
Michael Jurkadee05892010-07-27 10:01:56 -070040import android.view.View.OnTouchListener;
Winson Chungaafa03c2010-06-11 17:34:16 -070041import android.view.animation.Animation;
42import android.view.animation.LayoutAnimationController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070044import java.util.ArrayList;
45import java.util.Arrays;
Romain Guyedcce092010-03-04 13:03:17 -080046
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047public class CellLayout extends ViewGroup {
Winson Chungaafa03c2010-06-11 17:34:16 -070048 static final String TAG = "CellLayout";
Michael Jurkadee05892010-07-27 10:01:56 -070049 // we make the dimmed bitmap smaller than the screen itself for memory + perf reasons
50 static final float DIMMED_BITMAP_SCALE = 0.25f;
Winson Chungaafa03c2010-06-11 17:34:16 -070051
The Android Open Source Project31dd5032009-03-03 19:32:27 -080052 private boolean mPortrait;
53
54 private int mCellWidth;
55 private int mCellHeight;
Winson Chungaafa03c2010-06-11 17:34:16 -070056
Winson Chungaafa03c2010-06-11 17:34:16 -070057 private int mLeftPadding;
58 private int mRightPadding;
59 private int mTopPadding;
60 private int mBottomPadding;
61
Adam Cohend22015c2010-07-26 22:02:18 -070062 private int mCountX;
63 private int mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080064
65 private int mWidthGap;
66 private int mHeightGap;
67
68 private final Rect mRect = new Rect();
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -070069 private final RectF mRectF = new RectF();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080070 private final CellInfo mCellInfo = new CellInfo();
Winson Chungaafa03c2010-06-11 17:34:16 -070071
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070072 // This is a temporary variable to prevent having to allocate a new object just to
73 // return an (x, y) value from helper functions. Do NOT use it to maintain other state.
74 private final int[] mTmpCellXY = new int[2];
75
The Android Open Source Project31dd5032009-03-03 19:32:27 -080076 boolean[][] mOccupied;
77
Michael Jurkadee05892010-07-27 10:01:56 -070078 private OnTouchListener mInterceptTouchListener;
79
80 // this is what the home screen fades to when it shrinks
81 // (ie in all apps and in home screen customize mode)
82 private Bitmap mDimmedBitmap;
83 private Canvas mDimmedBitmapCanvas;
84 private float mDimmedBitmapAlpha;
Michael Jurkaa63c4522010-08-19 13:52:27 -070085 private boolean mDimmedBitmapDirty = false;
Michael Jurkadee05892010-07-27 10:01:56 -070086 private final Paint mDimmedBitmapPaint = new Paint();
87 private final Rect mLayoutRect = new Rect();
88 private final Rect mDimmedBitmapRect = new Rect();
Michael Jurkaa63c4522010-08-19 13:52:27 -070089 private Drawable mDimmedBitmapBackground;
90 private Drawable mDimmedBitmapBackgroundHover;
91 // If we're actively dragging something over this screen and it's small,
92 // mHover is true
93 private boolean mHover = false;
Michael Jurkadee05892010-07-27 10:01:56 -070094
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070095 private final RectF mDragRect = new RectF();
96
97 // When dragging, used to indicate a vacant drop location
98 private Drawable mVacantDrawable;
99
100 // When dragging, used to indicate an occupied drop location
101 private Drawable mOccupiedDrawable;
102
103 // Updated to point to mVacantDrawable or mOccupiedDrawable, as appropriate
104 private Drawable mDragRectDrawable;
105
106 // When a drag operation is in progress, holds the nearest cell to the touch point
107 private final int[] mDragCell = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108
109 private boolean mDirtyTag;
Mike Cleronf8bbd342009-10-23 16:15:16 -0700110 private boolean mLastDownOnOccupiedCell = false;
Winson Chungaafa03c2010-06-11 17:34:16 -0700111
112 private final WallpaperManager mWallpaperManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113
114 public CellLayout(Context context) {
115 this(context, null);
116 }
117
118 public CellLayout(Context context, AttributeSet attrs) {
119 this(context, attrs, 0);
120 }
121
122 public CellLayout(Context context, AttributeSet attrs, int defStyle) {
123 super(context, attrs, defStyle);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700124
125 // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
126 // the user where a dragged item will land when dropped.
127 setWillNotDraw(false);
128 mVacantDrawable = getResources().getDrawable(R.drawable.rounded_rect_green);
129 mOccupiedDrawable = getResources().getDrawable(R.drawable.rounded_rect_red);
130
Michael Jurkaa63c4522010-08-19 13:52:27 -0700131 if (LauncherApplication.isScreenXLarge()) {
132 mDimmedBitmapBackground = getResources().getDrawable(
133 R.drawable.mini_home_screen_bg);
134
135 mDimmedBitmapBackgroundHover = getResources().getDrawable(
136 R.drawable.mini_home_screen_bg_hover);
137 }
138
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800139 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
140
141 mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
142 mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700143
Adam Cohend22015c2010-07-26 22:02:18 -0700144 mLeftPadding =
145 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisStartPadding, 10);
146 mRightPadding =
147 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisEndPadding, 10);
148 mTopPadding =
149 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisStartPadding, 10);
150 mBottomPadding =
151 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisEndPadding, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700152
Adam Cohend22015c2010-07-26 22:02:18 -0700153 mCountX = LauncherModel.getCellCountX();
154 mCountY = LauncherModel.getCellCountY();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800155
156 a.recycle();
157
158 setAlwaysDrawnWithCacheEnabled(false);
159
Romain Guy84f296c2009-11-04 15:00:44 -0800160 mWallpaperManager = WallpaperManager.getInstance(getContext());
Michael Jurkadee05892010-07-27 10:01:56 -0700161
162 mDimmedBitmapPaint.setFilterBitmap(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800163 }
164
Michael Jurkaa63c4522010-08-19 13:52:27 -0700165 public void setHover(boolean value) {
166 if (mHover != value) {
167 invalidate();
168 }
169 mHover = value;
170 }
171
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700172 @Override
Romain Guya6abce82009-11-10 02:54:41 -0800173 public void dispatchDraw(Canvas canvas) {
Michael Jurkaa63c4522010-08-19 13:52:27 -0700174 if (mDimmedBitmapAlpha > 0.0f) {
175 final Drawable bg = mHover ? mDimmedBitmapBackgroundHover : mDimmedBitmapBackground;
176 bg.setAlpha((int) (mDimmedBitmapAlpha * 255));
177 bg.draw(canvas);
178 }
Romain Guya6abce82009-11-10 02:54:41 -0800179 super.dispatchDraw(canvas);
180 }
181
182 @Override
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700183 protected void onDraw(Canvas canvas) {
184 if (!mDragRect.isEmpty()) {
185 mDragRectDrawable.setBounds(
186 (int)mDragRect.left,
187 (int)mDragRect.top,
188 (int)mDragRect.right,
189 (int)mDragRect.bottom);
190 mDragRectDrawable.draw(canvas);
191 }
Michael Jurkadee05892010-07-27 10:01:56 -0700192 if (mDimmedBitmap != null && mDimmedBitmapAlpha > 0.0f) {
193 if (mDimmedBitmapDirty) {
194 updateDimmedBitmap();
195 mDimmedBitmapDirty = false;
196 }
197 mDimmedBitmapPaint.setAlpha((int) (mDimmedBitmapAlpha * 255));
198
199 canvas.drawBitmap(mDimmedBitmap, mDimmedBitmapRect, mLayoutRect, mDimmedBitmapPaint);
200 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700201 }
202
203 @Override
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700204 public void cancelLongPress() {
205 super.cancelLongPress();
206
207 // Cancel long press for all children
208 final int count = getChildCount();
209 for (int i = 0; i < count; i++) {
210 final View child = getChildAt(i);
211 child.cancelLongPress();
212 }
213 }
214
Michael Jurkadee05892010-07-27 10:01:56 -0700215 public void setOnInterceptTouchListener(View.OnTouchListener listener) {
216 mInterceptTouchListener = listener;
217 }
218
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800219 int getCountX() {
Adam Cohend22015c2010-07-26 22:02:18 -0700220 return mCountX;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800221 }
222
223 int getCountY() {
Adam Cohend22015c2010-07-26 22:02:18 -0700224 return mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800225 }
226
Winson Chungaafa03c2010-06-11 17:34:16 -0700227 // Takes canonical layout parameters
228 public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
229 final LayoutParams lp = params;
230
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800231 // Generate an id for each view, this assumes we have at most 256x256 cells
232 // per workspace screen
Adam Cohend22015c2010-07-26 22:02:18 -0700233 if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700234 // If the horizontal or vertical span is set to -1, it is taken to
235 // mean that it spans the extent of the CellLayout
Adam Cohend22015c2010-07-26 22:02:18 -0700236 if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
237 if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800238
Winson Chungaafa03c2010-06-11 17:34:16 -0700239 child.setId(childId);
240
Michael Jurkadee05892010-07-27 10:01:56 -0700241 // We might be in the middle or end of shrinking/fading to a dimmed view
242 // Make sure this view's alpha is set the same as all the rest of the views
243 child.setAlpha(1.0f - mDimmedBitmapAlpha);
244
Winson Chungaafa03c2010-06-11 17:34:16 -0700245 addView(child, index, lp);
Michael Jurkadee05892010-07-27 10:01:56 -0700246
247 // next time we draw the dimmed bitmap we need to update it
248 mDimmedBitmapDirty = true;
Winson Chungaafa03c2010-06-11 17:34:16 -0700249 return true;
250 }
251 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800252 }
253
254 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700255 public void removeView(View view) {
256 super.removeView(view);
257 mDimmedBitmapDirty = true;
258 }
259
260 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800261 public void requestChildFocus(View child, View focused) {
262 super.requestChildFocus(child, focused);
263 if (child != null) {
264 Rect r = new Rect();
265 child.getDrawingRect(r);
266 requestRectangleOnScreen(r);
267 }
268 }
269
270 @Override
271 protected void onAttachedToWindow() {
272 super.onAttachedToWindow();
273 mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
274 }
275
Michael Jurkaaf442092010-06-10 17:01:57 -0700276 public void setTagToCellInfoForPoint(int touchX, int touchY) {
277 final CellInfo cellInfo = mCellInfo;
278 final Rect frame = mRect;
279 final int x = touchX + mScrollX;
280 final int y = touchY + mScrollY;
281 final int count = getChildCount();
282
283 boolean found = false;
284 for (int i = count - 1; i >= 0; i--) {
285 final View child = getChildAt(i);
286
287 if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
288 child.getHitRect(frame);
289 if (frame.contains(x, y)) {
290 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
291 cellInfo.cell = child;
292 cellInfo.cellX = lp.cellX;
293 cellInfo.cellY = lp.cellY;
294 cellInfo.spanX = lp.cellHSpan;
295 cellInfo.spanY = lp.cellVSpan;
Michael Jurkac28de512010-08-13 11:27:44 -0700296 cellInfo.intersectX = lp.cellX;
297 cellInfo.intersectY = lp.cellY;
Michael Jurkaaf442092010-06-10 17:01:57 -0700298 cellInfo.valid = true;
299 found = true;
300 mDirtyTag = false;
301 break;
302 }
303 }
304 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700305
Michael Jurkaaf442092010-06-10 17:01:57 -0700306 mLastDownOnOccupiedCell = found;
307
308 if (!found) {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700309 final int cellXY[] = mTmpCellXY;
Michael Jurkaaf442092010-06-10 17:01:57 -0700310 pointToCellExact(x, y, cellXY);
311
312 final boolean portrait = mPortrait;
Adam Cohend22015c2010-07-26 22:02:18 -0700313 final int xCount = mCountX;
314 final int yCount = mCountY;
Michael Jurkaaf442092010-06-10 17:01:57 -0700315
316 final boolean[][] occupied = mOccupied;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700317 findOccupiedCells(xCount, yCount, occupied, null, true);
Michael Jurkaaf442092010-06-10 17:01:57 -0700318
319 cellInfo.cell = null;
320 cellInfo.cellX = cellXY[0];
321 cellInfo.cellY = cellXY[1];
322 cellInfo.spanX = 1;
323 cellInfo.spanY = 1;
Michael Jurkac28de512010-08-13 11:27:44 -0700324 cellInfo.intersectX = cellXY[0];
325 cellInfo.intersectY = cellXY[1];
Michael Jurkaaf442092010-06-10 17:01:57 -0700326 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
327 cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
328
329 // Instead of finding the interesting vacant cells here, wait until a
330 // caller invokes getTag() to retrieve the result. Finding the vacant
331 // cells is a bit expensive and can generate many new objects, it's
332 // therefore better to defer it until we know we actually need it.
333
334 mDirtyTag = true;
335 }
336 setTag(cellInfo);
337 }
338
Winson Chungaafa03c2010-06-11 17:34:16 -0700339
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800340 @Override
341 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkadee05892010-07-27 10:01:56 -0700342 if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
343 return true;
344 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800345 final int action = ev.getAction();
346 final CellInfo cellInfo = mCellInfo;
347
348 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkaaf442092010-06-10 17:01:57 -0700349 setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800350 } else if (action == MotionEvent.ACTION_UP) {
351 cellInfo.cell = null;
352 cellInfo.cellX = -1;
353 cellInfo.cellY = -1;
354 cellInfo.spanX = 0;
355 cellInfo.spanY = 0;
356 cellInfo.valid = false;
357 mDirtyTag = false;
358 setTag(cellInfo);
359 }
360
361 return false;
362 }
363
364 @Override
365 public CellInfo getTag() {
366 final CellInfo info = (CellInfo) super.getTag();
367 if (mDirtyTag && info.valid) {
Adam Cohend22015c2010-07-26 22:02:18 -0700368 final int xCount = mCountX;
369 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800370
371 final boolean[][] occupied = mOccupied;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700372 findOccupiedCells(xCount, yCount, occupied, null, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800373
Michael Jurkac28de512010-08-13 11:27:44 -0700374 info.updateOccupiedCells(occupied, mCountX, mCountY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800375
376 mDirtyTag = false;
377 }
378 return info;
379 }
380
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700381 /**
382 * Check if the column 'x' is empty from rows 'top' to 'bottom', inclusive.
383 */
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800384 private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
385 for (int y = top; y <= bottom; y++) {
386 if (occupied[x][y]) {
387 return false;
388 }
389 }
390 return true;
391 }
392
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700393 /**
394 * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
395 */
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800396 private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
397 for (int x = left; x <= right; x++) {
398 if (occupied[x][y]) {
399 return false;
400 }
401 }
402 return true;
403 }
404
Michael Jurkac28de512010-08-13 11:27:44 -0700405 CellInfo updateOccupiedCells(boolean[] occupiedCells, View ignoreView) {
Adam Cohend22015c2010-07-26 22:02:18 -0700406 final int xCount = mCountX;
407 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800408
409 boolean[][] occupied = mOccupied;
410
411 if (occupiedCells != null) {
412 for (int y = 0; y < yCount; y++) {
413 for (int x = 0; x < xCount; x++) {
414 occupied[x][y] = occupiedCells[y * xCount + x];
415 }
416 }
417 } else {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700418 findOccupiedCells(xCount, yCount, occupied, ignoreView, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800419 }
420
421 CellInfo cellInfo = new CellInfo();
422
423 cellInfo.cellX = -1;
424 cellInfo.cellY = -1;
Michael Jurkac28de512010-08-13 11:27:44 -0700425 cellInfo.intersectX = -1;
426 cellInfo.intersectY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800427 cellInfo.spanY = 0;
428 cellInfo.spanX = 0;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800429 cellInfo.screen = mCellInfo.screen;
430
Michael Jurkac28de512010-08-13 11:27:44 -0700431 cellInfo.updateOccupiedCells(occupied, mCountX, mCountY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800432
Michael Jurkac28de512010-08-13 11:27:44 -0700433 cellInfo.valid = cellInfo.existsEmptyCell();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800434
435 // Assume the caller will perform their own cell searching, otherwise we
436 // risk causing an unnecessary rebuild after findCellForSpan()
Winson Chungaafa03c2010-06-11 17:34:16 -0700437
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800438 return cellInfo;
439 }
440
441 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700442 * Given a point, return the cell that strictly encloses that point
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800443 * @param x X coordinate of the point
444 * @param y Y coordinate of the point
445 * @param result Array of 2 ints to hold the x and y coordinate of the cell
446 */
447 void pointToCellExact(int x, int y, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700448 final int hStartPadding = getLeftPadding();
449 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800450
451 result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
452 result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
453
Adam Cohend22015c2010-07-26 22:02:18 -0700454 final int xAxis = mCountX;
455 final int yAxis = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800456
457 if (result[0] < 0) result[0] = 0;
458 if (result[0] >= xAxis) result[0] = xAxis - 1;
459 if (result[1] < 0) result[1] = 0;
460 if (result[1] >= yAxis) result[1] = yAxis - 1;
461 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700462
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800463 /**
464 * Given a point, return the cell that most closely encloses that point
465 * @param x X coordinate of the point
466 * @param y Y coordinate of the point
467 * @param result Array of 2 ints to hold the x and y coordinate of the cell
468 */
469 void pointToCellRounded(int x, int y, int[] result) {
470 pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
471 }
472
473 /**
474 * Given a cell coordinate, return the point that represents the upper left corner of that cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700475 *
476 * @param cellX X coordinate of the cell
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800477 * @param cellY Y coordinate of the cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700478 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800479 * @param result Array of 2 ints to hold the x and y coordinate of the point
480 */
481 void cellToPoint(int cellX, int cellY, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700482 final int hStartPadding = getLeftPadding();
483 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800484
485 result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
486 result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
487 }
488
Romain Guy84f296c2009-11-04 15:00:44 -0800489 int getCellWidth() {
490 return mCellWidth;
491 }
492
493 int getCellHeight() {
494 return mCellHeight;
495 }
496
Romain Guy1a304a12009-11-10 00:02:32 -0800497 int getLeftPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700498 return mLeftPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800499 }
500
501 int getTopPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700502 return mTopPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800503 }
504
505 int getRightPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700506 return mRightPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800507 }
508
509 int getBottomPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700510 return mBottomPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800511 }
512
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800513 @Override
514 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
515 // TODO: currently ignoring padding
Winson Chungaafa03c2010-06-11 17:34:16 -0700516
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800517 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700518 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
519
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800520 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
521 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700522
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800523 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
524 throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
525 }
526
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800527 final int cellWidth = mCellWidth;
528 final int cellHeight = mCellHeight;
529
Adam Cohend22015c2010-07-26 22:02:18 -0700530 if (mOccupied == null) {
531 mOccupied = new boolean[mCountX][mCountY];
Winson Chungaafa03c2010-06-11 17:34:16 -0700532 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800533
Adam Cohend22015c2010-07-26 22:02:18 -0700534 int numWidthGaps = mCountX - 1;
535 int numHeightGaps = mCountY - 1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800536
Adam Cohend22015c2010-07-26 22:02:18 -0700537 int vSpaceLeft = heightSpecSize - mTopPadding
538 - mBottomPadding - (cellHeight * mCountY);
539 mHeightGap = vSpaceLeft / numHeightGaps;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800540
Adam Cohend22015c2010-07-26 22:02:18 -0700541 int hSpaceLeft = widthSpecSize - mLeftPadding
542 - mRightPadding - (cellWidth * mCountX);
543 mWidthGap = hSpaceLeft / numWidthGaps;
Winson Chungaafa03c2010-06-11 17:34:16 -0700544
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800545 int count = getChildCount();
546
547 for (int i = 0; i < count; i++) {
548 View child = getChildAt(i);
549 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Winson Chungaafa03c2010-06-11 17:34:16 -0700550 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
551 mLeftPadding, mTopPadding);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800552
Winson Chungaafa03c2010-06-11 17:34:16 -0700553 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
554 MeasureSpec.EXACTLY);
555 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
556 MeasureSpec.EXACTLY);
557
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800558 child.measure(childWidthMeasureSpec, childheightMeasureSpec);
559 }
560
561 setMeasuredDimension(widthSpecSize, heightSpecSize);
562 }
563
564 @Override
565 protected void onLayout(boolean changed, int l, int t, int r, int b) {
566 int count = getChildCount();
567
568 for (int i = 0; i < count; i++) {
569 View child = getChildAt(i);
570 if (child.getVisibility() != GONE) {
571
572 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
573
574 int childLeft = lp.x;
575 int childTop = lp.y;
576 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
Romain Guy84f296c2009-11-04 15:00:44 -0800577
578 if (lp.dropped) {
579 lp.dropped = false;
580
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700581 final int[] cellXY = mTmpCellXY;
Romain Guy06762ab2010-01-25 16:51:08 -0800582 getLocationOnScreen(cellXY);
Romain Guy84f296c2009-11-04 15:00:44 -0800583 mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
Romain Guy06762ab2010-01-25 16:51:08 -0800584 cellXY[0] + childLeft + lp.width / 2,
585 cellXY[1] + childTop + lp.height / 2, 0, null);
Romain Guy84f296c2009-11-04 15:00:44 -0800586 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800587 }
588 }
589 }
590
591 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700592 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
593 super.onSizeChanged(w, h, oldw, oldh);
594 mLayoutRect.set(0, 0, w, h);
595 mDimmedBitmapRect.set(0, 0, (int) (DIMMED_BITMAP_SCALE * w), (int) (DIMMED_BITMAP_SCALE * h));
Michael Jurkaa63c4522010-08-19 13:52:27 -0700596 if (mDimmedBitmapBackground != null) {
597 mDimmedBitmapBackground.setBounds(mLayoutRect);
598 }
599 if (mDimmedBitmapBackgroundHover != null) {
600 mDimmedBitmapBackgroundHover.setBounds(mLayoutRect);
601 }
Michael Jurkadee05892010-07-27 10:01:56 -0700602 }
603
604 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800605 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
606 final int count = getChildCount();
607 for (int i = 0; i < count; i++) {
608 final View view = getChildAt(i);
609 view.setDrawingCacheEnabled(enabled);
610 // Update the drawing caches
Adam Powellfefa0ce2010-05-03 10:23:50 -0700611 view.buildDrawingCache(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800612 }
613 }
614
615 @Override
616 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
617 super.setChildrenDrawnWithCacheEnabled(enabled);
618 }
619
Michael Jurkadee05892010-07-27 10:01:56 -0700620 public float getDimmedBitmapAlpha() {
621 return mDimmedBitmapAlpha;
622 }
623
624 public void setDimmedBitmapAlpha(float alpha) {
625 // If we're dimming the screen after it was not dimmed, refresh
626 // to allow for updated widgets. We don't continually refresh it
627 // after this point, however, as an optimization
628 if (mDimmedBitmapAlpha == 0.0f && alpha > 0.0f) {
629 updateDimmedBitmap();
630 }
631 mDimmedBitmapAlpha = alpha;
632 setChildrenAlpha(1.0f - mDimmedBitmapAlpha);
Michael Jurka0142d492010-08-25 17:46:15 -0700633 invalidate();
Michael Jurkadee05892010-07-27 10:01:56 -0700634 }
635
636 private void setChildrenAlpha(float alpha) {
Michael Jurka0142d492010-08-25 17:46:15 -0700637 final int childCount = getChildCount();
638 for (int i = 0; i < childCount; i++) {
Michael Jurkadee05892010-07-27 10:01:56 -0700639 getChildAt(i).setAlpha(alpha);
640 }
641 }
642
643 public void updateDimmedBitmap() {
644 if (mDimmedBitmap == null) {
645 mDimmedBitmap = Bitmap.createBitmap((int) (getWidth() * DIMMED_BITMAP_SCALE),
646 (int) (getHeight() * DIMMED_BITMAP_SCALE), Bitmap.Config.ARGB_8888);
647 mDimmedBitmapCanvas = new Canvas(mDimmedBitmap);
648 mDimmedBitmapCanvas.scale(DIMMED_BITMAP_SCALE, DIMMED_BITMAP_SCALE);
649 }
650 // clear the canvas
651 mDimmedBitmapCanvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
652
653 // draw the screen into the bitmap
654 // just for drawing to the bitmap, make all the items on the screen opaque
655 setChildrenAlpha(1.0f);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700656 // call our superclass's dispatchdraw so we don't draw the background
657 super.dispatchDraw(mDimmedBitmapCanvas);
Michael Jurkadee05892010-07-27 10:01:56 -0700658 setChildrenAlpha(1.0f - mDimmedBitmapAlpha);
659
Michael Jurkaa63c4522010-08-19 13:52:27 -0700660 // replace all colored areas with a dark (semi-transparent black)
661 mDimmedBitmapCanvas.drawColor(Color.argb(160, 0, 0, 0), PorterDuff.Mode.SRC_IN);
Michael Jurkadee05892010-07-27 10:01:56 -0700662 }
663
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700664 private boolean isVacant(int originX, int originY, int spanX, int spanY) {
665 for (int i = 0; i < spanY; i++) {
666 if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
667 return false;
668 }
669 }
670 return true;
671 }
672
Patrick Dubroy440c3602010-07-13 17:50:32 -0700673 public View getChildAt(int x, int y) {
674 final int count = getChildCount();
675 for (int i = 0; i < count; i++) {
676 View child = getChildAt(i);
677 LayoutParams lp = (LayoutParams) child.getLayoutParams();
678
679 if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
680 (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
681 return child;
682 }
683 }
684 return null;
685 }
686
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700687 /**
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700688 * Estimate the size that a child with the given dimensions will take in the layout.
689 */
690 void estimateChildSize(int minWidth, int minHeight, int[] result) {
691 // Assuming it's placed at 0, 0, find where the bottom right cell will land
692 rectToCell(minWidth, minHeight, result);
693
694 // Then figure out the rect it will occupy
695 cellToRect(0, 0, result[0], result[1], mRectF);
696 result[0] = (int)mRectF.width();
697 result[1] = (int)mRectF.height();
698 }
699
700 /**
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700701 * Estimate where the top left cell of the dragged item will land if it is dropped.
702 *
703 * @param originX The X value of the top left corner of the item
704 * @param originY The Y value of the top left corner of the item
705 * @param spanX The number of horizontal cells that the item spans
706 * @param spanY The number of vertical cells that the item spans
707 * @param result The estimated drop cell X and Y.
708 */
709 void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
Adam Cohend22015c2010-07-26 22:02:18 -0700710 final int countX = mCountX;
711 final int countY = mCountY;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700712
Michael Jurkaa63c4522010-08-19 13:52:27 -0700713 // pointToCellRounded takes the top left of a cell but will pad that with
714 // cellWidth/2 and cellHeight/2 when finding the matching cell
715 pointToCellRounded(originX, originY, result);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700716
717 // If the item isn't fully on this screen, snap to the edges
718 int rightOverhang = result[0] + spanX - countX;
719 if (rightOverhang > 0) {
720 result[0] -= rightOverhang; // Snap to right
721 }
722 result[0] = Math.max(0, result[0]); // Snap to left
723 int bottomOverhang = result[1] + spanY - countY;
724 if (bottomOverhang > 0) {
725 result[1] -= bottomOverhang; // Snap to bottom
726 }
727 result[1] = Math.max(0, result[1]); // Snap to top
728 }
729
730 void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) {
731 final int[] originCell = mDragCell;
732 final int[] cellXY = mTmpCellXY;
733 estimateDropCell(originX, originY, spanX, spanY, cellXY);
734
735 // Only recalculate the bounding rect when necessary
736 if (!Arrays.equals(cellXY, originCell)) {
737 originCell[0] = cellXY[0];
738 originCell[1] = cellXY[1];
739
740 // Find the top left corner of the rect the object will occupy
741 final int[] topLeft = mTmpCellXY;
742 cellToPoint(originCell[0], originCell[1], topLeft);
743 final int left = topLeft[0];
744 final int top = topLeft[1];
745
746 // Now find the bottom right
747 final int[] bottomRight = mTmpCellXY;
748 cellToPoint(originCell[0] + spanX - 1, originCell[1] + spanY - 1, bottomRight);
749 bottomRight[0] += mCellWidth;
750 bottomRight[1] += mCellHeight;
751
Adam Cohend22015c2010-07-26 22:02:18 -0700752 final int countX = mCountX;
753 final int countY = mCountY;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700754 // TODO: It's not necessary to do this every time, but it's not especially expensive
755 findOccupiedCells(countX, countY, mOccupied, view, false);
756
757 boolean vacant = isVacant(originCell[0], originCell[1], spanX, spanY);
758 mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable;
759
760 // mDragRect will be rendered in onDraw()
761 mDragRect.set(left, top, bottomRight[0], bottomRight[1]);
762 invalidate();
763 }
764 }
765
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800766 /**
Jeff Sharkey70864282009-04-07 21:08:40 -0700767 * Find a vacant area that will fit the given bounds nearest the requested
768 * cell location. Uses Euclidean distance to score multiple vacant areas.
Winson Chungaafa03c2010-06-11 17:34:16 -0700769 *
Romain Guy51afc022009-05-04 18:03:43 -0700770 * @param pixelX The X location at which you want to search for a vacant area.
771 * @param pixelY The Y location at which you want to search for a vacant area.
Jeff Sharkey70864282009-04-07 21:08:40 -0700772 * @param spanX Horizontal span of the object.
773 * @param spanY Vertical span of the object.
774 * @param vacantCells Pre-computed set of vacant cells to search.
775 * @param recycle Previously returned value to possibly recycle.
776 * @return The X, Y cell of a vacant area that can contain this object,
777 * nearest the requested location.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800778 */
Jeff Sharkey70864282009-04-07 21:08:40 -0700779 int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
780 CellInfo vacantCells, int[] recycle) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700781
Jeff Sharkey70864282009-04-07 21:08:40 -0700782 // Keep track of best-scoring drop area
783 final int[] bestXY = recycle != null ? recycle : new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700784 double bestDistance = Double.MAX_VALUE;
Winson Chungaafa03c2010-06-11 17:34:16 -0700785
Michael Jurkac28de512010-08-13 11:27:44 -0700786 for (int x = 0; x < mCountX - (spanX - 1); x++) {
787 inner:
788 for (int y = 0; y < mCountY - (spanY - 1); y++) {
789 for (int i = 0; i < spanX; i++) {
790 for (int j = 0; j < spanY; j++) {
791 if (mOccupied[x + i][y + j]) {
792 // small optimization: we can skip to below the row we just found
793 // an occupied cell
794 y += j;
795 continue inner;
796 }
797 }
798 }
799 final int[] cellXY = mTmpCellXY;
800 cellToPoint(x, y, cellXY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800801
Michael Jurkac28de512010-08-13 11:27:44 -0700802 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
803 + Math.pow(cellXY[1] - pixelY, 2));
804 if (distance <= bestDistance) {
805 bestDistance = distance;
806 bestXY[0] = x;
807 bestXY[1] = y;
808 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800809 }
810 }
811
Winson Chungaafa03c2010-06-11 17:34:16 -0700812 // Return null if no suitable location found
Jeff Sharkey70864282009-04-07 21:08:40 -0700813 if (bestDistance < Double.MAX_VALUE) {
814 return bestXY;
815 } else {
816 return null;
817 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800818 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700819
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800820 /**
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700821 * Called when a drag and drop operation has finished (successfully or not).
822 */
823 void onDragComplete() {
824 // Invalidate the drag data
825 mDragCell[0] = -1;
826 mDragCell[1] = -1;
827
Michael Jurkaa63c4522010-08-19 13:52:27 -0700828 setHover(false);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700829 mDragRect.setEmpty();
830 invalidate();
831 }
832
833 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700834 * Mark a child as having been dropped.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800835 *
836 * @param child The child that is being dropped
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800837 */
Winson Chungaafa03c2010-06-11 17:34:16 -0700838 void onDropChild(View child) {
Romain Guyd94533d2009-08-17 10:01:15 -0700839 if (child != null) {
840 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Romain Guyd94533d2009-08-17 10:01:15 -0700841 lp.isDragging = false;
Romain Guy84f296c2009-11-04 15:00:44 -0800842 lp.dropped = true;
Romain Guyd94533d2009-08-17 10:01:15 -0700843 mDragRect.setEmpty();
844 child.requestLayout();
Romain Guyd94533d2009-08-17 10:01:15 -0700845 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700846 onDragComplete();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800847 }
848
849 void onDropAborted(View child) {
850 if (child != null) {
851 ((LayoutParams) child.getLayoutParams()).isDragging = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800852 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700853 onDragComplete();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800854 }
855
856 /**
857 * Start dragging the specified child
Winson Chungaafa03c2010-06-11 17:34:16 -0700858 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800859 * @param child The child that is being dragged
860 */
861 void onDragChild(View child) {
862 LayoutParams lp = (LayoutParams) child.getLayoutParams();
863 lp.isDragging = true;
864 mDragRect.setEmpty();
865 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700866
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800867 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800868 * Computes a bounding rectangle for a range of cells
Winson Chungaafa03c2010-06-11 17:34:16 -0700869 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800870 * @param cellX X coordinate of upper left corner expressed as a cell position
871 * @param cellY Y coordinate of upper left corner expressed as a cell position
Winson Chungaafa03c2010-06-11 17:34:16 -0700872 * @param cellHSpan Width in cells
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800873 * @param cellVSpan Height in cells
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700874 * @param resultRect Rect into which to put the results
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800875 */
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700876 public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800877 final int cellWidth = mCellWidth;
878 final int cellHeight = mCellHeight;
879 final int widthGap = mWidthGap;
880 final int heightGap = mHeightGap;
Winson Chungaafa03c2010-06-11 17:34:16 -0700881
882 final int hStartPadding = getLeftPadding();
883 final int vStartPadding = getTopPadding();
884
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800885 int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
886 int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
887
888 int x = hStartPadding + cellX * (cellWidth + widthGap);
889 int y = vStartPadding + cellY * (cellHeight + heightGap);
Winson Chungaafa03c2010-06-11 17:34:16 -0700890
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700891 resultRect.set(x, y, x + width, y + height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800892 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700893
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800894 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700895 * Computes the required horizontal and vertical cell spans to always
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800896 * fit the given rectangle.
Winson Chungaafa03c2010-06-11 17:34:16 -0700897 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800898 * @param width Width in pixels
899 * @param height Height in pixels
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700900 * @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 -0800901 */
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700902 public int[] rectToCell(int width, int height, int[] result) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800903 // Always assume we're working with the smallest span to make sure we
904 // reserve enough space in both orientations.
Joe Onorato79e56262009-09-21 15:23:04 -0400905 final Resources resources = getResources();
906 int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
907 int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800908 int smallerSize = Math.min(actualWidth, actualHeight);
Joe Onorato79e56262009-09-21 15:23:04 -0400909
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800910 // Always round up to next largest cell
911 int spanX = (width + smallerSize) / smallerSize;
912 int spanY = (height + smallerSize) / smallerSize;
Joe Onorato79e56262009-09-21 15:23:04 -0400913
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700914 if (result == null) {
915 return new int[] { spanX, spanY };
916 }
917 result[0] = spanX;
918 result[1] = spanY;
919 return result;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800920 }
921
922 /**
923 * Find the first vacant cell, if there is one.
924 *
925 * @param vacant Holds the x and y coordinate of the vacant cell
926 * @param spanX Horizontal cell span.
927 * @param spanY Vertical cell span.
Winson Chungaafa03c2010-06-11 17:34:16 -0700928 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800929 * @return True if a vacant cell was found
930 */
931 public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
Adam Cohend22015c2010-07-26 22:02:18 -0700932 final int xCount = mCountX;
933 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800934 final boolean[][] occupied = mOccupied;
935
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700936 findOccupiedCells(xCount, yCount, occupied, null, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800937
938 return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
939 }
940
941 static boolean findVacantCell(int[] vacant, int spanX, int spanY,
942 int xCount, int yCount, boolean[][] occupied) {
943
944 for (int x = 0; x < xCount; x++) {
945 for (int y = 0; y < yCount; y++) {
946 boolean available = !occupied[x][y];
947out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
948 for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
949 available = available && !occupied[i][j];
950 if (!available) break out;
951 }
952 }
953
954 if (available) {
955 vacant[0] = x;
956 vacant[1] = y;
957 return true;
958 }
959 }
960 }
961
962 return false;
963 }
964
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700965 /**
966 * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
967 */
968 boolean[] getOccupiedCellsFlattened() {
Adam Cohend22015c2010-07-26 22:02:18 -0700969 final int xCount = mCountX;
970 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800971 final boolean[][] occupied = mOccupied;
972
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700973 findOccupiedCells(xCount, yCount, occupied, null, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800974
975 final boolean[] flat = new boolean[xCount * yCount];
976 for (int y = 0; y < yCount; y++) {
977 for (int x = 0; x < xCount; x++) {
978 flat[y * xCount + x] = occupied[x][y];
979 }
980 }
981
982 return flat;
983 }
984
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700985 /**
986 * Update the array of occupied cells.
987 * @param ignoreView If non-null, the space occupied by this View is treated as vacant
988 * @param ignoreFolders If true, a cell occupied by a Folder is treated as vacant
989 */
990 private void findOccupiedCells(
991 int xCount, int yCount, boolean[][] occupied, View ignoreView, boolean ignoreFolders) {
992
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800993 for (int x = 0; x < xCount; x++) {
994 for (int y = 0; y < yCount; y++) {
995 occupied[x][y] = false;
996 }
997 }
998
999 int count = getChildCount();
1000 for (int i = 0; i < count; i++) {
1001 View child = getChildAt(i);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001002 if ((ignoreFolders && child instanceof Folder) || child.equals(ignoreView)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001003 continue;
1004 }
1005 LayoutParams lp = (LayoutParams) child.getLayoutParams();
1006
1007 for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
1008 for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
1009 occupied[x][y] = true;
1010 }
1011 }
1012 }
1013 }
1014
1015 @Override
1016 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
1017 return new CellLayout.LayoutParams(getContext(), attrs);
1018 }
1019
1020 @Override
1021 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1022 return p instanceof CellLayout.LayoutParams;
1023 }
1024
1025 @Override
1026 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1027 return new CellLayout.LayoutParams(p);
1028 }
1029
Winson Chungaafa03c2010-06-11 17:34:16 -07001030 public static class CellLayoutAnimationController extends LayoutAnimationController {
1031 public CellLayoutAnimationController(Animation animation, float delay) {
1032 super(animation, delay);
1033 }
1034
1035 @Override
1036 protected long getDelayForView(View view) {
1037 return (int) (Math.random() * 150);
1038 }
1039 }
1040
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001041 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1042 /**
1043 * Horizontal location of the item in the grid.
1044 */
1045 @ViewDebug.ExportedProperty
1046 public int cellX;
1047
1048 /**
1049 * Vertical location of the item in the grid.
1050 */
1051 @ViewDebug.ExportedProperty
1052 public int cellY;
1053
1054 /**
1055 * Number of cells spanned horizontally by the item.
1056 */
1057 @ViewDebug.ExportedProperty
1058 public int cellHSpan;
1059
1060 /**
1061 * Number of cells spanned vertically by the item.
1062 */
1063 @ViewDebug.ExportedProperty
1064 public int cellVSpan;
Winson Chungaafa03c2010-06-11 17:34:16 -07001065
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001066 /**
1067 * Is this item currently being dragged
1068 */
1069 public boolean isDragging;
1070
1071 // X coordinate of the view in the layout.
1072 @ViewDebug.ExportedProperty
1073 int x;
1074 // Y coordinate of the view in the layout.
1075 @ViewDebug.ExportedProperty
1076 int y;
1077
Romain Guy84f296c2009-11-04 15:00:44 -08001078 boolean dropped;
Romain Guyfcb9e712009-10-02 16:06:52 -07001079
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001080 public LayoutParams(Context c, AttributeSet attrs) {
1081 super(c, attrs);
1082 cellHSpan = 1;
1083 cellVSpan = 1;
1084 }
1085
1086 public LayoutParams(ViewGroup.LayoutParams source) {
1087 super(source);
1088 cellHSpan = 1;
1089 cellVSpan = 1;
1090 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001091
1092 public LayoutParams(LayoutParams source) {
1093 super(source);
1094 this.cellX = source.cellX;
1095 this.cellY = source.cellY;
1096 this.cellHSpan = source.cellHSpan;
1097 this.cellVSpan = source.cellVSpan;
1098 }
1099
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001100 public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
Romain Guy8f19cdd2010-01-08 15:07:00 -08001101 super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001102 this.cellX = cellX;
1103 this.cellY = cellY;
1104 this.cellHSpan = cellHSpan;
1105 this.cellVSpan = cellVSpan;
1106 }
1107
1108 public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
1109 int hStartPadding, int vStartPadding) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001110
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001111 final int myCellHSpan = cellHSpan;
1112 final int myCellVSpan = cellVSpan;
1113 final int myCellX = cellX;
1114 final int myCellY = cellY;
Winson Chungaafa03c2010-06-11 17:34:16 -07001115
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001116 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
1117 leftMargin - rightMargin;
1118 height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
1119 topMargin - bottomMargin;
1120
1121 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
1122 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
1123 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001124
1125 public String toString() {
1126 return "(" + this.cellX + ", " + this.cellY + ")";
1127 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001128 }
1129
1130 static final class CellInfo implements ContextMenu.ContextMenuInfo {
Michael Jurkac28de512010-08-13 11:27:44 -07001131 private boolean[][] mOccupied;
1132 private int mCountX;
1133 private int mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001134 View cell;
Michael Jurkaa63c4522010-08-19 13:52:27 -07001135 int cellX = -1;
1136 int cellY = -1;
Michael Jurkac28de512010-08-13 11:27:44 -07001137 // intersectX and intersectY constrain the results of findCellForSpan; any empty space
1138 // it results must include this point (unless intersectX and intersectY are -1)
Michael Jurkaa63c4522010-08-19 13:52:27 -07001139 int intersectX = -1;
1140 int intersectY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001141 int spanX;
1142 int spanY;
1143 int screen;
1144 boolean valid;
1145
Michael Jurkac28de512010-08-13 11:27:44 -07001146 void updateOccupiedCells(boolean[][] occupied, int xCount, int yCount) {
1147 mOccupied = occupied.clone();
1148 mCountX = xCount;
1149 mCountY = yCount;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001150 }
1151
Michael Jurkac28de512010-08-13 11:27:44 -07001152 void updateOccupiedCells(boolean[] occupied, int xCount, int yCount) {
1153 if (mOccupied == null || mCountX != xCount || mCountY != yCount) {
1154 mOccupied = new boolean[xCount][yCount];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001155 }
Michael Jurkac28de512010-08-13 11:27:44 -07001156 mCountX = xCount;
1157 mCountY = yCount;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001158 for (int y = 0; y < yCount; y++) {
1159 for (int x = 0; x < xCount; x++) {
Michael Jurkac28de512010-08-13 11:27:44 -07001160 mOccupied[x][y] = occupied[y * xCount + x];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001161 }
1162 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001163 }
1164
Michael Jurkac28de512010-08-13 11:27:44 -07001165 boolean existsEmptyCell() {
1166 return findCellForSpan(null, 1, 1);
1167 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001168 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001169 * Finds the upper-left coordinate of the first rectangle in the grid that can
Michael Jurkac28de512010-08-13 11:27:44 -07001170 * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
1171 * then this method will only return coordinates for rectangles that contain the cell
1172 * (intersectX, intersectY)
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001173 *
1174 * @param cellXY The array that will contain the position of a vacant cell if such a cell
1175 * can be found.
1176 * @param spanX The horizontal span of the cell we want to find.
1177 * @param spanY The vertical span of the cell we want to find.
1178 *
1179 * @return True if a vacant cell of the specified dimension was found, false otherwise.
1180 */
1181 boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
Michael Jurka0e260592010-06-30 17:07:39 -07001182 // return the span represented by the CellInfo only there is no view there
1183 // (this.cell == null) and there is enough space
Michael Jurkac28de512010-08-13 11:27:44 -07001184
Michael Jurka0e260592010-06-30 17:07:39 -07001185 if (this.cell == null && this.spanX >= spanX && this.spanY >= spanY) {
Michael Jurkac28de512010-08-13 11:27:44 -07001186 if (cellXY != null) {
1187 cellXY[0] = cellX;
1188 cellXY[1] = cellY;
1189 }
1190 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001191 }
1192
Michael Jurkac28de512010-08-13 11:27:44 -07001193 int startX = 0;
1194 if (intersectX >= 0) {
1195 startX = Math.max(startX, intersectX - (spanX - 1));
1196 }
1197 int endX = mCountX - (spanX - 1);
1198 if (intersectX >= 0) {
1199 endX = Math.min(endX, intersectX + (spanX - 1));
1200 }
1201 int startY = 0;
1202 if (intersectY >= 0) {
1203 startY = Math.max(startY, intersectY - (spanY - 1));
1204 }
1205 int endY = mCountY - (spanY - 1);
1206 if (intersectY >= 0) {
1207 endY = Math.min(endY, intersectY + (spanY - 1));
1208 }
1209
Michael Jurkaa63c4522010-08-19 13:52:27 -07001210 for (int x = startX; x < endX; x++) {
Michael Jurkac28de512010-08-13 11:27:44 -07001211 inner:
1212 for (int y = startY; y < endY; y++) {
1213 for (int i = 0; i < spanX; i++) {
1214 for (int j = 0; j < spanY; j++) {
1215 if (mOccupied[x + i][y + j]) {
1216 // small optimization: we can skip to below the row we just found
1217 // an occupied cell
1218 y += j;
1219 continue inner;
1220 }
1221 }
1222 }
1223 if (cellXY != null) {
1224 cellXY[0] = x;
1225 cellXY[1] = y;
1226 }
1227 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001228 }
1229 }
Michael Jurkac28de512010-08-13 11:27:44 -07001230 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001231 }
1232
1233 @Override
1234 public String toString() {
Winson Chungaafa03c2010-06-11 17:34:16 -07001235 return "Cell[view=" + (cell == null ? "null" : cell.getClass())
1236 + ", x=" + cellX + ", y=" + cellY + "]";
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001237 }
1238 }
Mike Cleronf8bbd342009-10-23 16:15:16 -07001239
1240 public boolean lastDownOnOccupiedCell() {
1241 return mLastDownOnOccupiedCell;
1242 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001243}