Merge "Substantially improved performance of dragging and drop animations"
diff --git a/res/values/config.xml b/res/values/config.xml
index a5d7b91..497011c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -63,7 +63,7 @@
 
     <!-- Parameters controlling the animation for when an item is dropped on the home screen,
          and it animates from its old position to the new one. -->
-    <integer name="config_dropAnimMaxDuration">600</integer>
+    <integer name="config_dropAnimMaxDuration">500</integer>
 
     <!-- The duration of the UserFolder opening and closing animation -->
     <integer name="config_folderAnimDuration">120</integer>
diff --git a/src/com/android/launcher2/ButtonDropTarget.java b/src/com/android/launcher2/ButtonDropTarget.java
index e3da860..f87889c 100644
--- a/src/com/android/launcher2/ButtonDropTarget.java
+++ b/src/com/android/launcher2/ButtonDropTarget.java
@@ -90,4 +90,8 @@
     public DropTarget getDropTargetDelegate(DragObject d) {
         return null;
     }
+
+    public void getLocationInDragLayer(int[] loc) {
+        mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
+    }
 }
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index f890808..b8515fc 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -706,8 +706,6 @@
                 // The child hit rect is relative to the CellLayoutChildren parent, so we need to
                 // offset that by this CellLayout's padding to test an (x,y) point that is relative
                 // to this view.
