Workspace.java revision 8e58e916061cbe2623697efac0924f2aa3753a92
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.AnimatorListenerAdapter;
24import android.animation.AnimatorSet;
25import android.animation.ObjectAnimator;
26import android.animation.PropertyValuesHolder;
27import android.app.WallpaperManager;
28import android.appwidget.AppWidgetManager;
29import android.appwidget.AppWidgetProviderInfo;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.content.pm.PackageManager;
34import android.content.pm.ProviderInfo;
35import android.content.res.Resources;
36import android.content.res.TypedArray;
37import android.graphics.Bitmap;
38import android.graphics.Canvas;
39import android.graphics.Matrix;
40import android.graphics.Rect;
41import android.graphics.Region.Op;
42import android.graphics.drawable.Drawable;
43import android.net.Uri;
44import android.os.IBinder;
45import android.os.Parcelable;
46import android.util.AttributeSet;
47import android.util.Log;
48import android.view.MotionEvent;
49import android.view.View;
50import android.widget.TextView;
51
52import java.util.ArrayList;
53import java.util.HashSet;
54
55/**
56 * The workspace is a wide area with a wallpaper and a finite number of pages.
57 * Each page contains a number of icons, folders or widgets the user can
58 * interact with. A workspace is meant to be used with a fixed width only.
59 */
60public class Workspace extends SmoothPagedView
61        implements DropTarget, DragSource, DragScroller, View.OnTouchListener {
62    @SuppressWarnings({"UnusedDeclaration"})
63    private static final String TAG = "Launcher.Workspace";
64
65    // This is how much the workspace shrinks when we enter all apps or
66    // customization mode
67    private static final float SHRINK_FACTOR = 0.16f;
68
69    // Y rotation to apply to the workspace screens
70    private static final float WORKSPACE_ROTATION = 12.5f;
71
72    // These are extra scale factors to apply to the mini home screens
73    // so as to achieve the desired transform
74    private static final float EXTRA_SCALE_FACTOR_0 = 0.97f;
75    private static final float EXTRA_SCALE_FACTOR_1 = 1.0f;
76    private static final float EXTRA_SCALE_FACTOR_2 = 1.08f;
77
78    private static final int BACKGROUND_FADE_OUT_DELAY = 300;
79    private static final int BACKGROUND_FADE_OUT_DURATION = 300;
80    private static final int BACKGROUND_FADE_IN_DURATION = 100;
81
82    // These animators are used to fade the background
83    private ObjectAnimator mBackgroundFadeInAnimation;
84    private ObjectAnimator mBackgroundFadeOutAnimation;
85    private float mBackgroundAlpha = 0;
86
87    private final WallpaperManager mWallpaperManager;
88
89    private int mDefaultPage;
90
91    private boolean mWaitingToShrinkToBottom = false;
92
93    private boolean mPageMoving = false;
94
95    /**
96     * CellInfo for the cell that is currently being dragged
97     */
98    private CellLayout.CellInfo mDragInfo;
99
100    /**
101     * Target drop area calculated during last acceptDrop call.
102     */
103    private int[] mTargetCell = null;
104
105    /**
106     * The CellLayout that is currently being dragged over
107     */
108    private CellLayout mDragTargetLayout = null;
109
110    private Launcher mLauncher;
111    private IconCache mIconCache;
112    private DragController mDragController;
113
114    // These are temporary variables to prevent having to allocate a new object just to
115    // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
116    private int[] mTempCell = new int[2];
117    private int[] mTempEstimate = new int[2];
118    private float[] mTempOriginXY = new float[2];
119    private float[] mTempDragCoordinates = new float[2];
120    private float[] mTempTouchCoordinates = new float[2];
121    private float[] mTempCellLayoutCenterCoordinates = new float[2];
122    private float[] mTempDragBottomRightCoordinates = new float[2];
123    private Matrix mTempInverseMatrix = new Matrix();
124
125    private static final int DEFAULT_CELL_COUNT_X = 4;
126    private static final int DEFAULT_CELL_COUNT_Y = 4;
127
128    private Drawable mPreviousIndicator;
129    private Drawable mNextIndicator;
130
131    // State variable that indicates whether the pages are small (ie when you're
132    // in all apps or customize mode)
133    private boolean mIsSmall = false;
134    private boolean mIsInUnshrinkAnimation = false;
135    private AnimatorListener mUnshrinkAnimationListener;
136    private enum ShrinkPosition {
137        SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM_HIDDEN, SHRINK_TO_BOTTOM_VISIBLE };
138    private ShrinkPosition mShrunkenState;
139
140    private boolean mInScrollArea = false;
141
142    private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper();
143    private Bitmap mDragOutline = null;
144    private final Rect mTempRect = new Rect();
145    private final int[] mTempXY = new int[2];
146
147    /**
148     * Used to inflate the Workspace from XML.
149     *
150     * @param context The application's context.
151     * @param attrs The attributes set containing the Workspace's customization values.
152     */
153    public Workspace(Context context, AttributeSet attrs) {
154        this(context, attrs, 0);
155    }
156
157    /**
158     * Used to inflate the Workspace from XML.
159     *
160     * @param context The application's context.
161     * @param attrs The attributes set containing the Workspace's customization values.
162     * @param defStyle Unused.
163     */
164    public Workspace(Context context, AttributeSet attrs, int defStyle) {
165        super(context, attrs, defStyle);
166        mContentIsRefreshable = false;
167
168        if (!LauncherApplication.isScreenXLarge()) {
169            mFadeInAdjacentScreens = false;
170        }
171
172        mWallpaperManager = WallpaperManager.getInstance(context);
173
174        TypedArray a = context.obtainStyledAttributes(attrs,
175                R.styleable.Workspace, defStyle, 0);
176        int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X);
177        int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y);
178        mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
179        a.recycle();
180
181        LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
182        setHapticFeedbackEnabled(false);
183
184        initWorkspace();
185    }
186
187    /**
188     * Initializes various states for this workspace.
189     */
190    protected void initWorkspace() {
191        Context context = getContext();
192        mCurrentPage = mDefaultPage;
193        Launcher.setScreen(mCurrentPage);
194        LauncherApplication app = (LauncherApplication)context.getApplicationContext();
195        mIconCache = app.getIconCache();
196
197        mUnshrinkAnimationListener = new AnimatorListenerAdapter() {
198            public void onAnimationStart(Animator animation) {
199                mIsInUnshrinkAnimation = true;
200            }
201            public void onAnimationEnd(Animator animation) {
202                mIsInUnshrinkAnimation = false;
203            }
204        };
205
206        mSnapVelocity = 600;
207    }
208
209    @Override
210    protected int getScrollMode() {
211        if (LauncherApplication.isScreenXLarge()) {
212            return SmoothPagedView.QUINTIC_MODE;
213        } else {
214            return SmoothPagedView.OVERSHOOT_MODE;
215        }
216    }
217
218    @Override
219    public void addView(View child, int index, LayoutParams params) {
220        if (!(child instanceof CellLayout)) {
221            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
222        }
223        ((CellLayout) child).setOnInterceptTouchListener(this);
224        super.addView(child, index, params);
225    }
226
227    @Override
228    public void addView(View child) {
229        if (!(child instanceof CellLayout)) {
230            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
231        }
232        ((CellLayout) child).setOnInterceptTouchListener(this);
233        super.addView(child);
234    }
235
236    @Override
237    public void addView(View child, int index) {
238        if (!(child instanceof CellLayout)) {
239            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
240        }
241        ((CellLayout) child).setOnInterceptTouchListener(this);
242        super.addView(child, index);
243    }
244
245    @Override
246    public void addView(View child, int width, int height) {
247        if (!(child instanceof CellLayout)) {
248            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
249        }
250        ((CellLayout) child).setOnInterceptTouchListener(this);
251        super.addView(child, width, height);
252    }
253
254    @Override
255    public void addView(View child, LayoutParams params) {
256        if (!(child instanceof CellLayout)) {
257            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
258        }
259        ((CellLayout) child).setOnInterceptTouchListener(this);
260        super.addView(child, params);
261    }
262
263    /**
264     * @return The open folder on the current screen, or null if there is none
265     */
266    Folder getOpenFolder() {
267        CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
268        int count = currentPage.getChildCount();
269        for (int i = 0; i < count; i++) {
270            View child = currentPage.getChildAt(i);
271            if (child instanceof Folder) {
272                Folder folder = (Folder) child;
273                if (folder.getInfo().opened)
274                    return folder;
275            }
276        }
277        return null;
278    }
279
280    ArrayList<Folder> getOpenFolders() {
281        final int screenCount = getChildCount();
282        ArrayList<Folder> folders = new ArrayList<Folder>(screenCount);
283
284        for (int screen = 0; screen < screenCount; screen++) {
285            CellLayout currentPage = (CellLayout) getChildAt(screen);
286            int count = currentPage.getChildCount();
287            for (int i = 0; i < count; i++) {
288                View child = currentPage.getChildAt(i);
289                if (child instanceof Folder) {
290                    Folder folder = (Folder) child;
291                    if (folder.getInfo().opened)
292                        folders.add(folder);
293                    break;
294                }
295            }
296        }
297
298        return folders;
299    }
300
301    boolean isDefaultPageShowing() {
302        return mCurrentPage == mDefaultPage;
303    }
304
305    /**
306     * Sets the current screen.
307     *
308     * @param currentPage
309     */
310    @Override
311    void setCurrentPage(int currentPage) {
312        super.setCurrentPage(currentPage);
313        updateWallpaperOffset(mScrollX);
314    }
315
316    /**
317     * Adds the specified child in the specified screen. The position and dimension of
318     * the child are defined by x, y, spanX and spanY.
319     *
320     * @param child The child to add in one of the workspace's screens.
321     * @param screen The screen in which to add the child.
322     * @param x The X position of the child in the screen's grid.
323     * @param y The Y position of the child in the screen's grid.
324     * @param spanX The number of cells spanned horizontally by the child.
325     * @param spanY The number of cells spanned vertically by the child.
326     */
327    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
328        addInScreen(child, screen, x, y, spanX, spanY, false);
329    }
330
331    void addInFullScreen(View child, int screen) {
332        addInScreen(child, screen, 0, 0, -1, -1);
333    }
334
335    /**
336     * Adds the specified child in the specified screen. The position and dimension of
337     * the child are defined by x, y, spanX and spanY.
338     *
339     * @param child The child to add in one of the workspace's screens.
340     * @param screen The screen in which to add the child.
341     * @param x The X position of the child in the screen's grid.
342     * @param y The Y position of the child in the screen's grid.
343     * @param spanX The number of cells spanned horizontally by the child.
344     * @param spanY The number of cells spanned vertically by the child.
345     * @param insert When true, the child is inserted at the beginning of the children list.
346     */
347    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
348        if (screen < 0 || screen >= getChildCount()) {
349            Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
350                + " (was " + screen + "); skipping child");
351            return;
352        }
353
354        final CellLayout group = (CellLayout) getChildAt(screen);
355        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
356        if (lp == null) {
357            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
358        } else {
359            lp.cellX = x;
360            lp.cellY = y;
361            lp.cellHSpan = spanX;
362            lp.cellVSpan = spanY;
363        }
364
365        // Get the canonical child id to uniquely represent this view in this screen
366        int childId = LauncherModel.getCellLayoutChildId(-1, screen, x, y, spanX, spanY);
367        if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) {
368            // TODO: This branch occurs when the workspace is adding views
369            // outside of the defined grid
370            // maybe we should be deleting these items from the LauncherModel?
371            Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
372        }
373
374        if (!(child instanceof Folder)) {
375            child.setHapticFeedbackEnabled(false);
376            child.setOnLongClickListener(mLongClickListener);
377        }
378        if (child instanceof DropTarget) {
379            mDragController.addDropTarget((DropTarget) child);
380        }
381    }
382
383    public boolean onTouch(View v, MotionEvent event) {
384        // this is an intercepted event being forwarded from a cell layout
385        if (mIsSmall || mIsInUnshrinkAnimation) {
386            mLauncher.onWorkspaceClick((CellLayout) v);
387            return true;
388        } else if (!mPageMoving) {
389            if (v == getChildAt(mCurrentPage - 1)) {
390                snapToPage(mCurrentPage - 1);
391                return true;
392            } else if (v == getChildAt(mCurrentPage + 1)) {
393                snapToPage(mCurrentPage + 1);
394                return true;
395            }
396        }
397        return false;
398    }
399
400    @Override
401    public boolean dispatchUnhandledMove(View focused, int direction) {
402        if (mIsSmall || mIsInUnshrinkAnimation) {
403            // when the home screens are shrunken, shouldn't allow side-scrolling
404            return false;
405        }
406        return super.dispatchUnhandledMove(focused, direction);
407    }
408
409    @Override
410    public boolean onInterceptTouchEvent(MotionEvent ev) {
411        if (mIsSmall || mIsInUnshrinkAnimation) {
412            // when the home screens are shrunken, shouldn't allow side-scrolling
413            return false;
414        }
415        return super.onInterceptTouchEvent(ev);
416    }
417
418    @Override
419    protected void determineScrollingStart(MotionEvent ev) {
420        if (!mIsSmall && !mIsInUnshrinkAnimation) super.determineScrollingStart(ev);
421    }
422
423    protected void onPageBeginMoving() {
424        if (mNextPage != INVALID_PAGE) {
425            // we're snapping to a particular screen
426            enableChildrenCache(mCurrentPage, mNextPage);
427        } else {
428            // this is when user is actively dragging a particular screen, they might
429            // swipe it either left or right (but we won't advance by more than one screen)
430            enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
431        }
432        showOutlines();
433        mPageMoving = true;
434    }
435
436    protected void onPageEndMoving() {
437        clearChildrenCache();
438        // Hide the outlines, as long as we're not dragging
439        if (!mDragController.dragging()) {
440            hideOutlines();
441        }
442        mPageMoving = false;
443    }
444
445    @Override
446    protected void notifyPageSwitchListener() {
447        super.notifyPageSwitchListener();
448
449        if (mPreviousIndicator != null) {
450            // if we know the next page, we show the indication for it right away; it looks
451            // weird if the indicators are lagging
452            int page = mNextPage;
453            if (page == INVALID_PAGE) {
454                page = mCurrentPage;
455            }
456            mPreviousIndicator.setLevel(page);
457            mNextIndicator.setLevel(page);
458        }
459        Launcher.setScreen(mCurrentPage);
460    };
461
462    private void updateWallpaperOffset() {
463        updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
464    }
465
466    private void updateWallpaperOffset(int scrollRange) {
467        final boolean isStaticWallpaper = (mWallpaperManager != null) &&
468                (mWallpaperManager.getWallpaperInfo() == null);
469        if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) {
470            IBinder token = getWindowToken();
471            if (token != null) {
472                mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
473                mWallpaperManager.setWallpaperOffsets(getWindowToken(),
474                        Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
475            }
476        }
477    }
478
479    public void showOutlines() {
480        if (!mIsSmall && !mIsInUnshrinkAnimation) {
481            if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel();
482            if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel();
483            mBackgroundFadeInAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 1.0f);
484            mBackgroundFadeInAnimation.setDuration(BACKGROUND_FADE_IN_DURATION);
485            mBackgroundFadeInAnimation.start();
486        }
487    }
488
489    public void hideOutlines() {
490        if (!mIsSmall && !mIsInUnshrinkAnimation) {
491            if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel();
492            if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel();
493            mBackgroundFadeOutAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 0.0f);
494            mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
495            mBackgroundFadeOutAnimation.setStartDelay(BACKGROUND_FADE_OUT_DELAY);
496            mBackgroundFadeOutAnimation.start();
497        }
498    }
499
500    public void setBackgroundAlpha(float alpha) {
501        mBackgroundAlpha = alpha;
502        for (int i = 0; i < getChildCount(); i++) {
503            CellLayout cl = (CellLayout) getChildAt(i);
504            cl.setBackgroundAlpha(alpha);
505        }
506    }
507
508    public float getBackgroundAlpha() {
509        return mBackgroundAlpha;
510    }
511
512    @Override
513    protected void screenScrolled(int screenCenter) {
514        final int halfScreenSize = getMeasuredWidth() / 2;
515        for (int i = 0; i < getChildCount(); i++) {
516            View v = getChildAt(i);
517            if (v != null) {
518                int totalDistance = v.getMeasuredWidth() + mPageSpacing;
519                int delta = screenCenter - (getChildOffset(i) -
520                        getRelativeChildOffset(i) + halfScreenSize);
521
522                float scrollProgress = delta/(totalDistance*1.0f);
523                scrollProgress = Math.min(scrollProgress, 1.0f);
524                scrollProgress = Math.max(scrollProgress, -1.0f);
525
526                float rotation = WORKSPACE_ROTATION * scrollProgress;
527                v.setRotationY(rotation);
528            }
529        }
530    }
531
532    protected void onAttachedToWindow() {
533        super.onAttachedToWindow();
534        computeScroll();
535        mDragController.setWindowToken(getWindowToken());
536    }
537
538    @Override
539    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
540        super.onLayout(changed, left, top, right, bottom);
541
542        // if shrinkToBottom() is called on initialization, it has to be deferred
543        // until after the first call to onLayout so that it has the correct width
544        if (mWaitingToShrinkToBottom) {
545            shrinkToBottom(false);
546            mWaitingToShrinkToBottom = false;
547        }
548
549        if (LauncherApplication.isInPlaceRotationEnabled()) {
550            // When the device is rotated, the scroll position of the current screen
551            // needs to be refreshed
552            setCurrentPage(getCurrentPage());
553        }
554    }
555
556    @Override
557    protected void dispatchDraw(Canvas canvas) {
558        if (mIsSmall || mIsInUnshrinkAnimation) {
559            // Draw all the workspaces if we're small
560            final int pageCount = getChildCount();
561            final long drawingTime = getDrawingTime();
562            for (int i = 0; i < pageCount; i++) {
563                final View page = (View) getChildAt(i);
564
565                drawChild(canvas, page, drawingTime);
566            }
567        } else {
568            super.dispatchDraw(canvas);
569        }
570    }
571
572    @Override
573    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
574        if (!mLauncher.isAllAppsVisible()) {
575            final Folder openFolder = getOpenFolder();
576            if (openFolder != null) {
577                return openFolder.requestFocus(direction, previouslyFocusedRect);
578            } else {
579                return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
580            }
581        }
582        return false;
583    }
584
585    @Override
586    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
587        if (!mLauncher.isAllAppsVisible()) {
588            final Folder openFolder = getOpenFolder();
589            if (openFolder != null) {
590                openFolder.addFocusables(views, direction);
591            } else {
592                super.addFocusables(views, direction, focusableMode);
593            }
594        }
595    }
596
597    @Override
598    public boolean dispatchTouchEvent(MotionEvent ev) {
599        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
600            // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
601            // ie when you click on a mini-screen, it zooms back to that screen)
602            if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) {
603                return false;
604            }
605        }
606        return super.dispatchTouchEvent(ev);
607    }
608
609    void enableChildrenCache(int fromPage, int toPage) {
610        if (fromPage > toPage) {
611            final int temp = fromPage;
612            fromPage = toPage;
613            toPage = temp;
614        }
615
616        final int screenCount = getChildCount();
617
618        fromPage = Math.max(fromPage, 0);
619        toPage = Math.min(toPage, screenCount - 1);
620
621        for (int i = fromPage; i <= toPage; i++) {
622            final CellLayout layout = (CellLayout) getChildAt(i);
623            layout.setChildrenDrawnWithCacheEnabled(true);
624            layout.setChildrenDrawingCacheEnabled(true);
625        }
626    }
627
628    void clearChildrenCache() {
629        final int screenCount = getChildCount();
630        for (int i = 0; i < screenCount; i++) {
631            final CellLayout layout = (CellLayout) getChildAt(i);
632            layout.setChildrenDrawnWithCacheEnabled(false);
633        }
634    }
635
636    @Override
637    public boolean onTouchEvent(MotionEvent ev) {
638        if (mLauncher.isAllAppsVisible()) {
639            // Cancel any scrolling that is in progress.
640            if (!mScroller.isFinished()) {
641                mScroller.abortAnimation();
642            }
643            setCurrentPage(mCurrentPage);
644            return false; // We don't want the events.  Let them fall through to the all apps view.
645        }
646
647        return super.onTouchEvent(ev);
648    }
649
650    public boolean isSmall() {
651        return mIsSmall;
652    }
653
654    void shrinkToTop(boolean animated) {
655        shrink(ShrinkPosition.SHRINK_TO_TOP, animated);
656    }
657
658    void shrinkToMiddle() {
659        shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true);
660    }
661
662    void shrinkToBottom() {
663        shrinkToBottom(true);
664    }
665
666    void shrinkToBottom(boolean animated) {
667        if (mFirstLayout) {
668            // (mFirstLayout == "first layout has not happened yet")
669            // if we get a call to shrink() as part of our initialization (for example, if
670            // Launcher is started in All Apps mode) then we need to wait for a layout call
671            // to get our width so we can layout the mini-screen views correctly
672            mWaitingToShrinkToBottom = true;
673        } else {
674            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, animated);
675        }
676    }
677
678    private float getYScaleForScreen(int screen) {
679        int x = Math.abs(screen - 2);
680
681        // TODO: This should be generalized for use with arbitrary rotation angles.
682        switch(x) {
683            case 0: return EXTRA_SCALE_FACTOR_0;
684            case 1: return EXTRA_SCALE_FACTOR_1;
685            case 2: return EXTRA_SCALE_FACTOR_2;
686        }
687        return 1.0f;
688    }
689
690    // we use this to shrink the workspace for the all apps view and the customize view
691    private void shrink(ShrinkPosition shrinkPosition, boolean animated) {
692        mIsSmall = true;
693        mShrunkenState = shrinkPosition;
694
695        // Stop any scrolling, move to the current page right away
696        setCurrentPage(mCurrentPage);
697        updateWhichPagesAcceptDrops(mShrunkenState);
698
699        // we intercept and reject all touch events when we're small, so be sure to reset the state
700        mTouchState = TOUCH_STATE_REST;
701        mActivePointerId = INVALID_POINTER;
702
703        final Resources res = getResources();
704        final int screenWidth = getWidth();
705        final int screenHeight = getHeight();
706
707        // Making the assumption that all pages have the same width as the 0th
708        final int pageWidth = getChildAt(0).getMeasuredWidth();
709        final int pageHeight = getChildAt(0).getMeasuredHeight();
710
711        final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth);
712        final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight);
713        final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing);
714
715        final int screenCount = getChildCount();
716        float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing;
717
718        float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
719        float finalAlpha = 1.0f;
720        float extraShrinkFactor = 1.0f;
721        if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) {
722             newY = screenHeight - newY - scaledPageHeight;
723        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) {
724
725            // We shrink and disappear to nothing in the case of all apps
726            // (which is when we shrink to the bottom)
727            newY = screenHeight - newY - scaledPageHeight;
728            finalAlpha = 0.0f;
729            extraShrinkFactor = 0.1f;
730        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) {
731            newY = screenHeight / 2 - scaledPageHeight / 2;
732            finalAlpha = 1.0f;
733        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_TOP) {
734            newY = screenHeight / 10;
735        }
736
737        // We animate all the screens to the centered position in workspace
738        // At the same time, the screens become greyed/dimmed
739
740        // newX is initialized to the left-most position of the centered screens
741        float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2;
742
743        // We are going to scale about the center of the view, so we need to adjust the positions
744        // of the views accordingly
745        newX -= (pageWidth - scaledPageWidth) / 2.0f;
746        newY -= (pageHeight - scaledPageHeight) / 2.0f;
747        for (int i = 0; i < screenCount; i++) {
748            CellLayout cl = (CellLayout) getChildAt(i);
749
750            float rotation = (-i + 2) * WORKSPACE_ROTATION;
751            float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f));
752            float rotationScaleY = getYScaleForScreen(i);
753
754            if (animated) {
755                final int duration = res.getInteger(R.integer.config_workspaceShrinkTime);
756                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(cl,
757                        PropertyValuesHolder.ofFloat("x", newX),
758                        PropertyValuesHolder.ofFloat("y", newY),
759                        PropertyValuesHolder.ofFloat("scaleX",
760                                SHRINK_FACTOR * rotationScaleX * extraShrinkFactor),
761                        PropertyValuesHolder.ofFloat("scaleY",
762                                SHRINK_FACTOR * rotationScaleY * extraShrinkFactor),
763                        PropertyValuesHolder.ofFloat("backgroundAlpha", finalAlpha),
764                        PropertyValuesHolder.ofFloat("alpha", finalAlpha),
765                        PropertyValuesHolder.ofFloat("rotationY", rotation));
766                anim.setDuration(duration);
767                anim.start();
768            } else {
769                cl.setX((int)newX);
770                cl.setY((int)newY);
771                cl.setScaleX(SHRINK_FACTOR * rotationScaleX);
772                cl.setScaleY(SHRINK_FACTOR * rotationScaleY);
773                cl.setBackgroundAlpha(1.0f);
774                cl.setAlpha(finalAlpha);
775                cl.setRotationY(rotation);
776            }
777            // increment newX for the next screen
778            newX += scaledPageWidth + extraScaledSpacing;
779        }
780        setChildrenDrawnWithCacheEnabled(true);
781    }
782
783
784    private void updateWhichPagesAcceptDrops(ShrinkPosition state) {
785        updateWhichPagesAcceptDropsHelper(state, false, 1, 1);
786    }
787
788
789    private void updateWhichPagesAcceptDropsDuringDrag(ShrinkPosition state, int spanX, int spanY) {
790        updateWhichPagesAcceptDropsHelper(state, true, spanX, spanY);
791    }
792
793    private void updateWhichPagesAcceptDropsHelper(
794            ShrinkPosition state, boolean isDragHappening, int spanX, int spanY) {
795        final int screenCount = getChildCount();
796        for (int i = 0; i < screenCount; i++) {
797            CellLayout cl = (CellLayout) getChildAt(i);
798
799            switch (state) {
800                case SHRINK_TO_TOP:
801                    if (!isDragHappening) {
802                        boolean showDropHighlight = i == mCurrentPage;
803                        cl.setAcceptsDrops(showDropHighlight);
804                        break;
805                    }
806                    // otherwise, fall through below and mark non-full screens as accepting drops
807                case SHRINK_TO_BOTTOM_HIDDEN:
808                case SHRINK_TO_BOTTOM_VISIBLE:
809                    if (!isDragHappening) {
810                        // even if a drag isn't happening, we don't want to show a screen as
811                        // accepting drops if it doesn't have at least one free cell
812                        spanX = 1;
813                        spanY = 1;
814                    }
815                    // the page accepts drops if we can find at least one empty spot
816                    cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY));
817                    break;
818                default:
819                     throw new RuntimeException(
820                             "updateWhichPagesAcceptDropsHelper passed an unhandled ShrinkPosition");
821            }
822        }
823    }
824
825    /*
826     *
827     * We call these methods (onDragStartedWithItemSpans/onDragStartedWithItemMinSize) whenever we
828     * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
829     *
830     * These methods mark the appropriate pages as accepting drops (which alters their visual
831     * appearance) and, if the pages are hidden, makes them visible.
832     *
833     */
834    public void onDragStartedWithItemSpans(int spanX, int spanY) {
835        updateWhichPagesAcceptDropsDuringDrag(mShrunkenState, spanX, spanY);
836        if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) {
837            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE, true);
838        }
839    }
840
841    public void onDragStartedWithItemMinSize(int minWidth, int minHeight) {
842        int[] spanXY = CellLayout.rectToCell(getResources(), minWidth, minHeight, null);
843        onDragStartedWithItemSpans(spanXY[0], spanXY[1]);
844    }
845
846    // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was
847    // never dragged over
848    public void onDragStopped() {
849        updateWhichPagesAcceptDrops(mShrunkenState);
850        if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) {
851            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, true);
852        }
853    }
854
855    // We call this when we trigger an unshrink by clicking on the CellLayout cl
856    public void unshrink(CellLayout clThatWasClicked) {
857        int newCurrentPage = mCurrentPage;
858        final int screenCount = getChildCount();
859        for (int i = 0; i < screenCount; i++) {
860            if (getChildAt(i) == clThatWasClicked) {
861                newCurrentPage = i;
862            }
863        }
864        unshrink(newCurrentPage);
865    }
866
867    @Override
868    protected boolean handlePagingClicks() {
869        return true;
870    }
871
872    private void unshrink(int newCurrentPage) {
873        if (mIsSmall) {
874            int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
875            int delta = newX - mScrollX;
876
877            final int screenCount = getChildCount();
878            for (int i = 0; i < screenCount; i++) {
879                CellLayout cl = (CellLayout) getChildAt(i);
880                cl.setX(cl.getX() + delta);
881            }
882            setCurrentPage(newCurrentPage);
883            unshrink();
884        }
885    }
886
887    void unshrink() {
888        unshrink(true);
889    }
890
891    void unshrink(boolean animated) {
892        if (mIsSmall) {
893            mIsSmall = false;
894            AnimatorSet s = new AnimatorSet();
895            final int screenCount = getChildCount();
896
897            final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
898            for (int i = 0; i < screenCount; i++) {
899                final CellLayout cl = (CellLayout)getChildAt(i);
900                float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f;
901                float rotation = 0.0f;
902
903                if (i < mCurrentPage) {
904                    rotation = WORKSPACE_ROTATION;
905                } else if (i > mCurrentPage) {
906                    rotation = -WORKSPACE_ROTATION;
907                }
908
909                if (animated) {
910
911                    s.playTogether(
912                            ObjectAnimator.ofFloat(cl, "translationX", 0.0f).setDuration(duration),
913                            ObjectAnimator.ofFloat(cl, "translationY", 0.0f).setDuration(duration),
914                            ObjectAnimator.ofFloat(cl, "scaleX", 1.0f).setDuration(duration),
915                            ObjectAnimator.ofFloat(cl, "scaleY", 1.0f).setDuration(duration),
916                            ObjectAnimator.ofFloat(cl, "backgroundAlpha", 0.0f).setDuration(duration),
917                            ObjectAnimator.ofFloat(cl, "alpha", finalAlphaValue).setDuration(duration),
918                            ObjectAnimator.ofFloat(cl, "rotationY", rotation).setDuration(duration));
919                } else {
920                    cl.setTranslationX(0.0f);
921                    cl.setTranslationY(0.0f);
922                    cl.setScaleX(1.0f);
923                    cl.setScaleY(1.0f);
924                    cl.setBackgroundAlpha(0.0f);
925                    cl.setAlpha(finalAlphaValue);
926                    cl.setRotationY(rotation);
927                }
928            }
929            if (animated) {
930                // If we call this when we're not animated, onAnimationEnd is never called on
931                // the listener; make sure we only use the listener when we're actually animating
932                s.addListener(mUnshrinkAnimationListener);
933                s.start();
934            }
935        }
936    }
937
938    /**
939     * Draw the View v into the given Canvas.
940     *
941     * @param v the view to draw
942     * @param destCanvas the canvas to draw on
943     * @param padding the horizontal and vertical padding to use when drawing
944     */
945    private void drawDragView(View v, Canvas destCanvas, int padding) {
946        final Rect clipRect = mTempRect;
947        v.getDrawingRect(clipRect);
948
949        // For a TextView, adjust the clip rect so that we don't include the text label
950        if (v instanceof TextView) {
951            final int iconHeight = ((TextView)v).getCompoundPaddingTop() - v.getPaddingTop();
952            clipRect.bottom = clipRect.top + iconHeight;
953        }
954
955        // Draw the View into the bitmap.
956        // The translate of scrollX and scrollY is necessary when drawing TextViews, because
957        // they set scrollX and scrollY to large values to achieve centered text
958
959        destCanvas.save();
960        destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
961        destCanvas.clipRect(clipRect, Op.REPLACE);
962        v.draw(destCanvas);
963        destCanvas.restore();
964    }
965
966    /**
967     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
968     * Responsibility for the bitmap is transferred to the caller.
969     */
970    private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
971        final int outlineColor = getResources().getColor(R.color.drag_outline_color);
972        final Bitmap b = Bitmap.createBitmap(
973                v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
974
975        canvas.setBitmap(b);
976        drawDragView(v, canvas, padding);
977        mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true);
978
979        return b;
980    }
981
982    /**
983     * Returns a new bitmap to show when the given View is being dragged around.
984     * Responsibility for the bitmap is transferred to the caller.
985     */
986    private Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
987        final int outlineColor = getResources().getColor(R.color.drag_outline_color);
988        final Bitmap b = Bitmap.createBitmap(
989                mDragOutline.getWidth(), mDragOutline.getHeight(), Bitmap.Config.ARGB_8888);
990
991        canvas.setBitmap(b);
992        canvas.drawBitmap(mDragOutline, 0, 0, null);
993        drawDragView(v, canvas, padding);
994        mOutlineHelper.applyOuterBlur(b, canvas, outlineColor);
995
996        return b;
997    }
998
999    void startDrag(CellLayout.CellInfo cellInfo) {
1000        View child = cellInfo.cell;
1001
1002        // Make sure the drag was started by a long press as opposed to a long click.
1003        if (!child.isInTouchMode()) {
1004            return;
1005        }
1006
1007        mDragInfo = cellInfo;
1008        mDragInfo.screen = mCurrentPage;
1009
1010        CellLayout current = getCurrentDropLayout();
1011
1012        current.onDragChild(child);
1013
1014        child.clearFocus();
1015        child.setPressed(false);
1016
1017        final Canvas canvas = new Canvas();
1018
1019        // We need to add extra padding to the bitmap to make room for the glow effect
1020        final int bitmapPadding = HolographicOutlineHelper.OUTER_BLUR_RADIUS;
1021
1022        // The outline is used to visualize where the item will land if dropped
1023        mDragOutline = createDragOutline(child, canvas, bitmapPadding);
1024
1025        // The drag bitmap follows the touch point around on the screen
1026        final Bitmap b = createDragBitmap(child, canvas, bitmapPadding);
1027
1028        final int bmpWidth = b.getWidth();
1029        final int bmpHeight = b.getHeight();
1030        child.getLocationOnScreen(mTempXY);
1031        final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
1032        final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2;
1033        mDragController.startDrag(b, screenX, screenY, 0, 0, bmpWidth, bmpHeight, this,
1034                child.getTag(), DragController.DRAG_ACTION_MOVE, null);
1035        b.recycle();
1036    }
1037
1038    void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY,
1039            boolean insertAtFirst, int intersectX, int intersectY) {
1040        final CellLayout cellLayout = (CellLayout) getChildAt(screen);
1041        View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info);
1042
1043        final int[] cellXY = new int[2];
1044        cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
1045        addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
1046        LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1047                LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1048                cellXY[0], cellXY[1]);
1049    }
1050
1051    private void setPositionForDropAnimation(
1052            View dragView, int dragViewX, int dragViewY, View parent, View child) {
1053        final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1054
1055        // Based on the position of the drag view, find the top left of the original view
1056        int viewX = dragViewX + (dragView.getWidth() - child.getWidth()) / 2;
1057        int viewY = dragViewY + (dragView.getHeight() - child.getHeight()) / 2;
1058        viewX -= getResources().getInteger(R.integer.config_dragViewOffsetX);
1059        viewY -= getResources().getInteger(R.integer.config_dragViewOffsetY);
1060
1061        // Set its old pos (in the new parent's coordinates); the CellLayout will
1062        // animate it from this position during the next layout pass
1063        lp.oldX = viewX - (parent.getLeft() - mScrollX);
1064        lp.oldY = viewY - (parent.getTop() - mScrollY);
1065    }
1066
1067    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
1068            DragView dragView, Object dragInfo) {
1069
1070        int originX = x - xOffset;
1071        int originY = y - yOffset;
1072
1073        if (mDragTargetLayout == null) {
1074            // Cancel the drag if we're not over a screen at time of drop
1075            if (mDragInfo != null) {
1076                // Set its position so the parent can animate it back
1077                final View parent = getChildAt(mDragInfo.screen);
1078                setPositionForDropAnimation(dragView, originX, originY, parent, mDragInfo.cell);
1079            }
1080            return;
1081        }
1082
1083        if (mIsSmall || mIsInUnshrinkAnimation) {
1084            // get originX and originY in the local coordinate system of the screen
1085            mTempOriginXY[0] = originX;
1086            mTempOriginXY[1] = originY;
1087            mapPointFromSelfToChild(mDragTargetLayout, mTempOriginXY);
1088            originX = (int)mTempOriginXY[0];
1089            originY = (int)mTempOriginXY[1];
1090        }
1091
1092        if (source != this) {
1093            onDropExternal(originX, originY, dragInfo, mDragTargetLayout);
1094        } else if (mDragInfo != null) {
1095            // Move internally
1096            final View cell = mDragInfo.cell;
1097            mTargetCell = findNearestVacantArea(originX, originY,
1098                    mDragInfo.spanX, mDragInfo.spanY, cell, mDragTargetLayout,
1099                    mTargetCell);
1100
1101            int screen = indexOfChild(mDragTargetLayout);
1102            if (screen != mDragInfo.screen) {
1103                final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1104                originalCellLayout.removeView(cell);
1105                addInScreen(cell, screen, mTargetCell[0], mTargetCell[1],
1106                        mDragInfo.spanX, mDragInfo.spanY);
1107            }
1108            mDragTargetLayout.onDropChild(cell);
1109
1110            // update the item's position after drop
1111            final ItemInfo info = (ItemInfo) cell.getTag();
1112            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
1113            mDragTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
1114            lp.cellX = mTargetCell[0];
1115            lp.cellY = mTargetCell[1];
1116            cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen,
1117                    mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
1118
1119            LauncherModel.moveItemInDatabase(mLauncher, info,
1120                    LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1121                    lp.cellX, lp.cellY);
1122
1123            // Prepare it to be animated into its new position
1124            // This must be called after the view has been re-parented
1125            setPositionForDropAnimation(dragView, originX, originY, mDragTargetLayout, cell);
1126        }
1127    }
1128
1129    public void onDragEnter(DragSource source, int x, int y, int xOffset,
1130            int yOffset, DragView dragView, Object dragInfo) {
1131        mDragTargetLayout = null; // Reset the drag state
1132
1133        if (!mIsSmall) {
1134            mDragTargetLayout = getCurrentDropLayout();
1135            mDragTargetLayout.onDragEnter(dragView);
1136            showOutlines();
1137        }
1138    }
1139
1140    public DropTarget getDropTargetDelegate(DragSource source, int x, int y,
1141            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1142
1143        if (mIsSmall || mIsInUnshrinkAnimation) {
1144            // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens
1145            return null;
1146        }
1147        // We may need to delegate the drag to a child view. If a 1x1 item
1148        // would land in a cell occupied by a DragTarget (e.g. a Folder),
1149        // then drag events should be handled by that child.
1150
1151        ItemInfo item = (ItemInfo)dragInfo;
1152        CellLayout currentLayout = getCurrentDropLayout();
1153
1154        int dragPointX, dragPointY;
1155        if (item.spanX == 1 && item.spanY == 1) {
1156            // For a 1x1, calculate the drop cell exactly as in onDragOver
1157            dragPointX = x - xOffset;
1158            dragPointY = y - yOffset;
1159        } else {
1160            // Otherwise, use the exact drag coordinates
1161            dragPointX = x;
1162            dragPointY = y;
1163        }
1164        dragPointX += mScrollX - currentLayout.getLeft();
1165        dragPointY += mScrollY - currentLayout.getTop();
1166
1167        // If we are dragging over a cell that contains a DropTarget that will
1168        // accept the drop, delegate to that DropTarget.
1169        final int[] cellXY = mTempCell;
1170        currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
1171        View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
1172        if (child instanceof DropTarget) {
1173            DropTarget target = (DropTarget)child;
1174            if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
1175                return target;
1176            }
1177        }
1178        return null;
1179    }
1180
1181    /*
1182    *
1183    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
1184    * coordinate space. The argument xy is modified with the return result.
1185    *
1186    */
1187   void mapPointFromSelfToChild(View v, float[] xy) {
1188       mapPointFromSelfToChild(v, xy, null);
1189   }
1190
1191   /*
1192    *
1193    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
1194    * coordinate space. The argument xy is modified with the return result.
1195    *
1196    * if cachedInverseMatrix is not null, this method will just use that matrix instead of
1197    * computing it itself; we use this to avoid redundant matrix inversions in
1198    * findMatchingPageForDragOver
1199    *
1200    */
1201   void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
1202       if (cachedInverseMatrix == null) {
1203           v.getMatrix().invert(mTempInverseMatrix);
1204           cachedInverseMatrix = mTempInverseMatrix;
1205       }
1206       xy[0] = xy[0] + mScrollX - v.getLeft();
1207       xy[1] = xy[1] + mScrollY - v.getTop();
1208       cachedInverseMatrix.mapPoints(xy);
1209   }
1210
1211   /*
1212    *
1213    * Convert the 2D coordinate xy from this CellLayout's coordinate space to
1214    * the parent View's coordinate space. The argument xy is modified with the return result.
1215    *
1216    */
1217   void mapPointFromChildToSelf(View v, float[] xy) {
1218       v.getMatrix().mapPoints(xy);
1219       xy[0] -= (mScrollX - v.getLeft());
1220       xy[1] -= (mScrollY - v.getTop());
1221   }
1222
1223    static private float squaredDistance(float[] point1, float[] point2) {
1224        float distanceX = point1[0] - point2[0];
1225        float distanceY = point2[1] - point2[1];
1226        return distanceX * distanceX + distanceY * distanceY;
1227    }
1228
1229    /*
1230     *
1231     * Returns true if the passed CellLayout cl overlaps with dragView
1232     *
1233     */
1234    boolean overlaps(CellLayout cl, DragView dragView,
1235            int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
1236        // Transform the coordinates of the item being dragged to the CellLayout's coordinates
1237        final float[] draggedItemTopLeft = mTempDragCoordinates;
1238        draggedItemTopLeft[0] = dragViewX + dragView.getScaledDragRegionXOffset();
1239        draggedItemTopLeft[1] = dragViewY + dragView.getScaledDragRegionYOffset();
1240        final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
1241        draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getScaledDragRegionWidth();
1242        draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getScaledDragRegionHeight();
1243
1244        // Transform the dragged item's top left coordinates
1245        // to the CellLayout's local coordinates
1246        mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
1247        float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
1248        float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
1249
1250        if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
1251            // Transform the dragged item's bottom right coordinates
1252            // to the CellLayout's local coordinates
1253            mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
1254            float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
1255            float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
1256
1257            if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
1258                float overlap = (overlapRegionRight - overlapRegionLeft) *
1259                         (overlapRegionBottom - overlapRegionTop);
1260                if (overlap > 0) {
1261                    return true;
1262                }
1263             }
1264        }
1265        return false;
1266    }
1267
1268    /*
1269     *
1270     * This method returns the CellLayout that is currently being dragged to. In order to drag
1271     * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
1272     * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
1273     *
1274     * Return null if no CellLayout is currently being dragged over
1275     *
1276     */
1277    private CellLayout findMatchingPageForDragOver(
1278            DragView dragView, int originX, int originY, int offsetX, int offsetY) {
1279        // We loop through all the screens (ie CellLayouts) and see which ones overlap
1280        // with the item being dragged and then choose the one that's closest to the touch point
1281        final int screenCount = getChildCount();
1282        CellLayout bestMatchingScreen = null;
1283        float smallestDistSoFar = Float.MAX_VALUE;
1284
1285        for (int i = 0; i < screenCount; i++) {
1286            CellLayout cl = (CellLayout)getChildAt(i);
1287
1288            final float[] touchXy = mTempTouchCoordinates;
1289            touchXy[0] = originX + offsetX;
1290            touchXy[1] = originY + offsetY;
1291
1292            // Transform the touch coordinates to the CellLayout's local coordinates
1293            // If the touch point is within the bounds of the cell layout, we can return immediately
1294            cl.getMatrix().invert(mTempInverseMatrix);
1295            mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
1296
1297            if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
1298                    touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
1299                return cl;
1300            }
1301
1302            if (overlaps(cl, dragView, originX, originY, mTempInverseMatrix)) {
1303                // Get the center of the cell layout in screen coordinates
1304                final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
1305                cellLayoutCenter[0] = cl.getWidth()/2;
1306                cellLayoutCenter[1] = cl.getHeight()/2;
1307                mapPointFromChildToSelf(cl, cellLayoutCenter);
1308
1309                touchXy[0] = originX + offsetX;
1310                touchXy[1] = originY + offsetY;
1311
1312                // Calculate the distance between the center of the CellLayout
1313                // and the touch point
1314                float dist = squaredDistance(touchXy, cellLayoutCenter);
1315
1316                if (dist < smallestDistSoFar) {
1317                    smallestDistSoFar = dist;
1318                    bestMatchingScreen = cl;
1319                }
1320            }
1321        }
1322        return bestMatchingScreen;
1323    }
1324
1325    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
1326            DragView dragView, Object dragInfo) {
1327        // When touch is inside the scroll area, skip dragOver actions for the current screen
1328        if (!mInScrollArea) {
1329            CellLayout layout;
1330            int originX = x - xOffset;
1331            int originY = y - yOffset;
1332            if (mIsSmall || mIsInUnshrinkAnimation) {
1333                layout = findMatchingPageForDragOver(
1334                        dragView, originX, originY, xOffset, yOffset);
1335
1336                if (layout != mDragTargetLayout) {
1337                    if (mDragTargetLayout != null) {
1338                        mDragTargetLayout.setHover(false);
1339                    }
1340                    mDragTargetLayout = layout;
1341                    if (mDragTargetLayout != null) {
1342                        mDragTargetLayout.setHover(true);
1343                    }
1344                }
1345            } else {
1346                layout = getCurrentDropLayout();
1347
1348                final ItemInfo item = (ItemInfo)dragInfo;
1349                if (dragInfo instanceof LauncherAppWidgetInfo) {
1350                    LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
1351
1352                    if (widgetInfo.spanX == -1) {
1353                        // Calculate the grid spans needed to fit this widget
1354                        int[] spans = layout.rectToCell(
1355                                widgetInfo.minWidth, widgetInfo.minHeight, null);
1356                        item.spanX = spans[0];
1357                        item.spanY = spans[1];
1358                    }
1359                }
1360
1361                if (source instanceof AllAppsPagedView) {
1362                    // This is a hack to fix the point used to determine which cell an icon from
1363                    // the all apps screen is over
1364                    if (item != null && item.spanX == 1 && layout != null) {
1365                        int dragRegionLeft = (dragView.getWidth() - layout.getCellWidth()) / 2;
1366
1367                        originX += dragRegionLeft - dragView.getDragRegionLeft();
1368                        if (dragView.getDragRegionWidth() != layout.getCellWidth()) {
1369                            dragView.setDragRegion(dragView.getDragRegionLeft(),
1370                                    dragView.getDragRegionTop(),
1371                                    layout.getCellWidth(),
1372                                    dragView.getDragRegionHeight());
1373                        }
1374                    }
1375                }
1376
1377                if (layout != mDragTargetLayout) {
1378                    if (mDragTargetLayout != null) {
1379                        mDragTargetLayout.onDragExit();
1380                    }
1381                    layout.onDragEnter(dragView);
1382                    mDragTargetLayout = layout;
1383                }
1384
1385                // only visualize the drop locations for moving icons within the home screen on
1386                // tablet on phone, we also visualize icons dragged in from All Apps
1387                if ((!LauncherApplication.isScreenXLarge() || source == this)
1388                        && mDragTargetLayout != null) {
1389                    final View child = (mDragInfo == null) ? null : mDragInfo.cell;
1390                    int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX);
1391                    int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY);
1392                    mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
1393                            localOriginX, localOriginY, item.spanX, item.spanY);
1394                }
1395            }
1396        }
1397    }
1398
1399    public void onDragExit(DragSource source, int x, int y, int xOffset,
1400            int yOffset, DragView dragView, Object dragInfo) {
1401        if (mDragTargetLayout != null) {
1402            mDragTargetLayout.onDragExit();
1403        }
1404        if (!mIsPageMoving) {
1405            hideOutlines();
1406        }
1407        clearAllHovers();
1408    }
1409
1410    private void onDropExternal(int x, int y, Object dragInfo,
1411            CellLayout cellLayout) {
1412        onDropExternal(x, y, dragInfo, cellLayout, false);
1413    }
1414
1415    /**
1416     * Add the item specified by dragInfo to the given layout.
1417     * This is basically the equivalent of onDropExternal, except it's not initiated
1418     * by drag and drop.
1419     * @return true if successful
1420     */
1421    public boolean addExternalItemToScreen(Object dragInfo, View layout) {
1422        CellLayout cl = (CellLayout) layout;
1423        ItemInfo info = (ItemInfo) dragInfo;
1424
1425        if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) {
1426            onDropExternal(0, 0, dragInfo, cl, false);
1427            return true;
1428        }
1429        mLauncher.showOutOfSpaceMessage();
1430        return false;
1431    }
1432
1433    // Drag from somewhere else
1434    // NOTE: This can also be called when we are outside of a drag event, when we want
1435    // to add an item to one of the workspace screens.
1436    private void onDropExternal(int x, int y, Object dragInfo,
1437            CellLayout cellLayout, boolean insertAtFirst) {
1438        int screen = indexOfChild(cellLayout);
1439        if (dragInfo instanceof PendingAddItemInfo) {
1440            PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
1441            // When dragging and dropping from customization tray, we deal with creating
1442            // widgets/shortcuts/folders in a slightly different way
1443            int[] touchXY = new int[2];
1444            touchXY[0] = x;
1445            touchXY[1] = y;
1446            switch (info.itemType) {
1447                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1448                    mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY);
1449                    break;
1450                case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
1451                    mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY);
1452                    break;
1453                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1454                    mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY);
1455                    break;
1456                default:
1457                    throw new IllegalStateException("Unknown item type: " + info.itemType);
1458            }
1459            cellLayout.onDragExit();
1460            cellLayout.animateDrop();
1461            return;
1462        }
1463
1464        // This is for other drag/drop cases, like dragging from All Apps
1465        ItemInfo info = (ItemInfo) dragInfo;
1466
1467        View view = null;
1468
1469        switch (info.itemType) {
1470        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1471        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1472            if (info.container == NO_ID && info instanceof ApplicationInfo) {
1473                // Came from all apps -- make a copy
1474                info = new ShortcutInfo((ApplicationInfo) info);
1475            }
1476            view = mLauncher.createShortcut(R.layout.application, cellLayout,
1477                    (ShortcutInfo) info);
1478            break;
1479        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1480            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
1481                    cellLayout, ((UserFolderInfo) info));
1482            break;
1483        default:
1484            throw new IllegalStateException("Unknown item type: " + info.itemType);
1485        }
1486
1487        // If the view is null, it has already been added.
1488        if (view == null) {
1489            cellLayout.onDragExit();
1490        } else {
1491            mTargetCell = findNearestVacantArea(x, y, 1, 1, null, cellLayout, mTargetCell);
1492            addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
1493                    mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
1494            cellLayout.onDropChild(view);
1495            cellLayout.animateDrop();
1496            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1497
1498            LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1499                    LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1500                    lp.cellX, lp.cellY);
1501        }
1502    }
1503
1504    /**
1505     * Return the current {@link CellLayout}, correctly picking the destination
1506     * screen while a scroll is in progress.
1507     */
1508    private CellLayout getCurrentDropLayout() {
1509        // if we're currently small, use findMatchingPageForDragOver instead
1510        if (mIsSmall) return null;
1511        int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
1512        return (CellLayout) getChildAt(index);
1513    }
1514
1515    /**
1516     * Return the current CellInfo describing our current drag; this method exists
1517     * so that Launcher can sync this object with the correct info when the activity is created/
1518     * destroyed
1519     *
1520     */
1521    public CellLayout.CellInfo getDragInfo() {
1522        return mDragInfo;
1523    }
1524
1525    /**
1526     * {@inheritDoc}
1527     */
1528    public boolean acceptDrop(DragSource source, int x, int y,
1529            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1530        if (mDragTargetLayout == null) {
1531            // cancel the drag if we're not over a screen at time of drop
1532            return false;
1533        }
1534
1535        final CellLayout.CellInfo dragCellInfo = mDragInfo;
1536        final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
1537        final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
1538
1539        final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
1540
1541        if (mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
1542            return true;
1543        } else {
1544            mLauncher.showOutOfSpaceMessage();
1545            return false;
1546        }
1547    }
1548
1549    /**
1550     * Calculate the nearest cell where the given object would be dropped.
1551     */
1552    private int[] findNearestVacantArea(int pixelX, int pixelY,
1553            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1554
1555        int localPixelX = pixelX - (layout.getLeft() - mScrollX);
1556        int localPixelY = pixelY - (layout.getTop() - mScrollY);
1557
1558        // Find the best target drop location
1559        return layout.findNearestVacantArea(
1560                localPixelX, localPixelY, spanX, spanY, ignoreView, recycle);
1561    }
1562
1563    /**
1564     * Estimate the size that a child with the given dimensions will take in the current screen.
1565     */
1566    void estimateChildSize(int minWidth, int minHeight, int[] result) {
1567        ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result);
1568    }
1569
1570    void setLauncher(Launcher launcher) {
1571        mLauncher = launcher;
1572    }
1573
1574    public void setDragController(DragController dragController) {
1575        mDragController = dragController;
1576    }
1577
1578    public void onDropCompleted(View target, boolean success) {
1579        if (success) {
1580            if (target != this && mDragInfo != null) {
1581                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1582                cellLayout.removeView(mDragInfo.cell);
1583                if (mDragInfo.cell instanceof DropTarget) {
1584                    mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1585                }
1586                // final Object tag = mDragInfo.cell.getTag();
1587            }
1588        } else if (mDragInfo != null) {
1589            ((CellLayout) getChildAt(mDragInfo.screen)).onDropAborted(mDragInfo.cell);
1590        }
1591
1592        mDragOutline = null;
1593        mDragInfo = null;
1594    }
1595
1596    public boolean isDropEnabled() {
1597        return true;
1598    }
1599
1600    @Override
1601    protected void onRestoreInstanceState(Parcelable state) {
1602        super.onRestoreInstanceState(state);
1603        Launcher.setScreen(mCurrentPage);
1604    }
1605
1606    @Override
1607    public void scrollLeft() {
1608        if (!mIsSmall && !mIsInUnshrinkAnimation) {
1609            super.scrollLeft();
1610        }
1611    }
1612
1613    @Override
1614    public void scrollRight() {
1615        if (!mIsSmall && !mIsInUnshrinkAnimation) {
1616            super.scrollRight();
1617        }
1618    }
1619
1620    @Override
1621    public void onEnterScrollArea(int direction) {
1622        if (!mIsSmall && !mIsInUnshrinkAnimation) {
1623            mInScrollArea = true;
1624            final int screen = getCurrentPage() + ((direction == DragController.SCROLL_LEFT) ? -1 : 1);
1625            if (0 <= screen && screen < getChildCount()) {
1626                ((CellLayout) getChildAt(screen)).setHover(true);
1627            }
1628
1629            if (mDragTargetLayout != null) {
1630                mDragTargetLayout.onDragExit();
1631                mDragTargetLayout = null;
1632            }
1633        }
1634    }
1635
1636    private void clearAllHovers() {
1637        final int childCount = getChildCount();
1638        for (int i = 0; i < childCount; i++) {
1639            ((CellLayout) getChildAt(i)).setHover(false);
1640        }
1641    }
1642
1643    @Override
1644    public void onExitScrollArea() {
1645        if (mInScrollArea) {
1646            mInScrollArea = false;
1647            clearAllHovers();
1648        }
1649    }
1650
1651    public Folder getFolderForTag(Object tag) {
1652        final int screenCount = getChildCount();
1653        for (int screen = 0; screen < screenCount; screen++) {
1654            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1655            int count = currentScreen.getChildCount();
1656            for (int i = 0; i < count; i++) {
1657                View child = currentScreen.getChildAt(i);
1658                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1659                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1660                    Folder f = (Folder) child;
1661                    if (f.getInfo() == tag && f.getInfo().opened) {
1662                        return f;
1663                    }
1664                }
1665            }
1666        }
1667        return null;
1668    }
1669
1670    public View getViewForTag(Object tag) {
1671        int screenCount = getChildCount();
1672        for (int screen = 0; screen < screenCount; screen++) {
1673            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1674            int count = currentScreen.getChildCount();
1675            for (int i = 0; i < count; i++) {
1676                View child = currentScreen.getChildAt(i);
1677                if (child.getTag() == tag) {
1678                    return child;
1679                }
1680            }
1681        }
1682        return null;
1683    }
1684
1685
1686    void removeItems(final ArrayList<ApplicationInfo> apps) {
1687        final int screenCount = getChildCount();
1688        final PackageManager manager = getContext().getPackageManager();
1689        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
1690
1691        final HashSet<String> packageNames = new HashSet<String>();
1692        final int appCount = apps.size();
1693        for (int i = 0; i < appCount; i++) {
1694            packageNames.add(apps.get(i).componentName.getPackageName());
1695        }
1696
1697        for (int i = 0; i < screenCount; i++) {
1698            final CellLayout layout = (CellLayout) getChildAt(i);
1699
1700            // Avoid ANRs by treating each screen separately
1701            post(new Runnable() {
1702                public void run() {
1703                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
1704                    childrenToRemove.clear();
1705
1706                    int childCount = layout.getChildCount();
1707                    for (int j = 0; j < childCount; j++) {
1708                        final View view = layout.getChildAt(j);
1709                        Object tag = view.getTag();
1710
1711                        if (tag instanceof ShortcutInfo) {
1712                            final ShortcutInfo info = (ShortcutInfo) tag;
1713                            final Intent intent = info.intent;
1714                            final ComponentName name = intent.getComponent();
1715
1716                            if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1717                                for (String packageName: packageNames) {
1718                                    if (packageName.equals(name.getPackageName())) {
1719                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1720                                        childrenToRemove.add(view);
1721                                    }
1722                                }
1723                            }
1724                        } else if (tag instanceof UserFolderInfo) {
1725                            final UserFolderInfo info = (UserFolderInfo) tag;
1726                            final ArrayList<ShortcutInfo> contents = info.contents;
1727                            final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
1728                            final int contentsCount = contents.size();
1729                            boolean removedFromFolder = false;
1730
1731                            for (int k = 0; k < contentsCount; k++) {
1732                                final ShortcutInfo appInfo = contents.get(k);
1733                                final Intent intent = appInfo.intent;
1734                                final ComponentName name = intent.getComponent();
1735
1736                                if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1737                                    for (String packageName: packageNames) {
1738                                        if (packageName.equals(name.getPackageName())) {
1739                                            toRemove.add(appInfo);
1740                                            LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
1741                                            removedFromFolder = true;
1742                                        }
1743                                    }
1744                                }
1745                            }
1746
1747                            contents.removeAll(toRemove);
1748                            if (removedFromFolder) {
1749                                final Folder folder = getOpenFolder();
1750                                if (folder != null)
1751                                    folder.notifyDataSetChanged();
1752                            }
1753                        } else if (tag instanceof LiveFolderInfo) {
1754                            final LiveFolderInfo info = (LiveFolderInfo) tag;
1755                            final Uri uri = info.uri;
1756                            final ProviderInfo providerInfo = manager.resolveContentProvider(
1757                                    uri.getAuthority(), 0);
1758
1759                            if (providerInfo != null) {
1760                                for (String packageName: packageNames) {
1761                                    if (packageName.equals(providerInfo.packageName)) {
1762                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1763                                        childrenToRemove.add(view);
1764                                    }
1765                                }
1766                            }
1767                        } else if (tag instanceof LauncherAppWidgetInfo) {
1768                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1769                            final AppWidgetProviderInfo provider =
1770                                    widgets.getAppWidgetInfo(info.appWidgetId);
1771                            if (provider != null) {
1772                                for (String packageName: packageNames) {
1773                                    if (packageName.equals(provider.provider.getPackageName())) {
1774                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1775                                        childrenToRemove.add(view);
1776                                    }
1777                                }
1778                            }
1779                        }
1780                    }
1781
1782                    childCount = childrenToRemove.size();
1783                    for (int j = 0; j < childCount; j++) {
1784                        View child = childrenToRemove.get(j);
1785                        layout.removeViewInLayout(child);
1786                        if (child instanceof DropTarget) {
1787                            mDragController.removeDropTarget((DropTarget)child);
1788                        }
1789                    }
1790
1791                    if (childCount > 0) {
1792                        layout.requestLayout();
1793                        layout.invalidate();
1794                    }
1795                }
1796            });
1797        }
1798    }
1799
1800    void updateShortcuts(ArrayList<ApplicationInfo> apps) {
1801        final int screenCount = getChildCount();
1802        for (int i = 0; i < screenCount; i++) {
1803            final CellLayout layout = (CellLayout) getChildAt(i);
1804            int childCount = layout.getChildCount();
1805            for (int j = 0; j < childCount; j++) {
1806                final View view = layout.getChildAt(j);
1807                Object tag = view.getTag();
1808                if (tag instanceof ShortcutInfo) {
1809                    ShortcutInfo info = (ShortcutInfo)tag;
1810                    // We need to check for ACTION_MAIN otherwise getComponent() might
1811                    // return null for some shortcuts (for instance, for shortcuts to
1812                    // web pages.)
1813                    final Intent intent = info.intent;
1814                    final ComponentName name = intent.getComponent();
1815                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1816                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1817                        final int appCount = apps.size();
1818                        for (int k = 0; k < appCount; k++) {
1819                            ApplicationInfo app = apps.get(k);
1820                            if (app.componentName.equals(name)) {
1821                                info.setIcon(mIconCache.getIcon(info.intent));
1822                                ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
1823                                        new FastBitmapDrawable(info.getIcon(mIconCache)),
1824                                        null, null);
1825                                }
1826                        }
1827                    }
1828                }
1829            }
1830        }
1831    }
1832
1833    void moveToDefaultScreen(boolean animate) {
1834        if (mIsSmall || mIsInUnshrinkAnimation) {
1835            mLauncher.showWorkspace(animate, (CellLayout)getChildAt(mDefaultPage));
1836        } else if (animate) {
1837            snapToPage(mDefaultPage);
1838        } else {
1839            setCurrentPage(mDefaultPage);
1840        }
1841        getChildAt(mDefaultPage).requestFocus();
1842    }
1843
1844    void setIndicators(Drawable previous, Drawable next) {
1845        mPreviousIndicator = previous;
1846        mNextIndicator = next;
1847        previous.setLevel(mCurrentPage);
1848        next.setLevel(mCurrentPage);
1849    }
1850
1851    @Override
1852    public void syncPages() {
1853    }
1854
1855    @Override
1856    public void syncPageItems(int page) {
1857    }
1858
1859}
1860