Merge "Revert "Fixing strict mode warning when installing an app in the bg.""
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 28de742..03f865d 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -41,7 +41,7 @@
     <string name="group_widgets" msgid="6704978494073105844">"Widget\'lar"</string>
     <string name="group_wallpapers" msgid="1568191644272224858">"Duvar Kağıtları"</string>
     <string name="out_of_space" msgid="3056555298801463098">"Ana ekranlarınızda başka yer kalmadı."</string>
-    <string name="hotseat_out_of_space" msgid="6304886797358479361">"Favori kısa yollarda yer yok"</string>
+    <string name="hotseat_out_of_space" msgid="6304886797358479361">"Favori kısayollarda yer yok"</string>
     <string name="invalid_hotseat_item" msgid="6545340627805449250">"Bu widget, hotseat için çok büyük."</string>
     <string name="shortcut_installed" msgid="7071557296331322355">"\"<xliff:g id="NAME">%s</xliff:g>\" kısayolu oluşturuldu."</string>
     <string name="shortcut_uninstalled" msgid="2129499669449749995">"\"<xliff:g id="NAME">%s</xliff:g>\" kısayolu kaldırıldı."</string>
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 840ec45..67ae1f1 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -577,6 +577,16 @@
                 info.boundWidget = hostView;
                 mWidgetCleanupState = WIDGET_INFLATED;
                 hostView.setVisibility(INVISIBLE);
+                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info.spanX,
+                        info.spanY, info, false);
+
+                // We want the first widget layout to be the correct size. This will be important
+                // for width size reporting to the AppWidgetManager.
+                DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0],
+                        unScaledSize[1]);
+                lp.x = lp.y = 0;
+                lp.customPosition = true;
+                hostView.setLayoutParams(lp);
                 mLauncher.getDragLayer().addView(hostView);
             }
         };
