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