blob: 3f9945fd2d8e60243b2d2380b3f9bf5d09838aae [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
Patrick Dubroyde7658b2010-09-27 11:15:43 -070021import android.animation.ValueAnimator;
22import android.animation.ValueAnimator.AnimatorUpdateListener;
Winson Chungaafa03c2010-06-11 17:34:16 -070023import android.app.WallpaperManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080024import android.content.Context;
Joe Onorato79e56262009-09-21 15:23:04 -040025import android.content.res.Resources;
Winson Chungaafa03c2010-06-11 17:34:16 -070026import android.content.res.TypedArray;
27import android.graphics.Canvas;
Patrick Dubroyde7658b2010-09-27 11:15:43 -070028import android.graphics.Point;
29import android.graphics.PointF;
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;
Winson Chungaafa03c2010-06-11 17:34:16 -070039import android.view.animation.Animation;
Winson Chung150fbab2010-09-29 17:14:26 -070040import android.view.animation.DecelerateInterpolator;
41import android.view.animation.Interpolator;
Winson Chungaafa03c2010-06-11 17:34:16 -070042import 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.Arrays;
Romain Guyedcce092010-03-04 13:03:17 -080045
Adam Cohenf34bab52010-09-30 14:11:56 -070046public class CellLayout extends ViewGroup implements Dimmable {
Winson Chungaafa03c2010-06-11 17:34:16 -070047 static final String TAG = "CellLayout";
48
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049 private int mCellWidth;
50 private int mCellHeight;
Winson Chungaafa03c2010-06-11 17:34:16 -070051
Winson Chungaafa03c2010-06-11 17:34:16 -070052 private int mLeftPadding;
53 private int mRightPadding;
54 private int mTopPadding;
55 private int mBottomPadding;
56
Adam Cohend22015c2010-07-26 22:02:18 -070057 private int mCountX;
58 private int mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059
60 private int mWidthGap;
61 private int mHeightGap;
62
63 private final Rect mRect = new Rect();
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -070064 private final RectF mRectF = new RectF();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065 private final CellInfo mCellInfo = new CellInfo();
Winson Chungaafa03c2010-06-11 17:34:16 -070066
Patrick Dubroyde7658b2010-09-27 11:15:43 -070067 // These are temporary variables to prevent having to allocate a new object just to
68 // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070069 private final int[] mTmpCellXY = new int[2];
Patrick Dubroyde7658b2010-09-27 11:15:43 -070070 private final int[] mTmpPoint = new int[2];
71 private final PointF mTmpPointF = new PointF();
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070072
The Android Open Source Project31dd5032009-03-03 19:32:27 -080073 boolean[][] mOccupied;
74
Michael Jurkadee05892010-07-27 10:01:56 -070075 private OnTouchListener mInterceptTouchListener;
76
Michael Jurka5f1c5092010-09-03 14:15:02 -070077 private float mBackgroundAlpha;
78 private final Rect mBackgroundLayoutRect = new Rect();
Adam Cohenf34bab52010-09-30 14:11:56 -070079
Michael Jurka5f1c5092010-09-03 14:15:02 -070080 private Drawable mBackground;
Adam Cohenf34bab52010-09-30 14:11:56 -070081 private Drawable mBackgroundMini;
82 private Drawable mBackgroundMiniHover;
Winson Chung150fbab2010-09-29 17:14:26 -070083 // If we're actively dragging something over this screen and it's small, mHover is true
Michael Jurkaa63c4522010-08-19 13:52:27 -070084 private boolean mHover = false;
Michael Jurkadee05892010-07-27 10:01:56 -070085
Patrick Dubroyde7658b2010-09-27 11:15:43 -070086 private final Point mDragCenter = new Point();
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070087
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070088 private Drawable mDragRectDrawable;
89
Winson Chung150fbab2010-09-29 17:14:26 -070090 // These arrays are used to implement the drag visualization on x-large screens.
91 // They are used as circular arrays, indexed by mDragRectCurrent.
92 private Rect[] mDragRects = new Rect[8];
93 private int[] mDragRectAlphas = new int[mDragRects.length];
94 private InterruptibleInOutAnimator[] mDragRectAnims =
95 new InterruptibleInOutAnimator[mDragRects.length];
96
97 // Used as an index into the above 3 arrays; indicates which is the most current value.
98 private int mDragRectCurrent = 0;
99
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700100 private Drawable mCrosshairsDrawable = null;
101 private ValueAnimator mCrosshairsAnimator = null;
102 private float mCrosshairsVisibility = 0.0f;
103
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700104 // When a drag operation is in progress, holds the nearest cell to the touch point
105 private final int[] mDragCell = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800106
Winson Chungaafa03c2010-06-11 17:34:16 -0700107 private final WallpaperManager mWallpaperManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108
109 public CellLayout(Context context) {
110 this(context, null);
111 }
112
113 public CellLayout(Context context, AttributeSet attrs) {
114 this(context, attrs, 0);
115 }
116
117 public CellLayout(Context context, AttributeSet attrs, int defStyle) {
118 super(context, attrs, defStyle);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700119
120 // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
121 // the user where a dragged item will land when dropped.
122 setWillNotDraw(false);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700123
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800124 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
125
126 mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
127 mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700128
Adam Cohend22015c2010-07-26 22:02:18 -0700129 mLeftPadding =
130 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisStartPadding, 10);
131 mRightPadding =
132 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisEndPadding, 10);
133 mTopPadding =
134 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisStartPadding, 10);
135 mBottomPadding =
136 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisEndPadding, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700137
Adam Cohend22015c2010-07-26 22:02:18 -0700138 mCountX = LauncherModel.getCellCountX();
139 mCountY = LauncherModel.getCellCountY();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700140 mOccupied = new boolean[mCountX][mCountY];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800141
142 a.recycle();
143
144 setAlwaysDrawnWithCacheEnabled(false);
145
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700146 mWallpaperManager = WallpaperManager.getInstance(context);
147
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700148 final Resources res = getResources();
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700149
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700150 if (LauncherApplication.isScreenXLarge()) {
Winson Chung150fbab2010-09-29 17:14:26 -0700151 mBackgroundMini = res.getDrawable(R.drawable.mini_home_screen_bg);
Adam Cohenf34bab52010-09-30 14:11:56 -0700152 mBackgroundMini.setFilterBitmap(true);
Winson Chung150fbab2010-09-29 17:14:26 -0700153 mBackground = res.getDrawable(R.drawable.home_screen_bg);
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700154 mBackground.setFilterBitmap(true);
Winson Chung150fbab2010-09-29 17:14:26 -0700155 mBackgroundMiniHover = res.getDrawable(R.drawable.mini_home_screen_bg_hover);
Adam Cohenf34bab52010-09-30 14:11:56 -0700156 mBackgroundMiniHover.setFilterBitmap(true);
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700157 }
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700158
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700159 // Initialize the data structures used for the drag visualization.
Winson Chung150fbab2010-09-29 17:14:26 -0700160
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700161 mDragRectDrawable = res.getDrawable(R.drawable.rounded_rect_green);
162 mCrosshairsDrawable = res.getDrawable(R.drawable.gardening_crosshairs);
163 Interpolator interp = new DecelerateInterpolator(2.5f); // Quint ease out
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700164
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700165 // Set up the animation for fading the crosshairs in and out
166 int animDuration = res.getInteger(R.integer.config_crosshairsFadeInTime);
167 mCrosshairsAnimator = new ValueAnimator<Float>(animDuration);
168 mCrosshairsAnimator.addUpdateListener(new AnimatorUpdateListener() {
169 public void onAnimationUpdate(ValueAnimator animation) {
170 mCrosshairsVisibility = ((Float) animation.getAnimatedValue()).floatValue();
171 CellLayout.this.invalidate();
172 }
173 });
174 mCrosshairsAnimator.setInterpolator(interp);
175
176 for (int i = 0; i < mDragRects.length; i++) {
177 mDragRects[i] = new Rect();
178 }
179
180 // When dragging things around the home screens, we show a green outline of
181 // where the item will land. The outlines gradually fade out, leaving a trail
182 // behind the drag path.
183 // Set up all the animations that are used to implement this fading.
184 final int duration = res.getInteger(R.integer.config_dragOutlineFadeTime);
185 final int fromAlphaValue = 0;
186 final int toAlphaValue = res.getInteger(R.integer.config_dragOutlineMaxAlpha);
187 for (int i = 0; i < mDragRectAnims.length; i++) {
188 final InterruptibleInOutAnimator anim =
189 new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue);
190 anim.setInterpolator(interp);
191 final int thisIndex = i;
192 anim.addUpdateListener(new AnimatorUpdateListener() {
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700193 public void onAnimationUpdate(ValueAnimator animation) {
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700194 mDragRectAlphas[thisIndex] = (Integer) animation.getAnimatedValue();
195 CellLayout.this.invalidate(mDragRects[thisIndex]);
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700196 }
197 });
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700198 mDragRectAnims[i] = anim;
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700199 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800200 }
201
Michael Jurkaa63c4522010-08-19 13:52:27 -0700202 public void setHover(boolean value) {
203 if (mHover != value) {
204 invalidate();
205 }
206 mHover = value;
207 }
208
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700209 private void animateCrosshairsTo(float value) {
210 final ValueAnimator anim = mCrosshairsAnimator;
211 long fullDuration = getResources().getInteger(R.integer.config_crosshairsFadeInTime);
212 anim.setDuration(fullDuration - anim.getCurrentPlayTime());
213 anim.setValues(mCrosshairsVisibility, value);
214 anim.cancel();
215 anim.start();
216 }
217
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700218 @Override
Romain Guya6abce82009-11-10 02:54:41 -0800219 public void dispatchDraw(Canvas canvas) {
Michael Jurka5f1c5092010-09-03 14:15:02 -0700220 if (mBackgroundAlpha > 0.0f) {
Adam Cohenf34bab52010-09-30 14:11:56 -0700221 Drawable bg;
222 if (mHover && getScaleX() < 0.5f) {
223 bg = mBackgroundMiniHover;
224 } else if (getScaleX() < 0.5f) {
225 bg = mBackgroundMini;
226 } else {
227 bg = mBackground;
228 }
Adam Cohen9c4949e2010-10-05 12:27:22 -0700229 if (bg != null) {
230 bg.setAlpha((int) (mBackgroundAlpha * 255));
231 bg.draw(canvas);
232 }
Michael Jurkaa63c4522010-08-19 13:52:27 -0700233 }
Romain Guya6abce82009-11-10 02:54:41 -0800234 super.dispatchDraw(canvas);
235 }
236
237 @Override
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700238 protected void onDraw(Canvas canvas) {
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700239 if (mCrosshairsVisibility > 0.0f) {
240 final int countX = mCountX;
241 final int countY = mCountY;
242
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700243 final float MAX_ALPHA = 0.4f;
244 final int MAX_VISIBLE_DISTANCE = 600;
245 final float DISTANCE_MULTIPLIER = 0.002f;
246
247 final Drawable d = mCrosshairsDrawable;
248 final int width = d.getIntrinsicWidth();
249 final int height = d.getIntrinsicHeight();
250
251 int x = getLeftPadding() - (mWidthGap / 2) - (width / 2);
252 for (int col = 0; col <= countX; col++) {
253 int y = getTopPadding() - (mHeightGap / 2) - (height / 2);
254 for (int row = 0; row <= countY; row++) {
255 mTmpPointF.set(x - mDragCenter.x, y - mDragCenter.y);
256 float dist = mTmpPointF.length();
257 // Crosshairs further from the drag point are more faint
258 float alpha = Math.min(MAX_ALPHA,
259 DISTANCE_MULTIPLIER * (MAX_VISIBLE_DISTANCE - dist));
260 if (alpha > 0.0f) {
261 d.setBounds(x, y, x + width, y + height);
262 d.setAlpha((int) (alpha * 255 * mCrosshairsVisibility));
263 d.draw(canvas);
264 }
265 y += mCellHeight + mHeightGap;
266 }
267 x += mCellWidth + mWidthGap;
268 }
Winson Chung150fbab2010-09-29 17:14:26 -0700269
270 for (int i = 0; i < mDragRects.length; i++) {
271 int alpha = mDragRectAlphas[i];
272 if (alpha > 0) {
273 mDragRectDrawable.setAlpha(alpha);
274 mDragRectDrawable.setBounds(mDragRects[i]);
275 mDragRectDrawable.draw(canvas);
276 }
277 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700278 }
279 }
280
Adam Cohenf34bab52010-09-30 14:11:56 -0700281 public void setDimmableProgress(float progress) {
282 for (int i = 0; i < getChildCount(); i++) {
283 Dimmable d = (Dimmable) getChildAt(i);
284 d.setDimmableProgress(progress);
285 }
286 }
287
288 public float getDimmableProgress() {
289 if (getChildCount() > 0) {
290 return ((Dimmable) getChildAt(0)).getDimmableProgress();
291 }
292 return 0.0f;
293 }
294
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700295 @Override
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700296 public void cancelLongPress() {
297 super.cancelLongPress();
298
299 // Cancel long press for all children
300 final int count = getChildCount();
301 for (int i = 0; i < count; i++) {
302 final View child = getChildAt(i);
303 child.cancelLongPress();
304 }
305 }
306
Michael Jurkadee05892010-07-27 10:01:56 -0700307 public void setOnInterceptTouchListener(View.OnTouchListener listener) {
308 mInterceptTouchListener = listener;
309 }
310
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800311 int getCountX() {
Adam Cohend22015c2010-07-26 22:02:18 -0700312 return mCountX;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800313 }
314
315 int getCountY() {
Adam Cohend22015c2010-07-26 22:02:18 -0700316 return mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800317 }
318
Winson Chungaafa03c2010-06-11 17:34:16 -0700319 public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
320 final LayoutParams lp = params;
321
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800322 // Generate an id for each view, this assumes we have at most 256x256 cells
323 // per workspace screen
Adam Cohend22015c2010-07-26 22:02:18 -0700324 if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700325 // If the horizontal or vertical span is set to -1, it is taken to
326 // mean that it spans the extent of the CellLayout
Adam Cohend22015c2010-07-26 22:02:18 -0700327 if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
328 if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800329
Winson Chungaafa03c2010-06-11 17:34:16 -0700330 child.setId(childId);
331
Michael Jurkadee05892010-07-27 10:01:56 -0700332 // We might be in the middle or end of shrinking/fading to a dimmed view
333 // Make sure this view's alpha is set the same as all the rest of the views
Michael Jurka5f1c5092010-09-03 14:15:02 -0700334 child.setAlpha(getAlpha());
Winson Chungaafa03c2010-06-11 17:34:16 -0700335 addView(child, index, lp);
Michael Jurkadee05892010-07-27 10:01:56 -0700336
Michael Jurka0280c3b2010-09-17 15:00:07 -0700337 markCellsAsOccupiedForView(child);
338
Winson Chungaafa03c2010-06-11 17:34:16 -0700339 return true;
340 }
341 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800342 }
343
344 @Override
Michael Jurka0280c3b2010-09-17 15:00:07 -0700345 public void removeAllViews() {
346 clearOccupiedCells();
347 }
348
349 @Override
350 public void removeAllViewsInLayout() {
351 clearOccupiedCells();
352 }
353
354 @Override
355 public void removeView(View view) {
356 markCellsAsUnoccupiedForView(view);
357 super.removeView(view);
358 }
359
360 @Override
361 public void removeViewAt(int index) {
362 markCellsAsUnoccupiedForView(getChildAt(index));
363 super.removeViewAt(index);
364 }
365
366 @Override
367 public void removeViewInLayout(View view) {
368 markCellsAsUnoccupiedForView(view);
369 super.removeViewInLayout(view);
370 }
371
372 @Override
373 public void removeViews(int start, int count) {
374 for (int i = start; i < start + count; i++) {
375 markCellsAsUnoccupiedForView(getChildAt(i));
376 }
377 super.removeViews(start, count);
378 }
379
380 @Override
381 public void removeViewsInLayout(int start, int count) {
382 for (int i = start; i < start + count; i++) {
383 markCellsAsUnoccupiedForView(getChildAt(i));
384 }
385 super.removeViewsInLayout(start, count);
386 }
387
388 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800389 public void requestChildFocus(View child, View focused) {
390 super.requestChildFocus(child, focused);
391 if (child != null) {
392 Rect r = new Rect();
393 child.getDrawingRect(r);
394 requestRectangleOnScreen(r);
395 }
396 }
397
398 @Override
399 protected void onAttachedToWindow() {
400 super.onAttachedToWindow();
401 mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
402 }
403
Michael Jurkaaf442092010-06-10 17:01:57 -0700404 public void setTagToCellInfoForPoint(int touchX, int touchY) {
405 final CellInfo cellInfo = mCellInfo;
406 final Rect frame = mRect;
407 final int x = touchX + mScrollX;
408 final int y = touchY + mScrollY;
409 final int count = getChildCount();
410
411 boolean found = false;
412 for (int i = count - 1; i >= 0; i--) {
413 final View child = getChildAt(i);
414
415 if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
416 child.getHitRect(frame);
417 if (frame.contains(x, y)) {
418 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
419 cellInfo.cell = child;
420 cellInfo.cellX = lp.cellX;
421 cellInfo.cellY = lp.cellY;
422 cellInfo.spanX = lp.cellHSpan;
423 cellInfo.spanY = lp.cellVSpan;
424 cellInfo.valid = true;
425 found = true;
Michael Jurkaaf442092010-06-10 17:01:57 -0700426 break;
427 }
428 }
429 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700430
Michael Jurkaaf442092010-06-10 17:01:57 -0700431 if (!found) {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700432 final int cellXY[] = mTmpCellXY;
Michael Jurkaaf442092010-06-10 17:01:57 -0700433 pointToCellExact(x, y, cellXY);
434
Michael Jurkaaf442092010-06-10 17:01:57 -0700435 cellInfo.cell = null;
436 cellInfo.cellX = cellXY[0];
437 cellInfo.cellY = cellXY[1];
438 cellInfo.spanX = 1;
439 cellInfo.spanY = 1;
Michael Jurka0280c3b2010-09-17 15:00:07 -0700440 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < mCountX &&
441 cellXY[1] < mCountY && !mOccupied[cellXY[0]][cellXY[1]];
Michael Jurkaaf442092010-06-10 17:01:57 -0700442 }
443 setTag(cellInfo);
444 }
445
Winson Chungaafa03c2010-06-11 17:34:16 -0700446
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800447 @Override
448 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkadee05892010-07-27 10:01:56 -0700449 if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
450 return true;
451 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800452 final int action = ev.getAction();
453 final CellInfo cellInfo = mCellInfo;
454
455 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkaaf442092010-06-10 17:01:57 -0700456 setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800457 } else if (action == MotionEvent.ACTION_UP) {
458 cellInfo.cell = null;
459 cellInfo.cellX = -1;
460 cellInfo.cellY = -1;
461 cellInfo.spanX = 0;
462 cellInfo.spanY = 0;
463 cellInfo.valid = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800464 setTag(cellInfo);
465 }
466
467 return false;
468 }
469
470 @Override
471 public CellInfo getTag() {
Michael Jurka0280c3b2010-09-17 15:00:07 -0700472 return (CellInfo) super.getTag();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800473 }
474
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700475 /**
476 * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
477 */
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800478 private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
479 for (int x = left; x <= right; x++) {
480 if (occupied[x][y]) {
481 return false;
482 }
483 }
484 return true;
485 }
486
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800487 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700488 * Given a point, return the cell that strictly encloses that point
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800489 * @param x X coordinate of the point
490 * @param y Y coordinate of the point
491 * @param result Array of 2 ints to hold the x and y coordinate of the cell
492 */
493 void pointToCellExact(int x, int y, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700494 final int hStartPadding = getLeftPadding();
495 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800496
497 result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
498 result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
499
Adam Cohend22015c2010-07-26 22:02:18 -0700500 final int xAxis = mCountX;
501 final int yAxis = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800502
503 if (result[0] < 0) result[0] = 0;
504 if (result[0] >= xAxis) result[0] = xAxis - 1;
505 if (result[1] < 0) result[1] = 0;
506 if (result[1] >= yAxis) result[1] = yAxis - 1;
507 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700508
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800509 /**
510 * Given a point, return the cell that most closely encloses that point
511 * @param x X coordinate of the point
512 * @param y Y coordinate of the point
513 * @param result Array of 2 ints to hold the x and y coordinate of the cell
514 */
515 void pointToCellRounded(int x, int y, int[] result) {
516 pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
517 }
518
519 /**
520 * Given a cell coordinate, return the point that represents the upper left corner of that cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700521 *
522 * @param cellX X coordinate of the cell
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800523 * @param cellY Y coordinate of the cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700524 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800525 * @param result Array of 2 ints to hold the x and y coordinate of the point
526 */
527 void cellToPoint(int cellX, int cellY, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700528 final int hStartPadding = getLeftPadding();
529 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800530
531 result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
532 result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
533 }
534
Romain Guy84f296c2009-11-04 15:00:44 -0800535 int getCellWidth() {
536 return mCellWidth;
537 }
538
539 int getCellHeight() {
540 return mCellHeight;
541 }
542
Romain Guy1a304a12009-11-10 00:02:32 -0800543 int getLeftPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700544 return mLeftPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800545 }
546
547 int getTopPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700548 return mTopPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800549 }
550
551 int getRightPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700552 return mRightPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800553 }
554
555 int getBottomPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700556 return mBottomPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800557 }
558
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800559 @Override
560 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
561 // TODO: currently ignoring padding
Winson Chungaafa03c2010-06-11 17:34:16 -0700562
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800563 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700564 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
565
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800566 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
567 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700568
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800569 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
570 throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
571 }
572
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800573 final int cellWidth = mCellWidth;
574 final int cellHeight = mCellHeight;
575
Adam Cohend22015c2010-07-26 22:02:18 -0700576 int numWidthGaps = mCountX - 1;
577 int numHeightGaps = mCountY - 1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800578
Michael Jurka0280c3b2010-09-17 15:00:07 -0700579 int vSpaceLeft = heightSpecSize - mTopPadding - mBottomPadding - (cellHeight * mCountY);
Adam Cohend22015c2010-07-26 22:02:18 -0700580 mHeightGap = vSpaceLeft / numHeightGaps;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800581
Michael Jurka0280c3b2010-09-17 15:00:07 -0700582 int hSpaceLeft = widthSpecSize - mLeftPadding - mRightPadding - (cellWidth * mCountX);
Adam Cohend22015c2010-07-26 22:02:18 -0700583 mWidthGap = hSpaceLeft / numWidthGaps;
Winson Chungaafa03c2010-06-11 17:34:16 -0700584
Michael Jurka5f1c5092010-09-03 14:15:02 -0700585 // center it around the min gaps
586 int minGap = Math.min(mWidthGap, mHeightGap);
587 mWidthGap = mHeightGap = minGap;
588
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800589 int count = getChildCount();
590
591 for (int i = 0; i < count; i++) {
592 View child = getChildAt(i);
593 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Winson Chungaafa03c2010-06-11 17:34:16 -0700594 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
595 mLeftPadding, mTopPadding);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800596
Michael Jurka0280c3b2010-09-17 15:00:07 -0700597 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
Winson Chungaafa03c2010-06-11 17:34:16 -0700598 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
599 MeasureSpec.EXACTLY);
600
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800601 child.measure(childWidthMeasureSpec, childheightMeasureSpec);
602 }
Michael Jurka5f1c5092010-09-03 14:15:02 -0700603 if (widthSpecMode == MeasureSpec.AT_MOST) {
604 int newWidth = mLeftPadding + mRightPadding + (mCountX * cellWidth) +
605 ((mCountX - 1) * minGap);
606 int newHeight = mTopPadding + mBottomPadding + (mCountY * cellHeight) +
607 ((mCountY - 1) * minGap);
608 setMeasuredDimension(newWidth, newHeight);
609 } else if (widthSpecMode == MeasureSpec.EXACTLY) {
610 setMeasuredDimension(widthSpecSize, heightSpecSize);
611 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800612 }
613
614 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -0700615 protected void onLayout(boolean changed, int l, int t, int r, int b) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800616 int count = getChildCount();
617
618 for (int i = 0; i < count; i++) {
619 View child = getChildAt(i);
620 if (child.getVisibility() != GONE) {
621
622 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
623
624 int childLeft = lp.x;
625 int childTop = lp.y;
626 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
Romain Guy84f296c2009-11-04 15:00:44 -0800627
628 if (lp.dropped) {
629 lp.dropped = false;
630
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700631 final int[] cellXY = mTmpCellXY;
Romain Guy06762ab2010-01-25 16:51:08 -0800632 getLocationOnScreen(cellXY);
Romain Guy84f296c2009-11-04 15:00:44 -0800633 mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
Romain Guy06762ab2010-01-25 16:51:08 -0800634 cellXY[0] + childLeft + lp.width / 2,
635 cellXY[1] + childTop + lp.height / 2, 0, null);
Romain Guy84f296c2009-11-04 15:00:44 -0800636 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800637 }
638 }
639 }
640
641 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700642 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
643 super.onSizeChanged(w, h, oldw, oldh);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700644 mBackgroundLayoutRect.set(0, 0, w, h);
645 if (mBackground != null) {
646 mBackground.setBounds(mBackgroundLayoutRect);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700647 }
Adam Cohenf34bab52010-09-30 14:11:56 -0700648 if (mBackgroundMiniHover != null) {
649 mBackgroundMiniHover.setBounds(mBackgroundLayoutRect);
650 }
651 if (mBackgroundMini != null) {
652 mBackgroundMini.setBounds(mBackgroundLayoutRect);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700653 }
Michael Jurkadee05892010-07-27 10:01:56 -0700654 }
655
656 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800657 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
658 final int count = getChildCount();
659 for (int i = 0; i < count; i++) {
660 final View view = getChildAt(i);
661 view.setDrawingCacheEnabled(enabled);
662 // Update the drawing caches
Adam Powellfefa0ce2010-05-03 10:23:50 -0700663 view.buildDrawingCache(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800664 }
665 }
666
667 @Override
668 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
669 super.setChildrenDrawnWithCacheEnabled(enabled);
670 }
671
Michael Jurka5f1c5092010-09-03 14:15:02 -0700672 public float getBackgroundAlpha() {
673 return mBackgroundAlpha;
Michael Jurkadee05892010-07-27 10:01:56 -0700674 }
675
Michael Jurka5f1c5092010-09-03 14:15:02 -0700676 public void setBackgroundAlpha(float alpha) {
677 mBackgroundAlpha = alpha;
Michael Jurka0142d492010-08-25 17:46:15 -0700678 invalidate();
Michael Jurkadee05892010-07-27 10:01:56 -0700679 }
680
Michael Jurka5f1c5092010-09-03 14:15:02 -0700681 // Need to return true to let the view system know we know how to handle alpha-- this is
682 // because when our children have an alpha of 0.0f, they are still rendering their "dimmed"
683 // versions
684 @Override
685 protected boolean onSetAlpha(int alpha) {
686 return true;
687 }
688
689 public void setAlpha(float alpha) {
690 setChildrenAlpha(alpha);
691 super.setAlpha(alpha);
692 }
693
Michael Jurkadee05892010-07-27 10:01:56 -0700694 private void setChildrenAlpha(float alpha) {
Michael Jurka0142d492010-08-25 17:46:15 -0700695 final int childCount = getChildCount();
696 for (int i = 0; i < childCount; i++) {
Michael Jurkadee05892010-07-27 10:01:56 -0700697 getChildAt(i).setAlpha(alpha);
698 }
699 }
700
Michael Jurka0280c3b2010-09-17 15:00:07 -0700701 private boolean isVacantIgnoring(
702 int originX, int originY, int spanX, int spanY, View ignoreView) {
703 if (ignoreView != null) {
704 markCellsAsUnoccupiedForView(ignoreView);
705 }
Michael Jurka28750fb2010-09-24 17:43:49 -0700706 boolean isVacant = true;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700707 for (int i = 0; i < spanY; i++) {
708 if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
Michael Jurka28750fb2010-09-24 17:43:49 -0700709 isVacant = false;
710 break;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700711 }
712 }
Michael Jurka0280c3b2010-09-17 15:00:07 -0700713 if (ignoreView != null) {
714 markCellsAsOccupiedForView(ignoreView);
715 }
Michael Jurka28750fb2010-09-24 17:43:49 -0700716 return isVacant;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700717 }
718
Michael Jurka0280c3b2010-09-17 15:00:07 -0700719 private boolean isVacant(int originX, int originY, int spanX, int spanY) {
720 return isVacantIgnoring(originX, originY, spanX, spanY, null);
721 }
722
Patrick Dubroy440c3602010-07-13 17:50:32 -0700723 public View getChildAt(int x, int y) {
724 final int count = getChildCount();
725 for (int i = 0; i < count; i++) {
726 View child = getChildAt(i);
727 LayoutParams lp = (LayoutParams) child.getLayoutParams();
728
729 if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
730 (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
731 return child;
732 }
733 }
734 return null;
735 }
736
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700737 /**
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700738 * Estimate the size that a child with the given dimensions will take in the layout.
739 */
740 void estimateChildSize(int minWidth, int minHeight, int[] result) {
741 // Assuming it's placed at 0, 0, find where the bottom right cell will land
742 rectToCell(minWidth, minHeight, result);
743
744 // Then figure out the rect it will occupy
745 cellToRect(0, 0, result[0], result[1], mRectF);
746 result[0] = (int)mRectF.width();
747 result[1] = (int)mRectF.height();
748 }
749
750 /**
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700751 * Estimate where the top left cell of the dragged item will land if it is dropped.
752 *
753 * @param originX The X value of the top left corner of the item
754 * @param originY The Y value of the top left corner of the item
755 * @param spanX The number of horizontal cells that the item spans
756 * @param spanY The number of vertical cells that the item spans
757 * @param result The estimated drop cell X and Y.
758 */
759 void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
Adam Cohend22015c2010-07-26 22:02:18 -0700760 final int countX = mCountX;
761 final int countY = mCountY;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700762
Michael Jurkaa63c4522010-08-19 13:52:27 -0700763 // pointToCellRounded takes the top left of a cell but will pad that with
764 // cellWidth/2 and cellHeight/2 when finding the matching cell
765 pointToCellRounded(originX, originY, result);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700766
767 // If the item isn't fully on this screen, snap to the edges
768 int rightOverhang = result[0] + spanX - countX;
769 if (rightOverhang > 0) {
770 result[0] -= rightOverhang; // Snap to right
771 }
772 result[0] = Math.max(0, result[0]); // Snap to left
773 int bottomOverhang = result[1] + spanY - countY;
774 if (bottomOverhang > 0) {
775 result[1] -= bottomOverhang; // Snap to bottom
776 }
777 result[1] = Math.max(0, result[1]); // Snap to top
778 }
779
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700780 void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) {
781 final int[] nearest = findNearestVacantArea(originX, originY, spanX, spanY, view, mDragCell);
782 mDragCenter.set(originX + (view.getWidth() / 2), originY + (view.getHeight() / 2));
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700783
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700784 if (nearest != null) {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700785 // Find the top left corner of the rect the object will occupy
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700786 final int[] topLeft = mTmpPoint;
787 cellToPoint(nearest[0], nearest[1], topLeft);
788
789 // Need to copy these, because the next call to cellToPoint will overwrite them
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700790 final int left = topLeft[0];
791 final int top = topLeft[1];
792
Winson Chung150fbab2010-09-29 17:14:26 -0700793 final Rect dragRect = mDragRects[mDragRectCurrent];
794
795 if (dragRect.isEmpty() || left != dragRect.left || top != dragRect.top) {
796 // Now find the bottom right
797 final int[] bottomRight = mTmpPoint;
798 cellToPoint(nearest[0] + spanX - 1, nearest[1] + spanY - 1, bottomRight);
799 bottomRight[0] += mCellWidth;
800 bottomRight[1] += mCellHeight;
801
802 final int oldIndex = mDragRectCurrent;
803 mDragRectCurrent = (oldIndex + 1) % mDragRects.length;
804
805 mDragRects[mDragRectCurrent].set(left, top, bottomRight[0], bottomRight[1]);
806
807 mDragRectAnims[oldIndex].animateOut();
808 mDragRectAnims[mDragRectCurrent].animateIn();
809 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700810 }
811 }
812
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800813 /**
Jeff Sharkey70864282009-04-07 21:08:40 -0700814 * Find a vacant area that will fit the given bounds nearest the requested
815 * cell location. Uses Euclidean distance to score multiple vacant areas.
Winson Chungaafa03c2010-06-11 17:34:16 -0700816 *
Romain Guy51afc022009-05-04 18:03:43 -0700817 * @param pixelX The X location at which you want to search for a vacant area.
818 * @param pixelY The Y location at which you want to search for a vacant area.
Jeff Sharkey70864282009-04-07 21:08:40 -0700819 * @param spanX Horizontal span of the object.
820 * @param spanY Vertical span of the object.
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700821 * @param result Array in which to place the result, or null (in which case a new array will
822 * be allocated)
Jeff Sharkey70864282009-04-07 21:08:40 -0700823 * @return The X, Y cell of a vacant area that can contain this object,
824 * nearest the requested location.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800825 */
Michael Jurka6a1435d2010-09-27 17:35:12 -0700826 int[] findNearestVacantArea(
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700827 int pixelX, int pixelY, int spanX, int spanY, int[] result) {
828 return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, result);
Michael Jurka6a1435d2010-09-27 17:35:12 -0700829 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700830
Michael Jurka6a1435d2010-09-27 17:35:12 -0700831 /**
832 * Find a vacant area that will fit the given bounds nearest the requested
833 * cell location. Uses Euclidean distance to score multiple vacant areas.
834 *
835 * @param pixelX The X location at which you want to search for a vacant area.
836 * @param pixelY The Y location at which you want to search for a vacant area.
837 * @param spanX Horizontal span of the object.
838 * @param spanY Vertical span of the object.
Michael Jurka6a1435d2010-09-27 17:35:12 -0700839 * @param ignoreView Considers space occupied by this view as unoccupied
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700840 * @param result Previously returned value to possibly recycle.
Michael Jurka6a1435d2010-09-27 17:35:12 -0700841 * @return The X, Y cell of a vacant area that can contain this object,
842 * nearest the requested location.
843 */
844 int[] findNearestVacantArea(
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700845 int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
Michael Jurkac6ee42e2010-09-30 12:04:50 -0700846 // mark space take by ignoreView as available (method checks if ignoreView is null)
847 markCellsAsUnoccupiedForView(ignoreView);
848
Jeff Sharkey70864282009-04-07 21:08:40 -0700849 // Keep track of best-scoring drop area
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700850 final int[] bestXY = result != null ? result : new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700851 double bestDistance = Double.MAX_VALUE;
Winson Chungaafa03c2010-06-11 17:34:16 -0700852
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700853 final int countX = mCountX;
854 final int countY = mCountY;
855 final boolean[][] occupied = mOccupied;
856
857 for (int x = 0; x < countX - (spanX - 1); x++) {
Michael Jurkac28de512010-08-13 11:27:44 -0700858 inner:
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700859 for (int y = 0; y < countY - (spanY - 1); y++) {
Michael Jurkac28de512010-08-13 11:27:44 -0700860 for (int i = 0; i < spanX; i++) {
861 for (int j = 0; j < spanY; j++) {
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700862 if (occupied[x + i][y + j]) {
Michael Jurkac28de512010-08-13 11:27:44 -0700863 // small optimization: we can skip to below the row we just found
864 // an occupied cell
865 y += j;
866 continue inner;
867 }
868 }
869 }
870 final int[] cellXY = mTmpCellXY;
871 cellToPoint(x, y, cellXY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800872
Michael Jurkac28de512010-08-13 11:27:44 -0700873 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
874 + Math.pow(cellXY[1] - pixelY, 2));
875 if (distance <= bestDistance) {
876 bestDistance = distance;
877 bestXY[0] = x;
878 bestXY[1] = y;
879 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800880 }
881 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -0700882 // re-mark space taken by ignoreView as occupied
883 markCellsAsOccupiedForView(ignoreView);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800884
Winson Chungaafa03c2010-06-11 17:34:16 -0700885 // Return null if no suitable location found
Jeff Sharkey70864282009-04-07 21:08:40 -0700886 if (bestDistance < Double.MAX_VALUE) {
887 return bestXY;
888 } else {
889 return null;
890 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800891 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700892
Michael Jurka0280c3b2010-09-17 15:00:07 -0700893 boolean existsEmptyCell() {
894 return findCellForSpan(null, 1, 1);
895 }
896
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800897 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -0700898 * Finds the upper-left coordinate of the first rectangle in the grid that can
899 * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
900 * then this method will only return coordinates for rectangles that contain the cell
901 * (intersectX, intersectY)
902 *
903 * @param cellXY The array that will contain the position of a vacant cell if such a cell
904 * can be found.
905 * @param spanX The horizontal span of the cell we want to find.
906 * @param spanY The vertical span of the cell we want to find.
907 *
908 * @return True if a vacant cell of the specified dimension was found, false otherwise.
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700909 */
Michael Jurka0280c3b2010-09-17 15:00:07 -0700910 boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
911 return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null);
912 }
913
914 /**
915 * Like above, but ignores any cells occupied by the item "ignoreView"
916 *
917 * @param cellXY The array that will contain the position of a vacant cell if such a cell
918 * can be found.
919 * @param spanX The horizontal span of the cell we want to find.
920 * @param spanY The vertical span of the cell we want to find.
921 * @param ignoreView The home screen item we should treat as not occupying any space
922 * @return
923 */
924 boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
925 return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, ignoreView);
926 }
927
928 /**
929 * Like above, but if intersectX and intersectY are not -1, then this method will try to
930 * return coordinates for rectangles that contain the cell [intersectX, intersectY]
931 *
932 * @param spanX The horizontal span of the cell we want to find.
933 * @param spanY The vertical span of the cell we want to find.
934 * @param ignoreView The home screen item we should treat as not occupying any space
935 * @param intersectX The X coordinate of the cell that we should try to overlap
936 * @param intersectX The Y coordinate of the cell that we should try to overlap
937 *
938 * @return True if a vacant cell of the specified dimension was found, false otherwise.
939 */
940 boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
941 int intersectX, int intersectY) {
942 return findCellForSpanThatIntersectsIgnoring(
943 cellXY, spanX, spanY, intersectX, intersectY, null);
944 }
945
946 /**
947 * The superset of the above two methods
948 */
949 boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
950 int intersectX, int intersectY, View ignoreView) {
Michael Jurkac6ee42e2010-09-30 12:04:50 -0700951 // mark space take by ignoreView as available (method checks if ignoreView is null)
952 markCellsAsUnoccupiedForView(ignoreView);
Michael Jurka0280c3b2010-09-17 15:00:07 -0700953
Michael Jurka28750fb2010-09-24 17:43:49 -0700954 boolean foundCell = false;
Michael Jurka0280c3b2010-09-17 15:00:07 -0700955 while (true) {
956 int startX = 0;
957 if (intersectX >= 0) {
958 startX = Math.max(startX, intersectX - (spanX - 1));
959 }
960 int endX = mCountX - (spanX - 1);
961 if (intersectX >= 0) {
962 endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0));
963 }
964 int startY = 0;
965 if (intersectY >= 0) {
966 startY = Math.max(startY, intersectY - (spanY - 1));
967 }
968 int endY = mCountY - (spanY - 1);
969 if (intersectY >= 0) {
970 endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0));
971 }
972
973 for (int x = startX; x < endX; x++) {
974 inner:
975 for (int y = startY; y < endY; y++) {
976 for (int i = 0; i < spanX; i++) {
977 for (int j = 0; j < spanY; j++) {
978 if (mOccupied[x + i][y + j]) {
979 // small optimization: we can skip to below the row we just found
980 // an occupied cell
981 y += j;
982 continue inner;
983 }
984 }
985 }
986 if (cellXY != null) {
987 cellXY[0] = x;
988 cellXY[1] = y;
989 }
Michael Jurka28750fb2010-09-24 17:43:49 -0700990 foundCell = true;
991 break;
Michael Jurka0280c3b2010-09-17 15:00:07 -0700992 }
993 }
994 if (intersectX == -1 && intersectY == -1) {
995 break;
996 } else {
997 // if we failed to find anything, try again but without any requirements of
998 // intersecting
999 intersectX = -1;
1000 intersectY = -1;
1001 continue;
1002 }
1003 }
1004
Michael Jurkac6ee42e2010-09-30 12:04:50 -07001005 // re-mark space taken by ignoreView as occupied
1006 markCellsAsOccupiedForView(ignoreView);
Michael Jurka28750fb2010-09-24 17:43:49 -07001007 return foundCell;
Michael Jurka0280c3b2010-09-17 15:00:07 -07001008 }
1009
1010 /**
1011 * Called when drag has left this CellLayout or has been completed (successfully or not)
1012 */
1013 void onDragExit() {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001014 // Invalidate the drag data
1015 mDragCell[0] = -1;
1016 mDragCell[1] = -1;
1017
Michael Jurkaa63c4522010-08-19 13:52:27 -07001018 setHover(false);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001019 invalidate();
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001020
1021 // Fade out the drag indicators
1022 if (mCrosshairsAnimator != null) {
1023 animateCrosshairsTo(0.0f);
1024 }
Winson Chung150fbab2010-09-29 17:14:26 -07001025
1026 mDragRectAnims[mDragRectCurrent].animateOut();
1027 mDragRectCurrent = (mDragRectCurrent + 1) % mDragRects.length;
1028 mDragRects[mDragRectCurrent].setEmpty();
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001029 }
1030
1031 /**
Winson Chungaafa03c2010-06-11 17:34:16 -07001032 * Mark a child as having been dropped.
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001033 * At the beginning of the drag operation, the child may have been on another
1034 * screen, but it is reparented before this method is called.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001035 *
1036 * @param child The child that is being dropped
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001037 */
Winson Chungaafa03c2010-06-11 17:34:16 -07001038 void onDropChild(View child) {
Romain Guyd94533d2009-08-17 10:01:15 -07001039 if (child != null) {
1040 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Romain Guyd94533d2009-08-17 10:01:15 -07001041 lp.isDragging = false;
Romain Guy84f296c2009-11-04 15:00:44 -08001042 lp.dropped = true;
Romain Guyd94533d2009-08-17 10:01:15 -07001043 child.requestLayout();
Romain Guyd94533d2009-08-17 10:01:15 -07001044 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07001045 onDragExit();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001046 }
1047
1048 void onDropAborted(View child) {
1049 if (child != null) {
1050 ((LayoutParams) child.getLayoutParams()).isDragging = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001051 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07001052 onDragExit();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001053 }
1054
1055 /**
1056 * Start dragging the specified child
Winson Chungaafa03c2010-06-11 17:34:16 -07001057 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001058 * @param child The child that is being dragged
1059 */
1060 void onDragChild(View child) {
1061 LayoutParams lp = (LayoutParams) child.getLayoutParams();
1062 lp.isDragging = true;
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001063 }
1064
1065 /**
1066 * A drag event has begun over this layout.
1067 * It may have begun over this layout (in which case onDragChild is called first),
1068 * or it may have begun on another layout.
1069 */
1070 void onDragEnter(View dragView) {
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001071 // Fade in the drag indicators
1072 if (mCrosshairsAnimator != null) {
1073 animateCrosshairsTo(1.0f);
1074 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001075 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001076
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001077 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001078 * Computes a bounding rectangle for a range of cells
Winson Chungaafa03c2010-06-11 17:34:16 -07001079 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001080 * @param cellX X coordinate of upper left corner expressed as a cell position
1081 * @param cellY Y coordinate of upper left corner expressed as a cell position
Winson Chungaafa03c2010-06-11 17:34:16 -07001082 * @param cellHSpan Width in cells
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001083 * @param cellVSpan Height in cells
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001084 * @param resultRect Rect into which to put the results
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001085 */
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001086 public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001087 final int cellWidth = mCellWidth;
1088 final int cellHeight = mCellHeight;
1089 final int widthGap = mWidthGap;
1090 final int heightGap = mHeightGap;
Winson Chungaafa03c2010-06-11 17:34:16 -07001091
1092 final int hStartPadding = getLeftPadding();
1093 final int vStartPadding = getTopPadding();
1094
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001095 int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
1096 int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
1097
1098 int x = hStartPadding + cellX * (cellWidth + widthGap);
1099 int y = vStartPadding + cellY * (cellHeight + heightGap);
Winson Chungaafa03c2010-06-11 17:34:16 -07001100
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001101 resultRect.set(x, y, x + width, y + height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001102 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001103
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001104 /**
Winson Chungaafa03c2010-06-11 17:34:16 -07001105 * Computes the required horizontal and vertical cell spans to always
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001106 * fit the given rectangle.
Winson Chungaafa03c2010-06-11 17:34:16 -07001107 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001108 * @param width Width in pixels
1109 * @param height Height in pixels
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -07001110 * @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 -08001111 */
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -07001112 public int[] rectToCell(int width, int height, int[] result) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001113 // Always assume we're working with the smallest span to make sure we
1114 // reserve enough space in both orientations.
Joe Onorato79e56262009-09-21 15:23:04 -04001115 final Resources resources = getResources();
1116 int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
1117 int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001118 int smallerSize = Math.min(actualWidth, actualHeight);
Joe Onorato79e56262009-09-21 15:23:04 -04001119
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001120 // Always round up to next largest cell
1121 int spanX = (width + smallerSize) / smallerSize;
1122 int spanY = (height + smallerSize) / smallerSize;
Joe Onorato79e56262009-09-21 15:23:04 -04001123
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -07001124 if (result == null) {
1125 return new int[] { spanX, spanY };
1126 }
1127 result[0] = spanX;
1128 result[1] = spanY;
1129 return result;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001130 }
1131
1132 /**
1133 * Find the first vacant cell, if there is one.
1134 *
1135 * @param vacant Holds the x and y coordinate of the vacant cell
1136 * @param spanX Horizontal cell span.
1137 * @param spanY Vertical cell span.
Winson Chungaafa03c2010-06-11 17:34:16 -07001138 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001139 * @return True if a vacant cell was found
1140 */
1141 public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001142
Michael Jurka0280c3b2010-09-17 15:00:07 -07001143 return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001144 }
1145
1146 static boolean findVacantCell(int[] vacant, int spanX, int spanY,
1147 int xCount, int yCount, boolean[][] occupied) {
1148
1149 for (int x = 0; x < xCount; x++) {
1150 for (int y = 0; y < yCount; y++) {
1151 boolean available = !occupied[x][y];
1152out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
1153 for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
1154 available = available && !occupied[i][j];
1155 if (!available) break out;
1156 }
1157 }
1158
1159 if (available) {
1160 vacant[0] = x;
1161 vacant[1] = y;
1162 return true;
1163 }
1164 }
1165 }
1166
1167 return false;
1168 }
1169
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001170 /**
1171 * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
1172 */
1173 boolean[] getOccupiedCellsFlattened() {
Adam Cohend22015c2010-07-26 22:02:18 -07001174 final int xCount = mCountX;
1175 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001176 final boolean[][] occupied = mOccupied;
1177
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001178 final boolean[] flat = new boolean[xCount * yCount];
1179 for (int y = 0; y < yCount; y++) {
1180 for (int x = 0; x < xCount; x++) {
1181 flat[y * xCount + x] = occupied[x][y];
1182 }
1183 }
1184
1185 return flat;
1186 }
1187
Michael Jurka0280c3b2010-09-17 15:00:07 -07001188 private void clearOccupiedCells() {
1189 for (int x = 0; x < mCountX; x++) {
1190 for (int y = 0; y < mCountY; y++) {
1191 mOccupied[x][y] = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001192 }
1193 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07001194 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001195
Michael Jurka0280c3b2010-09-17 15:00:07 -07001196 public void onMove(View view, int newCellX, int newCellY) {
1197 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1198 markCellsAsUnoccupiedForView(view);
1199 markCellsForView(newCellX, newCellY, lp.cellHSpan, lp.cellVSpan, true);
1200 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001201
Michael Jurka0280c3b2010-09-17 15:00:07 -07001202 private void markCellsAsOccupiedForView(View view) {
Michael Jurkac6ee42e2010-09-30 12:04:50 -07001203 if (view == null || view.getParent() != this) return;
Michael Jurka0280c3b2010-09-17 15:00:07 -07001204 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1205 markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
1206 }
1207
1208 private void markCellsAsUnoccupiedForView(View view) {
Michael Jurkac6ee42e2010-09-30 12:04:50 -07001209 if (view == null || view.getParent() != this) return;
Michael Jurka0280c3b2010-09-17 15:00:07 -07001210 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1211 markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
1212 }
1213
1214 private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean value) {
1215 for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
1216 for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
1217 mOccupied[x][y] = value;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001218 }
1219 }
1220 }
1221
1222 @Override
1223 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
1224 return new CellLayout.LayoutParams(getContext(), attrs);
1225 }
1226
1227 @Override
1228 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1229 return p instanceof CellLayout.LayoutParams;
1230 }
1231
1232 @Override
1233 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1234 return new CellLayout.LayoutParams(p);
1235 }
1236
Winson Chungaafa03c2010-06-11 17:34:16 -07001237 public static class CellLayoutAnimationController extends LayoutAnimationController {
1238 public CellLayoutAnimationController(Animation animation, float delay) {
1239 super(animation, delay);
1240 }
1241
1242 @Override
1243 protected long getDelayForView(View view) {
1244 return (int) (Math.random() * 150);
1245 }
1246 }
1247
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001248 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1249 /**
1250 * Horizontal location of the item in the grid.
1251 */
1252 @ViewDebug.ExportedProperty
1253 public int cellX;
1254
1255 /**
1256 * Vertical location of the item in the grid.
1257 */
1258 @ViewDebug.ExportedProperty
1259 public int cellY;
1260
1261 /**
1262 * Number of cells spanned horizontally by the item.
1263 */
1264 @ViewDebug.ExportedProperty
1265 public int cellHSpan;
1266
1267 /**
1268 * Number of cells spanned vertically by the item.
1269 */
1270 @ViewDebug.ExportedProperty
1271 public int cellVSpan;
Winson Chungaafa03c2010-06-11 17:34:16 -07001272
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001273 /**
1274 * Is this item currently being dragged
1275 */
1276 public boolean isDragging;
1277
1278 // X coordinate of the view in the layout.
1279 @ViewDebug.ExportedProperty
1280 int x;
1281 // Y coordinate of the view in the layout.
1282 @ViewDebug.ExportedProperty
1283 int y;
1284
Romain Guy84f296c2009-11-04 15:00:44 -08001285 boolean dropped;
Romain Guyfcb9e712009-10-02 16:06:52 -07001286
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001287 public LayoutParams(Context c, AttributeSet attrs) {
1288 super(c, attrs);
1289 cellHSpan = 1;
1290 cellVSpan = 1;
1291 }
1292
1293 public LayoutParams(ViewGroup.LayoutParams source) {
1294 super(source);
1295 cellHSpan = 1;
1296 cellVSpan = 1;
1297 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001298
1299 public LayoutParams(LayoutParams source) {
1300 super(source);
1301 this.cellX = source.cellX;
1302 this.cellY = source.cellY;
1303 this.cellHSpan = source.cellHSpan;
1304 this.cellVSpan = source.cellVSpan;
1305 }
1306
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001307 public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
Romain Guy8f19cdd2010-01-08 15:07:00 -08001308 super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001309 this.cellX = cellX;
1310 this.cellY = cellY;
1311 this.cellHSpan = cellHSpan;
1312 this.cellVSpan = cellVSpan;
1313 }
1314
1315 public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
1316 int hStartPadding, int vStartPadding) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001317
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001318 final int myCellHSpan = cellHSpan;
1319 final int myCellVSpan = cellVSpan;
1320 final int myCellX = cellX;
1321 final int myCellY = cellY;
Winson Chungaafa03c2010-06-11 17:34:16 -07001322
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001323 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
1324 leftMargin - rightMargin;
1325 height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
1326 topMargin - bottomMargin;
1327
1328 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
1329 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
1330 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001331
1332 public String toString() {
1333 return "(" + this.cellX + ", " + this.cellY + ")";
1334 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001335 }
1336
Michael Jurka0280c3b2010-09-17 15:00:07 -07001337 // This class stores info for two purposes:
1338 // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY,
1339 // its spanX, spanY, and the screen it is on
1340 // 2. When long clicking on an empty cell in a CellLayout, we save information about the
1341 // cellX and cellY coordinates and which page was clicked. We then set this as a tag on
1342 // the CellLayout that was long clicked
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001343 static final class CellInfo implements ContextMenu.ContextMenuInfo {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001344 View cell;
Michael Jurkaa63c4522010-08-19 13:52:27 -07001345 int cellX = -1;
1346 int cellY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001347 int spanX;
1348 int spanY;
1349 int screen;
1350 boolean valid;
1351
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001352 @Override
1353 public String toString() {
Winson Chungaafa03c2010-06-11 17:34:16 -07001354 return "Cell[view=" + (cell == null ? "null" : cell.getClass())
1355 + ", x=" + cellX + ", y=" + cellY + "]";
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001356 }
1357 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001358}