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