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