blob: d65a98cd6960dbbf7ad0245ba6fd92c73e9b5e3d [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
Dianne Hackborn8f573952009-08-10 23:21:09 -070019import android.app.WallpaperManager;
Romain Guy629de3e2010-01-13 12:20:59 -080020import android.appwidget.AppWidgetManager;
21import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080022import android.content.Context;
23import android.content.Intent;
24import android.content.ComponentName;
Romain Guy5c16f3e2010-01-12 17:24:58 -080025import android.content.pm.ProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080026import android.content.res.TypedArray;
Joe Onorato9c1289c2009-08-17 11:03:03 -040027import android.content.pm.PackageManager;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080028import android.graphics.Canvas;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080029import android.graphics.Rect;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070030import android.graphics.drawable.Drawable;
Romain Guy5c16f3e2010-01-12 17:24:58 -080031import android.net.Uri;
Joe Onorato956091b2010-02-19 12:47:40 -080032import android.os.IBinder;
Joe Onorato080d9b62009-11-02 12:01:11 -050033import android.os.Parcelable;
34import android.os.Parcel;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080035import android.util.AttributeSet;
36import android.view.MotionEvent;
37import android.view.VelocityTracker;
38import android.view.View;
39import android.view.ViewConfiguration;
40import android.view.ViewGroup;
41import android.view.ViewParent;
42import android.widget.Scroller;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070043import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080044
45import java.util.ArrayList;
46
Romain Guyedcce092010-03-04 13:03:17 -080047import com.android.launcher.R;
48
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049/**
50 * The workspace is a wide area with a wallpaper and a finite number of screens. Each
51 * screen contains a number of icons, folders or widgets the user can interact with.
52 * A workspace is meant to be used with a fixed width only.
53 */
Joe Onorato85a02a82009-09-08 12:34:22 -070054public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
Romain Guye47f55c2009-11-11 19:21:22 -080055 @SuppressWarnings({"UnusedDeclaration"})
Joe Onorato3a8820b2009-11-10 15:06:42 -080056 private static final String TAG = "Launcher.Workspace";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057 private static final int INVALID_SCREEN = -1;
Jeff Sharkey70864282009-04-07 21:08:40 -070058
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059 /**
60 * The velocity at which a fling gesture will cause us to snap to the next screen
61 */
62 private static final int SNAP_VELOCITY = 1000;
63
Dianne Hackborn8f573952009-08-10 23:21:09 -070064 private final WallpaperManager mWallpaperManager;
65
The Android Open Source Project31dd5032009-03-03 19:32:27 -080066 private int mDefaultScreen;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080067
68 private boolean mFirstLayout = true;
69
70 private int mCurrentScreen;
71 private int mNextScreen = INVALID_SCREEN;
72 private Scroller mScroller;
73 private VelocityTracker mVelocityTracker;
74
75 /**
76 * CellInfo for the cell that is currently being dragged
77 */
78 private CellLayout.CellInfo mDragInfo;
Jeff Sharkey70864282009-04-07 21:08:40 -070079
80 /**
81 * Target drop area calculated during last acceptDrop call.
82 */
83 private int[] mTargetCell = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080084
85 private float mLastMotionX;
86 private float mLastMotionY;
Mike Cleronf8bbd342009-10-23 16:15:16 -070087
The Android Open Source Project31dd5032009-03-03 19:32:27 -080088 private final static int TOUCH_STATE_REST = 0;
89 private final static int TOUCH_STATE_SCROLLING = 1;
90
91 private int mTouchState = TOUCH_STATE_REST;
92
93 private OnLongClickListener mLongClickListener;
94
95 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -080096 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -040097 private DragController mDragController;
Jeff Sharkey70864282009-04-07 21:08:40 -070098
99 /**
100 * Cache of vacant cells, used during drag events and invalidated as needed.
101 */
102 private CellLayout.CellInfo mVacantCache = null;
103
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800104 private int[] mTempCell = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700105 private int[] mTempEstimate = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800106
Joe Onoratoa9c28f62009-09-14 18:38:49 -0400107 private boolean mAllowLongPress = true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108
109 private int mTouchSlop;
Adam Powell3499d4a2010-03-09 19:12:45 -0800110 private int mPagingTouchSlop;
Romain Guya206daa2009-07-06 11:51:18 -0700111 private int mMaximumVelocity;
Adam Powell3499d4a2010-03-09 19:12:45 -0800112
113 private static final int INVALID_POINTER = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800114
Adam Powell3499d4a2010-03-09 19:12:45 -0800115 private int mActivePointerId = INVALID_POINTER;
116
Romain Guy8a73c512009-11-09 19:19:59 -0800117 private Drawable mPreviousIndicator;
118 private Drawable mNextIndicator;
119
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800120 /**
121 * Used to inflate the Workspace from XML.
122 *
123 * @param context The application's context.
124 * @param attrs The attribtues set containing the Workspace's customization values.
125 */
126 public Workspace(Context context, AttributeSet attrs) {
127 this(context, attrs, 0);
128 }
129
130 /**
131 * Used to inflate the Workspace from XML.
132 *
133 * @param context The application's context.
134 * @param attrs The attribtues set containing the Workspace's customization values.
135 * @param defStyle Unused.
136 */
137 public Workspace(Context context, AttributeSet attrs, int defStyle) {
138 super(context, attrs, defStyle);
139
Dianne Hackborn8f573952009-08-10 23:21:09 -0700140 mWallpaperManager = WallpaperManager.getInstance(context);
141
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800142 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
143 mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
144 a.recycle();
145
Joe Onorato0d44e942009-11-16 18:20:51 -0800146 setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800147 initWorkspace();
148 }
149
150 /**
151 * Initializes various states for this workspace.
152 */
153 private void initWorkspace() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800154 Context context = getContext();
155 mScroller = new Scroller(context);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800156 mCurrentScreen = mDefaultScreen;
157 Launcher.setScreen(mCurrentScreen);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800158 LauncherApplication app = (LauncherApplication)context.getApplicationContext();
159 mIconCache = app.getIconCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800160
Romain Guya206daa2009-07-06 11:51:18 -0700161 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
162 mTouchSlop = configuration.getScaledTouchSlop();
Adam Powell3499d4a2010-03-09 19:12:45 -0800163 mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
Romain Guya206daa2009-07-06 11:51:18 -0700164 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800165 }
166
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800167 @Override
168 public void addView(View child, int index, LayoutParams params) {
169 if (!(child instanceof CellLayout)) {
170 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
171 }
172 super.addView(child, index, params);
173 }
174
175 @Override
176 public void addView(View child) {
177 if (!(child instanceof CellLayout)) {
178 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
179 }
180 super.addView(child);
181 }
182
183 @Override
184 public void addView(View child, int index) {
185 if (!(child instanceof CellLayout)) {
186 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
187 }
188 super.addView(child, index);
189 }
190
191 @Override
192 public void addView(View child, int width, int height) {
193 if (!(child instanceof CellLayout)) {
194 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
195 }
196 super.addView(child, width, height);
197 }
198
199 @Override
200 public void addView(View child, LayoutParams params) {
201 if (!(child instanceof CellLayout)) {
202 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
203 }
204 super.addView(child, params);
205 }
206
207 /**
208 * @return The open folder on the current screen, or null if there is none
209 */
210 Folder getOpenFolder() {
211 CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
212 int count = currentScreen.getChildCount();
213 for (int i = 0; i < count; i++) {
214 View child = currentScreen.getChildAt(i);
215 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
216 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
217 return (Folder) child;
218 }
219 }
220 return null;
221 }
222
223 ArrayList<Folder> getOpenFolders() {
224 final int screens = getChildCount();
225 ArrayList<Folder> folders = new ArrayList<Folder>(screens);
226
227 for (int screen = 0; screen < screens; screen++) {
228 CellLayout currentScreen = (CellLayout) getChildAt(screen);
229 int count = currentScreen.getChildCount();
230 for (int i = 0; i < count; i++) {
231 View child = currentScreen.getChildAt(i);
232 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
233 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
234 folders.add((Folder) child);
235 break;
236 }
237 }
238 }
239
240 return folders;
241 }
242
243 boolean isDefaultScreenShowing() {
244 return mCurrentScreen == mDefaultScreen;
245 }
246
247 /**
248 * Returns the index of the currently displayed screen.
249 *
250 * @return The index of the currently displayed screen.
251 */
252 int getCurrentScreen() {
253 return mCurrentScreen;
254 }
255
256 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800257 * Sets the current screen.
258 *
259 * @param currentScreen
260 */
261 void setCurrentScreen(int currentScreen) {
Joe Onoratoc45b1682010-01-11 18:48:40 -0500262 if (!mScroller.isFinished()) mScroller.abortAnimation();
Romain Guy4c58c482009-05-12 17:35:41 -0700263 clearVacantCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800264 mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
Romain Guy483543f2010-01-28 14:06:31 -0800265 mPreviousIndicator.setLevel(mCurrentScreen);
266 mNextIndicator.setLevel(mCurrentScreen);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800267 scrollTo(mCurrentScreen * getWidth(), 0);
Romain Guyed2390e2010-02-18 00:22:58 +0100268 updateWallpaperOffset();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800269 invalidate();
270 }
271
272 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800273 * Adds the specified child in the current screen. The position and dimension of
274 * the child are defined by x, y, spanX and spanY.
275 *
276 * @param child The child to add in one of the workspace's screens.
277 * @param x The X position of the child in the screen's grid.
278 * @param y The Y position of the child in the screen's grid.
279 * @param spanX The number of cells spanned horizontally by the child.
280 * @param spanY The number of cells spanned vertically by the child.
281 */
282 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
283 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
284 }
285
286 /**
287 * Adds the specified child in the current screen. The position and dimension of
288 * the child are defined by x, y, spanX and spanY.
289 *
290 * @param child The child to add in one of the workspace's screens.
291 * @param x The X position of the child in the screen's grid.
292 * @param y The Y position of the child in the screen's grid.
293 * @param spanX The number of cells spanned horizontally by the child.
294 * @param spanY The number of cells spanned vertically by the child.
295 * @param insert When true, the child is inserted at the beginning of the children list.
296 */
297 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
298 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
299 }
300
301 /**
302 * Adds the specified child in the specified screen. The position and dimension of
303 * the child are defined by x, y, spanX and spanY.
304 *
305 * @param child The child to add in one of the workspace's screens.
306 * @param screen The screen in which to add the child.
307 * @param x The X position of the child in the screen's grid.
308 * @param y The Y position of the child in the screen's grid.
309 * @param spanX The number of cells spanned horizontally by the child.
310 * @param spanY The number of cells spanned vertically by the child.
311 */
312 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
313 addInScreen(child, screen, x, y, spanX, spanY, false);
314 }
315
316 /**
317 * Adds the specified child in the specified screen. The position and dimension of
318 * the child are defined by x, y, spanX and spanY.
319 *
320 * @param child The child to add in one of the workspace's screens.
321 * @param screen The screen in which to add the child.
322 * @param x The X position of the child in the screen's grid.
323 * @param y The Y position of the child in the screen's grid.
324 * @param spanX The number of cells spanned horizontally by the child.
325 * @param spanY The number of cells spanned vertically by the child.
326 * @param insert When true, the child is inserted at the beginning of the children list.
327 */
328 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
329 if (screen < 0 || screen >= getChildCount()) {
330 throw new IllegalStateException("The screen must be >= 0 and < " + getChildCount());
331 }
332
Romain Guy4c58c482009-05-12 17:35:41 -0700333 clearVacantCache();
334
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800335 final CellLayout group = (CellLayout) getChildAt(screen);
336 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
337 if (lp == null) {
338 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
339 } else {
340 lp.cellX = x;
341 lp.cellY = y;
342 lp.cellHSpan = spanX;
343 lp.cellVSpan = spanY;
344 }
345 group.addView(child, insert ? 0 : -1, lp);
346 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -0800347 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800348 child.setOnLongClickListener(mLongClickListener);
349 }
Joe Onorato00acb122009-08-04 16:04:30 -0400350 if (child instanceof DropTarget) {
351 mDragController.addDropTarget((DropTarget)child);
352 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800353 }
354
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800355 CellLayout.CellInfo findAllVacantCells(boolean[] occupied) {
356 CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
357 if (group != null) {
Jeff Sharkey70864282009-04-07 21:08:40 -0700358 return group.findAllVacantCells(occupied, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800359 }
360 return null;
361 }
362
Romain Guy4c58c482009-05-12 17:35:41 -0700363 private void clearVacantCache() {
364 if (mVacantCache != null) {
365 mVacantCache.clearVacantCells();
366 mVacantCache = null;
367 }
368 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800369
370 /**
371 * Registers the specified listener on each screen contained in this workspace.
372 *
373 * @param l The listener used to respond to long clicks.
374 */
375 @Override
376 public void setOnLongClickListener(OnLongClickListener l) {
377 mLongClickListener = l;
378 final int count = getChildCount();
379 for (int i = 0; i < count; i++) {
380 getChildAt(i).setOnLongClickListener(l);
381 }
382 }
383
Dianne Hackborn8f573952009-08-10 23:21:09 -0700384 private void updateWallpaperOffset() {
Romain Guy798300c2009-08-11 14:43:29 -0700385 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
386 }
387
388 private void updateWallpaperOffset(int scrollRange) {
Joe Onorato956091b2010-02-19 12:47:40 -0800389 IBinder token = getWindowToken();
390 if (token != null) {
391 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
392 mWallpaperManager.setWallpaperOffsets(getWindowToken(), mScrollX/(float)scrollRange, 0);
393 }
Dianne Hackborn8f573952009-08-10 23:21:09 -0700394 }
395
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800396 @Override
397 public void computeScroll() {
398 if (mScroller.computeScrollOffset()) {
399 mScrollX = mScroller.getCurrX();
400 mScrollY = mScroller.getCurrY();
Dianne Hackborn8f573952009-08-10 23:21:09 -0700401 updateWallpaperOffset();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800402 postInvalidate();
403 } else if (mNextScreen != INVALID_SCREEN) {
404 mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
Romain Guy8a73c512009-11-09 19:19:59 -0800405 mPreviousIndicator.setLevel(mCurrentScreen);
406 mNextIndicator.setLevel(mCurrentScreen);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800407 Launcher.setScreen(mCurrentScreen);
408 mNextScreen = INVALID_SCREEN;
409 clearChildrenCache();
410 }
411 }
412
413 @Override
414 protected void dispatchDraw(Canvas canvas) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700415 boolean restore = false;
Joe Onoratoe77c08d2009-08-01 00:01:20 -0700416 int restoreCount = 0;
417
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800418 // ViewGroup.dispatchDraw() supports many features we don't need:
419 // clip to padding, layout animation, animation listener, disappearing
420 // children, etc. The following implementation attempts to fast-track
421 // the drawing dispatch by drawing only what we know needs to be drawn.
422
Romain Guyf8e6a802009-12-07 17:48:02 -0800423 boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800424 // If we are not scrolling or flinging, draw only the current screen
425 if (fastDraw) {
426 drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
427 } else {
428 final long drawingTime = getDrawingTime();
429 // If we are flinging, draw only the current screen and the target screen
430 if (mNextScreen >= 0 && mNextScreen < getChildCount() &&
431 Math.abs(mCurrentScreen - mNextScreen) == 1) {
432 drawChild(canvas, getChildAt(mCurrentScreen), drawingTime);
433 drawChild(canvas, getChildAt(mNextScreen), drawingTime);
434 } else {
435 // If we are scrolling, draw all of our children
436 final int count = getChildCount();
437 for (int i = 0; i < count; i++) {
438 drawChild(canvas, getChildAt(i), drawingTime);
439 }
440 }
441 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700442
443 if (restore) {
Joe Onoratoe77c08d2009-08-01 00:01:20 -0700444 canvas.restoreToCount(restoreCount);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700445 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800446 }
447
Joe Onorato00acb122009-08-04 16:04:30 -0400448 protected void onAttachedToWindow() {
449 super.onAttachedToWindow();
Joe Onorato956091b2010-02-19 12:47:40 -0800450 computeScroll();
Joe Onorato00acb122009-08-04 16:04:30 -0400451 mDragController.setWindowToken(getWindowToken());
452 }
453
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800454 @Override
455 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
456 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
457
458 final int width = MeasureSpec.getSize(widthMeasureSpec);
459 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
460 if (widthMode != MeasureSpec.EXACTLY) {
461 throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
462 }
463
464 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
465 if (heightMode != MeasureSpec.EXACTLY) {
466 throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
467 }
468
469 // The children are given the same width and height as the workspace
470 final int count = getChildCount();
471 for (int i = 0; i < count; i++) {
472 getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
473 }
474
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800475
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800476 if (mFirstLayout) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800477 setHorizontalScrollBarEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800478 scrollTo(mCurrentScreen * width, 0);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800479 setHorizontalScrollBarEnabled(true);
Romain Guy798300c2009-08-11 14:43:29 -0700480 updateWallpaperOffset(width * (getChildCount() - 1));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800481 mFirstLayout = false;
482 }
483 }
484
485 @Override
486 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
487 int childLeft = 0;
488
489 final int count = getChildCount();
490 for (int i = 0; i < count; i++) {
491 final View child = getChildAt(i);
492 if (child.getVisibility() != View.GONE) {
493 final int childWidth = child.getMeasuredWidth();
494 child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
495 childLeft += childWidth;
496 }
497 }
498 }
499
500 @Override
501 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
502 int screen = indexOfChild(child);
503 if (screen != mCurrentScreen || !mScroller.isFinished()) {
504 if (!mLauncher.isWorkspaceLocked()) {
505 snapToScreen(screen);
506 }
507 return true;
508 }
509 return false;
510 }
511
512 @Override
513 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -0400514 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800515 final Folder openFolder = getOpenFolder();
516 if (openFolder != null) {
517 return openFolder.requestFocus(direction, previouslyFocusedRect);
518 } else {
519 int focusableScreen;
520 if (mNextScreen != INVALID_SCREEN) {
521 focusableScreen = mNextScreen;
522 } else {
523 focusableScreen = mCurrentScreen;
524 }
525 getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
526 }
527 }
528 return false;
529 }
530
531 @Override
532 public boolean dispatchUnhandledMove(View focused, int direction) {
533 if (direction == View.FOCUS_LEFT) {
534 if (getCurrentScreen() > 0) {
535 snapToScreen(getCurrentScreen() - 1);
536 return true;
537 }
538 } else if (direction == View.FOCUS_RIGHT) {
539 if (getCurrentScreen() < getChildCount() - 1) {
540 snapToScreen(getCurrentScreen() + 1);
541 return true;
542 }
543 }
544 return super.dispatchUnhandledMove(focused, direction);
545 }
546
547 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -0700548 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -0400549 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800550 final Folder openFolder = getOpenFolder();
551 if (openFolder == null) {
552 getChildAt(mCurrentScreen).addFocusables(views, direction);
553 if (direction == View.FOCUS_LEFT) {
554 if (mCurrentScreen > 0) {
555 getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
556 }
557 } else if (direction == View.FOCUS_RIGHT){
558 if (mCurrentScreen < getChildCount() - 1) {
559 getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
560 }
561 }
562 } else {
563 openFolder.addFocusables(views, direction);
564 }
565 }
566 }
567
568 @Override
Joe Onorato7bb17492009-09-24 17:51:01 -0700569 public boolean dispatchTouchEvent(MotionEvent ev) {
570 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
571 if (mLauncher.isWorkspaceLocked() || mLauncher.isAllAppsVisible()) {
572 return false;
573 }
574 }
575 return super.dispatchTouchEvent(ev);
576 }
577
578 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800579 public boolean onInterceptTouchEvent(MotionEvent ev) {
Joe Onorato68ba5ca2009-11-12 14:23:43 -0800580 final boolean workspaceLocked = mLauncher.isWorkspaceLocked();
581 final boolean allAppsVisible = mLauncher.isAllAppsVisible();
Joe Onorato68ba5ca2009-11-12 14:23:43 -0800582 if (workspaceLocked || allAppsVisible) {
Joe Onorato7c312c12009-08-13 21:36:53 -0700583 return false; // We don't want the events. Let them fall through to the all apps view.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800584 }
585
586 /*
587 * This method JUST determines whether we want to intercept the motion.
588 * If we return true, onTouchEvent will be called and we do the actual
589 * scrolling there.
590 */
591
592 /*
593 * Shortcut the most recurring case: the user is in the dragging
594 * state and he is moving his finger. We want to intercept this
595 * motion.
596 */
597 final int action = ev.getAction();
598 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
599 return true;
600 }
601
Adam Powell3499d4a2010-03-09 19:12:45 -0800602 switch (action & MotionEvent.ACTION_MASK) {
603 case MotionEvent.ACTION_MOVE: {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800604 /*
605 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
606 * whether the user has moved far enough from his original down touch.
607 */
608
609 /*
610 * Locally do absolute value. mLastMotionX is set to the y value
611 * of the down event.
612 */
Adam Powell3499d4a2010-03-09 19:12:45 -0800613 final int pointerIndex = ev.findPointerIndex(mActivePointerId);
614 final float x = ev.getX(pointerIndex);
615 final float y = ev.getY(pointerIndex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800616 final int xDiff = (int) Math.abs(x - mLastMotionX);
617 final int yDiff = (int) Math.abs(y - mLastMotionY);
618
Adam Powellc36f2452010-03-10 11:08:17 -0800619 final int touchSlop = mTouchSlop;
620 boolean xPaged = xDiff > mPagingTouchSlop;
621 boolean xMoved = xDiff > touchSlop;
622 boolean yMoved = yDiff > touchSlop;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800623
624 if (xMoved || yMoved) {
625
Adam Powellc36f2452010-03-10 11:08:17 -0800626 if (xPaged) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800627 // Scroll if the user moved far enough along the X axis
628 mTouchState = TOUCH_STATE_SCROLLING;
Adam Powell3499d4a2010-03-09 19:12:45 -0800629 mLastMotionX = x;
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800630 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800631 }
632 // Either way, cancel any pending longpress
633 if (mAllowLongPress) {
634 mAllowLongPress = false;
635 // Try canceling the long press. It could also have been scheduled
636 // by a distant descendant, so use the mAllowLongPress flag to block
637 // everything
638 final View currentScreen = getChildAt(mCurrentScreen);
639 currentScreen.cancelLongPress();
640 }
641 }
642 break;
Adam Powell3499d4a2010-03-09 19:12:45 -0800643 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800644
Adam Powell3499d4a2010-03-09 19:12:45 -0800645 case MotionEvent.ACTION_DOWN: {
646 final float x = ev.getX();
647 final float y = ev.getY();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800648 // Remember location of down touch
649 mLastMotionX = x;
650 mLastMotionY = y;
Adam Powell3499d4a2010-03-09 19:12:45 -0800651 mActivePointerId = ev.getPointerId(0);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800652 mAllowLongPress = true;
653
654 /*
655 * If being flinged and user touches the screen, initiate drag;
656 * otherwise don't. mScroller.isFinished should be false when
657 * being flinged.
658 */
659 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
660 break;
Adam Powell3499d4a2010-03-09 19:12:45 -0800661 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800662
663 case MotionEvent.ACTION_CANCEL:
664 case MotionEvent.ACTION_UP:
Mike Cleronf8bbd342009-10-23 16:15:16 -0700665
666 if (mTouchState != TOUCH_STATE_SCROLLING) {
Mike Cleronf8bbd342009-10-23 16:15:16 -0700667 final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
668 if (!currentScreen.lastDownOnOccupiedCell()) {
Romain Guy06762ab2010-01-25 16:51:08 -0800669 getLocationOnScreen(mTempCell);
Mike Cleronf8bbd342009-10-23 16:15:16 -0700670 // Send a tap to the wallpaper if the last down was on empty space
Adam Powell3499d4a2010-03-09 19:12:45 -0800671 final int pointerIndex = ev.findPointerIndex(mActivePointerId);
Mike Cleronf8bbd342009-10-23 16:15:16 -0700672 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
Romain Guy06762ab2010-01-25 16:51:08 -0800673 "android.wallpaper.tap",
Adam Powell3499d4a2010-03-09 19:12:45 -0800674 mTempCell[0] + (int) ev.getX(pointerIndex),
675 mTempCell[1] + (int) ev.getY(pointerIndex), 0, null);
Mike Cleronf8bbd342009-10-23 16:15:16 -0700676 }
677 }
678
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800679 // Release the drag
680 clearChildrenCache();
681 mTouchState = TOUCH_STATE_REST;
Adam Powell3499d4a2010-03-09 19:12:45 -0800682 mActivePointerId = INVALID_POINTER;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800683 mAllowLongPress = false;
Mike Cleronf8bbd342009-10-23 16:15:16 -0700684
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800685 break;
Adam Powell3499d4a2010-03-09 19:12:45 -0800686
687 case MotionEvent.ACTION_POINTER_UP:
688 onSecondaryPointerUp(ev);
689 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800690 }
691
692 /*
693 * The only time we want to intercept motion events is if we are in the
694 * drag mode.
695 */
696 return mTouchState != TOUCH_STATE_REST;
697 }
Adam Powell3499d4a2010-03-09 19:12:45 -0800698
699 private void onSecondaryPointerUp(MotionEvent ev) {
700 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
701 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
702 final int pointerId = ev.getPointerId(pointerIndex);
703 if (pointerId == mActivePointerId) {
704 // This was our active pointer going up. Choose a new
705 // active pointer and adjust accordingly.
706 // TODO: Make this decision more intelligent.
707 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
708 mLastMotionX = ev.getX(newPointerIndex);
709 mLastMotionY = ev.getY(newPointerIndex);
710 mActivePointerId = ev.getPointerId(newPointerIndex);
711 if (mVelocityTracker != null) {
712 mVelocityTracker.clear();
713 }
714 }
715 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800716
Joe Onorato3a8820b2009-11-10 15:06:42 -0800717 /**
718 * If one of our descendant views decides that it could be focused now, only
719 * pass that along if it's on the current screen.
720 *
721 * This happens when live folders requery, and if they're off screen, they
722 * end up calling requestFocus, which pulls it on screen.
723 */
724 @Override
725 public void focusableViewAvailable(View focused) {
726 View current = getChildAt(mCurrentScreen);
727 View v = focused;
728 while (true) {
729 if (v == current) {
730 super.focusableViewAvailable(focused);
731 return;
732 }
733 if (v == this) {
734 return;
735 }
736 ViewParent parent = v.getParent();
737 if (parent instanceof View) {
738 v = (View)v.getParent();
739 } else {
740 return;
741 }
742 }
743 }
744
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800745 void enableChildrenCache(int fromScreen, int toScreen) {
746 if (fromScreen > toScreen) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800747 fromScreen = toScreen;
748 toScreen = fromScreen;
749 }
750
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800751 final int count = getChildCount();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800752
753 fromScreen = Math.max(fromScreen, 0);
754 toScreen = Math.min(toScreen, count - 1);
755
756 for (int i = fromScreen; i <= toScreen; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800757 final CellLayout layout = (CellLayout) getChildAt(i);
758 layout.setChildrenDrawnWithCacheEnabled(true);
759 layout.setChildrenDrawingCacheEnabled(true);
760 }
761 }
762
763 void clearChildrenCache() {
764 final int count = getChildCount();
765 for (int i = 0; i < count; i++) {
766 final CellLayout layout = (CellLayout) getChildAt(i);
767 layout.setChildrenDrawnWithCacheEnabled(false);
768 }
769 }
770
771 @Override
772 public boolean onTouchEvent(MotionEvent ev) {
Mike Cleronf8bbd342009-10-23 16:15:16 -0700773
Joe Onorato2bc6b7c2009-10-01 14:08:30 -0700774 if (mLauncher.isWorkspaceLocked()) {
775 return false; // We don't want the events. Let them fall through to the all apps view.
776 }
777 if (mLauncher.isAllAppsVisible()) {
778 // Cancel any scrolling that is in progress.
779 if (!mScroller.isFinished()) {
780 mScroller.abortAnimation();
781 }
782 snapToScreen(mCurrentScreen);
Joe Onorato7c312c12009-08-13 21:36:53 -0700783 return false; // We don't want the events. Let them fall through to the all apps view.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800784 }
785
786 if (mVelocityTracker == null) {
787 mVelocityTracker = VelocityTracker.obtain();
788 }
789 mVelocityTracker.addMovement(ev);
790
791 final int action = ev.getAction();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800792
Adam Powell3499d4a2010-03-09 19:12:45 -0800793 switch (action & MotionEvent.ACTION_MASK) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800794 case MotionEvent.ACTION_DOWN:
795 /*
796 * If being flinged and user touches, stop the fling. isFinished
797 * will be false if being flinged.
798 */
799 if (!mScroller.isFinished()) {
800 mScroller.abortAnimation();
801 }
802
803 // Remember where the motion event started
Adam Powell3499d4a2010-03-09 19:12:45 -0800804 mLastMotionX = ev.getX();
805 mActivePointerId = ev.getPointerId(0);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800806 break;
807 case MotionEvent.ACTION_MOVE:
808 if (mTouchState == TOUCH_STATE_SCROLLING) {
809 // Scroll to follow the motion event
Adam Powell3499d4a2010-03-09 19:12:45 -0800810 final int pointerIndex = ev.findPointerIndex(mActivePointerId);
811 final float x = ev.getX(pointerIndex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800812 final int deltaX = (int) (mLastMotionX - x);
813 mLastMotionX = x;
814
815 if (deltaX < 0) {
816 if (mScrollX > 0) {
817 scrollBy(Math.max(-mScrollX, deltaX), 0);
Dianne Hackborn8f573952009-08-10 23:21:09 -0700818 updateWallpaperOffset();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800819 }
820 } else if (deltaX > 0) {
821 final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -
822 mScrollX - getWidth();
823 if (availableToScroll > 0) {
824 scrollBy(Math.min(availableToScroll, deltaX), 0);
Dianne Hackborn8f573952009-08-10 23:21:09 -0700825 updateWallpaperOffset();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800826 }
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800827 } else {
828 awakenScrollBars();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800829 }
830 }
831 break;
832 case MotionEvent.ACTION_UP:
833 if (mTouchState == TOUCH_STATE_SCROLLING) {
834 final VelocityTracker velocityTracker = mVelocityTracker;
Romain Guya206daa2009-07-06 11:51:18 -0700835 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
Adam Powell3499d4a2010-03-09 19:12:45 -0800836 final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
837
838 final int screenWidth = getWidth();
839 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
840
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800841 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
842 // Fling hard enough to move left
Adam Powell3499d4a2010-03-09 19:12:45 -0800843 snapToScreen(Math.min(whichScreen, mCurrentScreen - 1));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800844 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
845 // Fling hard enough to move right
Adam Powell3499d4a2010-03-09 19:12:45 -0800846 snapToScreen(Math.max(whichScreen, mCurrentScreen + 1));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800847 } else {
848 snapToDestination();
849 }
850
851 if (mVelocityTracker != null) {
852 mVelocityTracker.recycle();
853 mVelocityTracker = null;
854 }
855 }
856 mTouchState = TOUCH_STATE_REST;
Adam Powell3499d4a2010-03-09 19:12:45 -0800857 mActivePointerId = INVALID_POINTER;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800858 break;
859 case MotionEvent.ACTION_CANCEL:
860 mTouchState = TOUCH_STATE_REST;
Adam Powell3499d4a2010-03-09 19:12:45 -0800861 mActivePointerId = INVALID_POINTER;
862 break;
863 case MotionEvent.ACTION_POINTER_UP:
864 onSecondaryPointerUp(ev);
865 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800866 }
867
868 return true;
869 }
870
871 private void snapToDestination() {
872 final int screenWidth = getWidth();
873 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
874
875 snapToScreen(whichScreen);
876 }
877
878 void snapToScreen(int whichScreen) {
Romain Guye47f55c2009-11-11 19:21:22 -0800879 //if (!mScroller.isFinished()) return;
Romain Guy82d94d92009-05-06 17:43:13 -0700880
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800881 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800882
883 clearVacantCache();
884 enableChildrenCache(mCurrentScreen, whichScreen);
885
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800886 final int screenDelta = Math.abs(whichScreen - mCurrentScreen);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800887
888 mNextScreen = whichScreen;
Romain Guye47f55c2009-11-11 19:21:22 -0800889
890 mPreviousIndicator.setLevel(mNextScreen);
891 mNextIndicator.setLevel(mNextScreen);
Romain Guyed2390e2010-02-18 00:22:58 +0100892
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800893 View focusedChild = getFocusedChild();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800894 if (focusedChild != null && screenDelta != 0 && focusedChild == getChildAt(mCurrentScreen)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800895 focusedChild.clearFocus();
896 }
897
898 final int newX = whichScreen * getWidth();
899 final int delta = newX - mScrollX;
Adam Powell3499d4a2010-03-09 19:12:45 -0800900 final int duration = screenDelta != 0 ? screenDelta * 300 : 300;
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800901 awakenScrollBars(duration);
Romain Guyf8e6a802009-12-07 17:48:02 -0800902
Romain Guyf8e6a802009-12-07 17:48:02 -0800903 if (!mScroller.isFinished()) mScroller.abortAnimation();
Joe Onoratoc45b1682010-01-11 18:48:40 -0500904 mScroller.startScroll(mScrollX, 0, delta, 0, duration);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800905 invalidate();
906 }
907
908 void startDrag(CellLayout.CellInfo cellInfo) {
909 View child = cellInfo.cell;
910
911 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +0000912 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800913 return;
914 }
915
916 mDragInfo = cellInfo;
917 mDragInfo.screen = mCurrentScreen;
918
919 CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
920
921 current.onDragChild(child);
Joe Onorato00acb122009-08-04 16:04:30 -0400922 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800923 invalidate();
924 }
925
926 @Override
927 protected Parcelable onSaveInstanceState() {
928 final SavedState state = new SavedState(super.onSaveInstanceState());
929 state.currentScreen = mCurrentScreen;
930 return state;
931 }
932
933 @Override
934 protected void onRestoreInstanceState(Parcelable state) {
935 SavedState savedState = (SavedState) state;
936 super.onRestoreInstanceState(savedState.getSuperState());
937 if (savedState.currentScreen != -1) {
938 mCurrentScreen = savedState.currentScreen;
939 Launcher.setScreen(mCurrentScreen);
940 }
941 }
942
Joe Onorato0589f0f2010-02-08 13:44:00 -0800943 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800944 addApplicationShortcut(info, cellInfo, false);
945 }
946
Joe Onorato0589f0f2010-02-08 13:44:00 -0800947 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800948 boolean insertAtFirst) {
949 final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
950 final int[] result = new int[2];
951
952 layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
953 onDropExternal(result[0], result[1], info, layout, insertAtFirst);
954 }
955
Joe Onorato00acb122009-08-04 16:04:30 -0400956 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
957 DragView dragView, Object dragInfo) {
Jeff Sharkey70864282009-04-07 21:08:40 -0700958 final CellLayout cellLayout = getCurrentDropLayout();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800959 if (source != this) {
960 onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout);
961 } else {
962 // Move internally
963 if (mDragInfo != null) {
964 final View cell = mDragInfo.cell;
Romain Guy52d9cb32009-09-11 17:29:51 -0700965 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
966 if (index != mDragInfo.screen) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800967 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
968 originalCellLayout.removeView(cell);
969 cellLayout.addView(cell);
970 }
Romain Guy263e0192009-05-11 11:50:46 -0700971 mTargetCell = estimateDropCell(x - xOffset, y - yOffset,
Jeff Sharkey70864282009-04-07 21:08:40 -0700972 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell);
973 cellLayout.onDropChild(cell, mTargetCell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800974
Romain Guy84f296c2009-11-04 15:00:44 -0800975 final ItemInfo info = (ItemInfo) cell.getTag();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800976 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
977 LauncherModel.moveItemInDatabase(mLauncher, info,
Romain Guy52d9cb32009-09-11 17:29:51 -0700978 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, lp.cellX, lp.cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800979 }
980 }
981 }
982
983 public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
Joe Onorato00acb122009-08-04 16:04:30 -0400984 DragView dragView, Object dragInfo) {
Romain Guy4c58c482009-05-12 17:35:41 -0700985 clearVacantCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800986 }
987
988 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
Joe Onorato00acb122009-08-04 16:04:30 -0400989 DragView dragView, Object dragInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800990 }
991
992 public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
Joe Onorato00acb122009-08-04 16:04:30 -0400993 DragView dragView, Object dragInfo) {
Romain Guy4c58c482009-05-12 17:35:41 -0700994 clearVacantCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800995 }
996
997 private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) {
998 onDropExternal(x, y, dragInfo, cellLayout, false);
999 }
1000
1001 private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout,
1002 boolean insertAtFirst) {
1003 // Drag from somewhere else
1004 ItemInfo info = (ItemInfo) dragInfo;
1005
1006 View view;
1007
1008 switch (info.itemType) {
1009 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1010 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Joe Onoratoe48e7c12010-02-19 13:10:40 -08001011 if (info.container == NO_ID && info instanceof ApplicationInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001012 // Came from all apps -- make a copy
Joe Onorato59d52452010-02-16 15:55:38 -05001013 info = new ShortcutInfo((ApplicationInfo)info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001014 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001015 view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo)info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001016 break;
1017 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1018 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
1019 (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info));
1020 break;
1021 default:
1022 throw new IllegalStateException("Unknown item type: " + info.itemType);
1023 }
1024
1025 cellLayout.addView(view, insertAtFirst ? 0 : -1);
Joe Onorato0d44e942009-11-16 18:20:51 -08001026 view.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001027 view.setOnLongClickListener(mLongClickListener);
Joe Onorato00acb122009-08-04 16:04:30 -04001028 if (view instanceof DropTarget) {
Romain Guy207e40e2009-09-29 16:19:19 -07001029 mDragController.addDropTarget((DropTarget) view);
Joe Onorato00acb122009-08-04 16:04:30 -04001030 }
1031
Romain Guy263e0192009-05-11 11:50:46 -07001032 mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
Jeff Sharkey70864282009-04-07 21:08:40 -07001033 cellLayout.onDropChild(view, mTargetCell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001034 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1035
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001036 LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1037 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY);
1038 }
Jeff Sharkey70864282009-04-07 21:08:40 -07001039
1040 /**
1041 * Return the current {@link CellLayout}, correctly picking the destination
1042 * screen while a scroll is in progress.
1043 */
1044 private CellLayout getCurrentDropLayout() {
1045 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1046 return (CellLayout) getChildAt(index);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001047 }
1048
Jeff Sharkey70864282009-04-07 21:08:40 -07001049 /**
1050 * {@inheritDoc}
1051 */
1052 public boolean acceptDrop(DragSource source, int x, int y,
Joe Onorato00acb122009-08-04 16:04:30 -04001053 int xOffset, int yOffset, DragView dragView, Object dragInfo) {
Romain Guy4c58c482009-05-12 17:35:41 -07001054 final CellLayout layout = getCurrentDropLayout();
1055 final CellLayout.CellInfo cellInfo = mDragInfo;
1056 final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1057 final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1058
1059 if (mVacantCache == null) {
1060 final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1061 mVacantCache = layout.findAllVacantCells(null, ignoreView);
1062 }
1063
1064 return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false);
Jeff Sharkey70864282009-04-07 21:08:40 -07001065 }
1066
1067 /**
1068 * {@inheritDoc}
1069 */
1070 public Rect estimateDropLocation(DragSource source, int x, int y,
Joe Onorato00acb122009-08-04 16:04:30 -04001071 int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
Jeff Sharkey70864282009-04-07 21:08:40 -07001072 final CellLayout layout = getCurrentDropLayout();
1073
1074 final CellLayout.CellInfo cellInfo = mDragInfo;
1075 final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1076 final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1077 final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1078
1079 final Rect location = recycle != null ? recycle : new Rect();
1080
1081 // Find drop cell and convert into rectangle
Romain Guy263e0192009-05-11 11:50:46 -07001082 int[] dropCell = estimateDropCell(x - xOffset, y - yOffset,
Jeff Sharkey70864282009-04-07 21:08:40 -07001083 spanX, spanY, ignoreView, layout, mTempCell);
1084
1085 if (dropCell == null) {
1086 return null;
1087 }
1088
1089 layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
1090 location.left = mTempEstimate[0];
1091 location.top = mTempEstimate[1];
1092
1093 layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
1094 location.right = mTempEstimate[0];
1095 location.bottom = mTempEstimate[1];
1096
1097 return location;
1098 }
1099
1100 /**
1101 * Calculate the nearest cell where the given object would be dropped.
1102 */
Romain Guy263e0192009-05-11 11:50:46 -07001103 private int[] estimateDropCell(int pixelX, int pixelY,
Jeff Sharkey70864282009-04-07 21:08:40 -07001104 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1105 // Create vacant cell cache if none exists
1106 if (mVacantCache == null) {
1107 mVacantCache = layout.findAllVacantCells(null, ignoreView);
1108 }
1109
1110 // Find the best target drop location
1111 return layout.findNearestVacantArea(pixelX, pixelY,
1112 spanX, spanY, mVacantCache, recycle);
1113 }
1114
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001115 void setLauncher(Launcher launcher) {
1116 mLauncher = launcher;
1117 }
1118
Joe Onorato00acb122009-08-04 16:04:30 -04001119 public void setDragController(DragController dragController) {
1120 mDragController = dragController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001121 }
1122
1123 public void onDropCompleted(View target, boolean success) {
Romain Guy207e40e2009-09-29 16:19:19 -07001124 clearVacantCache();
1125
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001126 if (success){
1127 if (target != this && mDragInfo != null) {
1128 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1129 cellLayout.removeView(mDragInfo.cell);
Joe Onorato00acb122009-08-04 16:04:30 -04001130 if (mDragInfo.cell instanceof DropTarget) {
1131 mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1132 }
Romain Guy207e40e2009-09-29 16:19:19 -07001133 //final Object tag = mDragInfo.cell.getTag();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001134 }
1135 } else {
1136 if (mDragInfo != null) {
1137 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1138 cellLayout.onDropAborted(mDragInfo.cell);
1139 }
1140 }
1141
1142 mDragInfo = null;
1143 }
1144
1145 public void scrollLeft() {
Romain Guy4c58c482009-05-12 17:35:41 -07001146 clearVacantCache();
Romain Guyf8e6a802009-12-07 17:48:02 -08001147 if (mScroller.isFinished()) {
1148 if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);
1149 } else {
1150 if (mNextScreen > 0) snapToScreen(mNextScreen - 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001151 }
1152 }
1153
1154 public void scrollRight() {
Romain Guy4c58c482009-05-12 17:35:41 -07001155 clearVacantCache();
Romain Guyf8e6a802009-12-07 17:48:02 -08001156 if (mScroller.isFinished()) {
1157 if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);
1158 } else {
1159 if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001160 }
1161 }
1162
1163 public int getScreenForView(View v) {
1164 int result = -1;
1165 if (v != null) {
1166 ViewParent vp = v.getParent();
1167 int count = getChildCount();
1168 for (int i = 0; i < count; i++) {
1169 if (vp == getChildAt(i)) {
1170 return i;
1171 }
1172 }
1173 }
1174 return result;
1175 }
Karl Rosaen138a0412009-04-23 19:00:21 -07001176
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001177 public Folder getFolderForTag(Object tag) {
1178 int screenCount = getChildCount();
1179 for (int screen = 0; screen < screenCount; screen++) {
1180 CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1181 int count = currentScreen.getChildCount();
1182 for (int i = 0; i < count; i++) {
1183 View child = currentScreen.getChildAt(i);
1184 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1185 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1186 Folder f = (Folder) child;
1187 if (f.getInfo() == tag) {
1188 return f;
1189 }
1190 }
1191 }
1192 }
1193 return null;
1194 }
1195
1196 public View getViewForTag(Object tag) {
1197 int screenCount = getChildCount();
1198 for (int screen = 0; screen < screenCount; screen++) {
1199 CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1200 int count = currentScreen.getChildCount();
1201 for (int i = 0; i < count; i++) {
1202 View child = currentScreen.getChildAt(i);
1203 if (child.getTag() == tag) {
1204 return child;
1205 }
1206 }
1207 }
1208 return null;
1209 }
1210
1211 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001212 * @return True is long presses are still allowed for the current touch
1213 */
1214 public boolean allowLongPress() {
1215 return mAllowLongPress;
1216 }
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001217
1218 /**
1219 * Set true to allow long-press events to be triggered, usually checked by
1220 * {@link Launcher} to accept or block dpad-initiated long-presses.
1221 */
1222 public void setAllowLongPress(boolean allowLongPress) {
1223 mAllowLongPress = allowLongPress;
1224 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001225
Romain Guy629de3e2010-01-13 12:20:59 -08001226 void removeItemsForPackage(final String packageName) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001227 final int count = getChildCount();
Romain Guy5c16f3e2010-01-12 17:24:58 -08001228 final PackageManager manager = getContext().getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -08001229 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
Romain Guy574d20e2009-06-01 15:34:04 -07001230
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001231 for (int i = 0; i < count; i++) {
1232 final CellLayout layout = (CellLayout) getChildAt(i);
Romain Guy574d20e2009-06-01 15:34:04 -07001233
Romain Guy629de3e2010-01-13 12:20:59 -08001234 // Avoid ANRs by treating each screen separately
1235 post(new Runnable() {
1236 public void run() {
1237 final ArrayList<View> childrenToRemove = new ArrayList<View>();
1238 childrenToRemove.clear();
1239
1240 int childCount = layout.getChildCount();
1241 for (int j = 0; j < childCount; j++) {
1242 final View view = layout.getChildAt(j);
1243 Object tag = view.getTag();
1244
Joe Onorato0589f0f2010-02-08 13:44:00 -08001245 if (tag instanceof ShortcutInfo) {
1246 final ShortcutInfo info = (ShortcutInfo) tag;
Romain Guy629de3e2010-01-13 12:20:59 -08001247 // We need to check for ACTION_MAIN otherwise getComponent() might
1248 // return null for some shortcuts (for instance, for shortcuts to
1249 // web pages.)
1250 final Intent intent = info.intent;
1251 final ComponentName name = intent.getComponent();
1252
1253 if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
1254 name != null && packageName.equals(name.getPackageName())) {
1255 // TODO: This should probably be done on a worker thread
1256 LauncherModel.deleteItemFromDatabase(mLauncher, info);
1257 childrenToRemove.add(view);
1258 }
1259 } else if (tag instanceof UserFolderInfo) {
1260 final UserFolderInfo info = (UserFolderInfo) tag;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001261 final ArrayList<ShortcutInfo> contents = info.contents;
1262 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
Romain Guy629de3e2010-01-13 12:20:59 -08001263 final int contentsCount = contents.size();
1264 boolean removedFromFolder = false;
1265
1266 for (int k = 0; k < contentsCount; k++) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001267 final ShortcutInfo appInfo = contents.get(k);
Romain Guy629de3e2010-01-13 12:20:59 -08001268 final Intent intent = appInfo.intent;
1269 final ComponentName name = intent.getComponent();
1270
1271 if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
1272 name != null && packageName.equals(name.getPackageName())) {
1273 toRemove.add(appInfo);
1274 // TODO: This should probably be done on a worker thread
1275 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
1276 removedFromFolder = true;
1277 }
1278 }
1279
1280 contents.removeAll(toRemove);
1281 if (removedFromFolder) {
1282 final Folder folder = getOpenFolder();
1283 if (folder != null) folder.notifyDataSetChanged();
1284 }
1285 } else if (tag instanceof LiveFolderInfo) {
1286 final LiveFolderInfo info = (LiveFolderInfo) tag;
1287 final Uri uri = info.uri;
1288 final ProviderInfo providerInfo = manager.resolveContentProvider(
1289 uri.getAuthority(), 0);
Romain Guy574d20e2009-06-01 15:34:04 -07001290
Romain Guy629de3e2010-01-13 12:20:59 -08001291 if (providerInfo == null ||
1292 packageName.equals(providerInfo.packageName)) {
1293 // TODO: This should probably be done on a worker thread
1294 LauncherModel.deleteItemFromDatabase(mLauncher, info);
1295 childrenToRemove.add(view);
1296 }
1297 } else if (tag instanceof LauncherAppWidgetInfo) {
1298 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1299 final AppWidgetProviderInfo provider =
1300 widgets.getAppWidgetInfo(info.appWidgetId);
1301 if (provider == null ||
1302 packageName.equals(provider.provider.getPackageName())) {
1303 // TODO: This should probably be done on a worker thread
1304 LauncherModel.deleteItemFromDatabase(mLauncher, info);
1305 childrenToRemove.add(view);
1306 }
Romain Guy574d20e2009-06-01 15:34:04 -07001307 }
1308 }
Romain Guy629de3e2010-01-13 12:20:59 -08001309
1310 childCount = childrenToRemove.size();
1311 for (int j = 0; j < childCount; j++) {
1312 View child = childrenToRemove.get(j);
1313 layout.removeViewInLayout(child);
1314 if (child instanceof DropTarget) {
1315 mDragController.removeDropTarget((DropTarget)child);
1316 }
Romain Guy574d20e2009-06-01 15:34:04 -07001317 }
Romain Guy629de3e2010-01-13 12:20:59 -08001318
1319 if (childCount > 0) {
1320 layout.requestLayout();
1321 layout.invalidate();
Romain Guy5c16f3e2010-01-12 17:24:58 -08001322 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001323 }
Romain Guy629de3e2010-01-13 12:20:59 -08001324 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001325 }
1326 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001327
1328 void updateShortcutsForPackage(String packageName) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001329 final PackageManager pm = mLauncher.getPackageManager();
1330
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001331 final int count = getChildCount();
1332 for (int i = 0; i < count; i++) {
1333 final CellLayout layout = (CellLayout) getChildAt(i);
1334 int childCount = layout.getChildCount();
1335 for (int j = 0; j < childCount; j++) {
1336 final View view = layout.getChildAt(j);
1337 Object tag = view.getTag();
Joe Onorato0589f0f2010-02-08 13:44:00 -08001338 if (tag instanceof ShortcutInfo) {
1339 ShortcutInfo info = (ShortcutInfo)tag;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001340 // We need to check for ACTION_MAIN otherwise getComponent() might
1341 // return null for some shortcuts (for instance, for shortcuts to
1342 // web pages.)
1343 final Intent intent = info.intent;
1344 final ComponentName name = intent.getComponent();
1345 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1346 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null &&
1347 packageName.equals(name.getPackageName())) {
1348
Joe Onorato0589f0f2010-02-08 13:44:00 -08001349 info.setIcon(mIconCache.getIcon(info.intent));
1350 ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null,
1351 new FastBitmapDrawable(info.getIcon(mIconCache)), null, null);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001352 }
1353 }
1354 }
1355 }
1356 }
1357
Joe Onorato14f122b2009-11-19 14:06:36 -08001358 void moveToDefaultScreen(boolean animate) {
Joe Onoratoc45b1682010-01-11 18:48:40 -05001359 if (animate) {
1360 snapToScreen(mDefaultScreen);
1361 } else {
1362 setCurrentScreen(mDefaultScreen);
1363 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001364 getChildAt(mDefaultScreen).requestFocus();
1365 }
1366
Romain Guy8a73c512009-11-09 19:19:59 -08001367 void setIndicators(Drawable previous, Drawable next) {
1368 mPreviousIndicator = previous;
1369 mNextIndicator = next;
1370 previous.setLevel(mCurrentScreen);
1371 next.setLevel(mCurrentScreen);
1372 }
1373
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001374 public static class SavedState extends BaseSavedState {
1375 int currentScreen = -1;
1376
1377 SavedState(Parcelable superState) {
1378 super(superState);
1379 }
1380
1381 private SavedState(Parcel in) {
1382 super(in);
1383 currentScreen = in.readInt();
1384 }
1385
1386 @Override
1387 public void writeToParcel(Parcel out, int flags) {
1388 super.writeToParcel(out, flags);
1389 out.writeInt(currentScreen);
1390 }
1391
1392 public static final Parcelable.Creator<SavedState> CREATOR =
1393 new Parcelable.Creator<SavedState>() {
1394 public SavedState createFromParcel(Parcel in) {
1395 return new SavedState(in);
1396 }
1397
1398 public SavedState[] newArray(int size) {
1399 return new SavedState[size];
1400 }
1401 };
1402 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001403}