blob: 1d2d75e51b928dba6636d34211c7c4f97d0ddae5 [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 Onorato080d9b62009-11-02 12:01:11 -050032import android.os.Parcelable;
33import android.os.Parcel;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.util.AttributeSet;
35import android.view.MotionEvent;
36import android.view.VelocityTracker;
37import android.view.View;
38import android.view.ViewConfiguration;
39import android.view.ViewGroup;
40import android.view.ViewParent;
41import android.widget.Scroller;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070042import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043
44import java.util.ArrayList;
45
46/**
47 * The workspace is a wide area with a wallpaper and a finite number of screens. Each
48 * screen contains a number of icons, folders or widgets the user can interact with.
49 * A workspace is meant to be used with a fixed width only.
50 */
Joe Onorato85a02a82009-09-08 12:34:22 -070051public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
Romain Guye47f55c2009-11-11 19:21:22 -080052 @SuppressWarnings({"UnusedDeclaration"})
Joe Onorato3a8820b2009-11-10 15:06:42 -080053 private static final String TAG = "Launcher.Workspace";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080054 private static final int INVALID_SCREEN = -1;
Jeff Sharkey70864282009-04-07 21:08:40 -070055
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056 /**
57 * The velocity at which a fling gesture will cause us to snap to the next screen
58 */
59 private static final int SNAP_VELOCITY = 1000;
60
Dianne Hackborn8f573952009-08-10 23:21:09 -070061 private final WallpaperManager mWallpaperManager;
62
The Android Open Source Project31dd5032009-03-03 19:32:27 -080063 private int mDefaultScreen;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080064
65 private boolean mFirstLayout = true;
66
67 private int mCurrentScreen;
68 private int mNextScreen = INVALID_SCREEN;
69 private Scroller mScroller;
70 private VelocityTracker mVelocityTracker;
71
72 /**
73 * CellInfo for the cell that is currently being dragged
74 */
75 private CellLayout.CellInfo mDragInfo;
Jeff Sharkey70864282009-04-07 21:08:40 -070076
77 /**
78 * Target drop area calculated during last acceptDrop call.
79 */
80 private int[] mTargetCell = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081
82 private float mLastMotionX;
83 private float mLastMotionY;
Mike Cleronf8bbd342009-10-23 16:15:16 -070084
The Android Open Source Project31dd5032009-03-03 19:32:27 -080085 private final static int TOUCH_STATE_REST = 0;
86 private final static int TOUCH_STATE_SCROLLING = 1;
87
88 private int mTouchState = TOUCH_STATE_REST;
89
90 private OnLongClickListener mLongClickListener;
91
92 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -080093 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -040094 private DragController mDragController;
Jeff Sharkey70864282009-04-07 21:08:40 -070095
96 /**
97 * Cache of vacant cells, used during drag events and invalidated as needed.
98 */
99 private CellLayout.CellInfo mVacantCache = null;
100
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800101 private int[] mTempCell = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700102 private int[] mTempEstimate = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800103
Joe Onoratoa9c28f62009-09-14 18:38:49 -0400104 private boolean mAllowLongPress = true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800105
106 private int mTouchSlop;
Romain Guya206daa2009-07-06 11:51:18 -0700107 private int mMaximumVelocity;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108
Romain Guy8a73c512009-11-09 19:19:59 -0800109 private Drawable mPreviousIndicator;
110 private Drawable mNextIndicator;
111
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800112 /**
113 * Used to inflate the Workspace from XML.
114 *
115 * @param context The application's context.
116 * @param attrs The attribtues set containing the Workspace's customization values.
117 */
118 public Workspace(Context context, AttributeSet attrs) {
119 this(context, attrs, 0);
120 }
121
122 /**
123 * Used to inflate the Workspace from XML.
124 *
125 * @param context The application's context.
126 * @param attrs The attribtues set containing the Workspace's customization values.
127 * @param defStyle Unused.
128 */
129 public Workspace(Context context, AttributeSet attrs, int defStyle) {
130 super(context, attrs, defStyle);
131
Dianne Hackborn8f573952009-08-10 23:21:09 -0700132 mWallpaperManager = WallpaperManager.getInstance(context);
133
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800134 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
135 mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
136 a.recycle();
137
Joe Onorato0d44e942009-11-16 18:20:51 -0800138 setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800139 initWorkspace();
140 }
141
142 /**
143 * Initializes various states for this workspace.
144 */
145 private void initWorkspace() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800146 Context context = getContext();
147 mScroller = new Scroller(context);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800148 mCurrentScreen = mDefaultScreen;
149 Launcher.setScreen(mCurrentScreen);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800150 LauncherApplication app = (LauncherApplication)context.getApplicationContext();
151 mIconCache = app.getIconCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800152
Romain Guya206daa2009-07-06 11:51:18 -0700153 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
154 mTouchSlop = configuration.getScaledTouchSlop();
155 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800156 }
157
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800158 @Override
159 public void addView(View child, int index, LayoutParams params) {
160 if (!(child instanceof CellLayout)) {
161 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
162 }
163 super.addView(child, index, params);
164 }
165
166 @Override
167 public void addView(View child) {
168 if (!(child instanceof CellLayout)) {
169 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
170 }
171 super.addView(child);
172 }
173
174 @Override
175 public void addView(View child, int index) {
176 if (!(child instanceof CellLayout)) {
177 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
178 }
179 super.addView(child, index);
180 }
181
182 @Override
183 public void addView(View child, int width, int height) {
184 if (!(child instanceof CellLayout)) {
185 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
186 }
187 super.addView(child, width, height);
188 }
189
190 @Override
191 public void addView(View child, LayoutParams params) {
192 if (!(child instanceof CellLayout)) {
193 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
194 }
195 super.addView(child, params);
196 }
197
198 /**
199 * @return The open folder on the current screen, or null if there is none
200 */
201 Folder getOpenFolder() {
202 CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
203 int count = currentScreen.getChildCount();
204 for (int i = 0; i < count; i++) {
205 View child = currentScreen.getChildAt(i);
206 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
207 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
208 return (Folder) child;
209 }
210 }
211 return null;
212 }
213
214 ArrayList<Folder> getOpenFolders() {
215 final int screens = getChildCount();
216 ArrayList<Folder> folders = new ArrayList<Folder>(screens);
217
218 for (int screen = 0; screen < screens; screen++) {
219 CellLayout currentScreen = (CellLayout) getChildAt(screen);
220 int count = currentScreen.getChildCount();
221 for (int i = 0; i < count; i++) {
222 View child = currentScreen.getChildAt(i);
223 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
224 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
225 folders.add((Folder) child);
226 break;
227 }
228 }
229 }
230
231 return folders;
232 }
233
234 boolean isDefaultScreenShowing() {
235 return mCurrentScreen == mDefaultScreen;
236 }
237
238 /**
239 * Returns the index of the currently displayed screen.
240 *
241 * @return The index of the currently displayed screen.
242 */
243 int getCurrentScreen() {
244 return mCurrentScreen;
245 }
246
247 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800248 * Sets the current screen.
249 *
250 * @param currentScreen
251 */
252 void setCurrentScreen(int currentScreen) {
Joe Onoratoc45b1682010-01-11 18:48:40 -0500253 if (!mScroller.isFinished()) mScroller.abortAnimation();
Romain Guy4c58c482009-05-12 17:35:41 -0700254 clearVacantCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800255 mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
Romain Guy483543f2010-01-28 14:06:31 -0800256 mPreviousIndicator.setLevel(mCurrentScreen);
257 mNextIndicator.setLevel(mCurrentScreen);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800258 scrollTo(mCurrentScreen * getWidth(), 0);
259 invalidate();
260 }
261
262 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800263 * Adds the specified child in the current screen. The position and dimension of
264 * the child are defined by x, y, spanX and spanY.
265 *
266 * @param child The child to add in one of the workspace's screens.
267 * @param x The X position of the child in the screen's grid.
268 * @param y The Y position of the child in the screen's grid.
269 * @param spanX The number of cells spanned horizontally by the child.
270 * @param spanY The number of cells spanned vertically by the child.
271 */
272 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
273 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
274 }
275
276 /**
277 * Adds the specified child in the current screen. The position and dimension of
278 * the child are defined by x, y, spanX and spanY.
279 *
280 * @param child The child to add in one of the workspace's screens.
281 * @param x The X position of the child in the screen's grid.
282 * @param y The Y position of the child in the screen's grid.
283 * @param spanX The number of cells spanned horizontally by the child.
284 * @param spanY The number of cells spanned vertically by the child.
285 * @param insert When true, the child is inserted at the beginning of the children list.
286 */
287 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
288 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
289 }
290
291 /**
292 * Adds the specified child in the specified screen. The position and dimension of
293 * the child are defined by x, y, spanX and spanY.
294 *
295 * @param child The child to add in one of the workspace's screens.
296 * @param screen The screen in which to add the child.
297 * @param x The X position of the child in the screen's grid.
298 * @param y The Y position of the child in the screen's grid.
299 * @param spanX The number of cells spanned horizontally by the child.
300 * @param spanY The number of cells spanned vertically by the child.
301 */
302 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
303 addInScreen(child, screen, x, y, spanX, spanY, false);
304 }
305
306 /**
307 * Adds the specified child in the specified screen. The position and dimension of
308 * the child are defined by x, y, spanX and spanY.
309 *
310 * @param child The child to add in one of the workspace's screens.
311 * @param screen The screen in which to add the child.
312 * @param x The X position of the child in the screen's grid.
313 * @param y The Y position of the child in the screen's grid.
314 * @param spanX The number of cells spanned horizontally by the child.
315 * @param spanY The number of cells spanned vertically by the child.
316 * @param insert When true, the child is inserted at the beginning of the children list.
317 */
318 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
319 if (screen < 0 || screen >= getChildCount()) {
320 throw new IllegalStateException("The screen must be >= 0 and < " + getChildCount());
321 }
322
Romain Guy4c58c482009-05-12 17:35:41 -0700323 clearVacantCache();
324
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800325 final CellLayout group = (CellLayout) getChildAt(screen);
326 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
327 if (lp == null) {
328 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
329 } else {
330 lp.cellX = x;
331 lp.cellY = y;
332 lp.cellHSpan = spanX;
333 lp.cellVSpan = spanY;
334 }
335 group.addView(child, insert ? 0 : -1, lp);
336 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -0800337 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800338 child.setOnLongClickListener(mLongClickListener);
339 }
Joe Onorato00acb122009-08-04 16:04:30 -0400340 if (child instanceof DropTarget) {
341 mDragController.addDropTarget((DropTarget)child);
342 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800343 }
344
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800345 CellLayout.CellInfo findAllVacantCells(boolean[] occupied) {
346 CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
347 if (group != null) {
Jeff Sharkey70864282009-04-07 21:08:40 -0700348 return group.findAllVacantCells(occupied, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800349 }
350 return null;
351 }
352
Romain Guy4c58c482009-05-12 17:35:41 -0700353 private void clearVacantCache() {
354 if (mVacantCache != null) {
355 mVacantCache.clearVacantCells();
356 mVacantCache = null;
357 }
358 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800359
360 /**
361 * Registers the specified listener on each screen contained in this workspace.
362 *
363 * @param l The listener used to respond to long clicks.
364 */
365 @Override
366 public void setOnLongClickListener(OnLongClickListener l) {
367 mLongClickListener = l;
368 final int count = getChildCount();
369 for (int i = 0; i < count; i++) {
370 getChildAt(i).setOnLongClickListener(l);
371 }
372 }
373
Dianne Hackborn8f573952009-08-10 23:21:09 -0700374 private void updateWallpaperOffset() {
Romain Guy798300c2009-08-11 14:43:29 -0700375 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
376 }
377
378 private void updateWallpaperOffset(int scrollRange) {
Marco Nelissenc07c79b2009-11-09 16:08:03 -0800379 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
Dianne Hackborn49cdb1b2009-08-13 16:50:18 -0700380 mWallpaperManager.setWallpaperOffsets(getWindowToken(), mScrollX / (float) scrollRange, 0);
Dianne Hackborn8f573952009-08-10 23:21:09 -0700381 }
382
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800383 @Override
384 public void computeScroll() {
385 if (mScroller.computeScrollOffset()) {
386 mScrollX = mScroller.getCurrX();
387 mScrollY = mScroller.getCurrY();
Dianne Hackborn8f573952009-08-10 23:21:09 -0700388 updateWallpaperOffset();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800389 postInvalidate();
390 } else if (mNextScreen != INVALID_SCREEN) {
391 mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
Romain Guy8a73c512009-11-09 19:19:59 -0800392 mPreviousIndicator.setLevel(mCurrentScreen);
393 mNextIndicator.setLevel(mCurrentScreen);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800394 Launcher.setScreen(mCurrentScreen);
395 mNextScreen = INVALID_SCREEN;
396 clearChildrenCache();
397 }
398 }
399
400 @Override
401 protected void dispatchDraw(Canvas canvas) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700402 boolean restore = false;
Joe Onoratoe77c08d2009-08-01 00:01:20 -0700403 int restoreCount = 0;
404
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800405 // ViewGroup.dispatchDraw() supports many features we don't need:
406 // clip to padding, layout animation, animation listener, disappearing
407 // children, etc. The following implementation attempts to fast-track
408 // the drawing dispatch by drawing only what we know needs to be drawn.
409
Romain Guyf8e6a802009-12-07 17:48:02 -0800410 boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800411 // If we are not scrolling or flinging, draw only the current screen
412 if (fastDraw) {
413 drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
414 } else {
415 final long drawingTime = getDrawingTime();
416 // If we are flinging, draw only the current screen and the target screen
417 if (mNextScreen >= 0 && mNextScreen < getChildCount() &&
418 Math.abs(mCurrentScreen - mNextScreen) == 1) {
419 drawChild(canvas, getChildAt(mCurrentScreen), drawingTime);
420 drawChild(canvas, getChildAt(mNextScreen), drawingTime);
421 } else {
422 // If we are scrolling, draw all of our children
423 final int count = getChildCount();
424 for (int i = 0; i < count; i++) {
425 drawChild(canvas, getChildAt(i), drawingTime);
426 }
427 }
428 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700429
430 if (restore) {
Joe Onoratoe77c08d2009-08-01 00:01:20 -0700431 canvas.restoreToCount(restoreCount);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700432 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800433 }
434
Joe Onorato00acb122009-08-04 16:04:30 -0400435 protected void onAttachedToWindow() {
436 super.onAttachedToWindow();
437 mDragController.setWindowToken(getWindowToken());
438 }
439
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800440 @Override
441 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
442 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
443
444 final int width = MeasureSpec.getSize(widthMeasureSpec);
445 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
446 if (widthMode != MeasureSpec.EXACTLY) {
447 throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
448 }
449
450 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
451 if (heightMode != MeasureSpec.EXACTLY) {
452 throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
453 }
454
455 // The children are given the same width and height as the workspace
456 final int count = getChildCount();
457 for (int i = 0; i < count; i++) {
458 getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
459 }
460
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800461
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800462 if (mFirstLayout) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800463 setHorizontalScrollBarEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800464 scrollTo(mCurrentScreen * width, 0);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800465 setHorizontalScrollBarEnabled(true);
Romain Guy798300c2009-08-11 14:43:29 -0700466 updateWallpaperOffset(width * (getChildCount() - 1));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800467 mFirstLayout = false;
468 }
469 }
470
471 @Override
472 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
473 int childLeft = 0;
474
475 final int count = getChildCount();
476 for (int i = 0; i < count; i++) {
477 final View child = getChildAt(i);
478 if (child.getVisibility() != View.GONE) {
479 final int childWidth = child.getMeasuredWidth();
480 child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
481 childLeft += childWidth;
482 }
483 }
484 }
485
486 @Override
487 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
488 int screen = indexOfChild(child);
489 if (screen != mCurrentScreen || !mScroller.isFinished()) {
490 if (!mLauncher.isWorkspaceLocked()) {
491 snapToScreen(screen);
492 }
493 return true;
494 }
495 return false;
496 }
497
498 @Override
499 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -0400500 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800501 final Folder openFolder = getOpenFolder();
502 if (openFolder != null) {
503 return openFolder.requestFocus(direction, previouslyFocusedRect);
504 } else {
505 int focusableScreen;
506 if (mNextScreen != INVALID_SCREEN) {
507 focusableScreen = mNextScreen;
508 } else {
509 focusableScreen = mCurrentScreen;
510 }
511 getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
512 }
513 }
514 return false;
515 }
516
517 @Override
518 public boolean dispatchUnhandledMove(View focused, int direction) {
519 if (direction == View.FOCUS_LEFT) {
520 if (getCurrentScreen() > 0) {
521 snapToScreen(getCurrentScreen() - 1);
522 return true;
523 }
524 } else if (direction == View.FOCUS_RIGHT) {
525 if (getCurrentScreen() < getChildCount() - 1) {
526 snapToScreen(getCurrentScreen() + 1);
527 return true;
528 }
529 }
530 return super.dispatchUnhandledMove(focused, direction);
531 }
532
533 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -0700534 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -0400535 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800536 final Folder openFolder = getOpenFolder();
537 if (openFolder == null) {
538 getChildAt(mCurrentScreen).addFocusables(views, direction);
539 if (direction == View.FOCUS_LEFT) {
540 if (mCurrentScreen > 0) {
541 getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
542 }
543 } else if (direction == View.FOCUS_RIGHT){
544 if (mCurrentScreen < getChildCount() - 1) {
545 getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
546 }
547 }
548 } else {
549 openFolder.addFocusables(views, direction);
550 }
551 }
552 }
553
554 @Override
Joe Onorato7bb17492009-09-24 17:51:01 -0700555 public boolean dispatchTouchEvent(MotionEvent ev) {
556 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
557 if (mLauncher.isWorkspaceLocked() || mLauncher.isAllAppsVisible()) {
558 return false;
559 }
560 }
561 return super.dispatchTouchEvent(ev);
562 }
563
564 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800565 public boolean onInterceptTouchEvent(MotionEvent ev) {
Joe Onorato68ba5ca2009-11-12 14:23:43 -0800566 final boolean workspaceLocked = mLauncher.isWorkspaceLocked();
567 final boolean allAppsVisible = mLauncher.isAllAppsVisible();
Joe Onorato68ba5ca2009-11-12 14:23:43 -0800568 if (workspaceLocked || allAppsVisible) {
Joe Onorato7c312c12009-08-13 21:36:53 -0700569 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 -0800570 }
571
572 /*
573 * This method JUST determines whether we want to intercept the motion.
574 * If we return true, onTouchEvent will be called and we do the actual
575 * scrolling there.
576 */
577
578 /*
579 * Shortcut the most recurring case: the user is in the dragging
580 * state and he is moving his finger. We want to intercept this
581 * motion.
582 */
583 final int action = ev.getAction();
584 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
585 return true;
586 }
587
588 final float x = ev.getX();
589 final float y = ev.getY();
590
591 switch (action) {
592 case MotionEvent.ACTION_MOVE:
593 /*
594 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
595 * whether the user has moved far enough from his original down touch.
596 */
597
598 /*
599 * Locally do absolute value. mLastMotionX is set to the y value
600 * of the down event.
601 */
602 final int xDiff = (int) Math.abs(x - mLastMotionX);
603 final int yDiff = (int) Math.abs(y - mLastMotionY);
604
605 final int touchSlop = mTouchSlop;
606 boolean xMoved = xDiff > touchSlop;
607 boolean yMoved = yDiff > touchSlop;
608
609 if (xMoved || yMoved) {
610
611 if (xMoved) {
612 // Scroll if the user moved far enough along the X axis
613 mTouchState = TOUCH_STATE_SCROLLING;
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800614 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800615 }
616 // Either way, cancel any pending longpress
617 if (mAllowLongPress) {
618 mAllowLongPress = false;
619 // Try canceling the long press. It could also have been scheduled
620 // by a distant descendant, so use the mAllowLongPress flag to block
621 // everything
622 final View currentScreen = getChildAt(mCurrentScreen);
623 currentScreen.cancelLongPress();
624 }
625 }
626 break;
627
628 case MotionEvent.ACTION_DOWN:
629 // Remember location of down touch
630 mLastMotionX = x;
631 mLastMotionY = y;
632 mAllowLongPress = true;
633
634 /*
635 * If being flinged and user touches the screen, initiate drag;
636 * otherwise don't. mScroller.isFinished should be false when
637 * being flinged.
638 */
639 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
640 break;
641
642 case MotionEvent.ACTION_CANCEL:
643 case MotionEvent.ACTION_UP:
Mike Cleronf8bbd342009-10-23 16:15:16 -0700644
645 if (mTouchState != TOUCH_STATE_SCROLLING) {
Mike Cleronf8bbd342009-10-23 16:15:16 -0700646 final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
647 if (!currentScreen.lastDownOnOccupiedCell()) {
Romain Guy06762ab2010-01-25 16:51:08 -0800648 getLocationOnScreen(mTempCell);
Mike Cleronf8bbd342009-10-23 16:15:16 -0700649 // Send a tap to the wallpaper if the last down was on empty space
650 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
Romain Guy06762ab2010-01-25 16:51:08 -0800651 "android.wallpaper.tap",
652 mTempCell[0] + (int) ev.getX(),
653 mTempCell[1] + (int) ev.getY(), 0, null);
Mike Cleronf8bbd342009-10-23 16:15:16 -0700654 }
655 }
656
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800657 // Release the drag
658 clearChildrenCache();
659 mTouchState = TOUCH_STATE_REST;
660 mAllowLongPress = false;
Mike Cleronf8bbd342009-10-23 16:15:16 -0700661
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800662 break;
663 }
664
665 /*
666 * The only time we want to intercept motion events is if we are in the
667 * drag mode.
668 */
669 return mTouchState != TOUCH_STATE_REST;
670 }
671
Joe Onorato3a8820b2009-11-10 15:06:42 -0800672 /**
673 * If one of our descendant views decides that it could be focused now, only
674 * pass that along if it's on the current screen.
675 *
676 * This happens when live folders requery, and if they're off screen, they
677 * end up calling requestFocus, which pulls it on screen.
678 */
679 @Override
680 public void focusableViewAvailable(View focused) {
681 View current = getChildAt(mCurrentScreen);
682 View v = focused;
683 while (true) {
684 if (v == current) {
685 super.focusableViewAvailable(focused);
686 return;
687 }
688 if (v == this) {
689 return;
690 }
691 ViewParent parent = v.getParent();
692 if (parent instanceof View) {
693 v = (View)v.getParent();
694 } else {
695 return;
696 }
697 }
698 }
699
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800700 void enableChildrenCache(int fromScreen, int toScreen) {
701 if (fromScreen > toScreen) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800702 fromScreen = toScreen;
703 toScreen = fromScreen;
704 }
705
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800706 final int count = getChildCount();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800707
708 fromScreen = Math.max(fromScreen, 0);
709 toScreen = Math.min(toScreen, count - 1);
710
711 for (int i = fromScreen; i <= toScreen; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800712 final CellLayout layout = (CellLayout) getChildAt(i);
713 layout.setChildrenDrawnWithCacheEnabled(true);
714 layout.setChildrenDrawingCacheEnabled(true);
715 }
716 }
717
718 void clearChildrenCache() {
719 final int count = getChildCount();
720 for (int i = 0; i < count; i++) {
721 final CellLayout layout = (CellLayout) getChildAt(i);
722 layout.setChildrenDrawnWithCacheEnabled(false);
723 }
724 }
725
726 @Override
727 public boolean onTouchEvent(MotionEvent ev) {
Mike Cleronf8bbd342009-10-23 16:15:16 -0700728
Joe Onorato2bc6b7c2009-10-01 14:08:30 -0700729 if (mLauncher.isWorkspaceLocked()) {
730 return false; // We don't want the events. Let them fall through to the all apps view.
731 }
732 if (mLauncher.isAllAppsVisible()) {
733 // Cancel any scrolling that is in progress.
734 if (!mScroller.isFinished()) {
735 mScroller.abortAnimation();
736 }
737 snapToScreen(mCurrentScreen);
Joe Onorato7c312c12009-08-13 21:36:53 -0700738 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 -0800739 }
740
741 if (mVelocityTracker == null) {
742 mVelocityTracker = VelocityTracker.obtain();
743 }
744 mVelocityTracker.addMovement(ev);
745
746 final int action = ev.getAction();
747 final float x = ev.getX();
748
749 switch (action) {
750 case MotionEvent.ACTION_DOWN:
751 /*
752 * If being flinged and user touches, stop the fling. isFinished
753 * will be false if being flinged.
754 */
755 if (!mScroller.isFinished()) {
756 mScroller.abortAnimation();
757 }
758
759 // Remember where the motion event started
760 mLastMotionX = x;
761 break;
762 case MotionEvent.ACTION_MOVE:
763 if (mTouchState == TOUCH_STATE_SCROLLING) {
764 // Scroll to follow the motion event
765 final int deltaX = (int) (mLastMotionX - x);
766 mLastMotionX = x;
767
768 if (deltaX < 0) {
769 if (mScrollX > 0) {
770 scrollBy(Math.max(-mScrollX, deltaX), 0);
Dianne Hackborn8f573952009-08-10 23:21:09 -0700771 updateWallpaperOffset();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800772 }
773 } else if (deltaX > 0) {
774 final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -
775 mScrollX - getWidth();
776 if (availableToScroll > 0) {
777 scrollBy(Math.min(availableToScroll, deltaX), 0);
Dianne Hackborn8f573952009-08-10 23:21:09 -0700778 updateWallpaperOffset();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800779 }
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800780 } else {
781 awakenScrollBars();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800782 }
783 }
784 break;
785 case MotionEvent.ACTION_UP:
786 if (mTouchState == TOUCH_STATE_SCROLLING) {
787 final VelocityTracker velocityTracker = mVelocityTracker;
Romain Guya206daa2009-07-06 11:51:18 -0700788 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800789 int velocityX = (int) velocityTracker.getXVelocity();
790
791 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
792 // Fling hard enough to move left
793 snapToScreen(mCurrentScreen - 1);
794 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
795 // Fling hard enough to move right
796 snapToScreen(mCurrentScreen + 1);
797 } else {
798 snapToDestination();
799 }
800
801 if (mVelocityTracker != null) {
802 mVelocityTracker.recycle();
803 mVelocityTracker = null;
804 }
805 }
806 mTouchState = TOUCH_STATE_REST;
807 break;
808 case MotionEvent.ACTION_CANCEL:
809 mTouchState = TOUCH_STATE_REST;
810 }
811
812 return true;
813 }
814
815 private void snapToDestination() {
816 final int screenWidth = getWidth();
817 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
818
819 snapToScreen(whichScreen);
820 }
821
822 void snapToScreen(int whichScreen) {
Romain Guye47f55c2009-11-11 19:21:22 -0800823 //if (!mScroller.isFinished()) return;
Romain Guy82d94d92009-05-06 17:43:13 -0700824
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800825 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800826
827 clearVacantCache();
828 enableChildrenCache(mCurrentScreen, whichScreen);
829
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800830 final int screenDelta = Math.abs(whichScreen - mCurrentScreen);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800831
832 mNextScreen = whichScreen;
Romain Guye47f55c2009-11-11 19:21:22 -0800833
834 mPreviousIndicator.setLevel(mNextScreen);
835 mNextIndicator.setLevel(mNextScreen);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800836
837 View focusedChild = getFocusedChild();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800838 if (focusedChild != null && screenDelta != 0 && focusedChild == getChildAt(mCurrentScreen)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800839 focusedChild.clearFocus();
840 }
841
842 final int newX = whichScreen * getWidth();
843 final int delta = newX - mScrollX;
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800844 final int duration = screenDelta * 300;
845 awakenScrollBars(duration);
Romain Guyf8e6a802009-12-07 17:48:02 -0800846
Romain Guyf8e6a802009-12-07 17:48:02 -0800847 if (!mScroller.isFinished()) mScroller.abortAnimation();
Joe Onoratoc45b1682010-01-11 18:48:40 -0500848 mScroller.startScroll(mScrollX, 0, delta, 0, duration);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800849 invalidate();
850 }
851
852 void startDrag(CellLayout.CellInfo cellInfo) {
853 View child = cellInfo.cell;
854
855 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +0000856 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800857 return;
858 }
859
860 mDragInfo = cellInfo;
861 mDragInfo.screen = mCurrentScreen;
862
863 CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
864
865 current.onDragChild(child);
Joe Onorato00acb122009-08-04 16:04:30 -0400866 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800867 invalidate();
868 }
869
870 @Override
871 protected Parcelable onSaveInstanceState() {
872 final SavedState state = new SavedState(super.onSaveInstanceState());
873 state.currentScreen = mCurrentScreen;
874 return state;
875 }
876
877 @Override
878 protected void onRestoreInstanceState(Parcelable state) {
879 SavedState savedState = (SavedState) state;
880 super.onRestoreInstanceState(savedState.getSuperState());
881 if (savedState.currentScreen != -1) {
882 mCurrentScreen = savedState.currentScreen;
883 Launcher.setScreen(mCurrentScreen);
884 }
885 }
886
Joe Onorato0589f0f2010-02-08 13:44:00 -0800887 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800888 addApplicationShortcut(info, cellInfo, false);
889 }
890
Joe Onorato0589f0f2010-02-08 13:44:00 -0800891 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800892 boolean insertAtFirst) {
893 final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
894 final int[] result = new int[2];
895
896 layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
897 onDropExternal(result[0], result[1], info, layout, insertAtFirst);
898 }
899
Joe Onorato00acb122009-08-04 16:04:30 -0400900 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
901 DragView dragView, Object dragInfo) {
Jeff Sharkey70864282009-04-07 21:08:40 -0700902 final CellLayout cellLayout = getCurrentDropLayout();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800903 if (source != this) {
904 onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout);
905 } else {
906 // Move internally
907 if (mDragInfo != null) {
908 final View cell = mDragInfo.cell;
Romain Guy52d9cb32009-09-11 17:29:51 -0700909 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
910 if (index != mDragInfo.screen) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800911 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
912 originalCellLayout.removeView(cell);
913 cellLayout.addView(cell);
914 }
Romain Guy263e0192009-05-11 11:50:46 -0700915 mTargetCell = estimateDropCell(x - xOffset, y - yOffset,
Jeff Sharkey70864282009-04-07 21:08:40 -0700916 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell);
917 cellLayout.onDropChild(cell, mTargetCell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800918
Romain Guy84f296c2009-11-04 15:00:44 -0800919 final ItemInfo info = (ItemInfo) cell.getTag();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800920 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
921 LauncherModel.moveItemInDatabase(mLauncher, info,
Romain Guy52d9cb32009-09-11 17:29:51 -0700922 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, lp.cellX, lp.cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800923 }
924 }
925 }
926
927 public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
Joe Onorato00acb122009-08-04 16:04:30 -0400928 DragView dragView, Object dragInfo) {
Romain Guy4c58c482009-05-12 17:35:41 -0700929 clearVacantCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800930 }
931
932 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
Joe Onorato00acb122009-08-04 16:04:30 -0400933 DragView dragView, Object dragInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800934 }
935
936 public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
Joe Onorato00acb122009-08-04 16:04:30 -0400937 DragView dragView, Object dragInfo) {
Romain Guy4c58c482009-05-12 17:35:41 -0700938 clearVacantCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800939 }
940
941 private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) {
942 onDropExternal(x, y, dragInfo, cellLayout, false);
943 }
944
945 private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout,
946 boolean insertAtFirst) {
947 // Drag from somewhere else
948 ItemInfo info = (ItemInfo) dragInfo;
949
950 View view;
951
952 switch (info.itemType) {
953 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
954 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
955 if (info.container == NO_ID) {
956 // Came from all apps -- make a copy
Joe Onorato0589f0f2010-02-08 13:44:00 -0800957 info = new ShortcutInfo((ShortcutInfo)info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800958 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800959 view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo)info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800960 break;
961 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
962 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
963 (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info));
964 break;
965 default:
966 throw new IllegalStateException("Unknown item type: " + info.itemType);
967 }
968
969 cellLayout.addView(view, insertAtFirst ? 0 : -1);
Joe Onorato0d44e942009-11-16 18:20:51 -0800970 view.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800971 view.setOnLongClickListener(mLongClickListener);
Joe Onorato00acb122009-08-04 16:04:30 -0400972 if (view instanceof DropTarget) {
Romain Guy207e40e2009-09-29 16:19:19 -0700973 mDragController.addDropTarget((DropTarget) view);
Joe Onorato00acb122009-08-04 16:04:30 -0400974 }
975
Romain Guy263e0192009-05-11 11:50:46 -0700976 mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
Jeff Sharkey70864282009-04-07 21:08:40 -0700977 cellLayout.onDropChild(view, mTargetCell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800978 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
979
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800980 LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
981 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY);
982 }
Jeff Sharkey70864282009-04-07 21:08:40 -0700983
984 /**
985 * Return the current {@link CellLayout}, correctly picking the destination
986 * screen while a scroll is in progress.
987 */
988 private CellLayout getCurrentDropLayout() {
989 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
990 return (CellLayout) getChildAt(index);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800991 }
992
Jeff Sharkey70864282009-04-07 21:08:40 -0700993 /**
994 * {@inheritDoc}
995 */
996 public boolean acceptDrop(DragSource source, int x, int y,
Joe Onorato00acb122009-08-04 16:04:30 -0400997 int xOffset, int yOffset, DragView dragView, Object dragInfo) {
Romain Guy4c58c482009-05-12 17:35:41 -0700998 final CellLayout layout = getCurrentDropLayout();
999 final CellLayout.CellInfo cellInfo = mDragInfo;
1000 final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1001 final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1002
1003 if (mVacantCache == null) {
1004 final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1005 mVacantCache = layout.findAllVacantCells(null, ignoreView);
1006 }
1007
1008 return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false);
Jeff Sharkey70864282009-04-07 21:08:40 -07001009 }
1010
1011 /**
1012 * {@inheritDoc}
1013 */
1014 public Rect estimateDropLocation(DragSource source, int x, int y,
Joe Onorato00acb122009-08-04 16:04:30 -04001015 int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
Jeff Sharkey70864282009-04-07 21:08:40 -07001016 final CellLayout layout = getCurrentDropLayout();
1017
1018 final CellLayout.CellInfo cellInfo = mDragInfo;
1019 final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1020 final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1021 final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1022
1023 final Rect location = recycle != null ? recycle : new Rect();
1024
1025 // Find drop cell and convert into rectangle
Romain Guy263e0192009-05-11 11:50:46 -07001026 int[] dropCell = estimateDropCell(x - xOffset, y - yOffset,
Jeff Sharkey70864282009-04-07 21:08:40 -07001027 spanX, spanY, ignoreView, layout, mTempCell);
1028
1029 if (dropCell == null) {
1030 return null;
1031 }
1032
1033 layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
1034 location.left = mTempEstimate[0];
1035 location.top = mTempEstimate[1];
1036
1037 layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
1038 location.right = mTempEstimate[0];
1039 location.bottom = mTempEstimate[1];
1040
1041 return location;
1042 }
1043
1044 /**
1045 * Calculate the nearest cell where the given object would be dropped.
1046 */
Romain Guy263e0192009-05-11 11:50:46 -07001047 private int[] estimateDropCell(int pixelX, int pixelY,
Jeff Sharkey70864282009-04-07 21:08:40 -07001048 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1049 // Create vacant cell cache if none exists
1050 if (mVacantCache == null) {
1051 mVacantCache = layout.findAllVacantCells(null, ignoreView);
1052 }
1053
1054 // Find the best target drop location
1055 return layout.findNearestVacantArea(pixelX, pixelY,
1056 spanX, spanY, mVacantCache, recycle);
1057 }
1058
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001059 void setLauncher(Launcher launcher) {
1060 mLauncher = launcher;
1061 }
1062
Joe Onorato00acb122009-08-04 16:04:30 -04001063 public void setDragController(DragController dragController) {
1064 mDragController = dragController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001065 }
1066
1067 public void onDropCompleted(View target, boolean success) {
Romain Guy207e40e2009-09-29 16:19:19 -07001068 clearVacantCache();
1069
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001070 if (success){
1071 if (target != this && mDragInfo != null) {
1072 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1073 cellLayout.removeView(mDragInfo.cell);
Joe Onorato00acb122009-08-04 16:04:30 -04001074 if (mDragInfo.cell instanceof DropTarget) {
1075 mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1076 }
Romain Guy207e40e2009-09-29 16:19:19 -07001077 //final Object tag = mDragInfo.cell.getTag();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001078 }
1079 } else {
1080 if (mDragInfo != null) {
1081 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1082 cellLayout.onDropAborted(mDragInfo.cell);
1083 }
1084 }
1085
1086 mDragInfo = null;
1087 }
1088
1089 public void scrollLeft() {
Romain Guy4c58c482009-05-12 17:35:41 -07001090 clearVacantCache();
Romain Guyf8e6a802009-12-07 17:48:02 -08001091 if (mScroller.isFinished()) {
1092 if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);
1093 } else {
1094 if (mNextScreen > 0) snapToScreen(mNextScreen - 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001095 }
1096 }
1097
1098 public void scrollRight() {
Romain Guy4c58c482009-05-12 17:35:41 -07001099 clearVacantCache();
Romain Guyf8e6a802009-12-07 17:48:02 -08001100 if (mScroller.isFinished()) {
1101 if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);
1102 } else {
1103 if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001104 }
1105 }
1106
1107 public int getScreenForView(View v) {
1108 int result = -1;
1109 if (v != null) {
1110 ViewParent vp = v.getParent();
1111 int count = getChildCount();
1112 for (int i = 0; i < count; i++) {
1113 if (vp == getChildAt(i)) {
1114 return i;
1115 }
1116 }
1117 }
1118 return result;
1119 }
Karl Rosaen138a0412009-04-23 19:00:21 -07001120
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001121 public Folder getFolderForTag(Object tag) {
1122 int screenCount = getChildCount();
1123 for (int screen = 0; screen < screenCount; screen++) {
1124 CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1125 int count = currentScreen.getChildCount();
1126 for (int i = 0; i < count; i++) {
1127 View child = currentScreen.getChildAt(i);
1128 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1129 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1130 Folder f = (Folder) child;
1131 if (f.getInfo() == tag) {
1132 return f;
1133 }
1134 }
1135 }
1136 }
1137 return null;
1138 }
1139
1140 public View getViewForTag(Object tag) {
1141 int screenCount = getChildCount();
1142 for (int screen = 0; screen < screenCount; screen++) {
1143 CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1144 int count = currentScreen.getChildCount();
1145 for (int i = 0; i < count; i++) {
1146 View child = currentScreen.getChildAt(i);
1147 if (child.getTag() == tag) {
1148 return child;
1149 }
1150 }
1151 }
1152 return null;
1153 }
1154
1155 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001156 * @return True is long presses are still allowed for the current touch
1157 */
1158 public boolean allowLongPress() {
1159 return mAllowLongPress;
1160 }
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001161
1162 /**
1163 * Set true to allow long-press events to be triggered, usually checked by
1164 * {@link Launcher} to accept or block dpad-initiated long-presses.
1165 */
1166 public void setAllowLongPress(boolean allowLongPress) {
1167 mAllowLongPress = allowLongPress;
1168 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001169
Romain Guy629de3e2010-01-13 12:20:59 -08001170 void removeItemsForPackage(final String packageName) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001171 final int count = getChildCount();
Romain Guy5c16f3e2010-01-12 17:24:58 -08001172 final PackageManager manager = getContext().getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -08001173 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
Romain Guy574d20e2009-06-01 15:34:04 -07001174
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001175 for (int i = 0; i < count; i++) {
1176 final CellLayout layout = (CellLayout) getChildAt(i);
Romain Guy574d20e2009-06-01 15:34:04 -07001177
Romain Guy629de3e2010-01-13 12:20:59 -08001178 // Avoid ANRs by treating each screen separately
1179 post(new Runnable() {
1180 public void run() {
1181 final ArrayList<View> childrenToRemove = new ArrayList<View>();
1182 childrenToRemove.clear();
1183
1184 int childCount = layout.getChildCount();
1185 for (int j = 0; j < childCount; j++) {
1186 final View view = layout.getChildAt(j);
1187 Object tag = view.getTag();
1188
Joe Onorato0589f0f2010-02-08 13:44:00 -08001189 if (tag instanceof ShortcutInfo) {
1190 final ShortcutInfo info = (ShortcutInfo) tag;
Romain Guy629de3e2010-01-13 12:20:59 -08001191 // We need to check for ACTION_MAIN otherwise getComponent() might
1192 // return null for some shortcuts (for instance, for shortcuts to
1193 // web pages.)
1194 final Intent intent = info.intent;
1195 final ComponentName name = intent.getComponent();
1196
1197 if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
1198 name != null && packageName.equals(name.getPackageName())) {
1199 // TODO: This should probably be done on a worker thread
1200 LauncherModel.deleteItemFromDatabase(mLauncher, info);
1201 childrenToRemove.add(view);
1202 }
1203 } else if (tag instanceof UserFolderInfo) {
1204 final UserFolderInfo info = (UserFolderInfo) tag;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001205 final ArrayList<ShortcutInfo> contents = info.contents;
1206 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
Romain Guy629de3e2010-01-13 12:20:59 -08001207 final int contentsCount = contents.size();
1208 boolean removedFromFolder = false;
1209
1210 for (int k = 0; k < contentsCount; k++) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001211 final ShortcutInfo appInfo = contents.get(k);
Romain Guy629de3e2010-01-13 12:20:59 -08001212 final Intent intent = appInfo.intent;
1213 final ComponentName name = intent.getComponent();
1214
1215 if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
1216 name != null && packageName.equals(name.getPackageName())) {
1217 toRemove.add(appInfo);
1218 // TODO: This should probably be done on a worker thread
1219 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
1220 removedFromFolder = true;
1221 }
1222 }
1223
1224 contents.removeAll(toRemove);
1225 if (removedFromFolder) {
1226 final Folder folder = getOpenFolder();
1227 if (folder != null) folder.notifyDataSetChanged();
1228 }
1229 } else if (tag instanceof LiveFolderInfo) {
1230 final LiveFolderInfo info = (LiveFolderInfo) tag;
1231 final Uri uri = info.uri;
1232 final ProviderInfo providerInfo = manager.resolveContentProvider(
1233 uri.getAuthority(), 0);
Romain Guy574d20e2009-06-01 15:34:04 -07001234
Romain Guy629de3e2010-01-13 12:20:59 -08001235 if (providerInfo == null ||
1236 packageName.equals(providerInfo.packageName)) {
1237 // TODO: This should probably be done on a worker thread
1238 LauncherModel.deleteItemFromDatabase(mLauncher, info);
1239 childrenToRemove.add(view);
1240 }
1241 } else if (tag instanceof LauncherAppWidgetInfo) {
1242 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1243 final AppWidgetProviderInfo provider =
1244 widgets.getAppWidgetInfo(info.appWidgetId);
1245 if (provider == null ||
1246 packageName.equals(provider.provider.getPackageName())) {
1247 // TODO: This should probably be done on a worker thread
1248 LauncherModel.deleteItemFromDatabase(mLauncher, info);
1249 childrenToRemove.add(view);
1250 }
Romain Guy574d20e2009-06-01 15:34:04 -07001251 }
1252 }
Romain Guy629de3e2010-01-13 12:20:59 -08001253
1254 childCount = childrenToRemove.size();
1255 for (int j = 0; j < childCount; j++) {
1256 View child = childrenToRemove.get(j);
1257 layout.removeViewInLayout(child);
1258 if (child instanceof DropTarget) {
1259 mDragController.removeDropTarget((DropTarget)child);
1260 }
Romain Guy574d20e2009-06-01 15:34:04 -07001261 }
Romain Guy629de3e2010-01-13 12:20:59 -08001262
1263 if (childCount > 0) {
1264 layout.requestLayout();
1265 layout.invalidate();
Romain Guy5c16f3e2010-01-12 17:24:58 -08001266 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001267 }
Romain Guy629de3e2010-01-13 12:20:59 -08001268 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001269 }
1270 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001271
1272 void updateShortcutsForPackage(String packageName) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001273 final PackageManager pm = mLauncher.getPackageManager();
1274
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001275 final int count = getChildCount();
1276 for (int i = 0; i < count; i++) {
1277 final CellLayout layout = (CellLayout) getChildAt(i);
1278 int childCount = layout.getChildCount();
1279 for (int j = 0; j < childCount; j++) {
1280 final View view = layout.getChildAt(j);
1281 Object tag = view.getTag();
Joe Onorato0589f0f2010-02-08 13:44:00 -08001282 if (tag instanceof ShortcutInfo) {
1283 ShortcutInfo info = (ShortcutInfo)tag;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001284 // We need to check for ACTION_MAIN otherwise getComponent() might
1285 // return null for some shortcuts (for instance, for shortcuts to
1286 // web pages.)
1287 final Intent intent = info.intent;
1288 final ComponentName name = intent.getComponent();
1289 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1290 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null &&
1291 packageName.equals(name.getPackageName())) {
1292
Joe Onorato0589f0f2010-02-08 13:44:00 -08001293 info.setIcon(mIconCache.getIcon(info.intent));
1294 ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null,
1295 new FastBitmapDrawable(info.getIcon(mIconCache)), null, null);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001296 }
1297 }
1298 }
1299 }
1300 }
1301
Joe Onorato14f122b2009-11-19 14:06:36 -08001302 void moveToDefaultScreen(boolean animate) {
Joe Onoratoc45b1682010-01-11 18:48:40 -05001303 if (animate) {
1304 snapToScreen(mDefaultScreen);
1305 } else {
1306 setCurrentScreen(mDefaultScreen);
1307 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001308 getChildAt(mDefaultScreen).requestFocus();
1309 }
1310
Romain Guy8a73c512009-11-09 19:19:59 -08001311 void setIndicators(Drawable previous, Drawable next) {
1312 mPreviousIndicator = previous;
1313 mNextIndicator = next;
1314 previous.setLevel(mCurrentScreen);
1315 next.setLevel(mCurrentScreen);
1316 }
1317
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001318 public static class SavedState extends BaseSavedState {
1319 int currentScreen = -1;
1320
1321 SavedState(Parcelable superState) {
1322 super(superState);
1323 }
1324
1325 private SavedState(Parcel in) {
1326 super(in);
1327 currentScreen = in.readInt();
1328 }
1329
1330 @Override
1331 public void writeToParcel(Parcel out, int flags) {
1332 super.writeToParcel(out, flags);
1333 out.writeInt(currentScreen);
1334 }
1335
1336 public static final Parcelable.Creator<SavedState> CREATOR =
1337 new Parcelable.Creator<SavedState>() {
1338 public SavedState createFromParcel(Parcel in) {
1339 return new SavedState(in);
1340 }
1341
1342 public SavedState[] newArray(int size) {
1343 return new SavedState[size];
1344 }
1345 };
1346 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001347}