-                final int tmpXY[] = mTmpXY;
-                child.getLocationOnScreen(tmpXY);
                 frame.offset(mPaddingLeft, mPaddingTop);
 
                 if (frame.contains(x, y)) {
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 9a6335b..f6058a0 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -61,7 +61,7 @@
     static final int SCROLL_LEFT = 0;
     static final int SCROLL_RIGHT = 1;
 
-    private Context mContext;
+    private Launcher mLauncher;
     private Handler mHandler;
     private final Vibrator mVibrator = new Vibrator();
 
@@ -78,9 +78,6 @@
     /** Y coordinate of the down event. */
     private int mMotionDownY;
 
-    /** Info about the screen for clamping. */
-    private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
-
     /** the area at the edge of the screen that makes the workspace go left
      *   or right while you're dragging.
      */
@@ -139,10 +136,10 @@
      *
      * @param context The application's context.
      */
-    public DragController(Context context) {
-        mContext = context;
+    public DragController(Launcher launcher) {
+        mLauncher = launcher;
         mHandler = new Handler();
-        mScrollZone = context.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
+        mScrollZone = launcher.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
     }
 
     public boolean dragging() {
@@ -183,11 +180,11 @@
         }
 
         int[] loc = mCoordinatesTemp;
-        v.getLocationOnScreen(loc);
-        int screenX = loc[0];
-        int screenY = loc[1];
+        mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
+        int dragLayerX = loc[0];
+        int dragLayerY = loc[1];
 
-        startDrag(b, screenX, screenY, source, dragInfo, dragAction, dragRegion);
+        startDrag(b, dragLayerX, dragLayerY, source, dragInfo, dragAction, dragRegion);
         b.recycle();
 
         if (dragAction == DRAG_ACTION_MOVE) {
@@ -210,11 +207,11 @@
     public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo, int dragAction,
             Rect dragRegion) {
         int[] loc = mCoordinatesTemp;
-        v.getLocationOnScreen(loc);
-        int screenX = loc[0];
-        int screenY = loc[1];
+        mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
+        int dragLayerX = loc[0];
+        int dragLayerY = loc[1];
 
-        startDrag(bmp, screenX, screenY, source, dragInfo, dragAction, dragRegion);
+        startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, dragAction, dragRegion);
 
         if (dragAction == DRAG_ACTION_MOVE) {
             v.setVisibility(View.GONE);
@@ -226,16 +223,16 @@
      *
      * @param b The bitmap to display as the drag image.  It will be re-scaled to the
      *          enlarged size.
-     * @param screenX The x position on screen of the left-top of the bitmap.
-     * @param screenY The y position on screen of the left-top of the bitmap.
+     * @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
+     * @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
      * @param source An object representing where the drag originated
      * @param dragInfo The data associated with the object that is being dragged
      * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
      *        {@link #DRAG_ACTION_COPY}
      */
-    public void startDrag(Bitmap b, int screenX, int screenY,
+    public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,
             DragSource source, Object dragInfo, int dragAction) {
-        startDrag(b, screenX, screenY, source, dragInfo, dragAction, null);
+        startDrag(b, dragLayerX, dragLayerY, source, dragInfo, dragAction, null);
     }
 
     /**
@@ -243,8 +240,8 @@
      *
      * @param b The bitmap to display as the drag image.  It will be re-scaled to the
      *          enlarged size.
-     * @param screenX The x position on screen of the left-top of the bitmap.
-     * @param screenY The y position on screen of the left-top of the bitmap.
+     * @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
+     * @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
      * @param source An object representing where the drag originated
      * @param dragInfo The data associated with the object that is being dragged
      * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
@@ -252,7 +249,7 @@
      * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
      *          Makes dragging feel more precise, e.g. you can clip out a transparent border
      */
-    public void startDrag(Bitmap b, int screenX, int screenY,
+    public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,
             DragSource source, Object dragInfo, int dragAction, Rect dragRegion) {
         if (PROFILE_DRAWING_DURING_DRAG) {
             android.os.Debug.startMethodTracing("Launcher");
@@ -261,7 +258,7 @@
         // Hide soft keyboard, if visible
         if (mInputMethodManager == null) {
             mInputMethodManager = (InputMethodManager)
-                    mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+                    mLauncher.getSystemService(Context.INPUT_METHOD_SERVICE);
         }
         mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
 
@@ -269,8 +266,8 @@
             listener.onDragStart(source, dragInfo, dragAction);
         }
 
-        final int registrationX = ((int)mMotionDownX) - screenX;
-        final int registrationY = ((int)mMotionDownY) - screenY;
+        final int registrationX = mMotionDownX - dragLayerX;
+        final int registrationY = mMotionDownY - dragLayerY;
 
         final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
         final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
@@ -278,14 +275,14 @@
         mDragging = true;
 
         mDragObject.dragComplete = false;
-        mDragObject.xOffset = mMotionDownX - (screenX + dragRegionLeft);
-        mDragObject.yOffset = mMotionDownY - (screenY + dragRegionTop);
+        mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
+        mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
         mDragObject.dragSource = source;
         mDragObject.dragInfo = dragInfo;
 
         mVibrator.vibrate(VIBRATE_DURATION);
 
-        final DragView dragView = mDragObject.dragView = new DragView(mContext, b, registrationX,
+        final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
                 registrationY, 0, 0, b.getWidth(), b.getHeight());
 
         final DragSource dragSource = source;
@@ -299,9 +296,8 @@
             dragView.setDragRegion(new Rect(dragRegion));
         }
 
-        dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
-
-        handleMoveEvent((int) mMotionDownX, (int) mMotionDownY);
+        dragView.show(mMotionDownX, mMotionDownY);
+        handleMoveEvent(mMotionDownX, mMotionDownY);
     }
 
     /**
@@ -398,25 +394,21 @@
         }
         final int action = ev.getAction();
 
-        if (action == MotionEvent.ACTION_DOWN) {
-            recordScreenSize();
-        }
-
-        final int screenX = clamp((int)ev.getRawX(), 0, mDisplayMetrics.widthPixels);
-        final int screenY = clamp((int)ev.getRawY(), 0, mDisplayMetrics.heightPixels);
+        final int dragLayerX = (int) ev.getX();
+        final int dragLayerY = (int) ev.getY();
 
         switch (action) {
             case MotionEvent.ACTION_MOVE:
                 break;
             case MotionEvent.ACTION_DOWN:
                 // Remember location of down touch
-                mMotionDownX = screenX;
-                mMotionDownY = screenY;
+                mMotionDownX = dragLayerX;
+                mMotionDownY = dragLayerY;
                 mLastDropTarget = null;
                 break;
             case MotionEvent.ACTION_UP:
                 if (mDragging) {
-                    drop(screenX, screenY);
+                    drop(dragLayerX, dragLayerY);
                 }
                 endDrag();
                 break;
@@ -475,7 +467,7 @@
 
         // After a scroll, the touch point will still be in the scroll region.
         // Rather than scrolling immediately, require a bit of twiddling to scroll again
-        final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
+        final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop();
         mDistanceSinceScroll +=
             Math.sqrt(Math.pow(mLastTouch[0] - x, 2) + Math.pow(mLastTouch[1] - y, 2));
         mLastTouch[0] = x;
@@ -514,16 +506,16 @@
         }
 
         final int action = ev.getAction();
-        final int screenX = clamp((int)ev.getRawX(), 0, mDisplayMetrics.widthPixels);
-        final int screenY = clamp((int)ev.getRawY(), 0, mDisplayMetrics.heightPixels);
+        final int dragLayerX = (int) ev.getX();
+        final int dragLayerY = (int) ev.getY();
 
         switch (action) {
         case MotionEvent.ACTION_DOWN:
             // Remember where the motion event started
-            mMotionDownX = screenX;
-            mMotionDownY = screenY;
+            mMotionDownX = dragLayerX;
+            mMotionDownY = dragLayerY;
 
-            if ((screenX < mScrollZone) || (screenX > mScrollView.getWidth() - mScrollZone)) {
+            if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
                 mScrollState = SCROLL_WAITING_IN_ZONE;
                 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
             } else {
@@ -531,15 +523,15 @@
             }
             break;
         case MotionEvent.ACTION_MOVE:
-            handleMoveEvent(screenX, screenY);
+            handleMoveEvent(dragLayerX, dragLayerY);
             break;
         case MotionEvent.ACTION_UP:
             // Ensure that we've processed a move event at the current pointer location.
-            handleMoveEvent(screenX, screenY);
+            handleMoveEvent(dragLayerX, dragLayerY);
 
             mHandler.removeCallbacks(mScrollRunnable);
             if (mDragging) {
-                drop(screenX, screenY);
+                drop(dragLayerX, dragLayerY);
             }
             endDrag();
             break;
@@ -581,8 +573,8 @@
 
             target.getHitRect(r);
 
-            // Convert the hit rect to screen coordinates
-            target.getLocationOnScreen(dropCoordinates);
+            // Convert the hit rect to DragLayer coordinates
+            target.getLocationInDragLayer(dropCoordinates);
             r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
 
             mDragObject.x = x;
@@ -591,7 +583,7 @@
                 DropTarget delegate = target.getDropTargetDelegate(mDragObject);
                 if (delegate != null) {
                     target = delegate;
-                    target.getLocationOnScreen(dropCoordinates);
+                    target.getLocationInDragLayer(dropCoordinates);
                 }
 
                 // Make dropCoordinates relative to the DropTarget
@@ -604,28 +596,6 @@
         return null;
     }
 
-    /**
-     * Get the screen size so we can clamp events to the screen size so even if
-     * you drag off the edge of the screen, we find something.
-     */
-    private void recordScreenSize() {
-        ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
-                .getDefaultDisplay().getMetrics(mDisplayMetrics);
-    }
-
-    /**
-     * Clamp val to be &gt;= min and &lt; max.
-     */
-    private static int clamp(int val, int min, int max) {
-        if (val < min) {
-            return min;
-        } else if (val >= max) {
-            return max - 1;
-        } else {
-            return val;
-        }
-    }
-
     public void setDragScoller(DragScroller scroller) {
         mDragScroller = scroller;
     }
@@ -672,7 +642,7 @@
     /**
      * Specifies the delete region.  We won't scroll on touch events over the delete region.
      *
-     * @param region The rectangle in screen coordinates of the delete region.
+     * @param region The rectangle in DragLayer coordinates of the delete region.
      */
     void setDeleteRegion(RectF region) {
         mDeleteRegion = region;
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index d76b902..dfd8160 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -18,12 +18,12 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -33,9 +33,7 @@
 import android.view.ViewParent;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 
 import com.android.launcher.R;
 
@@ -58,8 +56,10 @@
 
     // Variables relating to animation of views after drop
     private ValueAnimator mDropAnim = null;
-    private TimeInterpolator mQuintEaseOutInterpolator = new DecelerateInterpolator(2.5f);
+    private ValueAnimator mFadeOutAnim = null;
+    private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
     private View mDropView = null;
+
     private int[] mDropViewPos = new int[2];
     private float mDropViewScale;
     private float mDropViewAlpha;
@@ -168,14 +168,16 @@
     }
 
     public void getDescendantRectRelativeToSelf(View descendant, Rect r) {
-        descendant.getHitRect(r);
         mTmpXY[0] = 0;
         mTmpXY[1] = 0;
         getDescendantCoordRelativeToSelf(descendant, mTmpXY);
-        r.offset(mTmpXY[0], mTmpXY[1]);
+        r.set(mTmpXY[0], mTmpXY[1],
+                mTmpXY[0] + descendant.getWidth(), mTmpXY[1] + descendant.getHeight());
     }
 
-    public void getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
+    private void getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
+        coord[0] += descendant.getLeft();
+        coord[1] += descendant.getTop();
         ViewParent viewParent = descendant.getParent();
         while (viewParent instanceof View && viewParent != this) {
             final View view = (View)viewParent;
@@ -185,13 +187,19 @@
         }
     }
 
+    public void getLocationInDragLayer(View child, int[] loc) {
+        loc[0] = 0;
+        loc[1] = 0;
+        getDescendantCoordRelativeToSelf(child, loc);
+    }
+
     public void getViewRectRelativeToSelf(View v, Rect r) {
         int[] loc = new int[2];
-        getLocationOnScreen(loc);
+        getLocationInWindow(loc);
         int x = loc[0];
         int y = loc[1];
 
-        v.getLocationOnScreen(loc);
+        v.getLocationInWindow(loc);
         int vX = loc[0];
         int vY = loc[1];
 
@@ -205,21 +213,6 @@
         return mDragController.dispatchUnhandledMove(focused, direction);
     }
 
-    public View createDragView(Bitmap b, int xPos, int yPos) {
-        ImageView imageView = new ImageView(mContext);
-        imageView.setImageBitmap(b);
-        imageView.setX(xPos);
-        imageView.setY(yPos);
-        addView(imageView, b.getWidth(), b.getHeight());
-
-        return imageView;
-    }
-
-    public View createDragView(View v) {
-        v.getLocationOnScreen(mTmpXY);
-        return createDragView(mDragController.getViewBitmap(v), mTmpXY[0], mTmpXY[1]);
-    }
-
     public static class LayoutParams extends FrameLayout.LayoutParams {
         public int x, y;
         public boolean customPosition = false;
@@ -320,29 +313,40 @@
         int coord[] = new int[2];
         coord[0] = lp.x;
         coord[1] = lp.y;
-        getDescendantCoordRelativeToSelf(child, coord);
+        // Since the child hasn't necessarily been laid out, we force the lp to be updated with
+        // the correct coordinates and use these to determine the final location
+        getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
+        int toX = coord[0] - (dragView.getWidth() - child.getMeasuredWidth()) / 2;
+        int toY = coord[1] - (dragView.getHeight() - child.getMeasuredHeight()) / 2;
 
         final int fromX = r.left + (dragView.getWidth() - child.getMeasuredWidth())  / 2;
         final int fromY = r.top + (dragView.getHeight() - child.getMeasuredHeight())  / 2;
         child.setVisibility(INVISIBLE);
-        animateViewIntoPosition(child, fromX, fromY, coord[0], coord[1]);
+        child.setAlpha(0);
+        Runnable onCompleteRunnable = new Runnable() {
+            public void run() {
+                child.setVisibility(VISIBLE);
+                ObjectAnimator oa = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
+                oa.setDuration(60);
+                oa.start();
+            }
+        };
+        animateViewIntoPosition(dragView, fromX, fromY, toX, toY, onCompleteRunnable, true);
     }
 
     private void animateViewIntoPosition(final View view, final int fromX, final int fromY,
-            final int toX, final int toY) {
-        Rect from = new Rect(fromX, fromY, fromX + view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
+            final int toX, final int toY, Runnable onCompleteRunnable, boolean fadeOut) {
+        Rect from = new Rect(fromX, fromY, fromX +
+                view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
         Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
-        animateView(view, from, to, 1f, -1);
-    }
+        animateView(view, from, to, 1f, 1.0f, -1, null, null, onCompleteRunnable, true);
 
-    public void animateView(final View view, final Rect from, final Rect to,
-            final float finalAlpha, int duration) {
-        animateView(view, from, to, finalAlpha, 1.0f, duration, null, null);
     }
 
     public void animateView(final View view, final Rect from, final Rect to, final float finalAlpha,
             final float finalScale, int duration, final Interpolator motionInterpolator,
-            final Interpolator alphaInterpolator) {
+            final Interpolator alphaInterpolator, final Runnable onCompleteRunnable,
+            final boolean fadeOut) {
         // Calculate the duration of the animation based on the object's distance
         final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) +
                 Math.pow(to.top - from.top, 2));
@@ -353,19 +357,19 @@
         if (duration < 0) {
             duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
             if (dist < maxDist) {
-                duration *= mQuintEaseOutInterpolator.getInterpolation(dist / maxDist);
+                duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
             }
         }
 
         if (mDropAnim != null) {
-            mDropAnim.end();
+            mDropAnim.cancel();
         }
 
         mDropView = view;
         final float initialAlpha = view.getAlpha();
         mDropAnim = new ValueAnimator();
         if (alphaInterpolator == null || motionInterpolator == null) {
-            mDropAnim.setInterpolator(mQuintEaseOutInterpolator);
+            mDropAnim.setInterpolator(mCubicEaseOutInterpolator);
         }
 
         mDropAnim.setDuration(duration);
@@ -395,8 +399,12 @@
         });
         mDropAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
-                if (mDropView != null) {
-                    mDropView.setVisibility(View.VISIBLE);
+                if (onCompleteRunnable != null) {
+                    onCompleteRunnable.run();
+                }
+                if (fadeOut) {
+                    fadeOutDragView();
+                } else {
                     mDropView = null;
                 }
             }
@@ -404,6 +412,29 @@
         mDropAnim.start();
     }
 
