Workspace.java revision df4b83dd9d6380ab963c62d1f9d1312efc87cb0f
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        boolean isPortrait = getMeasuredHeight() > getMeasuredWidth();
719        float newY = (isPortrait ?
720                getResources().getDimension(R.dimen.smallScreenVerticalMarginPortrait) :
721                getResources().getDimension(R.dimen.smallScreenVerticalMarginLandscape));
722        float finalAlpha = 1.0f;
723        float extraShrinkFactor = 1.0f;
724        if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) {
725             newY = screenHeight - newY - scaledPageHeight;
726        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) {
727
728            // We shrink and disappear to nothing in the case of all apps
729            // (which is when we shrink to the bottom)
730            newY = screenHeight - newY - scaledPageHeight;
731            finalAlpha = 0.0f;
732            extraShrinkFactor = 0.1f;
733        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) {
734            newY = screenHeight / 2 - scaledPageHeight / 2;
735            finalAlpha = 1.0f;
736        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_TOP) {
737            newY = screenHeight / 10;
738        }
739
740        // We animate all the screens to the centered position in workspace
741        // At the same time, the screens become greyed/dimmed
742
743        // newX is initialized to the left-most position of the centered screens
744        float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2;
745
746        // We are going to scale about the center of the view, so we need to adjust the positions
747        // of the views accordingly
748        newX -= (pageWidth - scaledPageWidth) / 2.0f;
749        newY -= (pageHeight - scaledPageHeight) / 2.0f;
750        for (int i = 0; i < screenCount; i++) {
751            CellLayout cl = (CellLayout) getChildAt(i);
752
753            float rotation = (-i + 2) * WORKSPACE_ROTATION;
754            float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f));
755            float rotationScaleY = getYScaleForScreen(i);
756
757            if (animated) {
758                final int duration = res.getInteger(R.integer.config_workspaceShrinkTime);
759                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(cl,
760                        PropertyValuesHolder.ofFloat("x", newX),
761                        PropertyValuesHolder.ofFloat("y", newY),
762                        PropertyValuesHolder.ofFloat("scaleX",
763                                SHRINK_FACTOR * rotationScaleX * extraShrinkFactor),
764                        PropertyValuesHolder.ofFloat("scaleY",
765                                SHRINK_FACTOR * rotationScaleY * extraShrinkFactor),
766                        PropertyValuesHolder.ofFloat("backgroundAlpha", finalAlpha),
767                        PropertyValuesHolder.ofFloat("alpha", finalAlpha),
768                        PropertyValuesHolder.ofFloat("rotationY", rotation));
769                anim.setDuration(duration);
770                anim.start();
771            } else {
772                cl.setX((int)newX);
773                cl.setY((int)newY);
774                cl.setScaleX(SHRINK_FACTOR * rotationScaleX);
775                cl.setScaleY(SHRINK_FACTOR * rotationScaleY);
776                cl.setBackgroundAlpha(1.0f);
777                cl.setAlpha(finalAlpha);
778                cl.setRotationY(rotation);
779            }
780            // increment newX for the next screen
781            newX += scaledPageWidth + extraScaledSpacing;
782        }
783        setChildrenDrawnWithCacheEnabled(true);
784    }
785
786
787    private void updateWhichPagesAcceptDrops(ShrinkPosition state) {
788        updateWhichPagesAcceptDropsHelper(state, false, 1, 1);
789    }
790
791
792    private void updateWhichPagesAcceptDropsDuringDrag(ShrinkPosition state, int spanX, int spanY) {
793        updateWhichPagesAcceptDropsHelper(state, true, spanX, spanY);
794    }
795
796    private void updateWhichPagesAcceptDropsHelper(
797            ShrinkPosition state, boolean isDragHappening, int spanX, int spanY) {
798        final int screenCount = getChildCount();
799        for (int i = 0; i < screenCount; i++) {
800            CellLayout cl = (CellLayout) getChildAt(i);
801
802            switch (state) {
803                case SHRINK_TO_TOP:
804                    if (!isDragHappening) {
805                        boolean showDropHighlight = i == mCurrentPage;
806                        cl.setAcceptsDrops(showDropHighlight);
807                        break;
808                    }
809                    // otherwise, fall through below and mark non-full screens as accepting drops
810                case SHRINK_TO_BOTTOM_HIDDEN:
811                case SHRINK_TO_BOTTOM_VISIBLE:
812                    if (!isDragHappening) {
813                        // even if a drag isn't happening, we don't want to show a screen as
814                        // accepting drops if it doesn't have at least one free cell
815                        spanX = 1;
816                        spanY = 1;
817                    }
818                    // the page accepts drops if we can find at least one empty spot
819                    cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY));
820                    break;
821                default:
822                     throw new RuntimeException(
823                             "updateWhichPagesAcceptDropsHelper passed an unhandled ShrinkPosition");
824            }
825        }
826    }
827
828    /*
829     *
830     * We call these methods (onDragStartedWithItemSpans/onDragStartedWithItemMinSize) whenever we
831     * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
832     *
833     * These methods mark the appropriate pages as accepting drops (which alters their visual
834     * appearance) and, if the pages are hidden, makes them visible.
835     *
836     */
837    public void onDragStartedWithItemSpans(int spanX, int spanY) {
838        updateWhichPagesAcceptDropsDuringDrag(mShrunkenState, spanX, spanY);
839        if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) {
840            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE, true);
841        }
842    }
843
844    public void onDragStartedWithItemMinSize(int minWidth, int minHeight) {
845        int[] spanXY = CellLayout.rectToCell(getResources(), minWidth, minHeight, null);
846        onDragStartedWithItemSpans(spanXY[0], spanXY[1]);
847    }
848
849    // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was
850    // never dragged over
851    public void onDragStopped() {
852        updateWhichPagesAcceptDrops(mShrunkenState);
853        if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) {
854            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, true);
855        }
856    }
857
858    // We call this when we trigger an unshrink by clicking on the CellLayout cl
859    public void unshrink(CellLayout clThatWasClicked) {
860        int newCurrentPage = mCurrentPage;
861        final int screenCount = getChildCount();
862        for (int i = 0; i < screenCount; i++) {
863            if (getChildAt(i) == clThatWasClicked) {
864                newCurrentPage = i;
865            }
866        }
867        unshrink(newCurrentPage);
868    }
869
870    @Override
871    protected boolean handlePagingClicks() {
872        return true;
873    }
874
875    private void unshrink(int newCurrentPage) {
876        if (mIsSmall) {
877            int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
878            int delta = newX - mScrollX;
879
880            final int screenCount = getChildCount();
881            for (int i = 0; i < screenCount; i++) {
882                CellLayout cl = (CellLayout) getChildAt(i);
883                cl.setX(cl.getX() + delta);
884            }
885            setCurrentPage(newCurrentPage);
886            unshrink();
887        }
888    }
889
890    void unshrink() {
891        unshrink(true);
892    }
893
894    void unshrink(boolean animated) {
895        if (mIsSmall) {
896            mIsSmall = false;
897            AnimatorSet s = new AnimatorSet();
898            final int screenCount = getChildCount();
899
900            final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
901            for (int i = 0; i < screenCount; i++) {
902                final CellLayout cl = (CellLayout)getChildAt(i);
903                float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f;
904                float rotation = 0.0f;
905
906                if (i < mCurrentPage) {
907                    rotation = WORKSPACE_ROTATION;
908                } else if (i > mCurrentPage) {
909                    rotation = -WORKSPACE_ROTATION;
910                }
911
912                if (animated) {
913
914                    s.playTogether(
915                            ObjectAnimator.ofFloat(cl, "translationX", 0.0f).setDuration(duration),
916                            ObjectAnimator.ofFloat(cl, "translationY", 0.0f).setDuration(duration),
917                            ObjectAnimator.ofFloat(cl, "scaleX", 1.0f).setDuration(duration),
918                            ObjectAnimator.ofFloat(cl, "scaleY", 1.0f).setDuration(duration),
919                            ObjectAnimator.ofFloat(cl, "backgroundAlpha", 0.0f).setDuration(duration),
920                            ObjectAnimator.ofFloat(cl, "alpha", finalAlphaValue).setDuration(duration),
921                            ObjectAnimator.ofFloat(cl, "rotationY", rotation).setDuration(duration));
922                } else {
923                    cl.setTranslationX(0.0f);
924                    cl.setTranslationY(0.0f);
925                    cl.setScaleX(1.0f);
926                    cl.setScaleY(1.0f);
927                    cl.setBackgroundAlpha(0.0f);
928                    cl.setAlpha(finalAlphaValue);
929                    cl.setRotationY(rotation);
930                }
931            }
932            if (animated) {
933                // If we call this when we're not animated, onAnimationEnd is never called on
934                // the listener; make sure we only use the listener when we're actually animating
935                s.addListener(mUnshrinkAnimationListener);
936                s.start();
937            }
938        }
939    }
940
941    /**
942     * Draw the View v into the given Canvas.
943     *
944     * @param v the view to draw
945     * @param destCanvas the canvas to draw on
946     * @param padding the horizontal and vertical padding to use when drawing
947     */
948    private void drawDragView(View v, Canvas destCanvas, int padding) {
949        final Rect clipRect = mTempRect;
950        v.getDrawingRect(clipRect);
951
952        // For a TextView, adjust the clip rect so that we don't include the text label
953        if (v instanceof TextView) {
954            final int iconHeight = ((TextView)v).getCompoundPaddingTop() - v.getPaddingTop();
955            clipRect.bottom = clipRect.top + iconHeight;
956        }
957
958        // Draw the View into the bitmap.
959        // The translate of scrollX and scrollY is necessary when drawing TextViews, because
960        // they set scrollX and scrollY to large values to achieve centered text
961
962        destCanvas.save();
963        destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
964        destCanvas.clipRect(clipRect, Op.REPLACE);
965        v.draw(destCanvas);
966        destCanvas.restore();
967    }
968
969    /**
970     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
971     * Responsibility for the bitmap is transferred to the caller.
972     */
973    private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
974        final int outlineColor = getResources().getColor(R.color.drag_outline_color);
975        final Bitmap b = Bitmap.createBitmap(
976                v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
977
978        canvas.setBitmap(b);
979        drawDragView(v, canvas, padding);
980        mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true);
981
982        return b;
983    }
984
985    /**
986     * Returns a new bitmap to show when the given View is being dragged around.
987     * Responsibility for the bitmap is transferred to the caller.
988     */
989    private Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
990        final int outlineColor = getResources().getColor(R.color.drag_outline_color);
991        final Bitmap b = Bitmap.createBitmap(
992                mDragOutline.getWidth(), mDragOutline.getHeight(), Bitmap.Config.ARGB_8888);
993
994        canvas.setBitmap(b);
995        canvas.drawBitmap(mDragOutline, 0, 0, null);
996        drawDragView(v, canvas, padding);
997        mOutlineHelper.applyOuterBlur(b, canvas, outlineColor);
998
999        return b;
1000    }
1001
1002    void startDrag(CellLayout.CellInfo cellInfo) {
1003        View child = cellInfo.cell;
1004
1005        // Make sure the drag was started by a long press as opposed to a long click.
1006        if (!child.isInTouchMode()) {
1007            return;
1008        }
1009
1010        mDragInfo = cellInfo;
1011        mDragInfo.screen = mCurrentPage;
1012
1013        CellLayout current = getCurrentDropLayout();
1014
1015        current.onDragChild(child);
1016
1017        child.clearFocus();
1018        child.setPressed(false);
1019
1020        final Canvas canvas = new Canvas();
1021
1022        // We need to add extra padding to the bitmap to make room for the glow effect
1023        final int bitmapPadding = HolographicOutlineHelper.OUTER_BLUR_RADIUS;
1024
1025        // The outline is used to visualize where the item will land if dropped
1026        mDragOutline = createDragOutline(child, canvas, bitmapPadding);
1027
1028        // The drag bitmap follows the touch point around on the screen
1029        final Bitmap b = createDragBitmap(child, canvas, bitmapPadding);
1030
1031        final int bmpWidth = b.getWidth();
1032        final int bmpHeight = b.getHeight();
1033        child.getLocationOnScreen(mTempXY);
1034        final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
1035        final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2;
1036        mDragController.startDrag(b, screenX, screenY, 0, 0, bmpWidth, bmpHeight, this,
1037                child.getTag(), DragController.DRAG_ACTION_MOVE, null);
1038        b.recycle();
1039    }
1040
1041    void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY,
1042            boolean insertAtFirst, int intersectX, int intersectY) {
1043        final CellLayout cellLayout = (CellLayout) getChildAt(screen);
1044        View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info);
1045
1046        final int[] cellXY = new int[2];
1047        cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
1048        addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
1049        LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1050                LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1051                cellXY[0], cellXY[1]);
1052    }
1053
1054    private void setPositionForDropAnimation(
1055            View dragView, int dragViewX, int dragViewY, View parent, View child) {
1056        final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1057
1058        // Based on the position of the drag view, find the top left of the original view
1059        int viewX = dragViewX + (dragView.getWidth() - child.getWidth()) / 2;
1060        int viewY = dragViewY + (dragView.getHeight() - child.getHeight()) / 2;
1061        viewX -= getResources().getInteger(R.integer.config_dragViewOffsetX);
1062        viewY -= getResources().getInteger(R.integer.config_dragViewOffsetY);
1063
1064        // Set its old pos (in the new parent's coordinates); the CellLayout will
1065        // animate it from this position during the next layout pass
1066        lp.oldX = viewX - (parent.getLeft() - mScrollX);
1067        lp.oldY = viewY - (parent.getTop() - mScrollY);
1068    }
1069
1070    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
1071            DragView dragView, Object dragInfo) {
1072
1073        int originX = x - xOffset;
1074        int originY = y - yOffset;
1075
1076        if (mDragTargetLayout == null) {
1077            // Cancel the drag if we're not over a screen at time of drop
1078            if (mDragInfo != null) {
1079                // Set its position so the parent can animate it back
1080                final View parent = getChildAt(mDragInfo.screen);
1081                setPositionForDropAnimation(dragView, originX, originY, parent, mDragInfo.cell);
1082            }
1083            return;
1084        }
1085
1086        if (mIsSmall || mIsInUnshrinkAnimation) {
1087            // get originX and originY in the local coordinate system of the screen
1088            mTempOriginXY[0] = originX;
1089            mTempOriginXY[1] = originY;
1090            mapPointFromSelfToChild(mDragTargetLayout, mTempOriginXY);
1091            originX = (int)mTempOriginXY[0];
1092            originY = (int)mTempOriginXY[1];
1093        }
1094
1095        if (source != this) {
1096            onDropExternal(originX, originY, dragInfo, mDragTargetLayout);
1097        } else if (mDragInfo != null) {
1098            // Move internally
1099            final View cell = mDragInfo.cell;
1100            mTargetCell = findNearestVacantArea(originX, originY,
1101                    mDragInfo.spanX, mDragInfo.spanY, cell, mDragTargetLayout,
1102                    mTargetCell);
1103
1104            int screen = indexOfChild(mDragTargetLayout);
1105            if (screen != mDragInfo.screen) {
1106                final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1107                originalCellLayout.removeView(cell);
1108                addInScreen(cell, screen, mTargetCell[0], mTargetCell[1],
1109                        mDragInfo.spanX, mDragInfo.spanY);
1110            }
1111            mDragTargetLayout.onDropChild(cell);
1112
1113            // update the item's position after drop
1114            final ItemInfo info = (ItemInfo) cell.getTag();
1115            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
1116            mDragTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
1117            lp.cellX = mTargetCell[0];
1118            lp.cellY = mTargetCell[1];
1119            cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen,
1120                    mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
1121
1122            LauncherModel.moveItemInDatabase(mLauncher, info,
1123                    LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1124                    lp.cellX, lp.cellY);
1125
1126            // Prepare it to be animated into its new position
1127            // This must be called after the view has been re-parented
1128            setPositionForDropAnimation(dragView, originX, originY, mDragTargetLayout, cell);
1129        }
1130    }
1131
1132    public void onDragEnter(DragSource source, int x, int y, int xOffset,
1133            int yOffset, DragView dragView, Object dragInfo) {
1134        mDragTargetLayout = null; // Reset the drag state
1135
1136        if (!mIsSmall) {
1137            mDragTargetLayout = getCurrentDropLayout();
1138            mDragTargetLayout.onDragEnter(dragView);
1139            showOutlines();
1140        }
1141    }
1142
1143    public DropTarget getDropTargetDelegate(DragSource source, int x, int y,
1144            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1145
1146        if (mIsSmall || mIsInUnshrinkAnimation) {
1147            // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens
1148            return null;
1149        }
1150        // We may need to delegate the drag to a child view. If a 1x1 item
1151        // would land in a cell occupied by a DragTarget (e.g. a Folder),
1152        // then drag events should be handled by that child.
1153
1154        ItemInfo item = (ItemInfo)dragInfo;
1155        CellLayout currentLayout = getCurrentDropLayout();
1156
1157        int dragPointX, dragPointY;
1158        if (item.spanX == 1 && item.spanY == 1) {
1159            // For a 1x1, calculate the drop cell exactly as in onDragOver
1160            dragPointX = x - xOffset;
1161            dragPointY = y - yOffset;
1162        } else {
1163            // Otherwise, use the exact drag coordinates
1164            dragPointX = x;
1165            dragPointY = y;
1166        }
1167        dragPointX += mScrollX - currentLayout.getLeft();
1168        dragPointY += mScrollY - currentLayout.getTop();
1169
1170        // If we are dragging over a cell that contains a DropTarget that will
1171        // accept the drop, delegate to that DropTarget.
1172        final int[] cellXY = mTempCell;
1173        currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
1174        View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
1175        if (child instanceof DropTarget) {
1176            DropTarget target = (DropTarget)child;
1177            if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
1178                return target;
1179            }
1180        }
1181        return null;
1182    }
1183
1184    /*
1185    *
1186    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
1187    * coordinate space. The argument xy is modified with the return result.
1188    *
1189    */
1190   void mapPointFromSelfToChild(View v, float[] xy) {
1191       mapPointFromSelfToChild(v, xy, null);
1192   }
1193
1194   /*
1195    *
1196    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
1197    * coordinate space. The argument xy is modified with the return result.
1198    *
1199    * if cachedInverseMatrix is not null, this method will just use that matrix instead of
1200    * computing it itself; we use this to avoid redundant matrix inversions in
1201    * findMatchingPageForDragOver
1202    *
1203    */
1204   void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
1205       if (cachedInverseMatrix == null) {
1206           v.getMatrix().invert(mTempInverseMatrix);
1207           cachedInverseMatrix = mTempInverseMatrix;
1208       }
1209       xy[0] = xy[0] + mScrollX - v.getLeft();
1210       xy[1] = xy[1] + mScrollY - v.getTop();
1211       cachedInverseMatrix.mapPoints(xy);
1212   }
1213
1214   /*
1215    *
1216    * Convert the 2D coordinate xy from this CellLayout's coordinate space to
1217    * the parent View's coordinate space. The argument xy is modified with the return result.
1218    *
1219    */
1220   void mapPointFromChildToSelf(View v, float[] xy) {
1221       v.getMatrix().mapPoints(xy);
1222       xy[0] -= (mScrollX - v.getLeft());
1223       xy[1] -= (mScrollY - v.getTop());
1224   }
1225
1226    static private float squaredDistance(float[] point1, float[] point2) {
1227        float distanceX = point1[0] - point2[0];
1228        float distanceY = point2[1] - point2[1];
1229        return distanceX * distanceX + distanceY * distanceY;
1230    }
1231
1232    /*
1233     *
1234     * Returns true if the passed CellLayout cl overlaps with dragView
1235     *
1236     */
1237    boolean overlaps(CellLayout cl, DragView dragView,
1238            int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
1239        // Transform the coordinates of the item being dragged to the CellLayout's coordinates
1240        final float[] draggedItemTopLeft = mTempDragCoordinates;
1241        draggedItemTopLeft[0] = dragViewX + dragView.getScaledDragRegionXOffset();
1242        draggedItemTopLeft[1] = dragViewY + dragView.getScaledDragRegionYOffset();
1243        final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
1244        draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getScaledDragRegionWidth();
1245        draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getScaledDragRegionHeight();
1246
1247        // Transform the dragged item's top left coordinates
1248        // to the CellLayout's local coordinates
1249        mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
1250        float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
1251        float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
1252
1253        if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
1254            // Transform the dragged item's bottom right coordinates
1255            // to the CellLayout's local coordinates
1256            mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
1257            float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
1258            float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
1259
1260            if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
1261                float overlap = (overlapRegionRight - overlapRegionLeft) *
1262                         (overlapRegionBottom - overlapRegionTop);
1263                if (overlap > 0) {
1264                    return true;
1265                }
1266             }
1267        }
1268        return false;
1269    }
1270
1271    /*
1272     *
1273     * This method returns the CellLayout that is currently being dragged to. In order to drag
1274     * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
1275     * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
1276     *
1277     * Return null if no CellLayout is currently being dragged over
1278     *
1279     */
1280    private CellLayout findMatchingPageForDragOver(
1281            DragView dragView, int originX, int originY, int offsetX, int offsetY) {
1282        // We loop through all the screens (ie CellLayouts) and see which ones overlap
1283        // with the item being dragged and then choose the one that's closest to the touch point
1284        final int screenCount = getChildCount();
1285        CellLayout bestMatchingScreen = null;
1286        float smallestDistSoFar = Float.MAX_VALUE;
1287
1288        for (int i = 0; i < screenCount; i++) {
1289            CellLayout cl = (CellLayout)getChildAt(i);
1290
1291            final float[] touchXy = mTempTouchCoordinates;
1292            touchXy[0] = originX + offsetX;
1293            touchXy[1] = originY + offsetY;
1294
1295            // Transform the touch coordinates to the CellLayout's local coordinates
1296            // If the touch point is within the bounds of the cell layout, we can return immediately
1297            cl.getMatrix().invert(mTempInverseMatrix);
1298            mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
1299
1300            if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
1301                    touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
1302                return cl;
1303            }
1304
1305            if (overlaps(cl, dragView, originX, originY, mTempInverseMatrix)) {
1306                // Get the center of the cell layout in screen coordinates
1307                final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
1308                cellLayoutCenter[0] = cl.getWidth()/2;
1309                cellLayoutCenter[1] = cl.getHeight()/2;
1310                mapPointFromChildToSelf(cl, cellLayoutCenter);
1311
1312                touchXy[0] = originX + offsetX;
1313                touchXy[1] = originY + offsetY;
1314
1315                // Calculate the distance between the center of the CellLayout
1316                // and the touch point
1317                float dist = squaredDistance(touchXy, cellLayoutCenter);
1318
1319                if (dist < smallestDistSoFar) {
1320                    smallestDistSoFar = dist;
1321                    bestMatchingScreen = cl;
1322                }
1323            }
1324        }
1325        return bestMatchingScreen;
1326    }
1327
1328    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
1329            DragView dragView, Object dragInfo) {
1330        // When touch is inside the scroll area, skip dragOver actions for the current screen
1331        if (!mInScrollArea) {
1332            CellLayout layout;
1333            int originX = x - xOffset;
1334            int originY = y - yOffset;
1335            if (mIsSmall || mIsInUnshrinkAnimation) {
1336                layout = findMatchingPageForDragOver(
1337                        dragView, originX, originY, xOffset, yOffset);
1338
1339                if (layout != mDragTargetLayout) {
1340                    if (mDragTargetLayout != null) {
1341                        mDragTargetLayout.setHover(false);
1342                    }
1343                    mDragTargetLayout = layout;
1344                    if (mDragTargetLayout != null) {
1345                        mDragTargetLayout.setHover(true);
1346                    }
1347                }
1348            } else {
1349                layout = getCurrentDropLayout();
1350
1351                final ItemInfo item = (ItemInfo)dragInfo;
1352                if (dragInfo instanceof LauncherAppWidgetInfo) {
1353                    LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
1354
1355                    if (widgetInfo.spanX == -1) {
1356                        // Calculate the grid spans needed to fit this widget
1357                        int[] spans = layout.rectToCell(
1358                                widgetInfo.minWidth, widgetInfo.minHeight, null);
1359                        item.spanX = spans[0];
1360                        item.spanY = spans[1];
1361                    }
1362                }
1363
1364                if (source instanceof AllAppsPagedView) {
1365                    // This is a hack to fix the point used to determine which cell an icon from
1366                    // the all apps screen is over
1367                    if (item != null && item.spanX == 1 && layout != null) {
1368                        int dragRegionLeft = (dragView.getWidth() - layout.getCellWidth()) / 2;
1369
1370                        originX += dragRegionLeft - dragView.getDragRegionLeft();
1371                        if (dragView.getDragRegionWidth() != layout.getCellWidth()) {
1372                            dragView.setDragRegion(dragView.getDragRegionLeft(),
1373                                    dragView.getDragRegionTop(),
1374                                    layout.getCellWidth(),
1375                                    dragView.getDragRegionHeight());
1376                        }
1377                    }
1378                }
1379
1380                if (layout != mDragTargetLayout) {
1381                    if (mDragTargetLayout != null) {
1382                        mDragTargetLayout.onDragExit();
1383                    }
1384                    layout.onDragEnter(dragView);
1385                    mDragTargetLayout = layout;
1386                }
1387
1388                // only visualize the drop locations for moving icons within the home screen on
1389                // tablet on phone, we also visualize icons dragged in from All Apps
1390                if ((!LauncherApplication.isScreenXLarge() || source == this)
1391                        && mDragTargetLayout != null) {
1392                    final View child = (mDragInfo == null) ? null : mDragInfo.cell;
1393                    int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX);
1394                    int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY);
1395                    mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
1396                            localOriginX, localOriginY, item.spanX, item.spanY);
1397                }
1398            }
1399        }
1400    }
1401
1402    public void onDragExit(DragSource source, int x, int y, int xOffset,
1403            int yOffset, DragView dragView, Object dragInfo) {
1404        if (mDragTargetLayout != null) {
1405            mDragTargetLayout.onDragExit();
1406        }
1407        if (!mIsPageMoving) {
1408            hideOutlines();
1409        }
1410        clearAllHovers();
1411    }
1412
1413    private void onDropExternal(int x, int y, Object dragInfo,
1414            CellLayout cellLayout) {
1415        onDropExternal(x, y, dragInfo, cellLayout, false);
1416    }
1417
1418    /**
1419     * Add the item specified by dragInfo to the given layout.
1420     * This is basically the equivalent of onDropExternal, except it's not initiated
1421     * by drag and drop.
1422     * @return true if successful
1423     */
1424    public boolean addExternalItemToScreen(Object dragInfo, View layout) {
1425        CellLayout cl = (CellLayout) layout;
1426        ItemInfo info = (ItemInfo) dragInfo;
1427
1428        if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) {
1429            onDropExternal(0, 0, dragInfo, cl, false);
1430            return true;
1431        }
1432        mLauncher.showOutOfSpaceMessage();
1433        return false;
1434    }
1435
1436    // Drag from somewhere else
1437    // NOTE: This can also be called when we are outside of a drag event, when we want
1438    // to add an item to one of the workspace screens.
1439    private void onDropExternal(int x, int y, Object dragInfo,
1440            CellLayout cellLayout, boolean insertAtFirst) {
1441        int screen = indexOfChild(cellLayout);
1442        if (dragInfo instanceof PendingAddItemInfo) {
1443            PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
1444            // When dragging and dropping from customization tray, we deal with creating
1445            // widgets/shortcuts/folders in a slightly different way
1446            int[] touchXY = new int[2];
1447            touchXY[0] = x;
1448            touchXY[1] = y;
1449            switch (info.itemType) {
1450                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1451                    mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY);
1452                    break;
1453                case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
1454                    mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY);
1455                    break;
1456                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1457                    mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY);
1458                    break;
1459                default:
1460                    throw new IllegalStateException("Unknown item type: " + info.itemType);
1461            }
1462            cellLayout.onDragExit();
1463            cellLayout.animateDrop();
1464            return;
1465        }
1466
1467        // This is for other drag/drop cases, like dragging from All Apps
1468        ItemInfo info = (ItemInfo) dragInfo;
1469
1470        View view = null;
1471
1472        switch (info.itemType) {
1473        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1474        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1475            if (info.container == NO_ID && info instanceof ApplicationInfo) {
1476                // Came from all apps -- make a copy
1477                info = new ShortcutInfo((ApplicationInfo) info);
1478            }
1479            view = mLauncher.createShortcut(R.layout.application, cellLayout,
1480                    (ShortcutInfo) info);
1481            break;
1482        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1483            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
1484                    cellLayout, ((UserFolderInfo) info));
1485            break;
1486        default:
1487            throw new IllegalStateException("Unknown item type: " + info.itemType);
1488        }
1489
1490        // If the view is null, it has already been added.
1491        if (view == null) {
1492            cellLayout.onDragExit();
1493        } else {
1494            mTargetCell = findNearestVacantArea(x, y, 1, 1, null, cellLayout, mTargetCell);
1495            addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
1496                    mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
1497            cellLayout.onDropChild(view);
1498            cellLayout.animateDrop();
1499            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1500
1501            LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1502                    LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1503                    lp.cellX, lp.cellY);
1504        }
1505    }
1506
1507    /**
1508     * Return the current {@link CellLayout}, correctly picking the destination
1509     * screen while a scroll is in progress.
1510     */
1511    private CellLayout getCurrentDropLayout() {
1512        // if we're currently small, use findMatchingPageForDragOver instead
1513        if (mIsSmall) return null;
1514        int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
1515        return (CellLayout) getChildAt(index);
1516    }
1517
1518    /**
1519     * Return the current CellInfo describing our current drag; this method exists
1520     * so that Launcher can sync this object with the correct info when the activity is created/
1521     * destroyed
1522     *
1523     */
1524    public CellLayout.CellInfo getDragInfo() {
1525        return mDragInfo;
1526    }
1527
1528    /**
1529     * {@inheritDoc}
1530     */
1531    public boolean acceptDrop(DragSource source, int x, int y,
1532            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1533        if (mDragTargetLayout == null) {
1534            // cancel the drag if we're not over a screen at time of drop
1535            return false;
1536        }
1537
1538        final CellLayout.CellInfo dragCellInfo = mDragInfo;
1539        final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
1540        final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
1541
1542        final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
1543
1544        if (mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
1545            return true;
1546        } else {
1547            mLauncher.showOutOfSpaceMessage();
1548            return false;
1549        }
1550    }
1551
1552    /**
1553     * Calculate the nearest cell where the given object would be dropped.
1554     */
1555    private int[] findNearestVacantArea(int pixelX, int pixelY,
1556            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1557
1558        int localPixelX = pixelX - (layout.getLeft() - mScrollX);
1559        int localPixelY = pixelY - (layout.getTop() - mScrollY);
1560
1561        // Find the best target drop location
1562        return layout.findNearestVacantArea(
1563                localPixelX, localPixelY, spanX, spanY, ignoreView, recycle);
1564    }
1565
1566    /**
1567     * Estimate the size that a child with the given dimensions will take in the current screen.
1568     */
1569    void estimateChildSize(int minWidth, int minHeight, int[] result) {
1570        ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result);
1571    }
1572
1573    void setLauncher(Launcher launcher) {
1574        mLauncher = launcher;
1575    }
1576
1577    public void setDragController(DragController dragController) {
1578        mDragController = dragController;
1579    }
1580
1581    public void onDropCompleted(View target, boolean success) {
1582        if (success) {
1583            if (target != this && mDragInfo != null) {
1584                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1585                cellLayout.removeView(mDragInfo.cell);
1586                if (mDragInfo.cell instanceof DropTarget) {
1587                    mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1588                }
1589                // final Object tag = mDragInfo.cell.getTag();
1590            }
1591        } else if (mDragInfo != null) {
1592            ((CellLayout) getChildAt(mDragInfo.screen)).onDropAborted(mDragInfo.cell);
1593        }
1594
1595        mDragOutline = null;
1596        mDragInfo = null;
1597    }
1598
1599    public boolean isDropEnabled() {
1600        return true;
1601    }
1602
1603    @Override
1604    protected void onRestoreInstanceState(Parcelable state) {
1605        super.onRestoreInstanceState(state);
1606        Launcher.setScreen(mCurrentPage);
1607    }
1608
1609    @Override
1610    public void scrollLeft() {
1611        if (!mIsSmall && !mIsInUnshrinkAnimation) {
1612            super.scrollLeft();
1613        }
1614    }
1615
1616    @Override
1617    public void scrollRight() {
1618        if (!mIsSmall && !mIsInUnshrinkAnimation) {
1619            super.scrollRight();
1620        }
1621    }
1622
1623    @Override
1624    public void onEnterScrollArea(int direction) {
1625        if (!mIsSmall && !mIsInUnshrinkAnimation) {
1626            mInScrollArea = true;
1627            final int screen = getCurrentPage() + ((direction == DragController.SCROLL_LEFT) ? -1 : 1);
1628            if (0 <= screen && screen < getChildCount()) {
1629                ((CellLayout) getChildAt(screen)).setHover(true);
1630            }
1631
1632            if (mDragTargetLayout != null) {
1633                mDragTargetLayout.onDragExit();
1634                mDragTargetLayout = null;
1635            }
1636        }
1637    }
1638
1639    private void clearAllHovers() {
1640        final int childCount = getChildCount();
1641        for (int i = 0; i < childCount; i++) {
1642            ((CellLayout) getChildAt(i)).setHover(false);
1643        }
1644    }
1645
1646    @Override
1647    public void onExitScrollArea() {
1648        if (mInScrollArea) {
1649            mInScrollArea = false;
1650            clearAllHovers();
1651        }
1652    }
1653
1654    public Folder getFolderForTag(Object tag) {
1655        final int screenCount = getChildCount();
1656        for (int screen = 0; screen < screenCount; screen++) {
1657            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1658            int count = currentScreen.getChildCount();
1659            for (int i = 0; i < count; i++) {
1660                View child = currentScreen.getChildAt(i);
1661                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1662                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1663                    Folder f = (Folder) child;
1664                    if (f.getInfo() == tag && f.getInfo().opened) {
1665                        return f;
1666                    }
1667                }
1668            }
1669        }
1670        return null;
1671    }
1672
1673    public View getViewForTag(Object tag) {
1674        int screenCount = getChildCount();
1675        for (int screen = 0; screen < screenCount; screen++) {
1676            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1677            int count = currentScreen.getChildCount();
1678            for (int i = 0; i < count; i++) {
1679                View child = currentScreen.getChildAt(i);
1680                if (child.getTag() == tag) {
1681                    return child;
1682                }
1683            }
1684        }
1685        return null;
1686    }
1687
1688
1689    void removeItems(final ArrayList<ApplicationInfo> apps) {
1690        final int screenCount = getChildCount();
1691        final PackageManager manager = getContext().getPackageManager();
1692        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
1693
1694        final HashSet<String> packageNames = new HashSet<String>();
1695        final int appCount = apps.size();
1696        for (int i = 0; i < appCount; i++) {
1697            packageNames.add(apps.get(i).componentName.getPackageName());
1698        }
1699
1700        for (int i = 0; i < screenCount; i++) {
1701            final CellLayout layout = (CellLayout) getChildAt(i);
1702
1703            // Avoid ANRs by treating each screen separately
1704            post(new Runnable() {
1705                public void run() {
1706                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
1707                    childrenToRemove.clear();
1708
1709                    int childCount = layout.getChildCount();
1710                    for (int j = 0; j < childCount; j++) {
1711                        final View view = layout.getChildAt(j);
1712                        Object tag = view.getTag();
1713
1714                        if (tag instanceof ShortcutInfo) {
1715                            final ShortcutInfo info = (ShortcutInfo) tag;
1716                            final Intent intent = info.intent;
1717                            final ComponentName name = intent.getComponent();
1718
1719                            if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1720                                for (String packageName: packageNames) {
1721                                    if (packageName.equals(name.getPackageName())) {
1722                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1723                                        childrenToRemove.add(view);
1724                                    }
1725                                }
1726                            }
1727                        } else if (tag instanceof UserFolderInfo) {
1728                            final UserFolderInfo info = (UserFolderInfo) tag;
1729                            final ArrayList<ShortcutInfo> contents = info.contents;
1730                            final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
1731                            final int contentsCount = contents.size();
1732                            boolean removedFromFolder = false;
1733
1734                            for (int k = 0; k < contentsCount; k++) {
1735                                final ShortcutInfo appInfo = contents.get(k);
1736                                final Intent intent = appInfo.intent;
1737                                final ComponentName name = intent.getComponent();
1738
1739                                if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1740                                    for (String packageName: packageNames) {
1741                                        if (packageName.equals(name.getPackageName())) {
1742                                            toRemove.add(appInfo);
1743                                            LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
1744                                            removedFromFolder = true;
1745                                        }
1746                                    }
1747                                }
1748                            }
1749
1750                            contents.removeAll(toRemove);
1751                            if (removedFromFolder) {
1752                                final Folder folder = getOpenFolder();
1753                                if (folder != null)
1754                                    folder.notifyDataSetChanged();
1755                            }
1756                        } else if (tag instanceof LiveFolderInfo) {
1757                            final LiveFolderInfo info = (LiveFolderInfo) tag;
1758                            final Uri uri = info.uri;
1759                            final ProviderInfo providerInfo = manager.resolveContentProvider(
1760                                    uri.getAuthority(), 0);
1761
1762                            if (providerInfo != null) {
1763                                for (String packageName: packageNames) {
1764                                    if (packageName.equals(providerInfo.packageName)) {
1765                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1766                                        childrenToRemove.add(view);
1767                                    }
1768                                }
1769                            }
1770                        } else if (tag instanceof LauncherAppWidgetInfo) {
1771                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1772                            final AppWidgetProviderInfo provider =
1773                                    widgets.getAppWidgetInfo(info.appWidgetId);
1774                            if (provider != null) {
1775                                for (String packageName: packageNames) {
1776                                    if (packageName.equals(provider.provider.getPackageName())) {
1777                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1778                                        childrenToRemove.add(view);
1779                                    }
1780                                }
1781                            }
1782                        }
1783                    }
1784
1785                    childCount = childrenToRemove.size();
1786                    for (int j = 0; j < childCount; j++) {
1787                        View child = childrenToRemove.get(j);
1788                        layout.removeViewInLayout(child);
1789                        if (child instanceof DropTarget) {
1790                            mDragController.removeDropTarget((DropTarget)child);
1791                        }
1792                    }
1793
1794                    if (childCount > 0) {
1795                        layout.requestLayout();
1796                        layout.invalidate();
1797                    }
1798                }
1799            });
1800        }
1801    }
1802
1803    void updateShortcuts(ArrayList<ApplicationInfo> apps) {
1804        final int screenCount = getChildCount();
1805        for (int i = 0; i < screenCount; i++) {
1806            final CellLayout layout = (CellLayout) getChildAt(i);
1807            int childCount = layout.getChildCount();
1808            for (int j = 0; j < childCount; j++) {
1809                final View view = layout.getChildAt(j);
1810                Object tag = view.getTag();
1811                if (tag instanceof ShortcutInfo) {
1812                    ShortcutInfo info = (ShortcutInfo)tag;
1813                    // We need to check for ACTION_MAIN otherwise getComponent() might
1814                    // return null for some shortcuts (for instance, for shortcuts to
1815                    // web pages.)
1816                    final Intent intent = info.intent;
1817                    final ComponentName name = intent.getComponent();
1818                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1819                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1820                        final int appCount = apps.size();
1821                        for (int k = 0; k < appCount; k++) {
1822                            ApplicationInfo app = apps.get(k);
1823                            if (app.componentName.equals(name)) {
1824                                info.setIcon(mIconCache.getIcon(info.intent));
1825                                ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
1826                                        new FastBitmapDrawable(info.getIcon(mIconCache)),
1827                                        null, null);
1828                                }
1829                        }
1830                    }
1831                }
1832            }
1833        }
1834    }
1835
1836    void moveToDefaultScreen(boolean animate) {
1837        if (mIsSmall || mIsInUnshrinkAnimation) {
1838            mLauncher.showWorkspace(animate, (CellLayout)getChildAt(mDefaultPage));
1839        } else if (animate) {
1840            snapToPage(mDefaultPage);
1841        } else {
1842            setCurrentPage(mDefaultPage);
1843        }
1844        getChildAt(mDefaultPage).requestFocus();
1845    }
1846
1847    void setIndicators(Drawable previous, Drawable next) {
1848        mPreviousIndicator = previous;
1849        mNextIndicator = next;
1850        previous.setLevel(mCurrentPage);
1851        next.setLevel(mCurrentPage);
1852    }
1853
1854    @Override
1855    public void syncPages() {
1856    }
1857
1858    @Override
1859    public void syncPageItems(int page) {
1860    }
1861
1862}
1863