Merge "Ensuring that ShortcutInfo always has an icon" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index 28beff2..713d082 100644
--- a/Android.mk
+++ b/Android.mk
@@ -41,6 +41,8 @@
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
 LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
+
 LOCAL_AAPT_FLAGS := \
     --auto-add-overlay \
     --extra-packages android.support.v7.recyclerview \
@@ -66,6 +68,7 @@
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
 LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
 
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := launcher_proto_lib
diff --git a/build.gradle b/build.gradle
index 4629caa..982ae2e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -85,6 +85,7 @@
                     remove java
                     javanano {
                         option "java_package=launcher_log.proto|com.android.launcher3.userevent.nano"
+                        option "enum_style=java"
                     }
                 }
             }
diff --git a/proguard.flags b/proguard.flags
index f1a3eaf..6cbab08 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -91,3 +91,6 @@
 #}
 -keep class android.support.v7.widget.RecyclerView { *; }
 
+-keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** {
+  *;
+}
diff --git a/res/drawable-hdpi/page_hover_left.9.png b/res/drawable-hdpi/page_hover_left.9.png
deleted file mode 100644
index 3f11d0b..0000000
--- a/res/drawable-hdpi/page_hover_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left_active.9.png b/res/drawable-hdpi/page_hover_left_active.9.png
deleted file mode 100644
index abe4c31..0000000
--- a/res/drawable-hdpi/page_hover_left_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right.9.png b/res/drawable-hdpi/page_hover_right.9.png
deleted file mode 100644
index 3bcf191..0000000
--- a/res/drawable-hdpi/page_hover_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right_active.9.png b/res/drawable-hdpi/page_hover_right_active.9.png
deleted file mode 100644
index 101e4dc..0000000
--- a/res/drawable-hdpi/page_hover_right_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/screenpanel.9.png b/res/drawable-hdpi/screenpanel.9.png
deleted file mode 100644
index 5bccd33..0000000
--- a/res/drawable-hdpi/screenpanel.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/screenpanel_hover.9.png b/res/drawable-hdpi/screenpanel_hover.9.png
deleted file mode 100644
index f6b8c62..0000000
--- a/res/drawable-hdpi/screenpanel_hover.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left.9.png b/res/drawable-mdpi/page_hover_left.9.png
deleted file mode 100644
index 2b6094c..0000000
--- a/res/drawable-mdpi/page_hover_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left_active.9.png b/res/drawable-mdpi/page_hover_left_active.9.png
deleted file mode 100644
index 9eb00a2..0000000
--- a/res/drawable-mdpi/page_hover_left_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right.9.png b/res/drawable-mdpi/page_hover_right.9.png
deleted file mode 100644
index c2e59835..0000000
--- a/res/drawable-mdpi/page_hover_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right_active.9.png b/res/drawable-mdpi/page_hover_right_active.9.png
deleted file mode 100644
index d2771a1..0000000
--- a/res/drawable-mdpi/page_hover_right_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/screenpanel.9.png b/res/drawable-mdpi/screenpanel.9.png
deleted file mode 100644
index 9603c12..0000000
--- a/res/drawable-mdpi/screenpanel.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/screenpanel_hover.9.png b/res/drawable-mdpi/screenpanel_hover.9.png
deleted file mode 100644
index 7f28ce0..0000000
--- a/res/drawable-mdpi/screenpanel_hover.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left.9.png b/res/drawable-xhdpi/page_hover_left.9.png
deleted file mode 100644
index dbcc0ab..0000000
--- a/res/drawable-xhdpi/page_hover_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left_active.9.png b/res/drawable-xhdpi/page_hover_left_active.9.png
deleted file mode 100644
index 3233efe..0000000
--- a/res/drawable-xhdpi/page_hover_left_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right.9.png b/res/drawable-xhdpi/page_hover_right.9.png
deleted file mode 100644
index d82f809..0000000
--- a/res/drawable-xhdpi/page_hover_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right_active.9.png b/res/drawable-xhdpi/page_hover_right_active.9.png
deleted file mode 100644
index 819ea19..0000000
--- a/res/drawable-xhdpi/page_hover_right_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/screenpanel.9.png b/res/drawable-xhdpi/screenpanel.9.png
deleted file mode 100644
index 75343f7..0000000
--- a/res/drawable-xhdpi/screenpanel.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/screenpanel_hover.9.png b/res/drawable-xhdpi/screenpanel_hover.9.png
deleted file mode 100644
index 55b4d6e..0000000
--- a/res/drawable-xhdpi/screenpanel_hover.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left.9.png b/res/drawable-xxhdpi/page_hover_left.9.png
deleted file mode 100644
index c81f86c..0000000
--- a/res/drawable-xxhdpi/page_hover_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left_active.9.png b/res/drawable-xxhdpi/page_hover_left_active.9.png
deleted file mode 100644
index 858a3b2..0000000
--- a/res/drawable-xxhdpi/page_hover_left_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right.9.png b/res/drawable-xxhdpi/page_hover_right.9.png
deleted file mode 100644
index c529770..0000000
--- a/res/drawable-xxhdpi/page_hover_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right_active.9.png b/res/drawable-xxhdpi/page_hover_right_active.9.png
deleted file mode 100644
index 9900553..0000000
--- a/res/drawable-xxhdpi/page_hover_right_active.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/screenpanel.9.png b/res/drawable-xxhdpi/screenpanel.9.png
deleted file mode 100644
index b221b37..0000000
--- a/res/drawable-xxhdpi/screenpanel.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/screenpanel_hover.9.png b/res/drawable-xxhdpi/screenpanel_hover.9.png
deleted file mode 100644
index 418cf0a..0000000
--- a/res/drawable-xxhdpi/screenpanel_hover.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/bg_celllayout.xml b/res/drawable/bg_celllayout.xml
index d2219b3..b81b37f 100644
--- a/res/drawable/bg_celllayout.xml
+++ b/res/drawable/bg_celllayout.xml
@@ -18,20 +18,18 @@
 */
 -->
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <item>
-        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_active="true" >
+        <shape android:shape="rectangle" >
+            <stroke
+                android:width="@dimen/spring_loaded_panel_border"
+                android:color="@color/spring_loaded_highlighted_panel_border_color" />
             <solid android:color="@color/spring_loaded_panel_color" />
         </shape>
     </item>
     <item>
-        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
-            <stroke
-                android:width="@dimen/spring_loaded_panel_border"
-                android:color="@color/spring_loaded_highlighted_panel_border_color" />
-            <solid android:color="@android:color/transparent" />
+        <shape android:shape="rectangle" >
+            <solid android:color="@color/spring_loaded_panel_color" />
         </shape>
     </item>
-
-</transition>
\ No newline at end of file
+</selector>
diff --git a/res/drawable/bg_screenpanel.xml b/res/drawable/bg_screenpanel.xml
deleted file mode 100644
index 346fca0..0000000
--- a/res/drawable/bg_screenpanel.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- TODO(twickham): Remove this file and the screenpanel drawables -->
-<transition xmlns:android="http://schemas.android.com/apk/res/android" >
-
-    <item android:drawable="@drawable/screenpanel"/>
-    <item android:drawable="@drawable/screenpanel_hover"/>
-
-</transition>
\ No newline at end of file
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
deleted file mode 100644
index 514980f..0000000
--- a/res/values-sw720dp-land/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <!-- the area at the edge of the screen that makes the workspace go left
-         or right while you're dragging. -->
-    <dimen name="scroll_zone">100dip</dimen>
-</resources>
diff --git a/res/values-sw720dp-port/dimens.xml b/res/values-sw720dp-port/dimens.xml
deleted file mode 100644
index 6f594d5..0000000
--- a/res/values-sw720dp-port/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-<!-- Workspace -->
-    <!-- the area at the edge of the screen that makes the workspace go left
-         or right while you're dragging. -->
-    <dimen name="scroll_zone">40dp</dimen>
-</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 188f98f..a8a25af 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -109,14 +109,6 @@
     <!-- the distance an icon must be dragged before button drop targets accept it -->
     <dimen name="drag_distanceThreshold">30dp</dimen>
 
-    <!-- the area at the edge of the screen that makes the workspace go left
-         or right while you're dragging. -->
-    <dimen name="scroll_zone">20dp</dimen>
-
-    <!-- When dragging an item, how much bigger (fixed dps) the dragged view
-         should be. If 0, it will not be scaled at all. -->
-    <dimen name="dragViewScale">12dp</dimen>
-
     <!-- Elevation for the drag view. It should be larger than elevation of all other drag sources
          and drop targets like all-apps and folders -->
     <dimen name="drag_elevation">30dp</dimen>
diff --git a/src/com/android/launcher3/AnotherWindowDropTarget.java b/src/com/android/launcher3/AnotherWindowDropTarget.java
index 7074f78..052e5d0 100644
--- a/src/com/android/launcher3/AnotherWindowDropTarget.java
+++ b/src/com/android/launcher3/AnotherWindowDropTarget.java
@@ -48,9 +48,6 @@
     public void onDragExit(DragObject dragObject) {}
 
     @Override
-    public void onFlingToDelete(DragObject dragObject, PointF vec) {}
-
-    @Override
     public boolean acceptDrop(DragObject dragObject) {
         return dragObject.dragInfo instanceof ShortcutInfo;
     }
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index c45ff7b..54faca3 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -354,19 +354,18 @@
     }
 
     public void snapToWidget(boolean animate) {
-        final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
         DeviceProfile profile = mLauncher.getDeviceProfile();
-        int newWidth = (int) (mWidgetView.getWidth() * profile.appWidgetScale.x)
-                + 2 * mBackgroundPadding - mWidgetPadding.left - mWidgetPadding.right;
-        int newHeight = (int) (mWidgetView.getHeight() * profile.appWidgetScale.y)
-                + 2 * mBackgroundPadding - mWidgetPadding.top - mWidgetPadding.bottom;
+        float scale = Math.min(profile.appWidgetScale.x, profile.appWidgetScale.y);
 
-        mTmpPt[0] = mWidgetView.getLeft();
-        mTmpPt[1] = mWidgetView.getTop();
-        mDragLayer.getDescendantCoordRelativeToSelf(mCellLayout.getShortcutsAndWidgets(), mTmpPt);
+        mDragLayer.getViewRectRelativeToSelf(mWidgetView, sTmpRect);
 
-        int newX = mTmpPt[0] - mBackgroundPadding + mWidgetPadding.left;
-        int newY = mTmpPt[1] - mBackgroundPadding + mWidgetPadding.top;
+        int newWidth = 2 * mBackgroundPadding
+                + (int) (scale * (sTmpRect.width() - mWidgetPadding.left - mWidgetPadding.right));
+        int newHeight = 2 * mBackgroundPadding
+                + (int) (scale * (sTmpRect.height() - mWidgetPadding.top - mWidgetPadding.bottom));
+
+        int newX = (int) (sTmpRect.left - mBackgroundPadding + scale * mWidgetPadding.left);
+        int newY = (int) (sTmpRect.top - mBackgroundPadding + scale * mWidgetPadding.top);
 
         // We need to make sure the frame's touchable regions lie fully within the bounds of the
         // DragLayer. We allow the actual handles to be clipped, but we shift the touch regions
@@ -384,6 +383,7 @@
             mBottomTouchRegionAdjustment = 0;
         }
 
+        final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
         if (!animate) {
             lp.width = newWidth;
             lp.height = newHeight;
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 60a2cc3..e613b3b 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -28,7 +28,6 @@
 import android.content.res.TypedArray;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -119,9 +118,6 @@
     }
 
     @Override
-    public void onFlingToDelete(DragObject d, PointF vec) { }
-
-    @Override
     public final void onDragEnter(DragObject d) {
         d.dragView.setColor(mHoverColor);
         if (Utilities.ATLEAST_LOLLIPOP) {
@@ -243,10 +239,7 @@
         final Rect from = new Rect();
         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
 
-        int width = mDrawable.getIntrinsicWidth();
-        int height = mDrawable.getIntrinsicHeight();
-        final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
-                width, height);
+        final Rect to = getIconRect(d);
         final float scale = (float) to.width() / from.width();
         mDropTargetBar.deferOnDragEnd();
 
@@ -268,7 +261,7 @@
     @Override
     public void prepareAccessibilityDrop() { }
 
-    @Thunk abstract void completeDrop(DragObject d);
+    public abstract void completeDrop(DragObject d);
 
     @Override
     public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) {
@@ -280,7 +273,11 @@
         outRect.offsetTo(coords[0], coords[1]);
     }
 
