Workspace.java revision 6b4adbc0558fcebbd44998bd00dcd334ddbee32d
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 com.android.launcher.R;
20
21import android.animation.Animatable;
22import android.animation.Animatable.AnimatableListener;
23import android.animation.PropertyAnimator;
24import android.animation.PropertyValuesHolder;
25import android.animation.Sequencer;
26import android.app.WallpaperManager;
27import android.appwidget.AppWidgetManager;
28import android.appwidget.AppWidgetProviderInfo;
29import android.content.ComponentName;
30import android.content.Context;
31import android.content.Intent;
32import android.content.pm.PackageManager;
33import android.content.pm.ProviderInfo;
34import android.content.res.Resources;
35import android.content.res.TypedArray;
36import android.graphics.Canvas;
37import android.graphics.Matrix;
38import android.graphics.Paint;
39import android.graphics.Rect;
40import android.graphics.drawable.Drawable;
41import android.net.Uri;
42import android.os.IBinder;
43import android.os.Parcel;
44import android.os.Parcelable;
45import android.util.AttributeSet;
46import android.util.Log;
47import android.view.MotionEvent;
48import android.view.VelocityTracker;
49import android.view.View;
50import android.view.ViewConfiguration;
51import android.view.ViewGroup;
52import android.view.ViewParent;
53import android.view.animation.Interpolator;
54import android.widget.Scroller;
55import android.widget.TextView;
56import android.widget.Toast;
57
58import java.util.ArrayList;
59import java.util.HashSet;
60
61/**
62 * The workspace is a wide area with a wallpaper and a finite number of screens.
63 * Each screen contains a number of icons, folders or widgets the user can
64 * interact with. A workspace is meant to be used with a fixed width only.
65 */
66public class Workspace extends ViewGroup
67        implements DropTarget, DragSource, DragScroller, View.OnTouchListener {
68    @SuppressWarnings({"UnusedDeclaration"})
69    private static final String TAG = "Launcher.Workspace";
70    private static final int INVALID_SCREEN = -1;
71    // This is how much the workspace shrinks when we enter all apps or
72    // customization mode
73    private static final float SHRINK_FACTOR = 0.16f;
74    private static final int SHRINK_TO_TOP = 0;
75    private static final int SHRINK_TO_MIDDLE = 1;
76    private static final int SHRINK_TO_BOTTOM = 2;
77
78    /**
79     * The velocity at which a fling gesture will cause us to snap to the next
80     * screen
81     */
82    private static final int SNAP_VELOCITY = 600;
83
84    private final WallpaperManager mWallpaperManager;
85
86    private int mDefaultScreen;
87
88    private boolean mFirstLayout = true;
89    private boolean mWaitingToShrinkToBottom = false;
90
91    private int mCurrentScreen;
92    private int mNextScreen = INVALID_SCREEN;
93    private Scroller mScroller;
94    private VelocityTracker mVelocityTracker;
95
96    /**
97     * CellInfo for the cell that is currently being dragged
98     */
99    private CellLayout.CellInfo mDragInfo;
100
101    /**
102     * Target drop area calculated during last acceptDrop call.
103     */
104    private int[] mTargetCell = null;
105
106    /**
107     * The CellLayout that is currently being dragged over
108     */
109    private CellLayout mDragTargetLayout = null;
110
111    private float mLastMotionX;
112    private float mLastMotionY;
113
114    private final static int TOUCH_STATE_REST = 0;
115    private final static int TOUCH_STATE_SCROLLING = 1;
116
117    private int mTouchState = TOUCH_STATE_REST;
118
119    private OnLongClickListener mLongClickListener;
120
121    private Launcher mLauncher;
122    private IconCache mIconCache;
123    private DragController mDragController;
124
125
126    private int[] mTempCell = new int[2];
127    private int[] mTempEstimate = new int[2];
128    private float[] mTempDragCoordinates = new float[2];
129    private float[] mTempDragBottomRightCoordinates = new float[2];
130
131    private boolean mAllowLongPress = true;
132
133    private int mTouchSlop;
134    private int mMaximumVelocity;
135
136    private static final int INVALID_POINTER = -1;
137    private static final int DEFAULT_CELL_COUNT_X = 4;
138    private static final int DEFAULT_CELL_COUNT_Y = 4;
139
140    private int mActivePointerId = INVALID_POINTER;
141
142    private Drawable mPreviousIndicator;
143    private Drawable mNextIndicator;
144
145    private static final float NANOTIME_DIV = 1000000000.0f;
146    private static final float SMOOTHING_SPEED = 0.75f;
147    private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
148    private float mSmoothingTime;
149    private float mTouchX;
150
151    private WorkspaceOvershootInterpolator mScrollInterpolator;
152
153    private static final float BASELINE_FLING_VELOCITY = 2500.f;
154    private static final float FLING_VELOCITY_INFLUENCE = 0.4f;
155
156    private Paint mDropIndicatorPaint;
157
158    // State variable that indicated whether the screens are small (ie when you're
159    // in all apps or customize mode)
160    private boolean mIsSmall;
161    private AnimatableListener mUnshrinkAnimationListener;
162
163    private static class WorkspaceOvershootInterpolator implements Interpolator {
164        private static final float DEFAULT_TENSION = 1.3f;
165        private float mTension;
166
167        public WorkspaceOvershootInterpolator() {
168            mTension = DEFAULT_TENSION;
169        }
170
171        public void setDistance(int distance) {
172            mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
173        }
174
175        public void disableSettle() {
176            mTension = 0.f;
177        }
178
179        public float getInterpolation(float t) {
180            // _o(t) = t * t * ((tension + 1) * t + tension)
181            // o(t) = _o(t - 1) + 1
182            t -= 1.0f;
183            return t * t * ((mTension + 1) * t + mTension) + 1.0f;
184        }
185    }
186
187    /**
188     * Used to inflate the Workspace from XML.
189     *
190     * @param context The application's context.
191     * @param attrs The attribtues set containing the Workspace's customization values.
192     */
193    public Workspace(Context context, AttributeSet attrs) {
194        this(context, attrs, 0);
195    }
196
197    /**
198     * Used to inflate the Workspace from XML.
199     *
200     * @param context The application's context.
201     * @param attrs The attribtues set containing the Workspace's customization values.
202     * @param defStyle Unused.
203     */
204    public Workspace(Context context, AttributeSet attrs, int defStyle) {
205        super(context, attrs, defStyle);
206
207        mWallpaperManager = WallpaperManager.getInstance(context);
208
209        TypedArray a = context.obtainStyledAttributes(attrs,
210                R.styleable.Workspace, defStyle, 0);
211        int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X);
212        int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y);
213        mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
214        a.recycle();
215
216        LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
217        setHapticFeedbackEnabled(false);
218        initWorkspace();
219    }
220
221    /**
222     * Initializes various states for this workspace.
223     */
224    private void initWorkspace() {
225        Context context = getContext();
226        mScrollInterpolator = new WorkspaceOvershootInterpolator();
227        mScroller = new Scroller(context, mScrollInterpolator);
228        mCurrentScreen = mDefaultScreen;
229        Launcher.setScreen(mCurrentScreen);
230        LauncherApplication app = (LauncherApplication)context.getApplicationContext();
231        mIconCache = app.getIconCache();
232
233        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
234        mTouchSlop = configuration.getScaledTouchSlop();
235        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
236        mUnshrinkAnimationListener = new AnimatableListener() {
237            public void onAnimationStart(Animatable animation) {}
238            public void onAnimationEnd(Animatable animation) {
239                mIsSmall = false;
240            }
241            public void onAnimationCancel(Animatable animation) {}
242            public void onAnimationRepeat(Animatable animation) {}
243        };
244    }
245
246    @Override
247    public void addView(View child, int index, LayoutParams params) {
248        if (!(child instanceof CellLayout)) {
249            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
250        }
251        super.addView(child, index, params);
252    }
253
254    @Override
255    public void addView(View child) {
256        if (!(child instanceof CellLayout)) {
257            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
258        }
259        super.addView(child);
260    }
261
262    @Override
263    public void addView(View child, int index) {
264        if (!(child instanceof CellLayout)) {
265            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
266        }
267        super.addView(child, index);
268    }
269
270    @Override
271    public void addView(View child, int width, int height) {
272        if (!(child instanceof CellLayout)) {
273            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
274        }
275        super.addView(child, width, height);
276    }
277
278    @Override
279    public void addView(View child, LayoutParams params) {
280        if (!(child instanceof CellLayout)) {
281            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
282        }
283        super.addView(child, params);
284    }
285
286    /**
287     * @return The open folder on the current screen, or null if there is none
288     */
289    Folder getOpenFolder() {
290        CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
291        int count = currentScreen.getChildCount();
292        for (int i = 0; i < count; i++) {
293            View child = currentScreen.getChildAt(i);
294            if (child instanceof Folder) {
295                Folder folder = (Folder) child;
296                if (folder.getInfo().opened)
297                    return folder;
298            }
299        }
300        return null;
301    }
302
303    ArrayList<Folder> getOpenFolders() {
304        final int screenCount = getChildCount();
305        ArrayList<Folder> folders = new ArrayList<Folder>(screenCount);
306
307        for (int screen = 0; screen < screenCount; screen++) {
308            CellLayout currentScreen = (CellLayout) getChildAt(screen);
309            int count = currentScreen.getChildCount();
310            for (int i = 0; i < count; i++) {
311                View child = currentScreen.getChildAt(i);
312                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child
313                        .getLayoutParams();
314                if (child instanceof Folder) {
315                    Folder folder = (Folder) child;
316                    if (folder.getInfo().opened)
317                        folders.add(folder);
318                    break;
319                }
320            }
321        }
322
323        return folders;
324    }
325
326    boolean isDefaultScreenShowing() {
327        return mCurrentScreen == mDefaultScreen;
328    }
329
330    /**
331     * Returns the index of the currently displayed screen.
332     *
333     * @return The index of the currently displayed screen.
334     */
335    int getCurrentScreen() {
336        return mCurrentScreen;
337    }
338
339    /**
340     * Sets the current screen.
341     *
342     * @param currentScreen
343     */
344    void setCurrentScreen(int currentScreen) {
345        setCurrentScreen(currentScreen, true);
346    }
347
348    void setCurrentScreen(int currentScreen, boolean animateScrolling) {
349        setCurrentScreen(currentScreen, animateScrolling, getWidth());
350    }
351
352    void setCurrentScreen(int currentScreen, boolean animateScrolling, int screenWidth) {
353        if (!mScroller.isFinished())
354            mScroller.abortAnimation();
355        mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
356        if (mPreviousIndicator != null) {
357            mPreviousIndicator.setLevel(mCurrentScreen);
358            mNextIndicator.setLevel(mCurrentScreen);
359        }
360        if (animateScrolling) {
361            scrollTo(mCurrentScreen * screenWidth, 0);
362        } else {
363            mScrollX = mCurrentScreen * screenWidth;
364        }
365        updateWallpaperOffset(screenWidth * (getChildCount() - 1));
366        invalidate();
367    }
368
369    /**
370     * Adds the specified child in the current screen. The position and dimension of
371     * the child are defined by x, y, spanX and spanY.
372     *
373     * @param child The child to add in one of the workspace's screens.
374     * @param x The X position of the child in the screen's grid.
375     * @param y The Y position of the child in the screen's grid.
376     * @param spanX The number of cells spanned horizontally by the child.
377     * @param spanY The number of cells spanned vertically by the child.
378     */
379    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
380        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
381    }
382
383    /**
384     * Adds the specified child in the current screen. The position and dimension of
385     * the child are defined by x, y, spanX and spanY.
386     *
387     * @param child The child to add in one of the workspace's screens.
388     * @param x The X position of the child in the screen's grid.
389     * @param y The Y position of the child in the screen's grid.
390     * @param spanX The number of cells spanned horizontally by the child.
391     * @param spanY The number of cells spanned vertically by the child.
392     * @param insert When true, the child is inserted at the beginning of the children list.
393     */
394    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
395        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
396    }
397
398    /**
399     * Adds the specified child in the specified screen. The position and dimension of
400     * the child are defined by x, y, spanX and spanY.
401     *
402     * @param child The child to add in one of the workspace's screens.
403     * @param screen The screen in which to add the child.
404     * @param x The X position of the child in the screen's grid.
405     * @param y The Y position of the child in the screen's grid.
406     * @param spanX The number of cells spanned horizontally by the child.
407     * @param spanY The number of cells spanned vertically by the child.
408     */
409    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
410        addInScreen(child, screen, x, y, spanX, spanY, false);
411    }
412
413    void addInFullScreen(View child, int screen) {
414        addInScreen(child, screen, 0, 0, -1, -1);
415    }
416
417    /**
418     * Adds the specified child in the specified screen. The position and dimension of
419     * the child are defined by x, y, spanX and spanY.
420     *
421     * @param child The child to add in one of the workspace's screens.
422     * @param screen The screen in which to add the child.
423     * @param x The X position of the child in the screen's grid.
424     * @param y The Y position of the child in the screen's grid.
425     * @param spanX The number of cells spanned horizontally by the child.
426     * @param spanY The number of cells spanned vertically by the child.
427     * @param insert When true, the child is inserted at the beginning of the children list.
428     */
429    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
430        if (screen < 0 || screen >= getChildCount()) {
431            Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
432                + " (was " + screen + "); skipping child");
433            return;
434        }
435
436        final CellLayout group = (CellLayout) getChildAt(screen);
437        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
438        if (lp == null) {
439            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
440        } else {
441            lp.cellX = x;
442            lp.cellY = y;
443            lp.cellHSpan = spanX;
444            lp.cellVSpan = spanY;
445        }
446
447        // Get the canonical child id to uniquely represent this view in this screen
448        int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY);
449        if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) {
450            // TODO: This branch occurs when the workspace is adding views
451            // outside of the defined grid
452            // maybe we should be deleting these items from the LauncherModel?
453            Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
454        }
455
456        if (!(child instanceof Folder)) {
457            child.setHapticFeedbackEnabled(false);
458            child.setOnLongClickListener(mLongClickListener);
459        }
460        if (child instanceof DropTarget) {
461            mDragController.addDropTarget((DropTarget) child);
462        }
463    }
464
465    CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) {
466        CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
467        if (group != null) {
468            return group.updateOccupiedCells(occupied, null);
469        }
470        return null;
471    }
472
473    public boolean onTouch(View v, MotionEvent event) {
474        // this is an intercepted event being forwarded from a cell layout
475        if (mIsSmall) {
476            unshrink((CellLayout)v);
477            mLauncher.onWorkspaceUnshrink();
478            return true;
479        }
480        return false;
481    }
482
483    /**
484     * Registers the specified listener on each screen contained in this workspace.
485     *
486     * @param l The listener used to respond to long clicks.
487     */
488    @Override
489    public void setOnLongClickListener(OnLongClickListener l) {
490        mLongClickListener = l;
491        final int screenCount = getChildCount();
492        for (int i = 0; i < screenCount; i++) {
493            getChildAt(i).setOnLongClickListener(l);
494        }
495    }
496
497    private void updateWallpaperOffset() {
498        updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
499    }
500
501    private void updateWallpaperOffset(int scrollRange) {
502        final boolean isStaticWallpaper = (mWallpaperManager != null) &&
503                (mWallpaperManager.getWallpaperInfo() == null);
504        if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) {
505            IBinder token = getWindowToken();
506            if (token != null) {
507                mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
508                mWallpaperManager.setWallpaperOffsets(getWindowToken(),
509                        Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
510            }
511        }
512    }
513
514    @Override
515    public void scrollTo(int x, int y) {
516        super.scrollTo(x, y);
517        mTouchX = x;
518        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
519    }
520
521    @Override
522    public void computeScroll() {
523        if (mScroller.computeScrollOffset()) {
524            mTouchX = mScrollX = mScroller.getCurrX();
525            mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
526            mScrollY = mScroller.getCurrY();
527            updateWallpaperOffset();
528            postInvalidate();
529        } else if (mNextScreen != INVALID_SCREEN) {
530            mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
531            if (mPreviousIndicator != null) {
532                mPreviousIndicator.setLevel(mCurrentScreen);
533                mNextIndicator.setLevel(mCurrentScreen);
534            }
535            Launcher.setScreen(mCurrentScreen);
536            mNextScreen = INVALID_SCREEN;
537            clearChildrenCache();
538        } else if (mTouchState == TOUCH_STATE_SCROLLING) {
539            final float now = System.nanoTime() / NANOTIME_DIV;
540            final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
541            final float dx = mTouchX - mScrollX;
542            mScrollX += dx * e;
543            mSmoothingTime = now;
544
545            // Keep generating points as long as we're more than 1px away from the target
546            if (dx > 1.f || dx < -1.f) {
547                updateWallpaperOffset();
548                postInvalidate();
549            }
550        }
551    }
552
553    @Override
554    protected void dispatchDraw(Canvas canvas) {
555        boolean restore = false;
556        int restoreCount = 0;
557
558        // ViewGroup.dispatchDraw() supports many features we don't need:
559        // clip to padding, layout animation, animation listener, disappearing
560        // children, etc. The following implementation attempts to fast-track
561        // the drawing dispatch by drawing only what we know needs to be drawn.
562
563        boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
564
565        // if the screens are all small, we need to draw all the screens since
566        // they're most likely all visible
567        if (mIsSmall) {
568            final int screenCount = getChildCount();
569            for (int i = 0; i < screenCount; i++) {
570                CellLayout cl = (CellLayout)getChildAt(i);
571                drawChild(canvas, cl, getDrawingTime());
572            }
573        } else if (fastDraw) {
574            // If we are not scrolling or flinging, draw only the current screen
575            drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
576        } else {
577            final long drawingTime = getDrawingTime();
578            final float scrollPos = (float) mScrollX / getWidth();
579            final int leftScreen = (int) scrollPos;
580            final int rightScreen = leftScreen + 1;
581            if (leftScreen >= 0) {
582                drawChild(canvas, getChildAt(leftScreen), drawingTime);
583            }
584            if (scrollPos != leftScreen && rightScreen < getChildCount()) {
585                drawChild(canvas, getChildAt(rightScreen), drawingTime);
586            }
587        }
588
589        if (restore) {
590            canvas.restoreToCount(restoreCount);
591        }
592    }
593
594    protected void onAttachedToWindow() {
595        super.onAttachedToWindow();
596        computeScroll();
597        mDragController.setWindowToken(getWindowToken());
598    }
599
600    @Override
601    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
602        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
603
604        final int width = MeasureSpec.getSize(widthMeasureSpec);
605        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
606        if (widthMode != MeasureSpec.EXACTLY) {
607            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
608        }
609
610        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
611        if (heightMode != MeasureSpec.EXACTLY) {
612            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
613        }
614
615        // The children are given the same width and height as the workspace
616        final int screenCount = getChildCount();
617        for (int i = 0; i < screenCount; i++) {
618            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
619        }
620
621        if (mFirstLayout) {
622            setHorizontalScrollBarEnabled(false);
623            setCurrentScreen(mCurrentScreen, false, width);
624            setHorizontalScrollBarEnabled(true);
625        }
626    }
627
628    @Override
629    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
630        if (mFirstLayout) {
631            mFirstLayout = false;
632        }
633        int childLeft = 0;
634        final int screenCount = getChildCount();
635        for (int i = 0; i < screenCount; i++) {
636            final View child = getChildAt(i);
637            if (child.getVisibility() != View.GONE) {
638                final int childWidth = child.getMeasuredWidth();
639                child.layout(childLeft, 0,
640                        childLeft + childWidth, child.getMeasuredHeight());
641                childLeft += childWidth;
642            }
643        }
644
645        // if shrinkToBottom() is called on initialization, it has to be deferred
646        // until after the first call to onLayout so that it has the correct width
647        if (mWaitingToShrinkToBottom) {
648            shrinkToBottom(false);
649            mWaitingToShrinkToBottom = false;
650        }
651
652        if (LauncherApplication.isInPlaceRotationEnabled()) {
653            // When the device is rotated, the scroll position of the current screen
654            // needs to be refreshed
655            setCurrentScreen(getCurrentScreen());
656        }
657    }
658
659    @Override
660    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
661        int screen = indexOfChild(child);
662        if (screen != mCurrentScreen || !mScroller.isFinished()) {
663            snapToScreen(screen);
664            return true;
665        }
666        return false;
667    }
668
669    @Override
670    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
671        if (!mLauncher.isAllAppsVisible()) {
672            final Folder openFolder = getOpenFolder();
673            if (openFolder != null) {
674                return openFolder.requestFocus(direction, previouslyFocusedRect);
675            } else {
676                int focusableScreen;
677                if (mNextScreen != INVALID_SCREEN) {
678                    focusableScreen = mNextScreen;
679                } else {
680                    focusableScreen = mCurrentScreen;
681                }
682                getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
683            }
684        }
685        return false;
686    }
687
688    @Override
689    public boolean dispatchUnhandledMove(View focused, int direction) {
690        if (direction == View.FOCUS_LEFT) {
691            if (getCurrentScreen() > 0) {
692                snapToScreen(getCurrentScreen() - 1);
693                return true;
694            }
695        } else if (direction == View.FOCUS_RIGHT) {
696            if (getCurrentScreen() < getChildCount() - 1) {
697                snapToScreen(getCurrentScreen() + 1);
698                return true;
699            }
700        }
701        return super.dispatchUnhandledMove(focused, direction);
702    }
703
704    @Override
705    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
706        if (!mLauncher.isAllAppsVisible()) {
707            final Folder openFolder = getOpenFolder();
708            if (openFolder == null) {
709                getChildAt(mCurrentScreen).addFocusables(views, direction);
710                if (direction == View.FOCUS_LEFT) {
711                    if (mCurrentScreen > 0) {
712                        getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
713                    }
714                } else if (direction == View.FOCUS_RIGHT) {
715                    if (mCurrentScreen < getChildCount() - 1) {
716                        getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
717                    }
718                }
719            } else {
720                openFolder.addFocusables(views, direction);
721            }
722        }
723    }
724
725    @Override
726    public boolean dispatchTouchEvent(MotionEvent ev) {
727        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
728            // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
729            // ie when you click on a mini-screen, it zooms back to that screen)
730            if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) {
731                return false;
732            }
733        }
734        return super.dispatchTouchEvent(ev);
735    }
736
737    /**
738     * {@inheritDoc}
739     */
740    @Override
741    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
742        if (disallowIntercept) {
743            // We need to make sure to cancel our long press if
744            // a scrollable widget takes over touch events
745            final View currentScreen = getChildAt(mCurrentScreen);
746            currentScreen.cancelLongPress();
747        }
748        super.requestDisallowInterceptTouchEvent(disallowIntercept);
749    }
750
751    @Override
752    public boolean onInterceptTouchEvent(MotionEvent ev) {
753        final boolean allAppsVisible = mLauncher.isAllAppsVisible();
754
755        // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
756        // ie when you click on a mini-screen, it zooms back to that screen)
757        if (!LauncherApplication.isScreenXLarge() && allAppsVisible) {
758            return false; // We don't want the events.  Let them fall through to the all apps view.
759        }
760
761        /*
762         * This method JUST determines whether we want to intercept the motion.
763         * If we return true, onTouchEvent will be called and we do the actual
764         * scrolling there.
765         */
766
767        /*
768         * Shortcut the most recurring case: the user is in the dragging
769         * state and he is moving his finger.  We want to intercept this
770         * motion.
771         */
772        final int action = ev.getAction();
773        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
774            return true;
775        }
776
777        if (mVelocityTracker == null) {
778            mVelocityTracker = VelocityTracker.obtain();
779        }
780        mVelocityTracker.addMovement(ev);
781
782        switch (action & MotionEvent.ACTION_MASK) {
783            case MotionEvent.ACTION_MOVE: {
784                /*
785                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
786                 * whether the user has moved far enough from his original down touch.
787                 */
788
789                /*
790                 * Locally do absolute value. mLastMotionX is set to the y value
791                 * of the down event.
792                 */
793                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
794                final float x = ev.getX(pointerIndex);
795                final float y = ev.getY(pointerIndex);
796                final int xDiff = (int) Math.abs(x - mLastMotionX);
797                final int yDiff = (int) Math.abs(y - mLastMotionY);
798
799                final int touchSlop = mTouchSlop;
800                boolean xMoved = xDiff > touchSlop;
801                boolean yMoved = yDiff > touchSlop;
802
803                if (xMoved || yMoved) {
804
805                    if (xMoved) {
806                        // Scroll if the user moved far enough along the X axis
807                        mTouchState = TOUCH_STATE_SCROLLING;
808                        mLastMotionX = x;
809                        mTouchX = mScrollX;
810                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
811                        enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
812                    }
813                    // Either way, cancel any pending longpress
814                    if (mAllowLongPress) {
815                        mAllowLongPress = false;
816                        // Try canceling the long press. It could also have been scheduled
817                        // by a distant descendant, so use the mAllowLongPress flag to block
818                        // everything
819                        final View currentScreen = getChildAt(mCurrentScreen);
820                        currentScreen.cancelLongPress();
821                    }
822                }
823                break;
824            }
825
826        case MotionEvent.ACTION_DOWN: {
827            final float x = ev.getX();
828            final float y = ev.getY();
829            // Remember location of down touch
830            mLastMotionX = x;
831            mLastMotionY = y;
832            mActivePointerId = ev.getPointerId(0);
833            mAllowLongPress = true;
834
835                /*
836                 * If being flinged and user touches the screen, initiate drag;
837                 * otherwise don't.  mScroller.isFinished should be false when
838                 * being flinged.
839                 */
840                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
841                break;
842            }
843
844            case MotionEvent.ACTION_CANCEL:
845            case MotionEvent.ACTION_UP:
846
847                if (mTouchState != TOUCH_STATE_SCROLLING) {
848                    final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
849                    if (!currentScreen.lastDownOnOccupiedCell()) {
850                        getLocationOnScreen(mTempCell);
851                        // Send a tap to the wallpaper if the last down was on empty space
852                        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
853                        mWallpaperManager.sendWallpaperCommand(getWindowToken(),
854                                "android.wallpaper.tap",
855                                mTempCell[0] + (int) ev.getX(pointerIndex),
856                                mTempCell[1] + (int) ev.getY(pointerIndex), 0, null);
857                    }
858                }
859
860                // Release the drag
861                clearChildrenCache();
862                mTouchState = TOUCH_STATE_REST;
863                mActivePointerId = INVALID_POINTER;
864                mAllowLongPress = false;
865
866                if (mVelocityTracker != null) {
867                    mVelocityTracker.recycle();
868                    mVelocityTracker = null;
869                }
870
871            break;
872
873        case MotionEvent.ACTION_POINTER_UP:
874            onSecondaryPointerUp(ev);
875            break;
876        }
877
878        /*
879         * The only time we want to intercept motion events is if we are in the
880         * drag mode.
881         */
882        return mTouchState != TOUCH_STATE_REST;
883    }
884
885    private void onSecondaryPointerUp(MotionEvent ev) {
886        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
887                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
888        final int pointerId = ev.getPointerId(pointerIndex);
889        if (pointerId == mActivePointerId) {
890            // This was our active pointer going up. Choose a new
891            // active pointer and adjust accordingly.
892            // TODO: Make this decision more intelligent.
893            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
894            mLastMotionX = ev.getX(newPointerIndex);
895            mLastMotionY = ev.getY(newPointerIndex);
896            mActivePointerId = ev.getPointerId(newPointerIndex);
897            if (mVelocityTracker != null) {
898                mVelocityTracker.clear();
899            }
900        }
901    }
902
903    /**
904     * If one of our descendant views decides that it could be focused now, only
905     * pass that along if it's on the current screen.
906     *
907     * This happens when live folders requery, and if they're off screen, they
908     * end up calling requestFocus, which pulls it on screen.
909     */
910    @Override
911    public void focusableViewAvailable(View focused) {
912        View current = getChildAt(mCurrentScreen);
913        View v = focused;
914        while (true) {
915            if (v == current) {
916                super.focusableViewAvailable(focused);
917                return;
918            }
919            if (v == this) {
920                return;
921            }
922            ViewParent parent = v.getParent();
923            if (parent instanceof View) {
924                v = (View) v.getParent();
925            } else {
926                return;
927            }
928        }
929    }
930
931    void enableChildrenCache(int fromScreen, int toScreen) {
932        if (fromScreen > toScreen) {
933            final int temp = fromScreen;
934            fromScreen = toScreen;
935            toScreen = temp;
936        }
937
938        final int screenCount = getChildCount();
939
940        fromScreen = Math.max(fromScreen, 0);
941        toScreen = Math.min(toScreen, screenCount - 1);
942
943        for (int i = fromScreen; i <= toScreen; i++) {
944            final CellLayout layout = (CellLayout) getChildAt(i);
945            layout.setChildrenDrawnWithCacheEnabled(true);
946            layout.setChildrenDrawingCacheEnabled(true);
947        }
948    }
949
950    void clearChildrenCache() {
951        final int screenCount = getChildCount();
952        for (int i = 0; i < screenCount; i++) {
953            final CellLayout layout = (CellLayout) getChildAt(i);
954            layout.setChildrenDrawnWithCacheEnabled(false);
955        }
956    }
957
958    @Override
959    public boolean onTouchEvent(MotionEvent ev) {
960        if (mLauncher.isAllAppsVisible()) {
961            // Cancel any scrolling that is in progress.
962            if (!mScroller.isFinished()) {
963                mScroller.abortAnimation();
964            }
965            snapToScreen(mCurrentScreen);
966            return false; // We don't want the events.  Let them fall through to the all apps view.
967        }
968
969        if (mVelocityTracker == null) {
970            mVelocityTracker = VelocityTracker.obtain();
971        }
972        mVelocityTracker.addMovement(ev);
973
974        final int action = ev.getAction();
975
976        switch (action & MotionEvent.ACTION_MASK) {
977        case MotionEvent.ACTION_DOWN:
978            /*
979             * If being flinged and user touches, stop the fling. isFinished
980             * will be false if being flinged.
981             */
982            if (!mScroller.isFinished()) {
983                mScroller.abortAnimation();
984            }
985
986            // Remember where the motion event started
987            mLastMotionX = ev.getX();
988            mActivePointerId = ev.getPointerId(0);
989            if (mTouchState == TOUCH_STATE_SCROLLING) {
990                enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
991            }
992            break;
993        case MotionEvent.ACTION_MOVE:
994            if (mTouchState == TOUCH_STATE_SCROLLING) {
995                // Scroll to follow the motion event
996                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
997                final float x = ev.getX(pointerIndex);
998                final float deltaX = mLastMotionX - x;
999                mLastMotionX = x;
1000
1001                if (deltaX < 0) {
1002                    if (mTouchX > 0) {
1003                        mTouchX += Math.max(-mTouchX, deltaX);
1004                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
1005                        invalidate();
1006                    }
1007                } else if (deltaX > 0) {
1008                    final float availableToScroll = getChildAt(getChildCount() - 1).getRight() -
1009                            mTouchX - getWidth();
1010                    if (availableToScroll > 0) {
1011                        mTouchX += Math.min(availableToScroll, deltaX);
1012                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
1013                        invalidate();
1014                    }
1015                } else {
1016                    awakenScrollBars();
1017                }
1018            }
1019            break;
1020        case MotionEvent.ACTION_UP:
1021            if (mTouchState == TOUCH_STATE_SCROLLING) {
1022                final VelocityTracker velocityTracker = mVelocityTracker;
1023                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1024                final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
1025
1026                final int screenWidth = getWidth();
1027                final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
1028                final float scrolledPos = (float) mScrollX / screenWidth;
1029
1030                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
1031                    // Fling hard enough to move left.
1032                    // Don't fling across more than one screen at a time.
1033                    final int bound = scrolledPos < whichScreen ?
1034                            mCurrentScreen - 1 : mCurrentScreen;
1035                    snapToScreen(Math.min(whichScreen, bound), velocityX, true);
1036                } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
1037                    // Fling hard enough to move right
1038                    // Don't fling across more than one screen at a time.
1039                    final int bound = scrolledPos > whichScreen ?
1040                            mCurrentScreen + 1 : mCurrentScreen;
1041                    snapToScreen(Math.max(whichScreen, bound), velocityX, true);
1042                } else {
1043                    snapToScreen(whichScreen, 0, true);
1044                }
1045
1046                if (mVelocityTracker != null) {
1047                    mVelocityTracker.recycle();
1048                    mVelocityTracker = null;
1049                }
1050            }
1051            mTouchState = TOUCH_STATE_REST;
1052            mActivePointerId = INVALID_POINTER;
1053            break;
1054        case MotionEvent.ACTION_CANCEL:
1055            mTouchState = TOUCH_STATE_REST;
1056            mActivePointerId = INVALID_POINTER;
1057            break;
1058        case MotionEvent.ACTION_POINTER_UP:
1059            onSecondaryPointerUp(ev);
1060            break;
1061        }
1062
1063        return true;
1064    }
1065
1066    public boolean isSmall() {
1067        return mIsSmall;
1068    }
1069
1070    void shrinkToTop(boolean animated) {
1071        shrink(SHRINK_TO_TOP, animated);
1072    }
1073
1074    void shrinkToMiddle() {
1075        shrink(SHRINK_TO_MIDDLE, true);
1076    }
1077
1078    void shrinkToBottom() {
1079        shrinkToBottom(true);
1080    }
1081
1082    void shrinkToBottom(boolean animated) {
1083        if (mFirstLayout) {
1084            // (mFirstLayout == "first layout has not happened yet")
1085            // if we get a call to shrink() as part of our initialization (for example, if
1086            // Launcher is started in All Apps mode) then we need to wait for a layout call
1087            // to get our width so we can layout the mini-screen views correctly
1088            mWaitingToShrinkToBottom = true;
1089        } else {
1090            shrink(SHRINK_TO_BOTTOM, animated);
1091        }
1092    }
1093
1094    // we use this to shrink the workspace for the all apps view and the customize view
1095    private void shrink(int shrinkPosition, boolean animated) {
1096        mIsSmall = true;
1097        final Resources res = getResources();
1098        final int screenWidth = getWidth();
1099        final int screenHeight = getHeight();
1100        final int scaledScreenWidth = (int) (SHRINK_FACTOR * screenWidth);
1101        final int scaledScreenHeight = (int) (SHRINK_FACTOR * screenHeight);
1102        final float scaledSpacing = res.getDimension(R.dimen.smallScreenSpacing);
1103
1104        final int screenCount = getChildCount();
1105        float totalWidth = screenCount * scaledScreenWidth + (screenCount - 1) * scaledSpacing;
1106
1107        float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
1108        if (shrinkPosition == SHRINK_TO_BOTTOM) {
1109            newY = screenHeight - newY - scaledScreenHeight;
1110        } else if (shrinkPosition == SHRINK_TO_MIDDLE) {
1111            newY = screenHeight / 2 - scaledScreenHeight / 2;
1112        }
1113
1114        // We animate all the screens to the centered position in workspace
1115        // At the same time, the screens become greyed/dimmed
1116
1117        // newX is initialized to the left-most position of the centered screens
1118        float newX = (mCurrentScreen + 1) * screenWidth - screenWidth / 2 - totalWidth / 2;
1119        for (int i = 0; i < screenCount; i++) {
1120            CellLayout cl = (CellLayout) getChildAt(i);
1121            cl.setPivotX(0.0f);
1122            cl.setPivotY(0.0f);
1123            if (animated) {
1124                final int duration = res.getInteger(R.integer.config_workspaceShrinkTime);
1125                new PropertyAnimator(duration, cl,
1126                        new PropertyValuesHolder("x", newX),
1127                        new PropertyValuesHolder("y", newY),
1128                        new PropertyValuesHolder("scaleX", SHRINK_FACTOR),
1129                        new PropertyValuesHolder("scaleY", SHRINK_FACTOR),
1130                        new PropertyValuesHolder("dimmedBitmapAlpha", 1.0f)).start();
1131            } else {
1132                cl.setX((int)newX);
1133                cl.setY((int)newY);
1134                cl.setScaleX(SHRINK_FACTOR);
1135                cl.setScaleY(SHRINK_FACTOR);
1136                cl.setDimmedBitmapAlpha(1.0f);
1137            }
1138            // increment newX for the next screen
1139            newX += scaledScreenWidth + scaledSpacing;
1140            cl.setOnInterceptTouchListener(this);
1141        }
1142        setChildrenDrawnWithCacheEnabled(true);
1143    }
1144
1145    // We call this when we trigger an unshrink by clicking on the CellLayout cl
1146    private void unshrink(CellLayout clThatWasClicked) {
1147        int newCurrentScreen = mCurrentScreen;
1148        final int screenCount = getChildCount();
1149        for (int i = 0; i < screenCount; i++) {
1150            if (getChildAt(i) == clThatWasClicked) {
1151                newCurrentScreen = i;
1152            }
1153        }
1154        unshrink(newCurrentScreen);
1155    }
1156
1157    private void unshrink(int newCurrentScreen) {
1158        if (mIsSmall) {
1159            int delta = (newCurrentScreen - mCurrentScreen)*getWidth();
1160
1161            final int screenCount = getChildCount();
1162            for (int i = 0; i < screenCount; i++) {
1163                CellLayout cl = (CellLayout) getChildAt(i);
1164                cl.setX(cl.getX() + delta);
1165            }
1166            mScrollX = newCurrentScreen * getWidth();
1167
1168            unshrink();
1169            setCurrentScreen(newCurrentScreen);
1170        }
1171    }
1172
1173    void unshrink() {
1174        unshrink(true);
1175    }
1176
1177    void unshrink(boolean animated) {
1178        if (mIsSmall) {
1179            Sequencer s = new Sequencer();
1180            final int screenCount = getChildCount();
1181
1182            final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
1183            for (int i = 0; i < screenCount; i++) {
1184                final CellLayout cl = (CellLayout)getChildAt(i);
1185                cl.setPivotX(0.0f);
1186                cl.setPivotY(0.0f);
1187                if (animated) {
1188                    s.playTogether(
1189                            new PropertyAnimator(duration, cl, "translationX", 0.0f),
1190                            new PropertyAnimator(duration, cl, "translationY", 0.0f),
1191                            new PropertyAnimator(duration, cl, "scaleX", 1.0f),
1192                            new PropertyAnimator(duration, cl, "scaleY", 1.0f),
1193                            new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 0.0f));
1194                } else {
1195                    cl.setTranslationX(0.0f);
1196                    cl.setTranslationY(0.0f);
1197                    cl.setScaleX(1.0f);
1198                    cl.setScaleY(1.0f);
1199                    cl.setDimmedBitmapAlpha(0.0f);
1200                }
1201            }
1202            s.addListener(mUnshrinkAnimationListener);
1203            s.start();
1204        }
1205    }
1206
1207    void snapToScreen(int whichScreen) {
1208        snapToScreen(whichScreen, 0, false);
1209    }
1210
1211    private void snapToScreen(int whichScreen, int velocity, boolean settle) {
1212        // if (!mScroller.isFinished()) return;
1213
1214        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
1215
1216        enableChildrenCache(mCurrentScreen, whichScreen);
1217
1218        mNextScreen = whichScreen;
1219
1220        if (mPreviousIndicator != null) {
1221            mPreviousIndicator.setLevel(mNextScreen);
1222            mNextIndicator.setLevel(mNextScreen);
1223        }
1224
1225        View focusedChild = getFocusedChild();
1226        if (focusedChild != null && whichScreen != mCurrentScreen &&
1227                focusedChild == getChildAt(mCurrentScreen)) {
1228            focusedChild.clearFocus();
1229        }
1230
1231        final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));
1232        final int newX = whichScreen * getWidth();
1233        final int delta = newX - mScrollX;
1234        int duration = (screenDelta + 1) * 100;
1235
1236        if (!mScroller.isFinished()) {
1237            mScroller.abortAnimation();
1238        }
1239
1240        if (settle) {
1241            mScrollInterpolator.setDistance(screenDelta);
1242        } else {
1243            mScrollInterpolator.disableSettle();
1244        }
1245
1246        velocity = Math.abs(velocity);
1247        if (velocity > 0) {
1248            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
1249                    * FLING_VELOCITY_INFLUENCE;
1250        } else {
1251            duration += 100;
1252        }
1253
1254        awakenScrollBars(duration);
1255        mScroller.startScroll(mScrollX, 0, delta, 0, duration);
1256        invalidate();
1257    }
1258
1259    void startDrag(CellLayout.CellInfo cellInfo) {
1260        View child = cellInfo.cell;
1261
1262        // Make sure the drag was started by a long press as opposed to a long click.
1263        if (!child.isInTouchMode()) {
1264            return;
1265        }
1266
1267        mDragInfo = cellInfo;
1268        mDragInfo.screen = mCurrentScreen;
1269
1270        CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
1271
1272        current.onDragChild(child);
1273        mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
1274        invalidate();
1275    }
1276
1277    @Override
1278    protected Parcelable onSaveInstanceState() {
1279        final SavedState state = new SavedState(super.onSaveInstanceState());
1280        state.currentScreen = mCurrentScreen;
1281        return state;
1282    }
1283
1284    @Override
1285    protected void onRestoreInstanceState(Parcelable state) {
1286        SavedState savedState = (SavedState) state;
1287        super.onRestoreInstanceState(savedState.getSuperState());
1288        if (savedState.currentScreen != -1) {
1289            setCurrentScreen(savedState.currentScreen, false);
1290            Launcher.setScreen(mCurrentScreen);
1291        }
1292    }
1293
1294    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
1295        addApplicationShortcut(info, cellInfo, false);
1296    }
1297
1298    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo,
1299            boolean insertAtFirst) {
1300        final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
1301        final int[] result = new int[2];
1302
1303        layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
1304        onDropExternal(result[0], result[1], info, layout, insertAtFirst);
1305    }
1306
1307    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
1308            DragView dragView, Object dragInfo) {
1309        CellLayout cellLayout = getCurrentDropLayout();
1310        int originX = x - xOffset;
1311        int originY = y - yOffset;
1312        if (mIsSmall) {
1313            // find out which target layout is over
1314            final float[] localXY = mTempDragCoordinates;
1315            localXY[0] = originX;
1316            localXY[1] = originY;
1317            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
1318            // we need to subtract left/top here because DragController already adds
1319            // dragRegionLeft/Top to xOffset and yOffset
1320            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
1321            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
1322            cellLayout =  findMatchingScreenForDragOver(localXY, localBottomRightXY);
1323            if (cellLayout == null) {
1324                // cancel the drag if we're not over a mini-screen at time of drop
1325                // TODO: maybe add a nice fade here?
1326                return;
1327            }
1328            // localXY will be transformed into the local screen's coordinate space; save that info
1329            originX = (int)localXY[0];
1330            originY = (int)localXY[1];
1331        }
1332        if (source != this) {
1333            onDropExternal(originX, originY, dragInfo, cellLayout);
1334        } else {
1335            // Move internally
1336            if (mDragInfo != null) {
1337                final View cell = mDragInfo.cell;
1338                int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1339                if (index != mDragInfo.screen) {
1340                    final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1341                    originalCellLayout.removeView(cell);
1342                    addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY,
1343                            mDragInfo.spanX, mDragInfo.spanY);
1344                }
1345
1346                mTargetCell = estimateDropCell(originX, originY,
1347                        mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout,
1348                        mTargetCell);
1349                cellLayout.onDropChild(cell);
1350
1351                // update the item's position after drop
1352                final ItemInfo info = (ItemInfo) cell.getTag();
1353                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell
1354                        .getLayoutParams();
1355                lp.cellX = mTargetCell[0];
1356                lp.cellY = mTargetCell[1];
1357
1358                LauncherModel.moveItemInDatabase(mLauncher, info,
1359                        LauncherSettings.Favorites.CONTAINER_DESKTOP, index,
1360                        lp.cellX, lp.cellY);
1361            }
1362        }
1363    }
1364
1365    public void onDragEnter(DragSource source, int x, int y, int xOffset,
1366            int yOffset, DragView dragView, Object dragInfo) {
1367    }
1368
1369    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
1370            DragView dragView, Object dragInfo) {
1371
1372        // We may need to delegate the drag to a child view. If a 1x1 item
1373        // would land in a cell occupied by a DragTarget (e.g. a Folder),
1374        // then drag events should be handled by that child.
1375
1376        ItemInfo item = (ItemInfo)dragInfo;
1377        CellLayout currentLayout = getCurrentDropLayout();
1378
1379        int dragPointX, dragPointY;
1380        if (item.spanX == 1 && item.spanY == 1) {
1381            // For a 1x1, calculate the drop cell exactly as in onDragOver
1382            dragPointX = x - xOffset;
1383            dragPointY = y - yOffset;
1384        } else {
1385            // Otherwise, use the exact drag coordinates
1386            dragPointX = x;
1387            dragPointY = y;
1388        }
1389
1390        // If we are dragging over a cell that contains a DropTarget that will
1391        // accept the drop, delegate to that DropTarget.
1392        final int[] cellXY = mTempCell;
1393        currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
1394        View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
1395        if (child instanceof DropTarget) {
1396            DropTarget target = (DropTarget)child;
1397            if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
1398                return target;
1399            }
1400        }
1401        return null;
1402    }
1403
1404    // xy = upper left corner of item being dragged
1405    // bottomRightXy = lower right corner of item being dragged
1406    // This method will see which mini-screen is most overlapped by the item being dragged, and
1407    // return it. It will also transform the parameters xy and bottomRightXy into the local
1408    // coordinate space of the returned screen
1409    private CellLayout findMatchingScreenForDragOver(float[] xy, float[] bottomRightXy) {
1410        float x = xy[0];
1411        float y = xy[1];
1412        float right = bottomRightXy[0];
1413        float bottom = bottomRightXy[1];
1414
1415        float bestX = 0;
1416        float bestY = 0;
1417        float bestRight = 0;
1418        float bestBottom = 0;
1419
1420        Matrix inverseMatrix = new Matrix();
1421
1422        // We loop through all the screens (ie CellLayouts) and see which one overlaps the most
1423        // with the item being dragged.
1424        final int screenCount = getChildCount();
1425        CellLayout bestMatchingScreen = null;
1426        float bestOverlapSoFar = 0;
1427        for (int i = 0; i < screenCount; i++) {
1428            CellLayout cl = (CellLayout)getChildAt(i);
1429            // Transform the coordinates of the item being dragged to the CellLayout's coordinates
1430            float left = cl.getLeft();
1431            float top = cl.getTop();
1432            xy[0] = x + mScrollX - left;
1433            xy[1] = y + mScrollY - top;
1434            cl.getMatrix().invert(inverseMatrix);
1435
1436            bottomRightXy[0] = right + mScrollX - left;
1437            bottomRightXy[1] = bottom + mScrollY - top;
1438
1439            inverseMatrix.mapPoints(xy);
1440            inverseMatrix.mapPoints(bottomRightXy);
1441
1442            float dragRegionX = xy[0];
1443            float dragRegionY = xy[1];
1444            float dragRegionRight = bottomRightXy[0];
1445            float dragRegionBottom = bottomRightXy[1];
1446
1447            // Find the overlapping region
1448            float overlapLeft = Math.max(0f, dragRegionX);
1449            float overlapTop = Math.max(0f, dragRegionY);
1450            float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom);
1451            float overlapRight = Math.min(cl.getWidth(), dragRegionRight);
1452
1453            if (overlapRight >= 0 && overlapLeft <= cl.getWidth() &&
1454                    overlapTop >= 0 && overlapBottom <= cl.getHeight()) {
1455                // Calculate the size of the overlapping region
1456                float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
1457                if (overlap > bestOverlapSoFar) {
1458                    bestOverlapSoFar = overlap;
1459                    bestMatchingScreen = cl;
1460                    bestX = xy[0];
1461                    bestY = xy[1];
1462                    bestRight = bottomRightXy[0];
1463                    bestBottom = bottomRightXy[1];
1464                }
1465             }
1466        }
1467        if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) {
1468            if (mDragTargetLayout != null) {
1469                mDragTargetLayout.onDragComplete();
1470            }
1471            mDragTargetLayout = bestMatchingScreen;
1472        }
1473        xy[0] = bestX;
1474        xy[1] = bestY;
1475        bottomRightXy[0] = bestRight;
1476        bottomRightXy[1] = bestBottom;
1477        return bestMatchingScreen;
1478    }
1479
1480    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
1481            DragView dragView, Object dragInfo) {
1482
1483        final ItemInfo item = (ItemInfo)dragInfo;
1484        CellLayout currentLayout = getCurrentDropLayout();
1485
1486        if (dragInfo instanceof LauncherAppWidgetInfo) {
1487            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
1488
1489            if (widgetInfo.spanX == -1) {
1490                // Calculate the grid spans needed to fit this widget
1491                int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null);
1492                item.spanX = spans[0];
1493                item.spanY = spans[1];
1494            }
1495        }
1496        int originX = x - xOffset;
1497        int originY = y - yOffset;
1498        if (mIsSmall) {
1499            // find out which mini screen the dragged item is over
1500            final float[] localXY = mTempDragCoordinates;
1501            localXY[0] = originX;
1502            localXY[1] = originY;
1503            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
1504
1505            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
1506            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
1507            currentLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY);
1508            if (currentLayout != null) {
1509                currentLayout.setHover(true);
1510            }
1511
1512            originX = (int)localXY[0];
1513            originY = (int)localXY[1];
1514        }
1515
1516        if (source != this) {
1517            // This is a hack to fix the point used to determine which cell an icon from the all
1518            // apps screen is over
1519            if (item != null && item.spanX == 1 && currentLayout != null) {
1520                int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2;
1521
1522                originX += dragRegionLeft - dragView.getDragRegionLeft();
1523                if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) {
1524                    dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(),
1525                            currentLayout.getCellWidth(), dragView.getDragRegionHeight());
1526                }
1527            }
1528        }
1529        if (currentLayout != mDragTargetLayout) {
1530            if (mDragTargetLayout != null) {
1531                mDragTargetLayout.onDragComplete();
1532            }
1533            mDragTargetLayout = currentLayout;
1534        }
1535
1536        // only visualize the drop locations for moving icons within the home screen on tablet
1537        // on phone, we also visualize icons dragged in from All Apps
1538        if ((!LauncherApplication.isScreenXLarge() || source == this)
1539                && mDragTargetLayout != null) {
1540            final View child = (mDragInfo == null) ? null : mDragInfo.cell;
1541            mDragTargetLayout.visualizeDropLocation(
1542                    child, originX, originY, item.spanX, item.spanY);
1543        }
1544    }
1545
1546    public void onDragExit(DragSource source, int x, int y, int xOffset,
1547            int yOffset, DragView dragView, Object dragInfo) {
1548        if (mDragTargetLayout != null) {
1549            mDragTargetLayout.onDragComplete();
1550            mDragTargetLayout = null;
1551        }
1552    }
1553
1554    private void onDropExternal(int x, int y, Object dragInfo,
1555            CellLayout cellLayout) {
1556        onDropExternal(x, y, dragInfo, cellLayout, false);
1557    }
1558
1559    private void onDropExternal(int x, int y, Object dragInfo,
1560            CellLayout cellLayout, boolean insertAtFirst) {
1561        // Drag from somewhere else
1562        ItemInfo info = (ItemInfo) dragInfo;
1563
1564        View view = null;
1565
1566        switch (info.itemType) {
1567        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1568        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1569            if (info.container == NO_ID && info instanceof ApplicationInfo) {
1570                // Came from all apps -- make a copy
1571                info = new ShortcutInfo((ApplicationInfo) info);
1572            }
1573            view = mLauncher.createShortcut(R.layout.application, cellLayout,
1574                    (ShortcutInfo) info);
1575            break;
1576        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1577            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
1578                    (ViewGroup) getChildAt(mCurrentScreen),
1579                    ((UserFolderInfo) info));
1580            break;
1581        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1582            cellLayout.setTagToCellInfoForPoint(x, y);
1583            int[] position = new int[2];
1584            position[0] = x;
1585            position[1] = y;
1586            mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName,
1587                    cellLayout.getTag(), position);
1588            break;
1589        default:
1590            throw new IllegalStateException("Unknown item type: "
1591                    + info.itemType);
1592        }
1593
1594        // If the view is null, it has already been added.
1595        if (view == null) {
1596            cellLayout.onDragComplete();
1597        } else {
1598            mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
1599            addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
1600                    mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
1601            cellLayout.onDropChild(view);
1602            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1603
1604            LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1605                    LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen,
1606                    lp.cellX, lp.cellY);
1607        }
1608    }
1609
1610    /**
1611     * Return the current {@link CellLayout}, correctly picking the destination
1612     * screen while a scroll is in progress.
1613     */
1614    private CellLayout getCurrentDropLayout() {
1615        int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1616        return (CellLayout) getChildAt(index);
1617    }
1618
1619    /**
1620     * {@inheritDoc}
1621     */
1622    public boolean acceptDrop(DragSource source, int x, int y,
1623            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1624        final CellLayout layout = getCurrentDropLayout();
1625        final CellLayout.CellInfo dragCellInfo = mDragInfo;
1626        final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
1627        final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
1628
1629        final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
1630        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
1631
1632        if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) {
1633            return true;
1634        } else {
1635            Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
1636            return false;
1637        }
1638    }
1639
1640    /**
1641     * {@inheritDoc}
1642     */
1643    public Rect estimateDropLocation(DragSource source, int x, int y,
1644            int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
1645        final CellLayout layout = getCurrentDropLayout();
1646
1647        final CellLayout.CellInfo cellInfo = mDragInfo;
1648        final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1649        final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1650        final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1651
1652        final Rect location = recycle != null ? recycle : new Rect();
1653
1654        // Find drop cell and convert into rectangle
1655        int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX,
1656                spanY, ignoreView, layout, mTempCell);
1657
1658        if (dropCell == null) {
1659            return null;
1660        }
1661
1662        layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
1663        location.left = mTempEstimate[0];
1664        location.top = mTempEstimate[1];
1665
1666        layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
1667        location.right = mTempEstimate[0];
1668        location.bottom = mTempEstimate[1];
1669
1670        return location;
1671    }
1672
1673    /**
1674     * Calculate the nearest cell where the given object would be dropped.
1675     */
1676    private int[] estimateDropCell(int pixelX, int pixelY,
1677            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1678
1679        final int[] cellXY = mTempCell;
1680        layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY);
1681        layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate);
1682
1683        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
1684        // Find the best target drop location
1685        return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle);
1686    }
1687
1688    /**
1689     * Estimate the size that a child with the given dimensions will take in the current screen.
1690     */
1691    void estimateChildSize(int minWidth, int minHeight, int[] result) {
1692        ((CellLayout)getChildAt(mCurrentScreen)).estimateChildSize(minWidth, minHeight, result);
1693    }
1694
1695    void setLauncher(Launcher launcher) {
1696        mLauncher = launcher;
1697    }
1698
1699    public void setDragController(DragController dragController) {
1700        mDragController = dragController;
1701    }
1702
1703    public void onDropCompleted(View target, boolean success) {
1704        if (success) {
1705            if (target != this && mDragInfo != null) {
1706                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1707                cellLayout.removeView(mDragInfo.cell);
1708                if (mDragInfo.cell instanceof DropTarget) {
1709                    mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1710                }
1711                // final Object tag = mDragInfo.cell.getTag();
1712            }
1713        } else {
1714            if (mDragInfo != null) {
1715                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1716                cellLayout.onDropAborted(mDragInfo.cell);
1717            }
1718        }
1719
1720        mDragInfo = null;
1721    }
1722
1723    public void scrollLeft() {
1724        if (!mIsSmall) {
1725            if (mScroller.isFinished()) {
1726                if (mCurrentScreen > 0)
1727                    snapToScreen(mCurrentScreen - 1);
1728            } else {
1729                if (mNextScreen > 0)
1730                    snapToScreen(mNextScreen - 1);
1731            }
1732        }
1733    }
1734
1735    public void scrollRight() {
1736        if (!mIsSmall) {
1737            if (mScroller.isFinished()) {
1738                if (mCurrentScreen < getChildCount() - 1)
1739                    snapToScreen(mCurrentScreen + 1);
1740            } else {
1741                if (mNextScreen < getChildCount() - 1)
1742                    snapToScreen(mNextScreen + 1);
1743            }
1744        }
1745    }
1746
1747    public int getScreenForView(View v) {
1748        int result = -1;
1749        if (v != null) {
1750            ViewParent vp = v.getParent();
1751            final int screenCount = getChildCount();
1752            for (int i = 0; i < screenCount; i++) {
1753                if (vp == getChildAt(i)) {
1754                    return i;
1755                }
1756            }
1757        }
1758        return result;
1759    }
1760
1761    public Folder getFolderForTag(Object tag) {
1762        final int screenCount = getChildCount();
1763        for (int screen = 0; screen < screenCount; screen++) {
1764            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1765            int count = currentScreen.getChildCount();
1766            for (int i = 0; i < count; i++) {
1767                View child = currentScreen.getChildAt(i);
1768                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1769                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1770                    Folder f = (Folder) child;
1771                    if (f.getInfo() == tag && f.getInfo().opened) {
1772                        return f;
1773                    }
1774                }
1775            }
1776        }
1777        return null;
1778    }
1779
1780    public View getViewForTag(Object tag) {
1781        int screenCount = getChildCount();
1782        for (int screen = 0; screen < screenCount; screen++) {
1783            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1784            int count = currentScreen.getChildCount();
1785            for (int i = 0; i < count; i++) {
1786                View child = currentScreen.getChildAt(i);
1787                if (child.getTag() == tag) {
1788                    return child;
1789                }
1790            }
1791        }
1792        return null;
1793    }
1794
1795    /**
1796     * @return True is long presses are still allowed for the current touch
1797     */
1798    public boolean allowLongPress() {
1799        return mAllowLongPress;
1800    }
1801
1802    /**
1803     * Set true to allow long-press events to be triggered, usually checked by
1804     * {@link Launcher} to accept or block dpad-initiated long-presses.
1805     */
1806    public void setAllowLongPress(boolean allowLongPress) {
1807        mAllowLongPress = allowLongPress;
1808    }
1809
1810    void removeItems(final ArrayList<ApplicationInfo> apps) {
1811        final int screenCount = getChildCount();
1812        final PackageManager manager = getContext().getPackageManager();
1813        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
1814
1815        final HashSet<String> packageNames = new HashSet<String>();
1816        final int appCount = apps.size();
1817        for (int i = 0; i < appCount; i++) {
1818            packageNames.add(apps.get(i).componentName.getPackageName());
1819        }
1820
1821        for (int i = 0; i < screenCount; i++) {
1822            final CellLayout layout = (CellLayout) getChildAt(i);
1823
1824            // Avoid ANRs by treating each screen separately
1825            post(new Runnable() {
1826                public void run() {
1827                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
1828                    childrenToRemove.clear();
1829
1830                    int childCount = layout.getChildCount();
1831                    for (int j = 0; j < childCount; j++) {
1832                        final View view = layout.getChildAt(j);
1833                        Object tag = view.getTag();
1834
1835                        if (tag instanceof ShortcutInfo) {
1836                            final ShortcutInfo info = (ShortcutInfo) tag;
1837                            final Intent intent = info.intent;
1838                            final ComponentName name = intent.getComponent();
1839
1840                            if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1841                                for (String packageName: packageNames) {
1842                                    if (packageName.equals(name.getPackageName())) {
1843                                        // TODO: This should probably be done on a worker thread
1844                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1845                                        childrenToRemove.add(view);
1846                                    }
1847                                }
1848                            }
1849                        } else if (tag instanceof UserFolderInfo) {
1850                            final UserFolderInfo info = (UserFolderInfo) tag;
1851                            final ArrayList<ShortcutInfo> contents = info.contents;
1852                            final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
1853                            final int contentsCount = contents.size();
1854                            boolean removedFromFolder = false;
1855
1856                            for (int k = 0; k < contentsCount; k++) {
1857                                final ShortcutInfo appInfo = contents.get(k);
1858                                final Intent intent = appInfo.intent;
1859                                final ComponentName name = intent.getComponent();
1860
1861                                if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1862                                    for (String packageName: packageNames) {
1863                                        if (packageName.equals(name.getPackageName())) {
1864                                            toRemove.add(appInfo);
1865                                            // TODO: This should probably be done on a worker thread
1866                                            LauncherModel.deleteItemFromDatabase(
1867                                                    mLauncher, appInfo);
1868                                            removedFromFolder = true;
1869                                        }
1870                                    }
1871                                }
1872                            }
1873
1874                            contents.removeAll(toRemove);
1875                            if (removedFromFolder) {
1876                                final Folder folder = getOpenFolder();
1877                                if (folder != null)
1878                                    folder.notifyDataSetChanged();
1879                            }
1880                        } else if (tag instanceof LiveFolderInfo) {
1881                            final LiveFolderInfo info = (LiveFolderInfo) tag;
1882                            final Uri uri = info.uri;
1883                            final ProviderInfo providerInfo = manager.resolveContentProvider(
1884                                    uri.getAuthority(), 0);
1885
1886                            if (providerInfo != null) {
1887                                for (String packageName: packageNames) {
1888                                    if (packageName.equals(providerInfo.packageName)) {
1889                                        // TODO: This should probably be done on a worker thread
1890                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1891                                        childrenToRemove.add(view);
1892                                    }
1893                                }
1894                            }
1895                        } else if (tag instanceof LauncherAppWidgetInfo) {
1896                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1897                            final AppWidgetProviderInfo provider =
1898                                    widgets.getAppWidgetInfo(info.appWidgetId);
1899                            if (provider != null) {
1900                                for (String packageName: packageNames) {
1901                                    if (packageName.equals(provider.provider.getPackageName())) {
1902                                        // TODO: This should probably be done on a worker thread
1903                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1904                                        childrenToRemove.add(view);
1905                                    }
1906                                }
1907                            }
1908                        }
1909                    }
1910
1911                    childCount = childrenToRemove.size();
1912                    for (int j = 0; j < childCount; j++) {
1913                        View child = childrenToRemove.get(j);
1914                        layout.removeViewInLayout(child);
1915                        if (child instanceof DropTarget) {
1916                            mDragController.removeDropTarget((DropTarget)child);
1917                        }
1918                    }
1919
1920                    if (childCount > 0) {
1921                        layout.requestLayout();
1922                        layout.invalidate();
1923                    }
1924                }
1925            });
1926        }
1927    }
1928
1929    void updateShortcuts(ArrayList<ApplicationInfo> apps) {
1930        final PackageManager pm = mLauncher.getPackageManager();
1931
1932        final int screenCount = getChildCount();
1933        for (int i = 0; i < screenCount; i++) {
1934            final CellLayout layout = (CellLayout) getChildAt(i);
1935            int childCount = layout.getChildCount();
1936            for (int j = 0; j < childCount; j++) {
1937                final View view = layout.getChildAt(j);
1938                Object tag = view.getTag();
1939                if (tag instanceof ShortcutInfo) {
1940                    ShortcutInfo info = (ShortcutInfo)tag;
1941                    // We need to check for ACTION_MAIN otherwise getComponent() might
1942                    // return null for some shortcuts (for instance, for shortcuts to
1943                    // web pages.)
1944                    final Intent intent = info.intent;
1945                    final ComponentName name = intent.getComponent();
1946                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1947                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1948                        final int appCount = apps.size();
1949                        for (int k = 0; k < appCount; k++) {
1950                            ApplicationInfo app = apps.get(k);
1951                            if (app.componentName.equals(name)) {
1952                                info.setIcon(mIconCache.getIcon(info.intent));
1953                                ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
1954                                        new FastBitmapDrawable(info.getIcon(mIconCache)),
1955                                        null, null);
1956                                }
1957                        }
1958                    }
1959                }
1960            }
1961        }
1962    }
1963
1964    void moveToDefaultScreen(boolean animate) {
1965        if (animate) {
1966            if (mIsSmall) {
1967                unshrink(mDefaultScreen);
1968            } else {
1969                snapToScreen(mDefaultScreen);
1970            }
1971        } else {
1972            setCurrentScreen(mDefaultScreen);
1973        }
1974        getChildAt(mDefaultScreen).requestFocus();
1975    }
1976
1977    void setIndicators(Drawable previous, Drawable next) {
1978        mPreviousIndicator = previous;
1979        mNextIndicator = next;
1980        previous.setLevel(mCurrentScreen);
1981        next.setLevel(mCurrentScreen);
1982    }
1983
1984    public static class SavedState extends BaseSavedState {
1985        int currentScreen = -1;
1986
1987        SavedState(Parcelable superState) {
1988            super(superState);
1989        }
1990
1991        private SavedState(Parcel in) {
1992            super(in);
1993            currentScreen = in.readInt();
1994        }
1995
1996        @Override
1997        public void writeToParcel(Parcel out, int flags) {
1998            super.writeToParcel(out, flags);
1999            out.writeInt(currentScreen);
2000        }
2001
2002        public static final Parcelable.Creator<SavedState> CREATOR =
2003                new Parcelable.Creator<SavedState>() {
2004            public SavedState createFromParcel(Parcel in) {
2005                return new SavedState(in);
2006            }
2007
2008            public SavedState[] newArray(int size) {
2009                return new SavedState[size];
2010            }
2011        };
2012    }
2013}
2014