blob: 0f41cc9d9d3c7af8f23b1b50200af2c222ca9244 [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
Michael Jurkaaf442092010-06-10 17:01:57 -070019import com.android.launcher.R;
Adam Powell495f2892010-04-16 16:40:55 -070020
Dianne Hackborn8f573952009-08-10 23:21:09 -070021import android.app.WallpaperManager;
Romain Guy629de3e2010-01-13 12:20:59 -080022import android.appwidget.AppWidgetManager;
23import android.appwidget.AppWidgetProviderInfo;
Adam Powell495f2892010-04-16 16:40:55 -070024import android.content.ComponentName;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080025import android.content.Context;
26import android.content.Intent;
Adam Powell495f2892010-04-16 16:40:55 -070027import android.content.pm.PackageManager;
Romain Guy5c16f3e2010-01-12 17:24:58 -080028import android.content.pm.ProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080029import android.content.res.TypedArray;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.graphics.Canvas;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080031import android.graphics.Rect;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070032import android.graphics.drawable.Drawable;
Romain Guy5c16f3e2010-01-12 17:24:58 -080033import android.net.Uri;
Joe Onorato956091b2010-02-19 12:47:40 -080034import android.os.IBinder;
Joe Onorato080d9b62009-11-02 12:01:11 -050035import android.os.Parcel;
Adam Powell495f2892010-04-16 16:40:55 -070036import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import android.util.AttributeSet;
Daniel Sandler291ad122010-05-24 16:03:53 -040038import android.util.Log;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.view.MotionEvent;
40import android.view.VelocityTracker;
41import android.view.View;
42import android.view.ViewConfiguration;
43import android.view.ViewGroup;
44import android.view.ViewParent;
Winson Chungaafa03c2010-06-11 17:34:16 -070045import android.view.animation.Animation;
Adam Powell2762dd82010-04-26 11:52:44 -070046import android.view.animation.Interpolator;
Winson Chungaafa03c2010-06-11 17:34:16 -070047import android.view.animation.RotateAnimation;
48import android.view.animation.Animation.AnimationListener;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049import android.widget.Scroller;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070050import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051
Michael Jurkaaf442092010-06-10 17:01:57 -070052import java.util.ArrayList;
53import java.util.HashSet;
Romain Guyedcce092010-03-04 13:03:17 -080054
The Android Open Source Project31dd5032009-03-03 19:32:27 -080055/**
Winson Chungaafa03c2010-06-11 17:34:16 -070056 * The workspace is a wide area with a wallpaper and a finite number of screens.
57 * Each screen contains a number of icons, folders or widgets the user can
58 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059 */
Joe Onorato85a02a82009-09-08 12:34:22 -070060public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
Romain Guye47f55c2009-11-11 19:21:22 -080061 @SuppressWarnings({"UnusedDeclaration"})
Joe Onorato3a8820b2009-11-10 15:06:42 -080062 private static final String TAG = "Launcher.Workspace";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080063 private static final int INVALID_SCREEN = -1;
Winson Chungaafa03c2010-06-11 17:34:16 -070064
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065 /**
Winson Chungaafa03c2010-06-11 17:34:16 -070066 * The velocity at which a fling gesture will cause us to snap to the next
67 * screen
The Android Open Source Project31dd5032009-03-03 19:32:27 -080068 */
Adam Powella588b872010-04-13 12:40:24 -070069 private static final int SNAP_VELOCITY = 600;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080070
Dianne Hackborn8f573952009-08-10 23:21:09 -070071 private final WallpaperManager mWallpaperManager;
Winson Chungaafa03c2010-06-11 17:34:16 -070072
The Android Open Source Project31dd5032009-03-03 19:32:27 -080073 private int mDefaultScreen;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080074
75 private boolean mFirstLayout = true;
76
77 private int mCurrentScreen;
78 private int mNextScreen = INVALID_SCREEN;
79 private Scroller mScroller;
80 private VelocityTracker mVelocityTracker;
81
82 /**
83 * CellInfo for the cell that is currently being dragged
84 */
85 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -070086
Jeff Sharkey70864282009-04-07 21:08:40 -070087 /**
88 * Target drop area calculated during last acceptDrop call.
89 */
90 private int[] mTargetCell = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080091
92 private float mLastMotionX;
93 private float mLastMotionY;
Winson Chungaafa03c2010-06-11 17:34:16 -070094
The Android Open Source Project31dd5032009-03-03 19:32:27 -080095 private final static int TOUCH_STATE_REST = 0;
96 private final static int TOUCH_STATE_SCROLLING = 1;
97
98 private int mTouchState = TOUCH_STATE_REST;
99
100 private OnLongClickListener mLongClickListener;
101
102 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800103 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -0400104 private DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700105
Jeff Sharkey70864282009-04-07 21:08:40 -0700106 /**
107 * Cache of vacant cells, used during drag events and invalidated as needed.
108 */
109 private CellLayout.CellInfo mVacantCache = null;
Winson Chungaafa03c2010-06-11 17:34:16 -0700110
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800111 private int[] mTempCell = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700112 private int[] mTempEstimate = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113
Joe Onoratoa9c28f62009-09-14 18:38:49 -0400114 private boolean mAllowLongPress = true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800115
116 private int mTouchSlop;
Romain Guya206daa2009-07-06 11:51:18 -0700117 private int mMaximumVelocity;
Winson Chungaafa03c2010-06-11 17:34:16 -0700118
Adam Powell3499d4a2010-03-09 19:12:45 -0800119 private static final int INVALID_POINTER = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800120
Adam Powell3499d4a2010-03-09 19:12:45 -0800121 private int mActivePointerId = INVALID_POINTER;
Winson Chungaafa03c2010-06-11 17:34:16 -0700122
Romain Guy8a73c512009-11-09 19:19:59 -0800123 private Drawable mPreviousIndicator;
124 private Drawable mNextIndicator;
Winson Chungaafa03c2010-06-11 17:34:16 -0700125
Adam Powellfe8e2792010-05-04 11:34:24 -0700126 private static final float NANOTIME_DIV = 1000000000.0f;
127 private static final float SMOOTHING_SPEED = 0.75f;
128 private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
129 private float mSmoothingTime;
130 private float mTouchX;
Romain Guy8a73c512009-11-09 19:19:59 -0800131
Adam Powell2762dd82010-04-26 11:52:44 -0700132 private WorkspaceOvershootInterpolator mScrollInterpolator;
133
Adam Powelld40613a2010-04-26 16:43:44 -0700134 private static final float BASELINE_FLING_VELOCITY = 2500.f;
135 private static final float FLING_VELOCITY_INFLUENCE = 0.4f;
Winson Chungaafa03c2010-06-11 17:34:16 -0700136
Adam Powell2762dd82010-04-26 11:52:44 -0700137 private static class WorkspaceOvershootInterpolator implements Interpolator {
Adam Powelld40613a2010-04-26 16:43:44 -0700138 private static final float DEFAULT_TENSION = 1.3f;
Adam Powell2762dd82010-04-26 11:52:44 -0700139 private float mTension;
140
141 public WorkspaceOvershootInterpolator() {
142 mTension = DEFAULT_TENSION;
143 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700144
Adam Powell2762dd82010-04-26 11:52:44 -0700145 public void setDistance(int distance) {
146 mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
147 }
148
Adam Powelld40613a2010-04-26 16:43:44 -0700149 public void disableSettle() {
150 mTension = 0.f;
151 }
152
Adam Powell2762dd82010-04-26 11:52:44 -0700153 public float getInterpolation(float t) {
154 // _o(t) = t * t * ((tension + 1) * t + tension)
155 // o(t) = _o(t - 1) + 1
156 t -= 1.0f;
157 return t * t * ((mTension + 1) * t + mTension) + 1.0f;
158 }
159 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700160
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800161 /**
162 * Used to inflate the Workspace from XML.
163 *
164 * @param context The application's context.
165 * @param attrs The attribtues set containing the Workspace's customization values.
166 */
167 public Workspace(Context context, AttributeSet attrs) {
168 this(context, attrs, 0);
169 }
170
171 /**
172 * Used to inflate the Workspace from XML.
173 *
174 * @param context The application's context.
175 * @param attrs The attribtues set containing the Workspace's customization values.
176 * @param defStyle Unused.
177 */
178 public Workspace(Context context, AttributeSet attrs, int defStyle) {
179 super(context, attrs, defStyle);
180
Dianne Hackborn8f573952009-08-10 23:21:09 -0700181 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700182
183 TypedArray a = context.obtainStyledAttributes(attrs,
184 R.styleable.Workspace, defStyle, 0);
185 int canonicalDeviceWidth = a.getInt(R.styleable.Workspace_canonicalDeviceWidth, 4);
186 int canonicalDeviceHeight = a.getInt(R.styleable.Workspace_canonicalDeviceHeight, 4);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800187 mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
188 a.recycle();
189
Winson Chungaafa03c2010-06-11 17:34:16 -0700190 LauncherModel.updateWorkspaceLayoutCells(canonicalDeviceWidth,
191 canonicalDeviceHeight);
Joe Onorato0d44e942009-11-16 18:20:51 -0800192 setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800193 initWorkspace();
194 }
195
196 /**
197 * Initializes various states for this workspace.
198 */
199 private void initWorkspace() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800200 Context context = getContext();
Adam Powell2762dd82010-04-26 11:52:44 -0700201 mScrollInterpolator = new WorkspaceOvershootInterpolator();
202 mScroller = new Scroller(context, mScrollInterpolator);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800203 mCurrentScreen = mDefaultScreen;
204 Launcher.setScreen(mCurrentScreen);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800205 LauncherApplication app = (LauncherApplication)context.getApplicationContext();
206 mIconCache = app.getIconCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800207
Romain Guya206daa2009-07-06 11:51:18 -0700208 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
209 mTouchSlop = configuration.getScaledTouchSlop();
210 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800211 }
212
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800213 @Override
214 public void addView(View child, int index, LayoutParams params) {
215 if (!(child instanceof CellLayout)) {
216 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
217 }
218 super.addView(child, index, params);
219 }
220
221 @Override
222 public void addView(View child) {
223 if (!(child instanceof CellLayout)) {
224 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
225 }
226 super.addView(child);
227 }
228
229 @Override
230 public void addView(View child, int index) {
231 if (!(child instanceof CellLayout)) {
232 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
233 }
234 super.addView(child, index);
235 }
236
237 @Override
238 public void addView(View child, int width, int height) {
239 if (!(child instanceof CellLayout)) {
240 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
241 }
242 super.addView(child, width, height);
243 }
244
245 @Override
246 public void addView(View child, LayoutParams params) {
247 if (!(child instanceof CellLayout)) {
248 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
249 }
250 super.addView(child, params);
251 }
252
253 /**
254 * @return The open folder on the current screen, or null if there is none
255 */
256 Folder getOpenFolder() {
257 CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
258 int count = currentScreen.getChildCount();
259 for (int i = 0; i < count; i++) {
260 View child = currentScreen.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700261 if (child instanceof Folder) {
262 Folder folder = (Folder) child;
263 if (folder.getInfo().opened)
264 return folder;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800265 }
266 }
267 return null;
268 }
269
270 ArrayList<Folder> getOpenFolders() {
271 final int screens = getChildCount();
272 ArrayList<Folder> folders = new ArrayList<Folder>(screens);
273
274 for (int screen = 0; screen < screens; screen++) {
275 CellLayout currentScreen = (CellLayout) getChildAt(screen);
276 int count = currentScreen.getChildCount();
277 for (int i = 0; i < count; i++) {
278 View child = currentScreen.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700279 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child
280 .getLayoutParams();
281 if (child instanceof Folder) {
282 Folder folder = (Folder) child;
283 if (folder.getInfo().opened)
284 folders.add(folder);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800285 break;
286 }
287 }
288 }
289
290 return folders;
291 }
292
293 boolean isDefaultScreenShowing() {
294 return mCurrentScreen == mDefaultScreen;
295 }
296
297 /**
298 * Returns the index of the currently displayed screen.
299 *
300 * @return The index of the currently displayed screen.
301 */
302 int getCurrentScreen() {
303 return mCurrentScreen;
304 }
305
306 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800307 * Sets the current screen.
308 *
309 * @param currentScreen
310 */
311 void setCurrentScreen(int currentScreen) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700312 if (!mScroller.isFinished())
313 mScroller.abortAnimation();
Romain Guy4c58c482009-05-12 17:35:41 -0700314 clearVacantCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800315 mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
Winson Chungaafa03c2010-06-11 17:34:16 -0700316 if (mPreviousIndicator != null) {
317 mPreviousIndicator.setLevel(mCurrentScreen);
318 mNextIndicator.setLevel(mCurrentScreen);
319 }
320
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800321 scrollTo(mCurrentScreen * getWidth(), 0);
Romain Guyed2390e2010-02-18 00:22:58 +0100322 updateWallpaperOffset();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800323 invalidate();
324 }
325
326 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800327 * Adds the specified child in the current screen. The position and dimension of
328 * the child are defined by x, y, spanX and spanY.
329 *
330 * @param child The child to add in one of the workspace's screens.
331 * @param x The X position of the child in the screen's grid.
332 * @param y The Y position of the child in the screen's grid.
333 * @param spanX The number of cells spanned horizontally by the child.
334 * @param spanY The number of cells spanned vertically by the child.
335 */
336 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
337 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
338 }
339
340 /**
341 * Adds the specified child in the current screen. The position and dimension of
342 * the child are defined by x, y, spanX and spanY.
343 *
344 * @param child The child to add in one of the workspace's screens.
345 * @param x The X position of the child in the screen's grid.
346 * @param y The Y position of the child in the screen's grid.
347 * @param spanX The number of cells spanned horizontally by the child.
348 * @param spanY The number of cells spanned vertically by the child.
349 * @param insert When true, the child is inserted at the beginning of the children list.
350 */
351 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
352 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
353 }
354
355 /**
356 * Adds the specified child in the specified screen. The position and dimension of
357 * the child are defined by x, y, spanX and spanY.
358 *
359 * @param child The child to add in one of the workspace's screens.
360 * @param screen The screen in which to add the child.
361 * @param x The X position of the child in the screen's grid.
362 * @param y The Y position of the child in the screen's grid.
363 * @param spanX The number of cells spanned horizontally by the child.
364 * @param spanY The number of cells spanned vertically by the child.
365 */
366 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
367 addInScreen(child, screen, x, y, spanX, spanY, false);
368 }
369
Winson Chungaafa03c2010-06-11 17:34:16 -0700370 void addInFullScreen(View child, int screen) {
371 addInScreen(child, screen, 0, 0, -1, -1);
372 }
373
374 public void rotateCurrentScreensChildren() {
375
376 // close all the folders first
377 final ArrayList<Folder> openFolders = getOpenFolders();
378
379 WorkspaceOvershootInterpolator wi = new WorkspaceOvershootInterpolator();
380 RotateAnimation ra = new RotateAnimation((float) LauncherModel
381 .getPreviousOrientationRelativeToCurrent(), 0,
382 Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
383 0.5f);
384 ra.setInterpolator(wi);
385 CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
386 ra.setStartOffset(150);
387 ra.setDuration(650 + (int) (Math.random() * 400) - 200);
388
389 CellLayout.CellLayoutAnimationController animationController = new CellLayout.CellLayoutAnimationController(
390 ra, 0.0f);
391 currentScreen.setLayoutAnimation(animationController);
392 currentScreen.setLayoutAnimationListener(new AnimationListener() {
393 public void onAnimationStart(Animation animation) {
394 // do nothing
395 }
396
397 public void onAnimationRepeat(Animation animation) {
398 // do nothing
399 }
400
401 public void onAnimationEnd(Animation animation) {
402 for (int j = 0; j < openFolders.size(); ++j) {
403 Folder folder = openFolders.get(j);
404 if (!folder.getInfo().opened) {
405 mLauncher.openFolder(folder.getInfo());
406 }
407 }
408 }
409 });
410 animationController.start();
411
412 for (int j = 0; j < openFolders.size(); ++j) {
413 mLauncher.closeFolder(openFolders.get(j));
414 }
415 }
416
417 public void refreshWorkspaceChildren() {
418 final int count = getChildCount();
419 View child;
420
421 CellLayout.LayoutParams lp;
422 int widthMeasureSpec = MeasureSpec.makeMeasureSpec(LauncherModel
423 .getLocalDeviceWidth(), MeasureSpec.EXACTLY);
424 int heightMeasureSpec = MeasureSpec.makeMeasureSpec(LauncherModel
425 .getLocalDeviceHeight(), MeasureSpec.EXACTLY);
426
427 clearVacantCache();
428
429 for (int i = 0; i < count; i++) {
430 final CellLayout layout = (CellLayout) getChildAt(i);
431 int numChildren = layout.getChildCount();
432
433 // save reference to all current children
434 for (int j = 0; j < numChildren; j++) {
435 child = layout.getChildAt(j);
436
437 lp = (CellLayout.LayoutParams) child.getLayoutParams();
438 LauncherModelOrientationHelper.Coordinates localCoord = LauncherModel
439 .getLocalCoordinatesFromPreviousLocalCoordinates(lp);
440
441 lp.cellX = localCoord.x;
442 lp.cellY = localCoord.y;
443 }
444
445 layout.measure(widthMeasureSpec, heightMeasureSpec);
446 }
447 }
448
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800449 /**
450 * Adds the specified child in the specified screen. The position and dimension of
451 * the child are defined by x, y, spanX and spanY.
452 *
453 * @param child The child to add in one of the workspace's screens.
454 * @param screen The screen in which to add the child.
455 * @param x The X position of the child in the screen's grid.
456 * @param y The Y position of the child in the screen's grid.
457 * @param spanX The number of cells spanned horizontally by the child.
458 * @param spanY The number of cells spanned vertically by the child.
459 * @param insert When true, the child is inserted at the beginning of the children list.
460 */
461 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
462 if (screen < 0 || screen >= getChildCount()) {
Daniel Sandler291ad122010-05-24 16:03:53 -0400463 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
464 + " (was " + screen + "); skipping child");
465 return;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800466 }
467
Romain Guy4c58c482009-05-12 17:35:41 -0700468 clearVacantCache();
469
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800470 final CellLayout group = (CellLayout) getChildAt(screen);
471 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
472 if (lp == null) {
473 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
474 } else {
475 lp.cellX = x;
476 lp.cellY = y;
477 lp.cellHSpan = spanX;
478 lp.cellVSpan = spanY;
479 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700480
481 // get the canonical child id to uniquely represent this view in this
482 // screen
483 int childId = LauncherModel.getCanonicalCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY);
484 if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) {
485 // TODO: This branch occurs when the workspace is adding views
486 // outside of the defined grid
487 // maybe we should be deleting these items from the LauncherMode?
488 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
489 }
490
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800491 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -0800492 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800493 child.setOnLongClickListener(mLongClickListener);
494 }
Joe Onorato00acb122009-08-04 16:04:30 -0400495 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700496 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -0400497 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800498 }
499
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800500 CellLayout.CellInfo findAllVacantCells(boolean[] occupied) {
501 CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
502 if (group != null) {
Jeff Sharkey70864282009-04-07 21:08:40 -0700503 return group.findAllVacantCells(occupied, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800504 }
505 return null;
506 }
507
Romain Guy4c58c482009-05-12 17:35:41 -0700508 private void clearVacantCache() {
509 if (mVacantCache != null) {
510 mVacantCache.clearVacantCells();
511 mVacantCache = null;
512 }
513 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800514
515 /**
516 * Registers the specified listener on each screen contained in this workspace.
517 *
518 * @param l The listener used to respond to long clicks.
519 */
520 @Override
521 public void setOnLongClickListener(OnLongClickListener l) {
522 mLongClickListener = l;
523 final int count = getChildCount();
524 for (int i = 0; i < count; i++) {
525 getChildAt(i).setOnLongClickListener(l);
526 }
527 }
528
Dianne Hackborn8f573952009-08-10 23:21:09 -0700529 private void updateWallpaperOffset() {
Romain Guy798300c2009-08-11 14:43:29 -0700530 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
531 }
532
533 private void updateWallpaperOffset(int scrollRange) {
Joe Onorato956091b2010-02-19 12:47:40 -0800534 IBinder token = getWindowToken();
535 if (token != null) {
536 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
Adam Powell495f2892010-04-16 16:40:55 -0700537 mWallpaperManager.setWallpaperOffsets(getWindowToken(),
538 Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
Joe Onorato956091b2010-02-19 12:47:40 -0800539 }
Dianne Hackborn8f573952009-08-10 23:21:09 -0700540 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700541
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800542 @Override
Adam Powellfe8e2792010-05-04 11:34:24 -0700543 public void scrollTo(int x, int y) {
544 super.scrollTo(x, y);
545 mTouchX = x;
546 mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
547 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700548
Adam Powellfe8e2792010-05-04 11:34:24 -0700549 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800550 public void computeScroll() {
551 if (mScroller.computeScrollOffset()) {
Adam Powellfe8e2792010-05-04 11:34:24 -0700552 mTouchX = mScrollX = mScroller.getCurrX();
553 mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800554 mScrollY = mScroller.getCurrY();
Dianne Hackborn8f573952009-08-10 23:21:09 -0700555 updateWallpaperOffset();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800556 postInvalidate();
557 } else if (mNextScreen != INVALID_SCREEN) {
558 mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
Michael Jurkaaf442092010-06-10 17:01:57 -0700559 if (mPreviousIndicator != null) {
560 mPreviousIndicator.setLevel(mCurrentScreen);
561 mNextIndicator.setLevel(mCurrentScreen);
562 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800563 Launcher.setScreen(mCurrentScreen);
564 mNextScreen = INVALID_SCREEN;
565 clearChildrenCache();
Adam Powellfe8e2792010-05-04 11:34:24 -0700566 } else if (mTouchState == TOUCH_STATE_SCROLLING) {
567 final float now = System.nanoTime() / NANOTIME_DIV;
568 final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
569 final float dx = mTouchX - mScrollX;
570 mScrollX += dx * e;
571 mSmoothingTime = now;
572
573 // Keep generating points as long as we're more than 1px away from the target
574 if (dx > 1.f || dx < -1.f) {
575 updateWallpaperOffset();
576 postInvalidate();
577 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800578 }
579 }
580
581 @Override
582 protected void dispatchDraw(Canvas canvas) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700583 boolean restore = false;
Joe Onoratoe77c08d2009-08-01 00:01:20 -0700584 int restoreCount = 0;
585
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800586 // ViewGroup.dispatchDraw() supports many features we don't need:
587 // clip to padding, layout animation, animation listener, disappearing
588 // children, etc. The following implementation attempts to fast-track
589 // the drawing dispatch by drawing only what we know needs to be drawn.
590
Romain Guyf8e6a802009-12-07 17:48:02 -0800591 boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800592 // If we are not scrolling or flinging, draw only the current screen
593 if (fastDraw) {
594 drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
595 } else {
596 final long drawingTime = getDrawingTime();
Adam Powelld40613a2010-04-26 16:43:44 -0700597 final float scrollPos = (float) mScrollX / getWidth();
598 final int leftScreen = (int) scrollPos;
599 final int rightScreen = leftScreen + 1;
600 if (leftScreen >= 0) {
601 drawChild(canvas, getChildAt(leftScreen), drawingTime);
602 }
603 if (scrollPos != leftScreen && rightScreen < getChildCount()) {
604 drawChild(canvas, getChildAt(rightScreen), drawingTime);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800605 }
606 }
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700607
608 if (restore) {
Joe Onoratoe77c08d2009-08-01 00:01:20 -0700609 canvas.restoreToCount(restoreCount);
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700610 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800611 }
612
Joe Onorato00acb122009-08-04 16:04:30 -0400613 protected void onAttachedToWindow() {
614 super.onAttachedToWindow();
Joe Onorato956091b2010-02-19 12:47:40 -0800615 computeScroll();
Joe Onorato00acb122009-08-04 16:04:30 -0400616 mDragController.setWindowToken(getWindowToken());
617 }
618
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800619 @Override
620 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
621 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
622
623 final int width = MeasureSpec.getSize(widthMeasureSpec);
624 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
625 if (widthMode != MeasureSpec.EXACTLY) {
626 throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
627 }
628
629 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
630 if (heightMode != MeasureSpec.EXACTLY) {
631 throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
632 }
633
634 // The children are given the same width and height as the workspace
635 final int count = getChildCount();
636 for (int i = 0; i < count; i++) {
637 getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
638 }
639
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800640 if (mFirstLayout) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800641 setHorizontalScrollBarEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800642 scrollTo(mCurrentScreen * width, 0);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800643 setHorizontalScrollBarEnabled(true);
Romain Guy798300c2009-08-11 14:43:29 -0700644 updateWallpaperOffset(width * (getChildCount() - 1));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800645 mFirstLayout = false;
646 }
647 }
648
649 @Override
650 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
651 int childLeft = 0;
652
653 final int count = getChildCount();
654 for (int i = 0; i < count; i++) {
655 final View child = getChildAt(i);
656 if (child.getVisibility() != View.GONE) {
657 final int childWidth = child.getMeasuredWidth();
658 child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
659 childLeft += childWidth;
660 }
661 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700662
663 if (LauncherApplication.isInPlaceRotationEnabled()) {
664 // When the device is rotated, the scroll position of the current screen
665 // needs to be refreshed
666 setCurrentScreen(getCurrentScreen());
667 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800668 }
669
670 @Override
671 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
672 int screen = indexOfChild(child);
673 if (screen != mCurrentScreen || !mScroller.isFinished()) {
674 if (!mLauncher.isWorkspaceLocked()) {
675 snapToScreen(screen);
676 }
677 return true;
678 }
679 return false;
680 }
681
682 @Override
683 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -0400684 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800685 final Folder openFolder = getOpenFolder();
686 if (openFolder != null) {
687 return openFolder.requestFocus(direction, previouslyFocusedRect);
688 } else {
689 int focusableScreen;
690 if (mNextScreen != INVALID_SCREEN) {
691 focusableScreen = mNextScreen;
692 } else {
693 focusableScreen = mCurrentScreen;
694 }
695 getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
696 }
697 }
698 return false;
699 }
700
701 @Override
702 public boolean dispatchUnhandledMove(View focused, int direction) {
703 if (direction == View.FOCUS_LEFT) {
704 if (getCurrentScreen() > 0) {
705 snapToScreen(getCurrentScreen() - 1);
706 return true;
707 }
708 } else if (direction == View.FOCUS_RIGHT) {
709 if (getCurrentScreen() < getChildCount() - 1) {
710 snapToScreen(getCurrentScreen() + 1);
711 return true;
712 }
713 }
714 return super.dispatchUnhandledMove(focused, direction);
715 }
716
717 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -0700718 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -0400719 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800720 final Folder openFolder = getOpenFolder();
721 if (openFolder == null) {
722 getChildAt(mCurrentScreen).addFocusables(views, direction);
723 if (direction == View.FOCUS_LEFT) {
724 if (mCurrentScreen > 0) {
725 getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
726 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700727 } else if (direction == View.FOCUS_RIGHT) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800728 if (mCurrentScreen < getChildCount() - 1) {
729 getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
730 }
731 }
732 } else {
733 openFolder.addFocusables(views, direction);
734 }
735 }
736 }
737
738 @Override
Joe Onorato7bb17492009-09-24 17:51:01 -0700739 public boolean dispatchTouchEvent(MotionEvent ev) {
740 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
741 if (mLauncher.isWorkspaceLocked() || mLauncher.isAllAppsVisible()) {
742 return false;
743 }
744 }
745 return super.dispatchTouchEvent(ev);
746 }
747
748 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800749 public boolean onInterceptTouchEvent(MotionEvent ev) {
Joe Onorato68ba5ca2009-11-12 14:23:43 -0800750 final boolean workspaceLocked = mLauncher.isWorkspaceLocked();
751 final boolean allAppsVisible = mLauncher.isAllAppsVisible();
Joe Onorato68ba5ca2009-11-12 14:23:43 -0800752 if (workspaceLocked || allAppsVisible) {
Joe Onorato7c312c12009-08-13 21:36:53 -0700753 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 -0800754 }
755
756 /*
757 * This method JUST determines whether we want to intercept the motion.
758 * If we return true, onTouchEvent will be called and we do the actual
759 * scrolling there.
760 */
761
762 /*
763 * Shortcut the most recurring case: the user is in the dragging
764 * state and he is moving his finger. We want to intercept this
765 * motion.
766 */
767 final int action = ev.getAction();
768 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
769 return true;
770 }
771
Adam Powella588b872010-04-13 12:40:24 -0700772 if (mVelocityTracker == null) {
773 mVelocityTracker = VelocityTracker.obtain();
774 }
775 mVelocityTracker.addMovement(ev);
Winson Chungaafa03c2010-06-11 17:34:16 -0700776
Adam Powell3499d4a2010-03-09 19:12:45 -0800777 switch (action & MotionEvent.ACTION_MASK) {
778 case MotionEvent.ACTION_MOVE: {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800779 /*
780 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
781 * whether the user has moved far enough from his original down touch.
782 */
783
784 /*
785 * Locally do absolute value. mLastMotionX is set to the y value
786 * of the down event.
787 */
Adam Powell3499d4a2010-03-09 19:12:45 -0800788 final int pointerIndex = ev.findPointerIndex(mActivePointerId);
789 final float x = ev.getX(pointerIndex);
790 final float y = ev.getY(pointerIndex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800791 final int xDiff = (int) Math.abs(x - mLastMotionX);
792 final int yDiff = (int) Math.abs(y - mLastMotionY);
793
Adam Powellc36f2452010-03-10 11:08:17 -0800794 final int touchSlop = mTouchSlop;
Adam Powellc36f2452010-03-10 11:08:17 -0800795 boolean xMoved = xDiff > touchSlop;
796 boolean yMoved = yDiff > touchSlop;
Winson Chungaafa03c2010-06-11 17:34:16 -0700797
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800798 if (xMoved || yMoved) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700799
Adam Powellfe8e2792010-05-04 11:34:24 -0700800 if (xMoved) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800801 // Scroll if the user moved far enough along the X axis
802 mTouchState = TOUCH_STATE_SCROLLING;
Adam Powell3499d4a2010-03-09 19:12:45 -0800803 mLastMotionX = x;
Adam Powellfe8e2792010-05-04 11:34:24 -0700804 mTouchX = mScrollX;
805 mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
Adam Powellfefa0ce2010-05-03 10:23:50 -0700806 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800807 }
808 // Either way, cancel any pending longpress
809 if (mAllowLongPress) {
810 mAllowLongPress = false;
811 // Try canceling the long press. It could also have been scheduled
812 // by a distant descendant, so use the mAllowLongPress flag to block
813 // everything
814 final View currentScreen = getChildAt(mCurrentScreen);
815 currentScreen.cancelLongPress();
816 }
817 }
818 break;
Adam Powell3499d4a2010-03-09 19:12:45 -0800819 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800820
Winson Chungaafa03c2010-06-11 17:34:16 -0700821 case MotionEvent.ACTION_DOWN: {
822 final float x = ev.getX();
823 final float y = ev.getY();
824 // Remember location of down touch
825 mLastMotionX = x;
826 mLastMotionY = y;
827 mActivePointerId = ev.getPointerId(0);
828 mAllowLongPress = true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800829
830 /*
831 * If being flinged and user touches the screen, initiate drag;
832 * otherwise don't. mScroller.isFinished should be false when
833 * being flinged.
834 */
835 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
836 break;
Adam Powell3499d4a2010-03-09 19:12:45 -0800837 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800838
839 case MotionEvent.ACTION_CANCEL:
840 case MotionEvent.ACTION_UP:
Winson Chungaafa03c2010-06-11 17:34:16 -0700841
Mike Cleronf8bbd342009-10-23 16:15:16 -0700842 if (mTouchState != TOUCH_STATE_SCROLLING) {
Mike Cleronf8bbd342009-10-23 16:15:16 -0700843 final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
844 if (!currentScreen.lastDownOnOccupiedCell()) {
Romain Guy06762ab2010-01-25 16:51:08 -0800845 getLocationOnScreen(mTempCell);
Mike Cleronf8bbd342009-10-23 16:15:16 -0700846 // Send a tap to the wallpaper if the last down was on empty space
Adam Powell3499d4a2010-03-09 19:12:45 -0800847 final int pointerIndex = ev.findPointerIndex(mActivePointerId);
Winson Chungaafa03c2010-06-11 17:34:16 -0700848 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
Romain Guy06762ab2010-01-25 16:51:08 -0800849 "android.wallpaper.tap",
Adam Powell3499d4a2010-03-09 19:12:45 -0800850 mTempCell[0] + (int) ev.getX(pointerIndex),
851 mTempCell[1] + (int) ev.getY(pointerIndex), 0, null);
Mike Cleronf8bbd342009-10-23 16:15:16 -0700852 }
853 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700854
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800855 // Release the drag
856 clearChildrenCache();
857 mTouchState = TOUCH_STATE_REST;
Adam Powell3499d4a2010-03-09 19:12:45 -0800858 mActivePointerId = INVALID_POINTER;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800859 mAllowLongPress = false;
Winson Chungaafa03c2010-06-11 17:34:16 -0700860
Adam Powella588b872010-04-13 12:40:24 -0700861 if (mVelocityTracker != null) {
862 mVelocityTracker.recycle();
863 mVelocityTracker = null;
864 }
Mike Cleronf8bbd342009-10-23 16:15:16 -0700865
Winson Chungaafa03c2010-06-11 17:34:16 -0700866 break;
867
868 case MotionEvent.ACTION_POINTER_UP:
869 onSecondaryPointerUp(ev);
870 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800871 }
872
873 /*
874 * The only time we want to intercept motion events is if we are in the
875 * drag mode.
876 */
877 return mTouchState != TOUCH_STATE_REST;
878 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700879
Adam Powell3499d4a2010-03-09 19:12:45 -0800880 private void onSecondaryPointerUp(MotionEvent ev) {
881 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
882 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
883 final int pointerId = ev.getPointerId(pointerIndex);
884 if (pointerId == mActivePointerId) {
885 // This was our active pointer going up. Choose a new
886 // active pointer and adjust accordingly.
887 // TODO: Make this decision more intelligent.
888 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
889 mLastMotionX = ev.getX(newPointerIndex);
890 mLastMotionY = ev.getY(newPointerIndex);
891 mActivePointerId = ev.getPointerId(newPointerIndex);
892 if (mVelocityTracker != null) {
893 mVelocityTracker.clear();
894 }
895 }
896 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800897
Joe Onorato3a8820b2009-11-10 15:06:42 -0800898 /**
899 * If one of our descendant views decides that it could be focused now, only
900 * pass that along if it's on the current screen.
901 *
902 * This happens when live folders requery, and if they're off screen, they
903 * end up calling requestFocus, which pulls it on screen.
904 */
905 @Override
906 public void focusableViewAvailable(View focused) {
907 View current = getChildAt(mCurrentScreen);
908 View v = focused;
909 while (true) {
910 if (v == current) {
911 super.focusableViewAvailable(focused);
912 return;
913 }
914 if (v == this) {
915 return;
916 }
917 ViewParent parent = v.getParent();
918 if (parent instanceof View) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700919 v = (View) v.getParent();
Joe Onorato3a8820b2009-11-10 15:06:42 -0800920 } else {
921 return;
922 }
923 }
924 }
925
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800926 void enableChildrenCache(int fromScreen, int toScreen) {
927 if (fromScreen > toScreen) {
Adam Powellfea5d022010-04-29 11:42:45 -0700928 final int temp = fromScreen;
929 fromScreen = toScreen;
Adam Powelld40613a2010-04-26 16:43:44 -0700930 toScreen = temp;
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800931 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700932
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800933 final int count = getChildCount();
Adam Powellfea5d022010-04-29 11:42:45 -0700934
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800935 fromScreen = Math.max(fromScreen, 0);
936 toScreen = Math.min(toScreen, count - 1);
Adam Powellfea5d022010-04-29 11:42:45 -0700937
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800938 for (int i = fromScreen; i <= toScreen; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800939 final CellLayout layout = (CellLayout) getChildAt(i);
940 layout.setChildrenDrawnWithCacheEnabled(true);
941 layout.setChildrenDrawingCacheEnabled(true);
942 }
943 }
944
945 void clearChildrenCache() {
946 final int count = getChildCount();
947 for (int i = 0; i < count; i++) {
948 final CellLayout layout = (CellLayout) getChildAt(i);
949 layout.setChildrenDrawnWithCacheEnabled(false);
950 }
951 }
952
953 @Override
954 public boolean onTouchEvent(MotionEvent ev) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700955
Joe Onorato2bc6b7c2009-10-01 14:08:30 -0700956 if (mLauncher.isWorkspaceLocked()) {
957 return false; // We don't want the events. Let them fall through to the all apps view.
958 }
959 if (mLauncher.isAllAppsVisible()) {
960 // Cancel any scrolling that is in progress.
961 if (!mScroller.isFinished()) {
962 mScroller.abortAnimation();
963 }
964 snapToScreen(mCurrentScreen);
Joe Onorato7c312c12009-08-13 21:36:53 -0700965 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 -0800966 }
967
968 if (mVelocityTracker == null) {
969 mVelocityTracker = VelocityTracker.obtain();
970 }
971 mVelocityTracker.addMovement(ev);
972
973 final int action = ev.getAction();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800974
Adam Powell3499d4a2010-03-09 19:12:45 -0800975 switch (action & MotionEvent.ACTION_MASK) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800976 case MotionEvent.ACTION_DOWN:
977 /*
978 * If being flinged and user touches, stop the fling. isFinished
979 * will be false if being flinged.
980 */
981 if (!mScroller.isFinished()) {
982 mScroller.abortAnimation();
983 }
984
985 // Remember where the motion event started
Adam Powell3499d4a2010-03-09 19:12:45 -0800986 mLastMotionX = ev.getX();
987 mActivePointerId = ev.getPointerId(0);
Adam Powellfefa0ce2010-05-03 10:23:50 -0700988 if (mTouchState == TOUCH_STATE_SCROLLING) {
989 enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
990 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800991 break;
992 case MotionEvent.ACTION_MOVE:
993 if (mTouchState == TOUCH_STATE_SCROLLING) {
994 // Scroll to follow the motion event
Adam Powell3499d4a2010-03-09 19:12:45 -0800995 final int pointerIndex = ev.findPointerIndex(mActivePointerId);
Adam Powellfe8e2792010-05-04 11:34:24 -0700996 final float x = ev.getX(pointerIndex);
Adam Powellfea5d022010-04-29 11:42:45 -0700997 final float deltaX = mLastMotionX - x;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800998 mLastMotionX = x;
999
1000 if (deltaX < 0) {
Adam Powellfe8e2792010-05-04 11:34:24 -07001001 if (mTouchX > 0) {
1002 mTouchX += Math.max(-mTouchX, deltaX);
1003 mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
1004 invalidate();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001005 }
1006 } else if (deltaX > 0) {
Adam Powellfe8e2792010-05-04 11:34:24 -07001007 final float availableToScroll = getChildAt(getChildCount() - 1).getRight() -
1008 mTouchX - getWidth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001009 if (availableToScroll > 0) {
Adam Powellfe8e2792010-05-04 11:34:24 -07001010 mTouchX += Math.min(availableToScroll, deltaX);
1011 mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
1012 invalidate();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001013 }
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001014 } else {
1015 awakenScrollBars();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001016 }
1017 }
1018 break;
1019 case MotionEvent.ACTION_UP:
1020 if (mTouchState == TOUCH_STATE_SCROLLING) {
1021 final VelocityTracker velocityTracker = mVelocityTracker;
Romain Guya206daa2009-07-06 11:51:18 -07001022 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
Adam Powell3499d4a2010-03-09 19:12:45 -08001023 final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
Winson Chungaafa03c2010-06-11 17:34:16 -07001024
Adam Powell3499d4a2010-03-09 19:12:45 -08001025 final int screenWidth = getWidth();
1026 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
Adam Powell327a9a32010-04-02 10:41:38 -07001027 final float scrolledPos = (float) mScrollX / screenWidth;
Winson Chungaafa03c2010-06-11 17:34:16 -07001028
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001029 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
Adam Powell327a9a32010-04-02 10:41:38 -07001030 // Fling hard enough to move left.
1031 // Don't fling across more than one screen at a time.
1032 final int bound = scrolledPos < whichScreen ?
1033 mCurrentScreen - 1 : mCurrentScreen;
Adam Powelld40613a2010-04-26 16:43:44 -07001034 snapToScreen(Math.min(whichScreen, bound), velocityX, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001035 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
1036 // Fling hard enough to move right
Adam Powell327a9a32010-04-02 10:41:38 -07001037 // Don't fling across more than one screen at a time.
1038 final int bound = scrolledPos > whichScreen ?
1039 mCurrentScreen + 1 : mCurrentScreen;
Adam Powelld40613a2010-04-26 16:43:44 -07001040 snapToScreen(Math.max(whichScreen, bound), velocityX, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001041 } else {
Adam Powelld40613a2010-04-26 16:43:44 -07001042 snapToScreen(whichScreen, 0, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001043 }
1044
1045 if (mVelocityTracker != null) {
1046 mVelocityTracker.recycle();
1047 mVelocityTracker = null;
1048 }
1049 }
1050 mTouchState = TOUCH_STATE_REST;
Adam Powell3499d4a2010-03-09 19:12:45 -08001051 mActivePointerId = INVALID_POINTER;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001052 break;
1053 case MotionEvent.ACTION_CANCEL:
1054 mTouchState = TOUCH_STATE_REST;
Adam Powell3499d4a2010-03-09 19:12:45 -08001055 mActivePointerId = INVALID_POINTER;
1056 break;
1057 case MotionEvent.ACTION_POINTER_UP:
1058 onSecondaryPointerUp(ev);
1059 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001060 }
1061
1062 return true;
1063 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001064
Adam Powelld40613a2010-04-26 16:43:44 -07001065 void snapToScreen(int whichScreen) {
1066 snapToScreen(whichScreen, 0, false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001067 }
1068
Adam Powelld40613a2010-04-26 16:43:44 -07001069 private void snapToScreen(int whichScreen, int velocity, boolean settle) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001070 // if (!mScroller.isFinished()) return;
Romain Guy82d94d92009-05-06 17:43:13 -07001071
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001072 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
Winson Chungaafa03c2010-06-11 17:34:16 -07001073
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001074 clearVacantCache();
1075 enableChildrenCache(mCurrentScreen, whichScreen);
1076
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001077 mNextScreen = whichScreen;
Romain Guye47f55c2009-11-11 19:21:22 -08001078
Michael Jurkaaf442092010-06-10 17:01:57 -07001079 if (mPreviousIndicator != null) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001080 mPreviousIndicator.setLevel(mNextScreen);
1081 mNextIndicator.setLevel(mNextScreen);
Michael Jurkaaf442092010-06-10 17:01:57 -07001082 }
Romain Guyed2390e2010-02-18 00:22:58 +01001083
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001084 View focusedChild = getFocusedChild();
Adam Powelld40613a2010-04-26 16:43:44 -07001085 if (focusedChild != null && whichScreen != mCurrentScreen &&
1086 focusedChild == getChildAt(mCurrentScreen)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001087 focusedChild.clearFocus();
1088 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001089
Adam Powelld40613a2010-04-26 16:43:44 -07001090 final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001091 final int newX = whichScreen * getWidth();
1092 final int delta = newX - mScrollX;
Adam Powelld40613a2010-04-26 16:43:44 -07001093 int duration = (screenDelta + 1) * 100;
Romain Guyf8e6a802009-12-07 17:48:02 -08001094
Adam Powelld40613a2010-04-26 16:43:44 -07001095 if (!mScroller.isFinished()) {
1096 mScroller.abortAnimation();
1097 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001098
Adam Powelld40613a2010-04-26 16:43:44 -07001099 if (settle) {
1100 mScrollInterpolator.setDistance(screenDelta);
1101 } else {
1102 mScrollInterpolator.disableSettle();
1103 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001104
Adam Powelld40613a2010-04-26 16:43:44 -07001105 velocity = Math.abs(velocity);
1106 if (velocity > 0) {
1107 duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
1108 * FLING_VELOCITY_INFLUENCE;
1109 } else {
1110 duration += 100;
1111 }
1112
1113 awakenScrollBars(duration);
Joe Onoratoc45b1682010-01-11 18:48:40 -05001114 mScroller.startScroll(mScrollX, 0, delta, 0, duration);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001115 invalidate();
1116 }
1117
1118 void startDrag(CellLayout.CellInfo cellInfo) {
1119 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07001120
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001121 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +00001122 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001123 return;
1124 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001125
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001126 mDragInfo = cellInfo;
1127 mDragInfo.screen = mCurrentScreen;
Winson Chungaafa03c2010-06-11 17:34:16 -07001128
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001129 CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
1130
1131 current.onDragChild(child);
Joe Onorato00acb122009-08-04 16:04:30 -04001132 mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001133 invalidate();
1134 }
1135
1136 @Override
1137 protected Parcelable onSaveInstanceState() {
1138 final SavedState state = new SavedState(super.onSaveInstanceState());
1139 state.currentScreen = mCurrentScreen;
1140 return state;
1141 }
1142
1143 @Override
1144 protected void onRestoreInstanceState(Parcelable state) {
1145 SavedState savedState = (SavedState) state;
1146 super.onRestoreInstanceState(savedState.getSuperState());
1147 if (savedState.currentScreen != -1) {
1148 mCurrentScreen = savedState.currentScreen;
1149 Launcher.setScreen(mCurrentScreen);
1150 }
1151 }
1152
Joe Onorato0589f0f2010-02-08 13:44:00 -08001153 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001154 addApplicationShortcut(info, cellInfo, false);
1155 }
1156
Joe Onorato0589f0f2010-02-08 13:44:00 -08001157 void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001158 boolean insertAtFirst) {
1159 final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
1160 final int[] result = new int[2];
1161
1162 layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
1163 onDropExternal(result[0], result[1], info, layout, insertAtFirst);
1164 }
1165
Joe Onorato00acb122009-08-04 16:04:30 -04001166 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
1167 DragView dragView, Object dragInfo) {
Jeff Sharkey70864282009-04-07 21:08:40 -07001168 final CellLayout cellLayout = getCurrentDropLayout();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001169 if (source != this) {
1170 onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout);
1171 } else {
1172 // Move internally
1173 if (mDragInfo != null) {
1174 final View cell = mDragInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07001175 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
Romain Guy52d9cb32009-09-11 17:29:51 -07001176 if (index != mDragInfo.screen) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001177 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1178 originalCellLayout.removeView(cell);
Winson Chungaafa03c2010-06-11 17:34:16 -07001179 addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY,
1180 mDragInfo.spanX, mDragInfo.spanY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001181 }
Romain Guy263e0192009-05-11 11:50:46 -07001182 mTargetCell = estimateDropCell(x - xOffset, y - yOffset,
Winson Chungaafa03c2010-06-11 17:34:16 -07001183 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout,
1184 mTargetCell);
1185 cellLayout.onDropChild(cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001186
Winson Chungaafa03c2010-06-11 17:34:16 -07001187 // update the item's position after drop
Romain Guy84f296c2009-11-04 15:00:44 -08001188 final ItemInfo info = (ItemInfo) cell.getTag();
Winson Chungaafa03c2010-06-11 17:34:16 -07001189 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell
1190 .getLayoutParams();
1191 lp.cellX = mTargetCell[0];
1192 lp.cellY = mTargetCell[1];
1193
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001194 LauncherModel.moveItemInDatabase(mLauncher, info,
Winson Chungaafa03c2010-06-11 17:34:16 -07001195 LauncherSettings.Favorites.CONTAINER_DESKTOP, index,
1196 lp.cellX, lp.cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001197 }
1198 }
1199 }
1200
Winson Chungaafa03c2010-06-11 17:34:16 -07001201 public void onDragEnter(DragSource source, int x, int y, int xOffset,
1202 int yOffset, DragView dragView, Object dragInfo) {
Romain Guy4c58c482009-05-12 17:35:41 -07001203 clearVacantCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001204 }
1205
Winson Chungaafa03c2010-06-11 17:34:16 -07001206 public void onDragOver(DragSource source, int x, int y, int xOffset,
1207 int yOffset, DragView dragView, Object dragInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001208 }
1209
Winson Chungaafa03c2010-06-11 17:34:16 -07001210 public void onDragExit(DragSource source, int x, int y, int xOffset,
1211 int yOffset, DragView dragView, Object dragInfo) {
Romain Guy4c58c482009-05-12 17:35:41 -07001212 clearVacantCache();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001213 }
1214
Winson Chungaafa03c2010-06-11 17:34:16 -07001215 private void onDropExternal(int x, int y, Object dragInfo,
1216 CellLayout cellLayout) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001217 onDropExternal(x, y, dragInfo, cellLayout, false);
1218 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001219
1220 private void onDropExternal(int x, int y, Object dragInfo,
1221 CellLayout cellLayout, boolean insertAtFirst) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001222 // Drag from somewhere else
1223 ItemInfo info = (ItemInfo) dragInfo;
1224
Michael Jurkaaf442092010-06-10 17:01:57 -07001225 View view = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001226
1227 switch (info.itemType) {
1228 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1229 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Joe Onoratoe48e7c12010-02-19 13:10:40 -08001230 if (info.container == NO_ID && info instanceof ApplicationInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001231 // Came from all apps -- make a copy
Winson Chungaafa03c2010-06-11 17:34:16 -07001232 info = new ShortcutInfo((ApplicationInfo) info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001233 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001234 view = mLauncher.createShortcut(R.layout.application, cellLayout,
1235 (ShortcutInfo) info);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001236 break;
1237 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1238 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
Winson Chungaafa03c2010-06-11 17:34:16 -07001239 (ViewGroup) getChildAt(mCurrentScreen),
1240 ((UserFolderInfo) info));
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001241 break;
Michael Jurkaaf442092010-06-10 17:01:57 -07001242 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1243 cellLayout.setTagToCellInfoForPoint(x, y);
1244 mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName, cellLayout.getTag());
1245 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001246 default:
Winson Chungaafa03c2010-06-11 17:34:16 -07001247 throw new IllegalStateException("Unknown item type: "
1248 + info.itemType);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001249 }
1250
Michael Jurkaaf442092010-06-10 17:01:57 -07001251 // addAppWidgetFromDrop already took care of attaching the widget view to the appropriate cell
1252 // TODO why aren't we calling addInScreen here?
1253 if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001254 mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout,
1255 mTargetCell);
1256 addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
1257 mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
1258 cellLayout.onDropChild(view);
1259 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view
1260 .getLayoutParams();
Michael Jurkaaf442092010-06-10 17:01:57 -07001261
1262 LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
Winson Chungaafa03c2010-06-11 17:34:16 -07001263 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen,
1264 lp.cellX, lp.cellY);
Joe Onorato00acb122009-08-04 16:04:30 -04001265 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001266 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001267
Jeff Sharkey70864282009-04-07 21:08:40 -07001268 /**
1269 * Return the current {@link CellLayout}, correctly picking the destination
1270 * screen while a scroll is in progress.
1271 */
1272 private CellLayout getCurrentDropLayout() {
1273 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1274 return (CellLayout) getChildAt(index);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001275 }
1276
Jeff Sharkey70864282009-04-07 21:08:40 -07001277 /**
1278 * {@inheritDoc}
1279 */
1280 public boolean acceptDrop(DragSource source, int x, int y,
Joe Onorato00acb122009-08-04 16:04:30 -04001281 int xOffset, int yOffset, DragView dragView, Object dragInfo) {
Romain Guy4c58c482009-05-12 17:35:41 -07001282 final CellLayout layout = getCurrentDropLayout();
1283 final CellLayout.CellInfo cellInfo = mDragInfo;
1284 final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1285 final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1286
1287 if (mVacantCache == null) {
1288 final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1289 mVacantCache = layout.findAllVacantCells(null, ignoreView);
1290 }
1291
1292 return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false);
Jeff Sharkey70864282009-04-07 21:08:40 -07001293 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001294
Jeff Sharkey70864282009-04-07 21:08:40 -07001295 /**
1296 * {@inheritDoc}
1297 */
1298 public Rect estimateDropLocation(DragSource source, int x, int y,
Joe Onorato00acb122009-08-04 16:04:30 -04001299 int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
Jeff Sharkey70864282009-04-07 21:08:40 -07001300 final CellLayout layout = getCurrentDropLayout();
Winson Chungaafa03c2010-06-11 17:34:16 -07001301
Jeff Sharkey70864282009-04-07 21:08:40 -07001302 final CellLayout.CellInfo cellInfo = mDragInfo;
1303 final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1304 final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1305 final View ignoreView = cellInfo == null ? null : cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07001306
Jeff Sharkey70864282009-04-07 21:08:40 -07001307 final Rect location = recycle != null ? recycle : new Rect();
Winson Chungaafa03c2010-06-11 17:34:16 -07001308
Jeff Sharkey70864282009-04-07 21:08:40 -07001309 // Find drop cell and convert into rectangle
Winson Chungaafa03c2010-06-11 17:34:16 -07001310 int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX,
1311 spanY, ignoreView, layout, mTempCell);
1312
Jeff Sharkey70864282009-04-07 21:08:40 -07001313 if (dropCell == null) {
1314 return null;
1315 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001316
Jeff Sharkey70864282009-04-07 21:08:40 -07001317 layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
1318 location.left = mTempEstimate[0];
1319 location.top = mTempEstimate[1];
Winson Chungaafa03c2010-06-11 17:34:16 -07001320
Jeff Sharkey70864282009-04-07 21:08:40 -07001321 layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
1322 location.right = mTempEstimate[0];
1323 location.bottom = mTempEstimate[1];
Winson Chungaafa03c2010-06-11 17:34:16 -07001324
Jeff Sharkey70864282009-04-07 21:08:40 -07001325 return location;
1326 }
1327
1328 /**
1329 * Calculate the nearest cell where the given object would be dropped.
1330 */
Romain Guy263e0192009-05-11 11:50:46 -07001331 private int[] estimateDropCell(int pixelX, int pixelY,
Jeff Sharkey70864282009-04-07 21:08:40 -07001332 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1333 // Create vacant cell cache if none exists
1334 if (mVacantCache == null) {
1335 mVacantCache = layout.findAllVacantCells(null, ignoreView);
1336 }
1337
1338 // Find the best target drop location
1339 return layout.findNearestVacantArea(pixelX, pixelY,
1340 spanX, spanY, mVacantCache, recycle);
1341 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001342
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001343 void setLauncher(Launcher launcher) {
1344 mLauncher = launcher;
1345 }
1346
Joe Onorato00acb122009-08-04 16:04:30 -04001347 public void setDragController(DragController dragController) {
1348 mDragController = dragController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001349 }
1350
1351 public void onDropCompleted(View target, boolean success) {
Romain Guy207e40e2009-09-29 16:19:19 -07001352 clearVacantCache();
1353
Winson Chungaafa03c2010-06-11 17:34:16 -07001354 if (success) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001355 if (target != this && mDragInfo != null) {
1356 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1357 cellLayout.removeView(mDragInfo.cell);
Joe Onorato00acb122009-08-04 16:04:30 -04001358 if (mDragInfo.cell instanceof DropTarget) {
1359 mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1360 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001361 // final Object tag = mDragInfo.cell.getTag();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001362 }
1363 } else {
1364 if (mDragInfo != null) {
1365 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1366 cellLayout.onDropAborted(mDragInfo.cell);
1367 }
1368 }
1369
1370 mDragInfo = null;
1371 }
1372
1373 public void scrollLeft() {
Romain Guy4c58c482009-05-12 17:35:41 -07001374 clearVacantCache();
Romain Guyf8e6a802009-12-07 17:48:02 -08001375 if (mScroller.isFinished()) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001376 if (mCurrentScreen > 0)
1377 snapToScreen(mCurrentScreen - 1);
Romain Guyf8e6a802009-12-07 17:48:02 -08001378 } else {
Winson Chungaafa03c2010-06-11 17:34:16 -07001379 if (mNextScreen > 0)
1380 snapToScreen(mNextScreen - 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001381 }
1382 }
1383
1384 public void scrollRight() {
Romain Guy4c58c482009-05-12 17:35:41 -07001385 clearVacantCache();
Romain Guyf8e6a802009-12-07 17:48:02 -08001386 if (mScroller.isFinished()) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001387 if (mCurrentScreen < getChildCount() - 1)
1388 snapToScreen(mCurrentScreen + 1);
Romain Guyf8e6a802009-12-07 17:48:02 -08001389 } else {
Winson Chungaafa03c2010-06-11 17:34:16 -07001390 if (mNextScreen < getChildCount() - 1)
1391 snapToScreen(mNextScreen + 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001392 }
1393 }
1394
1395 public int getScreenForView(View v) {
1396 int result = -1;
1397 if (v != null) {
1398 ViewParent vp = v.getParent();
1399 int count = getChildCount();
1400 for (int i = 0; i < count; i++) {
1401 if (vp == getChildAt(i)) {
1402 return i;
1403 }
1404 }
1405 }
1406 return result;
1407 }
Karl Rosaen138a0412009-04-23 19:00:21 -07001408
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001409 public Folder getFolderForTag(Object tag) {
1410 int screenCount = getChildCount();
1411 for (int screen = 0; screen < screenCount; screen++) {
1412 CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1413 int count = currentScreen.getChildCount();
1414 for (int i = 0; i < count; i++) {
1415 View child = currentScreen.getChildAt(i);
1416 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1417 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1418 Folder f = (Folder) child;
Winson Chungaafa03c2010-06-11 17:34:16 -07001419 if (f.getInfo() == tag && f.getInfo().opened) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001420 return f;
1421 }
1422 }
1423 }
1424 }
1425 return null;
1426 }
1427
1428 public View getViewForTag(Object tag) {
1429 int screenCount = getChildCount();
1430 for (int screen = 0; screen < screenCount; screen++) {
1431 CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1432 int count = currentScreen.getChildCount();
1433 for (int i = 0; i < count; i++) {
1434 View child = currentScreen.getChildAt(i);
1435 if (child.getTag() == tag) {
1436 return child;
1437 }
1438 }
1439 }
1440 return null;
1441 }
1442
1443 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001444 * @return True is long presses are still allowed for the current touch
1445 */
1446 public boolean allowLongPress() {
1447 return mAllowLongPress;
1448 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001449
The Android Open Source Projectca9475f2009-03-13 13:04:24 -07001450 /**
1451 * Set true to allow long-press events to be triggered, usually checked by
1452 * {@link Launcher} to accept or block dpad-initiated long-presses.
1453 */
1454 public void setAllowLongPress(boolean allowLongPress) {
1455 mAllowLongPress = allowLongPress;
1456 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001457
Joe Onorato64e6be72010-03-05 15:05:52 -05001458 void removeItems(final ArrayList<ApplicationInfo> apps) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001459 final int count = getChildCount();
Romain Guy5c16f3e2010-01-12 17:24:58 -08001460 final PackageManager manager = getContext().getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -08001461 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
Romain Guy574d20e2009-06-01 15:34:04 -07001462
Joe Onorato64e6be72010-03-05 15:05:52 -05001463 final HashSet<String> packageNames = new HashSet<String>();
1464 final int appCount = apps.size();
1465 for (int i = 0; i < appCount; i++) {
1466 packageNames.add(apps.get(i).componentName.getPackageName());
1467 }
1468
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001469 for (int i = 0; i < count; i++) {
1470 final CellLayout layout = (CellLayout) getChildAt(i);
Romain Guy574d20e2009-06-01 15:34:04 -07001471
Romain Guy629de3e2010-01-13 12:20:59 -08001472 // Avoid ANRs by treating each screen separately
1473 post(new Runnable() {
1474 public void run() {
1475 final ArrayList<View> childrenToRemove = new ArrayList<View>();
1476 childrenToRemove.clear();
Winson Chungaafa03c2010-06-11 17:34:16 -07001477
Romain Guy629de3e2010-01-13 12:20:59 -08001478 int childCount = layout.getChildCount();
1479 for (int j = 0; j < childCount; j++) {
1480 final View view = layout.getChildAt(j);
1481 Object tag = view.getTag();
Winson Chungaafa03c2010-06-11 17:34:16 -07001482
Joe Onorato0589f0f2010-02-08 13:44:00 -08001483 if (tag instanceof ShortcutInfo) {
1484 final ShortcutInfo info = (ShortcutInfo) tag;
Romain Guy629de3e2010-01-13 12:20:59 -08001485 final Intent intent = info.intent;
1486 final ComponentName name = intent.getComponent();
Winson Chungaafa03c2010-06-11 17:34:16 -07001487
Joe Onorato64e6be72010-03-05 15:05:52 -05001488 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1489 for (String packageName: packageNames) {
1490 if (packageName.equals(name.getPackageName())) {
1491 // TODO: This should probably be done on a worker thread
1492 LauncherModel.deleteItemFromDatabase(mLauncher, info);
1493 childrenToRemove.add(view);
1494 }
1495 }
Romain Guy629de3e2010-01-13 12:20:59 -08001496 }
1497 } else if (tag instanceof UserFolderInfo) {
1498 final UserFolderInfo info = (UserFolderInfo) tag;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001499 final ArrayList<ShortcutInfo> contents = info.contents;
1500 final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
Romain Guy629de3e2010-01-13 12:20:59 -08001501 final int contentsCount = contents.size();
1502 boolean removedFromFolder = false;
Winson Chungaafa03c2010-06-11 17:34:16 -07001503
Romain Guy629de3e2010-01-13 12:20:59 -08001504 for (int k = 0; k < contentsCount; k++) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001505 final ShortcutInfo appInfo = contents.get(k);
Romain Guy629de3e2010-01-13 12:20:59 -08001506 final Intent intent = appInfo.intent;
1507 final ComponentName name = intent.getComponent();
Winson Chungaafa03c2010-06-11 17:34:16 -07001508
Joe Onorato64e6be72010-03-05 15:05:52 -05001509 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1510 for (String packageName: packageNames) {
1511 if (packageName.equals(name.getPackageName())) {
1512 toRemove.add(appInfo);
1513 // TODO: This should probably be done on a worker thread
1514 LauncherModel.deleteItemFromDatabase(
1515 mLauncher, appInfo);
1516 removedFromFolder = true;
1517 }
1518 }
Romain Guy629de3e2010-01-13 12:20:59 -08001519 }
1520 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001521
Romain Guy629de3e2010-01-13 12:20:59 -08001522 contents.removeAll(toRemove);
1523 if (removedFromFolder) {
1524 final Folder folder = getOpenFolder();
Winson Chungaafa03c2010-06-11 17:34:16 -07001525 if (folder != null)
1526 folder.notifyDataSetChanged();
Romain Guy629de3e2010-01-13 12:20:59 -08001527 }
1528 } else if (tag instanceof LiveFolderInfo) {
1529 final LiveFolderInfo info = (LiveFolderInfo) tag;
1530 final Uri uri = info.uri;
1531 final ProviderInfo providerInfo = manager.resolveContentProvider(
1532 uri.getAuthority(), 0);
Romain Guy574d20e2009-06-01 15:34:04 -07001533
Joe Onoratof11079b2010-04-15 11:47:28 -07001534 if (providerInfo != null) {
Joe Onorato64e6be72010-03-05 15:05:52 -05001535 for (String packageName: packageNames) {
1536 if (packageName.equals(providerInfo.packageName)) {
1537 // TODO: This should probably be done on a worker thread
1538 LauncherModel.deleteItemFromDatabase(mLauncher, info);
Winson Chungaafa03c2010-06-11 17:34:16 -07001539 childrenToRemove.add(view);
Joe Onorato64e6be72010-03-05 15:05:52 -05001540 }
1541 }
Romain Guy629de3e2010-01-13 12:20:59 -08001542 }
1543 } else if (tag instanceof LauncherAppWidgetInfo) {
1544 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1545 final AppWidgetProviderInfo provider =
1546 widgets.getAppWidgetInfo(info.appWidgetId);
Daniel Sandlere1cc6c32010-05-07 11:49:29 -04001547 if (provider != null) {
Joe Onorato64e6be72010-03-05 15:05:52 -05001548 for (String packageName: packageNames) {
1549 if (packageName.equals(provider.provider.getPackageName())) {
1550 // TODO: This should probably be done on a worker thread
1551 LauncherModel.deleteItemFromDatabase(mLauncher, info);
Winson Chungaafa03c2010-06-11 17:34:16 -07001552 childrenToRemove.add(view);
Joe Onorato64e6be72010-03-05 15:05:52 -05001553 }
1554 }
Romain Guy629de3e2010-01-13 12:20:59 -08001555 }
Romain Guy574d20e2009-06-01 15:34:04 -07001556 }
1557 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001558
Romain Guy629de3e2010-01-13 12:20:59 -08001559 childCount = childrenToRemove.size();
1560 for (int j = 0; j < childCount; j++) {
1561 View child = childrenToRemove.get(j);
1562 layout.removeViewInLayout(child);
1563 if (child instanceof DropTarget) {
1564 mDragController.removeDropTarget((DropTarget)child);
1565 }
Romain Guy574d20e2009-06-01 15:34:04 -07001566 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001567
Romain Guy629de3e2010-01-13 12:20:59 -08001568 if (childCount > 0) {
1569 layout.requestLayout();
1570 layout.invalidate();
Romain Guy5c16f3e2010-01-12 17:24:58 -08001571 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001572 }
Romain Guy629de3e2010-01-13 12:20:59 -08001573 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001574 }
1575 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001576
Joe Onorato64e6be72010-03-05 15:05:52 -05001577 void updateShortcuts(ArrayList<ApplicationInfo> apps) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001578 final PackageManager pm = mLauncher.getPackageManager();
1579
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001580 final int count = getChildCount();
1581 for (int i = 0; i < count; i++) {
1582 final CellLayout layout = (CellLayout) getChildAt(i);
1583 int childCount = layout.getChildCount();
1584 for (int j = 0; j < childCount; j++) {
1585 final View view = layout.getChildAt(j);
1586 Object tag = view.getTag();
Joe Onorato0589f0f2010-02-08 13:44:00 -08001587 if (tag instanceof ShortcutInfo) {
1588 ShortcutInfo info = (ShortcutInfo)tag;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001589 // We need to check for ACTION_MAIN otherwise getComponent() might
1590 // return null for some shortcuts (for instance, for shortcuts to
1591 // web pages.)
1592 final Intent intent = info.intent;
1593 final ComponentName name = intent.getComponent();
1594 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
Joe Onorato64e6be72010-03-05 15:05:52 -05001595 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1596 final int appCount = apps.size();
Winson Chungaafa03c2010-06-11 17:34:16 -07001597 for (int k = 0; k < appCount; k++) {
Joe Onorato64e6be72010-03-05 15:05:52 -05001598 ApplicationInfo app = apps.get(k);
1599 if (app.componentName.equals(name)) {
1600 info.setIcon(mIconCache.getIcon(info.intent));
1601 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
1602 new FastBitmapDrawable(info.getIcon(mIconCache)),
1603 null, null);
1604 }
1605 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001606 }
1607 }
1608 }
1609 }
1610 }
1611
Joe Onorato14f122b2009-11-19 14:06:36 -08001612 void moveToDefaultScreen(boolean animate) {
Joe Onoratoc45b1682010-01-11 18:48:40 -05001613 if (animate) {
1614 snapToScreen(mDefaultScreen);
1615 } else {
1616 setCurrentScreen(mDefaultScreen);
1617 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001618 getChildAt(mDefaultScreen).requestFocus();
1619 }
1620
Romain Guy8a73c512009-11-09 19:19:59 -08001621 void setIndicators(Drawable previous, Drawable next) {
1622 mPreviousIndicator = previous;
1623 mNextIndicator = next;
1624 previous.setLevel(mCurrentScreen);
1625 next.setLevel(mCurrentScreen);
1626 }
1627
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001628 public static class SavedState extends BaseSavedState {
1629 int currentScreen = -1;
1630
1631 SavedState(Parcelable superState) {
1632 super(superState);
1633 }
1634
1635 private SavedState(Parcel in) {
1636 super(in);
1637 currentScreen = in.readInt();
1638 }
1639
1640 @Override
1641 public void writeToParcel(Parcel out, int flags) {
1642 super.writeToParcel(out, flags);
1643 out.writeInt(currentScreen);
1644 }
1645
1646 public static final Parcelable.Creator<SavedState> CREATOR =
1647 new Parcelable.Creator<SavedState>() {
1648 public SavedState createFromParcel(Parcel in) {
1649 return new SavedState(in);
1650 }
1651
1652 public SavedState[] newArray(int size) {
1653 return new SavedState[size];
1654 }
1655 };
1656 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001657}