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