+    private void fadeOutDragView() {
+        mFadeOutAnim = new ValueAnimator();
+        mFadeOutAnim.setDuration(150);
+        mFadeOutAnim.setFloatValues(0f, 1f);
+        mFadeOutAnim.removeAllUpdateListeners();
+        mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() {
+            public void onAnimationUpdate(ValueAnimator animation) {
+                final float percent = (Float) animation.getAnimatedValue();
+                mDropViewAlpha = 1 - percent;
+                int width = mDropView.getMeasuredWidth();
+                int height = mDropView.getMeasuredHeight();
+                invalidate(mDropViewPos[0], mDropViewPos[1],
+                        mDropViewPos[0] + width, mDropViewPos[1] + height);
+            }
+        });
+        mFadeOutAnim.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator animation) {
+                mDropView = null;
+            }
+        });
+        mFadeOutAnim.start();
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index d4dc785..aff3d42 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -19,18 +19,14 @@
 
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
-import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.IBinder;
-import android.view.Gravity;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 import android.view.animation.DecelerateInterpolator;
@@ -44,13 +40,13 @@
     private int mRegistrationY;
 
     private Rect mDragRegion = null;
+    private DragLayer mDragLayer = null;
 
     ValueAnimator mAnim;
     private float mOffsetX = 0.0f;
     private float mOffsetY = 0.0f;
 
