Workspace.java revision df7c3a7853abd4dec460247370fe0b17b50866ac
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            if (!mLauncher.isWorkspaceLocked()) {
664                snapToScreen(screen);
665            }
666            return true;
667        }
668        return false;
669    }
670
671    @Override
672    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
673        if (!mLauncher.isAllAppsVisible()) {
674            final Folder openFolder = getOpenFolder();
675            if (openFolder != null) {
676                return openFolder.requestFocus(direction, previouslyFocusedRect);
677            } else {
678                int focusableScreen;
679                if (mNextScreen != INVALID_SCREEN) {
680                    focusableScreen = mNextScreen;
681                } else {
682                    focusableScreen = mCurrentScreen;
683                }
684                getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
685            }
686        }
687        return false;
688    }
689
690    @Override
691    public boolean dispatchUnhandledMove(View focused, int direction) {
692        if (direction == View.FOCUS_LEFT) {
693            if (getCurrentScreen() > 0) {
694                snapToScreen(getCurrentScreen() - 1);
695                return true;
696            }
697        } else if (direction == View.FOCUS_RIGHT) {
698            if (getCurrentScreen() < getChildCount() - 1) {
699                snapToScreen(getCurrentScreen() + 1);
700                return true;
701            }
702        }
703        return super.dispatchUnhandledMove(focused, direction);
704    }
705
706    @Override
707    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
708        if (!mLauncher.isAllAppsVisible()) {
709            final Folder openFolder = getOpenFolder();
710            if (openFolder == null) {
711                getChildAt(mCurrentScreen).addFocusables(views, direction);
712                if (direction == View.FOCUS_LEFT) {
713                    if (mCurrentScreen > 0) {
714                        getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
715                    }
716                } else if (direction == View.FOCUS_RIGHT) {
717                    if (mCurrentScreen < getChildCount() - 1) {
718                        getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
719                    }
720                }
721            } else {
722                openFolder.addFocusables(views, direction);
723            }
724        }
725    }
726
727    @Override
728    public boolean dispatchTouchEvent(MotionEvent ev) {
729        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
730            // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
731            // ie when you click on a mini-screen, it zooms back to that screen)
732            if (mLauncher.isWorkspaceLocked() ||
733                    (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible())) {
734                return false;
735            }
736        }
737        return super.dispatchTouchEvent(ev);
738    }
739
740    /**
741     * {@inheritDoc}
742     */
743    @Override
744    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
745        if (disallowIntercept) {
746            // We need to make sure to cancel our long press if
747            // a scrollable widget takes over touch events
748            final View currentScreen = getChildAt(mCurrentScreen);
749            currentScreen.cancelLongPress();
750        }
751        super.requestDisallowInterceptTouchEvent(disallowIntercept);
752    }
753
754    @Override
755    public boolean onInterceptTouchEvent(MotionEvent ev) {
756        final boolean workspaceLocked = mLauncher.isWorkspaceLocked();
757        final boolean allAppsVisible = mLauncher.isAllAppsVisible();
758
759        // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
760        // ie when you click on a mini-screen, it zooms back to that screen)
761        if (workspaceLocked || (!LauncherApplication.isScreenXLarge() && allAppsVisible)) {
762            return false; // We don't want the events.  Let them fall through to the all apps view.
763        }
764
765        /*
766         * This method JUST determines whether we want to intercept the motion.
767         * If we return true, onTouchEvent will be called and we do the actual
768         * scrolling there.
769         */
770
771        /*
772         * Shortcut the most recurring case: the user is in the dragging
773         * state and he is moving his finger.  We want to intercept this
774         * motion.
775         */
776        final int action = ev.getAction();
777        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
778            return true;
779        }
780
781        if (mVelocityTracker == null) {
782            mVelocityTracker = VelocityTracker.obtain();
783        }
784        mVelocityTracker.addMovement(ev);
785
786        switch (action & MotionEvent.ACTION_MASK) {
787            case MotionEvent.ACTION_MOVE: {
788                /*
789                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
790                 * whether the user has moved far enough from his original down touch.
791                 */
792
793                /*
794                 * Locally do absolute value. mLastMotionX is set to the y value
795                 * of the down event.
796                 */
797                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
798                final float x = ev.getX(pointerIndex);
799                final float y = ev.getY(pointerIndex);
800                final int xDiff = (int) Math.abs(x - mLastMotionX);
801                final int yDiff = (int) Math.abs(y - mLastMotionY);
802
803                final int touchSlop = mTouchSlop;
804                boolean xMoved = xDiff > touchSlop;
805                boolean yMoved = yDiff > touchSlop;
806
807                if (xMoved || yMoved) {
808
809                    if (xMoved) {
810                        // Scroll if the user moved far enough along the X axis
811                        mTouchState = TOUCH_STATE_SCROLLING;
812                        mLastMotionX = x;
813                        mTouchX = mScrollX;
814                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
815                        enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
816                    }
817                    // Either way, cancel any pending longpress
818                    if (mAllowLongPress) {
819                        mAllowLongPress = false;
820                        // Try canceling the long press. It could also have been scheduled
821                        // by a distant descendant, so use the mAllowLongPress flag to block
822                        // everything
823                        final View currentScreen = getChildAt(mCurrentScreen);
824                        currentScreen.cancelLongPress();
825                    }
826                }
827                break;
828            }
829
830        case MotionEvent.ACTION_DOWN: {
831            final float x = ev.getX();
832            final float y = ev.getY();
833            // Remember location of down touch
834            mLastMotionX = x;
835            mLastMotionY = y;
836            mActivePointerId = ev.getPointerId(0);
837            mAllowLongPress = true;
838
839                /*
840                 * If being flinged and user touches the screen, initiate drag;
841                 * otherwise don't.  mScroller.isFinished should be false when
842                 * being flinged.
843                 */
844                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
845                break;
846            }
847
848            case MotionEvent.ACTION_CANCEL:
849            case MotionEvent.ACTION_UP:
850
851                if (mTouchState != TOUCH_STATE_SCROLLING) {
852                    final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
853                    if (!currentScreen.lastDownOnOccupiedCell()) {
854                        getLocationOnScreen(mTempCell);
855                        // Send a tap to the wallpaper if the last down was on empty space
856                        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
857                        mWallpaperManager.sendWallpaperCommand(getWindowToken(),
858                                "android.wallpaper.tap",
859                                mTempCell[0] + (int) ev.getX(pointerIndex),
860                                mTempCell[1] + (int) ev.getY(pointerIndex), 0, null);
861                    }
862                }
863
864                // Release the drag
865                clearChildrenCache();
866                mTouchState = TOUCH_STATE_REST;
867                mActivePointerId = INVALID_POINTER;
868                mAllowLongPress = false;
869
870                if (mVelocityTracker != null) {
871                    mVelocityTracker.recycle();
872                    mVelocityTracker = null;
873                }
874
875            break;
876
877        case MotionEvent.ACTION_POINTER_UP:
878            onSecondaryPointerUp(ev);
879            break;
880        }
881
882        /*
883         * The only time we want to intercept motion events is if we are in the
884         * drag mode.
885         */
886        return mTouchState != TOUCH_STATE_REST;
887    }
888
889    private void onSecondaryPointerUp(MotionEvent ev) {
890        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
891                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
892        final int pointerId = ev.getPointerId(pointerIndex);
893        if (pointerId == mActivePointerId) {
894            // This was our active pointer going up. Choose a new
895            // active pointer and adjust accordingly.
896            // TODO: Make this decision more intelligent.
897            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
898            mLastMotionX = ev.getX(newPointerIndex);
899            mLastMotionY = ev.getY(newPointerIndex);
900            mActivePointerId = ev.getPointerId(newPointerIndex);
901            if (mVelocityTracker != null) {
902                mVelocityTracker.clear();
903            }
904        }
905    }
906
907    /**
908     * If one of our descendant views decides that it could be focused now, only
909     * pass that along if it's on the current screen.
910     *
911     * This happens when live folders requery, and if they're off screen, they
912     * end up calling requestFocus, which pulls it on screen.
913     */
914    @Override
915    public void focusableViewAvailable(View focused) {
916        View current = getChildAt(mCurrentScreen);
917        View v = focused;
918        while (true) {
919            if (v == current) {
920                super.focusableViewAvailable(focused);
921                return;
922            }
923            if (v == this) {
924                return;
925            }
926            ViewParent parent = v.getParent();
927            if (parent instanceof View) {
928                v = (View) v.getParent();
929            } else {
930                return;
931            }
932        }
933    }
934
935    void enableChildrenCache(int fromScreen, int toScreen) {
936        if (fromScreen > toScreen) {
937            final int temp = fromScreen;
938            fromScreen = toScreen;
939            toScreen = temp;
940        }
941
942        final int screenCount = getChildCount();
943
944        fromScreen = Math.max(fromScreen, 0);
945        toScreen = Math.min(toScreen, screenCount - 1);
946
947        for (int i = fromScreen; i <= toScreen; i++) {
948            final CellLayout layout = (CellLayout) getChildAt(i);
949            layout.setChildrenDrawnWithCacheEnabled(true);
950            layout.setChildrenDrawingCacheEnabled(true);
951        }
952    }
953
954    void clearChildrenCache() {
955        final int screenCount = getChildCount();
956        for (int i = 0; i < screenCount; i++) {
957            final CellLayout layout = (CellLayout) getChildAt(i);
958            layout.setChildrenDrawnWithCacheEnabled(false);
959        }
960    }
961
962    @Override
963    public boolean onTouchEvent(MotionEvent ev) {
964
965        if (mLauncher.isWorkspaceLocked()) {
966            return false; // We don't want the events.  Let them fall through to the all apps view.
967        }
968        if (mLauncher.isAllAppsVisible()) {
969            // Cancel any scrolling that is in progress.
970            if (!mScroller.isFinished()) {
971                mScroller.abortAnimation();
972            }
973            snapToScreen(mCurrentScreen);
974            return false; // We don't want the events.  Let them fall through to the all apps view.
975        }
976
977        if (mVelocityTracker == null) {
978            mVelocityTracker = VelocityTracker.obtain();
979        }
980        mVelocityTracker.addMovement(ev);
981
982        final int action = ev.getAction();
983
984        switch (action & MotionEvent.ACTION_MASK) {
985        case MotionEvent.ACTION_DOWN:
986            /*
987             * If being flinged and user touches, stop the fling. isFinished
988             * will be false if being flinged.
989             */
990            if (!mScroller.isFinished()) {
991                mScroller.abortAnimation();
992            }
993
994            // Remember where the motion event started
995            mLastMotionX = ev.getX();
996            mActivePointerId = ev.getPointerId(0);
997            if (mTouchState == TOUCH_STATE_SCROLLING) {
998                enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
999            }
1000            break;
1001        case MotionEvent.ACTION_MOVE:
1002            if (mTouchState == TOUCH_STATE_SCROLLING) {
1003                // Scroll to follow the motion event
1004                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
1005                final float x = ev.getX(pointerIndex);
1006                final float deltaX = mLastMotionX - x;
1007                mLastMotionX = x;
1008
1009                if (deltaX < 0) {
1010                    if (mTouchX > 0) {
1011                        mTouchX += Math.max(-mTouchX, deltaX);
1012                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
1013                        invalidate();
1014                    }
1015                } else if (deltaX > 0) {
1016                    final float availableToScroll = getChildAt(getChildCount() - 1).getRight() -
1017                            mTouchX - getWidth();
1018                    if (availableToScroll > 0) {
1019                        mTouchX += Math.min(availableToScroll, deltaX);
1020                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
1021                        invalidate();
1022                    }
1023                } else {
1024                    awakenScrollBars();
1025                }
1026            }
1027            break;
1028        case MotionEvent.ACTION_UP:
1029            if (mTouchState == TOUCH_STATE_SCROLLING) {
1030                final VelocityTracker velocityTracker = mVelocityTracker;
1031                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1032                final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
1033
1034                final int screenWidth = getWidth();
1035                final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
1036                final float scrolledPos = (float) mScrollX / screenWidth;
1037
1038                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
1039                    // Fling hard enough to move left.
1040                    // Don't fling across more than one screen at a time.
1041                    final int bound = scrolledPos < whichScreen ?
1042                            mCurrentScreen - 1 : mCurrentScreen;
1043                    snapToScreen(Math.min(whichScreen, bound), velocityX, true);
1044                } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
1045                    // Fling hard enough to move right
1046                    // Don't fling across more than one screen at a time.
1047                    final int bound = scrolledPos > whichScreen ?
1048                            mCurrentScreen + 1 : mCurrentScreen;
1049                    snapToScreen(Math.max(whichScreen, bound), velocityX, true);
1050                } else {
1051                    snapToScreen(whichScreen, 0, true);
1052                }
1053
1054                if (mVelocityTracker != null) {
1055                    mVelocityTracker.recycle();
1056                    mVelocityTracker = null;
1057                }
1058            }
1059            mTouchState = TOUCH_STATE_REST;
1060            mActivePointerId = INVALID_POINTER;
1061            break;
1062        case MotionEvent.ACTION_CANCEL:
1063            mTouchState = TOUCH_STATE_REST;
1064            mActivePointerId = INVALID_POINTER;
1065            break;
1066        case MotionEvent.ACTION_POINTER_UP:
1067            onSecondaryPointerUp(ev);
1068            break;
1069        }
1070
1071        return true;
1072    }
1073
1074    public boolean isSmall() {
1075        return mIsSmall;
1076    }
1077
1078    void shrinkToTop(boolean animated) {
1079        shrink(SHRINK_TO_TOP, animated);
1080    }
1081
1082    void shrinkToMiddle() {
1083        shrink(SHRINK_TO_MIDDLE, true);
1084    }
1085
1086    void shrinkToBottom() {
1087        shrinkToBottom(true);
1088    }
1089
1090    void shrinkToBottom(boolean animated) {
1091        if (mFirstLayout) {
1092            // (mFirstLayout == "first layout has not happened yet")
1093            // if we get a call to shrink() as part of our initialization (for example, if
1094            // Launcher is started in All Apps mode) then we need to wait for a layout call
1095            // to get our width so we can layout the mini-screen views correctly
1096            mWaitingToShrinkToBottom = true;
1097        } else {
1098            shrink(SHRINK_TO_BOTTOM, animated);
1099        }
1100    }
1101
1102    // we use this to shrink the workspace for the all apps view and the customize view
1103    private void shrink(int shrinkPosition, boolean animated) {
1104        mIsSmall = true;
1105        final Resources res = getResources();
1106        final int screenWidth = getWidth();
1107        final int screenHeight = getHeight();
1108        final int scaledScreenWidth = (int) (SHRINK_FACTOR * screenWidth);
1109        final int scaledScreenHeight = (int) (SHRINK_FACTOR * screenHeight);
1110        final float scaledSpacing = res.getDimension(R.dimen.smallScreenSpacing);
1111
1112        final int screenCount = getChildCount();
1113        float totalWidth = screenCount * scaledScreenWidth + (screenCount - 1) * scaledSpacing;
1114
1115        float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
1116        if (shrinkPosition == SHRINK_TO_BOTTOM) {
1117            newY = screenHeight - newY - scaledScreenHeight;
1118        } else if (shrinkPosition == SHRINK_TO_MIDDLE) {
1119            newY = screenHeight / 2 - scaledScreenHeight / 2;
1120        }
1121
1122        // We animate all the screens to the centered position in workspace
1123        // At the same time, the screens become greyed/dimmed
1124
1125        // newX is initialized to the left-most position of the centered screens
1126        float newX = (mCurrentScreen + 1) * screenWidth - screenWidth / 2 - totalWidth / 2;
1127        for (int i = 0; i < screenCount; i++) {
1128            CellLayout cl = (CellLayout) getChildAt(i);
1129            cl.setPivotX(0.0f);
1130            cl.setPivotY(0.0f);
1131            if (animated) {
1132                final int duration = res.getInteger(R.integer.config_workspaceShrinkTime);
1133                new PropertyAnimator(duration, cl,
1134                        new PropertyValuesHolder("x", newX),
1135                        new PropertyValuesHolder("y", newY),
1136                        new PropertyValuesHolder("scaleX", SHRINK_FACTOR),
1137                        new PropertyValuesHolder("scaleY", SHRINK_FACTOR),
1138                        new PropertyValuesHolder("dimmedBitmapAlpha", 1.0f)).start();
1139            } else {
1140                cl.setX((int)newX);
1141                cl.setY((int)newY);
1142                cl.setScaleX(SHRINK_FACTOR);
1143                cl.setScaleY(SHRINK_FACTOR);
1144                cl.setDimmedBitmapAlpha(1.0f);
1145            }
1146            // increment newX for the next screen
1147            newX += scaledScreenWidth + scaledSpacing;
1148            cl.setOnInterceptTouchListener(this);
1149        }
1150        setChildrenDrawnWithCacheEnabled(true);
1151    }
1152
1153    // We call this when we trigger an unshrink by clicking on the CellLayout cl
1154    private void unshrink(CellLayout clThatWasClicked) {
1155        int newCurrentScreen = mCurrentScreen;
1156        final int screenCount = getChildCount();
1157        for (int i = 0; i < screenCount; i++) {
1158            if (getChildAt(i) == clThatWasClicked) {
1159                newCurrentScreen = i;
1160            }
1161        }
1162        unshrink(newCurrentScreen);
1163    }
1164
1165    private void unshrink(int newCurrentScreen) {
1166        if (mIsSmall) {
1167            int delta = (newCurrentScreen - mCurrentScreen)*getWidth();
1168
1169            final int screenCount = getChildCount();
1170            for (int i = 0; i < screenCount; i++) {
1171                CellLayout cl = (CellLayout) getChildAt(i);
1172                cl.setX(cl.getX() + delta);
1173            }
1174            mScrollX = newCurrentScreen * getWidth();
1175
1176            unshrink();
1177            setCurrentScreen(newCurrentScreen);
1178        }
1179    }
1180
1181    void unshrink() {
1182        unshrink(true);
1183    }
1184
1185    void unshrink(boolean animated) {
1186        if (mIsSmall) {
1187            Sequencer s = new Sequencer();
1188            final int screenCount = getChildCount();
1189
1190            final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
1191            for (int i = 0; i < screenCount; i++) {
1192                final CellLayout cl = (CellLayout)getChildAt(i);
1193                cl.setPivotX(0.0f);
1194                cl.setPivotY(0.0f);
1195                if (animated) {
1196                    s.playTogether(
1197                            new PropertyAnimator(duration, cl, "translationX", 0.0f),
1198                            new PropertyAnimator(duration, cl, "translationY", 0.0f),
1199                            new PropertyAnimator(duration, cl, "scaleX", 1.0f),
1200                            new PropertyAnimator(duration, cl, "scaleY", 1.0f),
1201                            new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 0.0f));
1202                } else {
1203                    cl.setTranslationX(0.0f);
1204                    cl.setTranslationY(0.0f);
1205                    cl.setScaleX(1.0f);
1206                    cl.setScaleY(1.0f);
1207                    cl.setDimmedBitmapAlpha(0.0f);
1208                }
1209            }
1210            s.addListener(mUnshrinkAnimationListener);
1211            s.start();
1212        }
1213    }
1214
1215    void snapToScreen(int whichScreen) {
1216        snapToScreen(whichScreen, 0, false);
1217    }
1218
1219    private void snapToScreen(int whichScreen, int velocity, boolean settle) {
1220        // if (!mScroller.isFinished()) return;
1221
1222        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
1223
1224        enableChildrenCache(mCurrentScreen, whichScreen);
1225
1226        mNextScreen = whichScreen;
1227
1228        if (mPreviousIndicator != null) {
1229            mPreviousIndicator.setLevel(mNextScreen);
1230            mNextIndicator.setLevel(mNextScreen);
1231        }
1232
1233        View focusedChild = getFocusedChild();
1234        if (focusedChild != null && whichScreen != mCurrentScreen &&
1235                focusedChild == getChildAt(mCurrentScreen)) {
1236            focusedChild.clearFocus();
1237        }
1238
1239        final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));
1240        final int newX = whichScreen * getWidth();
1241        final int delta = newX - mScrollX;
1242        int duration = (screenDelta + 1) * 100;
1243
1244        if (!mScroller.isFinished()) {
1245            mScroller.abortAnimation();
1246        }
1247
1248        if (settle) {
1249            mScrollInterpolator.setDistance(screenDelta);
1250        } else {
1251            mScrollInterpolator.disableSettle();
1252        }
1253
1254        velocity = Math.abs(velocity);
1255        if (velocity > 0) {
1256            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
1257                    * FLING_VELOCITY_INFLUENCE;
1258        } else {
1259            duration += 100;
1260        }
1261
1262        awakenScrollBars(duration);
1263        mScroller.startScroll(mScrollX, 0, delta, 0, duration);
1264        invalidate();
1265    }
1266
1267    void startDrag(CellLayout.CellInfo cellInfo) {
1268        View child = cellInfo.cell;
1269
1270        // Make sure the drag was started by a long press as opposed to a long click.
1271        if (!child.isInTouchMode()) {
1272            return;
1273        }
1274
1275        mDragInfo = cellInfo;
1276        mDragInfo.screen = mCurrentScreen;
1277
1278        CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
1279
1280        current.onDragChild(child);
1281        mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
1282        invalidate();
1283    }
1284
1285    @Override
1286    protected Parcelable onSaveInstanceState() {
1287        final SavedState state = new SavedState(super.onSaveInstanceState());
1288        state.currentScreen = mCurrentScreen;
1289        return state;
1290    }
1291
1292    @Override
1293    protected void onRestoreInstanceState(Parcelable state) {
1294        SavedState savedState = (SavedState) state;
1295        super.onRestoreInstanceState(savedState.getSuperState());
1296        if (savedState.currentScreen != -1) {
1297            setCurrentScreen(savedState.currentScreen, false);
1298            Launcher.setScreen(mCurrentScreen);
1299        }
1300    }
1301
1302    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
1303        addApplicationShortcut(info, cellInfo, false);
1304    }
1305
1306    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo,
1307            boolean insertAtFirst) {
1308        final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
1309        final int[] result = new int[2];
1310
1311        layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
1312        onDropExternal(result[0], result[1], info, layout, insertAtFirst);
1313    }
1314
1315    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
1316            DragView dragView, Object dragInfo) {
1317        CellLayout cellLayout = getCurrentDropLayout();
1318        int originX = x - xOffset;
1319        int originY = y - yOffset;
1320        if (mIsSmall) {
1321            // find out which target layout is over
1322            final float[] localXY = mTempDragCoordinates;
1323            localXY[0] = originX;
1324            localXY[1] = originY;
1325            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
1326            // we need to subtract left/top here because DragController already adds
1327            // dragRegionLeft/Top to xOffset and yOffset
1328            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
1329            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
1330            cellLayout =  findMatchingScreenForDragOver(localXY, localBottomRightXY);
1331            if (cellLayout == null) {
1332                // cancel the drag if we're not over a mini-screen at time of drop
1333                // TODO: maybe add a nice fade here?
1334                return;
1335            }
1336            // localXY will be transformed into the local screen's coordinate space; save that info
1337            originX = (int)localXY[0];
1338            originY = (int)localXY[1];
1339        }
1340        if (source != this) {
1341            onDropExternal(originX, originY, dragInfo, cellLayout);
1342        } else {
1343            // Move internally
1344            if (mDragInfo != null) {
1345                final View cell = mDragInfo.cell;
1346                int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1347                if (index != mDragInfo.screen) {
1348                    final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1349                    originalCellLayout.removeView(cell);
1350                    addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY,
1351                            mDragInfo.spanX, mDragInfo.spanY);
1352                }
1353
1354                mTargetCell = estimateDropCell(originX, originY,
1355                        mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout,
1356                        mTargetCell);
1357                cellLayout.onDropChild(cell);
1358
1359                // update the item's position after drop
1360                final ItemInfo info = (ItemInfo) cell.getTag();
1361                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell
1362                        .getLayoutParams();
1363                lp.cellX = mTargetCell[0];
1364                lp.cellY = mTargetCell[1];
1365
1366                LauncherModel.moveItemInDatabase(mLauncher, info,
1367                        LauncherSettings.Favorites.CONTAINER_DESKTOP, index,
1368                        lp.cellX, lp.cellY);
1369            }
1370        }
1371    }
1372
1373    public void onDragEnter(DragSource source, int x, int y, int xOffset,
1374            int yOffset, DragView dragView, Object dragInfo) {
1375    }
1376
1377    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
1378            DragView dragView, Object dragInfo) {
1379
1380        // We may need to delegate the drag to a child view. If a 1x1 item
1381        // would land in a cell occupied by a DragTarget (e.g. a Folder),
1382        // then drag events should be handled by that child.
1383
1384        ItemInfo item = (ItemInfo)dragInfo;
1385        CellLayout currentLayout = getCurrentDropLayout();
1386
1387        int dragPointX, dragPointY;
1388        if (item.spanX == 1 && item.spanY == 1) {
1389            // For a 1x1, calculate the drop cell exactly as in onDragOver
1390            dragPointX = x - xOffset;
1391            dragPointY = y - yOffset;
1392        } else {
1393            // Otherwise, use the exact drag coordinates
1394            dragPointX = x;
1395            dragPointY = y;
1396        }
1397
1398        // If we are dragging over a cell that contains a DropTarget that will
1399        // accept the drop, delegate to that DropTarget.
1400        final int[] cellXY = mTempCell;
1401        currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
1402        View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
1403        if (child instanceof DropTarget) {
1404            DropTarget target = (DropTarget)child;
1405            if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
1406                return target;
1407            }
1408        }
1409        return null;
1410    }
1411
1412    // xy = upper left corner of item being dragged
1413    // bottomRightXy = lower right corner of item being dragged
1414    // This method will see which mini-screen is most overlapped by the item being dragged, and
1415    // return it. It will also transform the parameters xy and bottomRightXy into the local
1416    // coordinate space of the returned screen
1417    private CellLayout findMatchingScreenForDragOver(float[] xy, float[] bottomRightXy) {
1418        float x = xy[0];
1419        float y = xy[1];
1420        float right = bottomRightXy[0];
1421        float bottom = bottomRightXy[1];
1422
1423        float bestX = 0;
1424        float bestY = 0;
1425        float bestRight = 0;
1426        float bestBottom = 0;
1427
1428        Matrix inverseMatrix = new Matrix();
1429
1430        // We loop through all the screens (ie CellLayouts) and see which one overlaps the most
1431        // with the item being dragged.
1432        final int screenCount = getChildCount();
1433        CellLayout bestMatchingScreen = null;
1434        float bestOverlapSoFar = 0;
1435        for (int i = 0; i < screenCount; i++) {
1436            CellLayout cl = (CellLayout)getChildAt(i);
1437            // Transform the coordinates of the item being dragged to the CellLayout's coordinates
1438            float left = cl.getLeft();
1439            float top = cl.getTop();
1440            xy[0] = x + mScrollX - left;
1441            xy[1] = y + mScrollY - top;
1442            cl.getMatrix().invert(inverseMatrix);
1443
1444            bottomRightXy[0] = right + mScrollX - left;
1445            bottomRightXy[1] = bottom + mScrollY - top;
1446
1447            inverseMatrix.mapPoints(xy);
1448            inverseMatrix.mapPoints(bottomRightXy);
1449
1450            float dragRegionX = xy[0];
1451            float dragRegionY = xy[1];
1452            float dragRegionRight = bottomRightXy[0];
1453            float dragRegionBottom = bottomRightXy[1];
1454
1455            // Find the overlapping region
1456            float overlapLeft = Math.max(0f, dragRegionX);
1457            float overlapTop = Math.max(0f, dragRegionY);
1458            float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom);
1459            float overlapRight = Math.min(cl.getWidth(), dragRegionRight);
1460
1461            if (overlapRight >= 0 && overlapLeft <= cl.getWidth() &&
1462                    overlapTop >= 0 && overlapBottom <= cl.getHeight()) {
1463                // Calculate the size of the overlapping region
1464                float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
1465                if (overlap > bestOverlapSoFar) {
1466                    bestOverlapSoFar = overlap;
1467                    bestMatchingScreen = cl;
1468                    bestX = xy[0];
1469                    bestY = xy[1];
1470                    bestRight = bottomRightXy[0];
1471                    bestBottom = bottomRightXy[1];
1472                }
1473             }
1474        }
1475        if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) {
1476            if (mDragTargetLayout != null) {
1477                mDragTargetLayout.onDragComplete();
1478            }
1479            mDragTargetLayout = bestMatchingScreen;
1480        }
1481        xy[0] = bestX;
1482        xy[1] = bestY;
1483        bottomRightXy[0] = bestRight;
1484        bottomRightXy[1] = bestBottom;
1485        return bestMatchingScreen;
1486    }
1487
1488    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
1489            DragView dragView, Object dragInfo) {
1490
1491        final ItemInfo item = (ItemInfo)dragInfo;
1492        CellLayout currentLayout = getCurrentDropLayout();
1493
1494        if (dragInfo instanceof LauncherAppWidgetInfo) {
1495            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
1496
1497            if (widgetInfo.spanX == -1) {
1498                // Calculate the grid spans needed to fit this widget
1499                int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null);
1500                item.spanX = spans[0];
1501                item.spanY = spans[1];
1502            }
1503        }
1504        int originX = x - xOffset;
1505        int originY = y - yOffset;
1506        if (mIsSmall) {
1507            // find out which mini screen the dragged item is over
1508            final float[] localXY = mTempDragCoordinates;
1509            localXY[0] = originX;
1510            localXY[1] = originY;
1511            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
1512
1513            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
1514            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
1515            currentLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY);
1516            if (currentLayout != null) {
1517                currentLayout.setHover(true);
1518            }
1519
1520            originX = (int)localXY[0];
1521            originY = (int)localXY[1];
1522        }
1523
1524        if (source != this) {
1525            // This is a hack to fix the point used to determine which cell an icon from the all
1526            // apps screen is over
1527            if (item != null && item.spanX == 1 && currentLayout != null) {
1528                int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2;
1529
1530                originX += dragRegionLeft - dragView.getDragRegionLeft();
1531                if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) {
1532                    dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(),
1533                            currentLayout.getCellWidth(), dragView.getDragRegionHeight());
1534                }
1535            }
1536        }
1537        if (currentLayout != mDragTargetLayout) {
1538            if (mDragTargetLayout != null) {
1539                mDragTargetLayout.onDragComplete();
1540            }
1541            mDragTargetLayout = currentLayout;
1542        }
1543
1544        // only visualize the drop locations for moving icons within the home screen on tablet
1545        // on phone, we also visualize icons dragged in from All Apps
1546        if ((!LauncherApplication.isScreenXLarge() || source == this)
1547                && mDragTargetLayout != null) {
1548            final View child = (mDragInfo == null) ? null : mDragInfo.cell;
1549            mDragTargetLayout.visualizeDropLocation(
1550                    child, originX, originY, item.spanX, item.spanY);
1551        }
1552    }
1553
1554    public void onDragExit(DragSource source, int x, int y, int xOffset,
1555            int yOffset, DragView dragView, Object dragInfo) {
1556        if (mDragTargetLayout != null) {
1557            mDragTargetLayout.onDragComplete();
1558            mDragTargetLayout = null;
1559        }
1560    }
1561
1562    private void onDropExternal(int x, int y, Object dragInfo,
1563            CellLayout cellLayout) {
1564        onDropExternal(x, y, dragInfo, cellLayout, false);
1565    }
1566
1567    private void onDropExternal(int x, int y, Object dragInfo,
1568            CellLayout cellLayout, boolean insertAtFirst) {
1569        // Drag from somewhere else
1570        ItemInfo info = (ItemInfo) dragInfo;
1571
1572        View view = null;
1573
1574        switch (info.itemType) {
1575        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1576        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1577            if (info.container == NO_ID && info instanceof ApplicationInfo) {
1578                // Came from all apps -- make a copy
1579                info = new ShortcutInfo((ApplicationInfo) info);
1580            }
1581            view = mLauncher.createShortcut(R.layout.application, cellLayout,
1582                    (ShortcutInfo) info);
1583            break;
1584        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1585            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
1586                    (ViewGroup) getChildAt(mCurrentScreen),
1587                    ((UserFolderInfo) info));
1588            break;
1589        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1590            cellLayout.setTagToCellInfoForPoint(x, y);
1591            int[] position = new int[2];
1592            position[0] = x;
1593            position[1] = y;
1594            mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName,
1595                    cellLayout.getTag(), position);
1596            break;
1597        default:
1598            throw new IllegalStateException("Unknown item type: "
1599                    + info.itemType);
1600        }
1601
1602        // If the view is null, it has already been added.
1603        if (view == null) {
1604            cellLayout.onDragComplete();
1605        } else {
1606            mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
1607            addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
1608                    mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
1609            cellLayout.onDropChild(view);
1610            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1611
1612            LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1613                    LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen,
1614                    lp.cellX, lp.cellY);
1615        }
1616    }
1617
1618    /**
1619     * Return the current {@link CellLayout}, correctly picking the destination
1620     * screen while a scroll is in progress.
1621     */
1622    private CellLayout getCurrentDropLayout() {
1623        int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
1624        return (CellLayout) getChildAt(index);
1625    }
1626
1627    /**
1628     * {@inheritDoc}
1629     */
1630    public boolean acceptDrop(DragSource source, int x, int y,
1631            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1632        final CellLayout layout = getCurrentDropLayout();
1633        final CellLayout.CellInfo dragCellInfo = mDragInfo;
1634        final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
1635        final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
1636
1637        final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
1638        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
1639
1640        if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) {
1641            return true;
1642        } else {
1643            Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
1644            return false;
1645        }
1646    }
1647
1648    /**
1649     * {@inheritDoc}
1650     */
1651    public Rect estimateDropLocation(DragSource source, int x, int y,
1652            int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
1653        final CellLayout layout = getCurrentDropLayout();
1654
1655        final CellLayout.CellInfo cellInfo = mDragInfo;
1656        final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1657        final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1658        final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1659
1660        final Rect location = recycle != null ? recycle : new Rect();
1661
1662        // Find drop cell and convert into rectangle
1663        int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX,
1664                spanY, ignoreView, layout, mTempCell);
1665
1666        if (dropCell == null) {
1667            return null;
1668        }
1669
1670        layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
1671        location.left = mTempEstimate[0];
1672        location.top = mTempEstimate[1];
1673
1674        layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
1675        location.right = mTempEstimate[0];
1676        location.bottom = mTempEstimate[1];
1677
1678        return location;
1679    }
1680
1681    /**
1682     * Calculate the nearest cell where the given object would be dropped.
1683     */
1684    private int[] estimateDropCell(int pixelX, int pixelY,
1685            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1686
1687        final int[] cellXY = mTempCell;
1688        layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY);
1689        layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate);
1690
1691        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
1692        // Find the best target drop location
1693        return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle);
1694    }
1695
1696    /**
1697     * Estimate the size that a child with the given dimensions will take in the current screen.
1698     */
1699    void estimateChildSize(int minWidth, int minHeight, int[] result) {
1700        ((CellLayout)getChildAt(mCurrentScreen)).estimateChildSize(minWidth, minHeight, result);
1701    }
1702
1703    void setLauncher(Launcher launcher) {
1704        mLauncher = launcher;
1705    }
1706
1707    public void setDragController(DragController dragController) {
1708        mDragController = dragController;
1709    }
1710
1711    public void onDropCompleted(View target, boolean success) {
1712        if (success) {
1713            if (target != this && mDragInfo != null) {
1714                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1715                cellLayout.removeView(mDragInfo.cell);
1716                if (mDragInfo.cell instanceof DropTarget) {
1717                    mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1718                }
1719                // final Object tag = mDragInfo.cell.getTag();
1720            }
1721        } else {
1722            if (mDragInfo != null) {
1723                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1724                cellLayout.onDropAborted(mDragInfo.cell);
1725            }
1726        }
1727
1728        mDragInfo = null;
1729    }
1730
1731    public void scrollLeft() {
1732        if (!mIsSmall) {
1733            if (mScroller.isFinished()) {
1734                if (mCurrentScreen > 0)
1735                    snapToScreen(mCurrentScreen - 1);
1736            } else {
1737                if (mNextScreen > 0)
1738                    snapToScreen(mNextScreen - 1);
1739            }
1740        }
1741    }
1742
1743    public void scrollRight() {
1744        if (!mIsSmall) {
1745            if (mScroller.isFinished()) {
1746                if (mCurrentScreen < getChildCount() - 1)
1747                    snapToScreen(mCurrentScreen + 1);
1748            } else {
1749                if (mNextScreen < getChildCount() - 1)
1750                    snapToScreen(mNextScreen + 1);
1751            }
1752        }
1753    }
1754
1755    public int getScreenForView(View v) {
1756        int result = -1;
1757        if (v != null) {
1758            ViewParent vp = v.getParent();
1759            final int screenCount = getChildCount();
1760            for (int i = 0; i < screenCount; i++) {
1761                if (vp == getChildAt(i)) {
1762                    return i;
1763                }
1764            }
1765        }
1766        return result;
1767    }
1768
1769    public Folder getFolderForTag(Object tag) {
1770        final int screenCount = getChildCount();
1771        for (int screen = 0; screen < screenCount; screen++) {
1772            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1773            int count = currentScreen.getChildCount();
1774            for (int i = 0; i < count; i++) {
1775                View child = currentScreen.getChildAt(i);
1776                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1777                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1778                    Folder f = (Folder) child;
1779                    if (f.getInfo() == tag && f.getInfo().opened) {
1780                        return f;
1781                    }
1782                }
1783            }
1784        }
1785        return null;
1786    }
1787
1788    public View getViewForTag(Object tag) {
1789        int screenCount = getChildCount();
1790        for (int screen = 0; screen < screenCount; screen++) {
1791            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1792            int count = currentScreen.getChildCount();
1793            for (int i = 0; i < count; i++) {
1794                View child = currentScreen.getChildAt(i);
1795                if (child.getTag() == tag) {
1796                    return child;
1797                }
1798            }
1799        }
1800        return null;
1801    }
1802
1803    /**
1804     * @return True is long presses are still allowed for the current touch
1805     */
1806    public boolean allowLongPress() {
1807        return mAllowLongPress;
1808    }
1809
1810    /**
1811     * Set true to allow long-press events to be triggered, usually checked by
1812     * {@link Launcher} to accept or block dpad-initiated long-presses.
1813     */
1814    public void setAllowLongPress(boolean allowLongPress) {
1815        mAllowLongPress = allowLongPress;
1816    }
1817
1818    void removeItems(final ArrayList<ApplicationInfo> apps) {
1819        final int screenCount = getChildCount();
1820        final PackageManager manager = getContext().getPackageManager();
1821        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
1822
1823        final HashSet<String> packageNames = new HashSet<String>();
1824        final int appCount = apps.size();
1825        for (int i = 0; i < appCount; i++) {
1826            packageNames.add(apps.get(i).componentName.getPackageName());
1827        }
1828
1829        for (int i = 0; i < screenCount; i++) {
1830            final CellLayout layout = (CellLayout) getChildAt(i);
1831
1832            // Avoid ANRs by treating each screen separately
1833            post(new Runnable() {
1834                public void run() {
1835                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
1836                    childrenToRemove.clear();
1837
1838                    int childCount = layout.getChildCount();
1839                    for (int j = 0; j < childCount; j++) {
1840                        final View view = layout.getChildAt(j);
1841                        Object tag = view.getTag();
1842
1843                        if (tag instanceof ShortcutInfo) {
1844                            final ShortcutInfo info = (ShortcutInfo) tag;
1845                            final Intent intent = info.intent;
1846                            final ComponentName name = intent.getComponent();
1847
1848                            if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1849                                for (String packageName: packageNames) {
1850                                    if (packageName.equals(name.getPackageName())) {
1851                                        // TODO: This should probably be done on a worker thread
1852                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1853                                        childrenToRemove.add(view);
1854                                    }
1855                                }
1856                            }
1857                        } else if (tag instanceof UserFolderInfo) {
1858                            final UserFolderInfo info = (UserFolderInfo) tag;
1859                            final ArrayList<ShortcutInfo> contents = info.contents;
1860                            final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
1861                            final int contentsCount = contents.size();
1862                            boolean removedFromFolder = false;
1863
1864                            for (int k = 0; k < contentsCount; k++) {
1865                                final ShortcutInfo appInfo = contents.get(k);
1866                                final Intent intent = appInfo.intent;
1867                                final ComponentName name = intent.getComponent();
1868
1869                                if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1870                                    for (String packageName: packageNames) {
1871                                        if (packageName.equals(name.getPackageName())) {
1872                                            toRemove.add(appInfo);
1873                                            // TODO: This should probably be done on a worker thread
1874                                            LauncherModel.deleteItemFromDatabase(
1875                                                    mLauncher, appInfo);
1876                                            removedFromFolder = true;
1877                                        }
1878                                    }
1879                                }
1880                            }
1881
1882                            contents.removeAll(toRemove);
1883                            if (removedFromFolder) {
1884                                final Folder folder = getOpenFolder();
1885                                if (folder != null)
1886                                    folder.notifyDataSetChanged();
1887                            }
1888                        } else if (tag instanceof LiveFolderInfo) {
1889                            final LiveFolderInfo info = (LiveFolderInfo) tag;
1890                            final Uri uri = info.uri;
1891                            final ProviderInfo providerInfo = manager.resolveContentProvider(
1892                                    uri.getAuthority(), 0);
1893
1894                            if (providerInfo != null) {
1895                                for (String packageName: packageNames) {
1896                                    if (packageName.equals(providerInfo.packageName)) {
1897                                        // TODO: This should probably be done on a worker thread
1898                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1899                                        childrenToRemove.add(view);
1900                                    }
1901                                }
1902                            }
1903                        } else if (tag instanceof LauncherAppWidgetInfo) {
1904                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1905                            final AppWidgetProviderInfo provider =
1906                                    widgets.getAppWidgetInfo(info.appWidgetId);
1907                            if (provider != null) {
1908                                for (String packageName: packageNames) {
1909                                    if (packageName.equals(provider.provider.getPackageName())) {
1910                                        // TODO: This should probably be done on a worker thread
1911                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1912                                        childrenToRemove.add(view);
1913                                    }
1914                                }
1915                            }
1916                        }
1917                    }
1918
1919                    childCount = childrenToRemove.size();
1920                    for (int j = 0; j < childCount; j++) {
1921                        View child = childrenToRemove.get(j);
1922                        layout.removeViewInLayout(child);
1923                        if (child instanceof DropTarget) {
1924                            mDragController.removeDropTarget((DropTarget)child);
1925                        }
1926                    }
1927
1928                    if (childCount > 0) {
1929                        layout.requestLayout();
1930                        layout.invalidate();
1931                    }
1932                }
1933            });
1934        }
1935    }
1936
1937    void updateShortcuts(ArrayList<ApplicationInfo> apps) {
1938        final PackageManager pm = mLauncher.getPackageManager();
1939
1940        final int screenCount = getChildCount();
1941        for (int i = 0; i < screenCount; i++) {
1942            final CellLayout layout = (CellLayout) getChildAt(i);
1943            int childCount = layout.getChildCount();
1944            for (int j = 0; j < childCount; j++) {
1945                final View view = layout.getChildAt(j);
1946                Object tag = view.getTag();
1947                if (tag instanceof ShortcutInfo) {
1948                    ShortcutInfo info = (ShortcutInfo)tag;
1949                    // We need to check for ACTION_MAIN otherwise getComponent() might
1950                    // return null for some shortcuts (for instance, for shortcuts to
1951                    // web pages.)
1952                    final Intent intent = info.intent;
1953                    final ComponentName name = intent.getComponent();
1954                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1955                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1956                        final int appCount = apps.size();
1957                        for (int k = 0; k < appCount; k++) {
1958                            ApplicationInfo app = apps.get(k);
1959                            if (app.componentName.equals(name)) {
1960                                info.setIcon(mIconCache.getIcon(info.intent));
1961                                ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
1962                                        new FastBitmapDrawable(info.getIcon(mIconCache)),
1963                                        null, null);
1964                                }
1965                        }
1966                    }
1967                }
1968            }
1969        }
1970    }
1971
1972    void moveToDefaultScreen(boolean animate) {
1973        if (animate) {
1974            if (mIsSmall) {
1975                unshrink(mDefaultScreen);
1976            } else {
1977                snapToScreen(mDefaultScreen);
1978            }
1979        } else {
1980            setCurrentScreen(mDefaultScreen);
1981        }
1982        getChildAt(mDefaultScreen).requestFocus();
1983    }
1984
1985    void setIndicators(Drawable previous, Drawable next) {
1986        mPreviousIndicator = previous;
1987        mNextIndicator = next;
1988        previous.setLevel(mCurrentScreen);
1989        next.setLevel(mCurrentScreen);
1990    }
1991
1992    public static class SavedState extends BaseSavedState {
1993        int currentScreen = -1;
1994
1995        SavedState(Parcelable superState) {
1996            super(superState);
1997        }
1998
1999        private SavedState(Parcel in) {
2000            super(in);
2001            currentScreen = in.readInt();
2002        }
2003
2004        @Override
2005        public void writeToParcel(Parcel out, int flags) {
2006            super.writeToParcel(out, flags);
2007            out.writeInt(currentScreen);
2008        }
2009
2010        public static final Parcelable.Creator<SavedState> CREATOR =
2011                new Parcelable.Creator<SavedState>() {
2012            public SavedState createFromParcel(Parcel in) {
2013                return new SavedState(in);
2014            }
2015
2016            public SavedState[] newArray(int size) {
2017                return new SavedState[size];
2018            }
2019        };
2020    }
2021}
2022