Workspace.java revision 3a8820bdbad90642cf5cda4b00a8c92ecb699159
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 android.app.WallpaperManager;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ComponentName;
23import android.content.res.TypedArray;
24import android.content.pm.PackageManager;
25import android.graphics.Canvas;
26import android.graphics.RectF;
27import android.graphics.Rect;
28import android.graphics.drawable.Drawable;
29import android.os.Parcelable;
30import android.os.Parcel;
31import android.os.SystemClock;
32import android.util.AttributeSet;
33import android.util.Log;
34import android.view.MotionEvent;
35import android.view.VelocityTracker;
36import android.view.View;
37import android.view.ViewConfiguration;
38import android.view.ViewGroup;
39import android.view.ViewParent;
40import android.widget.Scroller;
41import android.widget.TextView;
42
43import java.util.ArrayList;
44
45/**
46 * The workspace is a wide area with a wallpaper and a finite number of screens. Each
47 * screen contains a number of icons, folders or widgets the user can interact with.
48 * A workspace is meant to be used with a fixed width only.
49 */
50public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
51    private static final String TAG = "Launcher.Workspace";
52    private static final int INVALID_SCREEN = -1;
53
54    /**
55     * The velocity at which a fling gesture will cause us to snap to the next screen
56     */
57    private static final int SNAP_VELOCITY = 1000;
58
59    private final WallpaperManager mWallpaperManager;
60
61    private int mDefaultScreen;
62
63    private boolean mFirstLayout = true;
64
65    private int mCurrentScreen;
66    private int mNextScreen = INVALID_SCREEN;
67    private Scroller mScroller;
68    private VelocityTracker mVelocityTracker;
69
70    /**
71     * CellInfo for the cell that is currently being dragged
72     */
73    private CellLayout.CellInfo mDragInfo;
74
75    /**
76     * Target drop area calculated during last acceptDrop call.
77     */
78    private int[] mTargetCell = null;
79
80    private float mLastMotionX;
81    private float mLastMotionY;
82
83    private final static int TOUCH_STATE_REST = 0;
84    private final static int TOUCH_STATE_SCROLLING = 1;
85
86    private int mTouchState = TOUCH_STATE_REST;
87
88    private OnLongClickListener mLongClickListener;
89
90    private Launcher mLauncher;
91    private DragController mDragController;
92
93    /**
94     * Cache of vacant cells, used during drag events and invalidated as needed.
95     */
96    private CellLayout.CellInfo mVacantCache = null;
97
98    private int[] mTempCell = new int[2];
99    private int[] mTempEstimate = new int[2];
100
101    private boolean mAllowLongPress = true;
102
103    private int mTouchSlop;
104    private int mMaximumVelocity;
105
106    final Rect mDrawerBounds = new Rect();
107    final Rect mClipBounds = new Rect();
108    int mDrawerContentHeight;
109    int mDrawerContentWidth;
110
111    private Drawable mPreviousIndicator;
112    private Drawable mNextIndicator;
113
114    private boolean mFading = true;
115
116    /**
117     * Used to inflate the Workspace from XML.
118     *
119     * @param context The application's context.
120     * @param attrs The attribtues set containing the Workspace's customization values.
121     */
122    public Workspace(Context context, AttributeSet attrs) {
123        this(context, attrs, 0);
124    }
125
126    /**
127     * Used to inflate the Workspace from XML.
128     *
129     * @param context The application's context.
130     * @param attrs The attribtues set containing the Workspace's customization values.
131     * @param defStyle Unused.
132     */
133    public Workspace(Context context, AttributeSet attrs, int defStyle) {
134        super(context, attrs, defStyle);
135
136        mWallpaperManager = WallpaperManager.getInstance(context);
137
138        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
139        mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
140        a.recycle();
141
142        initWorkspace();
143    }
144
145    /**
146     * Initializes various states for this workspace.
147     */
148    private void initWorkspace() {
149        mScroller = new Scroller(getContext());
150        mCurrentScreen = mDefaultScreen;
151        Launcher.setScreen(mCurrentScreen);
152
153        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
154        mTouchSlop = configuration.getScaledTouchSlop();
155        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
156    }
157
158    @Override
159    public void addView(View child, int index, LayoutParams params) {
160        if (!(child instanceof CellLayout)) {
161            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
162        }
163        super.addView(child, index, params);
164    }
165
166    @Override
167    public void addView(View child) {
168        if (!(child instanceof CellLayout)) {
169            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
170        }
171        super.addView(child);
172    }
173
174    @Override
175    public void addView(View child, int index) {
176        if (!(child instanceof CellLayout)) {
177            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
178        }
179        super.addView(child, index);
180    }
181
182    @Override
183    public void addView(View child, int width, int height) {
184        if (!(child instanceof CellLayout)) {
185            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
186        }
187        super.addView(child, width, height);
188    }
189
190    @Override
191    public void addView(View child, LayoutParams params) {
192        if (!(child instanceof CellLayout)) {
193            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
194        }
195        super.addView(child, params);
196    }
197
198    /**
199     * @return The open folder on the current screen, or null if there is none
200     */
201    Folder getOpenFolder() {
202        CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
203        int count = currentScreen.getChildCount();
204        for (int i = 0; i < count; i++) {
205            View child = currentScreen.getChildAt(i);
206            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
207            if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
208                return (Folder) child;
209            }
210        }
211        return null;
212    }
213
214    ArrayList<Folder> getOpenFolders() {
215        final int screens = getChildCount();
216        ArrayList<Folder> folders = new ArrayList<Folder>(screens);
217
218        for (int screen = 0; screen < screens; screen++) {
219            CellLayout currentScreen = (CellLayout) getChildAt(screen);
220            int count = currentScreen.getChildCount();
221            for (int i = 0; i < count; i++) {
222                View child = currentScreen.getChildAt(i);
223                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
224                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
225                    folders.add((Folder) child);
226                    break;
227                }
228            }
229        }
230
231        return folders;
232    }
233
234    boolean isDefaultScreenShowing() {
235        return mCurrentScreen == mDefaultScreen;
236    }
237
238    /**
239     * Returns the index of the currently displayed screen.
240     *
241     * @return The index of the currently displayed screen.
242     */
243    int getCurrentScreen() {
244        return mCurrentScreen;
245    }
246
247    /**
248     * Returns how many screens there are.
249     */
250    int getScreenCount() {
251        return getChildCount();
252    }
253
254    /**
255     * Computes a bounding rectangle for a range of cells
256     *
257     * @param cellX X coordinate of upper left corner expressed as a cell position
258     * @param cellY Y coordinate of upper left corner expressed as a cell position
259     * @param cellHSpan Width in cells
260     * @param cellVSpan Height in cells
261     * @param rect Rectnagle into which to put the results
262     */
263    public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF rect) {
264        ((CellLayout)getChildAt(mCurrentScreen)).cellToRect(cellX, cellY,
265                cellHSpan, cellVSpan, rect);
266    }
267
268    /**
269     * Sets the current screen.
270     *
271     * @param currentScreen
272     */
273    void setCurrentScreen(int currentScreen) {
274        clearVacantCache();
275        mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
276        scrollTo(mCurrentScreen * getWidth(), 0);
277        invalidate();
278    }
279
280    /**
281     * Adds the specified child in the current screen. The position and dimension of
282     * the child are defined by x, y, spanX and spanY.
283     *
284     * @param child The child to add in one of the workspace's screens.
285     * @param x The X position of the child in the screen's grid.
286     * @param y The Y position of the child in the screen's grid.
287     * @param spanX The number of cells spanned horizontally by the child.
288     * @param spanY The number of cells spanned vertically by the child.
289     */
290    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
291        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
292    }
293
294    /**
295     * Adds the specified child in the current screen. The position and dimension of
296     * the child are defined by x, y, spanX and spanY.
297     *
298     * @param child The child to add in one of the workspace's screens.
299     * @param x The X position of the child in the screen's grid.
300     * @param y The Y position of the child in the screen's grid.
301     * @param spanX The number of cells spanned horizontally by the child.
302     * @param spanY The number of cells spanned vertically by the child.
303     * @param insert When true, the child is inserted at the beginning of the children list.
304     */
305    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
306        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
307    }
308
309    /**
310     * Adds the specified child in the specified screen. The position and dimension of
311     * the child are defined by x, y, spanX and spanY.
312     *
313     * @param child The child to add in one of the workspace's screens.
314     * @param screen The screen in which to add the child.
315     * @param x The X position of the child in the screen's grid.
316     * @param y The Y position of the child in the screen's grid.
317     * @param spanX The number of cells spanned horizontally by the child.
318     * @param spanY The number of cells spanned vertically by the child.
319     */
320    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
321        addInScreen(child, screen, x, y, spanX, spanY, false);
322    }
323
324    /**
325     * Adds the specified child in the specified screen. The position and dimension of
326     * the child are defined by x, y, spanX and spanY.
327     *
328     * @param child The child to add in one of the workspace's screens.
329     * @param screen The screen in which to add the child.
330     * @param x The X position of the child in the screen's grid.
331     * @param y The Y position of the child in the screen's grid.
332     * @param spanX The number of cells spanned horizontally by the child.
333     * @param spanY The number of cells spanned vertically by the child.
334     * @param insert When true, the child is inserted at the beginning of the children list.
335     */
336    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
337        if (screen < 0 || screen >= getChildCount()) {
338            throw new IllegalStateException("The screen must be >= 0 and < " + getChildCount());
339        }
340
341        clearVacantCache();
342
343        final CellLayout group = (CellLayout) getChildAt(screen);
344        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
345        if (lp == null) {
346            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
347        } else {
348            lp.cellX = x;
349            lp.cellY = y;
350            lp.cellHSpan = spanX;
351            lp.cellVSpan = spanY;
352        }
353        group.addView(child, insert ? 0 : -1, lp);
354        if (!(child instanceof Folder)) {
355            child.setOnLongClickListener(mLongClickListener);
356        }
357        if (child instanceof DropTarget) {
358            mDragController.addDropTarget((DropTarget)child);
359        }
360    }
361
362    void addWidget(View view, Widget widget) {
363        addInScreen(view, widget.screen, widget.cellX, widget.cellY, widget.spanX,
364                widget.spanY, false);
365    }
366
367    void addWidget(View view, Widget widget, boolean insert) {
368        addInScreen(view, widget.screen, widget.cellX, widget.cellY, widget.spanX,
369                widget.spanY, insert);
370    }
371
372    CellLayout.CellInfo findAllVacantCells(boolean[] occupied) {
373        CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
374        if (group != null) {
375            return group.findAllVacantCells(occupied, null);
376        }
377        return null;
378    }
379
380    private void clearVacantCache() {
381        if (mVacantCache != null) {
382            mVacantCache.clearVacantCells();
383            mVacantCache = null;
384        }
385    }
386
387    /**
388     * Returns the coordinate of a vacant cell for the current screen.
389     */
390    boolean getVacantCell(int[] vacant, int spanX, int spanY) {
391        CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
392        if (group != null) {
393            return group.getVacantCell(vacant, spanX, spanY);
394        }
395        return false;
396    }
397
398    /**
399     * Adds the specified child in the current 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 spanX The number of cells spanned horizontally by the child.
404     * @param spanY The number of cells spanned vertically by the child.
405     */
406    void fitInCurrentScreen(View child, int spanX, int spanY) {
407        fitInScreen(child, mCurrentScreen, spanX, spanY);
408    }
409
410    /**
411     * Adds the specified child in the specified screen. The position and dimension of
412     * the child are defined by x, y, spanX and spanY.
413     *
414     * @param child The child to add in one of the workspace's screens.
415     * @param screen The screen in which to add the child.
416     * @param spanX The number of cells spanned horizontally by the child.
417     * @param spanY The number of cells spanned vertically by the child.
418     */
419    void fitInScreen(View child, int screen, int spanX, int spanY) {
420        if (screen < 0 || screen >= getChildCount()) {
421            throw new IllegalStateException("The screen must be >= 0 and < " + getChildCount());
422        }
423
424        final CellLayout group = (CellLayout) getChildAt(screen);
425        boolean vacant = group.getVacantCell(mTempCell, spanX, spanY);
426        if (vacant) {
427            group.addView(child,
428                    new CellLayout.LayoutParams(mTempCell[0], mTempCell[1], spanX, spanY));
429            child.setOnLongClickListener(mLongClickListener);
430            if (!(child instanceof Folder)) {
431                child.setOnLongClickListener(mLongClickListener);
432            }
433            if (child instanceof DropTarget) {
434                mDragController.addDropTarget((DropTarget)child);
435            }
436        }
437    }
438
439    /**
440     * Registers the specified listener on each screen contained in this workspace.
441     *
442     * @param l The listener used to respond to long clicks.
443     */
444    @Override
445    public void setOnLongClickListener(OnLongClickListener l) {
446        mLongClickListener = l;
447        final int count = getChildCount();
448        for (int i = 0; i < count; i++) {
449            getChildAt(i).setOnLongClickListener(l);
450        }
451    }
452
453    private void updateWallpaperOffset() {
454        updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
455    }
456
457    private void updateWallpaperOffset(int scrollRange) {
458        mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
459        mWallpaperManager.setWallpaperOffsets(getWindowToken(), mScrollX / (float) scrollRange, 0);
460    }
461
462    @Override
463    public void computeScroll() {
464        if (mScroller.computeScrollOffset()) {
465            mScrollX = mScroller.getCurrX();
466            mScrollY = mScroller.getCurrY();
467            updateWallpaperOffset();
468            postInvalidate();
469        } else if (mNextScreen != INVALID_SCREEN) {
470            mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
471            mPreviousIndicator.setLevel(mCurrentScreen);
472            mNextIndicator.setLevel(mCurrentScreen);
473            Launcher.setScreen(mCurrentScreen);
474            mNextScreen = INVALID_SCREEN;
475            clearChildrenCache();
476        }
477    }
478
479    public void startFading(boolean dest) {
480        mFading = dest;
481        invalidate();
482    }
483
484    @Override
485    protected void dispatchDraw(Canvas canvas) {
486        final boolean allAppsOpaque = mLauncher.isAllAppsOpaque();
487        if (mFading == allAppsOpaque) {
488            invalidate();
489        } else {
490            mFading = !allAppsOpaque;
491        }
492        if (allAppsOpaque) {
493            // If the launcher is up, draw black.
494            canvas.drawARGB(0xff, 0, 0, 0);
495            return;
496        }
497
498        if (Launcher.lastStartTime != 0) {
499            int itemCount = 0;
500            for (int i=0; i<getChildCount(); i++) {
501                View child = getChildAt(i);
502                if (child instanceof ViewGroup) {
503                    itemCount += ((ViewGroup)child).getChildCount();
504                }
505            }
506            if (!mLauncher.isWorkspaceLocked()) {
507                Log.d(Launcher.TAG, "time from start to draw (" + itemCount + " items): "
508                        + (SystemClock.uptimeMillis() - Launcher.lastStartTime) + "ms");
509                Launcher.lastStartTime = 0;
510            } else {
511                Log.d(Launcher.TAG, "drawing but not ready yet (" + itemCount + " items): "
512                        + (SystemClock.uptimeMillis() - Launcher.lastStartTime) + "ms");
513            }
514        }
515
516        boolean restore = false;
517        int restoreCount = 0;
518
519        // For the fade.  If view gets setAlpha(), use that instead.
520        float scale = mScale;
521        if (scale < 0.999f) {
522            int sx = mScrollX;
523
524            int alpha = (scale < 0.5f) ? (int)(255 * 2 * scale) : 255;
525
526            restoreCount = canvas.saveLayerAlpha(sx, 0, sx+getWidth(), getHeight(), alpha,
527                    Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
528            restore = true;
529
530            if (scale < 0.999f) {
531                int w = getWidth();
532                w += 2 * mCurrentScreen * w;
533                int dx = w/2;
534                int h = getHeight();
535                int dy = (h/2) - (h/4);
536                canvas.translate(dx, dy);
537                canvas.scale(scale, scale);
538                canvas.translate(-dx, -dy);
539            }
540        }
541
542        // ViewGroup.dispatchDraw() supports many features we don't need:
543        // clip to padding, layout animation, animation listener, disappearing
544        // children, etc. The following implementation attempts to fast-track
545        // the drawing dispatch by drawing only what we know needs to be drawn.
546
547        boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN
548                && scale > 0.999f;
549        // If we are not scrolling or flinging, draw only the current screen
550        if (fastDraw) {
551            drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
552        } else {
553            final long drawingTime = getDrawingTime();
554            // If we are flinging, draw only the current screen and the target screen
555            if (mNextScreen >= 0 && mNextScreen < getChildCount() &&
556                    Math.abs(mCurrentScreen - mNextScreen) == 1) {
557                drawChild(canvas, getChildAt(mCurrentScreen), drawingTime);
558                drawChild(canvas, getChildAt(mNextScreen), drawingTime);
559            } else {
560                // If we are scrolling, draw all of our children
561                final int count = getChildCount();
562                for (int i = 0; i < count; i++) {
563                    drawChild(canvas, getChildAt(i), drawingTime);
564                }
565            }
566        }
567
568        if (restore) {
569            canvas.restoreToCount(restoreCount);
570        }
571    }
572
573    private float mScale = 1.0f;
574    public void setScale(float scale) {
575        mScale = scale;
576        invalidate();
577    }
578
579    protected void onAttachedToWindow() {
580        super.onAttachedToWindow();
581        mDragController.setWindowToken(getWindowToken());
582    }
583
584    @Override
585    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
586        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
587
588        final int width = MeasureSpec.getSize(widthMeasureSpec);
589        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
590        if (widthMode != MeasureSpec.EXACTLY) {
591            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
592        }
593
594        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
595        if (heightMode != MeasureSpec.EXACTLY) {
596            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
597        }
598
599        // The children are given the same width and height as the workspace
600        final int count = getChildCount();
601        for (int i = 0; i < count; i++) {
602            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
603        }
604
605
606        if (mFirstLayout) {
607            setHorizontalScrollBarEnabled(false);
608            scrollTo(mCurrentScreen * width, 0);
609            setHorizontalScrollBarEnabled(true);
610            updateWallpaperOffset(width * (getChildCount() - 1));
611            mFirstLayout = false;
612        }
613    }
614
615    @Override
616    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
617        int childLeft = 0;
618
619        final int count = getChildCount();
620        for (int i = 0; i < count; i++) {
621            final View child = getChildAt(i);
622            if (child.getVisibility() != View.GONE) {
623                final int childWidth = child.getMeasuredWidth();
624                child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
625                childLeft += childWidth;
626            }
627        }
628    }
629
630    @Override
631    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
632        int screen = indexOfChild(child);
633        if (screen != mCurrentScreen || !mScroller.isFinished()) {
634            if (!mLauncher.isWorkspaceLocked()) {
635                snapToScreen(screen);
636            }
637            return true;
638        }
639        return false;
640    }
641
642    @Override
643    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
644        if (!mLauncher.isAllAppsVisible()) {
645            final Folder openFolder = getOpenFolder();
646            if (openFolder != null) {
647                return openFolder.requestFocus(direction, previouslyFocusedRect);
648            } else {
649                int focusableScreen;
650                if (mNextScreen != INVALID_SCREEN) {
651                    focusableScreen = mNextScreen;
652                } else {
653                    focusableScreen = mCurrentScreen;
654                }
655                getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
656            }
657        }
658        return false;
659    }
660
661    @Override
662    public boolean dispatchUnhandledMove(View focused, int direction) {
663        if (direction == View.FOCUS_LEFT) {
664            if (getCurrentScreen() > 0) {
665                snapToScreen(getCurrentScreen() - 1);
666                return true;
667            }
668        } else if (direction == View.FOCUS_RIGHT) {
669            if (getCurrentScreen() < getChildCount() - 1) {
670                snapToScreen(getCurrentScreen() + 1);
671                return true;
672            }
673        }
674        return super.dispatchUnhandledMove(focused, direction);
675    }
676
677    @Override
678    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
679        if (!mLauncher.isAllAppsVisible()) {
680            final Folder openFolder = getOpenFolder();
681            if (openFolder == null) {
682                getChildAt(mCurrentScreen).addFocusables(views, direction);
683                if (direction == View.FOCUS_LEFT) {
684                    if (mCurrentScreen > 0) {
685                        getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
686                    }
687                } else if (direction == View.FOCUS_RIGHT){
688                    if (mCurrentScreen < getChildCount() - 1) {
689                        getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
690                    }
691                }
692            } else {
693                openFolder.addFocusables(views, direction);
694            }
695        }
696    }
697
698    @Override
699    public boolean dispatchTouchEvent(MotionEvent ev) {
700        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
701            if (mLauncher.isWorkspaceLocked() || mLauncher.isAllAppsVisible()) {
702                return false;
703            }
704        }
705        return super.dispatchTouchEvent(ev);
706    }
707
708    @Override
709    public boolean onInterceptTouchEvent(MotionEvent ev) {
710        if (mLauncher.isWorkspaceLocked() || mLauncher.isAllAppsVisible()) {
711            return false; // We don't want the events.  Let them fall through to the all apps view.
712        }
713
714        /*
715         * This method JUST determines whether we want to intercept the motion.
716         * If we return true, onTouchEvent will be called and we do the actual
717         * scrolling there.
718         */
719
720        /*
721         * Shortcut the most recurring case: the user is in the dragging
722         * state and he is moving his finger.  We want to intercept this
723         * motion.
724         */
725        final int action = ev.getAction();
726        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
727            return true;
728        }
729
730        final float x = ev.getX();
731        final float y = ev.getY();
732
733        switch (action) {
734            case MotionEvent.ACTION_MOVE:
735                /*
736                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
737                 * whether the user has moved far enough from his original down touch.
738                 */
739
740                /*
741                 * Locally do absolute value. mLastMotionX is set to the y value
742                 * of the down event.
743                 */
744                final int xDiff = (int) Math.abs(x - mLastMotionX);
745                final int yDiff = (int) Math.abs(y - mLastMotionY);
746
747                final int touchSlop = mTouchSlop;
748                boolean xMoved = xDiff > touchSlop;
749                boolean yMoved = yDiff > touchSlop;
750
751                if (xMoved || yMoved) {
752
753                    if (xMoved) {
754                        // Scroll if the user moved far enough along the X axis
755                        mTouchState = TOUCH_STATE_SCROLLING;
756                        enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
757                    }
758                    // Either way, cancel any pending longpress
759                    if (mAllowLongPress) {
760                        mAllowLongPress = false;
761                        // Try canceling the long press. It could also have been scheduled
762                        // by a distant descendant, so use the mAllowLongPress flag to block
763                        // everything
764                        final View currentScreen = getChildAt(mCurrentScreen);
765                        currentScreen.cancelLongPress();
766                    }
767                }
768                break;
769
770            case MotionEvent.ACTION_DOWN:
771                // Remember location of down touch
772                mLastMotionX = x;
773                mLastMotionY = y;
774                mAllowLongPress = true;
775
776                /*
777                 * If being flinged and user touches the screen, initiate drag;
778                 * otherwise don't.  mScroller.isFinished should be false when
779                 * being flinged.
780                 */
781                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
782                break;
783
784            case MotionEvent.ACTION_CANCEL:
785            case MotionEvent.ACTION_UP:
786
787                if (mTouchState != TOUCH_STATE_SCROLLING) {
788
789                    final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
790                    if (!currentScreen.lastDownOnOccupiedCell()) {
791                        // Send a tap to the wallpaper if the last down was on empty space
792                        mWallpaperManager.sendWallpaperCommand(getWindowToken(),
793                                "android.wallpaper.tap", (int) ev.getX(), (int) ev.getY(), 0, null);
794                    }
795                }
796
797                // Release the drag
798                clearChildrenCache();
799                mTouchState = TOUCH_STATE_REST;
800                mAllowLongPress = false;
801
802                break;
803        }
804
805        /*
806         * The only time we want to intercept motion events is if we are in the
807         * drag mode.
808         */
809        return mTouchState != TOUCH_STATE_REST;
810    }
811
812    /**
813     * If one of our descendant views decides that it could be focused now, only
814     * pass that along if it's on the current screen.
815     *
816     * This happens when live folders requery, and if they're off screen, they
817     * end up calling requestFocus, which pulls it on screen.
818     */
819    @Override
820    public void focusableViewAvailable(View focused) {
821        View current = getChildAt(mCurrentScreen);
822        View v = focused;
823        while (true) {
824            if (v == current) {
825                super.focusableViewAvailable(focused);
826                return;
827            }
828            if (v == this) {
829                return;
830            }
831            ViewParent parent = v.getParent();
832            if (parent instanceof View) {
833                v = (View)v.getParent();
834            } else {
835                return;
836            }
837        }
838    }
839
840    void enableChildrenCache(int fromScreen, int toScreen) {
841        if (fromScreen > toScreen) {
842            fromScreen = toScreen;
843            toScreen = fromScreen;
844        }
845
846        final int count = getChildCount();
847
848        fromScreen = Math.max(fromScreen, 0);
849        toScreen = Math.min(toScreen, count - 1);
850
851        for (int i = fromScreen; i <= toScreen; i++) {
852            final CellLayout layout = (CellLayout) getChildAt(i);
853            layout.setChildrenDrawnWithCacheEnabled(true);
854            layout.setChildrenDrawingCacheEnabled(true);
855        }
856    }
857
858    void clearChildrenCache() {
859        final int count = getChildCount();
860        for (int i = 0; i < count; i++) {
861            final CellLayout layout = (CellLayout) getChildAt(i);
862            layout.setChildrenDrawnWithCacheEnabled(false);
863        }
864    }
865
866    @Override
867    public boolean onTouchEvent(MotionEvent ev) {
868
869        if (mLauncher.isWorkspaceLocked()) {
870            return false; // We don't want the events.  Let them fall through to the all apps view.
871        }
872        if (mLauncher.isAllAppsVisible()) {
873            // Cancel any scrolling that is in progress.
874            if (!mScroller.isFinished()) {
875                mScroller.abortAnimation();
876            }
877            snapToScreen(mCurrentScreen);
878            return false; // We don't want the events.  Let them fall through to the all apps view.
879        }
880
881        if (mVelocityTracker == null) {
882            mVelocityTracker = VelocityTracker.obtain();
883        }
884        mVelocityTracker.addMovement(ev);
885
886        final int action = ev.getAction();
887        final float x = ev.getX();
888
889        switch (action) {
890        case MotionEvent.ACTION_DOWN:
891            /*
892             * If being flinged and user touches, stop the fling. isFinished
893             * will be false if being flinged.
894             */
895            if (!mScroller.isFinished()) {
896                mScroller.abortAnimation();
897            }
898
899            // Remember where the motion event started
900            mLastMotionX = x;
901            break;
902        case MotionEvent.ACTION_MOVE:
903            if (mTouchState == TOUCH_STATE_SCROLLING) {
904                // Scroll to follow the motion event
905                final int deltaX = (int) (mLastMotionX - x);
906                mLastMotionX = x;
907
908                if (deltaX < 0) {
909                    if (mScrollX > 0) {
910                        scrollBy(Math.max(-mScrollX, deltaX), 0);
911                        updateWallpaperOffset();
912                    }
913                } else if (deltaX > 0) {
914                    final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -
915                            mScrollX - getWidth();
916                    if (availableToScroll > 0) {
917                        scrollBy(Math.min(availableToScroll, deltaX), 0);
918                        updateWallpaperOffset();
919                    }
920                } else {
921                    awakenScrollBars();
922                }
923            }
924            break;
925        case MotionEvent.ACTION_UP:
926            if (mTouchState == TOUCH_STATE_SCROLLING) {
927                final VelocityTracker velocityTracker = mVelocityTracker;
928                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
929                int velocityX = (int) velocityTracker.getXVelocity();
930
931                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
932                    // Fling hard enough to move left
933                    snapToScreen(mCurrentScreen - 1);
934                } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
935                    // Fling hard enough to move right
936                    snapToScreen(mCurrentScreen + 1);
937                } else {
938                    snapToDestination();
939                }
940
941                if (mVelocityTracker != null) {
942                    mVelocityTracker.recycle();
943                    mVelocityTracker = null;
944                }
945            }
946            mTouchState = TOUCH_STATE_REST;
947            break;
948        case MotionEvent.ACTION_CANCEL:
949            mTouchState = TOUCH_STATE_REST;
950        }
951
952        return true;
953    }
954
955    private void snapToDestination() {
956        final int screenWidth = getWidth();
957        final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
958
959        snapToScreen(whichScreen);
960    }
961
962    void snapToScreen(int whichScreen) {
963        if (!mScroller.isFinished()) return;
964
965        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
966
967        clearVacantCache();
968        enableChildrenCache(mCurrentScreen, whichScreen);
969
970
971        final int screenDelta = Math.abs(whichScreen - mCurrentScreen);
972
973        mNextScreen = whichScreen;
974
975        View focusedChild = getFocusedChild();
976        if (focusedChild != null && screenDelta != 0 && focusedChild == getChildAt(mCurrentScreen)) {
977            focusedChild.clearFocus();
978        }
979
980        final int newX = whichScreen * getWidth();
981        final int delta = newX - mScrollX;
982        final int duration = screenDelta * 300;
983        awakenScrollBars(duration);
984        mScroller.startScroll(mScrollX, 0, delta, 0, duration);
985        invalidate();
986    }
987
988    void startDrag(CellLayout.CellInfo cellInfo) {
989        View child = cellInfo.cell;
990
991        // Make sure the drag was started by a long press as opposed to a long click.
992        // Note that Search takes focus when clicked rather than entering touch mode
993        if (!child.isInTouchMode() && !(child instanceof Search)) {
994            return;
995        }
996
997        mDragInfo = cellInfo;
998        mDragInfo.screen = mCurrentScreen;
999
1000        CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
1001
1002        current.onDragChild(child);
1003        mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
1004        invalidate();
1005    }
1006
1007    @Override
1008    protected Parcelable onSaveInstanceState() {
1009        final SavedState state = new SavedState(super.onSaveInstanceState());
1010        state.currentScreen = mCurrentScreen;
1011        return state;
1012    }
1013
1014    @Override
1015    protected void onRestoreInstanceState(Parcelable state) {
1016        SavedState savedState = (SavedState) state;
1017        super.onRestoreInstanceState(savedState.getSuperState());
1018        if (savedState.currentScreen != -1) {
1019            mCurrentScreen = savedState.currentScreen;
1020            Launcher.setScreen(mCurrentScreen);
1021        }
1022    }
1023
1024    void addApplicationShortcut(ApplicationInfo info, CellLayout.CellInfo cellInfo) {
1025        addApplicationShortcut(info, cellInfo, false);
1026    }
1027
1028    void addApplicationShortcut(ApplicationInfo info, CellLayout.CellInfo cellInfo,
1029            boolean insertAtFirst) {
1030        final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
1031        final int[] result = new int[2];
1032
1033        layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
1034        onDropExternal(result[0], result[1], info, layout, insertAtFirst);
1035    }
1036
1037    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
1038            DragView dragView, Object dragInfo) {
1039        final CellLayout cellLayout = getCurrentDropLayout();
1040        if (source != this) {
1041            onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout);
1042        } else {
1043            // Move internally
1044            if (mDragInfo != null) {
1045                final View cell = mDragInfo.cell;
1046                int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1047                if (index != mDragInfo.screen) {
1048                    final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1049                    originalCellLayout.removeView(cell);
1050                    cellLayout.addView(cell);
1051                }
1052                mTargetCell = estimateDropCell(x - xOffset, y - yOffset,
1053                        mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell);
1054                cellLayout.onDropChild(cell, mTargetCell);
1055
1056                final ItemInfo info = (ItemInfo) cell.getTag();
1057                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
1058                LauncherModel.moveItemInDatabase(mLauncher, info,
1059                        LauncherSettings.Favorites.CONTAINER_DESKTOP, index, lp.cellX, lp.cellY);
1060            }
1061        }
1062    }
1063
1064    public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
1065            DragView dragView, Object dragInfo) {
1066        clearVacantCache();
1067    }
1068
1069    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
1070            DragView dragView, Object dragInfo) {
1071    }
1072
1073    public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
1074            DragView dragView, Object dragInfo) {
1075        clearVacantCache();
1076    }
1077
1078    private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) {
1079        onDropExternal(x, y, dragInfo, cellLayout, false);
1080    }
1081
1082    private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout,
1083            boolean insertAtFirst) {
1084        // Drag from somewhere else
1085        ItemInfo info = (ItemInfo) dragInfo;
1086
1087        View view;
1088
1089        switch (info.itemType) {
1090        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1091        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1092            if (info.container == NO_ID) {
1093                // Came from all apps -- make a copy
1094                info = new ApplicationInfo((ApplicationInfo) info);
1095            }
1096            view = mLauncher.createShortcut(R.layout.application, cellLayout,
1097                    (ApplicationInfo) info);
1098            break;
1099        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1100            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
1101                    (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info));
1102            break;
1103        default:
1104            throw new IllegalStateException("Unknown item type: " + info.itemType);
1105        }
1106
1107        cellLayout.addView(view, insertAtFirst ? 0 : -1);
1108        view.setOnLongClickListener(mLongClickListener);
1109        if (view instanceof DropTarget) {
1110            mDragController.addDropTarget((DropTarget) view);
1111        }
1112
1113        mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
1114        cellLayout.onDropChild(view, mTargetCell);
1115        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1116
1117        LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1118                LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY);
1119    }
1120
1121    /**
1122     * Return the current {@link CellLayout}, correctly picking the destination
1123     * screen while a scroll is in progress.
1124     */
1125    private CellLayout getCurrentDropLayout() {
1126        int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1127        return (CellLayout) getChildAt(index);
1128    }
1129
1130    /**
1131     * {@inheritDoc}
1132     */
1133    public boolean acceptDrop(DragSource source, int x, int y,
1134            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1135        final CellLayout layout = getCurrentDropLayout();
1136        final CellLayout.CellInfo cellInfo = mDragInfo;
1137        final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1138        final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1139
1140        if (mVacantCache == null) {
1141            final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1142            mVacantCache = layout.findAllVacantCells(null, ignoreView);
1143        }
1144
1145        return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false);
1146    }
1147
1148    /**
1149     * {@inheritDoc}
1150     */
1151    public Rect estimateDropLocation(DragSource source, int x, int y,
1152            int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
1153        final CellLayout layout = getCurrentDropLayout();
1154
1155        final CellLayout.CellInfo cellInfo = mDragInfo;
1156        final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1157        final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1158        final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1159
1160        final Rect location = recycle != null ? recycle : new Rect();
1161
1162        // Find drop cell and convert into rectangle
1163        int[] dropCell = estimateDropCell(x - xOffset, y - yOffset,
1164                spanX, spanY, ignoreView, layout, mTempCell);
1165
1166        if (dropCell == null) {
1167            return null;
1168        }
1169
1170        layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
1171        location.left = mTempEstimate[0];
1172        location.top = mTempEstimate[1];
1173
1174        layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
1175        location.right = mTempEstimate[0];
1176        location.bottom = mTempEstimate[1];
1177
1178        return location;
1179    }
1180
1181    /**
1182     * Calculate the nearest cell where the given object would be dropped.
1183     */
1184    private int[] estimateDropCell(int pixelX, int pixelY,
1185            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1186        // Create vacant cell cache if none exists
1187        if (mVacantCache == null) {
1188            mVacantCache = layout.findAllVacantCells(null, ignoreView);
1189        }
1190
1191        // Find the best target drop location
1192        return layout.findNearestVacantArea(pixelX, pixelY,
1193                spanX, spanY, mVacantCache, recycle);
1194    }
1195
1196    void setLauncher(Launcher launcher) {
1197        mLauncher = launcher;
1198    }
1199
1200    public void setDragController(DragController dragController) {
1201        mDragController = dragController;
1202    }
1203
1204    public void onDropCompleted(View target, boolean success) {
1205        clearVacantCache();
1206
1207        if (success){
1208            if (target != this && mDragInfo != null) {
1209                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1210                cellLayout.removeView(mDragInfo.cell);
1211                if (mDragInfo.cell instanceof DropTarget) {
1212                    mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1213                }
1214                //final Object tag = mDragInfo.cell.getTag();
1215            }
1216        } else {
1217            if (mDragInfo != null) {
1218                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1219                cellLayout.onDropAborted(mDragInfo.cell);
1220            }
1221        }
1222
1223        mDragInfo = null;
1224    }
1225
1226    public void scrollLeft() {
1227        clearVacantCache();
1228        if (mNextScreen == INVALID_SCREEN && mCurrentScreen > 0 && mScroller.isFinished()) {
1229            snapToScreen(mCurrentScreen - 1);
1230        }
1231    }
1232
1233    public void scrollRight() {
1234        clearVacantCache();
1235        if (mNextScreen == INVALID_SCREEN && mCurrentScreen < getChildCount() -1 &&
1236                mScroller.isFinished()) {
1237            snapToScreen(mCurrentScreen + 1);
1238        }
1239    }
1240
1241    public int getScreenForView(View v) {
1242        int result = -1;
1243        if (v != null) {
1244            ViewParent vp = v.getParent();
1245            int count = getChildCount();
1246            for (int i = 0; i < count; i++) {
1247                if (vp == getChildAt(i)) {
1248                    return i;
1249                }
1250            }
1251        }
1252        return result;
1253    }
1254
1255    /**
1256     * Find a search widget on the given screen
1257     */
1258    private Search findSearchWidget(CellLayout screen) {
1259        final int count = screen.getChildCount();
1260        for (int i = 0; i < count; i++) {
1261            View v = screen.getChildAt(i);
1262            if (v instanceof Search) {
1263                return (Search) v;
1264            }
1265        }
1266        return null;
1267    }
1268
1269    /**
1270     * Gets the first search widget on the current screen, if there is one.
1271     * Returns <code>null</code> otherwise.
1272     */
1273    public Search findSearchWidgetOnCurrentScreen() {
1274        CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
1275        return findSearchWidget(currentScreen);
1276    }
1277
1278    public Folder getFolderForTag(Object tag) {
1279        int screenCount = getChildCount();
1280        for (int screen = 0; screen < screenCount; screen++) {
1281            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1282            int count = currentScreen.getChildCount();
1283            for (int i = 0; i < count; i++) {
1284                View child = currentScreen.getChildAt(i);
1285                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1286                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1287                    Folder f = (Folder) child;
1288                    if (f.getInfo() == tag) {
1289                        return f;
1290                    }
1291                }
1292            }
1293        }
1294        return null;
1295    }
1296
1297    public View getViewForTag(Object tag) {
1298        int screenCount = getChildCount();
1299        for (int screen = 0; screen < screenCount; screen++) {
1300            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1301            int count = currentScreen.getChildCount();
1302            for (int i = 0; i < count; i++) {
1303                View child = currentScreen.getChildAt(i);
1304                if (child.getTag() == tag) {
1305                    return child;
1306                }
1307            }
1308        }
1309        return null;
1310    }
1311
1312    /**
1313     * @return True is long presses are still allowed for the current touch
1314     */
1315    public boolean allowLongPress() {
1316        return mAllowLongPress;
1317    }
1318
1319    /**
1320     * Set true to allow long-press events to be triggered, usually checked by
1321     * {@link Launcher} to accept or block dpad-initiated long-presses.
1322     */
1323    public void setAllowLongPress(boolean allowLongPress) {
1324        mAllowLongPress = allowLongPress;
1325    }
1326
1327    void removeShortcutsForPackage(String packageName) {
1328        final ArrayList<View> childrenToRemove = new ArrayList<View>();
1329        final int count = getChildCount();
1330
1331        for (int i = 0; i < count; i++) {
1332            final CellLayout layout = (CellLayout) getChildAt(i);
1333            int childCount = layout.getChildCount();
1334
1335            childrenToRemove.clear();
1336
1337            for (int j = 0; j < childCount; j++) {
1338                final View view = layout.getChildAt(j);
1339                Object tag = view.getTag();
1340
1341                if (tag instanceof ApplicationInfo) {
1342                    final ApplicationInfo info = (ApplicationInfo) tag;
1343                    // We need to check for ACTION_MAIN otherwise getComponent() might
1344                    // return null for some shortcuts (for instance, for shortcuts to
1345                    // web pages.)
1346                    final Intent intent = info.intent;
1347                    final ComponentName name = intent.getComponent();
1348
1349                    if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
1350                            name != null && packageName.equals(name.getPackageName())) {
1351                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1352                        childrenToRemove.add(view);
1353                    }
1354                } else if (tag instanceof UserFolderInfo) {
1355                    final UserFolderInfo info = (UserFolderInfo) tag;
1356                    final ArrayList<ApplicationInfo> contents = info.contents;
1357                    final ArrayList<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>(1);
1358                    final int contentsCount = contents.size();
1359                    boolean removedFromFolder = false;
1360
1361                    for (int k = 0; k < contentsCount; k++) {
1362                        final ApplicationInfo appInfo = contents.get(k);
1363                        final Intent intent = appInfo.intent;
1364                        final ComponentName name = intent.getComponent();
1365
1366                        if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
1367                                name != null && packageName.equals(name.getPackageName())) {
1368                            toRemove.add(appInfo);
1369                            LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
1370                            removedFromFolder = true;
1371                        }
1372                    }
1373
1374                    contents.removeAll(toRemove);
1375                    if (removedFromFolder) {
1376                        final Folder folder = getOpenFolder();
1377                        if (folder != null) folder.notifyDataSetChanged();
1378                    }
1379                }
1380            }
1381
1382            childCount = childrenToRemove.size();
1383            for (int j = 0; j < childCount; j++) {
1384                View child = childrenToRemove.get(j);
1385                layout.removeViewInLayout(child);
1386                if (child instanceof DropTarget) {
1387                    mDragController.removeDropTarget((DropTarget)child);
1388                }
1389            }
1390
1391            if (childCount > 0) {
1392                layout.requestLayout();
1393                layout.invalidate();
1394            }
1395        }
1396    }
1397
1398    void updateShortcutsForPackage(String packageName) {
1399        final PackageManager pm = mLauncher.getPackageManager();
1400
1401        final int count = getChildCount();
1402        for (int i = 0; i < count; i++) {
1403            final CellLayout layout = (CellLayout) getChildAt(i);
1404            int childCount = layout.getChildCount();
1405            for (int j = 0; j < childCount; j++) {
1406                final View view = layout.getChildAt(j);
1407                Object tag = view.getTag();
1408                if (tag instanceof ApplicationInfo) {
1409                    ApplicationInfo info = (ApplicationInfo) tag;
1410                    // We need to check for ACTION_MAIN otherwise getComponent() might
1411                    // return null for some shortcuts (for instance, for shortcuts to
1412                    // web pages.)
1413                    final Intent intent = info.intent;
1414                    final ComponentName name = intent.getComponent();
1415                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1416                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null &&
1417                            packageName.equals(name.getPackageName())) {
1418
1419                        final Drawable icon = AppInfoCache.getIconDrawable(pm, info);
1420                        if (icon != null && icon != info.icon) {
1421                            info.icon.setCallback(null);
1422                            info.icon = Utilities.createIconThumbnail(icon, mContext);
1423                            info.filtered = true;
1424                            ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null,
1425                                    info.icon, null, null);
1426                        }
1427                    }
1428                }
1429            }
1430        }
1431    }
1432
1433    void moveToDefaultScreen() {
1434        snapToScreen(mDefaultScreen);
1435        getChildAt(mDefaultScreen).requestFocus();
1436    }
1437
1438    void setIndicators(Drawable previous, Drawable next) {
1439        mPreviousIndicator = previous;
1440        mNextIndicator = next;
1441        previous.setLevel(mCurrentScreen);
1442        next.setLevel(mCurrentScreen);
1443    }
1444
1445    public static class SavedState extends BaseSavedState {
1446        int currentScreen = -1;
1447
1448        SavedState(Parcelable superState) {
1449            super(superState);
1450        }
1451
1452        private SavedState(Parcel in) {
1453            super(in);
1454            currentScreen = in.readInt();
1455        }
1456
1457        @Override
1458        public void writeToParcel(Parcel out, int flags) {
1459            super.writeToParcel(out, flags);
1460            out.writeInt(currentScreen);
1461        }
1462
1463        public static final Parcelable.Creator<SavedState> CREATOR =
1464                new Parcelable.Creator<SavedState>() {
1465            public SavedState createFromParcel(Parcel in) {
1466                return new SavedState(in);
1467            }
1468
1469            public SavedState[] newArray(int size) {
1470                return new SavedState[size];
1471            }
1472        };
1473    }
1474
1475    void show() {
1476        setVisibility(VISIBLE);
1477    }
1478
1479    void hide() {
1480    }
1481}
1482