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