Workspace.java revision fc9f07d42511231a26fa182c32e2efaa0f41d478
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
778        if (source != this) {
779            onDropExternal(originX, originY, dragInfo, cellLayout);
780        } else {
781            // Move internally
782            if (mDragInfo != null) {
783                final View cell = mDragInfo.cell;
784
785                mTargetCell = findNearestVacantArea(originX, originY,
786                        mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout,
787                        mTargetCell);
788
789                int screen = indexOfChild(cellLayout);
790                if (screen != mDragInfo.screen) {
791                    final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
792                    originalCellLayout.removeView(cell);
793                    addInScreen(cell, screen, mTargetCell[0], mTargetCell[1],
794                            mDragInfo.spanX, mDragInfo.spanY);
795                }
796                cellLayout.onDropChild(cell);
797
798                // update the item's position after drop
799                final ItemInfo info = (ItemInfo) cell.getTag();
800                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
801                cellLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
802                lp.cellX = mTargetCell[0];
803                lp.cellY = mTargetCell[1];
804                cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen,
805                        mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
806
807                LauncherModel.moveItemInDatabase(mLauncher, info,
808                        LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
809                        lp.cellX, lp.cellY);
810            }
811        }
812    }
813
814    public void onDragEnter(DragSource source, int x, int y, int xOffset,
815            int yOffset, DragView dragView, Object dragInfo) {
816        getCurrentDropLayout().onDragEnter(dragView);
817    }
818
819    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
820            DragView dragView, Object dragInfo) {
821
822        if (mIsSmall) {
823            // If we're shrunken, don't let anyone drag on folders/etc  that are on the mini-screens
824            return null;
825        }
826        // We may need to delegate the drag to a child view. If a 1x1 item
827        // would land in a cell occupied by a DragTarget (e.g. a Folder),
828        // then drag events should be handled by that child.
829
830        ItemInfo item = (ItemInfo)dragInfo;
831        CellLayout currentLayout = getCurrentDropLayout();
832
833        int dragPointX, dragPointY;
834        if (item.spanX == 1 && item.spanY == 1) {
835            // For a 1x1, calculate the drop cell exactly as in onDragOver
836            dragPointX = x - xOffset;
837            dragPointY = y - yOffset;
838        } else {
839            // Otherwise, use the exact drag coordinates
840            dragPointX = x;
841            dragPointY = y;
842        }
843        dragPointX += mScrollX - currentLayout.getLeft();
844        dragPointY += mScrollY - currentLayout.getTop();
845
846        // If we are dragging over a cell that contains a DropTarget that will
847        // accept the drop, delegate to that DropTarget.
848        final int[] cellXY = mTempCell;
849        currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
850        View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
851        if (child instanceof DropTarget) {
852            DropTarget target = (DropTarget)child;
853            if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
854                return target;
855            }
856        }
857        return null;
858    }
859
860    private void mapPointGlobalToLocal(View v, float[] xy) {
861        xy[0] = xy[0] + mScrollX - v.getLeft();
862        xy[1] = xy[1] + mScrollY - v.getTop();
863        v.getMatrix().invert(mTempInverseMatrix);
864        mTempInverseMatrix.mapPoints(xy);
865    }
866
867    // xy = upper left corner of item being dragged
868    // bottomRightXy = lower right corner of item being dragged
869    // This method will see which mini-screen is most overlapped by the item being dragged, and
870    // return it. It will also transform the parameters xy and bottomRightXy into the local
871    // coordinate space of the returned screen
872    private CellLayout findMatchingPageForDragOver(DragView dragView, int originX, int originY) {
873        float x = originX + dragView.getScaledDragRegionXOffset();
874        float y = originY + dragView.getScaledDragRegionYOffset();
875        float right = x + dragView.getScaledDragRegionWidth();
876        float bottom = y + dragView.getScaledDragRegionHeight();
877
878        // We loop through all the screens (ie CellLayouts) and see which one overlaps the most
879        // with the item being dragged.
880        final int screenCount = getChildCount();
881        CellLayout bestMatchingScreen = null;
882        float smallestDistSoFar = Float.MAX_VALUE;
883        final float[] xy = mTempDragCoordinates;
884        final float[] bottomRightXy = mTempDragBottomRightCoordinates;
885        for (int i = 0; i < screenCount; i++) {
886            CellLayout cl = (CellLayout)getChildAt(i);
887            // Transform the coordinates of the item being dragged to the CellLayout's coordinates
888            float left = cl.getLeft();
889            float top = cl.getTop();
890            xy[0] = x + mScrollX - left;
891            xy[1] = y + mScrollY - top;
892
893            bottomRightXy[0] = right + mScrollX - left;
894            bottomRightXy[1] = bottom + mScrollY - top;
895
896            cl.getMatrix().invert(mTempInverseMatrix);
897            mTempInverseMatrix.mapPoints(xy);
898            mTempInverseMatrix.mapPoints(bottomRightXy);
899
900            float dragRegionX = xy[0];
901            float dragRegionY = xy[1];
902            float dragRegionRight = bottomRightXy[0];
903            float dragRegionBottom = bottomRightXy[1];
904            float dragRegionCenterX = (dragRegionX + dragRegionRight) / 2.0f;
905            float dragRegionCenterY = (dragRegionY + dragRegionBottom) / 2.0f;
906
907            // Find the overlapping region
908            float overlapLeft = Math.max(0f, dragRegionX);
909            float overlapTop = Math.max(0f, dragRegionY);
910            float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom);
911            float overlapRight = Math.min(cl.getWidth(), dragRegionRight);
912            if (overlapRight >= 0 && overlapLeft <= cl.getWidth() &&
913                    (overlapTop >= 0 && overlapBottom <= cl.getHeight())) {
914                // Calculate the distance between the two centers
915                float distX = dragRegionCenterX - cl.getWidth()/2;
916                float distY = dragRegionCenterY - cl.getHeight()/2;
917                float dist = distX * distX + distY * distY;
918
919                float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
920
921                // Calculate the closest overlapping region
922                if (overlap > 0 && dist < smallestDistSoFar) {
923                    smallestDistSoFar = dist;
924                    bestMatchingScreen = cl;
925                }
926             }
927        }
928
929        if (bestMatchingScreen != mDragTargetLayout) {
930            if (mDragTargetLayout != null) {
931                mDragTargetLayout.onDragExit();
932            }
933            mDragTargetLayout = bestMatchingScreen;
934        }
935        return bestMatchingScreen;
936    }
937
938    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
939            DragView dragView, Object dragInfo) {
940        CellLayout currentLayout;
941        int originX = x - xOffset;
942        int originY = y - yOffset;
943        if (mIsSmall) {
944            currentLayout = findMatchingPageForDragOver(dragView, originX, originY);
945
946            if (currentLayout == null) {
947                return;
948            }
949
950            currentLayout.setHover(true);
951            // get originX and originY in the local coordinate system of the screen
952            mTempOriginXY[0] = originX;
953            mTempOriginXY[1] = originY;
954            mapPointGlobalToLocal(currentLayout, mTempOriginXY);
955            originX = (int)mTempOriginXY[0];
956            originY = (int)mTempOriginXY[1];
957        } else {
958            currentLayout = getCurrentDropLayout();
959        }
960
961        final ItemInfo item = (ItemInfo)dragInfo;
962
963        if (dragInfo instanceof LauncherAppWidgetInfo) {
964            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
965
966            if (widgetInfo.spanX == -1) {
967                // Calculate the grid spans needed to fit this widget
968                int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null);
969                item.spanX = spans[0];
970                item.spanY = spans[1];
971            }
972        }
973
974        if (source != this) {
975            // This is a hack to fix the point used to determine which cell an icon from the all
976            // apps screen is over
977            if (item != null && item.spanX == 1 && currentLayout != null) {
978                int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2;
979
980                originX += dragRegionLeft - dragView.getDragRegionLeft();
981                if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) {
982                    dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(),
983                            currentLayout.getCellWidth(), dragView.getDragRegionHeight());
984                }
985            }
986        }
987        if (currentLayout != mDragTargetLayout) {
988            if (mDragTargetLayout != null) {
989                mDragTargetLayout.onDragExit();
990                currentLayout.onDragEnter(dragView);
991            }
992            mDragTargetLayout = currentLayout;
993        }
994
995        // only visualize the drop locations for moving icons within the home screen on tablet
996        // on phone, we also visualize icons dragged in from All Apps
997        if ((!LauncherApplication.isScreenXLarge() || source == this)
998                && mDragTargetLayout != null) {
999            final View child = (mDragInfo == null) ? null : mDragInfo.cell;
1000            int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX);
1001            int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY);
1002            mDragTargetLayout.visualizeDropLocation(
1003                    child, localOriginX, localOriginY, item.spanX, item.spanY);
1004        }
1005    }
1006
1007    public void onDragExit(DragSource source, int x, int y, int xOffset,
1008            int yOffset, DragView dragView, Object dragInfo) {
1009        if (mDragTargetLayout != null) {
1010            mDragTargetLayout.onDragExit();
1011            mDragTargetLayout = null;
1012        }
1013    }
1014
1015    private void onDropExternal(int x, int y, Object dragInfo,
1016            CellLayout cellLayout) {
1017        onDropExternal(x, y, dragInfo, cellLayout, false);
1018    }
1019
1020    /**
1021     * Add the item specified by dragInfo to the given layout.
1022     * This is basically the equivalent of onDropExternal, except it's not initiated
1023     * by drag and drop.
1024     * @return true if successful
1025     */
1026    public boolean addExternalItemToScreen(Object dragInfo, View layout) {
1027        CellLayout cl = (CellLayout) layout;
1028        ItemInfo info = (ItemInfo) dragInfo;
1029
1030        if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) {
1031            onDropExternal(0, 0, dragInfo, cl, false);
1032            return true;
1033        }
1034        mLauncher.showOutOfSpaceMessage();
1035        return false;
1036    }
1037
1038    // Drag from somewhere else
1039    private void onDropExternal(int x, int y, Object dragInfo,
1040            CellLayout cellLayout, boolean insertAtFirst) {
1041        int screen = indexOfChild(cellLayout);
1042        if (dragInfo instanceof PendingAddItemInfo) {
1043            PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
1044            // When dragging and dropping from customization tray, we deal with creating
1045            // widgets/shortcuts/folders in a slightly different way
1046            int[] touchXY = new int[2];
1047            touchXY[0] = x;
1048            touchXY[1] = y;
1049            switch (info.itemType) {
1050                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1051                    mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY);
1052                    break;
1053                case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
1054                    mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY);
1055                    break;
1056                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1057                    mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY);
1058                    break;
1059                default:
1060                    throw new IllegalStateException("Unknown item type: " + info.itemType);
1061            }
1062            cellLayout.onDragExit();
1063            return;
1064        }
1065
1066        // This is for other drag/drop cases, like dragging from All Apps
1067        ItemInfo info = (ItemInfo) dragInfo;
1068
1069        View view = null;
1070
1071        switch (info.itemType) {
1072        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1073        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1074            if (info.container == NO_ID && info instanceof ApplicationInfo) {
1075                // Came from all apps -- make a copy
1076                info = new ShortcutInfo((ApplicationInfo) info);
1077            }
1078            view = mLauncher.createShortcut(R.layout.application, cellLayout,
1079                    (ShortcutInfo) info);
1080            break;
1081        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1082            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
1083                    cellLayout, ((UserFolderInfo) info));
1084            break;
1085        default:
1086            throw new IllegalStateException("Unknown item type: " + info.itemType);
1087        }
1088
1089        // If the view is null, it has already been added.
1090        if (view == null) {
1091            cellLayout.onDragExit();
1092        } else {
1093            mTargetCell = findNearestVacantArea(x, y, 1, 1, null, cellLayout, mTargetCell);
1094            addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
1095                    mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
1096            cellLayout.onDropChild(view);
1097            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1098
1099            LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1100                    LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1101                    lp.cellX, lp.cellY);
1102        }
1103    }
1104
1105    /**
1106     * Return the current {@link CellLayout}, correctly picking the destination
1107     * screen while a scroll is in progress.
1108     */
1109    private CellLayout getCurrentDropLayout() {
1110        int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
1111        return (CellLayout) getChildAt(index);
1112    }
1113
1114    /**
1115     * Return the current CellInfo describing our current drag; this method exists
1116     * so that Launcher can sync this object with the correct info when the activity is created/
1117     * destroyed
1118     *
1119     */
1120    public CellLayout.CellInfo getDragInfo() {
1121        return mDragInfo;
1122    }
1123
1124    /**
1125     * {@inheritDoc}
1126     */
1127    public boolean acceptDrop(DragSource source, int x, int y,
1128            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1129        CellLayout layout;
1130        if (mIsSmall) {
1131            layout = findMatchingPageForDragOver(dragView, x - xOffset, y - yOffset);
1132            if (layout == null) {
1133                // cancel the drag if we're not over a mini-screen at time of drop
1134                return false;
1135            }
1136        } else {
1137            layout = getCurrentDropLayout();
1138        }
1139        final CellLayout.CellInfo dragCellInfo = mDragInfo;
1140        final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
1141        final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
1142
1143        final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
1144
1145        if (layout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
1146            return true;
1147        } else {
1148            mLauncher.showOutOfSpaceMessage();
1149            return false;
1150        }
1151    }
1152
1153    /**
1154     * Calculate the nearest cell where the given object would be dropped.
1155     */
1156    private int[] findNearestVacantArea(int pixelX, int pixelY,
1157            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1158
1159        int localPixelX = pixelX - (layout.getLeft() - mScrollX);
1160        int localPixelY = pixelY - (layout.getTop() - mScrollY);
1161
1162        // Find the best target drop location
1163        return layout.findNearestVacantArea(
1164                localPixelX, localPixelY, spanX, spanY, ignoreView, recycle);
1165    }
1166
1167    /**
1168     * Estimate the size that a child with the given dimensions will take in the current screen.
1169     */
1170    void estimateChildSize(int minWidth, int minHeight, int[] result) {
1171        ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result);
1172    }
1173
1174    void setLauncher(Launcher launcher) {
1175        mLauncher = launcher;
1176    }
1177
1178    public void setDragController(DragController dragController) {
1179        mDragController = dragController;
1180    }
1181
1182    public void onDropCompleted(View target, boolean success) {
1183        if (success) {
1184            if (target != this && mDragInfo != null) {
1185                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1186                cellLayout.removeView(mDragInfo.cell);
1187                if (mDragInfo.cell instanceof DropTarget) {
1188                    mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1189                }
1190                // final Object tag = mDragInfo.cell.getTag();
1191            }
1192        } else {
1193            if (mDragInfo != null) {
1194                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1195                cellLayout.onDropAborted(mDragInfo.cell);
1196            }
1197        }
1198
1199        mDragInfo = null;
1200    }
1201
1202    public boolean isDropEnabled() {
1203        return true;
1204    }
1205
1206    @Override
1207    protected void onRestoreInstanceState(Parcelable state) {
1208        super.onRestoreInstanceState(state);
1209        Launcher.setScreen(mCurrentPage);
1210    }
1211
1212    @Override
1213    public void scrollLeft() {
1214        if (!mIsSmall) {
1215            super.scrollLeft();
1216        }
1217    }
1218
1219    @Override
1220    public void scrollRight() {
1221        if (!mIsSmall) {
1222            super.scrollRight();
1223        }
1224    }
1225
1226    public Folder getFolderForTag(Object tag) {
1227        final int screenCount = getChildCount();
1228        for (int screen = 0; screen < screenCount; screen++) {
1229            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1230            int count = currentScreen.getChildCount();
1231            for (int i = 0; i < count; i++) {
1232                View child = currentScreen.getChildAt(i);
1233                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1234                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1235                    Folder f = (Folder) child;
1236                    if (f.getInfo() == tag && f.getInfo().opened) {
1237                        return f;
1238                    }
1239                }
1240            }
1241        }
1242        return null;
1243    }
1244
1245    public View getViewForTag(Object tag) {
1246        int screenCount = getChildCount();
1247        for (int screen = 0; screen < screenCount; screen++) {
1248            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1249            int count = currentScreen.getChildCount();
1250            for (int i = 0; i < count; i++) {
1251                View child = currentScreen.getChildAt(i);
1252                if (child.getTag() == tag) {
1253                    return child;
1254                }
1255            }
1256        }
1257        return null;
1258    }
1259
1260
1261    void removeItems(final ArrayList<ApplicationInfo> apps) {
1262        final int screenCount = getChildCount();
1263        final PackageManager manager = getContext().getPackageManager();
1264        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
1265
1266        final HashSet<String> packageNames = new HashSet<String>();
1267        final int appCount = apps.size();
1268        for (int i = 0; i < appCount; i++) {
1269            packageNames.add(apps.get(i).componentName.getPackageName());
1270        }
1271
1272        for (int i = 0; i < screenCount; i++) {
1273            final CellLayout layout = (CellLayout) getChildAt(i);
1274
1275            // Avoid ANRs by treating each screen separately
1276            post(new Runnable() {
1277                public void run() {
1278                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
1279                    childrenToRemove.clear();
1280
1281                    int childCount = layout.getChildCount();
1282                    for (int j = 0; j < childCount; j++) {
1283                        final View view = layout.getChildAt(j);
1284                        Object tag = view.getTag();
1285
1286                        if (tag instanceof ShortcutInfo) {
1287                            final ShortcutInfo info = (ShortcutInfo) tag;
1288                            final Intent intent = info.intent;
1289                            final ComponentName name = intent.getComponent();
1290
1291                            if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1292                                for (String packageName: packageNames) {
1293                                    if (packageName.equals(name.getPackageName())) {
1294                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1295                                        childrenToRemove.add(view);
1296                                    }
1297                                }
1298                            }
1299                        } else if (tag instanceof UserFolderInfo) {
1300                            final UserFolderInfo info = (UserFolderInfo) tag;
1301                            final ArrayList<ShortcutInfo> contents = info.contents;
1302                            final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
1303                            final int contentsCount = contents.size();
1304                            boolean removedFromFolder = false;
1305
1306                            for (int k = 0; k < contentsCount; k++) {
1307                                final ShortcutInfo appInfo = contents.get(k);
1308                                final Intent intent = appInfo.intent;
1309                                final ComponentName name = intent.getComponent();
1310
1311                                if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1312                                    for (String packageName: packageNames) {
1313                                        if (packageName.equals(name.getPackageName())) {
1314                                            toRemove.add(appInfo);
1315                                            LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
1316                                            removedFromFolder = true;
1317                                        }
1318                                    }
1319                                }
1320                            }
1321
1322                            contents.removeAll(toRemove);
1323                            if (removedFromFolder) {
1324                                final Folder folder = getOpenFolder();
1325                                if (folder != null)
1326                                    folder.notifyDataSetChanged();
1327                            }
1328                        } else if (tag instanceof LiveFolderInfo) {
1329                            final LiveFolderInfo info = (LiveFolderInfo) tag;
1330                            final Uri uri = info.uri;
1331                            final ProviderInfo providerInfo = manager.resolveContentProvider(
1332                                    uri.getAuthority(), 0);
1333
1334                            if (providerInfo != null) {
1335                                for (String packageName: packageNames) {
1336                                    if (packageName.equals(providerInfo.packageName)) {
1337                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1338                                        childrenToRemove.add(view);
1339                                    }
1340                                }
1341                            }
1342                        } else if (tag instanceof LauncherAppWidgetInfo) {
1343                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1344                            final AppWidgetProviderInfo provider =
1345                                    widgets.getAppWidgetInfo(info.appWidgetId);
1346                            if (provider != null) {
1347                                for (String packageName: packageNames) {
1348                                    if (packageName.equals(provider.provider.getPackageName())) {
1349                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1350                                        childrenToRemove.add(view);
1351                                    }
1352                                }
1353                            }
1354                        }
1355                    }
1356
1357                    childCount = childrenToRemove.size();
1358                    for (int j = 0; j < childCount; j++) {
1359                        View child = childrenToRemove.get(j);
1360                        layout.removeViewInLayout(child);
1361                        if (child instanceof DropTarget) {
1362                            mDragController.removeDropTarget((DropTarget)child);
1363                        }
1364                    }
1365
1366                    if (childCount > 0) {
1367                        layout.requestLayout();
1368                        layout.invalidate();
1369                    }
1370                }
1371            });
1372        }
1373    }
1374
1375    void updateShortcuts(ArrayList<ApplicationInfo> apps) {
1376        final int screenCount = getChildCount();
1377        for (int i = 0; i < screenCount; i++) {
1378            final CellLayout layout = (CellLayout) getChildAt(i);
1379            int childCount = layout.getChildCount();
1380            for (int j = 0; j < childCount; j++) {
1381                final View view = layout.getChildAt(j);
1382                Object tag = view.getTag();
1383                if (tag instanceof ShortcutInfo) {
1384                    ShortcutInfo info = (ShortcutInfo)tag;
1385                    // We need to check for ACTION_MAIN otherwise getComponent() might
1386                    // return null for some shortcuts (for instance, for shortcuts to
1387                    // web pages.)
1388                    final Intent intent = info.intent;
1389                    final ComponentName name = intent.getComponent();
1390                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1391                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1392                        final int appCount = apps.size();
1393                        for (int k = 0; k < appCount; k++) {
1394                            ApplicationInfo app = apps.get(k);
1395                            if (app.componentName.equals(name)) {
1396                                info.setIcon(mIconCache.getIcon(info.intent));
1397                                ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
1398                                        new FastBitmapDrawable(info.getIcon(mIconCache)),
1399                                        null, null);
1400                                }
1401                        }
1402                    }
1403                }
1404            }
1405        }
1406    }
1407
1408    void moveToDefaultScreen(boolean animate) {
1409        if (animate) {
1410            if (mIsSmall) {
1411                unshrink(mDefaultPage);
1412            } else {
1413                snapToPage(mDefaultPage);
1414            }
1415        } else {
1416            setCurrentPage(mDefaultPage);
1417        }
1418        getChildAt(mDefaultPage).requestFocus();
1419    }
1420
1421    void setIndicators(Drawable previous, Drawable next) {
1422        mPreviousIndicator = previous;
1423        mNextIndicator = next;
1424        previous.setLevel(mCurrentPage);
1425        next.setLevel(mCurrentPage);
1426    }
1427
1428    @Override
1429    public void syncPages() {
1430    }
1431
1432    @Override
1433    public void syncPageItems(int page) {
1434    }
1435
1436}
1437