-    private WindowManager.LayoutParams mLayoutParams;
-    private WindowManager mWindowManager;
+    private DragLayer.LayoutParams mLayoutParams;
 
     /**
      * A callback to be called the first time this view is drawn.
@@ -65,20 +61,19 @@
      * The registration point is the point inside our view that the touch events should
      * be centered upon.
      *
-     * @param context A context
+     * @param launcher The Launcher instance
      * @param bitmap The view that we're dragging around.  We scale it up when we draw it.
      * @param registrationX The x coordinate of the registration point.
      * @param registrationY The y coordinate of the registration point.
      */
-    public DragView(Context context, Bitmap bitmap, int registrationX, int registrationY,
+    public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
             int left, int top, int width, int height) {
-        super(context);
+        super(launcher);
+        mDragLayer = launcher.getDragLayer();
 
         final Resources res = getResources();
         final int dragScale = res.getInteger(R.integer.config_dragViewExtraPixels);
 
-        mWindowManager = WindowManagerImpl.getDefault();
-
         Matrix scale = new Matrix();
         final float scaleFactor = (width + dragScale) / width;
         if (scaleFactor != 1.0f) {
@@ -106,10 +101,10 @@
                 if (getParent() == null) {
                     animation.cancel();
                 } else {
-                    WindowManager.LayoutParams lp = mLayoutParams;
+                    DragLayer.LayoutParams lp = mLayoutParams;
                     lp.x += deltaX;
                     lp.y += deltaY;
-                    mWindowManager.updateViewLayout(DragView.this, lp);
+                    mDragLayer.requestLayout();
                 }
             }
         });
