Merge "Fix a monkey crash"
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 12fe971..8a4bdc7 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -385,37 +385,12 @@
     protected void determineDraggingStart(android.view.MotionEvent ev) {
         // Disable dragging by pulling an app down for now.
     }
+
     private void beginDraggingApplication(View v) {
-        // Make a copy of the ApplicationInfo
-        ApplicationInfo appInfo = new ApplicationInfo((ApplicationInfo) v.getTag());
-
-        // Compose the drag image (top compound drawable, index is 1)
-        final TextView tv = (TextView) v;
-        final Drawable icon = tv.getCompoundDrawables()[1];
-        Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
-                Bitmap.Config.ARGB_8888);
-        mCanvas.setBitmap(b);
-        mCanvas.save();
-        mCanvas.translate((v.getWidth() - icon.getIntrinsicWidth()) / 2, v.getPaddingTop());
-        icon.draw(mCanvas);
-        mCanvas.restore();
-
-        // Compose the visible rect of the drag image
-        Rect dragRect = null;
-        if (v instanceof TextView) {
-            int top = v.getPaddingTop();
-            int left = (b.getWidth() - mAppIconSize) / 2;
-            int right = left + mAppIconSize;
-            int bottom = top + mAppIconSize;
-            dragRect = new Rect(left, top, right, bottom);
-        }
-
-        // Start the drag
-        mLauncher.lockScreenOrientation();
-        mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1, b);
-        mDragController.startDrag(v, b, this, appInfo, DragController.DRAG_ACTION_COPY, dragRect);
-        b.recycle();
+        mLauncher.getWorkspace().onDragStartedWithItem(v);
+        mLauncher.getWorkspace().beginDragShared(v, this);
     }
+
     private void beginDraggingWidget(View v) {
         // Get the widget preview as the drag representation
         ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
diff --git a/src/com/android/launcher2/BubbleTextView.java b/src/com/android/launcher2/BubbleTextView.java
index 8c0c27c..57a6584 100644
--- a/src/com/android/launcher2/BubbleTextView.java
+++ b/src/com/android/launcher2/BubbleTextView.java
@@ -56,7 +56,6 @@
     private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper();
     private final Canvas mTempCanvas = new Canvas();
     private final Rect mTempRect = new Rect();
-    private final Paint mTempPaint = new Paint();
     private boolean mDidInvalidateForPressedState;
     private Bitmap mPressedOrFocusedBackground;
     private int mFocusedOutlineColor;
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index 0bdfec1..37abe5a 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -26,6 +26,7 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -34,6 +35,7 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
+import android.widget.TextView;
 
 import com.android.launcher.R;
 
@@ -167,12 +169,13 @@
         return mDragController.onTouchEvent(ev);
     }
 
-    public void getDescendantRectRelativeToSelf(View descendant, Rect r) {
+    public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
         mTmpXY[0] = 0;
         mTmpXY[1] = 0;
-        getDescendantCoordRelativeToSelf(descendant, mTmpXY);
+        float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
         r.set(mTmpXY[0], mTmpXY[1],
                 mTmpXY[0] + descendant.getWidth(), mTmpXY[1] + descendant.getHeight());
+        return scale;
     }
 
     private float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
@@ -315,6 +318,7 @@
     public void animateViewIntoPosition(DragView dragView, final View child) {
         animateViewIntoPosition(dragView, child, null);
     }