-    protected Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) {
+    public Rect getIconRect(DragObject dragObject) {
+        int viewWidth = dragObject.dragView.getMeasuredWidth();
+        int viewHeight = dragObject.dragView.getMeasuredHeight();
+        int drawableWidth = mDrawable.getIntrinsicWidth();
+        int drawableHeight = mDrawable.getIntrinsicHeight();
         DragLayer dragLayer = mLauncher.getDragLayer();
 
         // Find the rect to animate to (the view is center aligned)
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index c0087c4..1e212bf 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -33,7 +33,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
 import android.os.Build;
 import android.os.Parcelable;
 import android.support.annotation.IntDef;
@@ -53,7 +52,6 @@
 import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.DragPreviewProvider;
@@ -112,9 +110,9 @@
 
     private float mBackgroundAlpha;
 
-    private static final int BACKGROUND_ACTIVATE_DURATION =
-            FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 120 : 0;
-    private final TransitionDrawable mBackground;
+    private static final int[] BACKGROUND_STATE_ACTIVE = new int[] { android.R.attr.state_active };
+    private static final int[] BACKGROUND_STATE_DEFAULT = new int[0];
+    private final Drawable mBackground;
 
     // These values allow a fixed measurement to be set on the CellLayout.
     private int mFixedWidth = -1;
@@ -227,9 +225,7 @@
         setAlwaysDrawnWithCacheEnabled(false);
         final Resources res = getResources();
 
-        mBackground = (TransitionDrawable) res.getDrawable(
-                FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? R.drawable.bg_screenpanel
-                        : R.drawable.bg_celllayout);
+        mBackground = res.getDrawable(R.drawable.bg_celllayout);
         mBackground.setCallback(this);
         mBackground.setAlpha((int) (mBackgroundAlpha * 255));
 
@@ -424,15 +420,8 @@
     void setIsDragOverlapping(boolean isDragOverlapping) {
         if (mIsDragOverlapping != isDragOverlapping) {
             mIsDragOverlapping = isDragOverlapping;
-            if (mIsDragOverlapping) {
-                mBackground.startTransition(BACKGROUND_ACTIVATE_DURATION);
-            } else {
-                if (mBackgroundAlpha > 0f) {
-                    mBackground.reverseTransition(BACKGROUND_ACTIVATE_DURATION);
-                } else {
-                    mBackground.resetTransition();
-                }
-            }
+            mBackground.setState(mIsDragOverlapping
+                    ? BACKGROUND_STATE_ACTIVE : BACKGROUND_STATE_DEFAULT);
             invalidate();
         }
     }
@@ -951,7 +940,7 @@
                 lp.tmpCellX = cellX;
                 lp.tmpCellY = cellY;
             }
-            clc.setupLp(lp);
+            clc.setupLp(child);
             lp.isLockedToGrid = false;
             final int newX = lp.x;
             final int newY = lp.y;
@@ -972,7 +961,7 @@
             va.addUpdateListener(new AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
-                    float r = ((Float) animation.getAnimatedValue()).floatValue();
+                    float r = (Float) animation.getAnimatedValue();
                     lp.x = (int) ((1 - r) * oldX + r * newX);
                     lp.y = (int) ((1 - r) * oldY + r * newY);
                     child.requestLayout();
@@ -1027,6 +1016,10 @@
 
             if (resize) {
                 cellToRect(cellX, cellY, spanX, spanY, r);
+                if (v instanceof LauncherAppWidgetHostView) {
+                    DeviceProfile profile = mLauncher.getDeviceProfile();
+                    Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
+                }
             } else {
                 // Find the top left corner of the rect the object will occupy
                 final int[] topLeft = mTmpPoint;
@@ -1836,7 +1829,7 @@
      * the provided point and the provided cell
      */
     private void computeDirectionVector(float deltaX, float deltaY, int[] result) {
-        double angle = Math.atan(((float) deltaY) / deltaX);
+        double angle = Math.atan(deltaY / deltaX);
 
         result[0] = 0;
         result[1] = 0;
@@ -2051,7 +2044,7 @@
             va.addUpdateListener(new AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
-                    float r = ((Float) animation.getAnimatedValue()).floatValue();
+                    float r = (Float) animation.getAnimatedValue();
                     float r1 = (mode == MODE_HINT && repeating) ? 1.0f : r;
                     float x = r1 * finalDeltaX + (1 - r1) * initDeltaX;
                     float y = r1 * finalDeltaY + (1 - r1) * initDeltaY;
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 705f841..9097ed2 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -16,19 +16,13 @@
 
 package com.android.launcher3;
 
-import android.animation.TimeInterpolator;
 import android.content.Context;
-import android.graphics.PointF;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.animation.AnimationUtils;
 
-import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
-import com.android.launcher3.util.FlingAnimation;
-import com.android.launcher3.util.Thunk;
 
 public class DeleteDropTarget extends ButtonDropTarget {
 
@@ -78,7 +72,7 @@
     }
 
     @Override
-    @Thunk void completeDrop(DragObject d) {
+    public void completeDrop(DragObject d) {
         ItemInfo item = d.dragInfo;
         if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) {
             removeWorkspaceOrFolderItem(mLauncher, item, null);
@@ -96,53 +90,4 @@
         launcher.getWorkspace().stripEmptyScreens();
         launcher.getDragLayer().announceForAccessibility(launcher.getString(R.string.item_removed));
     }
-
-    @Override
-    public void onFlingToDelete(final DragObject d, PointF vel) {
-        // Don't highlight the icon as it's animating
-        d.dragView.setColor(0);
-
-        final DragLayer dragLayer = mLauncher.getDragLayer();
-        FlingAnimation fling = new FlingAnimation(d, vel,
-                getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
-                        mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()),
-                        dragLayer);
-
-        final int duration = fling.getDuration();
-        final long startTime = AnimationUtils.currentAnimationTimeMillis();
-
-        // NOTE: Because it takes time for the first frame of animation to actually be
-        // called and we expect the animation to be a continuation of the fling, we have
-        // to account for the time that has elapsed since the fling finished.  And since
-        // we don't have a startDelay, we will always get call to update when we call
-        // start() (which we want to ignore).
-        final TimeInterpolator tInterpolator = new TimeInterpolator() {
-            private int mCount = -1;
-            private float mOffset = 0f;
-
-            @Override
-            public float getInterpolation(float t) {
-                if (mCount < 0) {
-                    mCount++;
-                } else if (mCount == 0) {
-                    mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
-                            startTime) / duration);
-                    mCount++;
-                }
-                return Math.min(1f, mOffset + t);
-            }
-        };
-
-        Runnable onAnimationEndRunnable = new Runnable() {
-            @Override
-            public void run() {
-                mLauncher.exitSpringLoadedDragMode();
-                completeDrop(d);
-                mLauncher.getDragController().onDeferredEndFling(d);
-            }
-        };
-
-        dragLayer.animateView(d.dragView, fling, duration, tInterpolator, onAnimationEndRunnable,
-                DragLayer.ANIMATION_END_DISAPPEAR, null);
-    }
 }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index fbff4eb..27afdc0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -75,7 +75,6 @@
     public final Rect defaultWidgetPadding;
     private final int defaultPageSpacingPx;
     private final int topWorkspacePadding;
-    private float dragViewScale;
     public float workspaceSpringLoadShrinkFactor;
     public final int workspaceSpringLoadedBottomSpace;
 
@@ -284,9 +283,6 @@
         cellWidthPx = iconSizePx;
         cellHeightPx = iconSizePx + iconDrawablePaddingPx
                 + Utilities.calculateTextHeight(iconTextSizePx);
-        final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f
-                : res.getDimensionPixelSize(R.dimen.dragViewScale);
-        dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
 
         // Hotseat
         hotseatCellWidthPx = iconSizePx;
@@ -315,19 +311,28 @@
                 + res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom)
                 + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size));
 
-        updateFolderCellSize(1f, dm, res, folderBottomPanelSize);
+        updateFolderCellSize(1f, dm, res);
 
-        // Check to see if the icons fit within the available height.  If not, then scale down.
-        float usedHeight = (folderCellHeightPx * inv.numFolderRows) + folderBottomPanelSize;
-        int maxHeight = availableHeightPx - getTotalWorkspacePadding().y - (2 * edgeMarginPx);
-        if (usedHeight > maxHeight) {
-            float scale = maxHeight / usedHeight;
-            updateFolderCellSize(scale, dm, res, folderBottomPanelSize);
+        // Don't let the folder get too close to the edges of the screen.
+        int folderMargin = 4 * edgeMarginPx;
+
+        // Check if the icons fit within the available height.
+        float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize;
+        int maxHeight = availableHeightPx - getTotalWorkspacePadding().y - folderMargin;
+        float scaleY = maxHeight / usedHeight;
+
+        // Check if the icons fit within the available width.
+        float usedWidth = folderCellWidthPx * inv.numFolderColumns;
+        int maxWidth = availableWidthPx - getTotalWorkspacePadding().x - folderMargin;
+        float scaleX = maxWidth / usedWidth;
+
+        float scale = Math.min(scaleX, scaleY);
+        if (scale < 1f) {
+            updateFolderCellSize(scale, dm, res);
         }
     }
 
-    private void updateFolderCellSize(float scale, DisplayMetrics dm, Resources res,
-             int folderBottomPanelSize) {
+    private void updateFolderCellSize(float scale, DisplayMetrics dm, Resources res) {
         folderChildIconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
         folderChildTextSizePx =
                 (int) (res.getDimensionPixelSize(R.dimen.folder_child_text_size) * scale);
@@ -336,11 +341,8 @@
         int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding) * scale);
         int cellPaddingY = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_y_padding) * scale);
 
-        // Don't let the folder get too close to the edges of the screen.
-        folderCellWidthPx = Math.min(folderChildIconSizePx + 2 * cellPaddingX,
-                (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
-        folderCellHeightPx = Math.min(folderChildIconSizePx + 2 * cellPaddingY + textHeight,
-                (availableHeightPx - 4 * edgeMarginPx - folderBottomPanelSize) / inv.numFolderRows);
+        folderCellWidthPx = folderChildIconSizePx + 2 * cellPaddingX;
+        folderCellHeightPx = folderChildIconSizePx + 2 * cellPaddingY + textHeight;
         folderChildDrawablePaddingPx = Math.max(0,
                 (folderCellHeightPx - folderChildIconSizePx - textHeight) / 3);
     }
@@ -411,12 +413,11 @@
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
-                float gapScale = 1f + (dragViewScale - 1f) / 2f;
                 int width = getCurrentWidth();
                 int height = getCurrentHeight();
                 // The amount of screen space available for left/right padding.
-                int availablePaddingX = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) +
-                        ((inv.numColumns - 1) * gapScale * cellWidthPx)));
+                int availablePaddingX = Math.max(0, width - ((inv.numColumns * cellWidthPx) +
+                        ((inv.numColumns - 1) * cellWidthPx)));
                 availablePaddingX = (int) Math.min(availablePaddingX,
                             width * MAX_HORIZONTAL_PADDING_PERCENT);
                 int availablePaddingY = Math.max(0, height - topWorkspacePadding - paddingBottom
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index 2fb495f..dcd8f58 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -27,11 +27,6 @@
 public interface DragSource extends LogContainerProvider {
 
     /**
-     * @return whether items dragged from this source supports
-     */
-    boolean supportsFlingToDelete();
-
-    /**
      * @return whether items dragged from this source supports 'App Info'
      */
     boolean supportsAppInfoDropTarget();
@@ -48,13 +43,6 @@
     float getIntrinsicIconScaleFactor();
 
     /**
-     * A callback specifically made back to the source after an item from this source has been flung
-     * to be deleted on a DropTarget.  In such a situation, this method will be called after
-     * onDropCompleted, and more importantly, after the fling animation has completed.
-     */
-    void onFlingToDeleteCompleted();
-
-    /**
      * A callback made back to the source after an item from this source has been dropped on a
      * DropTarget.
      */
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index e91fc15..7d047d7 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -16,12 +16,10 @@
 
 package com.android.launcher3;
 
-import com.android.launcher3.dragndrop.DragView;
-
-import android.graphics.PointF;
 import android.graphics.Rect;
 
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragView;
 
 /**
  * Interface defining an object that can receive a drag.
@@ -117,13 +115,6 @@
     void onDragExit(DragObject dragObject);
 
     /**
-     * Handle an object being dropped as a result of flinging to delete and will be called in place
-     * of onDrop().  (This is only called on objects that are set as the DragController's
-     * fling-to-delete target.
-     */
-    void onFlingToDelete(DragObject dragObject, PointF vec);
-
-    /**
      * Check if a drop action can occur at, or near, the requested location.
      * This will be called just before onDrop.
      * @return True if the drop will be accepted, false otherwise.
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 42bab47..0840b70 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -21,6 +21,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewDebug;
+import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
@@ -56,11 +57,6 @@
 
     private ViewPropertyAnimator mCurrentAnimation;
 
-    // Drop targets
-    private ButtonDropTarget mDeleteDropTarget;
-    private ButtonDropTarget mAppInfoDropTarget;
-    private ButtonDropTarget mUninstallDropTarget;
-
     public DropTargetBar(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -73,30 +69,27 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        // Get the individual components
-        mDeleteDropTarget = (ButtonDropTarget) findViewById(R.id.delete_target_text);
-        mAppInfoDropTarget = (ButtonDropTarget) findViewById(R.id.info_target_text);
-        mUninstallDropTarget = (ButtonDropTarget) findViewById(R.id.uninstall_target_text);
-
-        mDeleteDropTarget.setDropTargetBar(this);
-        mAppInfoDropTarget.setDropTargetBar(this);
-        mUninstallDropTarget.setDropTargetBar(this);
-
         // Initialize with hidden state
         setAlpha(0f);
     }
 
     public void setup(DragController dragController) {
         dragController.addDragListener(this);
-        dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
+        setupButtonDropTarget(this, dragController);
+    }
 
-        dragController.addDragListener(mDeleteDropTarget);
-        dragController.addDragListener(mAppInfoDropTarget);
-        dragController.addDragListener(mUninstallDropTarget);
-
-        dragController.addDropTarget(mDeleteDropTarget);
-        dragController.addDropTarget(mAppInfoDropTarget);
-        dragController.addDropTarget(mUninstallDropTarget);
+    private void setupButtonDropTarget(View view, DragController dragController) {
+        if (view instanceof ButtonDropTarget) {
+            ButtonDropTarget bdt = (ButtonDropTarget) view;
+            bdt.setDropTargetBar(this);
+            dragController.addDragListener(bdt);
+            dragController.addDropTarget(bdt);
+        } else if (view instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) view;
+            for (int i = vg.getChildCount() - 1; i >= 0; i--) {
+                setupButtonDropTarget(vg.getChildAt(i), dragController);
+            }
+        }
     }
 
     private void animateToVisibility(boolean isVisible) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 3648fb7..4a8a272 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -38,7 +38,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dynamicui.ExtractedColors;
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
 public class Hotseat extends FrameLayout
@@ -175,7 +175,7 @@
     public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
         target.gridX = info.cellX;
         target.gridY = info.cellY;
-        targetParent.containerType = LauncherLogProto.HOTSEAT;
+        targetParent.containerType = ContainerType.HOTSEAT;
     }
 
     public void updateColor(ExtractedColors extractedColors, boolean animate) {
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 398c9d2..6a6d285 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -49,7 +49,7 @@
     }
 
     @Override
-    void completeDrop(DragObject d) {
+    public void completeDrop(DragObject d) {
         DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
                 ? (DropTargetResultCallback) d.dragSource : null;
         startDetailsActivityForInfo(d.dragInfo, mLauncher, callback);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 35d34a3..85e6a77 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -111,7 +111,9 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.DeepShortcutsContainer;
 import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.util.ActivityResultInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -1332,8 +1334,6 @@
         }
 
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
-        mDragController.setDragScroller(mWorkspace);
-        mDragController.setScrollView(mDragLayer);
         mDragController.setMoveTarget(mWorkspace);
         mDragController.addDropTarget(mWorkspace);
         mDropTargetBar.setup(mDragController);
@@ -1352,7 +1352,7 @@
 
         // Bind wallpaper button actions
         View wallpaperButton = findViewById(R.id.wallpaper_button);
-        new OverviewButtonClickListener(LauncherLogProto.WALLPAPER_BUTTON) {
+        new OverviewButtonClickListener(ControlType.WALLPAPER_BUTTON) {
             @Override
             public void handleViewClick(View view) {
                 onClickWallpaperPicker(view);
@@ -1362,7 +1362,7 @@
 
         // Bind widget button actions
         mWidgetsButton = findViewById(R.id.widget_button);
-        new OverviewButtonClickListener(LauncherLogProto.WIDGETS_BUTTON) {
+        new OverviewButtonClickListener(ControlType.WIDGETS_BUTTON) {
             @Override
             public void handleViewClick(View view) {
                 onClickAddWidgetButton(view);
@@ -1374,7 +1374,7 @@
         View settingsButton = findViewById(R.id.settings_button);
         boolean hasSettings = hasSettings();
         if (hasSettings) {
-            new OverviewButtonClickListener(LauncherLogProto.SETTINGS_BUTTON) {
+            new OverviewButtonClickListener(ControlType.SETTINGS_BUTTON) {
                 @Override
                 public void handleViewClick(View view) {
                     onClickSettingsButton(view);
@@ -1696,13 +1696,13 @@
 
             AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(this);
             if (topOpenView instanceof DeepShortcutsContainer) {
-                ued.logActionCommand(LauncherLogProto.Action.HOME_INTENT,
-                        topOpenView.getExtendedTouchView(), LauncherLogProto.DEEPSHORTCUTS);
+                ued.logActionCommand(Action.Command.HOME_INTENT,
+                        topOpenView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
             } else if (topOpenView instanceof Folder) {
-                ued.logActionCommand(LauncherLogProto.Action.HOME_INTENT,
-                            ((Folder) topOpenView).getFolderIcon(), LauncherLogProto.FOLDER);
+                ued.logActionCommand(Action.Command.HOME_INTENT,
+                            ((Folder) topOpenView).getFolderIcon(), ContainerType.FOLDER);
             } else if (alreadyOnHome) {
-                ued.logActionCommand(LauncherLogProto.Action.HOME_INTENT,
+                ued.logActionCommand(Action.Command.HOME_INTENT,
                         mWorkspace.getState().containerType, mWorkspace.getCurrentPage());
             }
 
@@ -2217,22 +2217,22 @@
                 topView.getActiveTextView().dispatchBackKey();
             } else {
                 if (topView instanceof DeepShortcutsContainer) {
-                    ued.logActionCommand(LauncherLogProto.Action.BACK,
-                            topView.getExtendedTouchView(), LauncherLogProto.DEEPSHORTCUTS);
+                    ued.logActionCommand(Action.Command.BACK,
+                            topView.getExtendedTouchView(), ContainerType.DEEPSHORTCUTS);
                 } else if (topView instanceof Folder) {
-                    ued.logActionCommand(LauncherLogProto.Action.BACK,
-                            ((Folder) topView).getFolderIcon(), LauncherLogProto.FOLDER);
+                    ued.logActionCommand(Action.Command.BACK,
+                            ((Folder) topView).getFolderIcon(), ContainerType.FOLDER);
                 }
                 topView.close(true);
             }
         } else if (isAppsViewVisible()) {
-            ued.logActionCommand(LauncherLogProto.Action.BACK, LauncherLogProto.ALLAPPS);
+            ued.logActionCommand(Action.Command.BACK, ContainerType.ALLAPPS);
             showWorkspace(true);
         } else if (isWidgetsViewVisible())  {
-            ued.logActionCommand(LauncherLogProto.Action.BACK, LauncherLogProto.WIDGETS);
+            ued.logActionCommand(Action.Command.BACK, ContainerType.WIDGETS);
             showOverviewMode(true);
         } else if (mWorkspace.isInOverviewMode()) {
-            ued.logActionCommand(LauncherLogProto.Action.BACK, LauncherLogProto.OVERVIEW);
+            ued.logActionCommand(Action.Command.BACK, ContainerType.OVERVIEW);
             showWorkspace(true);
         } else {
             // TODO: Log this case.
@@ -2366,8 +2366,8 @@
     protected void onClickAllAppsButton(View v) {
         if (LOGD) Log.d(TAG, "onClickAllAppsButton");
         if (!isAppsViewVisible()) {
-            getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.TAP,
-                    LauncherLogProto.ALL_APPS_BUTTON);
+            getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+                    ControlType.ALL_APPS_BUTTON);
             showAppsView(true /* animated */, true /* updatePredictedApps */,
                     false /* focusSearchBar */);
         }
