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