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