@@ -2376,8 +2376,8 @@
     protected void onLongClickAllAppsButton(View v) {
         if (LOGD) Log.d(TAG, "onLongClickAllAppsButton");
         if (!isAppsViewVisible()) {
-            getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.LONGPRESS,
-                    LauncherLogProto.ALL_APPS_BUTTON);
+            getUserEventDispatcher().logActionOnControl(Action.Touch.LONGPRESS,
+                    ControlType.ALL_APPS_BUTTON);
             showAppsView(true /* animated */,
                     true /* updatePredictedApps */, true /* focusSearchBar */);
         }
@@ -2760,8 +2760,8 @@
         if (v instanceof Workspace) {
             if (!mWorkspace.isInOverviewMode()) {
                 if (!mWorkspace.isTouchActive() && !ignoreLongPressToOverview) {
-                    getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.LONGPRESS,
-                            LauncherLogProto.Action.NONE, LauncherLogProto.WORKSPACE,
+                    getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+                            Action.Direction.NONE, ContainerType.WORKSPACE,
                             mWorkspace.getCurrentPage());
                     showOverviewMode(true);
                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
@@ -2791,14 +2791,14 @@
                 // User long pressed on empty space
                 if (mWorkspace.isInOverviewMode()) {
                     mWorkspace.startReordering(v);
-                    getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.LONGPRESS,
-                            LauncherLogProto.Action.NONE, LauncherLogProto.OVERVIEW);
+                    getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+                            Action.Direction.NONE, ContainerType.OVERVIEW);
                 } else {
                     if (ignoreLongPressToOverview) {
                         return false;
                     }
-                    getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.LONGPRESS,
-                            LauncherLogProto.Action.NONE, LauncherLogProto.WORKSPACE,
+                    getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+                            Action.Direction.NONE, ContainerType.WORKSPACE,
                             mWorkspace.getCurrentPage());
                     showOverviewMode(true);
                 }
@@ -3023,16 +3023,7 @@
         mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
                 Workspace.State.SPRING_LOADED, true /* animated */,
                 null /* onCompleteRunnable */);
-
-        if (isAppsViewVisible()) {
-            mState = State.APPS_SPRING_LOADED;
-        } else if (isWidgetsViewVisible()) {
-            mState = State.WIDGETS_SPRING_LOADED;
-        } else if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
-            mState = State.WORKSPACE_SPRING_LOADED;
-        } else {
-            mState = State.WORKSPACE;
-        }
+        mState = State.WORKSPACE_SPRING_LOADED;
     }
 
     public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
@@ -3067,7 +3058,7 @@
                 || mState == State.WIDGETS_SPRING_LOADED;
     }
 
-    void exitSpringLoadedDragMode() {
+    public void exitSpringLoadedDragMode() {
         if (mState == State.APPS_SPRING_LOADED) {
             showAppsView(true /* animated */,
                     false /* updatePredictedApps */, false /* focusSearchBar */);
diff --git a/src/com/android/launcher3/OverviewButtonClickListener.java b/src/com/android/launcher3/OverviewButtonClickListener.java
index c98f1d7..dd670d2 100644
--- a/src/com/android/launcher3/OverviewButtonClickListener.java
+++ b/src/com/android/launcher3/OverviewButtonClickListener.java
@@ -2,7 +2,7 @@
 
 import android.view.View;
 
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 
 /**
  * A specialized listener for Overview buttons where both clicks and long clicks are logged
@@ -11,7 +11,7 @@
 public abstract class OverviewButtonClickListener implements View.OnClickListener,
         View.OnLongClickListener {
 
-    private int mControlType; /** ControlType enum as defined in {@link LauncherLogProto} */
+    private int mControlType; /** ControlType enum as defined in {@link Action.Touch} */
 
     public OverviewButtonClickListener(int controlType) {
         mControlType = controlType;
@@ -25,14 +25,14 @@
     @Override
     public void onClick(View view) {
         if (shouldPerformClick(view)) {
-            handleViewClick(view, LauncherLogProto.Action.TAP);
+            handleViewClick(view, Action.Touch.TAP);
         }
     }
 
     @Override
     public boolean onLongClick(View view) {
         if (shouldPerformClick(view)) {
-            handleViewClick(view, LauncherLogProto.Action.LONGPRESS);
+            handleViewClick(view, Action.Touch.LONGPRESS);
         }
         return true;
     }
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
index 41074be..bae246e 100644
--- a/src/com/android/launcher3/PinchAnimationManager.java
+++ b/src/com/android/launcher3/PinchAnimationManager.java
@@ -24,7 +24,8 @@
 import android.view.View;
 import android.view.animation.LinearInterpolator;
 
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 import static com.android.launcher3.Workspace.State.NORMAL;
 import static com.android.launcher3.Workspace.State.OVERVIEW;
@@ -164,14 +165,14 @@
             // Passing threshold 3 ends the pinch and snaps to the new state.
             if (startState == OVERVIEW && goingTowards == NORMAL) {
                 mLauncher.getUserEventDispatcher().logActionOnContainer(
-                        LauncherLogProto.Action.PINCH, LauncherLogProto.Action.NONE,
-                        LauncherLogProto.OVERVIEW, mWorkspace.getCurrentPage());
+                        Action.Touch.PINCH, Action.Direction.NONE,
+                        ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
                 mLauncher.showWorkspace(true);
                 mWorkspace.snapToPage(mWorkspace.getCurrentPage());
             } else if (startState == NORMAL && goingTowards == OVERVIEW) {
                 mLauncher.getUserEventDispatcher().logActionOnContainer(
-                        LauncherLogProto.Action.PINCH, LauncherLogProto.Action.NONE,
-                        LauncherLogProto.WORKSPACE, mWorkspace.getCurrentPage());
+                        Action.Touch.PINCH, Action.Direction.NONE,
+                        ContainerType.WORKSPACE, mWorkspace.getCurrentPage());
                 mLauncher.showOverviewMode(true);
             }
         } else {
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 6c73762..eebce45 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -86,8 +86,15 @@
         }
     }
 
-    public void setupLp(CellLayout.LayoutParams lp) {
-        lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
+    public void setupLp(View child) {
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+        if (child instanceof LauncherAppWidgetHostView) {
+            DeviceProfile profile = mLauncher.getDeviceProfile();
+            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
+                    profile.appWidgetScale.x, profile.appWidgetScale.y);
+        } else {
+            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
+        }
     }
 
     // Set whether or not to invert the layout horizontally if the layout is in RTL mode.
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 7ea9aca..00c613a 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -95,7 +95,7 @@
     }
 
     @Override
-    void completeDrop(final DragObject d) {
+    public void completeDrop(final DragObject d) {
         DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
                 ? (DropTargetResultCallback) d.dragSource : null;
         startUninstallActivity(mLauncher, d.dragInfo, callback);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 197aaf7..3e44e33 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -264,6 +264,20 @@
         }
     }
 