+
     public void animateViewIntoPosition(DragView dragView, final View child,
             final Runnable onFinishAnimationRunnable) {
         ((CellLayoutChildren) child.getParent()).measureChild(child);
@@ -324,18 +328,30 @@
         getViewRectRelativeToSelf(dragView, r);
 
         int coord[] = new int[2];
-        coord[0] = lp.x + (lp.width / 2);
-        coord[1] = lp.y + (lp.height / 2);
+        coord[0] = lp.x;
+        coord[1] = lp.y;
         // 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
+        // the correct coordinates (above) and use these to determine the final location
         float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
-        int toX = coord[0] - lp.width / 2;
-        int toY = coord[1] - lp.height / 2;
-        toX -= (dragView.getWidth() - child.getMeasuredWidth()) / 2;
-        toY -= (dragView.getHeight() - child.getMeasuredHeight()) / 2;
+        int toX = coord[0];
+        int toY = coord[1];
+        if (child instanceof TextView) {
+            TextView tv = (TextView) child;
+            Drawable d = tv.getCompoundDrawables()[1];
 
-        final int fromX = r.left + (dragView.getWidth() - child.getMeasuredWidth())  / 2;
-        final int fromY = r.top + (dragView.getHeight() - child.getMeasuredHeight())  / 2;
+            // Center in the y coordinate about the target's drawable
+            toY += Math.round(scale * tv.getPaddingTop());
+            toY -= (dragView.getHeight() - (int) Math.round(scale * d.getIntrinsicHeight())) / 2;
+            // Center in the x coordinate about the target's drawable
+            toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
+        } else {
+            toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2;
+            toX -= (Math.round(scale * (dragView.getMeasuredWidth()
+                    - child.getMeasuredWidth()))) / 2;
+        }
+
+        final int fromX = r.left;
+        final int fromY = r.top;
         child.setVisibility(INVISIBLE);
         child.setAlpha(0);
         Runnable onCompleteRunnable = new Runnable() {
@@ -400,6 +416,10 @@
             mDropAnim.cancel();
         }
 
+        if (mFadeOutAnim != null) {
+            mFadeOutAnim.cancel();
+        }
+
         mDropView = view;
         final float initialAlpha = view.getAlpha();
         mDropAnim = new ValueAnimator();
@@ -424,8 +444,8 @@
                 float motionPercent = motionInterpolator == null ? percent :
                         motionInterpolator.getInterpolation(percent);
 
-                mDropViewPos[0] = from.left + (int) ((to.left - from.left) * motionPercent);
-                mDropViewPos[1] = from.top + (int) ((to.top - from.top) * motionPercent);
+                mDropViewPos[0] = from.left + (int) Math.round(((to.left - from.left) * motionPercent));
+                mDropViewPos[1] = from.top + (int) Math.round(((to.top - from.top) * motionPercent));
                 mDropViewScale = percent * finalScale + (1 - percent);
                 mDropViewAlpha = alphaPercent * finalAlpha + (1 - alphaPercent) * initialAlpha;
                 invalidate(mDropViewPos[0], mDropViewPos[1],
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 78df80f..dd0bffd 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -90,6 +90,7 @@
     private int[] mTargetCell = new int[2];
     private int[] mPreviousTargetCell = new int[2];
     private int[] mEmptyCell = new int[2];
+    private int[] mTempXY = new int[2];
     private Alarm mReorderAlarm = new Alarm();
     private Alarm mOnExitAlarm = new Alarm();
     private TextView mFolderName;
@@ -205,14 +206,13 @@
             }
 
             mLauncher.getWorkspace().onDragStartedWithItem(v);
-            mDragController.startDrag(v, this, item, DragController.DRAG_ACTION_COPY);
+            mLauncher.getWorkspace().beginDragShared(v, this);
             mIconDrawable = ((TextView) v).getCompoundDrawables()[1];
 
             mCurrentDragInfo = item;
             mEmptyCell[0] = item.cellX;
             mEmptyCell[1] = item.cellY;
             mCurrentDragView = v;
-
         }
         return true;
     }
@@ -270,18 +270,6 @@
         return true;
     }
 
-    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
-        if (!view.isInTouchMode()) {
-            return false;
-        }
-
-        ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position);
-
-        mDragController.startDrag(view, this, app, DragController.DRAG_ACTION_COPY);
-        mLauncher.closeFolder(this);
-        return true;
-    }
-
     public void setDragController(DragController dragController) {
         mDragController = dragController;
     }
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 952916d..93f56fb 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -24,6 +24,7 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -286,12 +287,13 @@
 
     public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
             final ShortcutInfo srcInfo, final View srcView, Rect dstRect,
-            Runnable postAnimationRunnable) {
+            float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
 
         Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
         computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth());
+
         // This will animate the dragView (srcView) into the new folder
-        onDrop(srcInfo, srcView, dstRect, 1, postAnimationRunnable);
+        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
 
         // This will animate the first item from it's position as an icon into its
         // position as the first item in the preview
@@ -309,8 +311,8 @@
         mFolderRingAnimator.animateToNaturalState();
     }
 