@@ -629,14 +639,10 @@
         if (createItemInfo instanceof PendingAddWidgetInfo) {
             PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo;
             createItemInfo = createWidgetInfo;
-            int[] spanXY = mLauncher.getSpanForWidget(createWidgetInfo, null);
-            int[] size = mLauncher.getWorkspace().estimateItemSize(spanXY[0],
-                    spanXY[1], createWidgetInfo, true);
-            createItemInfo.spanX = spanXY[0];
-            createItemInfo.spanY = spanXY[1];
-            int[] minSpanXY = mLauncher.getMinSpanForWidget(createWidgetInfo, null);
-            createWidgetInfo.minSpanX = minSpanXY[0];
-            createWidgetInfo.minSpanY = minSpanXY[1];
+            int spanX = createItemInfo.spanX;
+            int spanY = createItemInfo.spanY;
+            int[] size = mLauncher.getWorkspace().estimateItemSize(spanX, spanY,
+                    createWidgetInfo, true);
 
             FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
             float minScale = 1.25f;
@@ -644,7 +650,7 @@
             minWidth = Math.max((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
             minHeight = Math.max((int) (previewDrawable.getIntrinsicHeight() * minScale), size[1]);
             preview = getWidgetPreview(createWidgetInfo.componentName, createWidgetInfo.previewImage,
-                    createWidgetInfo.icon, spanXY[0], spanXY[1], minWidth, minHeight);
+                    createWidgetInfo.icon, spanX, spanY, minWidth, minHeight);
 
             // Determine the image view drawable scale relative to the preview
             float[] mv = new float[9];
@@ -731,7 +737,6 @@
      * @param target where the item was dragged to (can be null if the item was flung)
      */
     private void endDragging(View target, boolean isFlingToDelete, boolean success) {
-        mLauncher.getWorkspace().onDragStopped(success);
         if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
                 !(target instanceof DeleteDropTarget))) {
             // Exit spring loaded mode if we have not successfully dropped or have not handled the
@@ -1198,8 +1203,18 @@
                 // Fill in the widget information
                 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo;
                 createItemInfo = new PendingAddWidgetInfo(info, null, null);
-                int[] cellSpans = mLauncher.getSpanForWidget(info, null);
-                widget.applyFromAppWidgetProviderInfo(info, -1, cellSpans);
+
+                // Determine the widget spans and min resize spans.
+                int[] spanXY = mLauncher.getSpanForWidget(info, null);
+                int[] size = mLauncher.getWorkspace().estimateItemSize(spanXY[0],
+                        spanXY[1], createItemInfo, true);
+                createItemInfo.spanX = spanXY[0];
+                createItemInfo.spanY = spanXY[1];
+                int[] minSpanXY = mLauncher.getMinSpanForWidget(info, null);
+                createItemInfo.minSpanX = minSpanXY[0];
+                createItemInfo.minSpanY = minSpanXY[1];
+
+                widget.applyFromAppWidgetProviderInfo(info, -1, spanXY);
                 widget.setTag(createItemInfo);
                 widget.setShortPressListener(this);
             } else if (rawInfo instanceof ResolveInfo) {
diff --git a/src/com/android/launcher2/AppsCustomizeTabHost.java b/src/com/android/launcher2/AppsCustomizeTabHost.java
index af0f205..0199d01 100644
--- a/src/com/android/launcher2/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher2/AppsCustomizeTabHost.java
@@ -255,8 +255,8 @@
                     PagedViewWidget.setDeletePreviewsWhenDetachedFromWindow(true);
                     mAnimationBuffer.setAlpha(1f);
                     mAnimationBuffer.setVisibility(View.VISIBLE);
-                    LayoutParams p = new FrameLayout.LayoutParams(child.getWidth(),
-                            child.getHeight());
+                    LayoutParams p = new FrameLayout.LayoutParams(child.getMeasuredWidth(),
+                            child.getMeasuredHeight());
                     p.setMargins((int) child.getLeft(), (int) child.getTop(), 0, 0);
                     mAnimationBuffer.addView(child, p);
                 }
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 199c41a..da7c2b0 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -161,6 +161,7 @@
     private int[] mDirectionVector = new int[2];
     int[] mPreviousReorderDirection = new int[2];
     private static final int INVALID_DIRECTION = -100;
+    private DropTarget.DragEnforcer mDragEnforcer;
 
     public CellLayout(Context context) {
         this(context, null);
@@ -172,6 +173,7 @@
 
     public CellLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        mDragEnforcer = new DropTarget.DragEnforcer(mContext);
 
         // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
         // the user where a dragged item will land when dropped.
@@ -2508,6 +2510,7 @@
      * or it may have begun on another layout.
      */
     void onDragEnter() {
+        mDragEnforcer.onDragEnter();
         if (!mDragging) {
             // Fade in the drag indicators
             if (mCrosshairsAnimator != null) {
@@ -2521,6 +2524,7 @@
      * Called when drag has left this CellLayout or has been completed (successfully or not)
      */
     void onDragExit() {
+        mDragEnforcer.onDragExit();
         // This can actually be called when we aren't in a drag, e.g. when adding a new
         // item to this layout via the customize drawer.
         // Guard against that case.
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index eba89e5..2a1d65a 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -377,7 +377,11 @@
             if (rawDragInfo instanceof ShortcutInfo) {
                 ShortcutInfo dragInfo = (ShortcutInfo) rawDragInfo;
                 for (ApplicationInfo info : apps) {
-                    if (dragInfo.intent.getComponent().equals(info.intent.getComponent())) {
+                    // Added null checks to prevent NPE we've seen in the wild
+                    if (dragInfo != null &&
+                        dragInfo.intent != null &&
+                        info.intent != null &&
+                        dragInfo.intent.getComponent().equals(info.intent.getComponent())) {
                         cancelDrag();
                         return;
                     }
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index fb714c6..397d462 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher2;
 
+import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
 
@@ -63,6 +64,43 @@
         }
     }
 
+    public static class DragEnforcer implements DragController.DragListener {
+        int dragParity = 0;
+
+        public DragEnforcer(Context context) {
+            Launcher launcher = (Launcher) context;
+            launcher.getDragController().addDragListener(this);
+        }
+
+        void onDragEnter() {
+            dragParity++;
+            if (dragParity != 1) {
+                throw new RuntimeException("onDragEnter: Drag contract violated: " + dragParity);
+            }
+        }
+
+        void onDragExit() {
+            dragParity--;
+            if (dragParity != 0) {
+                throw new RuntimeException("onDragExit: Drag contract violated: " + dragParity);
+            }
+        }
+
+        @Override
+        public void onDragStart(DragSource source, Object info, int dragAction) {
+            if (dragParity != 0) {
+                throw new RuntimeException("onDragEnter: Drag contract violated: " + dragParity);
+            }
+        }
+
+        @Override
+        public void onDragEnd() {
+            if (dragParity != 0) {
+                throw new RuntimeException("onDragExit: Drag contract violated: " + dragParity);
+            }
+        }
+    }
+
     /**
      * Used to temporarily disable certain drop targets
      *
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index c005edf..513fa6b 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -325,7 +325,10 @@
     }
 
     public void onDragExit(Object dragInfo) {
-        if (!willAcceptItem((ItemInfo) dragInfo)) return;
+        onDragExit();
+    }
+
+    public void onDragExit() {
         mFolderRingAnimator.animateToNaturalState();
     }
 
diff --git a/src/com/android/launcher2/Hotseat.java b/src/com/android/launcher2/Hotseat.java
index 9e525bd..fbb45b9 100644
--- a/src/com/android/launcher2/Hotseat.java
+++ b/src/com/android/launcher2/Hotseat.java
@@ -104,7 +104,6 @@
                 inflater.inflate(R.layout.application, mContent, false);
         allAppsButton.setCompoundDrawablesWithIntrinsicBounds(null,
                 context.getResources().getDrawable(R.drawable.all_apps_button_icon), null, null);
-        // allAppsButton.setText(context.getString(R.string.all_apps_button_label));
         allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
         allAppsButton.setOnTouchListener(new View.OnTouchListener() {
             @Override
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index ee540f8..b3ce968 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -524,6 +524,10 @@
                 // We just wanted the activity result here so we can clear mWaitingForResult
                 break;
         }
+        // Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
+        // if you turned the screen off and then back while in All Apps, Launcher would not
+        // return to the workspace. Clearing mAddInfo.container here fixes this issue
+        resetAddInfo();
         return result;
     }
 
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 30eb86c..a9c4970 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -1228,7 +1228,39 @@
                 return;
             }
 
-            int N;
+            // Get the list of workspace items to load and unbind the existing ShortcutInfos
+            // before we call startBinding() below.
+            final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
+            final ArrayList<ItemInfo> tmpWorkspaceItems = unbindWorkspaceItemsOnMainThread();
+            // Order the items for loading as follows: current workspace, hotseat, everything else
+            Collections.sort(tmpWorkspaceItems, new Comparator<ItemInfo>() {
+                @Override
+                public int compare(ItemInfo lhs, ItemInfo rhs) {
+                    int cellCountX = LauncherModel.getCellCountX();
+                    int cellCountY = LauncherModel.getCellCountY();
+                    int screenOffset = cellCountX * cellCountY;
+                    int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
+                    long lr = (lhs.container * containerOffset + lhs.screen * screenOffset +
+                            lhs.cellY * cellCountX + lhs.cellX);
+                    long rr = (rhs.container * containerOffset + rhs.screen * screenOffset +
+                            rhs.cellY * cellCountX + rhs.cellX);
+                    return (int) (lr - rr);
+                }
+            });
+            // Precondition: the items are ordered by page, screen
+            final ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
+            for (ItemInfo ii : tmpWorkspaceItems) {
+                // Prepend the current items, hotseat items, append everything else
+                if (ii.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
+                        ii.screen == currentScreen) {
+                    workspaceItems.add(0, ii);
+                } else if (ii.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+                    workspaceItems.add(0, ii);
+                } else {
+                    workspaceItems.add(ii);
+                }
+            }
+
             // Tell the workspace that we're about to start firing items at it
             mHandler.post(new Runnable() {
                 public void run() {
@@ -1239,10 +1271,8 @@
                 }
             });
 
-            final ArrayList<ItemInfo> workspaceItems = unbindWorkspaceItemsOnMainThread();
-
             // Add the items to the workspace.
-            N = workspaceItems.size();
+            int N = workspaceItems.size();
             for (int i=0; i<N; i+=ITEMS_CHUNK) {
                 final int start = i;
                 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
@@ -1278,7 +1308,6 @@
             // but since getCurrentScreen() just returns the int, we should be okay.  This
             // is just a hint for the order, and if it's wrong, we'll be okay.
             // TODO: instead, we should have that push the current screen into here.
-            final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
             N = sAppWidgets.size();
             // once for the current screen
             for (int i=0; i<N; i++) {
@@ -1354,6 +1383,7 @@
             }
 
             // shallow copy
+            @SuppressWarnings("unchecked")
             final ArrayList<ApplicationInfo> list
                     = (ArrayList<ApplicationInfo>) mAllAppsList.data.clone();
             mHandler.post(new Runnable() {
@@ -1657,7 +1687,26 @@
         // but don't worry about that.  All we're doing with usingFallbackIcon is
         // to avoid saving lots of copies of that in the database, and most apps
         // have icons anyway.
-        final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
+
+        // Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and
+        // if that fails, or is ambiguious, fallback to the standard way of getting the resolve info
+        // via resolveActivity().
+        ResolveInfo resolveInfo = null;
+        ComponentName oldComponent = intent.getComponent();
+        Intent newIntent = new Intent(intent.getAction(), null);
+        newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        newIntent.setPackage(oldComponent.getPackageName());
+        List<ResolveInfo> infos = manager.queryIntentActivities(newIntent, 0);
+        for (ResolveInfo i : infos) {
+            ComponentName cn = new ComponentName(i.activityInfo.packageName,
+                    i.activityInfo.name);
+            if (cn.equals(oldComponent)) {
+                resolveInfo = i;
+            }
+        }
+        if (resolveInfo == null) {
+            resolveInfo = manager.resolveActivity(intent, 0);
+        }
         if (resolveInfo != null) {
             icon = mIconCache.getIcon(componentName, resolveInfo, labelCache);
         }
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 5434704..604e73c 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -396,7 +396,9 @@
     protected boolean computeScrollHelper() {
         if (mScroller.computeScrollOffset()) {
             // Don't bother scrolling if the page does not need to be moved
-            if (mScrollX != mScroller.getCurrX() || mScrollY != mScroller.getCurrY()) {
+            if (mScrollX != mScroller.getCurrX()
+                || mScrollY != mScroller.getCurrY()
+                || mOverScrollX != mScroller.getCurrX()) {
                 scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
             }
             invalidate();
@@ -721,20 +723,21 @@
 
     protected void getVisiblePages(int[] range) {
         final int pageCount = getChildCount();
+
         if (pageCount > 0) {
             final int screenWidth = getMeasuredWidth();
             int leftScreen = 0;
             int rightScreen = 0;
             View currPage = getPageAt(leftScreen);
             while (leftScreen < pageCount - 1 &&
-                    currPage.getRight() - currPage.getPaddingRight() < mScrollX) {
+                    currPage.getX() + currPage.getWidth() - currPage.getPaddingRight() < mScrollX) {
                 leftScreen++;
                 currPage = getPageAt(leftScreen);
             }
             rightScreen = leftScreen;
             currPage = getPageAt(rightScreen + 1);
             while (rightScreen < pageCount - 1 &&
-                    currPage.getLeft() + currPage.getPaddingLeft() < mScrollX + screenWidth) {
+                    currPage.getX() - currPage.getPaddingLeft() < mScrollX + screenWidth) {
                 rightScreen++;
                 currPage = getPageAt(rightScreen + 1);
             }
@@ -754,9 +757,11 @@
         int screenCenter = mOverScrollX + halfScreenSize;
 
         if (screenCenter != mLastScreenCenter || mForceScreenScrolled) {
+            // set mForceScreenScrolled before calling screenScrolled so that screenScrolled can
+            // set it for the next frame
+            mForceScreenScrolled = false;
             screenScrolled(screenCenter);
             mLastScreenCenter = screenCenter;
-            mForceScreenScrolled = false;
         }
 
         // Find out which screens are visible; as an optimization we only call draw on them
diff --git a/src/com/android/launcher2/PendingAddItemInfo.java b/src/com/android/launcher2/PendingAddItemInfo.java
index d36e217..26e946e 100644
--- a/src/com/android/launcher2/PendingAddItemInfo.java
+++ b/src/com/android/launcher2/PendingAddItemInfo.java
@@ -76,5 +76,9 @@
         configurationData = copy.configurationData;
         componentName = copy.componentName;
         itemType = copy.itemType;
+        spanX = copy.spanX;
+        spanY = copy.spanY;
+        minSpanX = copy.minSpanX;
+        minSpanY = copy.minSpanY;
     }
 }
diff --git a/src/com/android/launcher2/ShortcutInfo.java b/src/com/android/launcher2/ShortcutInfo.java
index ff3028b..76892db 100644
--- a/src/com/android/launcher2/ShortcutInfo.java
+++ b/src/com/android/launcher2/ShortcutInfo.java
@@ -149,7 +149,10 @@
 
     @Override
     public String toString() {
-        return "ShortcutInfo(title=" + title.toString() + ")";
+        return "ShortcutInfo(title=" + title.toString() + "intent=" + intent + "id=" + this.id
+                + " type=" + this.itemType + " container=" + this.container + " screen=" + screen
+                + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY
+                + " isGesture=" + isGesture + " dropPos=" + dropPos + ")";
     }
 
     public static void dumpShortcutInfoList(String tag, String label,
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 06e0185..8c3b465 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -35,12 +35,14 @@
 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.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region.Op;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.IBinder;
 import android.os.Parcelable;
@@ -92,7 +94,7 @@
     private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
     private static final int FLING_THRESHOLD_VELOCITY = 500;
 
-    private float mMaxDistanceForFolderCreation = 50.0f;
+    private float mMaxDistanceForFolderCreation;
 
     // These animators are used to fade the children's outlines
     private ObjectAnimator mChildrenOutlineFadeInAnimation;
@@ -125,11 +127,23 @@
      * Target drop area calculated during last acceptDrop call.
      */
     private int[] mTargetCell = new int[2];
+    private int mDragOverX = -1;
+    private int mDragOverY = -1;
 
     /**
      * The CellLayout that is currently being dragged over
      */
     private CellLayout mDragTargetLayout = null;
+    /**
+     * The CellLayout that we will show as glowing
+     */
+    private CellLayout mDragOverlappingLayout = null;
+
+    /**
+     * The CellLayout which will be dropped to
+     */
+    private CellLayout mDropToLayout = null;
+
     private boolean mDragHasEnteredWorkspace = false;
 
     private Launcher mLauncher;
@@ -171,6 +185,7 @@
     private final Rect mTempRect = new Rect();
     private final int[] mTempXY = new int[2];
     private float mOverscrollFade = 0;
+    private boolean mOverscrollTransformsSet;
     public static final int DRAG_BITMAP_PADDING = 0;
 
     // Camera and Matrix used to determine the final position of a neighboring CellLayout
@@ -196,9 +211,10 @@
     private final Alarm mFolderCreationAlarm = new Alarm();
     private final Alarm mReorderAlarm = new Alarm();
     private FolderRingAnimator mDragFolderRingAnimator = null;
-    private View mLastDragOverView = null;
+    private FolderIcon mDragOverFolderIcon = null;
     private boolean mCreateUserFolderOnDrop = false;
-    private boolean mWillAddToExistingFolder = false;
+    private boolean mAddToExistingFolderOnDrop = false;
+    private DropTarget.DragEnforcer mDragEnforcer;
 
     // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
     private float mXDown;
@@ -223,10 +239,6 @@
     private int mLastReorderX = -1;
     private int mLastReorderY = -1;
 
-    // Relating to workspace drag fade out
-    private float mDragFadeOutAlpha;
-    private int mDragFadeOutDuration;
-
     // These variables are used for storing the initial and final values during workspace animations
     private int mSavedScrollX;
     private float mSavedRotationY;
@@ -275,13 +287,12 @@
         super(context, attrs, defStyle);
         mContentIsRefreshable = false;
 
+        mDragEnforcer = new DropTarget.DragEnforcer(context);
         // With workspace, data is available straight from the get-go
         setDataIsReady();
 
         final Resources res = getResources();
         mFadeInAdjacentScreens = res.getBoolean(R.bool.config_workspaceFadeAdjacentScreens);
-        mDragFadeOutAlpha = res.getInteger(R.integer.config_dragFadeOutAlpha) / 100f;
-        mDragFadeOutDuration = res.getInteger(R.integer.config_dragFadeOutDuration);
         mWallpaperManager = WallpaperManager.getInstance(context);
 
         int cellCountX = DEFAULT_CELL_COUNT_X;
@@ -410,7 +421,7 @@
         mWallpaperTravelWidth = (int) (mDisplayWidth *
                 wallpaperTravelToScreenWidthRatio(mDisplayWidth, mDisplayHeight));
 
-        mMaxDistanceForFolderCreation = (0.5f * res.getDimensionPixelSize(R.dimen.app_icon_size));
+        mMaxDistanceForFolderCreation = (0.6f * res.getDimensionPixelSize(R.dimen.app_icon_size));
         mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
     }
 
@@ -1196,9 +1207,12 @@
                                 overScrollBackgroundAlphaInterpolator(Math.abs(scrollProgress)));
                         mOverScrollPageIndex = i;
                         cl.setOverScrollAmount(Math.abs(scrollProgress), i == 0);
-                        cl.setPivotX(cl.getMeasuredWidth() * (i == 0 ? 0.75f : 0.25f));
-                        cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
-                        cl.setOverscrollTransformsDirty(true);
+                        if (!mOverscrollTransformsSet) {
+                            mOverscrollTransformsSet = true;
+                            cl.setPivotX(cl.getMeasuredWidth() * (i == 0 ? 0.75f : 0.25f));
+                            cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
+                            cl.setOverscrollTransformsDirty(true);
+                        }
                     } else if (mOverScrollPageIndex != i) {
                         cl.setBackgroundAlphaMultiplier(
                                 backgroundAlphaInterpolator(Math.abs(scrollProgress)));
@@ -1212,7 +1226,8 @@
                 }
             }
         }
-        if (!isSwitchingState() && !isInOverscroll) {
+        if (mOverscrollTransformsSet && !isInOverscroll) {
+            mOverscrollTransformsSet = false;
             ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
             ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
         }
@@ -1226,18 +1241,21 @@
             float scrollProgress = getScrollProgress(screenCenter, cl, index);
             cl.setOverScrollAmount(Math.abs(scrollProgress), index == 0);
             float rotation = - WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
-            cl.setCameraDistance(mDensity * CAMERA_DISTANCE);
-            cl.setPivotX(cl.getMeasuredWidth() * (index == 0 ? 0.75f : 0.25f));
-            cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
             cl.setRotationY(rotation);
-            cl.setOverscrollTransformsDirty(true);
             setFadeForOverScroll(Math.abs(scrollProgress));
+            if (!mOverscrollTransformsSet) {
+                mOverscrollTransformsSet = true;
+                cl.setCameraDistance(mDensity * CAMERA_DISTANCE);
+                cl.setPivotX(cl.getMeasuredWidth() * (index == 0 ? 0.75f : 0.25f));
+                cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
+                cl.setOverscrollTransformsDirty(true);
+            }
         } else {
             if (mOverscrollFade != 0) {
                 setFadeForOverScroll(0);
             }
-            // We don't want to mess with the translations during transitions
-            if (!isSwitchingState()) {
+            if (mOverscrollTransformsSet) {
+                mOverscrollTransformsSet = false;
                 ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
                 ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
             }
@@ -1514,15 +1532,6 @@
                 size[1], alphaClipPaint);
     }
 
-    // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was
-    // never dragged over
-    public void onDragStopped(boolean success) {
-        // In the success case, DragController has already called onDragExit()
-        if (!success) {
-            doDragExit(null);
-        }
-    }
-
     public void exitWidgetResizeMode() {
         DragLayer dragLayer = mLauncher.getDragLayer();
         dragLayer.clearAllResizeFrames();
@@ -1568,6 +1577,7 @@
 
         final State oldState = mState;
         final boolean oldStateIsNormal = (oldState == State.NORMAL);
+        final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
         final boolean oldStateIsSmall = (oldState == State.SMALL);
         mState = state;
         final boolean stateIsNormal = (state == State.NORMAL);
@@ -1608,8 +1618,9 @@
             if ((oldStateIsSmall && stateIsNormal) ||
                 (oldStateIsNormal && stateIsSmall)) {
                 // To/from workspace - only show the current page unless the transition is not
-                //                     animated and the animation end callback below doesn't run
-                if (i == mCurrentPage || !animated) {
+                //                     animated and the animation end callback below doesn't run;
+                //                     or, if we're in spring-loaded mode
+                if (i == mCurrentPage || !animated || oldStateIsSpringLoaded) {
                     finalAlpha = 1f;
                     finalAlphaMultiplierValue = 0f;
                 } else {
@@ -1983,9 +1994,10 @@
      */
     public boolean acceptDrop(DragObject d) {
         // If it's an external drop (e.g. from All Apps), check if it should be accepted
+        CellLayout dropTargetLayout = mDropToLayout;
         if (d.dragSource != this) {
             // Don't accept the drop if we're not over a screen at time of drop
-            if (mDragTargetLayout == null) {
+            if (dropTargetLayout == null) {
                 return false;
             }
             if (!transitionStateShouldAllowDrop()) return false;
@@ -1994,10 +2006,10 @@
                     d.dragView, mDragViewVisualCenter);
 
             // We want the point to be mapped to the dragTarget.
-            if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
+            if (mLauncher.isHotseatLayout(dropTargetLayout)) {
                 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
             } else {
-                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
+                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
             }
 
             int spanX = 1;
@@ -2020,21 +2032,21 @@
             }
 
             mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
-                    (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
+                    (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
                     mTargetCell);
-            float distance = mDragTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
+            float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
                     mDragViewVisualCenter[1], mTargetCell);
-            if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout,
+            if (willCreateUserFolder((ItemInfo) d.dragInfo, dropTargetLayout,
                     mTargetCell, distance, true)) {
                 return true;
             }
-            if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout,
+            if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, dropTargetLayout,
                     mTargetCell, distance)) {
                 return true;
             }
 
             int[] resultSpan = new int[2];
-            mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+            mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
                     (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
                     null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
             boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
@@ -2043,7 +2055,7 @@
             if (!foundCell) {
                 // Don't show the message if we are dropping on the AllApps button and the hotseat
                 // is full
-                boolean isHotseat = mLauncher.isHotseatLayout(mDragTargetLayout);
+                boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
                 if (mTargetCell != null && isHotseat) {
                     Hotseat hotseat = mLauncher.getHotseat();
                     if (hotseat.isAllAppsButtonRank(
@@ -2167,8 +2179,8 @@
         if (distance > mMaxDistanceForFolderCreation) return false;
 
         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
-        if (!mWillAddToExistingFolder) return false;
-        mWillAddToExistingFolder = false;
+        if (!mAddToExistingFolderOnDrop) return false;
+        mAddToExistingFolderOnDrop = false;
 
         if (dropOverView instanceof FolderIcon) {
             FolderIcon fi = (FolderIcon) dropOverView;
@@ -2189,17 +2201,17 @@
         mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
                 mDragViewVisualCenter);
 
+        CellLayout dropTargetLayout = mDropToLayout;
+
         // We want the point to be mapped to the dragTarget.
-        if (mDragTargetLayout != null) {
-            if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
+        if (dropTargetLayout != null) {
+            if (mLauncher.isHotseatLayout(dropTargetLayout)) {
                 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
             } else {
-                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
+                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
             }
         }
 
-        CellLayout dropTargetLayout = mDragTargetLayout;
-
         int snapScreen = -1;
         boolean resizeOnDrop = false;
         if (d.dragSource != this) {
@@ -2252,7 +2264,7 @@
                 }
 
                 int[] resultSpan = new int[2];
-                mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+                mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
                         (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
                         mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
 
@@ -2295,7 +2307,8 @@
 
                         final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
                         AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
-                        if (pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
+                        if (pinfo != null &&
+                                pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
                             final Runnable addResizeFrame = new Runnable() {
                                 public void run() {
                                     DragLayer dragLayer = mLauncher.getDragLayer();
@@ -2395,14 +2408,15 @@
     }
 
     public void onDragEnter(DragObject d) {
+        mDragEnforcer.onDragEnter();
         mDragHasEnteredWorkspace = true;
-        if (mDragTargetLayout != null) {
-            mDragTargetLayout.setIsDragOverlapping(false);
-            mDragTargetLayout.onDragExit();
-        }
-        mDragTargetLayout = getCurrentDropLayout();
-        mDragTargetLayout.setIsDragOverlapping(true);
-        mDragTargetLayout.onDragEnter();
+        mCreateUserFolderOnDrop = false;
+        mAddToExistingFolderOnDrop = false;
+
+        mDropToLayout = null;
+        CellLayout layout = getCurrentDropLayout();
+        setCurrentDropLayout(layout);
+        setCurrentDragOverlappingLayout(layout);
 
         // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
         // don't need to show the outlines
@@ -2411,26 +2425,29 @@
         }
     }
 
-    private void doDragExit(DragObject d) {
-        // Clean up folders
-        cleanupFolderCreation(d);
+    public void onDragExit(DragObject d) {
+        mDragEnforcer.onDragExit();
+        mDragHasEnteredWorkspace = false;
 
-        // Clean up reorder
-        if (mReorderAlarm != null) {
-            mReorderAlarm.cancelAlarm();
-            mLastReorderX = -1;
-            mLastReorderY = -1;
+        // Here we store the final page that will be dropped to, if the workspace in fact
+        // receives the drop
+        if (mInScrollArea) {
+            mDropToLayout = mDragOverlappingLayout;
+        } else {
+            mDropToLayout = mDragTargetLayout;
+        }
+
+        if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
+            mCreateUserFolderOnDrop = true;
+        } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
+            mAddToExistingFolderOnDrop = true;
         }
 
         // Reset the scroll area and previous drag target
         onResetScrollArea();
+        setCurrentDropLayout(null);
+        setCurrentDragOverlappingLayout(null);
 
-        if (mDragTargetLayout != null) {
-            mDragTargetLayout.setIsDragOverlapping(false);
-            mDragTargetLayout.onDragExit();
-        }
-        mLastDragOverView = null;
-        mDragMode = DRAG_MODE_NONE;
         mSpringLoadedDragController.cancel();
 
         if (!mIsPageMoving) {
@@ -2438,9 +2455,82 @@
         }
     }
 
-    public void onDragExit(DragObject d) {
-        mDragHasEnteredWorkspace = false;
-        doDragExit(d);
+    void setCurrentDropLayout(CellLayout layout) {
+        if (mDragTargetLayout != null) {
+            mDragTargetLayout.revertTempState();
+            mDragTargetLayout.onDragExit();
+        }
+        mDragTargetLayout = layout;
+        if (mDragTargetLayout != null) {
+            mDragTargetLayout.onDragEnter();
+        }
+        cleanupReorder(true);
+        cleanupFolderCreation();
+        setCurrentDropOverCell(-1, -1);
+    }
+
+    void setCurrentDragOverlappingLayout(CellLayout layout) {
+        if (mDragOverlappingLayout != null) {
+            mDragOverlappingLayout.setIsDragOverlapping(false);
+        }
+        mDragOverlappingLayout = layout;
+        if (mDragOverlappingLayout != null) {
+            mDragOverlappingLayout.setIsDragOverlapping(true);
+        }
+        invalidate();
+    }
+
+    void setCurrentDropOverCell(int x, int y) {
+        if (x != mDragOverX || y != mDragOverY) {
+            mDragOverX = x;
+            mDragOverY = y;
+            setDragMode(DRAG_MODE_NONE);
+        }
+    }
+
+    void setDragMode(int dragMode) {
+        if (dragMode != mDragMode) {
+            if (dragMode == DRAG_MODE_NONE) {
+                cleanupAddToFolder();
+                // We don't want to cancel the re-order alarm every time the target cell changes
+                // as this feels to slow / unresponsive.
+                cleanupReorder(false);
+                cleanupFolderCreation();
+            } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
+                cleanupReorder(true);
+                cleanupFolderCreation();
+            } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
+                cleanupAddToFolder();
+                cleanupReorder(true);
+            } else if (dragMode == DRAG_MODE_REORDER) {
+                cleanupAddToFolder();
+                cleanupFolderCreation();
+            }
+            mDragMode = dragMode;
+        }
+    }
+
+    private void cleanupFolderCreation() {
+        if (mDragFolderRingAnimator != null) {
+            mDragFolderRingAnimator.animateToNaturalState();
+        }
+        mFolderCreationAlarm.cancelAlarm();
+    }
+
+    private void cleanupAddToFolder() {
+        if (mDragOverFolderIcon != null) {
+            mDragOverFolderIcon.onDragExit(null);
+            mDragOverFolderIcon = null;
+        }
+    }
+
+    private void cleanupReorder(boolean cancelAlarm) {
+        // Any pending reorders are canceled
+        if (cancelAlarm) {
+            mReorderAlarm.cancelAlarm();
+        }
+        mLastReorderX = -1;
+        mLastReorderY = -1;
     }
 
     public DropTarget getDropTargetDelegate(DragObject d) {
@@ -2514,6 +2604,11 @@
        xy[1] = xy[1] - v.getTop();
    }
 
+   void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
+       xy[0] = xy[0] - hotseat.getLeft() - hotseat.getLayout().getLeft();
+       xy[1] = xy[1] - hotseat.getTop() - hotseat.getLayout().getTop();
+   }
+
    /*
     *
     * Convert the 2D coordinate xy from this CellLayout's coordinate space to
@@ -2695,21 +2790,9 @@
                 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
             }
             if (layout != mDragTargetLayout) {
-                // Cancel all intermediate folder states
-                cleanupFolderCreation(d);
 
-                if (mDragTargetLayout != null) {
-                    mDragTargetLayout.setIsDragOverlapping(false);
-                    mDragTargetLayout.onDragExit();
-                }
-                mDragTargetLayout = layout;
-                if (mDragTargetLayout != null) {
-                    mDragTargetLayout.setIsDragOverlapping(true);
-                    mDragTargetLayout.onDragEnter();
-                } else {
-                    mLastDragOverView = null;
-                    mDragMode = DRAG_MODE_NONE;
-                }
+                setCurrentDropLayout(layout);
+                setCurrentDragOverlappingLayout(layout);
 
                 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
                 if (isInSpringLoadedMode) {
@@ -2732,13 +2815,8 @@
                 layout = getCurrentDropLayout();
             }
             if (layout != mDragTargetLayout) {
-                if (mDragTargetLayout != null) {
-                    mDragTargetLayout.setIsDragOverlapping(false);
-                    mDragTargetLayout.onDragExit();
-                }
-                mDragTargetLayout = layout;
-                mDragTargetLayout.setIsDragOverlapping(true);
-                mDragTargetLayout.onDragEnter();
+                setCurrentDropLayout(layout);
+                setCurrentDragOverlappingLayout(layout);
             }
         }
 
@@ -2746,31 +2824,26 @@
         if (mDragTargetLayout != null) {
             // We want the point to be mapped to the dragTarget.
             if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
-                mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
+                mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
             } else {
                 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
             }
+
             ItemInfo info = (ItemInfo) d.dragInfo;
 
             mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
                     (int) mDragViewVisualCenter[1], 1, 1, mDragTargetLayout, mTargetCell);
+
+            setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
+
             float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
                     mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
 
             final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
                     mTargetCell[1]);
 
-            final View lastDragOverView = mLastDragOverView;
-            if (mLastDragOverView != dragOverView) {
-                mDragMode = DRAG_MODE_NONE;
-                mLastDragOverView = dragOverView;
-                if (mReorderAlarm != null) {
-                    mReorderAlarm.cancelAlarm();
-                }
-            }
-
-            boolean folder = willCreateOrAddToFolder(info, mDragTargetLayout, mTargetCell,
-                    targetCellDistance, dragOverView, lastDragOverView);
+            manageFolderFeedback(info, mDragTargetLayout, mTargetCell,
+                    targetCellDistance, dragOverView);
 
             int minSpanX = item.spanX;
             int minSpanY = item.spanY;
@@ -2784,96 +2857,70 @@
                     (int) mDragViewVisualCenter[1], item.spanX, item.spanY, mDragTargetLayout,
                     reorderPosition);
 
-            if (!mDragTargetLayout.isNearestDropLocationOccupied((int) mDragViewVisualCenter[0],
-                    (int) mDragViewVisualCenter[1], item.spanX, item.spanY, child, mTargetCell)) {
-                // If the current hover area isn't occupied (permanently) by any items, then we
-                // reset all the reordering.
-                mDragTargetLayout.revertTempState();
-                mDragMode = DRAG_MODE_NONE;
-                mLastDragOverView = dragOverView;
-                if (mReorderAlarm != null) {
-                    mReorderAlarm.cancelAlarm();
-                }
-                mLastReorderX = -1;
-                mLastReorderY = -1;
+            boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
+                    mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
+                    item.spanY, child, mTargetCell);
+
+            if (!nearestDropOccupied) {
                 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
                         (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
                         mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false,
                         d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
-            } else if (!folder && !mReorderAlarm.alarmPending() &&
-                    (mLastReorderX != reorderPosition[0] || mLastReorderY != reorderPosition[1])) {
+            } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
+                    && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderPosition[0] ||
+                    mLastReorderY != reorderPosition[1])) {
                 // Otherwise, if we aren't adding to or creating a folder and there's no pending
                 // reorder, then we schedule a reorder
-                cancelFolderCreation();
                 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
                         minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
                 mReorderAlarm.setOnAlarmListener(listener);
                 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
-            } else if (folder) {
-                if (mReorderAlarm != null) {
-                    mReorderAlarm.cancelAlarm();
+            }
+
+            if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
+                    !nearestDropOccupied) {
+                if (mDragTargetLayout != null) {
+                    mDragTargetLayout.revertTempState();
                 }
-                mDragTargetLayout.revertTempState();
-                mLastReorderX = -1;
-                mLastReorderY = -1;
             }
         }
     }
 
-    private boolean willCreateOrAddToFolder(ItemInfo info, CellLayout targetLayout,
-            int[] targetCell, float distance, View dragOverView, View lastDragOverView) {
+    private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout,
+            int[] targetCell, float distance, View dragOverView) {
         boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
                 false);
 
-        if (userFolderPending && mDragMode == DRAG_MODE_NONE) {
+        if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
+                !mFolderCreationAlarm.alarmPending()) {
             mFolderCreationAlarm.setOnAlarmListener(new
                     FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]));
             mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
+            return;
         }
 
         boolean willAddToFolder =
                 willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
 
         if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
-            FolderIcon fi = ((FolderIcon) dragOverView);
-            mDragMode = DRAG_MODE_ADD_TO_FOLDER;
-            mWillAddToExistingFolder = true;
-            fi.onDragEnter(info);
+            mDragOverFolderIcon = ((FolderIcon) dragOverView);
+            mAddToExistingFolderOnDrop = true;
+            mDragOverFolderIcon.onDragEnter(info);
             if (targetLayout != null) {
                 targetLayout.clearDragOutlines();
             }
+            setDragMode(DRAG_MODE_ADD_TO_FOLDER);
+            return;
         }
 
-        if (dragOverView != lastDragOverView || (mCreateUserFolderOnDrop && !userFolderPending)
-                || (!willAddToFolder && mDragMode == DRAG_MODE_ADD_TO_FOLDER)) {
-            cancelFolderCreation();
-            mWillAddToExistingFolder = false;
-            if (lastDragOverView != null && lastDragOverView instanceof FolderIcon) {
-                ((FolderIcon) lastDragOverView).onDragExit(info);
-            }
+        if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
+            setDragMode(DRAG_MODE_NONE);
+        }
+        if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
+            setDragMode(DRAG_MODE_NONE);
         }
 
-        return (willAddToFolder || userFolderPending) && mDragMode != DRAG_MODE_REORDER;
-    }
-
-    private void cleanupFolderCreation(DragObject d) {
-        if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) {
-            mDragFolderRingAnimator.animateToNaturalState();
-        }
-        if (mLastDragOverView != null && mLastDragOverView instanceof FolderIcon) {
-            if (d != null) {
-                ((FolderIcon) mLastDragOverView).onDragExit(d.dragInfo);
-            }
-        }
-        mFolderCreationAlarm.cancelAlarm();
-    }
-
-    private void cancelFolderCreation() {
-        if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) {
-            mDragFolderRingAnimator.animateToNaturalState();
-        }
-        mCreateUserFolderOnDrop = false;
-        mFolderCreationAlarm.cancelAlarm();
+        return;
     }
 
     class FolderCreationAlarmListener implements OnAlarmListener {
@@ -2896,8 +2943,7 @@
             mDragFolderRingAnimator.animateToAcceptState();
             layout.showFolderAccept(mDragFolderRingAnimator);
             layout.clearDragOutlines();
-            mCreateUserFolderOnDrop = true;
-            mDragMode = DRAG_MODE_CREATE_FOLDER;
+            setDragMode(DRAG_MODE_CREATE_FOLDER);
         }
     }
 
@@ -2931,12 +2977,10 @@
 
             if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
                 mDragTargetLayout.revertTempState();
+            } else {
+                setDragMode(DRAG_MODE_REORDER);
             }
 
-            if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
-            }
-            mDragMode = DRAG_MODE_REORDER;
-
             boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
             mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
                 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
@@ -3013,9 +3057,9 @@
                         cellLayout, mTargetCell);
                 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
                         mDragViewVisualCenter[1], mTargetCell);
-                if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell,
+                if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
                         distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
-                                mDragTargetLayout, mTargetCell, distance)) {
+                                cellLayout, mTargetCell, distance)) {
                     findNearestVacantCell = false;
                 }
             }
@@ -3029,7 +3073,7 @@
                     minSpanY = item.minSpanY;
                 }
                 int[] resultSpan = new int[2];
-                mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+                mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
                         (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
                         null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
                 item.spanX = resultSpan[0];
@@ -3057,7 +3101,6 @@
                         throw new IllegalStateException("Unknown item type: " +
                                 pendingInfo.itemType);
                     }
-                    cellLayout.onDragExit();
                 }
             };
             View finalView = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
@@ -3111,7 +3154,7 @@
 
             if (touchXY != null) {
                 // when dragging and dropping, just find the closest free spot
-                mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
+                mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
                         (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
                         null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
             } else {
@@ -3326,10 +3369,6 @@
                 }
             }
         } else if (mDragInfo != null) {
-            // NOTE: When 'success' is true, onDragExit is called by the DragController before
-            // calling onDropCompleted(). We call it ourselves here, but maybe this should be
-            // moved into DragController.cancelDrag().
-            doDragExit(null);
             CellLayout cellLayout;
             if (mLauncher.isHotseatLayout(target)) {
                 cellLayout = mLauncher.getHotseat().getLayout();
@@ -3435,17 +3474,13 @@
 
             final int page = (mNextPage != INVALID_PAGE ? mNextPage : mCurrentPage) +
                        (direction == DragController.SCROLL_LEFT ? -1 : 1);
-            cancelFolderCreation();
+
+            // We always want to exit the current layout to ensure parity of enter / exit
+            setCurrentDropLayout(null);
 
             if (0 <= page && page < getChildCount()) {
                 CellLayout layout = (CellLayout) getChildAt(page);
-                // Exit the current layout and mark the overlapping layout
-                if (mDragTargetLayout != null) {
-                    mDragTargetLayout.setIsDragOverlapping(false);
-                    mDragTargetLayout.onDragExit();
-                }
-                mDragTargetLayout = layout;
-                mDragTargetLayout.setIsDragOverlapping(true);
+                setCurrentDragOverlappingLayout(layout);
 
                 // Workspace is responsible for drawing the edge glow on adjacent pages,
                 // so we need to redraw the workspace when this may have changed.
@@ -3460,17 +3495,11 @@
     public boolean onExitScrollArea() {
         boolean result = false;
         if (mInScrollArea) {
-            if (mDragTargetLayout != null) {
-                mDragTargetLayout.setIsDragOverlapping(false);
-                // Workspace is responsible for drawing the edge glow on adjacent pages,
-                // so we need to redraw the workspace when this may have changed.
-                invalidate();
-            }
-            if (mDragTargetLayout != null && mDragHasEnteredWorkspace) {
-                // Unmark the overlapping layout and re-enter the current layout
-                mDragTargetLayout = getCurrentDropLayout();
-                mDragTargetLayout.onDragEnter();
-            }
+            invalidate();
+            CellLayout layout = getCurrentDropLayout();
+            setCurrentDropLayout(layout);
+            setCurrentDragOverlappingLayout(layout);
+
             result = true;
             mInScrollArea = false;
         }
@@ -3478,14 +3507,7 @@
     }
 
     private void onResetScrollArea() {
-        if (mDragTargetLayout != null) {
-            // Unmark the overlapping layout
-            mDragTargetLayout.setIsDragOverlapping(false);
-
-            // Workspace is responsible for drawing the edge glow on adjacent pages,
-            // so we need to redraw the workspace when this may have changed.
-            invalidate();
-        }
+        setCurrentDragOverlappingLayout(null);
         mInScrollArea = false;
     }