blob: d4a12fb647ddc03b55b7e7ea87df8d8a17d5dfe8 [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
Chet Haase00397b12010-10-07 11:13:10 -070021import android.animation.TimeInterpolator;
Patrick Dubroyde7658b2010-09-27 11:15:43 -070022import android.animation.ValueAnimator;
23import android.animation.ValueAnimator.AnimatorUpdateListener;
Winson Chungaafa03c2010-06-11 17:34:16 -070024import android.app.WallpaperManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080025import android.content.Context;
Joe Onorato79e56262009-09-21 15:23:04 -040026import android.content.res.Resources;
Winson Chungaafa03c2010-06-11 17:34:16 -070027import android.content.res.TypedArray;
28import android.graphics.Canvas;
Patrick Dubroyde7658b2010-09-27 11:15:43 -070029import android.graphics.Point;
30import android.graphics.PointF;
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;
Winson Chungaafa03c2010-06-11 17:34:16 -070040import android.view.animation.Animation;
Winson Chung150fbab2010-09-29 17:14:26 -070041import android.view.animation.DecelerateInterpolator;
Winson Chungaafa03c2010-06-11 17:34:16 -070042import android.view.animation.LayoutAnimationController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043
Adam Cohenf34bab52010-09-30 14:11:56 -070044public class CellLayout extends ViewGroup implements Dimmable {
Winson Chungaafa03c2010-06-11 17:34:16 -070045 static final String TAG = "CellLayout";
46
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047 private int mCellWidth;
48 private int mCellHeight;
Winson Chungaafa03c2010-06-11 17:34:16 -070049
Winson Chungaafa03c2010-06-11 17:34:16 -070050 private int mLeftPadding;
51 private int mRightPadding;
52 private int mTopPadding;
53 private int mBottomPadding;
54
Adam Cohend22015c2010-07-26 22:02:18 -070055 private int mCountX;
56 private int mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057
58 private int mWidthGap;
59 private int mHeightGap;
60
61 private final Rect mRect = new Rect();
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -070062 private final RectF mRectF = new RectF();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080063 private final CellInfo mCellInfo = new CellInfo();
Winson Chungaafa03c2010-06-11 17:34:16 -070064
Patrick Dubroyde7658b2010-09-27 11:15:43 -070065 // These are temporary variables to prevent having to allocate a new object just to
66 // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070067 private final int[] mTmpCellXY = new int[2];
Patrick Dubroyde7658b2010-09-27 11:15:43 -070068 private final int[] mTmpPoint = new int[2];
69 private final PointF mTmpPointF = new PointF();
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070070
The Android Open Source Project31dd5032009-03-03 19:32:27 -080071 boolean[][] mOccupied;
72
Michael Jurkadee05892010-07-27 10:01:56 -070073 private OnTouchListener mInterceptTouchListener;
74
Michael Jurka5f1c5092010-09-03 14:15:02 -070075 private float mBackgroundAlpha;
Adam Cohenf34bab52010-09-30 14:11:56 -070076
Michael Jurka5f1c5092010-09-03 14:15:02 -070077 private Drawable mBackground;
Adam Cohenf34bab52010-09-30 14:11:56 -070078 private Drawable mBackgroundMini;
79 private Drawable mBackgroundMiniHover;
Patrick Dubroy1262e362010-10-06 15:49:50 -070080 private Drawable mBackgroundHover;
Michael Jurka3e7c7632010-10-02 16:01:03 -070081 private Drawable mBackgroundMiniAcceptsDrops;
82 private boolean mAcceptsDrops;
Patrick Dubroy1262e362010-10-06 15:49:50 -070083
84 // If we're actively dragging something over this screen, mHover is true
Michael Jurkaa63c4522010-08-19 13:52:27 -070085 private boolean mHover = false;
Michael Jurkadee05892010-07-27 10:01:56 -070086
Patrick Dubroyde7658b2010-09-27 11:15:43 -070087 private final Point mDragCenter = new Point();
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070088
Patrick Dubroy6569f2c2010-07-12 14:25:18 -070089 private Drawable mDragRectDrawable;
90
Winson Chung150fbab2010-09-29 17:14:26 -070091 // These arrays are used to implement the drag visualization on x-large screens.
92 // They are used as circular arrays, indexed by mDragRectCurrent.
93 private Rect[] mDragRects = new Rect[8];
94 private int[] mDragRectAlphas = new int[mDragRects.length];
95 private InterruptibleInOutAnimator[] mDragRectAnims =
Patrick Dubroy49250ad2010-10-08 15:33:52 -070096 new InterruptibleInOutAnimator[mDragRects.length];
Winson Chung150fbab2010-09-29 17:14:26 -070097
98 // Used as an index into the above 3 arrays; indicates which is the most current value.
99 private int mDragRectCurrent = 0;
100
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700101 private Drawable mCrosshairsDrawable = null;
Patrick Dubroy49250ad2010-10-08 15:33:52 -0700102 private InterruptibleInOutAnimator mCrosshairsAnimator = null;
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700103 private float mCrosshairsVisibility = 0.0f;
104
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700105 // When a drag operation is in progress, holds the nearest cell to the touch point
106 private final int[] mDragCell = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800107
Winson Chungaafa03c2010-06-11 17:34:16 -0700108 private final WallpaperManager mWallpaperManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800109
110 public CellLayout(Context context) {
111 this(context, null);
112 }
113
114 public CellLayout(Context context, AttributeSet attrs) {
115 this(context, attrs, 0);
116 }
117
118 public CellLayout(Context context, AttributeSet attrs, int defStyle) {
119 super(context, attrs, defStyle);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700120
121 // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
122 // the user where a dragged item will land when dropped.
123 setWillNotDraw(false);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700124
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800125 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
126
127 mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
128 mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700129
Adam Cohend22015c2010-07-26 22:02:18 -0700130 mLeftPadding =
131 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisStartPadding, 10);
132 mRightPadding =
133 a.getDimensionPixelSize(R.styleable.CellLayout_xAxisEndPadding, 10);
134 mTopPadding =
135 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisStartPadding, 10);
136 mBottomPadding =
137 a.getDimensionPixelSize(R.styleable.CellLayout_yAxisEndPadding, 10);
Winson Chungaafa03c2010-06-11 17:34:16 -0700138
Adam Cohend22015c2010-07-26 22:02:18 -0700139 mCountX = LauncherModel.getCellCountX();
140 mCountY = LauncherModel.getCellCountY();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700141 mOccupied = new boolean[mCountX][mCountY];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800142
143 a.recycle();
144
145 setAlwaysDrawnWithCacheEnabled(false);
146
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700147 mWallpaperManager = WallpaperManager.getInstance(context);
148
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700149 final Resources res = getResources();
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700150
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700151 if (LauncherApplication.isScreenXLarge()) {
Winson Chung150fbab2010-09-29 17:14:26 -0700152 mBackgroundMini = res.getDrawable(R.drawable.mini_home_screen_bg);
Adam Cohenf34bab52010-09-30 14:11:56 -0700153 mBackgroundMini.setFilterBitmap(true);
Winson Chung150fbab2010-09-29 17:14:26 -0700154 mBackground = res.getDrawable(R.drawable.home_screen_bg);
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700155 mBackground.setFilterBitmap(true);
Winson Chung150fbab2010-09-29 17:14:26 -0700156 mBackgroundMiniHover = res.getDrawable(R.drawable.mini_home_screen_bg_hover);
Adam Cohenf34bab52010-09-30 14:11:56 -0700157 mBackgroundMiniHover.setFilterBitmap(true);
Patrick Dubroy1262e362010-10-06 15:49:50 -0700158 mBackgroundHover = res.getDrawable(R.drawable.home_screen_bg_hover);
159 mBackgroundHover.setFilterBitmap(true);
Michael Jurka3e7c7632010-10-02 16:01:03 -0700160 mBackgroundMiniAcceptsDrops = res.getDrawable(
161 R.drawable.mini_home_screen_bg_accepts_drops);
162 mBackgroundMiniAcceptsDrops.setFilterBitmap(true);
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700163 }
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700164
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700165 // Initialize the data structures used for the drag visualization.
Winson Chung150fbab2010-09-29 17:14:26 -0700166
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700167 mDragRectDrawable = res.getDrawable(R.drawable.rounded_rect_green);
168 mCrosshairsDrawable = res.getDrawable(R.drawable.gardening_crosshairs);
Chet Haase00397b12010-10-07 11:13:10 -0700169 TimeInterpolator interp = new DecelerateInterpolator(2.5f); // Quint ease out
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700170
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700171 // Set up the animation for fading the crosshairs in and out
172 int animDuration = res.getInteger(R.integer.config_crosshairsFadeInTime);
Patrick Dubroy49250ad2010-10-08 15:33:52 -0700173 mCrosshairsAnimator = new InterruptibleInOutAnimator(animDuration, 0.0f, 1.0f);
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700174 mCrosshairsAnimator.addUpdateListener(new AnimatorUpdateListener() {
175 public void onAnimationUpdate(ValueAnimator animation) {
176 mCrosshairsVisibility = ((Float) animation.getAnimatedValue()).floatValue();
177 CellLayout.this.invalidate();
178 }
179 });
180 mCrosshairsAnimator.setInterpolator(interp);
181
182 for (int i = 0; i < mDragRects.length; i++) {
183 mDragRects[i] = new Rect();
184 }
185
186 // When dragging things around the home screens, we show a green outline of
187 // where the item will land. The outlines gradually fade out, leaving a trail
188 // behind the drag path.
189 // Set up all the animations that are used to implement this fading.
190 final int duration = res.getInteger(R.integer.config_dragOutlineFadeTime);
191 final int fromAlphaValue = 0;
192 final int toAlphaValue = res.getInteger(R.integer.config_dragOutlineMaxAlpha);
193 for (int i = 0; i < mDragRectAnims.length; i++) {
194 final InterruptibleInOutAnimator anim =
195 new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue);
196 anim.setInterpolator(interp);
197 final int thisIndex = i;
198 anim.addUpdateListener(new AnimatorUpdateListener() {
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700199 public void onAnimationUpdate(ValueAnimator animation) {
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700200 mDragRectAlphas[thisIndex] = (Integer) animation.getAnimatedValue();
201 CellLayout.this.invalidate(mDragRects[thisIndex]);
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700202 }
203 });
Patrick Dubroy046e7eb2010-10-06 12:14:43 -0700204 mDragRectAnims[i] = anim;
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700205 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800206 }
207
Michael Jurkaa63c4522010-08-19 13:52:27 -0700208 public void setHover(boolean value) {
209 if (mHover != value) {
Michael Jurka3e7c7632010-10-02 16:01:03 -0700210 mHover = value;
Michael Jurkaa63c4522010-08-19 13:52:27 -0700211 invalidate();
212 }
Michael Jurkaa63c4522010-08-19 13:52:27 -0700213 }
214
Patrick Dubroy1262e362010-10-06 15:49:50 -0700215 public void drawChildren(Canvas canvas) {
216 super.dispatchDraw(canvas);
217 }
218
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700219 @Override
Patrick Dubroy1262e362010-10-06 15:49:50 -0700220 protected void onDraw(Canvas canvas) {
Michael Jurka3e7c7632010-10-02 16:01:03 -0700221 // When we're large, we are either drawn in a "hover" state (ie when dragging an item to
222 // a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f)
223 // When we're small, we are either drawn normally or in the "accepts drops" state (during
224 // a drag). However, we also drag the mini hover background *over* one of those two
225 // backgrounds
Michael Jurka5f1c5092010-09-03 14:15:02 -0700226 if (mBackgroundAlpha > 0.0f) {
Adam Cohenf34bab52010-09-30 14:11:56 -0700227 Drawable bg;
Patrick Dubroy1262e362010-10-06 15:49:50 -0700228 if (getScaleX() < 0.5f) {
Michael Jurka3e7c7632010-10-02 16:01:03 -0700229 bg = mAcceptsDrops ? mBackgroundMiniAcceptsDrops : mBackgroundMini;
Adam Cohenf34bab52010-09-30 14:11:56 -0700230 } else {
Patrick Dubroy1262e362010-10-06 15:49:50 -0700231 bg = mHover ? mBackgroundHover : mBackground;
Adam Cohenf34bab52010-09-30 14:11:56 -0700232 }
Adam Cohen9c4949e2010-10-05 12:27:22 -0700233 if (bg != null) {
234 bg.setAlpha((int) (mBackgroundAlpha * 255));
235 bg.draw(canvas);
236 }
Michael Jurka3e7c7632010-10-02 16:01:03 -0700237 if (mHover && getScaleX() < 0.5f) {
238 mBackgroundMiniHover.setAlpha((int) (mBackgroundAlpha * 255));
239 mBackgroundMiniHover.draw(canvas);
240 }
Michael Jurkaa63c4522010-08-19 13:52:27 -0700241 }
Romain Guya6abce82009-11-10 02:54:41 -0800242
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700243 if (mCrosshairsVisibility > 0.0f) {
244 final int countX = mCountX;
245 final int countY = mCountY;
246
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700247 final float MAX_ALPHA = 0.4f;
248 final int MAX_VISIBLE_DISTANCE = 600;
249 final float DISTANCE_MULTIPLIER = 0.002f;
250
251 final Drawable d = mCrosshairsDrawable;
252 final int width = d.getIntrinsicWidth();
253 final int height = d.getIntrinsicHeight();
254
255 int x = getLeftPadding() - (mWidthGap / 2) - (width / 2);
256 for (int col = 0; col <= countX; col++) {
257 int y = getTopPadding() - (mHeightGap / 2) - (height / 2);
258 for (int row = 0; row <= countY; row++) {
259 mTmpPointF.set(x - mDragCenter.x, y - mDragCenter.y);
260 float dist = mTmpPointF.length();
261 // Crosshairs further from the drag point are more faint
262 float alpha = Math.min(MAX_ALPHA,
263 DISTANCE_MULTIPLIER * (MAX_VISIBLE_DISTANCE - dist));
264 if (alpha > 0.0f) {
265 d.setBounds(x, y, x + width, y + height);
266 d.setAlpha((int) (alpha * 255 * mCrosshairsVisibility));
267 d.draw(canvas);
268 }
269 y += mCellHeight + mHeightGap;
270 }
271 x += mCellWidth + mWidthGap;
272 }
Winson Chung150fbab2010-09-29 17:14:26 -0700273
274 for (int i = 0; i < mDragRects.length; i++) {
275 int alpha = mDragRectAlphas[i];
276 if (alpha > 0) {
277 mDragRectDrawable.setAlpha(alpha);
278 mDragRectDrawable.setBounds(mDragRects[i]);
279 mDragRectDrawable.draw(canvas);
280 }
281 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700282 }
283 }
284
Adam Cohenf34bab52010-09-30 14:11:56 -0700285 public void setDimmableProgress(float progress) {
286 for (int i = 0; i < getChildCount(); i++) {
287 Dimmable d = (Dimmable) getChildAt(i);
288 d.setDimmableProgress(progress);
289 }
290 }
291
292 public float getDimmableProgress() {
293 if (getChildCount() > 0) {
294 return ((Dimmable) getChildAt(0)).getDimmableProgress();
295 }
296 return 0.0f;
297 }
298
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700299 @Override
Jeff Sharkey83f111d2009-04-20 21:03:13 -0700300 public void cancelLongPress() {
301 super.cancelLongPress();
302
303 // Cancel long press for all children
304 final int count = getChildCount();
305 for (int i = 0; i < count; i++) {
306 final View child = getChildAt(i);
307 child.cancelLongPress();
308 }
309 }
310
Michael Jurkadee05892010-07-27 10:01:56 -0700311 public void setOnInterceptTouchListener(View.OnTouchListener listener) {
312 mInterceptTouchListener = listener;
313 }
314
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800315 int getCountX() {
Adam Cohend22015c2010-07-26 22:02:18 -0700316 return mCountX;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800317 }
318
319 int getCountY() {
Adam Cohend22015c2010-07-26 22:02:18 -0700320 return mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800321 }
322
Winson Chungaafa03c2010-06-11 17:34:16 -0700323 public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
324 final LayoutParams lp = params;
325
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800326 // Generate an id for each view, this assumes we have at most 256x256 cells
327 // per workspace screen
Adam Cohend22015c2010-07-26 22:02:18 -0700328 if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700329 // If the horizontal or vertical span is set to -1, it is taken to
330 // mean that it spans the extent of the CellLayout
Adam Cohend22015c2010-07-26 22:02:18 -0700331 if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
332 if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800333
Winson Chungaafa03c2010-06-11 17:34:16 -0700334 child.setId(childId);
335
Michael Jurkadee05892010-07-27 10:01:56 -0700336 // We might be in the middle or end of shrinking/fading to a dimmed view
337 // Make sure this view's alpha is set the same as all the rest of the views
Michael Jurka5f1c5092010-09-03 14:15:02 -0700338 child.setAlpha(getAlpha());
Winson Chungaafa03c2010-06-11 17:34:16 -0700339 addView(child, index, lp);
Michael Jurkadee05892010-07-27 10:01:56 -0700340
Michael Jurka0280c3b2010-09-17 15:00:07 -0700341 markCellsAsOccupiedForView(child);
342
Winson Chungaafa03c2010-06-11 17:34:16 -0700343 return true;
344 }
345 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800346 }
Michael Jurka3e7c7632010-10-02 16:01:03 -0700347 public void setAcceptsDrops(boolean acceptsDrops) {
348 if (mAcceptsDrops != acceptsDrops) {
349 mAcceptsDrops = acceptsDrops;
350 invalidate();
351 }
352 }
353
354 public boolean getAcceptsDrops() {
355 return mAcceptsDrops;
356 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800357
358 @Override
Michael Jurka0280c3b2010-09-17 15:00:07 -0700359 public void removeAllViews() {
360 clearOccupiedCells();
361 }
362
363 @Override
364 public void removeAllViewsInLayout() {
365 clearOccupiedCells();
366 }
367
368 @Override
369 public void removeView(View view) {
370 markCellsAsUnoccupiedForView(view);
371 super.removeView(view);
372 }
373
374 @Override
375 public void removeViewAt(int index) {
376 markCellsAsUnoccupiedForView(getChildAt(index));
377 super.removeViewAt(index);
378 }
379
380 @Override
381 public void removeViewInLayout(View view) {
382 markCellsAsUnoccupiedForView(view);
383 super.removeViewInLayout(view);
384 }
385
386 @Override
387 public void removeViews(int start, int count) {
388 for (int i = start; i < start + count; i++) {
389 markCellsAsUnoccupiedForView(getChildAt(i));
390 }
391 super.removeViews(start, count);
392 }
393
394 @Override
395 public void removeViewsInLayout(int start, int count) {
396 for (int i = start; i < start + count; i++) {
397 markCellsAsUnoccupiedForView(getChildAt(i));
398 }
399 super.removeViewsInLayout(start, count);
400 }
401
402 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800403 public void requestChildFocus(View child, View focused) {
404 super.requestChildFocus(child, focused);
405 if (child != null) {
406 Rect r = new Rect();
407 child.getDrawingRect(r);
408 requestRectangleOnScreen(r);
409 }
410 }
411
412 @Override
413 protected void onAttachedToWindow() {
414 super.onAttachedToWindow();
415 mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
416 }
417
Michael Jurkaaf442092010-06-10 17:01:57 -0700418 public void setTagToCellInfoForPoint(int touchX, int touchY) {
419 final CellInfo cellInfo = mCellInfo;
420 final Rect frame = mRect;
421 final int x = touchX + mScrollX;
422 final int y = touchY + mScrollY;
423 final int count = getChildCount();
424
425 boolean found = false;
426 for (int i = count - 1; i >= 0; i--) {
427 final View child = getChildAt(i);
428
429 if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
430 child.getHitRect(frame);
431 if (frame.contains(x, y)) {
432 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
433 cellInfo.cell = child;
434 cellInfo.cellX = lp.cellX;
435 cellInfo.cellY = lp.cellY;
436 cellInfo.spanX = lp.cellHSpan;
437 cellInfo.spanY = lp.cellVSpan;
438 cellInfo.valid = true;
439 found = true;
Michael Jurkaaf442092010-06-10 17:01:57 -0700440 break;
441 }
442 }
443 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700444
Michael Jurkaaf442092010-06-10 17:01:57 -0700445 if (!found) {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700446 final int cellXY[] = mTmpCellXY;
Michael Jurkaaf442092010-06-10 17:01:57 -0700447 pointToCellExact(x, y, cellXY);
448
Michael Jurkaaf442092010-06-10 17:01:57 -0700449 cellInfo.cell = null;
450 cellInfo.cellX = cellXY[0];
451 cellInfo.cellY = cellXY[1];
452 cellInfo.spanX = 1;
453 cellInfo.spanY = 1;
Michael Jurka0280c3b2010-09-17 15:00:07 -0700454 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < mCountX &&
455 cellXY[1] < mCountY && !mOccupied[cellXY[0]][cellXY[1]];
Michael Jurkaaf442092010-06-10 17:01:57 -0700456 }
457 setTag(cellInfo);
458 }
459
Winson Chungaafa03c2010-06-11 17:34:16 -0700460
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800461 @Override
462 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkadee05892010-07-27 10:01:56 -0700463 if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
464 return true;
465 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800466 final int action = ev.getAction();
467 final CellInfo cellInfo = mCellInfo;
468
469 if (action == MotionEvent.ACTION_DOWN) {
Michael Jurkaaf442092010-06-10 17:01:57 -0700470 setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800471 } else if (action == MotionEvent.ACTION_UP) {
472 cellInfo.cell = null;
473 cellInfo.cellX = -1;
474 cellInfo.cellY = -1;
475 cellInfo.spanX = 0;
476 cellInfo.spanY = 0;
477 cellInfo.valid = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800478 setTag(cellInfo);
479 }
480
481 return false;
482 }
483
484 @Override
485 public CellInfo getTag() {
Michael Jurka0280c3b2010-09-17 15:00:07 -0700486 return (CellInfo) super.getTag();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800487 }
488
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700489 /**
490 * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
491 */
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800492 private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
493 for (int x = left; x <= right; x++) {
494 if (occupied[x][y]) {
495 return false;
496 }
497 }
498 return true;
499 }
500
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800501 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700502 * Given a point, return the cell that strictly encloses that point
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800503 * @param x X coordinate of the point
504 * @param y Y coordinate of the point
505 * @param result Array of 2 ints to hold the x and y coordinate of the cell
506 */
507 void pointToCellExact(int x, int y, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700508 final int hStartPadding = getLeftPadding();
509 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800510
511 result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
512 result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
513
Adam Cohend22015c2010-07-26 22:02:18 -0700514 final int xAxis = mCountX;
515 final int yAxis = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800516
517 if (result[0] < 0) result[0] = 0;
518 if (result[0] >= xAxis) result[0] = xAxis - 1;
519 if (result[1] < 0) result[1] = 0;
520 if (result[1] >= yAxis) result[1] = yAxis - 1;
521 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700522
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800523 /**
524 * Given a point, return the cell that most closely encloses that point
525 * @param x X coordinate of the point
526 * @param y Y coordinate of the point
527 * @param result Array of 2 ints to hold the x and y coordinate of the cell
528 */
529 void pointToCellRounded(int x, int y, int[] result) {
530 pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
531 }
532
533 /**
534 * Given a cell coordinate, return the point that represents the upper left corner of that cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700535 *
536 * @param cellX X coordinate of the cell
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800537 * @param cellY Y coordinate of the cell
Winson Chungaafa03c2010-06-11 17:34:16 -0700538 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800539 * @param result Array of 2 ints to hold the x and y coordinate of the point
540 */
541 void cellToPoint(int cellX, int cellY, int[] result) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700542 final int hStartPadding = getLeftPadding();
543 final int vStartPadding = getTopPadding();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800544
545 result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
546 result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
547 }
548
Romain Guy84f296c2009-11-04 15:00:44 -0800549 int getCellWidth() {
550 return mCellWidth;
551 }
552
553 int getCellHeight() {
554 return mCellHeight;
555 }
556
Romain Guy1a304a12009-11-10 00:02:32 -0800557 int getLeftPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700558 return mLeftPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800559 }
560
561 int getTopPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700562 return mTopPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800563 }
564
565 int getRightPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700566 return mRightPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800567 }
568
569 int getBottomPadding() {
Winson Chungaafa03c2010-06-11 17:34:16 -0700570 return mBottomPadding;
Romain Guy1a304a12009-11-10 00:02:32 -0800571 }
572
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800573 @Override
574 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
575 // TODO: currently ignoring padding
Winson Chungaafa03c2010-06-11 17:34:16 -0700576
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800577 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700578 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
579
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800580 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
581 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
Winson Chungaafa03c2010-06-11 17:34:16 -0700582
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800583 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
584 throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
585 }
586
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800587 final int cellWidth = mCellWidth;
588 final int cellHeight = mCellHeight;
589
Adam Cohend22015c2010-07-26 22:02:18 -0700590 int numWidthGaps = mCountX - 1;
591 int numHeightGaps = mCountY - 1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800592
Michael Jurka0280c3b2010-09-17 15:00:07 -0700593 int vSpaceLeft = heightSpecSize - mTopPadding - mBottomPadding - (cellHeight * mCountY);
Adam Cohend22015c2010-07-26 22:02:18 -0700594 mHeightGap = vSpaceLeft / numHeightGaps;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800595
Michael Jurka0280c3b2010-09-17 15:00:07 -0700596 int hSpaceLeft = widthSpecSize - mLeftPadding - mRightPadding - (cellWidth * mCountX);
Adam Cohend22015c2010-07-26 22:02:18 -0700597 mWidthGap = hSpaceLeft / numWidthGaps;
Winson Chungaafa03c2010-06-11 17:34:16 -0700598
Michael Jurka5f1c5092010-09-03 14:15:02 -0700599 // center it around the min gaps
600 int minGap = Math.min(mWidthGap, mHeightGap);
601 mWidthGap = mHeightGap = minGap;
602
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800603 int count = getChildCount();
604
605 for (int i = 0; i < count; i++) {
606 View child = getChildAt(i);
607 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Winson Chungaafa03c2010-06-11 17:34:16 -0700608 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
609 mLeftPadding, mTopPadding);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800610
Michael Jurka0280c3b2010-09-17 15:00:07 -0700611 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
Winson Chungaafa03c2010-06-11 17:34:16 -0700612 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
613 MeasureSpec.EXACTLY);
614
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800615 child.measure(childWidthMeasureSpec, childheightMeasureSpec);
616 }
Michael Jurka5f1c5092010-09-03 14:15:02 -0700617 if (widthSpecMode == MeasureSpec.AT_MOST) {
618 int newWidth = mLeftPadding + mRightPadding + (mCountX * cellWidth) +
619 ((mCountX - 1) * minGap);
620 int newHeight = mTopPadding + mBottomPadding + (mCountY * cellHeight) +
621 ((mCountY - 1) * minGap);
622 setMeasuredDimension(newWidth, newHeight);
623 } else if (widthSpecMode == MeasureSpec.EXACTLY) {
624 setMeasuredDimension(widthSpecSize, heightSpecSize);
625 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800626 }
627
628 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -0700629 protected void onLayout(boolean changed, int l, int t, int r, int b) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800630 int count = getChildCount();
631
632 for (int i = 0; i < count; i++) {
633 View child = getChildAt(i);
634 if (child.getVisibility() != GONE) {
635
636 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
637
638 int childLeft = lp.x;
639 int childTop = lp.y;
640 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
Romain Guy84f296c2009-11-04 15:00:44 -0800641
642 if (lp.dropped) {
643 lp.dropped = false;
644
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700645 final int[] cellXY = mTmpCellXY;
Romain Guy06762ab2010-01-25 16:51:08 -0800646 getLocationOnScreen(cellXY);
Romain Guy84f296c2009-11-04 15:00:44 -0800647 mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
Romain Guy06762ab2010-01-25 16:51:08 -0800648 cellXY[0] + childLeft + lp.width / 2,
649 cellXY[1] + childTop + lp.height / 2, 0, null);
Romain Guy84f296c2009-11-04 15:00:44 -0800650 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800651 }
652 }
653 }
654
655 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700656 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
657 super.onSizeChanged(w, h, oldw, oldh);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700658 if (mBackground != null) {
Patrick Dubroy1262e362010-10-06 15:49:50 -0700659 mBackground.setBounds(0, 0, w, h);
660 }
661 if (mBackgroundHover != null) {
662 mBackgroundHover.setBounds(0, 0, w, h);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700663 }
Adam Cohenf34bab52010-09-30 14:11:56 -0700664 if (mBackgroundMiniHover != null) {
Patrick Dubroy1262e362010-10-06 15:49:50 -0700665 mBackgroundMiniHover.setBounds(0, 0, w, h);
Adam Cohenf34bab52010-09-30 14:11:56 -0700666 }
667 if (mBackgroundMini != null) {
Patrick Dubroy1262e362010-10-06 15:49:50 -0700668 mBackgroundMini.setBounds(0, 0, w, h);
Michael Jurkaa63c4522010-08-19 13:52:27 -0700669 }
Michael Jurka3e7c7632010-10-02 16:01:03 -0700670 if (mBackgroundMiniAcceptsDrops != null) {
671 mBackgroundMiniAcceptsDrops.setBounds(0, 0, w, h);
672 }
Michael Jurkadee05892010-07-27 10:01:56 -0700673 }
674
675 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800676 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
677 final int count = getChildCount();
678 for (int i = 0; i < count; i++) {
679 final View view = getChildAt(i);
680 view.setDrawingCacheEnabled(enabled);
681 // Update the drawing caches
Adam Powellfefa0ce2010-05-03 10:23:50 -0700682 view.buildDrawingCache(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800683 }
684 }
685
686 @Override
687 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
688 super.setChildrenDrawnWithCacheEnabled(enabled);
689 }
690
Michael Jurka5f1c5092010-09-03 14:15:02 -0700691 public float getBackgroundAlpha() {
692 return mBackgroundAlpha;
Michael Jurkadee05892010-07-27 10:01:56 -0700693 }
694
Michael Jurka5f1c5092010-09-03 14:15:02 -0700695 public void setBackgroundAlpha(float alpha) {
696 mBackgroundAlpha = alpha;
Michael Jurka0142d492010-08-25 17:46:15 -0700697 invalidate();
Michael Jurkadee05892010-07-27 10:01:56 -0700698 }
699
Michael Jurka5f1c5092010-09-03 14:15:02 -0700700 // Need to return true to let the view system know we know how to handle alpha-- this is
701 // because when our children have an alpha of 0.0f, they are still rendering their "dimmed"
702 // versions
703 @Override
704 protected boolean onSetAlpha(int alpha) {
705 return true;
706 }
707
708 public void setAlpha(float alpha) {
709 setChildrenAlpha(alpha);
710 super.setAlpha(alpha);
711 }
712
Michael Jurkadee05892010-07-27 10:01:56 -0700713 private void setChildrenAlpha(float alpha) {
Michael Jurka0142d492010-08-25 17:46:15 -0700714 final int childCount = getChildCount();
715 for (int i = 0; i < childCount; i++) {
Michael Jurkadee05892010-07-27 10:01:56 -0700716 getChildAt(i).setAlpha(alpha);
717 }
718 }
719
Michael Jurka0280c3b2010-09-17 15:00:07 -0700720 private boolean isVacantIgnoring(
721 int originX, int originY, int spanX, int spanY, View ignoreView) {
722 if (ignoreView != null) {
723 markCellsAsUnoccupiedForView(ignoreView);
724 }
Michael Jurka28750fb2010-09-24 17:43:49 -0700725 boolean isVacant = true;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700726 for (int i = 0; i < spanY; i++) {
727 if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
Michael Jurka28750fb2010-09-24 17:43:49 -0700728 isVacant = false;
729 break;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700730 }
731 }
Michael Jurka0280c3b2010-09-17 15:00:07 -0700732 if (ignoreView != null) {
733 markCellsAsOccupiedForView(ignoreView);
734 }
Michael Jurka28750fb2010-09-24 17:43:49 -0700735 return isVacant;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700736 }
737
Michael Jurka0280c3b2010-09-17 15:00:07 -0700738 private boolean isVacant(int originX, int originY, int spanX, int spanY) {
739 return isVacantIgnoring(originX, originY, spanX, spanY, null);
740 }
741
Patrick Dubroy440c3602010-07-13 17:50:32 -0700742 public View getChildAt(int x, int y) {
743 final int count = getChildCount();
744 for (int i = 0; i < count; i++) {
745 View child = getChildAt(i);
746 LayoutParams lp = (LayoutParams) child.getLayoutParams();
747
748 if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
749 (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
750 return child;
751 }
752 }
753 return null;
754 }
755
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700756 /**
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -0700757 * Estimate the size that a child with the given dimensions will take in the layout.
758 */
759 void estimateChildSize(int minWidth, int minHeight, int[] result) {
760 // Assuming it's placed at 0, 0, find where the bottom right cell will land
761 rectToCell(minWidth, minHeight, result);
762
763 // Then figure out the rect it will occupy
764 cellToRect(0, 0, result[0], result[1], mRectF);
765 result[0] = (int)mRectF.width();
766 result[1] = (int)mRectF.height();
767 }
768
769 /**
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700770 * Estimate where the top left cell of the dragged item will land if it is dropped.
771 *
772 * @param originX The X value of the top left corner of the item
773 * @param originY The Y value of the top left corner of the item
774 * @param spanX The number of horizontal cells that the item spans
775 * @param spanY The number of vertical cells that the item spans
776 * @param result The estimated drop cell X and Y.
777 */
778 void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
Adam Cohend22015c2010-07-26 22:02:18 -0700779 final int countX = mCountX;
780 final int countY = mCountY;
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700781
Michael Jurkaa63c4522010-08-19 13:52:27 -0700782 // pointToCellRounded takes the top left of a cell but will pad that with
783 // cellWidth/2 and cellHeight/2 when finding the matching cell
784 pointToCellRounded(originX, originY, result);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700785
786 // If the item isn't fully on this screen, snap to the edges
787 int rightOverhang = result[0] + spanX - countX;
788 if (rightOverhang > 0) {
789 result[0] -= rightOverhang; // Snap to right
790 }
791 result[0] = Math.max(0, result[0]); // Snap to left
792 int bottomOverhang = result[1] + spanY - countY;
793 if (bottomOverhang > 0) {
794 result[1] -= bottomOverhang; // Snap to bottom
795 }
796 result[1] = Math.max(0, result[1]); // Snap to top
797 }
798
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700799 void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) {
800 final int[] nearest = findNearestVacantArea(originX, originY, spanX, spanY, view, mDragCell);
801 mDragCenter.set(originX + (view.getWidth() / 2), originY + (view.getHeight() / 2));
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700802
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700803 if (nearest != null) {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700804 // Find the top left corner of the rect the object will occupy
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700805 final int[] topLeft = mTmpPoint;
806 cellToPoint(nearest[0], nearest[1], topLeft);
807
808 // Need to copy these, because the next call to cellToPoint will overwrite them
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700809 final int left = topLeft[0];
810 final int top = topLeft[1];
811
Winson Chung150fbab2010-09-29 17:14:26 -0700812 final Rect dragRect = mDragRects[mDragRectCurrent];
813
814 if (dragRect.isEmpty() || left != dragRect.left || top != dragRect.top) {
815 // Now find the bottom right
816 final int[] bottomRight = mTmpPoint;
817 cellToPoint(nearest[0] + spanX - 1, nearest[1] + spanY - 1, bottomRight);
818 bottomRight[0] += mCellWidth;
819 bottomRight[1] += mCellHeight;
820
821 final int oldIndex = mDragRectCurrent;
822 mDragRectCurrent = (oldIndex + 1) % mDragRects.length;
823
824 mDragRects[mDragRectCurrent].set(left, top, bottomRight[0], bottomRight[1]);
825
826 mDragRectAnims[oldIndex].animateOut();
827 mDragRectAnims[mDragRectCurrent].animateIn();
828 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700829 }
Patrick Dubroy49250ad2010-10-08 15:33:52 -0700830
831 // If we are drawing crosshairs, the entire CellLayout needs to be invalidated
832 if (mCrosshairsDrawable != null) {
833 invalidate();
834 }
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700835 }
836
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800837 /**
Jeff Sharkey70864282009-04-07 21:08:40 -0700838 * Find a vacant area that will fit the given bounds nearest the requested
839 * cell location. Uses Euclidean distance to score multiple vacant areas.
Winson Chungaafa03c2010-06-11 17:34:16 -0700840 *
Romain Guy51afc022009-05-04 18:03:43 -0700841 * @param pixelX The X location at which you want to search for a vacant area.
842 * @param pixelY The Y location at which you want to search for a vacant area.
Jeff Sharkey70864282009-04-07 21:08:40 -0700843 * @param spanX Horizontal span of the object.
844 * @param spanY Vertical span of the object.
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700845 * @param result Array in which to place the result, or null (in which case a new array will
846 * be allocated)
Jeff Sharkey70864282009-04-07 21:08:40 -0700847 * @return The X, Y cell of a vacant area that can contain this object,
848 * nearest the requested location.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800849 */
Michael Jurka6a1435d2010-09-27 17:35:12 -0700850 int[] findNearestVacantArea(
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700851 int pixelX, int pixelY, int spanX, int spanY, int[] result) {
852 return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, result);
Michael Jurka6a1435d2010-09-27 17:35:12 -0700853 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700854
Michael Jurka6a1435d2010-09-27 17:35:12 -0700855 /**
856 * Find a vacant area that will fit the given bounds nearest the requested
857 * cell location. Uses Euclidean distance to score multiple vacant areas.
858 *
859 * @param pixelX The X location at which you want to search for a vacant area.
860 * @param pixelY The Y location at which you want to search for a vacant area.
861 * @param spanX Horizontal span of the object.
862 * @param spanY Vertical span of the object.
Michael Jurka6a1435d2010-09-27 17:35:12 -0700863 * @param ignoreView Considers space occupied by this view as unoccupied
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700864 * @param result Previously returned value to possibly recycle.
Michael Jurka6a1435d2010-09-27 17:35:12 -0700865 * @return The X, Y cell of a vacant area that can contain this object,
866 * nearest the requested location.
867 */
868 int[] findNearestVacantArea(
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700869 int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) {
Michael Jurkac6ee42e2010-09-30 12:04:50 -0700870 // mark space take by ignoreView as available (method checks if ignoreView is null)
871 markCellsAsUnoccupiedForView(ignoreView);
872
Jeff Sharkey70864282009-04-07 21:08:40 -0700873 // Keep track of best-scoring drop area
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700874 final int[] bestXY = result != null ? result : new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700875 double bestDistance = Double.MAX_VALUE;
Winson Chungaafa03c2010-06-11 17:34:16 -0700876
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700877 final int countX = mCountX;
878 final int countY = mCountY;
879 final boolean[][] occupied = mOccupied;
880
881 for (int x = 0; x < countX - (spanX - 1); x++) {
Michael Jurkac28de512010-08-13 11:27:44 -0700882 inner:
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700883 for (int y = 0; y < countY - (spanY - 1); y++) {
Michael Jurkac28de512010-08-13 11:27:44 -0700884 for (int i = 0; i < spanX; i++) {
885 for (int j = 0; j < spanY; j++) {
Patrick Dubroyde7658b2010-09-27 11:15:43 -0700886 if (occupied[x + i][y + j]) {
Michael Jurkac28de512010-08-13 11:27:44 -0700887 // small optimization: we can skip to below the row we just found
888 // an occupied cell
889 y += j;
890 continue inner;
891 }
892 }
893 }
894 final int[] cellXY = mTmpCellXY;
895 cellToPoint(x, y, cellXY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800896
Michael Jurkac28de512010-08-13 11:27:44 -0700897 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
898 + Math.pow(cellXY[1] - pixelY, 2));
899 if (distance <= bestDistance) {
900 bestDistance = distance;
901 bestXY[0] = x;
902 bestXY[1] = y;
903 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800904 }
905 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -0700906 // re-mark space taken by ignoreView as occupied
907 markCellsAsOccupiedForView(ignoreView);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800908
Winson Chungaafa03c2010-06-11 17:34:16 -0700909 // Return null if no suitable location found
Jeff Sharkey70864282009-04-07 21:08:40 -0700910 if (bestDistance < Double.MAX_VALUE) {
911 return bestXY;
912 } else {
913 return null;
914 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800915 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700916
Michael Jurka0280c3b2010-09-17 15:00:07 -0700917 boolean existsEmptyCell() {
918 return findCellForSpan(null, 1, 1);
919 }
920
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800921 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -0700922 * Finds the upper-left coordinate of the first rectangle in the grid that can
923 * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
924 * then this method will only return coordinates for rectangles that contain the cell
925 * (intersectX, intersectY)
926 *
927 * @param cellXY The array that will contain the position of a vacant cell if such a cell
928 * can be found.
929 * @param spanX The horizontal span of the cell we want to find.
930 * @param spanY The vertical span of the cell we want to find.
931 *
932 * @return True if a vacant cell of the specified dimension was found, false otherwise.
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700933 */
Michael Jurka0280c3b2010-09-17 15:00:07 -0700934 boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
935 return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null);
936 }
937
938 /**
939 * Like above, but ignores any cells occupied by the item "ignoreView"
940 *
941 * @param cellXY The array that will contain the position of a vacant cell if such a cell
942 * can be found.
943 * @param spanX The horizontal span of the cell we want to find.
944 * @param spanY The vertical span of the cell we want to find.
945 * @param ignoreView The home screen item we should treat as not occupying any space
946 * @return
947 */
948 boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
949 return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, ignoreView);
950 }
951
952 /**
953 * Like above, but if intersectX and intersectY are not -1, then this method will try to
954 * return coordinates for rectangles that contain the cell [intersectX, intersectY]
955 *
956 * @param spanX The horizontal span of the cell we want to find.
957 * @param spanY The vertical span of the cell we want to find.
958 * @param ignoreView The home screen item we should treat as not occupying any space
959 * @param intersectX The X coordinate of the cell that we should try to overlap
960 * @param intersectX The Y coordinate of the cell that we should try to overlap
961 *
962 * @return True if a vacant cell of the specified dimension was found, false otherwise.
963 */
964 boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
965 int intersectX, int intersectY) {
966 return findCellForSpanThatIntersectsIgnoring(
967 cellXY, spanX, spanY, intersectX, intersectY, null);
968 }
969
970 /**
971 * The superset of the above two methods
972 */
973 boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
974 int intersectX, int intersectY, View ignoreView) {
Michael Jurkac6ee42e2010-09-30 12:04:50 -0700975 // mark space take by ignoreView as available (method checks if ignoreView is null)
976 markCellsAsUnoccupiedForView(ignoreView);
Michael Jurka0280c3b2010-09-17 15:00:07 -0700977
Michael Jurka28750fb2010-09-24 17:43:49 -0700978 boolean foundCell = false;
Michael Jurka0280c3b2010-09-17 15:00:07 -0700979 while (true) {
980 int startX = 0;
981 if (intersectX >= 0) {
982 startX = Math.max(startX, intersectX - (spanX - 1));
983 }
984 int endX = mCountX - (spanX - 1);
985 if (intersectX >= 0) {
986 endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0));
987 }
988 int startY = 0;
989 if (intersectY >= 0) {
990 startY = Math.max(startY, intersectY - (spanY - 1));
991 }
992 int endY = mCountY - (spanY - 1);
993 if (intersectY >= 0) {
994 endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0));
995 }
996
997 for (int x = startX; x < endX; x++) {
998 inner:
999 for (int y = startY; y < endY; y++) {
1000 for (int i = 0; i < spanX; i++) {
1001 for (int j = 0; j < spanY; j++) {
1002 if (mOccupied[x + i][y + j]) {
1003 // small optimization: we can skip to below the row we just found
1004 // an occupied cell
1005 y += j;
1006 continue inner;
1007 }
1008 }
1009 }
1010 if (cellXY != null) {
1011 cellXY[0] = x;
1012 cellXY[1] = y;
1013 }
Michael Jurka28750fb2010-09-24 17:43:49 -07001014 foundCell = true;
1015 break;
Michael Jurka0280c3b2010-09-17 15:00:07 -07001016 }
1017 }
1018 if (intersectX == -1 && intersectY == -1) {
1019 break;
1020 } else {
1021 // if we failed to find anything, try again but without any requirements of
1022 // intersecting
1023 intersectX = -1;
1024 intersectY = -1;
1025 continue;
1026 }
1027 }
1028
Michael Jurkac6ee42e2010-09-30 12:04:50 -07001029 // re-mark space taken by ignoreView as occupied
1030 markCellsAsOccupiedForView(ignoreView);
Michael Jurka28750fb2010-09-24 17:43:49 -07001031 return foundCell;
Michael Jurka0280c3b2010-09-17 15:00:07 -07001032 }
1033
1034 /**
1035 * Called when drag has left this CellLayout or has been completed (successfully or not)
1036 */
1037 void onDragExit() {
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001038 // Invalidate the drag data
1039 mDragCell[0] = -1;
1040 mDragCell[1] = -1;
1041
Michael Jurkaa63c4522010-08-19 13:52:27 -07001042 setHover(false);
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001043
1044 // Fade out the drag indicators
1045 if (mCrosshairsAnimator != null) {
Patrick Dubroy49250ad2010-10-08 15:33:52 -07001046 mCrosshairsAnimator.animateOut();
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001047 }
Winson Chung150fbab2010-09-29 17:14:26 -07001048
1049 mDragRectAnims[mDragRectCurrent].animateOut();
1050 mDragRectCurrent = (mDragRectCurrent + 1) % mDragRects.length;
1051 mDragRects[mDragRectCurrent].setEmpty();
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001052 }
1053
1054 /**
Winson Chungaafa03c2010-06-11 17:34:16 -07001055 * Mark a child as having been dropped.
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001056 * At the beginning of the drag operation, the child may have been on another
1057 * screen, but it is reparented before this method is called.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001058 *
1059 * @param child The child that is being dropped
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001060 */
Winson Chungaafa03c2010-06-11 17:34:16 -07001061 void onDropChild(View child) {
Romain Guyd94533d2009-08-17 10:01:15 -07001062 if (child != null) {
1063 LayoutParams lp = (LayoutParams) child.getLayoutParams();
Romain Guyd94533d2009-08-17 10:01:15 -07001064 lp.isDragging = false;
Romain Guy84f296c2009-11-04 15:00:44 -08001065 lp.dropped = true;
Romain Guyd94533d2009-08-17 10:01:15 -07001066 child.requestLayout();
Romain Guyd94533d2009-08-17 10:01:15 -07001067 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07001068 onDragExit();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001069 }
1070
1071 void onDropAborted(View child) {
1072 if (child != null) {
1073 ((LayoutParams) child.getLayoutParams()).isDragging = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001074 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07001075 onDragExit();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001076 }
1077
1078 /**
1079 * Start dragging the specified child
Winson Chungaafa03c2010-06-11 17:34:16 -07001080 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001081 * @param child The child that is being dragged
1082 */
1083 void onDragChild(View child) {
1084 LayoutParams lp = (LayoutParams) child.getLayoutParams();
1085 lp.isDragging = true;
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001086 }
1087
1088 /**
1089 * A drag event has begun over this layout.
1090 * It may have begun over this layout (in which case onDragChild is called first),
1091 * or it may have begun on another layout.
1092 */
1093 void onDragEnter(View dragView) {
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001094 // Fade in the drag indicators
1095 if (mCrosshairsAnimator != null) {
Patrick Dubroy49250ad2010-10-08 15:33:52 -07001096 mCrosshairsAnimator.animateIn();
Patrick Dubroyde7658b2010-09-27 11:15:43 -07001097 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001098 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001099
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001100 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001101 * Computes a bounding rectangle for a range of cells
Winson Chungaafa03c2010-06-11 17:34:16 -07001102 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001103 * @param cellX X coordinate of upper left corner expressed as a cell position
1104 * @param cellY Y coordinate of upper left corner expressed as a cell position
Winson Chungaafa03c2010-06-11 17:34:16 -07001105 * @param cellHSpan Width in cells
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001106 * @param cellVSpan Height in cells
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001107 * @param resultRect Rect into which to put the results
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001108 */
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001109 public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001110 final int cellWidth = mCellWidth;
1111 final int cellHeight = mCellHeight;
1112 final int widthGap = mWidthGap;
1113 final int heightGap = mHeightGap;
Winson Chungaafa03c2010-06-11 17:34:16 -07001114
1115 final int hStartPadding = getLeftPadding();
1116 final int vStartPadding = getTopPadding();
1117
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001118 int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
1119 int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
1120
1121 int x = hStartPadding + cellX * (cellWidth + widthGap);
1122 int y = vStartPadding + cellY * (cellHeight + heightGap);
Winson Chungaafa03c2010-06-11 17:34:16 -07001123
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001124 resultRect.set(x, y, x + width, y + height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001125 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001126
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001127 /**
Winson Chungaafa03c2010-06-11 17:34:16 -07001128 * Computes the required horizontal and vertical cell spans to always
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001129 * fit the given rectangle.
Winson Chungaafa03c2010-06-11 17:34:16 -07001130 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001131 * @param width Width in pixels
1132 * @param height Height in pixels
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -07001133 * @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 -08001134 */
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -07001135 public int[] rectToCell(int width, int height, int[] result) {
Michael Jurka9987a5c2010-10-08 16:58:12 -07001136 return rectToCell(getResources(), width, height, result);
1137 }
1138
1139 public static int[] rectToCell(Resources resources, int width, int height, int[] result) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001140 // Always assume we're working with the smallest span to make sure we
1141 // reserve enough space in both orientations.
Joe Onorato79e56262009-09-21 15:23:04 -04001142 int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
1143 int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001144 int smallerSize = Math.min(actualWidth, actualHeight);
Joe Onorato79e56262009-09-21 15:23:04 -04001145
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001146 // Always round up to next largest cell
1147 int spanX = (width + smallerSize) / smallerSize;
1148 int spanY = (height + smallerSize) / smallerSize;
Joe Onorato79e56262009-09-21 15:23:04 -04001149
Patrick Dubroy8f86ddc2010-07-16 13:55:32 -07001150 if (result == null) {
1151 return new int[] { spanX, spanY };
1152 }
1153 result[0] = spanX;
1154 result[1] = spanY;
1155 return result;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001156 }
1157
1158 /**
1159 * Find the first vacant cell, if there is one.
1160 *
1161 * @param vacant Holds the x and y coordinate of the vacant cell
1162 * @param spanX Horizontal cell span.
1163 * @param spanY Vertical cell span.
Winson Chungaafa03c2010-06-11 17:34:16 -07001164 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001165 * @return True if a vacant cell was found
1166 */
1167 public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001168
Michael Jurka0280c3b2010-09-17 15:00:07 -07001169 return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001170 }
1171
1172 static boolean findVacantCell(int[] vacant, int spanX, int spanY,
1173 int xCount, int yCount, boolean[][] occupied) {
1174
1175 for (int x = 0; x < xCount; x++) {
1176 for (int y = 0; y < yCount; y++) {
1177 boolean available = !occupied[x][y];
1178out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
1179 for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
1180 available = available && !occupied[i][j];
1181 if (!available) break out;
1182 }
1183 }
1184
1185 if (available) {
1186 vacant[0] = x;
1187 vacant[1] = y;
1188 return true;
1189 }
1190 }
1191 }
1192
1193 return false;
1194 }
1195
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001196 /**
1197 * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
1198 */
1199 boolean[] getOccupiedCellsFlattened() {
Adam Cohend22015c2010-07-26 22:02:18 -07001200 final int xCount = mCountX;
1201 final int yCount = mCountY;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001202 final boolean[][] occupied = mOccupied;
1203
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001204 final boolean[] flat = new boolean[xCount * yCount];
1205 for (int y = 0; y < yCount; y++) {
1206 for (int x = 0; x < xCount; x++) {
1207 flat[y * xCount + x] = occupied[x][y];
1208 }
1209 }
1210
1211 return flat;
1212 }
1213
Michael Jurka0280c3b2010-09-17 15:00:07 -07001214 private void clearOccupiedCells() {
1215 for (int x = 0; x < mCountX; x++) {
1216 for (int y = 0; y < mCountY; y++) {
1217 mOccupied[x][y] = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001218 }
1219 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07001220 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001221
Michael Jurka0280c3b2010-09-17 15:00:07 -07001222 public void onMove(View view, int newCellX, int newCellY) {
1223 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1224 markCellsAsUnoccupiedForView(view);
1225 markCellsForView(newCellX, newCellY, lp.cellHSpan, lp.cellVSpan, true);
1226 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001227
Michael Jurka0280c3b2010-09-17 15:00:07 -07001228 private void markCellsAsOccupiedForView(View view) {
Michael Jurkac6ee42e2010-09-30 12:04:50 -07001229 if (view == null || view.getParent() != this) return;
Michael Jurka0280c3b2010-09-17 15:00:07 -07001230 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1231 markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
1232 }
1233
1234 private void markCellsAsUnoccupiedForView(View view) {
Michael Jurkac6ee42e2010-09-30 12:04:50 -07001235 if (view == null || view.getParent() != this) return;
Michael Jurka0280c3b2010-09-17 15:00:07 -07001236 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1237 markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
1238 }
1239
1240 private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean value) {
1241 for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
1242 for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
1243 mOccupied[x][y] = value;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001244 }
1245 }
1246 }
1247
1248 @Override
1249 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
1250 return new CellLayout.LayoutParams(getContext(), attrs);
1251 }
1252
1253 @Override
1254 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1255 return p instanceof CellLayout.LayoutParams;
1256 }
1257
1258 @Override
1259 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1260 return new CellLayout.LayoutParams(p);
1261 }
1262
Winson Chungaafa03c2010-06-11 17:34:16 -07001263 public static class CellLayoutAnimationController extends LayoutAnimationController {
1264 public CellLayoutAnimationController(Animation animation, float delay) {
1265 super(animation, delay);
1266 }
1267
1268 @Override
1269 protected long getDelayForView(View view) {
1270 return (int) (Math.random() * 150);
1271 }
1272 }
1273
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001274 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1275 /**
1276 * Horizontal location of the item in the grid.
1277 */
1278 @ViewDebug.ExportedProperty
1279 public int cellX;
1280
1281 /**
1282 * Vertical location of the item in the grid.
1283 */
1284 @ViewDebug.ExportedProperty
1285 public int cellY;
1286
1287 /**
1288 * Number of cells spanned horizontally by the item.
1289 */
1290 @ViewDebug.ExportedProperty
1291 public int cellHSpan;
1292
1293 /**
1294 * Number of cells spanned vertically by the item.
1295 */
1296 @ViewDebug.ExportedProperty
1297 public int cellVSpan;
Winson Chungaafa03c2010-06-11 17:34:16 -07001298
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001299 /**
1300 * Is this item currently being dragged
1301 */
1302 public boolean isDragging;
1303
1304 // X coordinate of the view in the layout.
1305 @ViewDebug.ExportedProperty
1306 int x;
1307 // Y coordinate of the view in the layout.
1308 @ViewDebug.ExportedProperty
1309 int y;
1310
Romain Guy84f296c2009-11-04 15:00:44 -08001311 boolean dropped;
Romain Guyfcb9e712009-10-02 16:06:52 -07001312
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001313 public LayoutParams(Context c, AttributeSet attrs) {
1314 super(c, attrs);
1315 cellHSpan = 1;
1316 cellVSpan = 1;
1317 }
1318
1319 public LayoutParams(ViewGroup.LayoutParams source) {
1320 super(source);
1321 cellHSpan = 1;
1322 cellVSpan = 1;
1323 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001324
1325 public LayoutParams(LayoutParams source) {
1326 super(source);
1327 this.cellX = source.cellX;
1328 this.cellY = source.cellY;
1329 this.cellHSpan = source.cellHSpan;
1330 this.cellVSpan = source.cellVSpan;
1331 }
1332
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001333 public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
Romain Guy8f19cdd2010-01-08 15:07:00 -08001334 super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001335 this.cellX = cellX;
1336 this.cellY = cellY;
1337 this.cellHSpan = cellHSpan;
1338 this.cellVSpan = cellVSpan;
1339 }
1340
1341 public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
1342 int hStartPadding, int vStartPadding) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001343
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001344 final int myCellHSpan = cellHSpan;
1345 final int myCellVSpan = cellVSpan;
1346 final int myCellX = cellX;
1347 final int myCellY = cellY;
Winson Chungaafa03c2010-06-11 17:34:16 -07001348
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001349 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
1350 leftMargin - rightMargin;
1351 height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
1352 topMargin - bottomMargin;
1353
1354 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
1355 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
1356 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001357
1358 public String toString() {
1359 return "(" + this.cellX + ", " + this.cellY + ")";
1360 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001361 }
1362
Michael Jurka0280c3b2010-09-17 15:00:07 -07001363 // This class stores info for two purposes:
1364 // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY,
1365 // its spanX, spanY, and the screen it is on
1366 // 2. When long clicking on an empty cell in a CellLayout, we save information about the
1367 // cellX and cellY coordinates and which page was clicked. We then set this as a tag on
1368 // the CellLayout that was long clicked
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001369 static final class CellInfo implements ContextMenu.ContextMenuInfo {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001370 View cell;
Michael Jurkaa63c4522010-08-19 13:52:27 -07001371 int cellX = -1;
1372 int cellY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001373 int spanX;
1374 int spanY;
1375 int screen;
1376 boolean valid;
1377
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001378 @Override
1379 public String toString() {
Winson Chungaafa03c2010-06-11 17:34:16 -07001380 return "Cell[view=" + (cell == null ? "null" : cell.getClass())
1381 + ", x=" + cellX + ", y=" + cellY + "]";
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001382 }
1383 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001384}