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