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