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