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