DragLayer.java revision 6441de0ec2a71862798dd51180d0811b42edd514
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 android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.animation.TimeInterpolator;
23import android.animation.ValueAnimator;
24import android.animation.ValueAnimator.AnimatorUpdateListener;
25import android.content.Context;
26import android.content.res.Resources;
27import android.graphics.Canvas;
28import android.graphics.Rect;
29import android.graphics.drawable.Drawable;
30import android.util.AttributeSet;
31import android.view.KeyEvent;
32import android.view.MotionEvent;
33import android.view.View;
34import android.view.ViewParent;
35import android.view.accessibility.AccessibilityEvent;
36import android.view.accessibility.AccessibilityManager;
37import android.view.animation.DecelerateInterpolator;
38import android.view.animation.Interpolator;
39import android.widget.FrameLayout;
40import android.widget.TextView;
41
42import com.android.launcher.R;
43
44import java.util.ArrayList;
45
46/**
47 * A ViewGroup that coordinates dragging across its descendants
48 */
49public class DragLayer extends FrameLayout {
50    private DragController mDragController;
51    private int[] mTmpXY = new int[2];
52
53    private int mXDown, mYDown;
54    private Launcher mLauncher;
55
56    // Variables relating to resizing widgets
57    private final ArrayList<AppWidgetResizeFrame> mResizeFrames =
58            new ArrayList<AppWidgetResizeFrame>();
59    private AppWidgetResizeFrame mCurrentResizeFrame;
60
61    // Variables relating to animation of views after drop
62    private ValueAnimator mDropAnim = null;
63    private ValueAnimator mFadeOutAnim = null;
64    private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
65    private View mDropView = null;
66    private int mAnchorViewInitialScrollX = 0;
67    private View mAnchorView = null;
68
69    private int[] mDropViewPos = new int[2];
70    private float mDropViewScale;
71    private float mDropViewAlpha;
72    private boolean mHoverPointClosesFolder = false;
73    private Rect mHitRect = new Rect();
74    private int mWorkspaceIndex = -1;
75    private int mQsbIndex = -1;
76
77    /**
78     * Used to create a new DragLayer from XML.
79     *
80     * @param context The application's context.
81     * @param attrs The attributes set containing the Workspace's customization values.
82     */
83    public DragLayer(Context context, AttributeSet attrs) {
84        super(context, attrs);
85
86        // Disable multitouch across the workspace/all apps/customize tray
87        setMotionEventSplittingEnabled(false);
88        setChildrenDrawingOrderEnabled(true);
89    }
90
91    public void setup(Launcher launcher, DragController controller) {
92        mLauncher = launcher;
93        mDragController = controller;
94    }
95
96    @Override
97    public boolean dispatchKeyEvent(KeyEvent event) {
98        return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
99    }
100
101    private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
102        getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
103        if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
104            return true;
105        }
106        return false;
107    }
108
109    private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
110        getDescendantRectRelativeToSelf(folder, mHitRect);
111        if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
112            return true;
113        }
114        return false;
115    }
116
117    private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
118        Rect hitRect = new Rect();
119        int x = (int) ev.getX();
120        int y = (int) ev.getY();
121
122        for (AppWidgetResizeFrame child: mResizeFrames) {
123            child.getHitRect(hitRect);
124            if (hitRect.contains(x, y)) {
125                if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) {
126                    mCurrentResizeFrame = child;
127                    mXDown = x;
128                    mYDown = y;
129                    requestDisallowInterceptTouchEvent(true);
130                    return true;
131                }
132            }
133        }
134
135        Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
136        if (currentFolder != null && !mLauncher.isFolderClingVisible() && intercept) {
137            if (currentFolder.isEditingName()) {
138                if (!isEventOverFolderTextRegion(currentFolder, ev)) {
139                    currentFolder.dismissEditingName();
140                    return true;
141                }
142            }
143
144            getDescendantRectRelativeToSelf(currentFolder, hitRect);
145            if (!isEventOverFolder(currentFolder, ev)) {
146                mLauncher.closeFolder();
147                return true;
148            }
149        }
150        return false;
151    }
152
153    @Override
154    public boolean onInterceptTouchEvent(MotionEvent ev) {
155        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
156            if (handleTouchDown(ev, true)) {
157                return true;
158            }
159        }
160        clearAllResizeFrames();
161        return mDragController.onInterceptTouchEvent(ev);
162    }
163
164    @Override
165    public boolean onInterceptHoverEvent(MotionEvent ev) {
166        Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
167        if (currentFolder == null) {
168            return false;
169        } else {
170            if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
171                final int action = ev.getAction();
172                boolean isOverFolder;
173                switch (action) {
174                    case MotionEvent.ACTION_HOVER_ENTER:
175                        isOverFolder = isEventOverFolder(currentFolder, ev);
176                        if (!isOverFolder) {
177                            sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
178                            mHoverPointClosesFolder = true;
179                            return true;
180                        } else if (isOverFolder) {
181                            mHoverPointClosesFolder = false;
182                        } else {
183                            return true;
184                        }
185                    case MotionEvent.ACTION_HOVER_MOVE:
186                        isOverFolder = isEventOverFolder(currentFolder, ev);
187                        if (!isOverFolder && !mHoverPointClosesFolder) {
188                            sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
189                            mHoverPointClosesFolder = true;
190                            return true;
191                        } else if (isOverFolder) {
192                            mHoverPointClosesFolder = false;
193                        } else {
194                            return true;
195                        }
196                }
197            }
198        }
199        return false;
200    }
201
202    private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
203        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
204            int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
205            AccessibilityEvent event = AccessibilityEvent.obtain(
206                    AccessibilityEvent.TYPE_VIEW_FOCUSED);
207            onInitializeAccessibilityEvent(event);
208            event.getText().add(mContext.getString(stringId));
209            AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event);
210        }
211    }
212
213    @Override
214    public boolean onHoverEvent(MotionEvent ev) {
215        // If we've received this, we've already done the necessary handling
216        // in onInterceptHoverEvent. Return true to consume the event.
217        return false;
218    }
219
220    @Override
221    public boolean onTouchEvent(MotionEvent ev) {
222        boolean handled = false;
223        int action = ev.getAction();
224
225        int x = (int) ev.getX();
226        int y = (int) ev.getY();
227
228        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
229            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
230                if (handleTouchDown(ev, false)) {
231                    return true;
232                }
233            }
234        }
235
236        if (mCurrentResizeFrame != null) {
237            handled = true;
238            switch (action) {
239                case MotionEvent.ACTION_MOVE:
240                    mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
241                    break;
242                case MotionEvent.ACTION_CANCEL:
243                case MotionEvent.ACTION_UP:
244                    mCurrentResizeFrame.commitResizeForDelta(x - mXDown, y - mYDown);
245                    mCurrentResizeFrame = null;
246            }
247        }
248        if (handled) return true;
249        return mDragController.onTouchEvent(ev);
250    }
251
252    /**
253     * Determine the rect of the descendant in this DragLayer's coordinates
254     *
255     * @param descendant The descendant whose coordinates we want to find.
256     * @param r The rect into which to place the results.
257     * @return The factor by which this descendant is scaled relative to this DragLayer.
258     */
259    public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
260        mTmpXY[0] = 0;
261        mTmpXY[1] = 0;
262        float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
263        r.set(mTmpXY[0], mTmpXY[1],
264                mTmpXY[0] + descendant.getWidth(), mTmpXY[1] + descendant.getHeight());
265        return scale;
266    }
267
268    public void getLocationInDragLayer(View child, int[] loc) {
269        loc[0] = 0;
270        loc[1] = 0;
271        getDescendantCoordRelativeToSelf(child, loc);
272    }
273
274    /**
275     * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's
276     * coordinates.
277     *
278     * @param descendant The descendant to which the passed coordinate is relative.
279     * @param coord The coordinate that we want mapped.
280     * @return The factor by which this descendant is scaled relative to this DragLayer.
281     */
282    public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
283        float scale = 1.0f;
284        float[] pt = {coord[0], coord[1]};
285        descendant.getMatrix().mapPoints(pt);
286        scale *= descendant.getScaleX();
287        pt[0] += descendant.getLeft();
288        pt[1] += descendant.getTop();
289        ViewParent viewParent = descendant.getParent();
290        while (viewParent instanceof View && viewParent != this) {
291            final View view = (View)viewParent;
292            view.getMatrix().mapPoints(pt);
293            scale *= view.getScaleX();
294            pt[0] += view.getLeft() - view.getScrollX();
295            pt[1] += view.getTop() - view.getScrollY();
296            viewParent = view.getParent();
297        }
298        coord[0] = (int) Math.round(pt[0]);
299        coord[1] = (int) Math.round(pt[1]);
300        return scale;
301    }
302
303    public void getViewRectRelativeToSelf(View v, Rect r) {
304        int[] loc = new int[2];
305        getLocationInWindow(loc);
306        int x = loc[0];
307        int y = loc[1];
308
309        v.getLocationInWindow(loc);
310        int vX = loc[0];
311        int vY = loc[1];
312
313        int left = vX - x;
314        int top = vY - y;
315        r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
316    }
317
318    @Override
319    public boolean dispatchUnhandledMove(View focused, int direction) {
320        return mDragController.dispatchUnhandledMove(focused, direction);
321    }
322
323    public static class LayoutParams extends FrameLayout.LayoutParams {
324        public int x, y;
325        public boolean customPosition = false;
326
327        /**
328         * {@inheritDoc}
329         */
330        public LayoutParams(int width, int height) {
331            super(width, height);
332        }
333
334        public void setWidth(int width) {
335            this.width = width;
336        }
337
338        public int getWidth() {
339            return width;
340        }
341
342        public void setHeight(int height) {
343            this.height = height;
344        }
345
346        public int getHeight() {
347            return height;
348        }
349
350        public void setX(int x) {
351            this.x = x;
352        }
353
354        public int getX() {
355            return x;
356        }
357
358        public void setY(int y) {
359            this.y = y;
360        }
361
362        public int getY() {
363            return y;
364        }
365    }
366
367    protected void onLayout(boolean changed, int l, int t, int r, int b) {
368        super.onLayout(changed, l, t, r, b);
369        int count = getChildCount();
370        for (int i = 0; i < count; i++) {
371            View child = getChildAt(i);
372            final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
373            if (flp instanceof LayoutParams) {
374                final LayoutParams lp = (LayoutParams) flp;
375                if (lp.customPosition) {
376                    child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
377                }
378            }
379        }
380    }
381
382    public void clearAllResizeFrames() {
383        if (mResizeFrames.size() > 0) {
384            for (AppWidgetResizeFrame frame: mResizeFrames) {
385                removeView(frame);
386            }
387            mResizeFrames.clear();
388        }
389    }
390
391    public boolean hasResizeFrames() {
392        return mResizeFrames.size() > 0;
393    }
394
395    public boolean isWidgetBeingResized() {
396        return mCurrentResizeFrame != null;
397    }
398
399    public void addResizeFrame(ItemInfo itemInfo, LauncherAppWidgetHostView widget,
400            CellLayout cellLayout) {
401        AppWidgetResizeFrame resizeFrame = new AppWidgetResizeFrame(getContext(),
402                itemInfo, widget, cellLayout, this);
403
404        LayoutParams lp = new LayoutParams(-1, -1);
405        lp.customPosition = true;
406
407        addView(resizeFrame, lp);
408        mResizeFrames.add(resizeFrame);
409
410        resizeFrame.snapToWidget(false);
411    }
412
413    public void animateViewIntoPosition(DragView dragView, final View child) {
414        animateViewIntoPosition(dragView, child, null);
415    }
416
417    public void animateViewIntoPosition(DragView dragView, final int[] pos, float scale,
418            Runnable onFinishRunnable) {
419        Rect r = new Rect();
420        getViewRectRelativeToSelf(dragView, r);
421        final int fromX = r.left;
422        final int fromY = r.top;
423
424        animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], scale,
425                onFinishRunnable, true, -1, null);
426    }
427
428    public void animateViewIntoPosition(DragView dragView, final View child,
429            final Runnable onFinishAnimationRunnable) {
430        animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, null);
431    }
432
433    public void animateViewIntoPosition(DragView dragView, final View child, int duration,
434            final Runnable onFinishAnimationRunnable, View anchorView) {
435        ((CellLayoutChildren) child.getParent()).measureChild(child);
436        CellLayout.LayoutParams lp =  (CellLayout.LayoutParams) child.getLayoutParams();
437
438        Rect r = new Rect();
439        getViewRectRelativeToSelf(dragView, r);
440
441        int coord[] = new int[2];
442        coord[0] = lp.x;
443        coord[1] = lp.y;
444        // Since the child hasn't necessarily been laid out, we force the lp to be updated with
445        // the correct coordinates (above) and use these to determine the final location
446        float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
447        int toX = coord[0];
448        int toY = coord[1];
449        if (child instanceof TextView) {
450            TextView tv = (TextView) child;
451            Drawable d = tv.getCompoundDrawables()[1];
452
453            // Center in the y coordinate about the target's drawable
454            toY += Math.round(scale * tv.getPaddingTop());
455            toY -= (dragView.getHeight() - (int) Math.round(scale * d.getIntrinsicHeight())) / 2;
456            // Center in the x coordinate about the target's drawable
457            toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
458        } else if (child instanceof FolderIcon) {
459            // Account for holographic blur padding on the drag view
460            toY -= HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS / 2;
461            // Center in the x coordinate about the target's drawable
462            toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
463        } else {
464            toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2;
465            toX -= (Math.round(scale * (dragView.getMeasuredWidth()
466                    - child.getMeasuredWidth()))) / 2;
467        }
468
469        final int fromX = r.left;
470        final int fromY = r.top;
471        child.setVisibility(INVISIBLE);
472        child.setAlpha(0);
473        Runnable onCompleteRunnable = new Runnable() {
474            public void run() {
475                child.setVisibility(VISIBLE);
476                ObjectAnimator oa = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
477                oa.setDuration(60);
478                oa.addListener(new AnimatorListenerAdapter() {
479                    @Override
480                    public void onAnimationEnd(android.animation.Animator animation) {
481                        if (onFinishAnimationRunnable != null) {
482                            onFinishAnimationRunnable.run();
483                        }
484                    }
485                });
486                oa.start();
487            }
488        };
489        animateViewIntoPosition(dragView, fromX, fromY, toX, toY, scale,
490                onCompleteRunnable, true, duration, anchorView);
491    }
492
493    private void animateViewIntoPosition(final View view, final int fromX, final int fromY,
494            final int toX, final int toY, float finalScale, Runnable onCompleteRunnable,
495            boolean fadeOut, int duration, View anchorView) {
496        Rect from = new Rect(fromX, fromY, fromX +
497                view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
498        Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
499        animateView(view, from, to, 1f, finalScale, duration, null, null,
500                onCompleteRunnable, true, anchorView);
501    }
502
503    /**
504     * This method animates a view at the end of a drag and drop animation.
505     *
506     * @param view The view to be animated. This view is drawn directly into DragLayer, and so
507     *        doesn't need to be a child of DragLayer.
508     * @param from The initial location of the view. Only the left and top parameters are used.
509     * @param to The final location of the view. Only the left and top parameters are used. This
510     *        location doesn't account for scaling, and so should be centered about the desired
511     *        final location (including scaling).
512     * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates.
513     * @param finalScale The final scale of the view. The view is scaled about its center.
514     * @param duration The duration of the animation.
515     * @param motionInterpolator The interpolator to use for the location of the view.
516     * @param alphaInterpolator The interpolator to use for the alpha of the view.
517     * @param onCompleteRunnable Optional runnable to run on animation completion.
518     * @param fadeOut Whether or not to fade out the view once the animation completes. If true,
519     *        the runnable will execute after the view is faded out.
520     * @param anchorView If not null, this represents the view which the animated view stays
521     *        anchored to in case scrolling is currently taking place. Note: currently this is
522     *        only used for the X dimension for the case of the workspace.
523     */
524    public void animateView(final View view, final Rect from, final Rect to, final float finalAlpha,
525            final float finalScale, int duration, final Interpolator motionInterpolator,
526            final Interpolator alphaInterpolator, final Runnable onCompleteRunnable,
527            final boolean fadeOut, View anchorView) {
528        // Calculate the duration of the animation based on the object's distance
529        final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) +
530                Math.pow(to.top - from.top, 2));
531        final Resources res = getResources();
532        final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
533
534        // If duration < 0, this is a cue to compute the duration based on the distance
535        if (duration < 0) {
536            duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
537            if (dist < maxDist) {
538                duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
539            }
540        }
541
542        if (mDropAnim != null) {
543            mDropAnim.cancel();
544        }
545
546        if (mFadeOutAnim != null) {
547            mFadeOutAnim.cancel();
548        }
549
550        mDropView = view;
551        final float initialAlpha = view.getAlpha();
552        mDropAnim = new ValueAnimator();
553        if (alphaInterpolator == null || motionInterpolator == null) {
554            mDropAnim.setInterpolator(mCubicEaseOutInterpolator);
555        }
556
557        if (anchorView != null) {
558            mAnchorViewInitialScrollX = anchorView.getScrollX();
559        }
560        mAnchorView = anchorView;
561
562        mDropAnim.setDuration(duration);
563        mDropAnim.setFloatValues(0.0f, 1.0f);
564        mDropAnim.removeAllUpdateListeners();
565        mDropAnim.addUpdateListener(new AnimatorUpdateListener() {
566            public void onAnimationUpdate(ValueAnimator animation) {
567                final float percent = (Float) animation.getAnimatedValue();
568                // Invalidate the old position
569                int width = view.getMeasuredWidth();
570                int height = view.getMeasuredHeight();
571                invalidate(mDropViewPos[0], mDropViewPos[1],
572                        mDropViewPos[0] + width, mDropViewPos[1] + height);
573
574                float alphaPercent = alphaInterpolator == null ? percent :
575                        alphaInterpolator.getInterpolation(percent);
576                float motionPercent = motionInterpolator == null ? percent :
577                        motionInterpolator.getInterpolation(percent);
578
579                mDropViewPos[0] = from.left + (int) Math.round(((to.left - from.left) * motionPercent));
580                mDropViewPos[1] = from.top + (int) Math.round(((to.top - from.top) * motionPercent));
581                mDropViewScale = percent * finalScale + (1 - percent);
582                mDropViewAlpha = alphaPercent * finalAlpha + (1 - alphaPercent) * initialAlpha;
583                invalidate(mDropViewPos[0], mDropViewPos[1],
584                        mDropViewPos[0] + width, mDropViewPos[1] + height);
585            }
586        });
587        mDropAnim.addListener(new AnimatorListenerAdapter() {
588            public void onAnimationEnd(Animator animation) {
589                if (onCompleteRunnable != null) {
590                    onCompleteRunnable.run();
591                }
592                if (fadeOut) {
593                    fadeOutDragView();
594                } else {
595                    mDropView = null;
596                }
597            }
598        });
599        mDropAnim.start();
600    }
601
602    private void fadeOutDragView() {
603        mFadeOutAnim = new ValueAnimator();
604        mFadeOutAnim.setDuration(150);
605        mFadeOutAnim.setFloatValues(0f, 1f);
606        mFadeOutAnim.removeAllUpdateListeners();
607        mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() {
608            public void onAnimationUpdate(ValueAnimator animation) {
609                final float percent = (Float) animation.getAnimatedValue();
610                mDropViewAlpha = 1 - percent;
611                int width = mDropView.getMeasuredWidth();
612                int height = mDropView.getMeasuredHeight();
613                invalidate(mDropViewPos[0], mDropViewPos[1],
614                        mDropViewPos[0] + width, mDropViewPos[1] + height);
615            }
616        });
617        mFadeOutAnim.addListener(new AnimatorListenerAdapter() {
618            public void onAnimationEnd(Animator animation) {
619                mDropView = null;
620            }
621        });
622        mFadeOutAnim.start();
623    }
624
625    @Override
626    protected void onViewAdded(View child) {
627        super.onViewAdded(child);
628        updateChildIndices();
629    }
630
631    @Override
632    protected void onViewRemoved(View child) {
633        super.onViewRemoved(child);
634        updateChildIndices();
635    }
636
637    private void updateChildIndices() {
638        if (mLauncher != null) {
639            mWorkspaceIndex = indexOfChild(mLauncher.getWorkspace());
640            mQsbIndex = indexOfChild(mLauncher.getSearchBar());
641        }
642    }
643
644    @Override
645    protected int getChildDrawingOrder(int childCount, int i) {
646        // We don't want to prioritize the workspace drawing on top of the other children in
647        // landscape for the overscroll event.
648        if (LauncherApplication.isScreenLandscape(getContext())) {
649            return super.getChildDrawingOrder(childCount, i);
650        }
651
652        if (mWorkspaceIndex == -1 || mQsbIndex == -1 ||
653                mLauncher.getWorkspace().isDrawingBackgroundGradient()) {
654            return i;
655        }
656
657        // This ensures that the workspace is drawn above the hotseat and qsb,
658        // except when the workspace is drawing a background gradient, in which
659        // case we want the workspace to stay behind these elements.
660        if (i == mQsbIndex) {
661            return mWorkspaceIndex;
662        } else if (i == mWorkspaceIndex) {
663            return mQsbIndex;
664        } else {
665            return i;
666        }
667    }
668
669    @Override
670    protected void dispatchDraw(Canvas canvas) {
671        super.dispatchDraw(canvas);
672        if (mDropView != null) {
673            // We are animating an item that was just dropped on the home screen.
674            // Render its View in the current animation position.
675            canvas.save(Canvas.MATRIX_SAVE_FLAG);
676            final int xPos = mDropViewPos[0] - mDropView.getScrollX() + (mAnchorView != null
677                    ? (mAnchorViewInitialScrollX - mAnchorView.getScrollX()) : 0);
678            final int yPos = mDropViewPos[1] - mDropView.getScrollY();
679            int width = mDropView.getMeasuredWidth();
680            int height = mDropView.getMeasuredHeight();
681            canvas.translate(xPos, yPos);
682            canvas.translate((1 - mDropViewScale) * width / 2, (1 - mDropViewScale) * height / 2);
683            canvas.scale(mDropViewScale, mDropViewScale);
684            mDropView.setAlpha(mDropViewAlpha);
685            mDropView.draw(canvas);
686            canvas.restore();
687        }
688    }
689}
690