Workspace.java revision 7f0f4f3c35fe0b6a94504ec39ac127fa935282ef
1/*
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
17package com.android.launcher2;
18
19import com.android.launcher.R;
20import com.android.launcher2.CellLayout.CellInfo;
21
22import android.animation.Animatable;
23import android.animation.PropertyAnimator;
24import android.animation.Sequencer;
25import android.animation.Animatable.AnimatableListener;
26import android.app.WallpaperManager;
27import android.appwidget.AppWidgetManager;
28import android.appwidget.AppWidgetProviderInfo;
29import android.content.ComponentName;
30import android.content.Context;
31import android.content.Intent;
32import android.content.pm.PackageManager;
33import android.content.pm.ProviderInfo;
34import android.content.res.Resources;
35import android.content.res.TypedArray;
36import android.graphics.Canvas;
37import android.graphics.Matrix;
38import android.graphics.Paint;
39import android.graphics.Rect;
40import android.graphics.drawable.Drawable;
41import android.net.Uri;
42import android.os.IBinder;
43import android.os.Parcel;
44import android.os.Parcelable;
45import android.util.AttributeSet;
46import android.util.Log;
47import android.view.MotionEvent;
48import android.view.VelocityTracker;
49import android.view.View;
50import android.view.ViewConfiguration;
51import android.view.ViewGroup;
52import android.view.ViewParent;
53import android.view.animation.Interpolator;
54import android.widget.Scroller;
55import android.widget.TextView;
56import android.widget.Toast;
57
58import java.util.ArrayList;
59import java.util.HashSet;
60
61/**
62 * The workspace is a wide area with a wallpaper and a finite number of screens.
63 * Each screen contains a number of icons, folders or widgets the user can
64 * interact with. A workspace is meant to be used with a fixed width only.
65 */
66public class Workspace extends ViewGroup
67        implements DropTarget, DragSource, DragScroller, View.OnTouchListener {
68    @SuppressWarnings({"UnusedDeclaration"})
69    private static final String TAG = "Launcher.Workspace";
70    private static final int INVALID_SCREEN = -1;
71    // This is how much the workspace shrinks when we enter all apps or
72    // customization mode
73    private static final float SHRINK_FACTOR = 0.16f;
74    private static final int SHRINK_TO_TOP = 0;
75    private static final int SHRINK_TO_MIDDLE = 1;
76    private static final int SHRINK_TO_BOTTOM = 2;
77
78    /**
79     * The velocity at which a fling gesture will cause us to snap to the next
80     * screen
81     */
82    private static final int SNAP_VELOCITY = 600;
83
84    private final WallpaperManager mWallpaperManager;
85
86    private int mDefaultScreen;
87
88    private boolean mFirstLayout = true;
89    private boolean mWaitingToShrinkToBottom = false;
90
91    private int mCurrentScreen;
92    private int mNextScreen = INVALID_SCREEN;
93    private Scroller mScroller;
94    private VelocityTracker mVelocityTracker;
95
96    /**
97     * CellInfo for the cell that is currently being dragged
98     */
99    private CellLayout.CellInfo mDragInfo;
100
101    /**
102     * Target drop area calculated during last acceptDrop call.
103     */
104    private int[] mTargetCell = null;
105
106    /**
107     * The CellLayout that is currently being dragged over
108     */
109    private CellLayout mDragTargetLayout = null;
110
111    private float mLastMotionX;
112    private float mLastMotionY;
113
114    private final static int TOUCH_STATE_REST = 0;
115    private final static int TOUCH_STATE_SCROLLING = 1;
116
117    private int mTouchState = TOUCH_STATE_REST;
118
119    private OnLongClickListener mLongClickListener;
120
121    private Launcher mLauncher;
122    private IconCache mIconCache;
123    private DragController mDragController;
124
125
126    private int[] mTempCell = new int[2];
127    private int[] mTempEstimate = new int[2];
128    private float[] mTempDragCoordinates = new float[2];
129    private float[] mTempDragBottomRightCoordinates = new float[2];
130
131    private boolean mAllowLongPress = true;
132
133    private int mTouchSlop;
134    private int mMaximumVelocity;
135
136    private static final int INVALID_POINTER = -1;
137    private static final int DEFAULT_CELL_COUNT_X = 4;
138    private static final int DEFAULT_CELL_COUNT_Y = 4;
139
140    private int mActivePointerId = INVALID_POINTER;
141
142    private Drawable mPreviousIndicator;
143    private Drawable mNextIndicator;
144
145    private static final float NANOTIME_DIV = 1000000000.0f;
146    private static final float SMOOTHING_SPEED = 0.75f;
147    private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
148    private float mSmoothingTime;
149    private float mTouchX;
150
151    private WorkspaceOvershootInterpolator mScrollInterpolator;
152
153    private static final float BASELINE_FLING_VELOCITY = 2500.f;
154    private static final float FLING_VELOCITY_INFLUENCE = 0.4f;
155
156    private Paint mDropIndicatorPaint;
157
158    // State variable that indicated whether the screens are small (ie when you're
159    // in all apps or customize mode)
160    private boolean mIsSmall;
161    private AnimatableListener mUnshrinkAnimationListener;
162
163    private static class WorkspaceOvershootInterpolator implements Interpolator {
164        private static final float DEFAULT_TENSION = 1.3f;
165        private float mTension;
166
167        public WorkspaceOvershootInterpolator() {
168            mTension = DEFAULT_TENSION;
169        }
170
171        public void setDistance(int distance) {
172            mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
173        }
174
175        public void disableSettle() {
176            mTension = 0.f;
177        }
178
179        public float getInterpolation(float t) {
180            // _o(t) = t * t * ((tension + 1) * t + tension)
181            // o(t) = _o(t - 1) + 1
182            t -= 1.0f;
183            return t * t * ((mTension + 1) * t + mTension) + 1.0f;
184        }
185    }
186
187    /**
188     * Used to inflate the Workspace from XML.
189     *
190     * @param context The application's context.
191     * @param attrs The attribtues set containing the Workspace's customization values.
192     */
193    public Workspace(Context context, AttributeSet attrs) {
194        this(context, attrs, 0);
195    }
196
197    /**
198     * Used to inflate the Workspace from XML.
199     *
200     * @param context The application's context.
201     * @param attrs The attribtues set containing the Workspace's customization values.
202     * @param defStyle Unused.
203     */
204    public Workspace(Context context, AttributeSet attrs, int defStyle) {
205        super(context, attrs, defStyle);
206
207        mWallpaperManager = WallpaperManager.getInstance(context);
208
209        TypedArray a = context.obtainStyledAttributes(attrs,
210                R.styleable.Workspace, defStyle, 0);
211        int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X);
212        int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y);
213        mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
214        a.recycle();
215
216        LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
217        setHapticFeedbackEnabled(false);
218        initWorkspace();
219    }
220
221    /**
222     * Initializes various states for this workspace.
223     */
224    private void initWorkspace() {
225        Context context = getContext();
226        mScrollInterpolator = new WorkspaceOvershootInterpolator();
227        mScroller = new Scroller(context, mScrollInterpolator);
228        mCurrentScreen = mDefaultScreen;
229        Launcher.setScreen(mCurrentScreen);
230        LauncherApplication app = (LauncherApplication)context.getApplicationContext();
231        mIconCache = app.getIconCache();
232
233        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
234        mTouchSlop = configuration.getScaledTouchSlop();
235        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
236        mUnshrinkAnimationListener = new AnimatableListener() {
237            public void onAnimationStart(Animatable animation) {}
238            public void onAnimationEnd(Animatable animation) {
239                mIsSmall = false;
240            }
241            public void onAnimationCancel(Animatable animation) {}
242            public void onAnimationRepeat(Animatable animation) {}
243        };
244    }
245
246    @Override
247    public void addView(View child, int index, LayoutParams params) {
248        if (!(child instanceof CellLayout)) {
249            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
250        }
251        super.addView(child, index, params);
252    }
253
254    @Override
255    public void addView(View child) {
256        if (!(child instanceof CellLayout)) {
257            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
258        }
259        super.addView(child);
260    }
261
262    @Override
263    public void addView(View child, int index) {
264        if (!(child instanceof CellLayout)) {
265            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
266        }
267        super.addView(child, index);
268    }
269
270    @Override
271    public void addView(View child, int width, int height) {
272        if (!(child instanceof CellLayout)) {
273            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
274        }
275        super.addView(child, width, height);
276    }
277
278    @Override
279    public void addView(View child, LayoutParams params) {
280        if (!(child instanceof CellLayout)) {
281            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
282        }
283        super.addView(child, params);
284    }
285
286    /**
287     * @return The open folder on the current screen, or null if there is none
288     */
289    Folder getOpenFolder() {
290        CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
291        int count = currentScreen.getChildCount();
292        for (int i = 0; i < count; i++) {
293            View child = currentScreen.getChildAt(i);
294            if (child instanceof Folder) {
295                Folder folder = (Folder) child;
296                if (folder.getInfo().opened)
297                    return folder;
298            }
299        }
300        return null;
301    }
302
303    ArrayList<Folder> getOpenFolders() {
304        final int screenCount = getChildCount();
305        ArrayList<Folder> folders = new ArrayList<Folder>(screenCount);
306
307        for (int screen = 0; screen < screenCount; screen++) {
308            CellLayout currentScreen = (CellLayout) getChildAt(screen);
309            int count = currentScreen.getChildCount();
310            for (int i = 0; i < count; i++) {
311                View child = currentScreen.getChildAt(i);
312                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child
313                        .getLayoutParams();
314                if (child instanceof Folder) {
315                    Folder folder = (Folder) child;
316                    if (folder.getInfo().opened)
317                        folders.add(folder);
318                    break;
319                }
320            }
321        }
322
323        return folders;
324    }
325
326    boolean isDefaultScreenShowing() {
327        return mCurrentScreen == mDefaultScreen;
328    }
329
330    /**
331     * Returns the index of the currently displayed screen.
332     *
333     * @return The index of the currently displayed screen.
334     */
335    int getCurrentScreen() {
336        return mCurrentScreen;
337    }
338
339    /**
340     * Sets the current screen.
341     *
342     * @param currentScreen
343     */
344    void setCurrentScreen(int currentScreen) {
345        setCurrentScreen(currentScreen, true);
346    }
347
348    void setCurrentScreen(int currentScreen, boolean animateScrolling) {
349        setCurrentScreen(currentScreen, animateScrolling, getWidth());
350    }
351
352    void setCurrentScreen(int currentScreen, boolean animateScrolling, int screenWidth) {
353        if (!mScroller.isFinished())
354            mScroller.abortAnimation();
355        mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
356        if (mPreviousIndicator != null) {
357            mPreviousIndicator.setLevel(mCurrentScreen);
358            mNextIndicator.setLevel(mCurrentScreen);
359        }
360        if (animateScrolling) {
361            scrollTo(mCurrentScreen * screenWidth, 0);
362        } else {
363            mScrollX = mCurrentScreen * screenWidth;
364        }
365        updateWallpaperOffset(screenWidth * (getChildCount() - 1));
366        invalidate();
367    }
368
369    /**
370     * Adds the specified child in the current screen. The position and dimension of
371     * the child are defined by x, y, spanX and spanY.
372     *
373     * @param child The child to add in one of the workspace's screens.
374     * @param x The X position of the child in the screen's grid.
375     * @param y The Y position of the child in the screen's grid.
376     * @param spanX The number of cells spanned horizontally by the child.
377     * @param spanY The number of cells spanned vertically by the child.
378     */
379    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
380        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
381    }
382
383    /**
384     * Adds the specified child in the current screen. The position and dimension of
385     * the child are defined by x, y, spanX and spanY.
386     *
387     * @param child The child to add in one of the workspace's screens.
388     * @param x The X position of the child in the screen's grid.
389     * @param y The Y position of the child in the screen's grid.
390     * @param spanX The number of cells spanned horizontally by the child.
391     * @param spanY The number of cells spanned vertically by the child.
392     * @param insert When true, the child is inserted at the beginning of the children list.
393     */
394    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
395        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
396    }
397
398    /**
399     * Adds the specified child in the specified screen. The position and dimension of
400     * the child are defined by x, y, spanX and spanY.
401     *
402     * @param child The child to add in one of the workspace's screens.
403     * @param screen The screen in which to add the child.
404     * @param x The X position of the child in the screen's grid.
405     * @param y The Y position of the child in the screen's grid.
406     * @param spanX The number of cells spanned horizontally by the child.
407     * @param spanY The number of cells spanned vertically by the child.
408     */
409    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
410        addInScreen(child, screen, x, y, spanX, spanY, false);
411    }
412
413    void addInFullScreen(View child, int screen) {
414        addInScreen(child, screen, 0, 0, -1, -1);
415    }
416
417    /**
418     * Adds the specified child in the specified screen. The position and dimension of
419     * the child are defined by x, y, spanX and spanY.
420     *
421     * @param child The child to add in one of the workspace's screens.
422     * @param screen The screen in which to add the child.
423     * @param x The X position of the child in the screen's grid.
424     * @param y The Y position of the child in the screen's grid.
425     * @param spanX The number of cells spanned horizontally by the child.
426     * @param spanY The number of cells spanned vertically by the child.
427     * @param insert When true, the child is inserted at the beginning of the children list.
428     */
429    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
430        if (screen < 0 || screen >= getChildCount()) {
431            Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
432                + " (was " + screen + "); skipping child");
433            return;
434        }
435
436        final CellLayout group = (CellLayout) getChildAt(screen);
437        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
438        if (lp == null) {
439            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
440        } else {
441            lp.cellX = x;
442            lp.cellY = y;
443            lp.cellHSpan = spanX;
444            lp.cellVSpan = spanY;
445        }
446
447        // Get the canonical child id to uniquely represent this view in this screen
448        int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY);
449        if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) {
450            // TODO: This branch occurs when the workspace is adding views
451            // outside of the defined grid
452            // maybe we should be deleting these items from the LauncherModel?
453            Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
454        }
455
456        if (!(child instanceof Folder)) {
457            child.setHapticFeedbackEnabled(false);
458            child.setOnLongClickListener(mLongClickListener);
459        }
460        if (child instanceof DropTarget) {
461            mDragController.addDropTarget((DropTarget) child);
462        }
463    }
464
465    CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) {
466        CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
467        if (group != null) {
468            return group.updateOccupiedCells(occupied, null);
469        }
470        return null;
471    }
472
473    public boolean onTouch(View v, MotionEvent event) {
474        // this is an intercepted event being forwarded from a cell layout
475        if (mIsSmall) {
476            unshrink((CellLayout)v);
477            mLauncher.onWorkspaceUnshrink();
478            return true;
479        }
480        return false;
481    }
482
483    /**
484     * Registers the specified listener on each screen contained in this workspace.
485     *
486     * @param l The listener used to respond to long clicks.
487     */
488    @Override
489    public void setOnLongClickListener(OnLongClickListener l) {
490        mLongClickListener = l;
491        final int screenCount = getChildCount();
492        for (int i = 0; i < screenCount; i++) {
493            getChildAt(i).setOnLongClickListener(l);
494        }
495    }
496
497    private void updateWallpaperOffset() {
498        updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
499    }
500
501    private void updateWallpaperOffset(int scrollRange) {
502        final boolean isStaticWallpaper = (mWallpaperManager != null) &&
503                (mWallpaperManager.getWallpaperInfo() == null);
504        if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) {
505            IBinder token = getWindowToken();
506            if (token != null) {
507                mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
508                mWallpaperManager.setWallpaperOffsets(getWindowToken(),
509                        Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
510            }
511        }
512    }
513
514    @Override
515    public void scrollTo(int x, int y) {
516        super.scrollTo(x, y);
517        mTouchX = x;
518        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
519    }
520
521    @Override
522    public void computeScroll() {
523        if (mScroller.computeScrollOffset()) {
524            mTouchX = mScrollX = mScroller.getCurrX();
525            mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
526            mScrollY = mScroller.getCurrY();
527            updateWallpaperOffset();
528            postInvalidate();
529        } else if (mNextScreen != INVALID_SCREEN) {
530            mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
531            if (mPreviousIndicator != null) {
532                mPreviousIndicator.setLevel(mCurrentScreen);
533                mNextIndicator.setLevel(mCurrentScreen);
534            }
535            Launcher.setScreen(mCurrentScreen);
536            mNextScreen = INVALID_SCREEN;
537            clearChildrenCache();
538        } else if (mTouchState == TOUCH_STATE_SCROLLING) {
539            final float now = System.nanoTime() / NANOTIME_DIV;
540            final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
541            final float dx = mTouchX - mScrollX;
542            mScrollX += dx * e;
543            mSmoothingTime = now;
544
545            // Keep generating points as long as we're more than 1px away from the target
546            if (dx > 1.f || dx < -1.f) {
547                updateWallpaperOffset();
548                postInvalidate();
549            }
550        }
551    }
552
553    @Override
554    protected void dispatchDraw(Canvas canvas) {
555        boolean restore = false;
556        int restoreCount = 0;
557
558        // ViewGroup.dispatchDraw() supports many features we don't need:
559        // clip to padding, layout animation, animation listener, disappearing
560        // children, etc. The following implementation attempts to fast-track
561        // the drawing dispatch by drawing only what we know needs to be drawn.
562
563        boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
564
565        // if the screens are all small, we need to draw all the screens since
566        // they're most likely all visible
567        if (mIsSmall) {
568            final int screenCount = getChildCount();
569            for (int i = 0; i < screenCount; i++) {
570                CellLayout cl = (CellLayout)getChildAt(i);
571                drawChild(canvas, cl, getDrawingTime());
572            }
573        } else if (fastDraw) {
574            // If we are not scrolling or flinging, draw only the current screen
575            drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
576        } else {
577            final long drawingTime = getDrawingTime();
578            final float scrollPos = (float) mScrollX / getWidth();
579            final int leftScreen = (int) scrollPos;
580            final int rightScreen = leftScreen + 1;
581            if (leftScreen >= 0) {
582                drawChild(canvas, getChildAt(leftScreen), drawingTime);
583            }
584            if (scrollPos != leftScreen && rightScreen < getChildCount()) {
585                drawChild(canvas, getChildAt(rightScreen), drawingTime);
586            }
587        }
588
589        if (restore) {
590            canvas.restoreToCount(restoreCount);
591        }
592    }
593
594    protected void onAttachedToWindow() {
595        super.onAttachedToWindow();
596        computeScroll();
597        mDragController.setWindowToken(getWindowToken());
598    }
599
600    @Override
601    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
602        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
603
604        final int width = MeasureSpec.getSize(widthMeasureSpec);
605        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
606        if (widthMode != MeasureSpec.EXACTLY) {
607            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
608        }
609
610        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
611        if (heightMode != MeasureSpec.EXACTLY) {
612            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
613        }
614
615        // The children are given the same width and height as the workspace
616        final int screenCount = getChildCount();
617        for (int i = 0; i < screenCount; i++) {
618            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
619        }
620
621        if (mFirstLayout) {
622            setHorizontalScrollBarEnabled(false);
623            setCurrentScreen(mCurrentScreen, false, width);
624            setHorizontalScrollBarEnabled(true);
625        }
626    }
627
628    @Override
629    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
630        if (mFirstLayout) {
631            mFirstLayout = false;
632        }
633        int childLeft = 0;
634        final int screenCount = getChildCount();
635        for (int i = 0; i < screenCount; i++) {
636            final View child = getChildAt(i);
637            if (child.getVisibility() != View.GONE) {
638                final int childWidth = child.getMeasuredWidth();
639                child.layout(childLeft, 0,
640                        childLeft + childWidth, child.getMeasuredHeight());
641                childLeft += childWidth;
642            }
643        }
644
645        // if shrinkToBottom() is called on initialization, it has to be deferred
646        // until after the first call to onLayout so that it has the correct width
647        if (mWaitingToShrinkToBottom) {
648            shrinkToBottom(false);
649            mWaitingToShrinkToBottom = false;
650        }
651
652        if (LauncherApplication.isInPlaceRotationEnabled()) {
653            // When the device is rotated, the scroll position of the current screen
654            // needs to be refreshed
655            setCurrentScreen(getCurrentScreen());
656        }
657    }
658
659    @Override
660    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
661        int screen = indexOfChild(child);
662        if (screen != mCurrentScreen || !mScroller.isFinished()) {
663            if (!mLauncher.isWorkspaceLocked()) {
664                snapToScreen(screen);
665            }
666            return true;
667        }
668        return false;
669    }
670
671    @Override
672    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
673        if (!mLauncher.isAllAppsVisible()) {
674            final Folder openFolder = getOpenFolder();
675            if (openFolder != null) {
676                return openFolder.requestFocus(direction, previouslyFocusedRect);
677            } else {
678                int focusableScreen;
679                if (mNextScreen != INVALID_SCREEN) {
680                    focusableScreen = mNextScreen;
681                } else {
682                    focusableScreen = mCurrentScreen;
683                }
684                getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
685            }
686        }
687        return false;
688    }
689
690    @Override
691    public boolean dispatchUnhandledMove(View focused, int direction) {
692        if (direction == View.FOCUS_LEFT) {
693            if (getCurrentScreen() > 0) {
694                snapToScreen(getCurrentScreen() - 1);
695                return true;
696            }
697        } else if (direction == View.FOCUS_RIGHT) {
698            if (getCurrentScreen() < getChildCount() - 1) {
699                snapToScreen(getCurrentScreen() + 1);
700                return true;
701            }
702        }
703        return super.dispatchUnhandledMove(focused, direction);
704    }
705
706    @Override
707    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
708        if (!mLauncher.isAllAppsVisible()) {
709            final Folder openFolder = getOpenFolder();
710            if (openFolder == null) {
711                getChildAt(mCurrentScreen).addFocusables(views, direction);
712                if (direction == View.FOCUS_LEFT) {
713                    if (mCurrentScreen > 0) {
714                        getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
715                    }
716                } else if (direction == View.FOCUS_RIGHT) {
717                    if (mCurrentScreen < getChildCount() - 1) {
718                        getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
719                    }
720                }
721            } else {
722                openFolder.addFocusables(views, direction);
723            }
724        }
725    }
726
727    @Override
728    public boolean dispatchTouchEvent(MotionEvent ev) {
729        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
730            // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
731            // ie when you click on a mini-screen, it zooms back to that screen)
732            if (mLauncher.isWorkspaceLocked() ||
733                    (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible())) {
734                return false;
735            }
736        }
737        return super.dispatchTouchEvent(ev);
738    }
739
740    /**
741     * {@inheritDoc}
742     */
743    @Override
744    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
745        if (disallowIntercept) {
746            // We need to make sure to cancel our long press if
747            // a scrollable widget takes over touch events
748            final View currentScreen = getChildAt(mCurrentScreen);
749            currentScreen.cancelLongPress();
750        }
751        super.requestDisallowInterceptTouchEvent(disallowIntercept);
752    }
753
754    @Override
755    public boolean onInterceptTouchEvent(MotionEvent ev) {
756        final boolean workspaceLocked = mLauncher.isWorkspaceLocked();
757        final boolean allAppsVisible = mLauncher.isAllAppsVisible();
758
759        // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
760        // ie when you click on a mini-screen, it zooms back to that screen)
761        if (workspaceLocked || (!LauncherApplication.isScreenXLarge() && allAppsVisible)) {
762            return false; // We don't want the events.  Let them fall through to the all apps view.
763        }
764
765        /*
766         * This method JUST determines whether we want to intercept the motion.
767         * If we return true, onTouchEvent will be called and we do the actual
768         * scrolling there.
769         */
770
771        /*
772         * Shortcut the most recurring case: the user is in the dragging
773         * state and he is moving his finger.  We want to intercept this
774         * motion.
775         */
776        final int action = ev.getAction();
777        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
778            return true;
779        }
780
781        if (mVelocityTracker == null) {
782            mVelocityTracker = VelocityTracker.obtain();
783        }
784        mVelocityTracker.addMovement(ev);
785
786        switch (action & MotionEvent.ACTION_MASK) {
787            case MotionEvent.ACTION_MOVE: {
788                /*
789                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
790                 * whether the user has moved far enough from his original down touch.
791                 */
792
793                /*
794                 * Locally do absolute value. mLastMotionX is set to the y value
795                 * of the down event.
796                 */
797                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
798                final float x = ev.getX(pointerIndex);
799                final float y = ev.getY(pointerIndex);
800                final int xDiff = (int) Math.abs(x - mLastMotionX);
801                final int yDiff = (int) Math.abs(y - mLastMotionY);
802
803                final int touchSlop = mTouchSlop;
804                boolean xMoved = xDiff > touchSlop;
805                boolean yMoved = yDiff > touchSlop;
806
807                if (xMoved || yMoved) {
808
809                    if (xMoved) {
810                        // Scroll if the user moved far enough along the X axis
811                        mTouchState = TOUCH_STATE_SCROLLING;
812                        mLastMotionX = x;
813                        mTouchX = mScrollX;
814                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
815                        enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
816                    }
817                    // Either way, cancel any pending longpress
818                    if (mAllowLongPress) {
819                        mAllowLongPress = false;
820                        // Try canceling the long press. It could also have been scheduled
821                        // by a distant descendant, so use the mAllowLongPress flag to block
822                        // everything
823                        final View currentScreen = getChildAt(mCurrentScreen);
824                        currentScreen.cancelLongPress();
825                    }
826                }
827                break;
828            }
829
830        case MotionEvent.ACTION_DOWN: {
831            final float x = ev.getX();
832            final float y = ev.getY();
833            // Remember location of down touch
834            mLastMotionX = x;
835            mLastMotionY = y;
836            mActivePointerId = ev.getPointerId(0);
837            mAllowLongPress = true;
838
839                /*
840                 * If being flinged and user touches the screen, initiate drag;
841                 * otherwise don't.  mScroller.isFinished should be false when
842                 * being flinged.
843                 */
844                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
845                break;
846            }
847
848            case MotionEvent.ACTION_CANCEL:
849            case MotionEvent.ACTION_UP:
850
851                if (mTouchState != TOUCH_STATE_SCROLLING) {
852                    final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
853                    if (!currentScreen.lastDownOnOccupiedCell()) {
854                        getLocationOnScreen(mTempCell);
855                        // Send a tap to the wallpaper if the last down was on empty space
856                        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
857                        mWallpaperManager.sendWallpaperCommand(getWindowToken(),
858                                "android.wallpaper.tap",
859                                mTempCell[0] + (int) ev.getX(pointerIndex),
860                                mTempCell[1] + (int) ev.getY(pointerIndex), 0, null);
861                    }
862                }
863
864                // Release the drag
865                clearChildrenCache();
866                mTouchState = TOUCH_STATE_REST;
867                mActivePointerId = INVALID_POINTER;
868                mAllowLongPress = false;
869
870                if (mVelocityTracker != null) {
871                    mVelocityTracker.recycle();
872                    mVelocityTracker = null;
873                }
874
875            break;
876
877        case MotionEvent.ACTION_POINTER_UP:
878            onSecondaryPointerUp(ev);
879            break;
880        }
881
882        /*
883         * The only time we want to intercept motion events is if we are in the
884         * drag mode.
885         */
886        return mTouchState != TOUCH_STATE_REST;
887    }
888
889    private void onSecondaryPointerUp(MotionEvent ev) {
890        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
891                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
892        final int pointerId = ev.getPointerId(pointerIndex);
893        if (pointerId == mActivePointerId) {
894            // This was our active pointer going up. Choose a new
895            // active pointer and adjust accordingly.
896            // TODO: Make this decision more intelligent.
897            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
898            mLastMotionX = ev.getX(newPointerIndex);
899            mLastMotionY = ev.getY(newPointerIndex);
900            mActivePointerId = ev.getPointerId(newPointerIndex);
901            if (mVelocityTracker != null) {
902                mVelocityTracker.clear();
903            }
904        }
905    }
906
907    /**
908     * If one of our descendant views decides that it could be focused now, only
909     * pass that along if it's on the current screen.
910     *
911     * This happens when live folders requery, and if they're off screen, they
912     * end up calling requestFocus, which pulls it on screen.
913     */
914    @Override
915    public void focusableViewAvailable(View focused) {
916        View current = getChildAt(mCurrentScreen);
917        View v = focused;
918        while (true) {
919            if (v == current) {
920                super.focusableViewAvailable(focused);
921                return;
922            }
923            if (v == this) {
924                return;
925            }
926            ViewParent parent = v.getParent();
927            if (parent instanceof View) {
928                v = (View) v.getParent();
929            } else {
930                return;
931            }
932        }
933    }
934
935    void enableChildrenCache(int fromScreen, int toScreen) {
936        if (fromScreen > toScreen) {
937            final int temp = fromScreen;
938            fromScreen = toScreen;
939            toScreen = temp;
940        }
941
942        final int screenCount = getChildCount();
943
944        fromScreen = Math.max(fromScreen, 0);
945        toScreen = Math.min(toScreen, screenCount - 1);
946
947        for (int i = fromScreen; i <= toScreen; i++) {
948            final CellLayout layout = (CellLayout) getChildAt(i);
949            layout.setChildrenDrawnWithCacheEnabled(true);
950            layout.setChildrenDrawingCacheEnabled(true);
951        }
952    }
953
954    void clearChildrenCache() {
955        final int screenCount = getChildCount();
956        for (int i = 0; i < screenCount; i++) {
957            final CellLayout layout = (CellLayout) getChildAt(i);
958            layout.setChildrenDrawnWithCacheEnabled(false);
959        }
960    }
961
962    @Override
963    public boolean onTouchEvent(MotionEvent ev) {
964
965        if (mLauncher.isWorkspaceLocked()) {
966            return false; // We don't want the events.  Let them fall through to the all apps view.
967        }
968        if (mLauncher.isAllAppsVisible()) {
969            // Cancel any scrolling that is in progress.
970            if (!mScroller.isFinished()) {
971                mScroller.abortAnimation();
972            }
973            snapToScreen(mCurrentScreen);
974            return false; // We don't want the events.  Let them fall through to the all apps view.
975        }
976
977        if (mVelocityTracker == null) {
978            mVelocityTracker = VelocityTracker.obtain();
979        }
980        mVelocityTracker.addMovement(ev);
981
982        final int action = ev.getAction();
983
984        switch (action & MotionEvent.ACTION_MASK) {
985        case MotionEvent.ACTION_DOWN:
986            /*
987             * If being flinged and user touches, stop the fling. isFinished
988             * will be false if being flinged.
989             */
990            if (!mScroller.isFinished()) {
991                mScroller.abortAnimation();
992            }
993
994            // Remember where the motion event started
995            mLastMotionX = ev.getX();
996            mActivePointerId = ev.getPointerId(0);
997            if (mTouchState == TOUCH_STATE_SCROLLING) {
998                enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
999            }
1000            break;
1001        case MotionEvent.ACTION_MOVE:
1002            if (mTouchState == TOUCH_STATE_SCROLLING) {
1003                // Scroll to follow the motion event
1004                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
1005                final float x = ev.getX(pointerIndex);
1006                final float deltaX = mLastMotionX - x;
1007                mLastMotionX = x;
1008
1009                if (deltaX < 0) {
1010                    if (mTouchX > 0) {
1011                        mTouchX += Math.max(-mTouchX, deltaX);
1012                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
1013                        invalidate();
1014                    }
1015                } else if (deltaX > 0) {
1016                    final float availableToScroll = getChildAt(getChildCount() - 1).getRight() -
1017                            mTouchX - getWidth();
1018                    if (availableToScroll > 0) {
1019                        mTouchX += Math.min(availableToScroll, deltaX);
1020                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
1021                        invalidate();
1022                    }
1023                } else {
1024                    awakenScrollBars();
1025                }
1026            }
1027            break;
1028        case MotionEvent.ACTION_UP:
1029            if (mTouchState == TOUCH_STATE_SCROLLING) {
1030                final VelocityTracker velocityTracker = mVelocityTracker;
1031                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1032                final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
1033
1034                final int screenWidth = getWidth();
1035                final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
1036                final float scrolledPos = (float) mScrollX / screenWidth;
1037
1038                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
1039                    // Fling hard enough to move left.
1040                    // Don't fling across more than one screen at a time.
1041                    final int bound = scrolledPos < whichScreen ?
1042                            mCurrentScreen - 1 : mCurrentScreen;
1043                    snapToScreen(Math.min(whichScreen, bound), velocityX, true);
1044                } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
1045                    // Fling hard enough to move right
1046                    // Don't fling across more than one screen at a time.
1047                    final int bound = scrolledPos > whichScreen ?
1048                            mCurrentScreen + 1 : mCurrentScreen;
1049                    snapToScreen(Math.max(whichScreen, bound), velocityX, true);
1050                } else {
1051                    snapToScreen(whichScreen, 0, true);
1052                }
1053
1054                if (mVelocityTracker != null) {
1055                    mVelocityTracker.recycle();
1056                    mVelocityTracker = null;
1057                }
1058            }
1059            mTouchState = TOUCH_STATE_REST;
1060            mActivePointerId = INVALID_POINTER;
1061            break;
1062        case MotionEvent.ACTION_CANCEL:
1063            mTouchState = TOUCH_STATE_REST;
1064            mActivePointerId = INVALID_POINTER;
1065            break;
1066        case MotionEvent.ACTION_POINTER_UP:
1067            onSecondaryPointerUp(ev);
1068            break;
1069        }
1070
1071        return true;
1072    }
1073
1074    public boolean isSmall() {
1075        return mIsSmall;
1076    }
1077
1078    void shrinkToTop(boolean animated) {
1079        shrink(SHRINK_TO_TOP, animated);
1080    }
1081
1082    void shrinkToMiddle() {
1083        shrink(SHRINK_TO_MIDDLE, true);
1084    }
1085
1086    void shrinkToBottom() {
1087        shrinkToBottom(true);
1088    }
1089
1090    void shrinkToBottom(boolean animated) {
1091        if (mFirstLayout) {
1092            // (mFirstLayout == "first layout has not happened yet")
1093            // if we get a call to shrink() as part of our initialization (for example, if
1094            // Launcher is started in All Apps mode) then we need to wait for a layout call
1095            // to get our width so we can layout the mini-screen views correctly
1096            mWaitingToShrinkToBottom = true;
1097        } else {
1098            shrink(SHRINK_TO_BOTTOM, animated);
1099        }
1100    }
1101
1102    // we use this to shrink the workspace for the all apps view and the customize view
1103    private void shrink(int shrinkPosition, boolean animated) {
1104        mIsSmall = true;
1105        final Resources res = getResources();
1106        final int screenWidth = getWidth();
1107        final int screenHeight = getHeight();
1108        final int scaledScreenWidth = (int) (SHRINK_FACTOR * screenWidth);
1109        final int scaledScreenHeight = (int) (SHRINK_FACTOR * screenHeight);
1110        final float scaledSpacing = res.getDimension(R.dimen.smallScreenSpacing);
1111
1112        final int screenCount = getChildCount();
1113        float totalWidth = screenCount * scaledScreenWidth + (screenCount - 1) * scaledSpacing;
1114
1115        float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
1116        if (shrinkPosition == SHRINK_TO_BOTTOM) {
1117            newY = screenHeight - newY - scaledScreenHeight;
1118        } else if (shrinkPosition == SHRINK_TO_MIDDLE) {
1119            newY = screenHeight / 2 - scaledScreenHeight / 2;
1120        }
1121
1122        // We animate all the screens to the centered position in workspace
1123        // At the same time, the screens become greyed/dimmed
1124
1125        // newX is initialized to the left-most position of the centered screens
1126        float newX = (mCurrentScreen + 1) * screenWidth - screenWidth / 2 - totalWidth / 2;
1127        Sequencer s = new Sequencer();
1128        for (int i = 0; i < screenCount; i++) {
1129            CellLayout cl = (CellLayout) getChildAt(i);
1130            cl.setPivotX(0.0f);
1131            cl.setPivotY(0.0f);
1132            if (animated) {
1133                final int duration = res.getInteger(R.integer.config_workspaceShrinkTime);
1134                s.playTogether(
1135                        new PropertyAnimator(duration, cl, "x", newX),
1136                        new PropertyAnimator(duration, cl, "y", newY),
1137                        new PropertyAnimator(duration, cl, "scaleX", SHRINK_FACTOR),
1138                        new PropertyAnimator(duration, cl, "scaleY", SHRINK_FACTOR),
1139                        new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 1.0f));
1140            } else {
1141                cl.setX((int)newX);
1142                cl.setY((int)newY);
1143                cl.setScaleX(SHRINK_FACTOR);
1144                cl.setScaleY(SHRINK_FACTOR);
1145                cl.setDimmedBitmapAlpha(1.0f);
1146            }
1147            // increment newX for the next screen
1148            newX += scaledScreenWidth + scaledSpacing;
1149            cl.setOnInterceptTouchListener(this);
1150        }
1151        setChildrenDrawnWithCacheEnabled(true);
1152        if (animated) s.start();
1153    }
1154
1155    // We call this when we trigger an unshrink by clicking on the CellLayout cl
1156    private void unshrink(CellLayout clThatWasClicked) {
1157        int newCurrentScreen = mCurrentScreen;
1158        final int screenCount = getChildCount();
1159        for (int i = 0; i < screenCount; i++) {
1160            if (getChildAt(i) == clThatWasClicked) {
1161                newCurrentScreen = i;
1162            }
1163        }
1164        unshrink(newCurrentScreen);
1165    }
1166
1167    private void unshrink(int newCurrentScreen) {
1168        if (mIsSmall) {
1169            int delta = (newCurrentScreen - mCurrentScreen)*getWidth();
1170
1171            final int screenCount = getChildCount();
1172            for (int i = 0; i < screenCount; i++) {
1173                CellLayout cl = (CellLayout) getChildAt(i);
1174                cl.setX(cl.getX() + delta);
1175            }
1176            mScrollX = newCurrentScreen * getWidth();
1177
1178            unshrink();
1179            setCurrentScreen(newCurrentScreen);
1180        }
1181    }
1182
1183    void unshrink() {
1184        unshrink(true);
1185    }
1186
1187    void unshrink(boolean animated) {
1188        if (mIsSmall) {
1189            Sequencer s = new Sequencer();
1190            final int screenCount = getChildCount();
1191
1192            final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
1193            for (int i = 0; i < screenCount; i++) {
1194                final CellLayout cl = (CellLayout)getChildAt(i);
1195                cl.setPivotX(0.0f);
1196                cl.setPivotY(0.0f);
1197                if (animated) {
1198                    s.playTogether(
1199                            new PropertyAnimator(duration, cl, "translationX", 0.0f),
1200                            new PropertyAnimator(duration, cl, "translationY", 0.0f),
1201                            new PropertyAnimator(duration, cl, "scaleX", 1.0f),
1202                            new PropertyAnimator(duration, cl, "scaleY", 1.0f),
1203                            new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 0.0f));
1204                } else {
1205                    cl.setTranslationX(0.0f);
1206                    cl.setTranslationY(0.0f);
1207                    cl.setScaleX(1.0f);
1208                    cl.setScaleY(1.0f);
1209                    cl.setDimmedBitmapAlpha(0.0f);
1210                }
1211            }
1212            s.addListener(mUnshrinkAnimationListener);
1213            s.start();
1214        }
1215    }
1216
1217    void snapToScreen(int whichScreen) {
1218        snapToScreen(whichScreen, 0, false);
1219    }
1220
1221    private void snapToScreen(int whichScreen, int velocity, boolean settle) {
1222        // if (!mScroller.isFinished()) return;
1223
1224        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
1225
1226        enableChildrenCache(mCurrentScreen, whichScreen);
1227
1228        mNextScreen = whichScreen;
1229
1230        if (mPreviousIndicator != null) {
1231            mPreviousIndicator.setLevel(mNextScreen);
1232            mNextIndicator.setLevel(mNextScreen);
1233        }
1234
1235        View focusedChild = getFocusedChild();
1236        if (focusedChild != null && whichScreen != mCurrentScreen &&
1237                focusedChild == getChildAt(mCurrentScreen)) {
1238            focusedChild.clearFocus();
1239        }
1240
1241        final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));
1242        final int newX = whichScreen * getWidth();
1243        final int delta = newX - mScrollX;
1244        int duration = (screenDelta + 1) * 100;
1245
1246        if (!mScroller.isFinished()) {
1247            mScroller.abortAnimation();
1248        }
1249
1250        if (settle) {
1251            mScrollInterpolator.setDistance(screenDelta);
1252        } else {
1253            mScrollInterpolator.disableSettle();
1254        }
1255
1256        velocity = Math.abs(velocity);
1257        if (velocity > 0) {
1258            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
1259                    * FLING_VELOCITY_INFLUENCE;
1260        } else {
1261            duration += 100;
1262        }
1263
1264        awakenScrollBars(duration);
1265        mScroller.startScroll(mScrollX, 0, delta, 0, duration);
1266        invalidate();
1267    }
1268
1269    void startDrag(CellLayout.CellInfo cellInfo) {
1270        View child = cellInfo.cell;
1271
1272        // Make sure the drag was started by a long press as opposed to a long click.
1273        if (!child.isInTouchMode()) {
1274            return;
1275        }
1276
1277        mDragInfo = cellInfo;
1278        mDragInfo.screen = mCurrentScreen;
1279
1280        CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
1281
1282        current.onDragChild(child);
1283        mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
1284        invalidate();
1285    }
1286
1287    @Override
1288    protected Parcelable onSaveInstanceState() {
1289        final SavedState state = new SavedState(super.onSaveInstanceState());
1290        state.currentScreen = mCurrentScreen;
1291        return state;
1292    }
1293
1294    @Override
1295    protected void onRestoreInstanceState(Parcelable state) {
1296        SavedState savedState = (SavedState) state;
1297        super.onRestoreInstanceState(savedState.getSuperState());
1298        if (savedState.currentScreen != -1) {
1299            setCurrentScreen(savedState.currentScreen, false);
1300            Launcher.setScreen(mCurrentScreen);
1301        }
1302    }
1303
1304    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
1305        addApplicationShortcut(info, cellInfo, false);
1306    }
1307
1308    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo,
1309            boolean insertAtFirst) {
1310        final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
1311        final int[] result = new int[2];
1312
1313        layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
1314        onDropExternal(result[0], result[1], info, layout, insertAtFirst);
1315    }
1316
1317    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
1318            DragView dragView, Object dragInfo) {
1319        CellLayout cellLayout = getCurrentDropLayout();
1320        int originX = x - xOffset;
1321        int originY = y - yOffset;
1322        if (mIsSmall) {
1323            // find out which target layout is over
1324            final float[] localXY = mTempDragCoordinates;
1325            localXY[0] = originX;
1326            localXY[1] = originY;
1327            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
1328            // we need to subtract left/top here because DragController already adds
1329            // dragRegionLeft/Top to xOffset and yOffset
1330            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
1331            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
1332            cellLayout =  findMatchingScreenForDragOver(localXY, localBottomRightXY);
1333            if (cellLayout == null) {
1334                // cancel the drag if we're not over a mini-screen at time of drop
1335                // TODO: maybe add a nice fade here?
1336                return;
1337            }
1338            // localXY will be transformed into the local screen's coordinate space; save that info
1339            originX = (int)localXY[0];
1340            originY = (int)localXY[1];
1341        }
1342        if (source != this) {
1343            onDropExternal(originX, originY, dragInfo, cellLayout);
1344        } else {
1345            // Move internally
1346            if (mDragInfo != null) {
1347                final View cell = mDragInfo.cell;
1348                int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1349                if (index != mDragInfo.screen) {
1350                    final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1351                    originalCellLayout.removeView(cell);
1352                    addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY,
1353                            mDragInfo.spanX, mDragInfo.spanY);
1354                }
1355
1356                mTargetCell = estimateDropCell(originX, originY,
1357                        mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout,
1358                        mTargetCell);
1359                cellLayout.onDropChild(cell);
1360
1361                // update the item's position after drop
1362                final ItemInfo info = (ItemInfo) cell.getTag();
1363                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell
1364                        .getLayoutParams();
1365                lp.cellX = mTargetCell[0];
1366                lp.cellY = mTargetCell[1];
1367
1368                LauncherModel.moveItemInDatabase(mLauncher, info,
1369                        LauncherSettings.Favorites.CONTAINER_DESKTOP, index,
1370                        lp.cellX, lp.cellY);
1371            }
1372        }
1373    }
1374
1375    public void onDragEnter(DragSource source, int x, int y, int xOffset,
1376            int yOffset, DragView dragView, Object dragInfo) {
1377    }
1378
1379    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
1380            DragView dragView, Object dragInfo) {
1381
1382        // We may need to delegate the drag to a child view. If a 1x1 item
1383        // would land in a cell occupied by a DragTarget (e.g. a Folder),
1384        // then drag events should be handled by that child.
1385
1386        ItemInfo item = (ItemInfo)dragInfo;
1387        CellLayout currentLayout = getCurrentDropLayout();
1388
1389        int dragPointX, dragPointY;
1390        if (item.spanX == 1 && item.spanY == 1) {
1391            // For a 1x1, calculate the drop cell exactly as in onDragOver
1392            dragPointX = x - xOffset;
1393            dragPointY = y - yOffset;
1394        } else {
1395            // Otherwise, use the exact drag coordinates
1396            dragPointX = x;
1397            dragPointY = y;
1398        }
1399
1400        // If we are dragging over a cell that contains a DropTarget that will
1401        // accept the drop, delegate to that DropTarget.
1402        final int[] cellXY = mTempCell;
1403        currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
1404        View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
1405        if (child instanceof DropTarget) {
1406            DropTarget target = (DropTarget)child;
1407            if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
1408                return target;
1409            }
1410        }
1411        return null;
1412    }
1413
1414    // xy = upper left corner of item being dragged
1415    // bottomRightXy = lower right corner of item being dragged
1416    // This method will see which mini-screen is most overlapped by the item being dragged, and
1417    // return it. It will also transform the parameters xy and bottomRightXy into the local
1418    // coordinate space of the returned screen
1419    private CellLayout findMatchingScreenForDragOver(float[] xy, float[] bottomRightXy) {
1420        float x = xy[0];
1421        float y = xy[1];
1422        float right = bottomRightXy[0];
1423        float bottom = bottomRightXy[1];
1424
1425        float bestX = 0;
1426        float bestY = 0;
1427        float bestRight = 0;
1428        float bestBottom = 0;
1429
1430        Matrix inverseMatrix = new Matrix();
1431
1432        // We loop through all the screens (ie CellLayouts) and see which one overlaps the most
1433        // with the item being dragged.
1434        final int screenCount = getChildCount();
1435        CellLayout bestMatchingScreen = null;
1436        float bestOverlapSoFar = 0;
1437        for (int i = 0; i < screenCount; i++) {
1438            CellLayout cl = (CellLayout)getChildAt(i);
1439            // Transform the coordinates of the item being dragged to the CellLayout's coordinates
1440            float left = cl.getLeft();
1441            float top = cl.getTop();
1442            xy[0] = x + mScrollX - left;
1443            xy[1] = y + mScrollY - top;
1444            cl.getMatrix().invert(inverseMatrix);
1445
1446            bottomRightXy[0] = right + mScrollX - left;
1447            bottomRightXy[1] = bottom + mScrollY - top;
1448
1449            inverseMatrix.mapPoints(xy);
1450            inverseMatrix.mapPoints(bottomRightXy);
1451
1452            float dragRegionX = xy[0];
1453            float dragRegionY = xy[1];
1454            float dragRegionRight = bottomRightXy[0];
1455            float dragRegionBottom = bottomRightXy[1];
1456
1457            // Find the overlapping region
1458            float overlapLeft = Math.max(0f, dragRegionX);
1459            float overlapTop = Math.max(0f, dragRegionY);
1460            float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom);
1461            float overlapRight = Math.min(cl.getWidth(), dragRegionRight);
1462
1463            if (overlapRight >= 0 && overlapLeft <= cl.getWidth() &&
1464                    overlapTop >= 0 && overlapBottom <= cl.getHeight()) {
1465                // Calculate the size of the overlapping region
1466                float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
1467                if (overlap > bestOverlapSoFar) {
1468                    bestOverlapSoFar = overlap;
1469                    bestMatchingScreen = cl;
1470                    bestX = xy[0];
1471                    bestY = xy[1];
1472                    bestRight = bottomRightXy[0];
1473                    bestBottom = bottomRightXy[1];
1474                }
1475             }
1476        }
1477        if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) {
1478            if (mDragTargetLayout != null) {
1479                mDragTargetLayout.onDragComplete();
1480            }
1481            mDragTargetLayout = bestMatchingScreen;
1482        }
1483        xy[0] = bestX;
1484        xy[1] = bestY;
1485        bottomRightXy[0] = bestRight;
1486        bottomRightXy[1] = bestBottom;
1487        return bestMatchingScreen;
1488    }
1489
1490    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
1491            DragView dragView, Object dragInfo) {
1492
1493        final ItemInfo item = (ItemInfo)dragInfo;
1494        CellLayout currentLayout = getCurrentDropLayout();
1495
1496        if (dragInfo instanceof LauncherAppWidgetInfo) {
1497            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
1498
1499            if (widgetInfo.spanX == -1) {
1500                // Calculate the grid spans needed to fit this widget
1501                int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null);
1502                item.spanX = spans[0];
1503                item.spanY = spans[1];
1504            }
1505        }
1506        int originX = x - xOffset;
1507        int originY = y - yOffset;
1508        if (mIsSmall) {
1509            // find out which mini screen the dragged item is over
1510            final float[] localXY = mTempDragCoordinates;
1511            localXY[0] = originX;
1512            localXY[1] = originY;
1513            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
1514
1515            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
1516            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
1517            currentLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY);
1518            if (currentLayout != null) {
1519                currentLayout.setHover(true);
1520            }
1521
1522            originX = (int)localXY[0];
1523            originY = (int)localXY[1];
1524        }
1525
1526        if (source != this) {
1527            // This is a hack to fix the point used to determine which cell an icon from the all
1528            // apps screen is over
1529            if (item != null && item.spanX == 1 && currentLayout != null) {
1530                int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2;
1531
1532                originX += dragRegionLeft - dragView.getDragRegionLeft();
1533                if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) {
1534                    dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(),
1535                            currentLayout.getCellWidth(), dragView.getDragRegionHeight());
1536                }
1537            }
1538        }
1539        if (currentLayout != mDragTargetLayout) {
1540            if (mDragTargetLayout != null) {
1541                mDragTargetLayout.onDragComplete();
1542            }
1543            mDragTargetLayout = currentLayout;
1544        }
1545
1546        // only visualize the drop locations for moving icons within the home screen on tablet
1547        // on phone, we also visualize icons dragged in from All Apps
1548        if ((!LauncherApplication.isScreenXLarge() || source == this)
1549                && mDragTargetLayout != null) {
1550            final View child = (mDragInfo == null) ? null : mDragInfo.cell;
1551            mDragTargetLayout.visualizeDropLocation(
1552                    child, originX, originY, item.spanX, item.spanY);
1553        }
1554    }
1555
1556    public void onDragExit(DragSource source, int x, int y, int xOffset,
1557            int yOffset, DragView dragView, Object dragInfo) {
1558        if (mDragTargetLayout != null) {
1559            mDragTargetLayout.onDragComplete();
1560            mDragTargetLayout = null;
1561        }
1562    }
1563
1564    private void onDropExternal(int x, int y, Object dragInfo,
1565            CellLayout cellLayout) {
1566        onDropExternal(x, y, dragInfo, cellLayout, false);
1567    }
1568
1569    private void onDropExternal(int x, int y, Object dragInfo,
1570            CellLayout cellLayout, boolean insertAtFirst) {
1571        // Drag from somewhere else
1572        ItemInfo info = (ItemInfo) dragInfo;
1573
1574        View view = null;
1575
1576        switch (info.itemType) {
1577        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1578        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1579            if (info.container == NO_ID && info instanceof ApplicationInfo) {
1580                // Came from all apps -- make a copy
1581                info = new ShortcutInfo((ApplicationInfo) info);
1582            }
1583            view = mLauncher.createShortcut(R.layout.application, cellLayout,
1584                    (ShortcutInfo) info);
1585            break;
1586        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1587            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
1588                    (ViewGroup) getChildAt(mCurrentScreen),
1589                    ((UserFolderInfo) info));
1590            break;
1591        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1592            cellLayout.setTagToCellInfoForPoint(x, y);
1593            int[] position = new int[2];
1594            position[0] = x;
1595            position[1] = y;
1596            mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName,
1597                    cellLayout.getTag(), position);
1598            break;
1599        default:
1600            throw new IllegalStateException("Unknown item type: "
1601                    + info.itemType);
1602        }
1603
1604        // If the view is null, it has already been added.
1605        if (view == null) {
1606            cellLayout.onDragComplete();
1607        } else {
1608            mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
1609            addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
1610                    mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
1611            cellLayout.onDropChild(view);
1612            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1613
1614            LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1615                    LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen,
1616                    lp.cellX, lp.cellY);
1617        }
1618    }
1619
1620    /**
1621     * Return the current {@link CellLayout}, correctly picking the destination
1622     * screen while a scroll is in progress.
1623     */
1624    private CellLayout getCurrentDropLayout() {
1625        int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1626        return (CellLayout) getChildAt(index);
1627    }
1628
1629    /**
1630     * {@inheritDoc}
1631     */
1632    public boolean acceptDrop(DragSource source, int x, int y,
1633            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1634        final CellLayout layout = getCurrentDropLayout();
1635        final CellLayout.CellInfo dragCellInfo = mDragInfo;
1636        final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
1637        final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
1638
1639        final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
1640        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
1641
1642        if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) {
1643            return true;
1644        } else {
1645            Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
1646            return false;
1647        }
1648    }
1649
1650    /**
1651     * {@inheritDoc}
1652     */
1653    public Rect estimateDropLocation(DragSource source, int x, int y,
1654            int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
1655        final CellLayout layout = getCurrentDropLayout();
1656
1657        final CellLayout.CellInfo cellInfo = mDragInfo;
1658        final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1659        final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1660        final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1661
1662        final Rect location = recycle != null ? recycle : new Rect();
1663
1664        // Find drop cell and convert into rectangle
1665        int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX,
1666                spanY, ignoreView, layout, mTempCell);
1667
1668        if (dropCell == null) {
1669            return null;
1670        }
1671
1672        layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
1673        location.left = mTempEstimate[0];
1674        location.top = mTempEstimate[1];
1675
1676        layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
1677        location.right = mTempEstimate[0];
1678        location.bottom = mTempEstimate[1];
1679
1680        return location;
1681    }
1682
1683    /**
1684     * Calculate the nearest cell where the given object would be dropped.
1685     */
1686    private int[] estimateDropCell(int pixelX, int pixelY,
1687            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1688
1689        final int[] cellXY = mTempCell;
1690        layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY);
1691        layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate);
1692
1693        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
1694        // Find the best target drop location
1695        return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle);
1696    }
1697
1698    /**
1699     * Estimate the size that a child with the given dimensions will take in the current screen.
1700     */
1701    void estimateChildSize(int minWidth, int minHeight, int[] result) {
1702        ((CellLayout)getChildAt(mCurrentScreen)).estimateChildSize(minWidth, minHeight, result);
1703    }
1704
1705    void setLauncher(Launcher launcher) {
1706        mLauncher = launcher;
1707    }
1708
1709    public void setDragController(DragController dragController) {
1710        mDragController = dragController;
1711    }
1712
1713    public void onDropCompleted(View target, boolean success) {
1714        if (success) {
1715            if (target != this && mDragInfo != null) {
1716                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1717                cellLayout.removeView(mDragInfo.cell);
1718                if (mDragInfo.cell instanceof DropTarget) {
1719                    mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1720                }
1721                // final Object tag = mDragInfo.cell.getTag();
1722            }
1723        } else {
1724            if (mDragInfo != null) {
1725                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1726                cellLayout.onDropAborted(mDragInfo.cell);
1727            }
1728        }
1729
1730        mDragInfo = null;
1731    }
1732
1733    public void scrollLeft() {
1734        if (!mIsSmall) {
1735            if (mScroller.isFinished()) {
1736                if (mCurrentScreen > 0)
1737                    snapToScreen(mCurrentScreen - 1);
1738            } else {
1739                if (mNextScreen > 0)
1740                    snapToScreen(mNextScreen - 1);
1741            }
1742        }
1743    }
1744
1745    public void scrollRight() {
1746        if (!mIsSmall) {
1747            if (mScroller.isFinished()) {
1748                if (mCurrentScreen < getChildCount() - 1)
1749                    snapToScreen(mCurrentScreen + 1);
1750            } else {
1751                if (mNextScreen < getChildCount() - 1)
1752                    snapToScreen(mNextScreen + 1);
1753            }
1754        }
1755    }
1756
1757    public int getScreenForView(View v) {
1758        int result = -1;
1759        if (v != null) {
1760            ViewParent vp = v.getParent();
1761            final int screenCount = getChildCount();
1762            for (int i = 0; i < screenCount; i++) {
1763                if (vp == getChildAt(i)) {
1764                    return i;
1765                }
1766            }
1767        }
1768        return result;
1769    }
1770
1771    public Folder getFolderForTag(Object tag) {
1772        final int screenCount = getChildCount();
1773        for (int screen = 0; screen < screenCount; screen++) {
1774            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1775            int count = currentScreen.getChildCount();
1776            for (int i = 0; i < count; i++) {
1777                View child = currentScreen.getChildAt(i);
1778                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1779                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1780                    Folder f = (Folder) child;
1781                    if (f.getInfo() == tag && f.getInfo().opened) {
1782                        return f;
1783                    }
1784                }
1785            }
1786        }
1787        return null;
1788    }
1789
1790    public View getViewForTag(Object tag) {
1791        int screenCount = getChildCount();
1792        for (int screen = 0; screen < screenCount; screen++) {
1793            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1794            int count = currentScreen.getChildCount();
1795            for (int i = 0; i < count; i++) {
1796                View child = currentScreen.getChildAt(i);
1797                if (child.getTag() == tag) {
1798                    return child;
1799                }
1800            }
1801        }
1802        return null;
1803    }
1804
1805    /**
1806     * @return True is long presses are still allowed for the current touch
1807     */
1808    public boolean allowLongPress() {
1809        return mAllowLongPress;
1810    }
1811
1812    /**
1813     * Set true to allow long-press events to be triggered, usually checked by
1814     * {@link Launcher} to accept or block dpad-initiated long-presses.
1815     */
1816    public void setAllowLongPress(boolean allowLongPress) {
1817        mAllowLongPress = allowLongPress;
1818    }
1819
1820    void removeItems(final ArrayList<ApplicationInfo> apps) {
1821        final int screenCount = getChildCount();
1822        final PackageManager manager = getContext().getPackageManager();
1823        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
1824
1825        final HashSet<String> packageNames = new HashSet<String>();
1826        final int appCount = apps.size();
1827        for (int i = 0; i < appCount; i++) {
1828            packageNames.add(apps.get(i).componentName.getPackageName());
1829        }
1830
1831        for (int i = 0; i < screenCount; i++) {
1832            final CellLayout layout = (CellLayout) getChildAt(i);
1833
1834            // Avoid ANRs by treating each screen separately
1835            post(new Runnable() {
1836                public void run() {
1837                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
1838                    childrenToRemove.clear();
1839
1840                    int childCount = layout.getChildCount();
1841                    for (int j = 0; j < childCount; j++) {
1842                        final View view = layout.getChildAt(j);
1843                        Object tag = view.getTag();
1844
1845                        if (tag instanceof ShortcutInfo) {
1846                            final ShortcutInfo info = (ShortcutInfo) tag;
1847                            final Intent intent = info.intent;
1848                            final ComponentName name = intent.getComponent();
1849
1850                            if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1851                                for (String packageName: packageNames) {
1852                                    if (packageName.equals(name.getPackageName())) {
1853                                        // TODO: This should probably be done on a worker thread
1854                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1855                                        childrenToRemove.add(view);
1856                                    }
1857                                }
1858                            }
1859                        } else if (tag instanceof UserFolderInfo) {
1860                            final UserFolderInfo info = (UserFolderInfo) tag;
1861                            final ArrayList<ShortcutInfo> contents = info.contents;
1862                            final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
1863                            final int contentsCount = contents.size();
1864                            boolean removedFromFolder = false;
1865
1866                            for (int k = 0; k < contentsCount; k++) {
1867                                final ShortcutInfo appInfo = contents.get(k);
1868                                final Intent intent = appInfo.intent;
1869                                final ComponentName name = intent.getComponent();
1870
1871                                if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1872                                    for (String packageName: packageNames) {
1873                                        if (packageName.equals(name.getPackageName())) {
1874                                            toRemove.add(appInfo);
1875                                            // TODO: This should probably be done on a worker thread
1876                                            LauncherModel.deleteItemFromDatabase(
1877                                                    mLauncher, appInfo);
1878                                            removedFromFolder = true;
1879                                        }
1880                                    }
1881                                }
1882                            }
1883
1884                            contents.removeAll(toRemove);
1885                            if (removedFromFolder) {
1886                                final Folder folder = getOpenFolder();
1887                                if (folder != null)
1888                                    folder.notifyDataSetChanged();
1889                            }
1890                        } else if (tag instanceof LiveFolderInfo) {
1891                            final LiveFolderInfo info = (LiveFolderInfo) tag;
1892                            final Uri uri = info.uri;
1893                            final ProviderInfo providerInfo = manager.resolveContentProvider(
1894                                    uri.getAuthority(), 0);
1895
1896                            if (providerInfo != null) {
1897                                for (String packageName: packageNames) {
1898                                    if (packageName.equals(providerInfo.packageName)) {
1899                                        // TODO: This should probably be done on a worker thread
1900                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1901                                        childrenToRemove.add(view);
1902                                    }
1903                                }
1904                            }
1905                        } else if (tag instanceof LauncherAppWidgetInfo) {
1906                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1907                            final AppWidgetProviderInfo provider =
1908                                    widgets.getAppWidgetInfo(info.appWidgetId);
1909                            if (provider != null) {
1910                                for (String packageName: packageNames) {
1911                                    if (packageName.equals(provider.provider.getPackageName())) {
1912                                        // TODO: This should probably be done on a worker thread
1913                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1914                                        childrenToRemove.add(view);
1915                                    }
1916                                }
1917                            }
1918                        }
1919                    }
1920
1921                    childCount = childrenToRemove.size();
1922                    for (int j = 0; j < childCount; j++) {
1923                        View child = childrenToRemove.get(j);
1924                        layout.removeViewInLayout(child);
1925                        if (child instanceof DropTarget) {
1926                            mDragController.removeDropTarget((DropTarget)child);
1927                        }
1928                    }
1929
1930                    if (childCount > 0) {
1931                        layout.requestLayout();
1932                        layout.invalidate();
1933                    }
1934                }
1935            });
1936        }
1937    }
1938
1939    void updateShortcuts(ArrayList<ApplicationInfo> apps) {
1940        final PackageManager pm = mLauncher.getPackageManager();
1941
1942        final int screenCount = getChildCount();
1943        for (int i = 0; i < screenCount; i++) {
1944            final CellLayout layout = (CellLayout) getChildAt(i);
1945            int childCount = layout.getChildCount();
1946            for (int j = 0; j < childCount; j++) {
1947                final View view = layout.getChildAt(j);
1948                Object tag = view.getTag();
1949                if (tag instanceof ShortcutInfo) {
1950                    ShortcutInfo info = (ShortcutInfo)tag;
1951                    // We need to check for ACTION_MAIN otherwise getComponent() might
1952                    // return null for some shortcuts (for instance, for shortcuts to
1953                    // web pages.)
1954                    final Intent intent = info.intent;
1955                    final ComponentName name = intent.getComponent();
1956                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1957                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1958                        final int appCount = apps.size();
1959                        for (int k = 0; k < appCount; k++) {
1960                            ApplicationInfo app = apps.get(k);
1961                            if (app.componentName.equals(name)) {
1962                                info.setIcon(mIconCache.getIcon(info.intent));
1963                                ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
1964                                        new FastBitmapDrawable(info.getIcon(mIconCache)),
1965                                        null, null);
1966                                }
1967                        }
1968                    }
1969                }
1970            }
1971        }
1972    }
1973
1974    void moveToDefaultScreen(boolean animate) {
1975        if (animate) {
1976            if (mIsSmall) {
1977                unshrink(mDefaultScreen);
1978            } else {
1979                snapToScreen(mDefaultScreen);
1980            }
1981        } else {
1982            setCurrentScreen(mDefaultScreen);
1983        }
1984        getChildAt(mDefaultScreen).requestFocus();
1985    }
1986
1987    void setIndicators(Drawable previous, Drawable next) {
1988        mPreviousIndicator = previous;
1989        mNextIndicator = next;
1990        previous.setLevel(mCurrentScreen);
1991        next.setLevel(mCurrentScreen);
1992    }
1993
1994    public static class SavedState extends BaseSavedState {
1995        int currentScreen = -1;
1996
1997        SavedState(Parcelable superState) {
1998            super(superState);
1999        }
2000
2001        private SavedState(Parcel in) {
2002            super(in);
2003            currentScreen = in.readInt();
2004        }
2005
2006        @Override
2007        public void writeToParcel(Parcel out, int flags) {
2008            super.writeToParcel(out, flags);
2009            out.writeInt(currentScreen);
2010        }
2011
2012        public static final Parcelable.Creator<SavedState> CREATOR =
2013                new Parcelable.Creator<SavedState>() {
2014            public SavedState createFromParcel(Parcel in) {
2015                return new SavedState(in);
2016            }
2017
2018            public SavedState[] newArray(int size) {
2019                return new SavedState[size];
2020            }
2021        };
2022    }
2023}
2024