+    public static float shrinkRect(Rect r, float scaleX, float scaleY) {
+        float scale = Math.min(Math.min(scaleX, scaleY), 1.0f);
+        if (scale < 1.0f) {
+            int deltaX = (int) (r.width() * (scaleX - scale) * 0.5f);
+            r.left += deltaX;
+            r.right -= deltaX;
+
+            int deltaY = (int) (r.height() * (scaleY - scale) * 0.5f);
+            r.top += deltaY;
+            r.bottom -= deltaY;
+        }
+        return scale;
+    }
+
     public static void startActivityForResultSafely(
             Activity activity, Intent intent, int requestCode) {
         try {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 13b3e28..ab98367 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -33,7 +33,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -68,14 +67,13 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.dragndrop.DragScroller;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.SpringLoadedDragController;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.shortcuts.DeepShortcutsContainer;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LongArrayMap;
@@ -95,7 +93,7 @@
  * interact with. A workspace is meant to be used with a fixed width only.
  */
 public class Workspace extends PagedView
-        implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
+        implements DropTarget, DragSource, View.OnTouchListener,
         DragController.DragListener, ViewGroup.OnHierarchyChangeListener,
         Insettable, DropTargetSource {
     private static final String TAG = "Launcher.Workspace";
@@ -189,11 +187,11 @@
     // in all apps or customize mode)
 
     public enum State {
-        NORMAL          (false, false, LauncherLogProto.WORKSPACE),
-        NORMAL_HIDDEN   (false, false, LauncherLogProto.ALLAPPS),
-        SPRING_LOADED   (false, true, LauncherLogProto.WORKSPACE),
-        OVERVIEW        (true, true, LauncherLogProto.OVERVIEW),
-        OVERVIEW_HIDDEN (true, false, LauncherLogProto.WIDGETS);
+        NORMAL          (false, false, ContainerType.WORKSPACE),
+        NORMAL_HIDDEN   (false, false, ContainerType.ALLAPPS),
+        SPRING_LOADED   (false, true, ContainerType.WORKSPACE),
+        OVERVIEW        (true, true, ContainerType.OVERVIEW),
+        OVERVIEW_HIDDEN (true, false, ContainerType.WIDGETS);
 
         public final boolean shouldUpdateWidget;
         public final boolean hasMultipleVisiblePages;
@@ -248,9 +246,6 @@
 
     private boolean mStripScreensOnPageStopMoving = false;
 
-    /** Is the user is dragging an item near the edge of a page? */
-    private boolean mInScrollArea = false;
-
     private DragPreviewProvider mOutlineProvider = null;
     public static final int DRAG_BITMAP_PADDING = DragPreviewProvider.DRAG_BITMAP_PADDING;
     private boolean mWorkspaceFadeInAdjacentScreens;
@@ -383,17 +378,36 @@
         mOnStateChangeListener = listener;
     }
 
-    // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
-    // dimension if unsuccessful
-    public int[] estimateItemSize(ItemInfo itemInfo, boolean springLoaded) {
+    /**
+     * Estimates the size of an item using spans: hSpan, vSpan.
+     *
+     * @param springLoaded True if we are in spring loaded mode.
+     * @param unscaledSize True if caller wants to return the unscaled size
+     * @return MAX_VALUE for each dimension if unsuccessful.
+     */
+    public int[] estimateItemSize(ItemInfo itemInfo, boolean springLoaded, boolean unscaledSize) {
         float shrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor;
         int[] size = new int[2];
         if (getChildCount() > 0) {
             // Use the first non-custom page to estimate the child position
             CellLayout cl = (CellLayout) getChildAt(numCustomPages());
+            boolean isWidget = itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+
             Rect r = estimateItemPosition(cl, 0, 0, itemInfo.spanX, itemInfo.spanY);
+
+            float scale = 1;
+            if (isWidget) {
+                DeviceProfile profile = mLauncher.getDeviceProfile();
+                scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
+            }
             size[0] = r.width();
             size[1] = r.height();
+
+            if (isWidget && unscaledSize) {
+                size[0] /= scale;
+                size[1] /= scale;
+            }
+
             if (springLoaded) {
                 size[0] *= shrinkFactor;
                 size[1] *= shrinkFactor;
@@ -464,10 +478,8 @@
             }
         }
 
-        if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
-            // Always enter the spring loaded mode
-            mLauncher.enterSpringLoadedDragMode();
-        }
+        // Always enter the spring loaded mode
+        mLauncher.enterSpringLoadedDragMode();
     }
 
     public void deferRemoveExtraEmptyScreen() {
@@ -2550,7 +2562,7 @@
 
                 // If the item being dropped is a shortcut and the nearest drop
                 // cell also contains a shortcut, then create a folder with the two shortcuts.
-                if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
+                if (createUserFolderIfNecessary(cell, container,
                         dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
                     return;
                 }
@@ -2765,10 +2777,6 @@
 
         mDropToLayout = null;
         setDropLayoutForDragObject(d);
-
-        if (!workspaceInModalState() && FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
-            mLauncher.getDragLayer().showPageHints();
-        }
     }
 
     @Override
@@ -2779,32 +2787,18 @@
 
         // Here we store the final page that will be dropped to, if the workspace in fact
         // receives the drop
-        if (mInScrollArea) {
-            if (isPageInTransition()) {
-                // If the user drops while the page is scrolling, we should use that page as the
-                // destination instead of the page that is being hovered over.
-                mDropToLayout = (CellLayout) getPageAt(getNextPage());
-            } else {
-                mDropToLayout = mDragOverlappingLayout;
-            }
-        } else {
-            mDropToLayout = mDragTargetLayout;
-        }
-
+        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();
+        // Reset the previous drag target
         setCurrentDropLayout(null);
         setCurrentDragOverlappingLayout(null);
 
         mSpringLoadedDragController.cancel();
-
-        mLauncher.getDragLayer().hidePageHints();
     }
 
     private void enforceDragParity(String event, int update, int expectedValue) {
@@ -3021,7 +3015,7 @@
 
     public void onDragOver(DragObject d) {
         // Skip drag over events while we are dragging over side pages
-        if (mInScrollArea || !transitionStateShouldAllowDrop()) return;
+        if (!transitionStateShouldAllowDrop()) return;
 
         ItemInfo item = d.dragInfo;
         if (item == null) {
@@ -3449,7 +3443,7 @@
     }
 
     public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
-        int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo, false);
+        int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo, false, true);
         int visibility = layout.getVisibility();
         layout.setVisibility(VISIBLE);
 
@@ -3475,6 +3469,10 @@
         int spanY = info.spanY;
 
         Rect r = estimateItemPosition(layout, targetCell[0], targetCell[1], spanX, spanY);
+        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
+            DeviceProfile profile = mLauncher.getDeviceProfile();
+            Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
+        }
         loc[0] = r.left;
         loc[1] = r.top;
 
@@ -3486,11 +3484,8 @@
         float dragViewScaleX = 1f;
         float dragViewScaleY = 1f;
         if (scale) {
-            float width = info.spanX * layout.mCellWidth;
-            float height = info.spanY * layout.mCellHeight;
-
-            dragViewScaleX = r.width() / width;
-            dragViewScaleY = r.height() / height;
+            dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
+            dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
         }
 
         // The animation will scale the dragView about its center, so we need to center about
@@ -3719,13 +3714,8 @@
     }
 
     @Override
-    public boolean supportsFlingToDelete() {
-        return true;
-    }
-
-    @Override
     public boolean supportsAppInfoDropTarget() {
-        return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND;
+        return true;
     }
 
     @Override
@@ -3733,16 +3723,6 @@
         return true;
     }
 
-    @Override
-    public void onFlingToDelete(DragObject d, PointF vec) {
-        // Do nothing
-    }
-
-    @Override
-    public void onFlingToDeleteCompleted() {
-        // Do nothing
-    }
-
     public boolean isDropEnabled() {
         return true;
     }
@@ -3798,65 +3778,6 @@
         }
     }
 
-    @Override
-    public boolean onEnterScrollArea(int x, int y, int direction) {
-        // Ignore the scroll area if we are dragging over the hot seat
-        boolean isPortrait = !mLauncher.getDeviceProfile().isLandscape;
-        if (mLauncher.getHotseat() != null && isPortrait) {
-            Rect r = new Rect();
-            mLauncher.getHotseat().getHitRect(r);
-            if (r.contains(x, y)) {
-                return false;
-            }
-        }
-
-        boolean result = false;
-        if (!workspaceInModalState() && !mIsSwitchingState && Folder.getOpen(mLauncher) == null) {
-            mInScrollArea = true;
-
-            final int page = getNextPage() +
-                       (direction == DragController.SCROLL_LEFT ? -1 : 1);
-            // We always want to exit the current layout to ensure parity of enter / exit
-            setCurrentDropLayout(null);
-
-            if (0 <= page && page < getChildCount()) {
-                // Ensure that we are not dragging over to the custom content screen
-                if (getScreenIdForPageIndex(page) == CUSTOM_CONTENT_SCREEN_ID) {
-                    return false;
-                }
-
-                CellLayout layout = (CellLayout) getChildAt(page);
-                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.
-                invalidate();
-                result = true;
-            }
-        }
-        return result;
-    }
-
-    @Override
-    public boolean onExitScrollArea() {
-        boolean result = false;
-        if (mInScrollArea) {
-            invalidate();
-            CellLayout layout = getCurrentDropLayout();
-            setCurrentDropLayout(layout);
-            setCurrentDragOverlappingLayout(layout);
-
-            result = true;
-            mInScrollArea = false;
-        }
-        return result;
-    }
-
-    private void onResetScrollArea() {
-        setCurrentDragOverlappingLayout(null);
-        mInScrollArea = false;
-    }
-
     /**
      * Returns a specific CellLayout
      */
@@ -4229,12 +4150,12 @@
         target.gridX = info.cellX;
         target.gridY = info.cellY;
         target.pageIndex = getCurrentPage();
-        targetParent.containerType = LauncherLogProto.WORKSPACE;
+        targetParent.containerType = ContainerType.WORKSPACE;
         if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
             target.rank = info.rank;
-            targetParent.containerType = LauncherLogProto.HOTSEAT;
+            targetParent.containerType = ContainerType.HOTSEAT;
         } else if (info.container >= 0) {
-            targetParent.containerType = LauncherLogProto.FOLDER;
+            targetParent.containerType = ContainerType.FOLDER;
         }
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index f98e027..97e0d98 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -51,7 +51,6 @@
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.graphics.TintedDrawableSpan;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
-import com.android.launcher3.shortcuts.DeepShortcutsContainer;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ComponentKey;
 
@@ -208,11 +207,6 @@
             return false;
         }
 
-        // IF a shortcuts container is open, container should not be pulled down.
-        if (DeepShortcutsContainer.getOpen(mLauncher) != null) {
-            return false;
-        }
-
         // IF scroller is at the very top OR there is no scroll bar because there is probably not
         // enough items to scroll, THEN it's okay for the container to be pulled down.
         if (mAppsRecyclerView.getScrollBar().getThumbOffsetY() <= 0) {
@@ -392,21 +386,10 @@
             }
         });
         mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
-        if (FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
-            // Enter spring loaded mode (the new workspace does this in
-            // onDragStart(), so we don't want to do it here)
-            mLauncher.enterSpringLoadedDragMode();
-        }
-
         return false;
     }
 
     @Override
-    public boolean supportsFlingToDelete() {
-        return true;
-    }
-
-    @Override
     public boolean supportsAppInfoDropTarget() {
         return true;
     }
@@ -423,14 +406,6 @@
     }
 
     @Override
-    public void onFlingToDeleteCompleted() {
-        // We just dismiss the drag when we fling, so cleanup here
-        mLauncher.exitSpringLoadedDragModeDelayed(true,
-                Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
-        mLauncher.unlockScreenOrientation(false);
-    }
-
-    @Override
     public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
             boolean success) {
         if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index ab34287..10d4c15 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -29,7 +29,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 import java.util.List;
 
@@ -184,7 +184,7 @@
 
     public int getContainerType(View v) {
         if (mApps.hasFilter()) {
-            return LauncherLogProto.SEARCHRESULT;
+            return ContainerType.SEARCHRESULT;
         } else {
             if (v instanceof BubbleTextView) {
                 BubbleTextView icon = (BubbleTextView) v;
@@ -193,11 +193,11 @@
                     List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
                     AlphabeticalAppsList.AdapterItem item = items.get(position);
                     if (item.viewType == AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON) {
-                        return LauncherLogProto.PREDICTION;
+                        return ContainerType.PREDICTION;
                     }
                 }
             }
-            return LauncherLogProto.ALLAPPS;
+            return ContainerType.ALLAPPS;
         }
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 42f1875..99cac41 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -17,6 +17,7 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
@@ -24,7 +25,10 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.TouchController;
 
 /**
@@ -118,6 +122,8 @@
                 mNoIntercept = true;
             } else if (!mLauncher.isAllAppsVisible() && !shouldPossiblyIntercept(ev)) {
                 mNoIntercept = true;
+            } else if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+                mNoIntercept = true;
             } else {
                 // Now figure out which direction scroll events the controller will start
                 // calling the callbacks.
@@ -221,9 +227,9 @@
 
                 if (!mLauncher.isAllAppsVisible()) {
                     mLauncher.getUserEventDispatcher().logActionOnContainer(
-                            LauncherLogProto.Action.FLING,
-                            LauncherLogProto.Action.UP,
-                            LauncherLogProto.HOTSEAT);
+                            Action.Touch.FLING,
+                            Action.Direction.UP,
+                            ContainerType.HOTSEAT);
                 }
                 mLauncher.showAppsView(true /* animated */,
                         false /* updatePredictedApps */,
@@ -241,9 +247,9 @@
                 calculateDuration(velocity, Math.abs(mAppsView.getTranslationY()));
                 if (!mLauncher.isAllAppsVisible()) {
                     mLauncher.getUserEventDispatcher().logActionOnContainer(
-                            LauncherLogProto.Action.SWIPE,
-                            LauncherLogProto.Action.UP,
-                            LauncherLogProto.HOTSEAT);
+                            Action.Touch.SWIPE,
+                            Action.Direction.UP,
+                            ContainerType.HOTSEAT);
                 }
                 mLauncher.showAppsView(true, /* animated */
                         false /* updatePredictedApps */,
diff --git a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
index e968f36..1623010 100644
--- a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
+++ b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
@@ -37,11 +37,6 @@
     }
 
     @Override
-    public boolean supportsFlingToDelete() {
-        return false;
-    }
-
-    @Override
     public boolean supportsAppInfoDropTarget() {
         return false;
     }
@@ -57,10 +52,6 @@
     }
 
     @Override
