Workspace.java revision b1254a6bdb11024042fb6c27e179210c1bbd6e1c
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.launcher2;
18
19import android.animation.Animator;
20import android.animation.ObjectAnimator;
21import com.android.launcher.R;
22
23import android.animation.PropertyValuesHolder;
24import android.animation.AnimatorSet;
25import android.animation.Animator.AnimatorListener;
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.Rect;
39import android.graphics.drawable.Drawable;
40import android.net.Uri;
41import android.os.IBinder;
42import android.os.Parcelable;
43import android.util.AttributeSet;
44import android.util.Log;
45import android.view.MotionEvent;
46import android.view.View;
47import android.view.ViewGroup;
48import android.widget.TextView;
49import android.widget.Toast;
50
51import java.util.ArrayList;
52import java.util.HashSet;
53
54/**
55 * The workspace is a wide area with a wallpaper and a finite number of pages.
56 * Each page contains a number of icons, folders or widgets the user can
57 * interact with. A workspace is meant to be used with a fixed width only.
58 */
59public class Workspace extends SmoothPagedView
60        implements DropTarget, DragSource, DragScroller, View.OnTouchListener {
61    @SuppressWarnings({"UnusedDeclaration"})
62    private static final String TAG = "Launcher.Workspace";
63
64    // This is how much the workspace shrinks when we enter all apps or
65    // customization mode
66    private static final float SHRINK_FACTOR = 0.16f;
67    private enum ShrinkPosition { SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM };
68
69    private final WallpaperManager mWallpaperManager;
70
71    private int mDefaultPage;
72
73    private boolean mWaitingToShrinkToBottom = false;
74
75    /**
76     * CellInfo for the cell that is currently being dragged
77     */
78    private CellLayout.CellInfo mDragInfo;
79
80    /**
81     * Target drop area calculated during last acceptDrop call.
82     */
83    private int[] mTargetCell = null;
84
85    /**
86     * The CellLayout that is currently being dragged over
87     */
88    private CellLayout mDragTargetLayout = null;
89
90    private Launcher mLauncher;
91    private IconCache mIconCache;
92    private DragController mDragController;
93
94
95    private int[] mTempCell = new int[2];
96    private int[] mTempEstimate = new int[2];
97    private float[] mTempDragCoordinates = new float[2];
98    private float[] mTempDragBottomRightCoordinates = new float[2];
99
100    private static final int DEFAULT_CELL_COUNT_X = 4;
101    private static final int DEFAULT_CELL_COUNT_Y = 4;
102
103    private Drawable mPreviousIndicator;
104    private Drawable mNextIndicator;
105
106    // State variable that indicated whether the pages are small (ie when you're
107    // in all apps or customize mode)
108    private boolean mIsSmall;
109    private AnimatorListener mUnshrinkAnimationListener;
110
111
112    /**
113     * Used to inflate the Workspace from XML.
114     *
115     * @param context The application's context.
116     * @param attrs The attributes set containing the Workspace's customization values.
117     */
118    public Workspace(Context context, AttributeSet attrs) {
119        this(context, attrs, 0);
120    }
121
122    /**
123     * Used to inflate the Workspace from XML.
124     *
125     * @param context The application's context.
126     * @param attrs The attributes set containing the Workspace's customization values.
127     * @param defStyle Unused.
128     */
129    public Workspace(Context context, AttributeSet attrs, int defStyle) {
130        super(context, attrs, defStyle);
131        mContentIsRefreshable = false;
132        mFadeInAdjacentScreens = false;
133
134        mWallpaperManager = WallpaperManager.getInstance(context);
135
136        TypedArray a = context.obtainStyledAttributes(attrs,
137                R.styleable.Workspace, defStyle, 0);
138        int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X);
139        int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y);
140        mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
141        a.recycle();
142
143        LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
144        setHapticFeedbackEnabled(false);
145
146        initWorkspace();
147    }
148
149    /**
150     * Initializes various states for this workspace.
151     */
152    protected void initWorkspace() {
153        Context context = getContext();
154        mCurrentPage = mDefaultPage;
155        Launcher.setScreen(mCurrentPage);
156        LauncherApplication app = (LauncherApplication)context.getApplicationContext();
157        mIconCache = app.getIconCache();
158
159        mUnshrinkAnimationListener = new AnimatorListener() {
160            public void onAnimationStart(Animator animation) {}
161            public void onAnimationEnd(Animator animation) {
162                mIsSmall = false;
163            }
164            public void onAnimationCancel(Animator animation) {}
165            public void onAnimationRepeat(Animator animation) {}
166        };
167
168        mSnapVelocity = 600;
169    }
170
171    @Override
172    public void addView(View child, int index, LayoutParams params) {
173        if (!(child instanceof CellLayout)) {
174            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
175        }
176        super.addView(child, index, params);
177    }
178
179    @Override
180    public void addView(View child) {
181        if (!(child instanceof CellLayout)) {
182            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
183        }
184        super.addView(child);
185    }
186
187    @Override
188    public void addView(View child, int index) {
189        if (!(child instanceof CellLayout)) {
190            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
191        }
192        super.addView(child, index);
193    }
194
195    @Override
196    public void addView(View child, int width, int height) {
197        if (!(child instanceof CellLayout)) {
198            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
199        }
200        super.addView(child, width, height);
201    }
202
203    @Override
204    public void addView(View child, LayoutParams params) {
205        if (!(child instanceof CellLayout)) {
206            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
207        }
208        super.addView(child, params);
209    }
210
211    /**
212     * @return The open folder on the current screen, or null if there is none
213     */
214    Folder getOpenFolder() {
215        CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
216        int count = currentPage.getChildCount();
217        for (int i = 0; i < count; i++) {
218            View child = currentPage.getChildAt(i);
219            if (child instanceof Folder) {
220                Folder folder = (Folder) child;
221                if (folder.getInfo().opened)
222                    return folder;
223            }
224        }
225        return null;
226    }
227
228    ArrayList<Folder> getOpenFolders() {
229        final int screenCount = getChildCount();
230        ArrayList<Folder> folders = new ArrayList<Folder>(screenCount);
231
232        for (int screen = 0; screen < screenCount; screen++) {
233            CellLayout currentPage = (CellLayout) getChildAt(screen);
234            int count = currentPage.getChildCount();
235            for (int i = 0; i < count; i++) {
236                View child = currentPage.getChildAt(i);
237                if (child instanceof Folder) {
238                    Folder folder = (Folder) child;
239                    if (folder.getInfo().opened)
240                        folders.add(folder);
241                    break;
242                }
243            }
244        }
245
246        return folders;
247    }
248
249    boolean isDefaultPageShowing() {
250        return mCurrentPage == mDefaultPage;
251    }
252
253    /**
254     * Sets the current screen.
255     *
256     * @param currentPage
257     */
258    @Override
259    void setCurrentPage(int currentPage) {
260        super.setCurrentPage(currentPage);
261        updateWallpaperOffset(mScrollX);
262    }
263
264    /**
265     * Adds the specified child in the current screen. The position and dimension of
266     * the child are defined by x, y, spanX and spanY.
267     *
268     * @param child The child to add in one of the workspace's screens.
269     * @param x The X position of the child in the screen's grid.
270     * @param y The Y position of the child in the screen's grid.
271     * @param spanX The number of cells spanned horizontally by the child.
272     * @param spanY The number of cells spanned vertically by the child.
273     */
274    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
275        addInScreen(child, mCurrentPage, x, y, spanX, spanY, false);
276    }
277
278    /**
279     * Adds the specified child in the current screen. The position and dimension of
280     * the child are defined by x, y, spanX and spanY.
281     *
282     * @param child The child to add in one of the workspace's screens.
283     * @param x The X position of the child in the screen's grid.
284     * @param y The Y position of the child in the screen's grid.
285     * @param spanX The number of cells spanned horizontally by the child.
286     * @param spanY The number of cells spanned vertically by the child.
287     * @param insert When true, the child is inserted at the beginning of the children list.
288     */
289    void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
290        addInScreen(child, mCurrentPage, x, y, spanX, spanY, insert);
291    }
292
293    /**
294     * Adds the specified child in the specified screen. The position and dimension of
295     * the child are defined by x, y, spanX and spanY.
296     *
297     * @param child The child to add in one of the workspace's screens.
298     * @param screen The screen in which to add the child.
299     * @param x The X position of the child in the screen's grid.
300     * @param y The Y position of the child in the screen's grid.
301     * @param spanX The number of cells spanned horizontally by the child.
302     * @param spanY The number of cells spanned vertically by the child.
303     */
304    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
305        addInScreen(child, screen, x, y, spanX, spanY, false);
306    }
307
308    void addInFullScreen(View child, int screen) {
309        addInScreen(child, screen, 0, 0, -1, -1);
310    }
311
312    /**
313     * Adds the specified child in the specified screen. The position and dimension of
314     * the child are defined by x, y, spanX and spanY.
315     *
316     * @param child The child to add in one of the workspace's screens.
317     * @param screen The screen in which to add the child.
318     * @param x The X position of the child in the screen's grid.
319     * @param y The Y position of the child in the screen's grid.
320     * @param spanX The number of cells spanned horizontally by the child.
321     * @param spanY The number of cells spanned vertically by the child.
322     * @param insert When true, the child is inserted at the beginning of the children list.
323     */
324    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
325        if (screen < 0 || screen >= getChildCount()) {
326            Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
327                + " (was " + screen + "); skipping child");
328            return;
329        }
330
331        final CellLayout group = (CellLayout) getChildAt(screen);
332        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
333        if (lp == null) {
334            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
335        } else {
336            lp.cellX = x;
337            lp.cellY = y;
338            lp.cellHSpan = spanX;
339            lp.cellVSpan = spanY;
340        }
341
342        // Get the canonical child id to uniquely represent this view in this screen
343        int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY);
344        if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) {
345            // TODO: This branch occurs when the workspace is adding views
346            // outside of the defined grid
347            // maybe we should be deleting these items from the LauncherModel?
348            Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
349        }
350
351        if (!(child instanceof Folder)) {
352            child.setHapticFeedbackEnabled(false);
353            child.setOnLongClickListener(mLongClickListener);
354        }
355        if (child instanceof DropTarget) {
356            mDragController.addDropTarget((DropTarget) child);
357        }
358    }
359
360    CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) {
361        CellLayout group = (CellLayout) getChildAt(mCurrentPage);
362        if (group != null) {
363            return group.updateOccupiedCells(occupied, null);
364        }
365        return null;
366    }
367
368    public boolean onTouch(View v, MotionEvent event) {
369        // this is an intercepted event being forwarded from a cell layout
370        if (mIsSmall) {
371            unshrink((CellLayout)v);
372            mLauncher.onWorkspaceUnshrink();
373            return true;
374        }
375        return false;
376    }
377
378    protected void pageBeginMoving() {
379        if (mNextPage != INVALID_PAGE) {
380            // we're snapping to a particular screen
381            enableChildrenCache(mCurrentPage, mNextPage);
382        } else {
383            // this is when user is actively dragging a particular screen, they might
384            // swipe it either left or right (but we won't advance by more than one screen)
385            enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
386        }
387    }
388
389    protected void pageEndMoving() {
390        clearChildrenCache();
391    }
392
393    @Override
394    protected void notifyPageSwitchListener() {
395        super.notifyPageSwitchListener();
396
397        if (mPreviousIndicator != null) {
398            // if we know the next page, we show the indication for it right away; it looks
399            // weird if the indicators are lagging
400            int page = mNextPage;
401            if (page == INVALID_PAGE) {
402                page = mCurrentPage;
403            }
404            mPreviousIndicator.setLevel(page);
405            mNextIndicator.setLevel(page);
406        }
407        Launcher.setScreen(mCurrentPage);
408    };
409
410    private void updateWallpaperOffset() {
411        updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
412    }
413
414    private void updateWallpaperOffset(int scrollRange) {
415        final boolean isStaticWallpaper = (mWallpaperManager != null) &&
416                (mWallpaperManager.getWallpaperInfo() == null);
417        if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) {
418            IBinder token = getWindowToken();
419            if (token != null) {
420                mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
421                mWallpaperManager.setWallpaperOffsets(getWindowToken(),
422                        Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
423            }
424        }
425    }
426
427    protected void onAttachedToWindow() {
428        super.onAttachedToWindow();
429        computeScroll();
430        mDragController.setWindowToken(getWindowToken());
431    }
432
433    @Override
434    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
435        super.onLayout(changed, left, top, right, bottom);
436
437        // if shrinkToBottom() is called on initialization, it has to be deferred
438        // until after the first call to onLayout so that it has the correct width
439        if (mWaitingToShrinkToBottom) {
440            shrinkToBottom(false);
441            mWaitingToShrinkToBottom = false;
442        }
443
444        if (LauncherApplication.isInPlaceRotationEnabled()) {
445            // When the device is rotated, the scroll position of the current screen
446            // needs to be refreshed
447            setCurrentPage(getCurrentPage());
448        }
449    }
450
451    @Override
452    protected void dispatchDraw(Canvas canvas) {
453        if (mIsSmall) {
454            // Draw all the workspaces if we're small
455            final int pageCount = getChildCount();
456            final long drawingTime = getDrawingTime();
457            for (int i = 0; i < pageCount; i++) {
458                final View page = (View) getChildAt(i);
459
460                if (page.getAlpha() != 1.0f) {
461                    page.setAlpha(1.0f);
462                }
463                drawChild(canvas, page, drawingTime);
464            }
465        } else {
466            super.dispatchDraw(canvas);
467        }
468    }
469
470    @Override
471    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
472        if (!mLauncher.isAllAppsVisible()) {
473            final Folder openFolder = getOpenFolder();
474            if (openFolder != null) {
475                return openFolder.requestFocus(direction, previouslyFocusedRect);
476            } else {
477                return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
478            }
479        }
480        return false;
481    }
482
483    @Override
484    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
485        if (!mLauncher.isAllAppsVisible()) {
486            final Folder openFolder = getOpenFolder();
487            if (openFolder != null) {
488                openFolder.addFocusables(views, direction);
489            } else {
490                super.addFocusables(views, direction, focusableMode);
491            }
492        }
493    }
494
495    @Override
496    public boolean dispatchTouchEvent(MotionEvent ev) {
497        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
498            // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
499            // ie when you click on a mini-screen, it zooms back to that screen)
500            if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) {
501                return false;
502            }
503        }
504        return super.dispatchTouchEvent(ev);
505    }
506
507    void enableChildrenCache(int fromPage, int toPage) {
508        if (fromPage > toPage) {
509            final int temp = fromPage;
510            fromPage = toPage;
511            toPage = temp;
512        }
513
514        final int screenCount = getChildCount();
515
516        fromPage = Math.max(fromPage, 0);
517        toPage = Math.min(toPage, screenCount - 1);
518
519        for (int i = fromPage; i <= toPage; i++) {
520            final CellLayout layout = (CellLayout) getChildAt(i);
521            layout.setChildrenDrawnWithCacheEnabled(true);
522            layout.setChildrenDrawingCacheEnabled(true);
523        }
524    }
525
526    void clearChildrenCache() {
527        final int screenCount = getChildCount();
528        for (int i = 0; i < screenCount; i++) {
529            final CellLayout layout = (CellLayout) getChildAt(i);
530            layout.setChildrenDrawnWithCacheEnabled(false);
531        }
532    }
533
534    @Override
535    public boolean onTouchEvent(MotionEvent ev) {
536        if (mLauncher.isAllAppsVisible()) {
537            // Cancel any scrolling that is in progress.
538            if (!mScroller.isFinished()) {
539                mScroller.abortAnimation();
540            }
541            snapToPage(mCurrentPage);
542            return false; // We don't want the events.  Let them fall through to the all apps view.
543        }
544
545        return super.onTouchEvent(ev);
546    }
547
548    public boolean isSmall() {
549        return mIsSmall;
550    }
551
552    void shrinkToTop(boolean animated) {
553        shrink(ShrinkPosition.SHRINK_TO_TOP, animated);
554    }
555
556    void shrinkToMiddle() {
557        shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true);
558    }
559
560    void shrinkToBottom() {
561        shrinkToBottom(true);
562    }
563
564    void shrinkToBottom(boolean animated) {
565        if (mFirstLayout) {
566            // (mFirstLayout == "first layout has not happened yet")
567            // if we get a call to shrink() as part of our initialization (for example, if
568            // Launcher is started in All Apps mode) then we need to wait for a layout call
569            // to get our width so we can layout the mini-screen views correctly
570            mWaitingToShrinkToBottom = true;
571        } else {
572            shrink(ShrinkPosition.SHRINK_TO_BOTTOM, animated);
573        }
574    }
575
576    // we use this to shrink the workspace for the all apps view and the customize view
577    private void shrink(ShrinkPosition shrinkPosition, boolean animated) {
578        mIsSmall = true;
579        final Resources res = getResources();
580        final int screenWidth = getWidth();
581        final int screenHeight = getHeight();
582
583        // Making the assumption that all pages have the same width as the 0th
584        final int pageWidth = getChildAt(0).getMeasuredWidth();
585        final int pageHeight = getChildAt(0).getMeasuredHeight();
586
587        final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth);
588        final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight);
589        final float scaledSpacing = res.getDimension(R.dimen.smallScreenSpacing);
590
591        final int screenCount = getChildCount();
592        float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * scaledSpacing;
593
594        float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
595        if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM) {
596            newY = screenHeight - newY - scaledPageHeight;
597        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) {
598            newY = screenHeight / 2 - scaledPageHeight / 2;
599        }
600
601        // We animate all the screens to the centered position in workspace
602        // At the same time, the screens become greyed/dimmed
603
604        // newX is initialized to the left-most position of the centered screens
605        float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2;
606        for (int i = 0; i < screenCount; i++) {
607            CellLayout cl = (CellLayout) getChildAt(i);
608            cl.setPivotX(0.0f);
609            cl.setPivotY(0.0f);
610            if (animated) {
611                final int duration = res.getInteger(R.integer.config_workspaceShrinkTime);
612                new ObjectAnimator(duration, cl,
613                        new PropertyValuesHolder("x", newX),
614                        new PropertyValuesHolder("y", newY),
615                        new PropertyValuesHolder("scaleX", SHRINK_FACTOR),
616                        new PropertyValuesHolder("scaleY", SHRINK_FACTOR),
617                        new PropertyValuesHolder("dimmedBitmapAlpha", 1.0f)).start();
618            } else {
619                cl.setX((int)newX);
620                cl.setY((int)newY);
621                cl.setScaleX(SHRINK_FACTOR);
622                cl.setScaleY(SHRINK_FACTOR);
623                cl.setDimmedBitmapAlpha(1.0f);
624            }
625            // increment newX for the next screen
626            newX += scaledPageWidth + scaledSpacing;
627            cl.setOnInterceptTouchListener(this);
628        }
629        setChildrenDrawnWithCacheEnabled(true);
630    }
631
632    // We call this when we trigger an unshrink by clicking on the CellLayout cl
633    private void unshrink(CellLayout clThatWasClicked) {
634        int newCurrentPage = mCurrentPage;
635        final int screenCount = getChildCount();
636        for (int i = 0; i < screenCount; i++) {
637            if (getChildAt(i) == clThatWasClicked) {
638                newCurrentPage = i;
639            }
640        }
641        unshrink(newCurrentPage);
642    }
643
644    private void unshrink(int newCurrentPage) {
645        if (mIsSmall) {
646            int delta = (newCurrentPage - mCurrentPage)*getWidth();
647
648            final int screenCount = getChildCount();
649            for (int i = 0; i < screenCount; i++) {
650                CellLayout cl = (CellLayout) getChildAt(i);
651                cl.setX(cl.getX() + delta);
652            }
653            snapToPage(newCurrentPage);
654            unshrink();
655
656            setCurrentPage(newCurrentPage);
657        }
658    }
659
660    void unshrink() {
661        unshrink(true);
662    }
663
664    void unshrink(boolean animated) {
665        if (mIsSmall) {
666            AnimatorSet s = new AnimatorSet();
667            final int screenCount = getChildCount();
668
669            final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
670            for (int i = 0; i < screenCount; i++) {
671                final CellLayout cl = (CellLayout)getChildAt(i);
672                cl.setPivotX(0.0f);
673                cl.setPivotY(0.0f);
674                if (animated) {
675                    s.playTogether(
676                            new ObjectAnimator<Float>(duration, cl, "translationX", 0.0f),
677                            new ObjectAnimator<Float>(duration, cl, "translationY", 0.0f),
678                            new ObjectAnimator<Float>(duration, cl, "scaleX", 1.0f),
679                            new ObjectAnimator<Float>(duration, cl, "scaleY", 1.0f),
680                            new ObjectAnimator<Float>(duration, cl, "dimmedBitmapAlpha", 0.0f));
681                } else {
682                    cl.setTranslationX(0.0f);
683                    cl.setTranslationY(0.0f);
684                    cl.setScaleX(1.0f);
685                    cl.setScaleY(1.0f);
686                    cl.setDimmedBitmapAlpha(0.0f);
687                }
688            }
689            s.addListener(mUnshrinkAnimationListener);
690            s.start();
691        }
692    }
693
694    void startDrag(CellLayout.CellInfo cellInfo) {
695        View child = cellInfo.cell;
696
697        // Make sure the drag was started by a long press as opposed to a long click.
698        if (!child.isInTouchMode()) {
699            return;
700        }
701
702        mDragInfo = cellInfo;
703        mDragInfo.screen = mCurrentPage;
704
705        CellLayout current = ((CellLayout) getChildAt(mCurrentPage));
706
707        current.onDragChild(child);
708        mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
709        invalidate();
710    }
711
712    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
713        addApplicationShortcut(info, cellInfo, false);
714    }
715
716    void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo,
717            boolean insertAtFirst) {
718        final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
719        final int[] result = new int[2];
720
721        layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
722        onDropExternal(result[0], result[1], info, layout, insertAtFirst);
723    }
724
725    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
726            DragView dragView, Object dragInfo) {
727        CellLayout cellLayout = getCurrentDropLayout();
728        int originX = x - xOffset;
729        int originY = y - yOffset;
730        if (mIsSmall) {
731            // find out which target layout is over
732            final float[] localXY = mTempDragCoordinates;
733            localXY[0] = originX;
734            localXY[1] = originY;
735            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
736            // we need to subtract left/top here because DragController already adds
737            // dragRegionLeft/Top to xOffset and yOffset
738            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
739            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
740            cellLayout =  findMatchingPageForDragOver(localXY, localBottomRightXY);
741            if (cellLayout == null) {
742                // cancel the drag if we're not over a mini-screen at time of drop
743                // TODO: maybe add a nice fade here?
744                return;
745            }
746            // localXY will be transformed into the local screen's coordinate space; save that info
747            originX = (int)localXY[0];
748            originY = (int)localXY[1];
749        }
750        if (source != this) {
751            onDropExternal(originX, originY, dragInfo, cellLayout);
752        } else {
753            // Move internally
754            if (mDragInfo != null) {
755                final View cell = mDragInfo.cell;
756                int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
757                if (index != mDragInfo.screen) {
758                    final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
759                    originalCellLayout.removeView(cell);
760                    addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY,
761                            mDragInfo.spanX, mDragInfo.spanY);
762                }
763
764                mTargetCell = estimateDropCell(originX, originY,
765                        mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout,
766                        mTargetCell);
767                cellLayout.onDropChild(cell);
768
769                // update the item's position after drop
770                final ItemInfo info = (ItemInfo) cell.getTag();
771                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell
772                        .getLayoutParams();
773                lp.cellX = mTargetCell[0];
774                lp.cellY = mTargetCell[1];
775
776                LauncherModel.moveItemInDatabase(mLauncher, info,
777                        LauncherSettings.Favorites.CONTAINER_DESKTOP, index,
778                        lp.cellX, lp.cellY);
779            }
780        }
781    }
782
783    public void onDragEnter(DragSource source, int x, int y, int xOffset,
784            int yOffset, DragView dragView, Object dragInfo) {
785    }
786
787    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
788            DragView dragView, Object dragInfo) {
789
790        // We may need to delegate the drag to a child view. If a 1x1 item
791        // would land in a cell occupied by a DragTarget (e.g. a Folder),
792        // then drag events should be handled by that child.
793
794        ItemInfo item = (ItemInfo)dragInfo;
795        CellLayout currentLayout = getCurrentDropLayout();
796
797        int dragPointX, dragPointY;
798        if (item.spanX == 1 && item.spanY == 1) {
799            // For a 1x1, calculate the drop cell exactly as in onDragOver
800            dragPointX = x - xOffset;
801            dragPointY = y - yOffset;
802        } else {
803            // Otherwise, use the exact drag coordinates
804            dragPointX = x;
805            dragPointY = y;
806        }
807
808        // If we are dragging over a cell that contains a DropTarget that will
809        // accept the drop, delegate to that DropTarget.
810        final int[] cellXY = mTempCell;
811        currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
812        View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
813        if (child instanceof DropTarget) {
814            DropTarget target = (DropTarget)child;
815            if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
816                return target;
817            }
818        }
819        return null;
820    }
821
822    // xy = upper left corner of item being dragged
823    // bottomRightXy = lower right corner of item being dragged
824    // This method will see which mini-screen is most overlapped by the item being dragged, and
825    // return it. It will also transform the parameters xy and bottomRightXy into the local
826    // coordinate space of the returned screen
827    private CellLayout findMatchingPageForDragOver(float[] xy, float[] bottomRightXy) {
828        float x = xy[0];
829        float y = xy[1];
830        float right = bottomRightXy[0];
831        float bottom = bottomRightXy[1];
832
833        float bestX = 0;
834        float bestY = 0;
835        float bestRight = 0;
836        float bestBottom = 0;
837
838        Matrix inverseMatrix = new Matrix();
839
840        // We loop through all the screens (ie CellLayouts) and see which one overlaps the most
841        // with the item being dragged.
842        final int screenCount = getChildCount();
843        CellLayout bestMatchingScreen = null;
844        float bestOverlapSoFar = 0;
845        for (int i = 0; i < screenCount; i++) {
846            CellLayout cl = (CellLayout)getChildAt(i);
847            // Transform the coordinates of the item being dragged to the CellLayout's coordinates
848            float left = cl.getLeft();
849            float top = cl.getTop();
850            xy[0] = x + mScrollX - left;
851            xy[1] = y + mScrollY - top;
852            cl.getMatrix().invert(inverseMatrix);
853
854            bottomRightXy[0] = right + mScrollX - left;
855            bottomRightXy[1] = bottom + mScrollY - top;
856
857            inverseMatrix.mapPoints(xy);
858            inverseMatrix.mapPoints(bottomRightXy);
859
860            float dragRegionX = xy[0];
861            float dragRegionY = xy[1];
862            float dragRegionRight = bottomRightXy[0];
863            float dragRegionBottom = bottomRightXy[1];
864
865            // Find the overlapping region
866            float overlapLeft = Math.max(0f, dragRegionX);
867            float overlapTop = Math.max(0f, dragRegionY);
868            float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom);
869            float overlapRight = Math.min(cl.getWidth(), dragRegionRight);
870
871            if (overlapRight >= 0 && overlapLeft <= cl.getWidth() &&
872                    overlapTop >= 0 && overlapBottom <= cl.getHeight()) {
873                // Calculate the size of the overlapping region
874                float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
875                if (overlap > bestOverlapSoFar) {
876                    bestOverlapSoFar = overlap;
877                    bestMatchingScreen = cl;
878                    bestX = xy[0];
879                    bestY = xy[1];
880                    bestRight = bottomRightXy[0];
881                    bestBottom = bottomRightXy[1];
882                }
883             }
884        }
885        if (bestMatchingScreen != null && bestMatchingScreen != mDragTargetLayout) {
886            if (mDragTargetLayout != null) {
887                mDragTargetLayout.onDragComplete();
888            }
889            mDragTargetLayout = bestMatchingScreen;
890        }
891        xy[0] = bestX;
892        xy[1] = bestY;
893        bottomRightXy[0] = bestRight;
894        bottomRightXy[1] = bestBottom;
895        return bestMatchingScreen;
896    }
897
898    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
899            DragView dragView, Object dragInfo) {
900
901        final ItemInfo item = (ItemInfo)dragInfo;
902        CellLayout currentLayout = getCurrentDropLayout();
903
904        if (dragInfo instanceof LauncherAppWidgetInfo) {
905            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
906
907            if (widgetInfo.spanX == -1) {
908                // Calculate the grid spans needed to fit this widget
909                int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null);
910                item.spanX = spans[0];
911                item.spanY = spans[1];
912            }
913        }
914        int originX = x - xOffset;
915        int originY = y - yOffset;
916        if (mIsSmall) {
917            // find out which mini screen the dragged item is over
918            final float[] localXY = mTempDragCoordinates;
919            localXY[0] = originX;
920            localXY[1] = originY;
921            final float[] localBottomRightXY = mTempDragBottomRightCoordinates;
922
923            localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
924            localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
925            currentLayout = findMatchingPageForDragOver(localXY, localBottomRightXY);
926            if (currentLayout != null) {
927                currentLayout.setHover(true);
928            }
929
930            originX = (int)localXY[0];
931            originY = (int)localXY[1];
932        }
933
934        if (source != this) {
935            // This is a hack to fix the point used to determine which cell an icon from the all
936            // apps screen is over
937            if (item != null && item.spanX == 1 && currentLayout != null) {
938                int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2;
939
940                originX += dragRegionLeft - dragView.getDragRegionLeft();
941                if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) {
942                    dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(),
943                            currentLayout.getCellWidth(), dragView.getDragRegionHeight());
944                }
945            }
946        }
947        if (currentLayout != mDragTargetLayout) {
948            if (mDragTargetLayout != null) {
949                mDragTargetLayout.onDragComplete();
950            }
951            mDragTargetLayout = currentLayout;
952        }
953
954        // only visualize the drop locations for moving icons within the home screen on tablet
955        // on phone, we also visualize icons dragged in from All Apps
956        if ((!LauncherApplication.isScreenXLarge() || source == this)
957                && mDragTargetLayout != null) {
958            final View child = (mDragInfo == null) ? null : mDragInfo.cell;
959            mDragTargetLayout.visualizeDropLocation(
960                    child, originX, originY, item.spanX, item.spanY);
961        }
962    }
963
964    public void onDragExit(DragSource source, int x, int y, int xOffset,
965            int yOffset, DragView dragView, Object dragInfo) {
966        if (mDragTargetLayout != null) {
967            mDragTargetLayout.onDragComplete();
968            mDragTargetLayout = null;
969        }
970    }
971
972    private void onDropExternal(int x, int y, Object dragInfo,
973            CellLayout cellLayout) {
974        onDropExternal(x, y, dragInfo, cellLayout, false);
975    }
976
977    private void onDropExternal(int x, int y, Object dragInfo,
978            CellLayout cellLayout, boolean insertAtFirst) {
979        // Drag from somewhere else
980        ItemInfo info = (ItemInfo) dragInfo;
981
982        View view = null;
983
984        switch (info.itemType) {
985        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
986        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
987            if (info.container == NO_ID && info instanceof ApplicationInfo) {
988                // Came from all apps -- make a copy
989                info = new ShortcutInfo((ApplicationInfo) info);
990            }
991            view = mLauncher.createShortcut(R.layout.application, cellLayout,
992                    (ShortcutInfo) info);
993            break;
994        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
995            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
996                    (ViewGroup) getChildAt(mCurrentPage),
997                    ((UserFolderInfo) info));
998            break;
999        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1000            cellLayout.setTagToCellInfoForPoint(x, y);
1001            int[] position = new int[2];
1002            position[0] = x;
1003            position[1] = y;
1004            mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName,
1005                    cellLayout.getTag(), position);
1006            break;
1007        default:
1008            throw new IllegalStateException("Unknown item type: "
1009                    + info.itemType);
1010        }
1011
1012        // If the view is null, it has already been added.
1013        if (view == null) {
1014            cellLayout.onDragComplete();
1015        } else {
1016            mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
1017            addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
1018                    mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
1019            cellLayout.onDropChild(view);
1020            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1021
1022            LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1023                    LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage,
1024                    lp.cellX, lp.cellY);
1025        }
1026    }
1027
1028    /**
1029     * Return the current {@link CellLayout}, correctly picking the destination
1030     * screen while a scroll is in progress.
1031     */
1032    private CellLayout getCurrentDropLayout() {
1033        int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
1034        return (CellLayout) getChildAt(index);
1035    }
1036
1037    /**
1038     * {@inheritDoc}
1039     */
1040    public boolean acceptDrop(DragSource source, int x, int y,
1041            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1042        final CellLayout layout = getCurrentDropLayout();
1043        final CellLayout.CellInfo dragCellInfo = mDragInfo;
1044        final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
1045        final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
1046
1047        final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
1048        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
1049
1050        if (cellInfo.findCellForSpan(mTempEstimate, spanX, spanY)) {
1051            return true;
1052        } else {
1053            Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
1054            return false;
1055        }
1056    }
1057
1058    /**
1059     * {@inheritDoc}
1060     */
1061    public Rect estimateDropLocation(DragSource source, int x, int y,
1062            int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
1063        final CellLayout layout = getCurrentDropLayout();
1064
1065        final CellLayout.CellInfo cellInfo = mDragInfo;
1066        final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
1067        final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
1068        final View ignoreView = cellInfo == null ? null : cellInfo.cell;
1069
1070        final Rect location = recycle != null ? recycle : new Rect();
1071
1072        // Find drop cell and convert into rectangle
1073        int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX,
1074                spanY, ignoreView, layout, mTempCell);
1075
1076        if (dropCell == null) {
1077            return null;
1078        }
1079
1080        layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
1081        location.left = mTempEstimate[0];
1082        location.top = mTempEstimate[1];
1083
1084        layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
1085        location.right = mTempEstimate[0];
1086        location.bottom = mTempEstimate[1];
1087
1088        return location;
1089    }
1090
1091    /**
1092     * Calculate the nearest cell where the given object would be dropped.
1093     */
1094    private int[] estimateDropCell(int pixelX, int pixelY,
1095            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1096
1097        final int[] cellXY = mTempCell;
1098        layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY);
1099        layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate);
1100
1101        final CellLayout.CellInfo cellInfo = layout.updateOccupiedCells(null, ignoreView);
1102        // Find the best target drop location
1103        return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, cellInfo, recycle);
1104    }
1105
1106    /**
1107     * Estimate the size that a child with the given dimensions will take in the current screen.
1108     */
1109    void estimateChildSize(int minWidth, int minHeight, int[] result) {
1110        ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result);
1111    }
1112
1113    void setLauncher(Launcher launcher) {
1114        mLauncher = launcher;
1115    }
1116
1117    public void setDragController(DragController dragController) {
1118        mDragController = dragController;
1119    }
1120
1121    public void onDropCompleted(View target, boolean success) {
1122        if (success) {
1123            if (target != this && mDragInfo != null) {
1124                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1125                cellLayout.removeView(mDragInfo.cell);
1126                if (mDragInfo.cell instanceof DropTarget) {
1127                    mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1128                }
1129                // final Object tag = mDragInfo.cell.getTag();
1130            }
1131        } else {
1132            if (mDragInfo != null) {
1133                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1134                cellLayout.onDropAborted(mDragInfo.cell);
1135            }
1136        }
1137
1138        mDragInfo = null;
1139    }
1140
1141    @Override
1142    protected void onRestoreInstanceState(Parcelable state) {
1143        super.onRestoreInstanceState(state);
1144        Launcher.setScreen(mCurrentPage);
1145    }
1146
1147    @Override
1148    public void scrollLeft() {
1149        if (!mIsSmall) {
1150            super.scrollLeft();
1151        }
1152    }
1153
1154    @Override
1155    public void scrollRight() {
1156        if (!mIsSmall) {
1157            super.scrollRight();
1158        }
1159    }
1160
1161    public Folder getFolderForTag(Object tag) {
1162        final int screenCount = getChildCount();
1163        for (int screen = 0; screen < screenCount; screen++) {
1164            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1165            int count = currentScreen.getChildCount();
1166            for (int i = 0; i < count; i++) {
1167                View child = currentScreen.getChildAt(i);
1168                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1169                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1170                    Folder f = (Folder) child;
1171                    if (f.getInfo() == tag && f.getInfo().opened) {
1172                        return f;
1173                    }
1174                }
1175            }
1176        }
1177        return null;
1178    }
1179
1180    public View getViewForTag(Object tag) {
1181        int screenCount = getChildCount();
1182        for (int screen = 0; screen < screenCount; screen++) {
1183            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1184            int count = currentScreen.getChildCount();
1185            for (int i = 0; i < count; i++) {
1186                View child = currentScreen.getChildAt(i);
1187                if (child.getTag() == tag) {
1188                    return child;
1189                }
1190            }
1191        }
1192        return null;
1193    }
1194
1195
1196    void removeItems(final ArrayList<ApplicationInfo> apps) {
1197        final int screenCount = getChildCount();
1198        final PackageManager manager = getContext().getPackageManager();
1199        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
1200
1201        final HashSet<String> packageNames = new HashSet<String>();
1202        final int appCount = apps.size();
1203        for (int i = 0; i < appCount; i++) {
1204            packageNames.add(apps.get(i).componentName.getPackageName());
1205        }
1206
1207        for (int i = 0; i < screenCount; i++) {
1208            final CellLayout layout = (CellLayout) getChildAt(i);
1209
1210            // Avoid ANRs by treating each screen separately
1211            post(new Runnable() {
1212                public void run() {
1213                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
1214                    childrenToRemove.clear();
1215
1216                    int childCount = layout.getChildCount();
1217                    for (int j = 0; j < childCount; j++) {
1218                        final View view = layout.getChildAt(j);
1219                        Object tag = view.getTag();
1220
1221                        if (tag instanceof ShortcutInfo) {
1222                            final ShortcutInfo info = (ShortcutInfo) tag;
1223                            final Intent intent = info.intent;
1224                            final ComponentName name = intent.getComponent();
1225
1226                            if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1227                                for (String packageName: packageNames) {
1228                                    if (packageName.equals(name.getPackageName())) {
1229                                        // TODO: This should probably be done on a worker thread
1230                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1231                                        childrenToRemove.add(view);
1232                                    }
1233                                }
1234                            }
1235                        } else if (tag instanceof UserFolderInfo) {
1236                            final UserFolderInfo info = (UserFolderInfo) tag;
1237                            final ArrayList<ShortcutInfo> contents = info.contents;
1238                            final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
1239                            final int contentsCount = contents.size();
1240                            boolean removedFromFolder = false;
1241
1242                            for (int k = 0; k < contentsCount; k++) {
1243                                final ShortcutInfo appInfo = contents.get(k);
1244                                final Intent intent = appInfo.intent;
1245                                final ComponentName name = intent.getComponent();
1246
1247                                if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1248                                    for (String packageName: packageNames) {
1249                                        if (packageName.equals(name.getPackageName())) {
1250                                            toRemove.add(appInfo);
1251                                            // TODO: This should probably be done on a worker thread
1252                                            LauncherModel.deleteItemFromDatabase(
1253                                                    mLauncher, appInfo);
1254                                            removedFromFolder = true;
1255                                        }
1256                                    }
1257                                }
1258                            }
1259
1260                            contents.removeAll(toRemove);
1261                            if (removedFromFolder) {
1262                                final Folder folder = getOpenFolder();
1263                                if (folder != null)
1264                                    folder.notifyDataSetChanged();
1265                            }
1266                        } else if (tag instanceof LiveFolderInfo) {
1267                            final LiveFolderInfo info = (LiveFolderInfo) tag;
1268                            final Uri uri = info.uri;
1269                            final ProviderInfo providerInfo = manager.resolveContentProvider(
1270                                    uri.getAuthority(), 0);
1271
1272                            if (providerInfo != null) {
1273                                for (String packageName: packageNames) {
1274                                    if (packageName.equals(providerInfo.packageName)) {
1275                                        // TODO: This should probably be done on a worker thread
1276                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1277                                        childrenToRemove.add(view);
1278                                    }
1279                                }
1280                            }
1281                        } else if (tag instanceof LauncherAppWidgetInfo) {
1282                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1283                            final AppWidgetProviderInfo provider =
1284                                    widgets.getAppWidgetInfo(info.appWidgetId);
1285                            if (provider != null) {
1286                                for (String packageName: packageNames) {
1287                                    if (packageName.equals(provider.provider.getPackageName())) {
1288                                        // TODO: This should probably be done on a worker thread
1289                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1290                                        childrenToRemove.add(view);
1291                                    }
1292                                }
1293                            }
1294                        }
1295                    }
1296
1297                    childCount = childrenToRemove.size();
1298                    for (int j = 0; j < childCount; j++) {
1299                        View child = childrenToRemove.get(j);
1300                        layout.removeViewInLayout(child);
1301                        if (child instanceof DropTarget) {
1302                            mDragController.removeDropTarget((DropTarget)child);
1303                        }
1304                    }
1305
1306                    if (childCount > 0) {
1307                        layout.requestLayout();
1308                        layout.invalidate();
1309                    }
1310                }
1311            });
1312        }
1313    }
1314
1315    void updateShortcuts(ArrayList<ApplicationInfo> apps) {
1316        final PackageManager pm = mLauncher.getPackageManager();
1317
1318        final int screenCount = getChildCount();
1319        for (int i = 0; i < screenCount; i++) {
1320            final CellLayout layout = (CellLayout) getChildAt(i);
1321            int childCount = layout.getChildCount();
1322            for (int j = 0; j < childCount; j++) {
1323                final View view = layout.getChildAt(j);
1324                Object tag = view.getTag();
1325                if (tag instanceof ShortcutInfo) {
1326                    ShortcutInfo info = (ShortcutInfo)tag;
1327                    // We need to check for ACTION_MAIN otherwise getComponent() might
1328                    // return null for some shortcuts (for instance, for shortcuts to
1329                    // web pages.)
1330                    final Intent intent = info.intent;
1331                    final ComponentName name = intent.getComponent();
1332                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1333                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1334                        final int appCount = apps.size();
1335                        for (int k = 0; k < appCount; k++) {
1336                            ApplicationInfo app = apps.get(k);
1337                            if (app.componentName.equals(name)) {
1338                                info.setIcon(mIconCache.getIcon(info.intent));
1339                                ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
1340                                        new FastBitmapDrawable(info.getIcon(mIconCache)),
1341                                        null, null);
1342                                }
1343                        }
1344                    }
1345                }
1346            }
1347        }
1348    }
1349
1350    void moveToDefaultScreen(boolean animate) {
1351        if (animate) {
1352            if (mIsSmall) {
1353                unshrink(mDefaultPage);
1354            } else {
1355                snapToPage(mDefaultPage);
1356            }
1357        } else {
1358            setCurrentPage(mDefaultPage);
1359        }
1360        getChildAt(mDefaultPage).requestFocus();
1361    }
1362
1363    void setIndicators(Drawable previous, Drawable next) {
1364        mPreviousIndicator = previous;
1365        mNextIndicator = next;
1366        previous.setLevel(mCurrentPage);
1367        next.setLevel(mCurrentPage);
1368    }
1369
1370    @Override
1371    public void syncPages() {
1372    }
1373
1374    @Override
1375    public void syncPageItems(int page) {
1376    }
1377
1378}
1379