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