-    public void onFlingToDeleteCompleted() {
-    }
-
-    @Override
     public void onDropCompleted(View target, DragObject d,
             boolean isFlingToDelete, boolean success) {
         if (!success) {
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 67ef5fc..745776d 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -17,31 +17,24 @@
 package com.android.launcher3.dragndrop;
 
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
-import android.os.Handler;
 import android.os.IBinder;
 import android.view.DragEvent;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -54,29 +47,14 @@
  * Class for initiating a drag within a view or across multiple views.
  */
 public class DragController implements DragDriver.EventListener, TouchController {
-    private static final String TAG = "Launcher.DragController";
-
-    public static final int SCROLL_DELAY = 500;
-    public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150;
-
     private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
 
-    private static final int SCROLL_OUTSIDE_ZONE = 0;
-    private static final int SCROLL_WAITING_IN_ZONE = 1;
-
-    public static final int SCROLL_NONE = -1;
-    public static final int SCROLL_LEFT = 0;
-    public static final int SCROLL_RIGHT = 1;
-
-    private static final float MAX_FLING_DEGREES = 35f;
-
     @Thunk Launcher mLauncher;
-    private Handler mHandler;
+    private FlingToDeleteHelper mFlingToDeleteHelper;
 
     // temporaries to avoid gc thrash
     private Rect mRectTemp = new Rect();
     private final int[] mCoordinatesTemp = new int[2];
-    private final boolean mIsRtl;
 
     /**
      * Drag driver for the current drag/drop operation, or null if there is no active DND operation.
@@ -93,34 +71,19 @@
     /** Y coordinate of the down event. */
     private int mMotionDownY;
 
-    /** the area at the edge of the screen that makes the workspace go left
-     *   or right while you're dragging.
-     */
-    private final int mScrollZone;
-
     private DropTarget.DragObject mDragObject;
 
     /** Who can receive drop events */
-    private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();
-    private ArrayList<DragListener> mListeners = new ArrayList<DragListener>();
-    private DropTarget mFlingToDeleteDropTarget;
+    private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
+    private ArrayList<DragListener> mListeners = new ArrayList<>();
 
     /** The window token used as the parent for the DragView. */
     private IBinder mWindowToken;
 
-    /** The view that will be scrolled when dragging to the left and right edges of the screen. */
-    private View mScrollView;
-
     private View mMoveTarget;
 
-    @Thunk DragScroller mDragScroller;
-    @Thunk int mScrollState = SCROLL_OUTSIDE_ZONE;
-    private ScrollRunnable mScrollRunnable = new ScrollRunnable();
-
     private DropTarget mLastDropTarget;
 
-    private InputMethodManager mInputMethodManager;
-
     @Thunk int mLastTouch[] = new int[2];
     @Thunk long mLastTouchUpTime = -1;
     @Thunk int mDistanceSinceScroll = 0;
@@ -128,9 +91,6 @@
     private int mTmpPoint[] = new int[2];
     private Rect mDragLayerRect = new Rect();
 
-    protected final int mFlingToDeleteThresholdVelocity;
-    private VelocityTracker mVelocityTracker;
-
     private boolean mIsInPreDrag;
 
     /**
@@ -155,15 +115,8 @@
      * Used to create a new DragLayer from XML.
      */
     public DragController(Launcher launcher) {
-        Resources r = launcher.getResources();
         mLauncher = launcher;
-        mHandler = new Handler();
-        mScrollZone = r.getDimensionPixelSize(R.dimen.scroll_zone);
-        mVelocityTracker = VelocityTracker.obtain();
-
-        mFlingToDeleteThresholdVelocity =
-                r.getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity);
-        mIsRtl = Utilities.isRtl(r);
+        mFlingToDeleteHelper = new FlingToDeleteHelper(launcher);
     }
 
     /**
@@ -208,11 +161,8 @@
         }
 
         // Hide soft keyboard, if visible
-        if (mInputMethodManager == null) {
-            mInputMethodManager = (InputMethodManager)
-                    mLauncher.getSystemService(Context.INPUT_METHOD_SERVICE);
-        }
-        mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
+        mLauncher.getSystemService(InputMethodManager.class)
+                .hideSoftInputFromWindow(mWindowToken, 0);
 
         mOptions = options;
         if (mOptions.systemDndStartPoint != null) {
@@ -234,11 +184,8 @@
                 && !mOptions.preDragCondition.shouldStartDrag(0);
 
         final Resources res = mLauncher.getResources();
-        final float scaleDps = FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND
-                ? res.getDimensionPixelSize(R.dimen.dragViewScale)
-                : mIsInPreDrag
-                    ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale)
-                    : 0f;
+        final float scaleDps = mIsInPreDrag
+                ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
                 registrationY, initialDragViewScale, scaleDps);
 
@@ -351,7 +298,6 @@
     private void endDrag() {
         if (isDragging()) {
             mDragDriver = null;
-            clearScrollRunnable();
             boolean isDeferred = false;
             if (mDragObject.dragView != null) {
                 isDeferred = mDragObject.deferDragViewCleanupPostAnimation;
@@ -369,7 +315,7 @@
             }
         }
 
-        releaseVelocityTracker();
+        mFlingToDeleteHelper.releaseVelocityTracker();
     }
 
     public void animateDragViewToOriginalPosition(final Runnable onComplete,
@@ -411,10 +357,6 @@
         }
     }
 
-    public void onDeferredEndFling(DropTarget.DragObject d) {
-        d.dragSource.onFlingToDeleteCompleted();
-    }
-
     /**
      * Clamps the position to the drag layer bounds.
      */
@@ -453,22 +395,16 @@
     }
 
     @Override
-    public void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride) {
+    public void onDriverDragEnd(float x, float y) {
         DropTarget dropTarget;
-        PointF vec = null;
-
-        if (dropTargetOverride != null) {
-            dropTarget = dropTargetOverride;
+        Runnable flingAnimation = mFlingToDeleteHelper.getFlingAnimation(mDragObject);
+        if (flingAnimation != null) {
+            dropTarget = mFlingToDeleteHelper.getDropTarget();
         } else {
-            vec = isFlingingToDelete(mDragObject.dragSource);
-            if (vec != null) {
-                dropTarget = mFlingToDeleteDropTarget;
-            } else {
-                dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp);
-            }
+            dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp);
         }
 
-        drop(dropTarget, x, y, vec);
+        drop(dropTarget, flingAnimation);
 
         endDrag();
     }
@@ -487,7 +423,7 @@
         }
 
         // Update the velocity tracker
-        acquireVelocityTrackerAndAddMovement(ev);
+        mFlingToDeleteHelper.recordMotionEvent(ev);
 
         final int action = ev.getAction();
         final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
@@ -535,16 +471,6 @@
         return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction);
     }
 
-    private void clearScrollRunnable() {
-        mHandler.removeCallbacks(mScrollRunnable);
-        if (mScrollState == SCROLL_WAITING_IN_ZONE) {
-            mScrollState = SCROLL_OUTSIDE_ZONE;
-            mScrollRunnable.setDirection(SCROLL_RIGHT);
-            mDragScroller.onExitScrollArea();
-            mLauncher.getDragLayer().onExitScrollArea();
-        }
-    }
-
     private void handleMoveEvent(int x, int y) {
         mDragObject.dragView.move(x, y);
 
@@ -559,7 +485,6 @@
         mDistanceSinceScroll += Math.hypot(mLastTouch[0] - x, mLastTouch[1] - y);
         mLastTouch[0] = x;
         mLastTouch[1] = y;
-        checkScrollState(x, y);
 
         if (mIsInPreDrag && mOptions.preDragCondition != null
                 && mOptions.preDragCondition.shouldStartDrag(mDistanceSinceScroll)) {
@@ -596,36 +521,6 @@
         mLastDropTarget = dropTarget;
     }
 
-    @Thunk void checkScrollState(int x, int y) {
-        final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop();
-        final int delay = mDistanceSinceScroll < slop ? RESCROLL_DELAY : SCROLL_DELAY;
-        final DragLayer dragLayer = mLauncher.getDragLayer();
-        final int forwardDirection = mIsRtl ? SCROLL_RIGHT : SCROLL_LEFT;
-        final int backwardsDirection = mIsRtl ? SCROLL_LEFT : SCROLL_RIGHT;
-
-        if (x < mScrollZone) {
-            if (mScrollState == SCROLL_OUTSIDE_ZONE) {
-                mScrollState = SCROLL_WAITING_IN_ZONE;
-                if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) {
-                    dragLayer.onEnterScrollArea();
-                    mScrollRunnable.setDirection(forwardDirection);
-                    mHandler.postDelayed(mScrollRunnable, delay);
-                }
-            }
-        } else if (x > mScrollView.getWidth() - mScrollZone) {
-            if (mScrollState == SCROLL_OUTSIDE_ZONE) {
-                mScrollState = SCROLL_WAITING_IN_ZONE;
-                if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) {
-                    dragLayer.onEnterScrollArea();
-                    mScrollRunnable.setDirection(backwardsDirection);
-                    mHandler.postDelayed(mScrollRunnable, delay);
-                }
-            }
-        } else {
-            clearScrollRunnable();
-        }
-    }
-
     /**
      * Call this from a drag source view.
      */
@@ -635,7 +530,7 @@
         }
 
         // Update the velocity tracker
-        acquireVelocityTrackerAndAddMovement(ev);
+        mFlingToDeleteHelper.recordMotionEvent(ev);
 
         final int action = ev.getAction();
         final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
@@ -647,17 +542,6 @@
                 // Remember where the motion event started
                 mMotionDownX = dragLayerX;
                 mMotionDownY = dragLayerY;
-
-                if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
-                    mScrollState = SCROLL_WAITING_IN_ZONE;
-                    mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
-                } else {
-                    mScrollState = SCROLL_OUTSIDE_ZONE;
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mHandler.removeCallbacks(mScrollRunnable);
                 break;
         }
 
@@ -688,47 +572,12 @@
 
         dropTarget.prepareAccessibilityDrop();
         // Perform the drop
-        drop(dropTarget, location[0], location[1], null);
+        drop(dropTarget, null);
         endDrag();
     }
 
-    /**
-     * Determines whether the user flung the current item to delete it.
-     *
-     * @return the vector at which the item was flung, or null if no fling was detected.
-     */
-    private PointF isFlingingToDelete(DragSource source) {
-        if (mFlingToDeleteDropTarget == null) return null;
-        if (!source.supportsFlingToDelete()) return null;
-
-        ViewConfiguration config = ViewConfiguration.get(mLauncher);
-        mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
-        PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
-        float theta = MAX_FLING_DEGREES + 1;
-        if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
-            // Do a quick dot product test to ensure that we are flinging upwards
-            PointF upVec = new PointF(0f, -1f);
-            theta = getAngleBetweenVectors(vel, upVec);
-        } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() &&
-                mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) {
-            // Remove icon is on left side instead of top, so check if we are flinging to the left.
-            PointF leftVec = new PointF(-1f, 0f);
-            theta = getAngleBetweenVectors(vel, leftVec);
-        }
-        if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
-            return vel;
-        }
-        return null;
-    }
-
-    private float getAngleBetweenVectors(PointF vec1, PointF vec2) {
-        return (float) Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) /
-                (vec1.length() * vec2.length()));
-    }
-
-    private void drop(DropTarget dropTarget, float x, float y, PointF flingVel) {
+    private void drop(DropTarget dropTarget, Runnable flingAnimation) {
         final int[] coordinates = mCoordinatesTemp;
-
         mDragObject.x = coordinates[0];
         mDragObject.y = coordinates[1];
 
@@ -750,8 +599,8 @@
         if (dropTarget != null) {
             dropTarget.onDragExit(mDragObject);
             if (dropTarget.acceptDrop(mDragObject)) {
-                if (flingVel != null) {
-                    dropTarget.onFlingToDelete(mDragObject, flingVel);
+                if (flingAnimation != null) {
+                    flingAnimation.run();
                 } else if (!mIsInPreDrag) {
                     dropTarget.onDrop(mDragObject);
                 }
@@ -762,7 +611,7 @@
         mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
         if (!mIsInPreDrag) {
             mDragObject.dragSource.onDropCompleted(
-                    dropTargetAsView, mDragObject, flingVel != null, accepted);
+                    dropTargetAsView, mDragObject, flingAnimation != null, accepted);
         }
     }
 
@@ -792,10 +641,6 @@
         return null;
     }
 
-    public void setDragScroller(DragScroller scroller) {
-        mDragScroller = scroller;
-    }
-
     public void setWindowToken(IBinder token) {
         mWindowToken = token;
     }
@@ -828,61 +673,4 @@
         mDropTargets.remove(target);
     }
 
-    /**
-     * Sets the current fling-to-delete drop target.
-     */
-    public void setFlingToDeleteDropTarget(DropTarget target) {
-        mFlingToDeleteDropTarget = target;
-    }
-
-    private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
-    }
-
-    private void releaseVelocityTracker() {
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    /**
-     * Set which view scrolls for touch events near the edge of the screen.
-     */
-    public void setScrollView(View v) {
-        mScrollView = v;
-    }
-
-    private class ScrollRunnable implements Runnable {
-        private int mDirection;
-
-        ScrollRunnable() {
-        }
-
-        public void run() {
-            if (mDragScroller != null) {
-                if (mDirection == SCROLL_LEFT) {
-                    mDragScroller.scrollLeft();
-                } else {
-                    mDragScroller.scrollRight();
-                }
-                mScrollState = SCROLL_OUTSIDE_ZONE;
-                mDistanceSinceScroll = 0;
-                mDragScroller.onExitScrollArea();
-                mLauncher.getDragLayer().onExitScrollArea();
-
-                if (isDragging()) {
-                    // Check the scroll again so that we can requeue the scroller if necessary
-                    checkScrollState(mLastTouch[0], mLastTouch[1]);
-                }
-            }
-        }
-
-        void setDirection(int direction) {
-            mDirection = direction;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index d0c8e16..a90cfff 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -23,7 +23,6 @@
 import android.view.DragEvent;
 import android.view.MotionEvent;
 
-import com.android.launcher3.DropTarget;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.ShortcutInfo;
@@ -40,7 +39,7 @@
     public interface EventListener {
         void onDriverDragMove(float x, float y);
         void onDriverDragExitWindow();
-        void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride);
+        void onDriverDragEnd(float x, float y);
         void onDriverDragCancel();
     }
 
@@ -62,7 +61,7 @@
                 break;
             case MotionEvent.ACTION_UP:
                 mEventListener.onDriverDragMove(ev.getX(), ev.getY());
-                mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null);
+                mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
                 break;
             case MotionEvent.ACTION_CANCEL:
                 mEventListener.onDriverDragCancel();
@@ -80,7 +79,7 @@
 
         switch (action) {
             case MotionEvent.ACTION_UP:
-                mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null);
+                mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
                 break;
             case MotionEvent.ACTION_CANCEL:
                 mEventListener.onDriverDragCancel();
@@ -160,7 +159,7 @@
 
             case DragEvent.ACTION_DRAG_ENDED:
                 if (mReceivedDropEvent) {
-                    mEventListener.onDriverDragEnd(mLastX, mLastY, null);
+                    mEventListener.onDriverDragEnd(mLastX, mLastY);
                 } else {
                     mEventListener.onDriverDragCancel();
                 }
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 9de4452..d656c84 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -33,7 +33,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.DragEvent;
@@ -117,13 +116,6 @@
     private final Rect mScrollChildPosition = new Rect();
     private final ViewGroupFocusHelper mFocusIndicatorHelper;
 
-    private boolean mInScrollArea;
-    private boolean mShowPageHints;
-    private Drawable mLeftHoverDrawable;
-    private Drawable mRightHoverDrawable;
-    private Drawable mLeftHoverDrawableActive;
-    private Drawable mRightHoverDrawableActive;
-
     // Related to pinch-to-go-to-overview gesture.
     private PinchToOverviewListener mPinchListener = null;
 
@@ -144,14 +136,7 @@
         setMotionEventSplittingEnabled(false);
         setChildrenDrawingOrderEnabled(true);
 
-        final Resources res = getResources();
-        if (FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
-            mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left);
-            mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right);
-            mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active);
-            mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active);
-        }
-        mIsRtl = Utilities.isRtl(res);
+        mIsRtl = Utilities.isRtl(getResources());
         mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
     }
 
@@ -911,29 +896,6 @@
         }
     }
 