@@ -209,58 +204,45 @@
      * Create a window containing this view and show it.
      *
      * @param windowToken obtained from v.getWindowToken() from one of your views
-     * @param touchX the x coordinate the user touched in screen coordinates
-     * @param touchY the y coordinate the user touched in screen coordinates
+     * @param touchX the x coordinate the user touched in DragLayer coordinates
+     * @param touchY the y coordinate the user touched in DragLayer coordinates
      */
-    public void show(IBinder windowToken, int touchX, int touchY) {
-        WindowManager.LayoutParams lp;
-        int pixelFormat;
-
-        pixelFormat = PixelFormat.TRANSLUCENT;
-
-        lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.WRAP_CONTENT,
-                touchX - mRegistrationX, touchY - mRegistrationY,
-                WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
-                    /*| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM*/,
-                pixelFormat);
-//        lp.token = mStatusBarView.getWindowToken();
-        lp.gravity = Gravity.LEFT | Gravity.TOP;
-        lp.token = windowToken;
-        lp.setTitle("DragView");
+    public void show(int touchX, int touchY) {
+        mDragLayer.addView(this);
+        DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0);
+        lp.width = mBitmap.getWidth();
+        lp.height = mBitmap.getHeight();
+        lp.x = touchX - mRegistrationX;
+        lp.y = touchY - mRegistrationY;
+        lp.customPosition = true;
+        setLayoutParams(lp);
         mLayoutParams = lp;
