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