-    void onEnterScrollArea() {
-        mInScrollArea = true;
-        invalidate();
-    }
-
-    void onExitScrollArea() {
-        mInScrollArea = false;
-        invalidate();
-    }
-
-    public void showPageHints() {
-        mShowPageHints = true;
-        Workspace workspace = mLauncher.getWorkspace();
-        getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.numCustomPages()),
-                mScrollChildPosition);
-        invalidate();
-    }
-
-    public void hidePageHints() {
-        mShowPageHints = false;
-        invalidate();
-    }
-
     public void invalidateScrim() {
         if (mBackgroundAlpha > 0.0f) {
             invalidate();
@@ -963,41 +925,6 @@
         super.dispatchDraw(canvas);
     }
 
-    private void drawPageHints(Canvas canvas) {
-        if (mShowPageHints) {
-            Workspace workspace = mLauncher.getWorkspace();
-            int width = getMeasuredWidth();
-            int page = workspace.getNextPage();
-            CellLayout leftPage = (CellLayout) workspace.getChildAt(mIsRtl ? page + 1 : page - 1);
-            CellLayout rightPage = (CellLayout) workspace.getChildAt(mIsRtl ? page - 1 : page + 1);
-
-            if (leftPage != null && leftPage.isDragTarget()) {
-                Drawable left = mInScrollArea && leftPage.getIsDragOverlapping() ?
-                        mLeftHoverDrawableActive : mLeftHoverDrawable;
-                left.setBounds(0, mScrollChildPosition.top,
-                        left.getIntrinsicWidth(), mScrollChildPosition.bottom);
-                left.draw(canvas);
-            }
-            if (rightPage != null && rightPage.isDragTarget()) {
-                Drawable right = mInScrollArea && rightPage.getIsDragOverlapping() ?
-                        mRightHoverDrawableActive : mRightHoverDrawable;
-                right.setBounds(width - right.getIntrinsicWidth(),
-                        mScrollChildPosition.top, width, mScrollChildPosition.bottom);
-                right.draw(canvas);
-            }
-        }
-    }
-
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        boolean ret = super.drawChild(canvas, child, drawingTime);
-
-        // We want to draw the page hints above the workspace, but below the drag view.
-        if (child instanceof Workspace) {
-            drawPageHints(canvas);
-        }
-        return ret;
-    }
-
     public void setBackgroundAlpha(float alpha) {
         if (alpha != mBackgroundAlpha) {
             mBackgroundAlpha = alpha;
diff --git a/src/com/android/launcher3/dragndrop/DragScroller.java b/src/com/android/launcher3/dragndrop/DragScroller.java
deleted file mode 100644
index 165d0b1..0000000
--- a/src/com/android/launcher3/dragndrop/DragScroller.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.dragndrop;
-
-/**
- * Handles scrolling while dragging
- *
- */
-public interface DragScroller {
-    void scrollLeft();
-    void scrollRight();
-
-    /**
-     * The touch point has entered the scroll area; a scroll is imminent.
-     * This event will only occur while a drag is active.
-     *
-     * @param direction The scroll direction
-     */
-    boolean onEnterScrollArea(int x, int y, int direction);
-
-    /**
-     * The touch point has left the scroll area.
-     * NOTE: This may not be called, if a drop occurs inside the scroll area.
-     */
-    boolean onExitScrollArea();
-}
diff --git a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
index ac50332..37cc9ad 100644
--- a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
+++ b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
@@ -45,7 +45,7 @@
         mLauncher = launcher;
         mAddInfo = addInfo;
 
-        mOutlineSize = mLauncher.getWorkspace().estimateItemSize(mAddInfo, false);
+        mOutlineSize = mLauncher.getWorkspace().estimateItemSize(mAddInfo, false, false);
     }
 
     public Rect getPreviewBounds() {
diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
new file mode 100644
index 0000000..a2aa27d
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dragndrop;
+
+import android.graphics.PointF;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.ButtonDropTarget;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.util.FlingAnimation;
+
+/**
+ * Utility class to manage fling to delete action during drag and drop.
+ */
+public class FlingToDeleteHelper {
+
+    private static final float MAX_FLING_DEGREES = 35f;
+
+    private final Launcher mLauncher;
+    private final int mFlingToDeleteThresholdVelocity;
+
+    private ButtonDropTarget mDropTarget;
+    private VelocityTracker mVelocityTracker;
+
+    public FlingToDeleteHelper(Launcher launcher) {
+        mLauncher = launcher;
+        mFlingToDeleteThresholdVelocity = launcher.getResources()
+                .getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity);
+    }
+
+    public void recordMotionEvent(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+    }
+
+    public void releaseVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    public DropTarget getDropTarget() {
+        return mDropTarget;
+    }
+
+    public Runnable getFlingAnimation(DropTarget.DragObject dragObject) {
+        PointF vel = isFlingingToDelete();
+        if (vel == null) {
+            return null;
+        }
+        return new FlingAnimation(dragObject, vel, mDropTarget, mLauncher);
+    }
+
+    /**
+     * Determines whether the user flung the current item to delete it.
+     *
+     * @return the vector at which the item was flung, or null if no fling was detected.
+     */
+    private PointF isFlingingToDelete() {
+        if (mDropTarget == null) {
+            mDropTarget = (ButtonDropTarget) mLauncher.findViewById(R.id.delete_target_text);
+        }
+        if (mDropTarget == null || !mDropTarget.isDropEnabled()) return null;
+        ViewConfiguration config = ViewConfiguration.get(mLauncher);
+        mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
+        PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
+        float theta = MAX_FLING_DEGREES + 1;
+        if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
+            // Do a quick dot product test to ensure that we are flinging upwards
+            PointF upVec = new PointF(0f, -1f);
+            theta = getAngleBetweenVectors(vel, upVec);
+        } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() &&
+                mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) {
+            // Remove icon is on left side instead of top, so check if we are flinging to the left.
+            PointF leftVec = new PointF(-1f, 0f);
+            theta = getAngleBetweenVectors(vel, leftVec);
+        }
+        if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
+            return vel;
+        }
+        return null;
+    }
+
+    private float getAngleBetweenVectors(PointF vec1, PointF vec2) {
+        return (float) Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) /
+                (vec1.length() * vec2.length()));
+    }
+}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index a81b4ca..52c6abc 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -25,12 +25,10 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
 import android.text.InputType;
 import android.text.Selection;
-import android.text.Spannable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.ActionMode;
@@ -45,13 +43,11 @@
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
 import android.widget.TextView;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Alarm;
 import com.android.launcher3.AppInfo;
-import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
@@ -66,6 +62,7 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LogDecelerateInterpolator;
 import com.android.launcher3.OnAlarmListener;
+import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.UninstallDropTarget.DropTargetSource;
@@ -79,7 +76,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.CircleRevealOutlineProvider;
 import com.android.launcher3.util.Thunk;
@@ -111,7 +108,12 @@
     /**
      * Time for which the scroll hint is shown before automatically changing page.
      */