-
-        mWindowManager.addView(this, lp);
-
         mAnim.start();
     }
 
     /**
      * Move the window containing this view.
      *
-     * @param touchX the x coordinate the user touched in screen coordinates
-     * @param touchY the y coordinate the user touched in screen coordinates
+     * @param touchX the x coordinate the user touched in DragLayer coordinates
+     * @param touchY the y coordinate the user touched in DragLayer coordinates
      */
     void move(int touchX, int touchY) {
-        WindowManager.LayoutParams lp = mLayoutParams;
+        DragLayer.LayoutParams lp = mLayoutParams;
         lp.x = touchX - mRegistrationX + (int) mOffsetX;
         lp.y = touchY - mRegistrationY + (int) mOffsetY;
-        mWindowManager.updateViewLayout(this, lp);
+        mDragLayer.requestLayout();
     }
 
     void remove() {
         post(new Runnable() {
             public void run() {
-                mWindowManager.removeView(DragView.this);
+                mDragLayer.removeView(DragView.this);
             }
         });
     }
 
     int[] getPosition(int[] result) {
-        WindowManager.LayoutParams lp = mLayoutParams;
+        DragLayer.LayoutParams lp = mLayoutParams;
         if (result == null) result = new int[2];
         result[0] = lp.x;
         result[1] = lp.y;
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index 6e18479..0712420 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -120,7 +120,7 @@
 
     // These methods are implemented in Views
     void getHitRect(Rect outRect);
-    void getLocationOnScreen(int[] loc);
+    void getLocationInDragLayer(int[] loc);
     int getLeft();
     int getTop();
 }
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 347eba0..8a1b957 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -35,7 +35,6 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.inputmethod.EditorInfo;
@@ -43,8 +42,6 @@
 import android.widget.AdapterView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
 
 import com.android.launcher.R;
 import com.android.launcher2.FolderInfo.FolderListener;
@@ -54,9 +51,8 @@
 /**
  * Represents a set of icons chosen by the user or generated by the system.
  */
-public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener,
-        OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget, FolderListener,
-        TextView.OnEditorActionListener {
+public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
+        View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener {
 
     protected DragController mDragController;
 
@@ -102,6 +98,7 @@
     private int mFolderNameHeight;
     private Rect mHitRect = new Rect();
     private Rect mTempRect = new Rect();
+    private boolean mFirstOpen = true;
 
     private boolean mIsEditingName = false;
     private InputMethodManager mInputMethodManager;
@@ -135,7 +132,6 @@
         if (sHintText == null) {
             sHintText = res.getString(R.string.folder_hint_text);
         }
-        setLayerType(LAYER_TYPE_HARDWARE, null);
     }
 
     @Override
@@ -177,15 +173,6 @@
         }
     };
 
-    public void onItemClick(AdapterView parent, View v, int position, long id) {
-        ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position);
-        int[] pos = new int[2];
-        v.getLocationOnScreen(pos);
-        app.intent.setSourceBounds(new Rect(pos[0], pos[1],
-                pos[0] + v.getWidth(), pos[1] + v.getHeight()));
-        mLauncher.startActivitySafely(app.intent, app);
-    }
-
     public void onClick(View v) {
         Object tag = v.getTag();
         if (tag instanceof ShortcutInfo) {
@@ -393,6 +380,12 @@
     }
 
     public void animateOpen() {
+        if (mFirstOpen) {
+            setLayerType(LAYER_TYPE_HARDWARE, null);
+            buildLayer();
+            mFirstOpen = false;
+        }
+
         positionAndSizeAsIcon();
 
         if (!(getParent() instanceof DragLayer)) return;
@@ -433,14 +426,25 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mState = STATE_OPEN;
+                setLayerType(LAYER_TYPE_NONE, null);
+                enableHardwareLayersForChildren();
             }
         });
         oa.setDuration(mExpandDuration);
         oa.start();
     }
 
