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