-    public static final int SCROLL_HINT_DURATION = DragController.SCROLL_DELAY;
+    public static final int SCROLL_HINT_DURATION = 500;
+    public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150;
+
+    public static final int SCROLL_NONE = -1;
+    public static final int SCROLL_LEFT = 0;
+    public static final int SCROLL_RIGHT = 1;
 
     /**
      * Fraction of icon width which behave as scroll region.
@@ -138,8 +140,6 @@
     private final int mMaterialExpandDuration;
     private final int mMaterialExpandStagger;
 
-    private final InputMethodManager mInputMethodManager;
-
     protected final Launcher mLauncher;
     protected DragController mDragController;
     public FolderInfo mInfo;
@@ -187,8 +187,8 @@
     // Folder scrolling
     private int mScrollAreaOffset;
 
-    @Thunk int mScrollHintDir = DragController.SCROLL_NONE;
-    @Thunk int mCurrentScrollDir = DragController.SCROLL_NONE;
+    @Thunk int mScrollHintDir = SCROLL_NONE;
+    @Thunk int mCurrentScrollDir = SCROLL_NONE;
 
     /**
      * Used to inflate the Workspace from XML.
@@ -199,9 +199,6 @@
     public Folder(Context context, AttributeSet attrs) {
         super(context, attrs);
         setAlwaysDrawnWithCacheEnabled(false);
-        mInputMethodManager = (InputMethodManager)
-                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
         Resources res = getResources();
         mExpandDuration = res.getInteger(R.integer.config_folderExpandDuration);
         mMaterialExpandDuration = res.getInteger(R.integer.config_materialFolderExpandDuration);
@@ -836,15 +833,15 @@
         boolean isOutsideRightEdge = x > (getWidth() - cellOverlap);
 
         if (currentPage > 0 && (mContent.mIsRtl ? isOutsideRightEdge : isOutsideLeftEdge)) {
-            showScrollHint(DragController.SCROLL_LEFT, d);
+            showScrollHint(SCROLL_LEFT, d);
         } else if (currentPage < (mContent.getPageCount() - 1)
                 && (mContent.mIsRtl ? isOutsideLeftEdge : isOutsideRightEdge)) {
-            showScrollHint(DragController.SCROLL_RIGHT, d);
+            showScrollHint(SCROLL_RIGHT, d);
         } else {
             mOnScrollHintAlarm.cancelAlarm();
-            if (mScrollHintDir != DragController.SCROLL_NONE) {
+            if (mScrollHintDir != SCROLL_NONE) {
                 mContent.clearScrollHint();
-                mScrollHintDir = DragController.SCROLL_NONE;
+                mScrollHintDir = SCROLL_NONE;
             }
         }
     }
@@ -902,9 +899,9 @@
 
         mOnScrollHintAlarm.cancelAlarm();
         mScrollPauseAlarm.cancelAlarm();
-        if (mScrollHintDir != DragController.SCROLL_NONE) {
+        if (mScrollHintDir != SCROLL_NONE) {
             mContent.clearScrollHint();
-            mScrollHintDir = DragController.SCROLL_NONE;
+            mScrollHintDir = SCROLL_NONE;
         }
     }
 
@@ -1010,13 +1007,8 @@
     }
 
     @Override
-    public boolean supportsFlingToDelete() {
-        return true;
-    }
-
-    @Override
     public boolean supportsAppInfoDropTarget() {
-        return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND;
+        return true;
     }
 
     @Override
@@ -1024,16 +1016,6 @@
         return true;
     }
 
-    @Override
-    public void onFlingToDelete(DragObject d, PointF vec) {
-        // Do nothing
-    }
-
-    @Override
-    public void onFlingToDeleteCompleted() {
-        // Do nothing
-    }
-
     private void updateItemLocationsInDatabaseBatch() {
         ArrayList<View> list = getItemsInReadingOrder();
         ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
@@ -1466,7 +1448,7 @@
         target.gridX = info.cellX;
         target.gridY = info.cellY;
         target.pageIndex = mContent.getCurrentPage();
-        targetParent.containerType = LauncherLogProto.FOLDER;
+        targetParent.containerType = ContainerType.FOLDER;
     }
 
     private class OnScrollHintListener implements OnAlarmListener {
@@ -1482,21 +1464,21 @@
          */
         @Override
         public void onAlarm(Alarm alarm) {
-            if (mCurrentScrollDir == DragController.SCROLL_LEFT) {
+            if (mCurrentScrollDir == SCROLL_LEFT) {
                 mContent.scrollLeft();
-                mScrollHintDir = DragController.SCROLL_NONE;
-            } else if (mCurrentScrollDir == DragController.SCROLL_RIGHT) {
+                mScrollHintDir = SCROLL_NONE;
+            } else if (mCurrentScrollDir == SCROLL_RIGHT) {
                 mContent.scrollRight();
-                mScrollHintDir = DragController.SCROLL_NONE;
+                mScrollHintDir = SCROLL_NONE;
             } else {
                 // This should not happen
                 return;
             }
-            mCurrentScrollDir = DragController.SCROLL_NONE;
+            mCurrentScrollDir = SCROLL_NONE;
 
             // Pause drag event until the scrolling is finished
             mScrollPauseAlarm.setOnAlarmListener(new OnScrollFinishedListener(mDragObject));
-            mScrollPauseAlarm.setAlarm(DragController.RESCROLL_DELAY);
+            mScrollPauseAlarm.setAlarm(RESCROLL_DELAY);
         }
     }
 
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 3a318b5..4c12032 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -483,7 +483,7 @@
      * Scrolls the current view by a fraction
      */
     public void showScrollHint(int direction) {
-        float fraction = (direction == DragController.SCROLL_LEFT) ^ mIsRtl
+        float fraction = (direction == Folder.SCROLL_LEFT) ^ mIsRtl
                 ? -SCROLL_HINT_FRACTION : SCROLL_HINT_FRACTION;
         int hint = (int) (fraction * getWidth());
         int scroll = getScrollForPage(getNextPage()) + hint;
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 395daa5..c13e8b3 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -1,5 +1,7 @@
 package com.android.launcher3.logging;
 
+import android.util.ArrayMap;
+import android.util.SparseArray;
 import android.view.View;
 
 import com.android.launcher3.ButtonDropTarget;
@@ -8,80 +10,73 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.UninstallDropTarget;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
 /**
- * Debugging helper methods.
- * toString() cannot be overriden inside auto generated {@link LauncherLogProto}.
- * Note: switch statement cannot be replaced with reflection as proguard strips the constants
+ * Helper methods for logging.
  */
 public class LoggerUtils {
-    private static final String TAG = "LoggerUtils";
+    private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
+    private static final String UNKNOWN = "UNKNOWN";
 
-    private static String getCommandStr(Action action) {
-        switch (action.command) {
-            case Action.HOME_INTENT: return "HOME_INTENT";
-            case Action.BACK: return "BACK";
-            default: return "UNKNOWN";
+    private static String getFieldName(int value, Class c) {
+        SparseArray<String> cache;
+        synchronized (sNameCache) {
+            cache = sNameCache.get(c);
+            if (cache == null) {
+                cache = new SparseArray<>();
+                for (Field f : c.getDeclaredFields()) {
+                    if (f.getType() == int.class && Modifier.isStatic(f.getModifiers())) {
+                        try {
+                            f.setAccessible(true);
+                            cache.put(f.getInt(null), f.getName());
+                        } catch (IllegalAccessException e) {
+                            // Ignore
+                        }
+                    }
+                }
+                sNameCache.put(c, cache);
+            }
         }
+        String result = cache.get(value);
+        return result != null ? result : UNKNOWN;
     }
 
-    private static String getTouchStr(Action action) {
-        switch (action.touch) {
-            case Action.TAP: return "TAP";
-            case Action.LONGPRESS: return "LONGPRESS";
-            case Action.DRAGDROP: return "DRAGDROP";
-            case Action.PINCH: return "PINCH";
-            case Action.SWIPE: return "SWIPE";
-            case Action.FLING: return "FLING";
-            default: return "UNKNOWN";
-        }
-    }
-
-    public static String getActionStr(LauncherLogProto.Action action) {
+    public static String getActionStr(Action action) {
         switch (action.type) {
-            case Action.TOUCH: return getTouchStr(action);
-            case Action.COMMAND: return getCommandStr(action);
-            default: return "UNKNOWN";
+            case Action.Type.TOUCH: return getFieldName(action.touch, Action.Touch.class);
+            case Action.Type.COMMAND: return getFieldName(action.command, Action.Command.class);
+            default: return UNKNOWN;
         }
     }
 
     public static String getTargetStr(Target t) {
-        String typeStr = "";
         if (t == null){
-            return typeStr;
+            return "";
         }
         switch (t.type) {
-            case Target.ITEM:
+            case Target.Type.ITEM:
                 return getItemStr(t);
-            case Target.CONTROL:
-                return getControlStr(t);
-            case Target.CONTAINER:
-                return getContainerStr(t);
+            case Target.Type.CONTROL:
+                return getFieldName(t.controlType, ControlType.class);
+            case Target.Type.CONTAINER:
+                return getFieldName(t.containerType, ContainerType.class)
+                        + " id=" + t.pageIndex;
             default:
                 return "UNKNOWN TARGET TYPE";
         }
     }
 
     private static String getItemStr(Target t) {
-        String typeStr = "";
-        if (t == null){
-            return typeStr;
-        }
-        switch(t.itemType){
-            case LauncherLogProto.APP_ICON: typeStr = "APPICON"; break;
-            case LauncherLogProto.SHORTCUT: typeStr = "SHORTCUT"; break;
-            case LauncherLogProto.WIDGET: typeStr = "WIDGET"; break;
-            case LauncherLogProto.DEEPSHORTCUT: typeStr = "DEEPSHORTCUT"; break;
-            case LauncherLogProto.FOLDER_ICON: typeStr = "FOLDERICON"; break;
-            case LauncherLogProto.SEARCHBOX: typeStr = "SEARCHBOX"; break;
-            case LauncherLogProto.EDITTEXT: typeStr = "EDITTEXT"; break;
-
-            default: typeStr = "UNKNOWN";
-        }
-
+        String typeStr = getFieldName(t.itemType, ItemType.class);
         if (t.packageNameHash != 0) {
             typeStr += ", packageHash=" + t.packageNameHash;
         }
@@ -94,190 +89,83 @@
         if (t.spanX != 0) {
             typeStr += ", spanX=" + t.spanX;
         }
-        return typeStr += ", grid=(" + t.gridX + "," + t.gridY + "), id=" + t.pageIndex;
+        return typeStr + ", grid=(" + t.gridX + "," + t.gridY + "), id=" + t.pageIndex;
     }
 
-    private static String getControlStr(Target t) {
-        if (t == null){
-            return "";
-        }
-        switch(t.controlType) {
-            case LauncherLogProto.ALL_APPS_BUTTON: return "ALL_APPS_BUTTON";
-            case LauncherLogProto.WIDGETS_BUTTON: return "WIDGETS_BUTTON";
-            case LauncherLogProto.WALLPAPER_BUTTON: return "WALLPAPER_BUTTON";
-            case LauncherLogProto.SETTINGS_BUTTON: return "SETTINGS_BUTTON";
-            case LauncherLogProto.REMOVE_TARGET: return "REMOVE_TARGET";
-            case LauncherLogProto.UNINSTALL_TARGET: return "UNINSTALL_TARGET";
-            case LauncherLogProto.APPINFO_TARGET: return "APPINFO_TARGET";
-            case LauncherLogProto.RESIZE_HANDLE: return "RESIZE_HANDLE";
-            default: return "UNKNOWN";
-        }
+    public static Target newItemTarget(View v) {
+        return (v.getTag() instanceof ItemInfo)
+                ? newItemTarget((ItemInfo) v.getTag())
+                : newTarget(Target.Type.ITEM);
     }
 
-    private static String getContainerStr(LauncherLogProto.Target t) {
-        String str = "";
-        if (t == null) {
-            return str;
-        }
-        switch (t.containerType) {
-            case LauncherLogProto.WORKSPACE:
-                str = "WORKSPACE";
-                break;
-            case LauncherLogProto.HOTSEAT:
-                str = "HOTSEAT";
-                break;
-            case LauncherLogProto.FOLDER:
-                str = "FOLDER";
-                break;
-            case LauncherLogProto.ALLAPPS:
-                str = "ALLAPPS";
-                break;
-            case LauncherLogProto.WIDGETS:
-                str = "WIDGETS";
-                break;
-            case LauncherLogProto.OVERVIEW:
-                str = "OVERVIEW";
-                break;
-            case LauncherLogProto.PREDICTION:
-                str = "PREDICTION";
-                break;
-            case LauncherLogProto.SEARCHRESULT:
-                str = "SEARCHRESULT";
-                break;
-            case LauncherLogProto.DEEPSHORTCUTS:
-                str = "DEEPSHORTCUTS";
-                break;
-            default:
-                str = "UNKNOWN";
-        }
-        return str + " id=" + t.pageIndex;
-    }
-
-    /**
-     * Used for launching an event by tapping on an icon.
-     */
-    public static LauncherLogProto.LauncherEvent initLauncherEvent(
-            int actionType,
-            View v,
-            int parentTargetType){
-        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
-
-        event.srcTarget = new LauncherLogProto.Target[2];
-        event.srcTarget[0] = initTarget(v);
-        event.srcTarget[1] = new LauncherLogProto.Target();
-        event.srcTarget[1].type = parentTargetType;
-
-        event.action = new LauncherLogProto.Action();
-        event.action.type = actionType;
-        return event;
-    }
-
-    /**
-     * Used for clicking on controls and buttons.
-     */
-    public static LauncherLogProto.LauncherEvent initLauncherEvent(
-            int actionType,
-            int childTargetType){
-        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
-
-        event.srcTarget = new LauncherLogProto.Target[1];
-        event.srcTarget[0] = new LauncherLogProto.Target();
-        event.srcTarget[0].type = childTargetType;
-
-        event.action = new LauncherLogProto.Action();
-        event.action.type = actionType;
-        return event;
-    }
-
-    /**
-     * Used for commands.
-     */
-    public static LauncherLogProto.LauncherEvent initLauncherEvent(int command,
-            boolean createSrcTarget) {
-        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
-        event.action = new LauncherLogProto.Action();
-        event.action.type = Action.COMMAND;
-        event.action.command = command;
-        event.srcTarget = null;
-
-        if (createSrcTarget) {
-            event.srcTarget = new LauncherLogProto.Target[1];
-            event.srcTarget[0] = new LauncherLogProto.Target();
-            event.srcTarget[0].type = Target.CONTAINER;
-        }
-        return event;
-    }
-
-    /**
-     * Used for drag and drop interaction.
-     */
-    public static LauncherLogProto.LauncherEvent initLauncherEvent(
-            int actionType,
-            ItemInfo info,
-            int parentSrcTargetType,
-            View parentDestTargetType){
-        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
-
-        event.srcTarget = new LauncherLogProto.Target[2];
-        event.srcTarget[0] = initTarget(info);
-        event.srcTarget[1] = new LauncherLogProto.Target();
-        event.srcTarget[1].type = parentSrcTargetType;
-
-        event.destTarget = new LauncherLogProto.Target[2];
-        event.destTarget[0] = initTarget(info);
-        event.destTarget[1] = initDropTarget(parentDestTargetType);
-
-        event.action = new LauncherLogProto.Action();
-        event.action.type = actionType;
-        return event;
-    }
-
-    private static Target initTarget(ItemInfo info) {
-        Target t = new LauncherLogProto.Target();
-        t.type = Target.ITEM;
+    public static Target newItemTarget(ItemInfo info) {
+        Target t = newTarget(Target.Type.ITEM);
         switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                t.itemType = LauncherLogProto.APP_ICON;
+                t.itemType = ItemType.APP_ICON;
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                t.itemType = LauncherLogProto.SHORTCUT;
+                t.itemType = ItemType.SHORTCUT;
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                t.itemType = LauncherLogProto.FOLDER_ICON;
+                t.itemType = ItemType.FOLDER_ICON;
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-                t.itemType = LauncherLogProto.WIDGET;
+                t.itemType = ItemType.WIDGET;
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                t.itemType = LauncherLogProto.DEEPSHORTCUT;
+                t.itemType = ItemType.DEEPSHORTCUT;
                 break;
         }
         return t;
     }
 
-    private static Target initDropTarget(View v) {
-        Target t = new LauncherLogProto.Target();
-        t.type = (v instanceof ButtonDropTarget)? Target.CONTROL : Target.CONTAINER;
-        if (t.type == Target.CONTAINER) {
-            return t;
+    public static Target newDropTarget(View v) {
+        if (!(v instanceof ButtonDropTarget)) {
+            return newTarget(Target.Type.CONTAINER);
         }
-
+        Target t = newTarget(Target.Type.CONTROL);
         if (v instanceof InfoDropTarget) {
-            t.controlType = LauncherLogProto.APPINFO_TARGET;
+            t.controlType = ControlType.APPINFO_TARGET;
         } else if (v instanceof UninstallDropTarget) {
-            t.controlType = LauncherLogProto.UNINSTALL_TARGET;
+            t.controlType = ControlType.UNINSTALL_TARGET;
         } else if (v instanceof DeleteDropTarget) {
-            t.controlType = LauncherLogProto.REMOVE_TARGET;
+            t.controlType = ControlType.REMOVE_TARGET;
         }
         return t;
     }
 
-    private static Target initTarget(View v) {
-        Target t = new LauncherLogProto.Target();
-        t.type = Target.ITEM;
-        if (!(v.getTag() instanceof ItemInfo)) {
-            return t;
-        }
-        return initTarget((ItemInfo) v.getTag());
+    public static Target newTarget(int targetType) {
+        Target t = new Target();
+        t.type = targetType;
+        return t;
+    }
+    public static Target newContainerTarget(int containerType) {
+        Target t = newTarget(Target.Type.CONTAINER);
+        t.containerType = containerType;
+        return t;
+    }
+
+    public static Action newAction(int type) {
+        Action a = new Action();
+        a.type = type;
+        return a;
+    }
+    public static Action newCommandAction(int command) {
+        Action a = newAction(Action.Type.COMMAND);
+        a.command = command;
+        return a;
+    }
+    public static Action newTouchAction(int touch) {
+        Action a = newAction(Action.Type.TOUCH);
+        a.touch = touch;
+        return a;
+    }
+
+    public static LauncherEvent newLauncherEvent(Action action, Target... srcTargets) {
+        LauncherEvent event = new LauncherEvent();
+        event.srcTarget = srcTargets;
+        event.action = action;
+        return event;
     }
 }
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 2fcdd39..8ded6b8 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -27,8 +27,8 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.ProviderConfig;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ComponentKey;
@@ -36,6 +36,14 @@
 import java.util.List;
 import java.util.Locale;
 
+import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.LoggerUtils.newDropTarget;
+import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
+import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.logging.LoggerUtils.newTarget;
+import static com.android.launcher3.logging.LoggerUtils.newTouchAction;
+
 /**
  * Manages the creation of {@link LauncherEvent}.
  * To debug this class, execute following command before side loading a new apk.
@@ -46,7 +54,9 @@
 
     private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
 
-    private final boolean mIsVerbose;
+    private static final String TAG = "UserEvent";
+    private static final boolean IS_VERBOSE =
+            ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(TAG);
 
     /**
      * Implemented by containers to provide a container source for a given child.
@@ -68,8 +78,7 @@
      * Recursively finds the parent of the given child which implements IconLogInfoProvider
      */
     public static LogContainerProvider getLaunchProviderRecursive(View v) {
-        ViewParent parent = null;
-
+        ViewParent parent;
         if (v != null) {
             parent = v.getParent();
         } else {
@@ -88,8 +97,6 @@
         return null;
     }
 
-    private String TAG = "UserEvent";
-
     private long mElapsedContainerMillis;
     private long mElapsedSessionMillis;
     private long mActionDurationMillis;
@@ -97,14 +104,6 @@
     // Used for filling in predictedRank on {@link Target}s.
     private List<ComponentKey> mPredictedApps;
 
-    public UserEventDispatcher() {
-        if (ProviderConfig.IS_DOGFOOD_BUILD) {
-            mIsVerbose = Utilities.isPropertyEnabled(TAG);
-        } else {
-            mIsVerbose = false;
-        }
-    }
-
     //                      APP_ICON    SHORTCUT    WIDGET
     // --------------------------------------------------------------
     // packageNameHash      required    optional    required
@@ -113,9 +112,8 @@
     // --------------------------------------------------------------
 
     protected LauncherEvent createLauncherEvent(View v, Intent intent) {
-        LauncherEvent event = LoggerUtils.initLauncherEvent(
-                Action.TOUCH, v, Target.CONTAINER);
-        event.action.touch = Action.TAP;
+        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
+                newItemTarget(v), newTarget(Target.Type.CONTAINER));
 
         // TODO: make idx percolate up the view hierarchy if needed.
         int idx = 0;
@@ -159,8 +157,8 @@
     }
 
     public void logActionCommand(int command, int containerType, int pageIndex) {
-        LauncherEvent event = LoggerUtils.initLauncherEvent(command, true);
-        event.srcTarget[0].containerType = containerType;
+        LauncherEvent event = newLauncherEvent(
+                newCommandAction(command), newContainerTarget(containerType));
         event.srcTarget[0].pageIndex = pageIndex;
         dispatchUserEvent(event, null);
     }
@@ -169,21 +167,21 @@
      * TODO: Make this function work when a container view is passed as the 2nd param.
      */
     public void logActionCommand(int command, View itemView, int containerType) {
-        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.COMMAND, itemView,
-                Target.CONTAINER);
-        event.action.command = command;
+        LauncherEvent event = newLauncherEvent(newCommandAction(command),
+                newItemTarget(itemView), newTarget(Target.Type.CONTAINER));
+
         if (fillInLogContainerData(event, itemView)) {
             // TODO: Remove the following two lines once fillInLogContainerData can take in a
             // container view.
-            event.srcTarget[0].type = Target.CONTAINER;
+            event.srcTarget[0].type = Target.Type.CONTAINER;
             event.srcTarget[0].containerType = containerType;
         }
         dispatchUserEvent(event, null);
     }
 
     public void logActionOnControl(int action, int controlType) {
-        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.CONTROL);
-        event.action.touch = action;
+        LauncherEvent event = newLauncherEvent(
+                newTouchAction(action), newTarget(Target.Type.CONTROL));
         event.srcTarget[0].controlType = controlType;
         dispatchUserEvent(event, null);
     }
@@ -193,24 +191,22 @@
     }
 
     public void logActionOnContainer(int action, int dir, int containerType, int pageIndex) {
-        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.CONTAINER);
-        event.action.touch = action;
+        LauncherEvent event = newLauncherEvent(newTouchAction(action),
+                newContainerTarget(containerType));
         event.action.dir = dir;
-        event.srcTarget[0].containerType = containerType;
         event.srcTarget[0].pageIndex = pageIndex;
         dispatchUserEvent(event, null);
     }
 
     public void logDeepShortcutsOpen(View icon) {
-        LauncherEvent event = LoggerUtils.initLauncherEvent(
-                Action.TOUCH, icon, Target.CONTAINER);
         LogContainerProvider provider = getLaunchProviderRecursive(icon);
         if (icon == null && !(icon.getTag() instanceof ItemInfo)) {
             return;
         }
         ItemInfo info = (ItemInfo) icon.getTag();
+        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.LONGPRESS),
+                newItemTarget(info), newTarget(Target.Type.CONTAINER));
         provider.fillInLogContainerData(icon, info, event.srcTarget[0], event.srcTarget[1]);
-        event.action.touch = Action.LONGPRESS;
         dispatchUserEvent(event, null);
 
         resetElapsedContainerMillis();
@@ -223,28 +219,18 @@
     /* Currently we are only interested in whether this event happens or not and don't
     * care about which screen moves to where. */
     public void logOverviewReorder() {
-        LauncherEvent event = new LauncherLogProto.LauncherEvent();
-
-        event.srcTarget = new LauncherLogProto.Target[2];
-        event.srcTarget[0] = new LauncherLogProto.Target();
-        event.srcTarget[0].type = Target.CONTAINER;
-        event.srcTarget[0].containerType = LauncherLogProto.WORKSPACE;
-        event.srcTarget[1] = new LauncherLogProto.Target();
-        event.srcTarget[1].type = Target.CONTAINER;
-        event.srcTarget[1].containerType = LauncherLogProto.OVERVIEW;
-
-        event.action = new LauncherLogProto.Action();
-        event.action.type = Action.TOUCH;
-        event.action.touch = Action.DRAGDROP;
+        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP),
+                newContainerTarget(ContainerType.WORKSPACE),
+                newContainerTarget(ContainerType.OVERVIEW));
         dispatchUserEvent(event, null);
-
     }
+
     public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
-        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH,
-                dragObj.originalDragInfo,
-                Target.CONTAINER,
-                dropTargetAsView);
-        event.action.touch = Action.DRAGDROP;
+        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP),
+                newItemTarget(dragObj.originalDragInfo), newTarget(Target.Type.CONTAINER));
+        event.destTarget = new Target[] {
+                newItemTarget(dragObj.originalDragInfo), newDropTarget(dropTargetAsView)
+        };
 
         dragObj.dragSource.fillInLogContainerData(null, dragObj.originalDragInfo,
                 event.srcTarget[0], event.srcTarget[1]);