+    void enableHardwareLayersForChildren() {
+        ArrayList<View> children = getItemsInReadingOrder();
+        for (View child: children) {
+            child.setLayerType(LAYER_TYPE_HARDWARE, null);
+        }
+    }
+
     public void animateClosed() {
         if (!(getParent() instanceof DragLayer)) return;
+        setLayerType(LAYER_TYPE_HARDWARE, null);
+        buildLayer();
 
         ObjectAnimator oa;
         if (mMode == PARTIAL_GROW) {
@@ -957,4 +961,8 @@
         }
         return mItemsInReadingOrder;
     }
+
+    public void getLocationInDragLayer(int[] loc) {
+        mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
+    }
 }
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 4071433..4978e98 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -55,7 +55,7 @@
     private static final int NUM_ITEMS_IN_PREVIEW = 3;
     private static final int CONSUMPTION_ANIMATION_DURATION = 100;
     private static final int DROP_IN_ANIMATION_DURATION = 400;
-    private static final int INITIAL_ITEM_ANIMATION_DURATION = 150;
+    private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
 
     // The degree to which the inner ring grows when accepting drop
     private static final float INNER_RING_GROWTH_FACTOR = 0.15f;
@@ -295,7 +295,7 @@
 
         // This will animate the first item from it's position as an icon into its
         // position as the first item in the preview
-        animateFirstItem(animateDrawable, DROP_IN_ANIMATION_DURATION);
+        animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION);
 
         postDelayed(new Runnable() {
             public void run() {
@@ -332,7 +332,7 @@
         float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
 
         dragLayer.animateView(animateView, from, to, finalAlpha, scale, DROP_IN_ANIMATION_DURATION,
-                new DecelerateInterpolator(2), new AccelerateInterpolator(2));
+                new DecelerateInterpolator(2), new AccelerateInterpolator(2), null, false);
         postDelayed(new Runnable() {
             public void run() {
                 addItem(item);
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index caf62b4..fd18338 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -205,8 +205,6 @@
     private FolderRingAnimator mDragFolderRingAnimator = null;
     private View mLastDragOverView = null;
     private boolean mCreateUserFolderOnDrop = false;
-    private int mCellWidth = -1;
-    private int mCellHeight = -1;
 
     // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
     private float mXDown;
@@ -1284,7 +1282,6 @@
         shrink(shrinkState, true);
     }
 
-
     // we use this to shrink the workspace for the all apps view and the customize view
     public void shrink(ShrinkState shrinkState, boolean animated) {
         if (mFirstLayout) {
@@ -2003,9 +2000,9 @@
         final int bmpWidth = b.getWidth();
         final int bmpHeight = b.getHeight();
 
-        child.getLocationOnScreen(mTempXY);
-        final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
-        final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2;
+        mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
+        final int dragLayerX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
+        final int dragLayerY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2;
 
         Rect dragRect = null;
         if (child instanceof BubbleTextView) {
@@ -2021,7 +2018,7 @@
         }
 
         mLauncher.lockScreenOrientation();
-        mDragController.startDrag(b, screenX, screenY, this, child.getTag(),
+        mDragController.startDrag(b, dragLayerX, dragLayerY, this, child.getTag(),
                 DragController.DRAG_ACTION_MOVE, dragRect);
         b.recycle();
     }
@@ -2288,11 +2285,11 @@
     }
 
     public void getViewLocationRelativeToSelf(View v, int[] location) {
-        getLocationOnScreen(location);
+        getLocationInWindow(location);
         int x = location[0];
         int y = location[1];
 
-        v.getLocationOnScreen(location);
+        v.getLocationInWindow(location);
         int vX = location[0];
         int vY = location[1];
 
@@ -3288,4 +3285,8 @@
         return String.format(mContext.getString(R.string.workspace_scroll_format),
                 page + 1, getChildCount());
     }
+
+    public void getLocationInDragLayer(int[] loc) {
+        mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
+    }
 }