-    private void onDrop(final ShortcutInfo item, View animateView, Rect finalRect, int index,
-            Runnable postAnimationRunnable) {
+    private void onDrop(final ShortcutInfo item, View animateView, Rect finalRect,
+            float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
         item.cellX = -1;
         item.cellY = -1;
         DragLayer dragLayer = mLauncher.getDragLayer();
@@ -319,22 +321,22 @@
         Rect to = finalRect;
         if (to == null) {
             to = new Rect();
-            dragLayer.getDescendantRectRelativeToSelf(this, to);
+            scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
         }
 
-        if (animateView.getMeasuredWidth() != to.width() ||
-                animateView.getMeasuredHeight() != to.height()) {
-            int offsetX = (animateView.getMeasuredWidth() - to.width()) / 2;
-            int offsetY = (animateView.getMeasuredHeight() - to.height()) / 2;
-            to.offset(-offsetX, -offsetY);
-        }
-        float scale = adjustFinalScreenRectForIndex(to, index);
+        int[] center = new int[2];
+        float scale = getLocalCenterForIndex(index, center);
+        center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
+        center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
+
+        to.offset(center[0] - animateView.getMeasuredWidth() / 2,
+                center[1] - animateView.getMeasuredHeight() / 2);
 
         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), postAnimationRunnable,
-                false);
+        dragLayer.animateView(animateView, from, to, finalAlpha, scale * scaleRelativeToDragLayer,
+                DROP_IN_ANIMATION_DURATION, new DecelerateInterpolator(2),
+                new AccelerateInterpolator(2), postAnimationRunnable, false);
         postDelayed(new Runnable() {
             public void run() {
                 addItem(item);
@@ -350,7 +352,7 @@
         } else {
             item = (ShortcutInfo) d.dragInfo;
         }
-        onDrop(item, d.dragView, null, mInfo.contents.size(), d.postAnimationRunnable);
+        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
     }
 
     public DropTarget getDropTargetDelegate(DragObject d) {
@@ -398,15 +400,16 @@
         Drawable drawable;
     }
 
-    private float adjustFinalScreenRectForIndex(Rect r, int index) {
+    private float getLocalCenterForIndex(int index, int[] center) {
         mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams);
 
         mParams.transX += mPreviewOffsetX;
         mParams.transY += mPreviewOffsetY;
-        float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2 - mTotalWidth / 2;
-        float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2 - mTotalWidth / 2;
+        float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2;
+        float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2;
 
-        r.offset((int) offsetX, (int) offsetY);
+        center[0] = (int) Math.round(offsetX);
+        center[1] = (int) Math.round(offsetY);
         return mParams.scale;
     }
 
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 885d360..b26e3ac 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -94,7 +94,7 @@
 
 import com.android.common.Search;
 import com.android.launcher.R;
-import com.android.launcher2.Workspace.ShrinkState;
+import com.android.launcher2.Workspace.State;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -845,7 +845,7 @@
         mWorkspace.setHapticFeedbackEnabled(false);
         mWorkspace.setOnLongClickListener(this);
         mWorkspace.setup(this, dragController);
-        mWorkspace.setWallpaperDimension();
+        dragController.addDragListener(mWorkspace);
 
         // Get the search/delete bar
         mSearchDeleteBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.qsb_bar);
@@ -2341,19 +2341,8 @@
 
         setPivotsForZoom(toView, toState, scale);
 
-
-        if (springLoaded) {
-            if (toState == State.APPS_CUSTOMIZE) {
-                // Shrink workspaces away if going back to AppsCustomize from spring loaded mode
-                mWorkspace.shrink(ShrinkState.BOTTOM_HIDDEN, animated);
-            } else {
-                // Shrink workspaces to bottom if going back to AllApps from spring loaded mode
-                mWorkspace.shrink(ShrinkState.BOTTOM_VISIBLE, animated);
-            }
-        } else {
-            // Shrink workspaces away if going to AllApps/AppsCustomize from workspace
-            mWorkspace.shrink(ShrinkState.BOTTOM_HIDDEN, animated);
-        }
+        // Shrink workspaces away if going to AppsCustomize from workspace
+        mWorkspace.shrink(Workspace.State.SMALL, animated);
 
         if (animated) {
             final ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
@@ -2563,7 +2552,7 @@
     }
     void exitSpringLoadedDragMode() {
         if (mState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
-            mWorkspace.exitSpringLoadedDragMode(Workspace.ShrinkState.BOTTOM_VISIBLE);
+            mWorkspace.exitSpringLoadedDragMode(Workspace.State.SMALL);
             cameraZoomOut(State.APPS_CUSTOMIZE, true, true);
             mState = State.APPS_CUSTOMIZE;
         }
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index e5e7a20..9ef1fa8 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -301,13 +301,17 @@
     }
 
     protected void pageBeginMoving() {
-        mIsPageMoving = true;
-        onPageBeginMoving();
+        if (!mIsPageMoving) {
+            mIsPageMoving = true;
+            onPageBeginMoving();
+        }
     }
 
     protected void pageEndMoving() {
-        onPageEndMoving();
-        mIsPageMoving = false;
+        if (mIsPageMoving) {
+            mIsPageMoving = false;
+            onPageEndMoving();
+        }
     }
 
     protected boolean isPageMoving() {
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 87279d0..701b33b 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -39,6 +39,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Camera;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Rect;
@@ -74,7 +75,8 @@
  * interact with. A workspace is meant to be used with a fixed width only.
  */
 public class Workspace extends SmoothPagedView
-        implements DropTarget, DragSource, DragScroller, View.OnTouchListener {
+        implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
+        DragController.DragListener {
     @SuppressWarnings({"UnusedDeclaration"})
     private static final String TAG = "Launcher.Workspace";
 
@@ -151,15 +153,21 @@
 
     // State variable that indicates whether the pages are small (ie when you're
     // in all apps or customize mode)
-    private boolean mIsSmall = false;
-    private boolean mIsInUnshrinkAnimation = false;
+
+    enum State { NORMAL, SPRING_LOADED, SMALL };
+    private State mState;
+    private boolean mIsSwitchingState = false;
+
+    private boolean mSwitchStateAfterFirstLayout = false;
+    private State mStateAfterFirstLayout;
+
+    private AnimatorSet mAnimator;
     private AnimatorListener mShrinkAnimationListener;
     private AnimatorListener mUnshrinkAnimationListener;
-    enum ShrinkState { SPRING_LOADED, MIDDLE, BOTTOM_HIDDEN, BOTTOM_VISIBLE };
-    private ShrinkState mShrinkState;
-    private boolean mWaitingToShrink = false;
-    private ShrinkState mWaitingToShrinkState;
-    private AnimatorSet mAnimator;
+
+    boolean mAnimatingViewIntoPlace = false;
+    boolean mIsDragOccuring = false;
+    boolean mChildrenLayersEnabled = true;
 
     /** Is the user is dragging an item near the edge of a page? */
     private boolean mInScrollArea = false;
@@ -277,6 +285,16 @@
         setMotionEventSplittingEnabled(true);
     }
 
+    public void onDragStart(DragSource source, Object info, int dragAction) {
+        mIsDragOccuring = true;
+        updateChildrenLayersEnabled();
+    }
+
+    public void onDragEnd() {
+        mIsDragOccuring = false;
+        updateChildrenLayersEnabled();
+    }
+
     /**
      * Initializes various states for this workspace.
      */
@@ -299,25 +317,26 @@
         mUnshrinkAnimationListener = new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                mIsInUnshrinkAnimation = true;
+                mIsSwitchingState = true;
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
-                mIsInUnshrinkAnimation = false;
+                mIsSwitchingState = false;
                 mSyncWallpaperOffsetWithScroll = true;
                 mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
                 mAnimator = null;
-                enableChildrenLayers(false);
+                updateChildrenLayersEnabled();
             }
         };
         mShrinkAnimationListener = new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                enableChildrenLayers(true);