@@ -278,26 +264,26 @@
         ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
         ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
 
-        if (!mIsVerbose) {
+        if (!IS_VERBOSE) {
             return;
         }
-        Log.d(TAG, String.format(Locale.US,
-                "\naction:%s\n Source child:%s\tparent:%s",
-                LoggerUtils.getActionStr(ev.action),
-                LoggerUtils.getTargetStr(ev.srcTarget != null ? ev.srcTarget[0] : null),
-                LoggerUtils.getTargetStr(ev.srcTarget != null && ev.srcTarget.length > 1 ?
-                        ev.srcTarget[1] : null)));
-        if (ev.destTarget != null && ev.destTarget.length > 0) {
-            Log.d(TAG, String.format(Locale.US,
-                    " Destination child:%s\tparent:%s",
-                    LoggerUtils.getTargetStr(ev.destTarget != null ? ev.destTarget[0] : null),
-                    LoggerUtils.getTargetStr(ev.destTarget != null && ev.destTarget.length > 1 ?
-                            ev.destTarget[1] : null)));
+        String log = "action:" + LoggerUtils.getActionStr(ev.action);
+        if (ev.srcTarget != null && ev.srcTarget.length > 0) {
+            log += "\n Source " + getTargetsStr(ev.srcTarget);
         }
-        Log.d(TAG, String.format(Locale.US,
-                " Elapsed container %d ms session %d ms action %d ms",
+        if (ev.destTarget != null && ev.destTarget.length > 0) {
+            log += "\n Destination " + getTargetsStr(ev.destTarget);
+        }
+        log += String.format(Locale.US,
+                "\n Elapsed container %d ms session %d ms action %d ms",
                 ev.elapsedContainerMillis,
                 ev.elapsedSessionMillis,
-                ev.actionDurationMillis));
+                ev.actionDurationMillis);
+        Log.d(TAG, log);
+    }
+
+    private static String getTargetsStr(Target[] targets) {
+        return "child:" + LoggerUtils.getTargetStr(targets[0]) +
+                (targets.length > 1 ? "\tparent:" + LoggerUtils.getTargetStr(targets[1]) : "");
     }
 }
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 314a862..9cbd5f8 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -66,7 +66,8 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.graphics.TriangleShape;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
 import java.util.Collections;
@@ -475,11 +476,6 @@
     }
 
     @Override
-    public boolean supportsFlingToDelete() {
-        return true;
-    }
-
-    @Override
     public boolean supportsAppInfoDropTarget() {
         return true;
     }
@@ -495,11 +491,6 @@
     }
 
     @Override
-    public void onFlingToDeleteCompleted() {
-        // Don't care; ignore.
-    }
-
-    @Override
     public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
             boolean success) {
         if (!success) {
@@ -533,9 +524,9 @@
 
     @Override
     public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        target.itemType = LauncherLogProto.DEEPSHORTCUT;
+        target.itemType = ItemType.DEEPSHORTCUT;
         // TODO: add target.rank
-        targetParent.containerType = LauncherLogProto.DEEPSHORTCUTS;
+        targetParent.containerType = ContainerType.DEEPSHORTCUTS;
     }
 
     @Override
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
index da8bae7..d475ee9 100644
--- a/src/com/android/launcher3/util/FlingAnimation.java
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -5,13 +5,16 @@
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 
+import com.android.launcher3.ButtonDropTarget;
 import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
 
-public class FlingAnimation implements AnimatorUpdateListener {
+public class FlingAnimation implements AnimatorUpdateListener, Runnable {
 
     /**
      * Maximum acceleration in one dimension (pixels per milliseconds)
@@ -19,40 +22,86 @@
     private static final float MAX_ACCELERATION = 0.5f;
     private static final int DRAG_END_DELAY = 300;
 
+    private final ButtonDropTarget mDropTarget;
+    private final Launcher mLauncher;
+
     protected final DragObject mDragObject;
-    protected final Rect mIconRect;
     protected final DragLayer mDragLayer;
-    protected final Rect mFrom;
-    protected final int mDuration;
-    protected final float mUX, mUY;
-    protected final float mAnimationTimeFraction;
     protected final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
+    protected final float mUX, mUY;
+
+    protected Rect mIconRect;
+    protected Rect mFrom;
+    protected int mDuration;
+    protected float mAnimationTimeFraction;
 
     protected float mAX, mAY;
 
-    /**
-     * @param vel initial fling velocity in pixels per second.
-     */
-    public FlingAnimation(DragObject d, PointF vel, Rect iconRect, DragLayer dragLayer) {
+    public FlingAnimation(DragObject d, PointF vel, ButtonDropTarget dropTarget, Launcher launcher) {
+        mDropTarget = dropTarget;
+        mLauncher = launcher;
         mDragObject = d;
         mUX = vel.x / 1000;
         mUY = vel.y / 1000;
-        mIconRect = iconRect;
+        mDragLayer = mLauncher.getDragLayer();
+    }
 
-        mDragLayer = dragLayer;
+    @Override
+    public void run() {
+        mIconRect = mDropTarget.getIconRect(mDragObject);
+
+        // Initiate from
         mFrom = new Rect();
-        dragLayer.getViewRectRelativeToSelf(d.dragView, mFrom);
-
-        float scale = d.dragView.getScaleX();
-        float xOffset = ((scale - 1f) * d.dragView.getMeasuredWidth()) / 2f;
-        float yOffset = ((scale - 1f) * d.dragView.getMeasuredHeight()) / 2f;
+        mDragLayer.getViewRectRelativeToSelf(mDragObject.dragView, mFrom);
+        float scale = mDragObject.dragView.getScaleX();
+        float xOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredWidth()) / 2f;
+        float yOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredHeight()) / 2f;
         mFrom.left += xOffset;
         mFrom.right -= xOffset;
         mFrom.top += yOffset;
         mFrom.bottom -= yOffset;
+        mDuration = Math.abs(mUY) > Math.abs(mUX) ? initFlingUpDuration() : initFlingLeftDuration();
 
-        mDuration = Math.abs(vel.y) > Math.abs(vel.x) ? initFlingUpDuration() : initFlingLeftDuration();
         mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY);
+
+        // Don't highlight the icon as it's animating
+        mDragObject.dragView.setColor(0);
+
+        final int duration = mDuration + DRAG_END_DELAY;
+        final long startTime = AnimationUtils.currentAnimationTimeMillis();
+
+        // NOTE: Because it takes time for the first frame of animation to actually be
+        // called and we expect the animation to be a continuation of the fling, we have
+        // to account for the time that has elapsed since the fling finished.  And since
+        // we don't have a startDelay, we will always get call to update when we call
+        // start() (which we want to ignore).
+        final TimeInterpolator tInterpolator = new TimeInterpolator() {
+            private int mCount = -1;
+            private float mOffset = 0f;
+
+            @Override
+            public float getInterpolation(float t) {
+                if (mCount < 0) {
+                    mCount++;
+                } else if (mCount == 0) {
+                    mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
+                            startTime) / duration);
+                    mCount++;
+                }
+                return Math.min(1f, mOffset + t);
+            }
+        };
+
+        Runnable onAnimationEndRunnable = new Runnable() {
+            @Override
+            public void run() {
+                mLauncher.exitSpringLoadedDragMode();
+                mDropTarget.completeDrop(mDragObject);
+            }
+        };
+
+        mDragLayer.animateView(mDragObject.dragView, this, duration, tInterpolator,
+                onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
     }
 
     /**
@@ -111,10 +160,6 @@
         return (int) Math.round(t);
     }
 
-    public final int getDuration() {
-        return mDuration + DRAG_END_DELAY;
-    }
-
     @Override
     public void onAnimationUpdate(ValueAnimator animation) {
         float t = animation.getAnimatedFraction();
diff --git a/src/com/android/launcher3/widget/PendingItemPreviewProvider.java b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
index 7c06701..722fbb8 100644
--- a/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
+++ b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
@@ -45,7 +45,7 @@
     @Override
     public Bitmap createDragOutline(Canvas canvas) {
         Workspace workspace = Launcher.getLauncher(mView.getContext()).getWorkspace();
-        int[] size = workspace.estimateItemSize(mAddInfo, false);
+        int[] size = workspace.estimateItemSize(mAddInfo, false, false);
 
         int w = size[0];
         int h = size[1];
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 049871f..b196053 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -130,7 +130,7 @@
                 mWidgetLoadingId = -1;
 
                 hostView.setVisibility(View.INVISIBLE);
-                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo, false);
+                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo, false, true);
                 // 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],
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 2e12942..310c1df 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -31,8 +31,6 @@
 import com.android.launcher3.DeleteDropTarget;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.folder.Folder;
 import com.android.launcher3.IconCache;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
@@ -42,11 +40,13 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.Thunk;
@@ -201,7 +201,7 @@
             // the widget if this is null, so we break out.
 
             PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo;
-            int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true);
+            int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true, false);
 
             Bitmap icon = image.getBitmap();
             float minScale = 1.25f;
@@ -246,11 +246,6 @@
     //
 
     @Override
-    public boolean supportsFlingToDelete() {
-        return true;
-    }
-
-    @Override
     public boolean supportsAppInfoDropTarget() {
         return true;
     }
@@ -270,14 +265,6 @@
     }
 
     @Override
-    public void onFlingToDeleteCompleted() {
-        // We just dismiss the drag when we fling, so cleanup here
-        mLauncher.exitSpringLoadedDragModeDelayed(true,
-                Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
-        mLauncher.unlockScreenOrientation(false);
-    }
-
-    @Override
     public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
             boolean success) {
         if (LOGD) {
@@ -323,6 +310,6 @@
 
     @Override
     public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        targetParent.containerType = LauncherLogProto.WIDGETS;
+        targetParent.containerType = ContainerType.WIDGETS;
     }
 }
\ No newline at end of file
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java
index 8ee5497..99d2654 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_config/com/android/launcher3/config/FeatureFlags.java
@@ -24,8 +24,6 @@
 
     // Custom flags go below this
     public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
-    // As opposed to the new spring-loaded workspace.
-    public static boolean LAUNCHER3_LEGACY_WORKSPACE_DND = false;
     public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
     public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = true;
     public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
diff --git a/src_config/com/android/launcher3/config/ProviderConfig.java b/src_config/com/android/launcher3/config/ProviderConfig.java
index 1d964b1..491fa65 100644
--- a/src_config/com/android/launcher3/config/ProviderConfig.java
+++ b/src_config/com/android/launcher3/config/ProviderConfig.java
@@ -20,5 +20,5 @@
 
     public static final String AUTHORITY = "com.android.launcher3.settings".intern();
 
-    public static boolean IS_DOGFOOD_BUILD = true;
+    public static final boolean IS_DOGFOOD_BUILD = true;
 }