+                mIsSwitchingState = true;
             }
             @Override
             public void onAnimationEnd(Animator animation) {
+                mIsSwitchingState = false;
                 mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
                 mAnimator = null;
             }
@@ -501,7 +520,7 @@
      */
     @Override
     public boolean onTouch(View v, MotionEvent event) {
-        return (mIsSmall || mIsInUnshrinkAnimation);
+        return (isSmall() || mIsSwitchingState);
     }
 
     protected void onWindowVisibilityChanged (int visibility) {
@@ -510,7 +529,7 @@
 
     @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
-        if (mIsSmall || mIsInUnshrinkAnimation) {
+        if (isSmall() || mIsSwitchingState) {
             // when the home screens are shrunken, shouldn't allow side-scrolling
             return false;
         }
@@ -529,7 +548,7 @@
 
     @Override
     protected void determineScrollingStart(MotionEvent ev) {
-        if (!mIsSmall && !mIsInUnshrinkAnimation) {
+        if (!isSmall() && !mIsSwitchingState) {
             float deltaX = Math.abs(ev.getX() - mXDown);
             float deltaY = Math.abs(ev.getY() - mYDown);
 
@@ -563,19 +582,23 @@
 
     @Override
     protected boolean isScrollingIndicatorEnabled() {
-        return mShrinkState != ShrinkState.SPRING_LOADED;
+        return mState != State.SPRING_LOADED;
     }
 
     protected void onPageBeginMoving() {
         super.onPageBeginMoving();
 
-        if (mNextPage != INVALID_PAGE) {
-            // we're snapping to a particular screen
-            enableChildrenCache(mCurrentPage, mNextPage);
+        if (isHardwareAccelerated()) {
+            updateChildrenLayersEnabled();
         } else {
-            // this is when user is actively dragging a particular screen, they might
-            // swipe it either left or right (but we won't advance by more than one screen)
-            enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
+            if (mNextPage != INVALID_PAGE) {
+                // we're snapping to a particular screen
+                enableChildrenCache(mCurrentPage, mNextPage);
+            } else {
+                // this is when user is actively dragging a particular screen, they might
+                // swipe it either left or right (but we won't advance by more than one screen)
+                enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
+            }
         }
 
         // Only show page outlines as we pan if we are on large screen
@@ -587,7 +610,12 @@
     protected void onPageEndMoving() {
         super.onPageEndMoving();
 
-        clearChildrenCache();
+        if (isHardwareAccelerated()) {
+            updateChildrenLayersEnabled();
+        } else {
+            clearChildrenCache();
+        }
+
         // Hide the outlines, as long as we're not dragging
         if (!mDragController.dragging()) {
             // Only hide page outlines as we pan if we are on large screen
@@ -868,7 +896,7 @@
     }
 
     void showOutlines() {
-        if (!mIsSmall && !mIsInUnshrinkAnimation) {
+        if (!isSmall() && !mIsSwitchingState) {
             if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
             if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
             mChildrenOutlineFadeInAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 1.0f);
@@ -878,7 +906,7 @@
     }
 
     void hideOutlines() {
-        if (!mIsSmall && !mIsInUnshrinkAnimation) {
+        if (!isSmall() && !mIsSwitchingState) {
             if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
             if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
             mChildrenOutlineFadeOutAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 0.0f);
@@ -1070,13 +1098,13 @@
 
         // if shrinkToBottom() is called on initialization, it has to be deferred
         // until after the first call to onLayout so that it has the correct width
-        if (mWaitingToShrink) {
+        if (mSwitchStateAfterFirstLayout) {
+            mSwitchStateAfterFirstLayout = false;
             // shrink can trigger a synchronous onLayout call, so we
             // post this to avoid a stack overflow / tangled onLayout calls
             post(new Runnable() {
                 public void run() {
-                    shrink(mWaitingToShrinkState, false);
-                    mWaitingToShrink = false;
+                    shrink(mStateAfterFirstLayout, false);
                 }
             });
         }
@@ -1140,7 +1168,7 @@
 
     @Override
     public int getDescendantFocusability() {
-        if (mIsSmall) {
+        if (isSmall()) {
             return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
         }
         return super.getDescendantFocusability();
@@ -1158,6 +1186,10 @@
         }
     }
 
+    public boolean isSmall() {
+        return mState == State.SMALL;
+    }
+
     void enableChildrenCache(int fromPage, int toPage) {
         if (fromPage > toPage) {
             final int temp = fromPage;
@@ -1185,23 +1217,24 @@
         }
     }
 
-    protected void enableChildrenLayers(boolean enable) {
-        for (int i = 0; i < getPageCount(); i++) {
-            ((ViewGroup)getChildAt(i)).setChildrenLayersEnabled(enable);
-        }
-    }
-    @Override
-    protected void pageBeginMoving() {
-        enableChildrenLayers(true);
-        super.pageBeginMoving();
+    private boolean childLayersEnabled() {
+        boolean isSmallOrSpringloaded =
+            isSmall() || mIsSwitchingState || mState == State.SPRING_LOADED;
+        return isSmallOrSpringloaded || isPageMoving() || mIsDragOccuring;
     }
 
-    @Override
-    protected void pageEndMoving() {
-        if (!mIsSmall && !mIsInUnshrinkAnimation) {
-            enableChildrenLayers(false);
+    private void updateChildrenLayersEnabled() {
+        boolean small =
+            isSmall() || mIsSwitchingState || mState == State.SPRING_LOADED;
+        boolean dragging = mAnimatingViewIntoPlace || mIsDragOccuring;
+        boolean enableChildrenLayers = small || dragging || isPageMoving();
+
+        if (enableChildrenLayers != mChildrenLayersEnabled) {
+            mChildrenLayersEnabled = enableChildrenLayers;
+            for (int i = 0; i < getPageCount(); i++) {
+                ((ViewGroup)getChildAt(i)).setChildrenLayersEnabled(enableChildrenLayers);
+            }
         }
-        super.pageEndMoving();
     }
 
     @Override
@@ -1219,10 +1252,6 @@
                 position[0], position[1], 0, null);
     }
 
-    public boolean isSmall() {
-        return mIsSmall;
-    }
-
     private float getYScaleForScreen(int screen) {
         int x = Math.abs(screen - 2);
 
@@ -1235,21 +1264,22 @@
         return 1.0f;
     }
 
-    public void shrink(ShrinkState shrinkState) {
+    public void shrink(State shrinkState) {
         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) {
+    public void shrink(State shrinkState, boolean animated) {
         if (mFirstLayout) {
             // (mFirstLayout == "first layout has not happened yet")
             // if we get a call to shrink() as part of our initialization (for example, if
             // Launcher is started in All Apps mode) then we need to wait for a layout call
             // to get our width so we can layout the mini-screen views correctly
-            mWaitingToShrink = true;
-            mWaitingToShrinkState = shrinkState;
+            mSwitchStateAfterFirstLayout = true;
+            mStateAfterFirstLayout = shrinkState;
             return;
         }
+
         // Stop any scrolling, move to the current page right away
         setCurrentPage((mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage);
 
@@ -1264,8 +1294,8 @@
         }
         currentPage.setBackgroundAlphaMultiplier(1.0f);
 
-        mIsSmall = true;
-        mShrinkState = shrinkState;
+        mState = shrinkState;
+        updateChildrenLayersEnabled();
 
         // we intercept and reject all touch events when we're small, so be sure to reset the state
         mTouchState = TOUCH_STATE_REST;
@@ -1296,24 +1326,11 @@
         float finalAlpha = 1.0f;
         float extraShrinkFactor = 1.0f;
 
-        if (shrinkState == ShrinkState.BOTTOM_VISIBLE) {
-             y = screenHeight - y - scaledPageHeight;
-        } else if (shrinkState == ShrinkState.BOTTOM_HIDDEN) {
-            // We shrink and disappear to nothing in the case of all apps
-            // (which is when we shrink to the bottom)
-            y = screenHeight - y - scaledPageHeight;
-            finalAlpha = 0.0f;
-        } else if (shrinkState == ShrinkState.MIDDLE) {
-            y = screenHeight / 2 - scaledPageHeight / 2;
-            finalAlpha = 1.0f;
-        }
+        // We shrink and disappear to nothing
+        y = screenHeight - y - scaledPageHeight;
+        finalAlpha = 0.0f;
 
-        int duration;
-        if (shrinkState == ShrinkState.BOTTOM_HIDDEN || shrinkState == ShrinkState.BOTTOM_VISIBLE) {
-            duration = res.getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
-        } else {
-            duration = res.getInteger(R.integer.config_customizeWorkspaceShrinkTime);
-        }
+        int duration = res.getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
 
         // We animate all the screens to the centered position in workspace
         // At the same time, the screens become greyed/dimmed
@@ -1403,13 +1420,11 @@
         if (enableWallpaperEffects) {
             switch (shrinkState) {
                 // animating in
-                case MIDDLE:
                 case SPRING_LOADED:
                     wallpaperOffset = 0.5f;
                     mWallpaperOffset.setVerticalCatchupConstant(isLandscape ? 0.34f : 0.32f);
                     break;
-                case BOTTOM_HIDDEN:
-                case BOTTOM_VISIBLE:
+                case SMALL:
                     // allapps
                     wallpaperOffset = 0.5f - offsetFromCenter;
                     mWallpaperOffset.setVerticalCatchupConstant(isLandscape ? 0.34f : 0.32f);
@@ -1582,7 +1597,7 @@
 
     public void unshrink(CellLayout clThatWasClicked, boolean springLoaded) {
         int newCurrentPage = indexOfChild(clThatWasClicked);
-        if (mIsSmall) {
+        if (isSmall()) {
             if (springLoaded) {
                 setLayoutScale(mSpringLoadedShrinkFactor);
             }
@@ -1593,11 +1608,10 @@
 
 
     public void enterSpringLoadedDragMode(CellLayout clThatWasClicked) {
-        mShrinkState = ShrinkState.SPRING_LOADED;
         unshrink(clThatWasClicked, true);
     }
 
-    public void exitSpringLoadedDragMode(ShrinkState shrinkState) {
+    public void exitSpringLoadedDragMode(State shrinkState) {
         shrink(shrinkState);
     }
 
@@ -1611,15 +1625,14 @@
     }
 
     void unshrink(boolean animated, boolean springLoaded) {
-        mWaitingToShrink = false;
-        if (mIsSmall) {
+        if (isSmall()) {
             float finalScaleFactor = 1.0f;
             float finalBackgroundAlpha = 0.0f;
             if (springLoaded) {
                 finalScaleFactor = mSpringLoadedShrinkFactor;
                 finalBackgroundAlpha = 1.0f;
             } else {
-                mIsSmall = false;
+                mState = springLoaded ? State.SPRING_LOADED : State.NORMAL;
             }
             if (mAnimator != null) {
                 mAnimator.cancel();
@@ -1712,9 +1725,8 @@
             final boolean enableWallpaperEffects =
                 isHardwareAccelerated() && LauncherApplication.isScreenLarge();
             if (enableWallpaperEffects) {
-                switch (mShrinkState) {
+                switch (mState) {
                     // animating out
-                    case MIDDLE:
                     case SPRING_LOADED:
                         if (animated) {
                             mWallpaperOffset.setHorizontalCatchupConstant(isLandscape ? 0.49f : 0.46f);
@@ -1722,8 +1734,7 @@
                             mWallpaperOffset.setOverrideHorizontalCatchupConstant(true);
                         }
                         break;
-                    case BOTTOM_HIDDEN:
-                    case BOTTOM_VISIBLE:
+                    case SMALL:
                         // all apps
                         if (animated) {
                             mWallpaperOffset.setHorizontalCatchupConstant(isLandscape ? 0.65f : 0.65f);
@@ -1818,32 +1829,57 @@
      * @param destCanvas the canvas to draw on
      * @param padding the horizontal and vertical padding to use when drawing
      */
-    private void drawDragView(View v, Canvas destCanvas, int padding) {
+    private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
         final Rect clipRect = mTempRect;
         v.getDrawingRect(clipRect);
 
-        // For a TextView, adjust the clip rect so that we don't include the text label
-        if (v instanceof BubbleTextView) {
-            final BubbleTextView tv = (BubbleTextView) v;
-            clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
-                    tv.getLayout().getLineTop(0);
-        } else if (v instanceof TextView) {
-            final TextView tv = (TextView) v;
-            clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
-                    tv.getLayout().getLineTop(0);
-        } else if (v instanceof FolderIcon) {
-            clipRect.bottom = getResources().getDimensionPixelSize(R.dimen.folder_preview_size);
+        destCanvas.save();
+        if (v instanceof TextView && pruneToDrawable) {
+            Drawable d = ((TextView) v).getCompoundDrawables()[1];
+            clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding);
+            destCanvas.translate(padding / 2, padding / 2);
+            d.draw(destCanvas);
+        } else {
+            if (v instanceof FolderIcon) {
+                clipRect.bottom = getResources().getDimensionPixelSize(R.dimen.folder_preview_size);
+            } else if (v instanceof BubbleTextView) {
+                final BubbleTextView tv = (BubbleTextView) v;
+                clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
+                        tv.getLayout().getLineTop(0);
+            } else if (v instanceof TextView) {
+                final TextView tv = (TextView) v;
+                clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
+                        tv.getLayout().getLineTop(0);
+            }
+            destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
+            destCanvas.clipRect(clipRect, Op.REPLACE);
+            v.draw(destCanvas);
+        }
+        destCanvas.restore();
+    }
+
+    /**
+     * Returns a new bitmap to show when the given View is being dragged around.
+     * Responsibility for the bitmap is transferred to the caller.
+     */
+    public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
+        final int outlineColor = getResources().getColor(R.color.drag_outline_color);
+        Bitmap b;
+
+        if (v instanceof TextView) {
+            Drawable d = ((TextView) v).getCompoundDrawables()[1];
+            b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding,
+                    d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888);
+        } else {
+            b = Bitmap.createBitmap(
+                    v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
         }
 
-        // Draw the View into the bitmap.
-        // The translate of scrollX and scrollY is necessary when drawing TextViews, because
-        // they set scrollX and scrollY to large values to achieve centered text
+        canvas.setBitmap(b);
+        drawDragView(v, canvas, padding, true);
+        mOutlineHelper.applyOuterBlur(b, canvas, outlineColor);
 
-        destCanvas.save();
-        destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
-        destCanvas.clipRect(clipRect, Op.REPLACE);
-        v.draw(destCanvas);
-        destCanvas.restore();
+        return b;
     }
 
     /**
@@ -1856,7 +1892,7 @@
                 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
 
         canvas.setBitmap(b);
-        drawDragView(v, canvas, padding);
+        drawDragView(v, canvas, padding, false);
         mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
         return b;
     }
@@ -1910,23 +1946,6 @@
         return b;
     }
 
-    /**
-     * Returns a new bitmap to show when the given View is being dragged around.
-     * Responsibility for the bitmap is transferred to the caller.
-     */
-    private Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
-        final int outlineColor = getResources().getColor(R.color.drag_outline_color);
-        final Bitmap b = Bitmap.createBitmap(
-                mDragOutline.getWidth(), mDragOutline.getHeight(), Bitmap.Config.ARGB_8888);
-
-        canvas.setBitmap(b);
-        canvas.drawBitmap(mDragOutline, 0, 0, null);
-        drawDragView(v, canvas, padding);
-        mOutlineHelper.applyOuterBlur(b, canvas, outlineColor);
-
-        return b;
-    }
-
     void startDrag(CellLayout.CellInfo cellInfo) {
         View child = cellInfo.cell;
 
@@ -1950,9 +1969,15 @@
 
         // The outline is used to visualize where the item will land if dropped
         mDragOutline = createDragOutline(child, canvas, bitmapPadding);
+        beginDragShared(child, this);
+    }
+
+    public void beginDragShared(View child, DragSource source) {
+        // We need to add extra padding to the bitmap to make room for the glow effect
+        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
 
         // The drag bitmap follows the touch point around on the screen
-        final Bitmap b = createDragBitmap(child, canvas, bitmapPadding);
+        final Bitmap b = createDragBitmap(child, new Canvas(), bitmapPadding);
 
         final int bmpWidth = b.getWidth();
         final int bmpHeight = b.getHeight();
@@ -1974,10 +1999,10 @@
             dragRect = new Rect(0, 0, child.getWidth(), previewSize);
         }
 
-        mLauncher.lockScreenOrientation();
-        mDragController.startDrag(b, dragLayerX, dragLayerY, this, child.getTag(),
+        mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
                 DragController.DRAG_ACTION_MOVE, dragRect);
         b.recycle();
+        mLauncher.lockScreenOrientation();
     }
 
     void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY,
@@ -2096,7 +2121,7 @@
             }
 
             Rect folderLocation = new Rect();
-            mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
+            float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
             target.removeView(v);
 
             FolderIcon fi = mLauncher.addFolder(screen, targetCell[0], targetCell[1]);
@@ -2105,7 +2130,7 @@
             sourceInfo.cellX = -1;
             sourceInfo.cellY = -1;
 
-            fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation,
+            fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
                     postAnimationRunnable);
             return true;
         }
@@ -2145,7 +2170,7 @@
         // new current/default screen, so any subsequent taps add items to that screen
         if (!mLauncher.isAllAppsVisible()) {
             int dragTargetIndex = indexOfChild(mDragTargetLayout);
-            if (mCurrentPage != dragTargetIndex && (mIsSmall || mIsInUnshrinkAnimation)) {
+            if (mCurrentPage != dragTargetIndex && (isSmall() || mIsSwitchingState)) {
                 scrollToNewPageWithoutMovingPages(dragTargetIndex);
             }
         }
@@ -2188,7 +2213,7 @@
                         (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY, cell,
                         dropTargetLayout, mTargetCell);
 
-                if (dropInscrollArea && mShrinkState != ShrinkState.SPRING_LOADED) {
+                if (dropInscrollArea && mState != State.SPRING_LOADED) {
                     snapToPage(screen);
                 }
 
@@ -2246,7 +2271,16 @@
 
             // Prepare it to be animated into its new position
             // This must be called after the view has been re-parented
-            mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell);
+            final Runnable disableHardwareLayersRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    mAnimatingViewIntoPlace = false;
+                    updateChildrenLayersEnabled();
+                }
+            };
+            mAnimatingViewIntoPlace = true;
+            mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell,
+                    disableHardwareLayersRunnable);
             parent.onDropChild(cell);
         }
     }
@@ -2618,7 +2652,7 @@
     public void onDragOver(DragObject d) {
         // Skip drag over events while we are dragging over side pages
         if (mInScrollArea) return;
-        if (mIsInUnshrinkAnimation) return;
+        if (mIsSwitchingState) return;
 
         CellLayout layout;
         ItemInfo item = (ItemInfo) d.dragInfo;
@@ -2627,7 +2661,7 @@
         if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
 
         // Identify whether we have dragged over a side page
-        if (mIsSmall) {
+        if (isSmall()) {
             int left = d.x - d.xOffset;
             int top = d.y - d.yOffset;
             layout = findMatchingPageForDragOver(d.dragView, left, top, d.xOffset, d.yOffset, true);
@@ -2647,7 +2681,7 @@
                     mLastDragOverView = null;
                 }
 
-                boolean isInSpringLoadedMode = (mShrinkState == ShrinkState.SPRING_LOADED);
+                boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
                 if (isInSpringLoadedMode) {
                     mSpringLoadedDragController.setAlarm(mDragTargetLayout);
                 }
@@ -2800,7 +2834,7 @@
             }
         };
         final int screen = indexOfChild(cellLayout);
-        if (screen != mCurrentPage && mShrinkState != ShrinkState.SPRING_LOADED) {
+        if (screen != mCurrentPage && mState != State.SPRING_LOADED) {
             snapToPage(screen);
         }
         if (dragInfo instanceof PendingAddItemInfo) {
@@ -2940,6 +2974,12 @@
         mLauncher = launcher;
         mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
         mDragController = dragController;
+
+
+        // hardware layers on children are enabled on startup, but should be disabled until
+        // needed
+        updateChildrenLayersEnabled();
+        setWallpaperDimension();
     }
 
     /**
@@ -2947,13 +2987,14 @@
      */
     public void onDropCompleted(View target, DragObject d, boolean success) {
         if (success) {
-            if (target != this && mDragInfo != null) {
-                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
-                cellLayout.removeView(mDragInfo.cell);
-                if (mDragInfo.cell instanceof DropTarget) {
-                    mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
+            if (target != this) {
+                if (mDragInfo != null) {
+                    final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
+                    cellLayout.removeView(mDragInfo.cell);
+                    if (mDragInfo.cell instanceof DropTarget) {
+                        mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
+                    }
                 }
-                // final Object tag = mDragInfo.cell.getTag();
             }
         } else if (mDragInfo != null) {
             // NOTE: When 'success' is true, onDragExit is called by the DragController before
@@ -2984,7 +3025,7 @@
 
     @Override
     public void scrollLeft() {
-        if (!mIsSmall && !mIsInUnshrinkAnimation) {
+        if (!isSmall() && !mIsSwitchingState) {
             super.scrollLeft();
         }
         Folder openFolder = getOpenFolder();
@@ -2995,7 +3036,7 @@
 
     @Override
     public void scrollRight() {
-        if (!mIsSmall && !mIsInUnshrinkAnimation) {
+        if (!isSmall() && !mIsSwitchingState) {
             super.scrollRight();
         }
         Folder openFolder = getOpenFolder();
@@ -3006,7 +3047,7 @@
 
     @Override
     public void onEnterScrollArea(int direction) {
-        if (!mIsSmall && !mIsInUnshrinkAnimation) {
+        if (!isSmall() && !mIsSwitchingState) {
             mInScrollArea = true;
 
             final int page = mCurrentPage + (direction == DragController.SCROLL_LEFT ? -1 : 1);
@@ -3239,7 +3280,7 @@
     }
 
     void moveToDefaultScreen(boolean animate) {
-        if (mIsSmall || mIsInUnshrinkAnimation) {
+        if (isSmall() || mIsSwitchingState) {
             mLauncher.showWorkspace(animate, (CellLayout)getChildAt(mDefaultPage));
         } else if (animate) {
             snapToPage(mDefaultPage);