Fixing the hotseat import logic
am: d70ef24233

Change-Id: I1be426419d7a7f044dfd81207c3ee3b1df43b404
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 3da3535..bbe1f4a 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -50,7 +50,7 @@
         android:fullBackupContent="@xml/backupscheme"
         android:hardwareAccelerated="true"
         android:icon="@mipmap/ic_launcher_home"
-        android:label="@string/app_name"
+        android:label="@string/derived_app_name"
         android:largeHeap="@bool/config_largeHeap"
         android:restoreAnyVersion="true"
         android:supportsRtl="true" >
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index fef80de..3a429c6 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -77,7 +77,8 @@
             android:id="@+id/apps_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:visibility="invisible" />
+            android:visibility="invisible"
+           launcher:layout_ignoreInsets="true" />
     </com.android.launcher3.dragndrop.DragLayer>
 
 </com.android.launcher3.LauncherRootView>
diff --git a/res/values/config.xml b/res/values/config.xml
index 94f02f9..2347f66 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -9,6 +9,10 @@
     <bool name="is_large_tablet">false</bool>
     <bool name="allow_rotation">false</bool>
 
+    <!-- A string pointer to the original app name string. This allows derived projects to
+     easily override the app name without providing all translations -->
+    <string name="derived_app_name" translatable="false">@string/app_name</string>
+
 <!-- DragController -->
     <item type="id" name="drag_event_parity" />
 
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 0d5043b..d5309b4 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -316,7 +316,7 @@
         parsers.put(TAG_APP_ICON, new AppShortcutParser());
         parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
         parsers.put(TAG_FOLDER, new FolderParser());
-        parsers.put(TAG_APPWIDGET, new AppWidgetParser());
+        parsers.put(TAG_APPWIDGET, new PendingWidgetParser());
         parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes));
         return parsers;
     }
@@ -459,8 +459,12 @@
     /**
      * AppWidget parser: Required attributes packageName, className, spanX and spanY.
      * Options child nodes: <extra key=... value=... />
+     * It adds a pending widget which allows the widget to come later. If there are extras, those
+     * are passed to widget options during bind.
+     * The config activity for the widget (if present) is not shown, so any optional configurations
+     * should be passed as extras and the widget should support reading these widget options.
      */
-    protected class AppWidgetParser implements TagParser {
+    protected class PendingWidgetParser implements TagParser {
 
         @Override
         public long parseAndAdd(XmlResourceParser parser)
@@ -468,27 +472,13 @@
             final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
             final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
             if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
-                if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
+                if (LOGD) Log.d(TAG, "Skipping invalid <appwidget> with no component");
                 return -1;
             }
 
-            ComponentName cn = new ComponentName(packageName, className);
-            try {
-                mPackageManager.getReceiverInfo(cn, 0);
-            } catch (Exception e) {
-                String[] packages = mPackageManager.currentToCanonicalPackageNames(
-                        new String[] { packageName });
-                cn = new ComponentName(packages[0], className);
-                try {
-                    mPackageManager.getReceiverInfo(cn, 0);
-                } catch (Exception e1) {
-                    if (LOGD) Log.d(TAG, "Can't find widget provider: " + className);
-                    return -1;
-                }
-            }
-
             mValues.put(Favorites.SPANX, getAttributeValue(parser, ATTR_SPAN_X));
             mValues.put(Favorites.SPANY, getAttributeValue(parser, ATTR_SPAN_Y));
+            mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
 
             // Read the extras
             Bundle extras = new Bundle();
@@ -513,38 +503,26 @@
                 }
             }
 
-            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
-            long insertedId = -1;
-            try {
-                int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+            return verifyAndInsert(new ComponentName(packageName, className), extras);
+        }
 
-                if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
-                    if (LOGD) Log.e(TAG, "Unable to bind app widget id " + cn);
-                    return -1;
-                }
-
-                mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
-                mValues.put(Favorites.APPWIDGET_ID, appWidgetId);
-                mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
-                mValues.put(Favorites._ID, mCallback.generateNewItemId());
-                insertedId = mCallback.insertAndCheck(mDb, mValues);
-                if (insertedId < 0) {
-                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
-                    return insertedId;
-                }
-
-                // Send a broadcast to configure the widget
-                if (!extras.isEmpty()) {
-                    Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
-                    intent.setComponent(cn);
-                    intent.putExtras(extras);
-                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
-                    mContext.sendBroadcast(intent);
-                }
-            } catch (RuntimeException ex) {
-                if (LOGD) Log.e(TAG, "Problem allocating appWidgetId", ex);
+        protected long verifyAndInsert(ComponentName cn, Bundle extras) {
+            mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
+            mValues.put(Favorites.RESTORED,
+                    LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
+                            LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
+                            LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
+            mValues.put(Favorites._ID, mCallback.generateNewItemId());
+            if (!extras.isEmpty()) {
+                mValues.put(Favorites.INTENT, new Intent().putExtras(extras).toUri(0));
             }
-            return insertedId;
+
+            long insertedId = mCallback.insertAndCheck(mDb, mValues);
+            if (insertedId < 0) {
+                return -1;
+            } else {
+                return insertedId;
+            }
         }
     }
 
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 5a4ed2f..0f6073e 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -62,6 +62,8 @@
 
     /** Whether this drop target is active for the current drag */
     protected boolean mActive;
+    /** Whether an accessible drag is in progress */
+    private boolean mAccessibleDrag;
     /** An item must be dragged at least this many pixels before this drop target is enabled. */
     private final int mDragDistanceThreshold;
 
@@ -218,8 +220,8 @@
 
     @Override
     public boolean isDropEnabled() {
-        return mActive
-                && mLauncher.getDragController().getDistanceDragged() >= mDragDistanceThreshold;
+        return mActive && (mAccessibleDrag ||
+                mLauncher.getDragController().getDistanceDragged() >= mDragDistanceThreshold);
     }
 
     @Override
@@ -307,6 +309,7 @@
     }
 
     public void enableAccessibleDrag(boolean enable) {
+        mAccessibleDrag = enable;
         setOnClickListener(enable ? this : null);
     }
 
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 77f6612..baccfd1 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -54,6 +54,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.util.CellAndSpan;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.ParcelableSparseArray;
@@ -1047,15 +1048,16 @@
         return false;
     }
 
-    void visualizeDropLocation(View v, Bitmap dragOutline, int cellX, int cellY, int spanX,
-            int spanY, boolean resize, DropTarget.DragObject dragObject) {
+    void visualizeDropLocation(View v, DragPreviewProvider outlineProvider, int cellX, int cellY,
+            int spanX, int spanY, boolean resize, DropTarget.DragObject dragObject) {
         final int oldDragCellX = mDragCell[0];
         final int oldDragCellY = mDragCell[1];
 
-        if (dragOutline == null && v == null) {
+        if (outlineProvider == null || outlineProvider.gerenatedDragOutline == null) {
             return;
         }
 
+        Bitmap dragOutline = outlineProvider.gerenatedDragOutline;
         if (cellX != oldDragCellX || cellY != oldDragCellY) {
             Point dragOffset = dragObject.dragView.getDragVisualizeOffset();
             Rect dragRegion = dragObject.dragView.getDragRegion();
@@ -1354,12 +1356,9 @@
                 // and that passed in.
                 int curDirectionScore = direction[0] * curDirection[0] +
                         direction[1] * curDirection[1];
-                boolean exactDirectionOnly = false;
-                boolean directionMatches = direction[0] == curDirection[0] &&
-                        direction[0] == curDirection[0];
-                if ((directionMatches || !exactDirectionOnly) &&
-                        Float.compare(distance,  bestDistance) < 0 || (Float.compare(distance,
-                        bestDistance) == 0 && curDirectionScore > bestDirectionScore)) {
+                if (Float.compare(distance,  bestDistance) < 0 ||
+                        (Float.compare(distance, bestDistance) == 0
+                                && curDirectionScore > bestDirectionScore)) {
                     bestDistance = distance;
                     bestDirectionScore = curDirectionScore;
                     bestXY[0] = x;
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index e6f34d9..ef28d1e 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -1,6 +1,8 @@
 package com.android.launcher3;
 
 import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -9,6 +11,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -42,6 +45,10 @@
     private static final String ATTR_SCREEN = "screen";
     private static final String ATTR_FOLDER_ITEMS = "folderItems";
 
+    // TODO: Remove support for this broadcast, instead use widget options to send bind time options
+    private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
+            "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
+
     public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
             LayoutParserCallback callback, Resources sourceRes, int layoutId) {
         super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
@@ -270,4 +277,61 @@
             return super.parseAndAdd(parser);
         }
     }
+
+
+    /**
+     * AppWidget parser which enforces that the app is already installed when the layout is parsed.
+     */
+    protected class AppWidgetParser extends PendingWidgetParser {
+
+        @Override
+        protected long verifyAndInsert(ComponentName cn, Bundle extras) {
+            try {
+                mPackageManager.getReceiverInfo(cn, 0);
+            } catch (Exception e) {
+                String[] packages = mPackageManager.currentToCanonicalPackageNames(
+                        new String[] { cn.getPackageName() });
+                cn = new ComponentName(packages[0], cn.getClassName());
+                try {
+                    mPackageManager.getReceiverInfo(cn, 0);
+                } catch (Exception e1) {
+                    Log.d(TAG, "Can't find widget provider: " + cn.getClassName());
+                    return -1;
+                }
+            }
+
+            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+            long insertedId = -1;
+            try {
+                int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+                if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
+                    Log.e(TAG, "Unable to bind app widget id " + cn);
+                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+                    return -1;
+                }
+
+                mValues.put(Favorites.APPWIDGET_ID, appWidgetId);
+                mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
+                mValues.put(Favorites._ID, mCallback.generateNewItemId());
+                insertedId = mCallback.insertAndCheck(mDb, mValues);
+                if (insertedId < 0) {
+                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+                    return insertedId;
+                }
+
+                // Send a broadcast to configure the widget
+                if (!extras.isEmpty()) {
+                    Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
+                    intent.setComponent(cn);
+                    intent.putExtras(extras);
+                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+                    mContext.sendBroadcast(intent);
+                }
+            } catch (RuntimeException ex) {
+                Log.e(TAG, "Problem allocating appWidgetId", ex);
+            }
+            return insertedId;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 15f47b4..e6802bd 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -589,9 +589,9 @@
             return new int[] {0, 0};
         }
 
-        // In landscape, we just match the vertical display width
-        int containerWidth = heightPx;
-        int padding = (availableWidthPx - containerWidth) / 2;
+        // In landscape, we match the width of the workspace
+        int padding = (pageIndicatorLandGutterRightNavBarPx +
+                hotseatBarHeightPx + hotseatLandGutterPx + mInsets.left) / 2;
         return new int[]{ padding, padding };
     }
 }
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index 427acea..6822311 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -84,7 +84,7 @@
         applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true);
     }
 
-    void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
+    public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
             int outlineColor, boolean clipAlpha) {
 
         // We start by removing most of the alpha channel so as to ignore shadows, and
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 7c0ed10..c738480 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -165,11 +165,9 @@
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         // We don't want any clicks to go through to the hotseat unless the workspace is in
-        // the normal state.
-        if (mLauncher.getWorkspace().workspaceInModalState()) {
-            return true;
-        }
-        return false;
+        // the normal state or an accessible drag is in progress.
+        return mLauncher.getWorkspace().workspaceInModalState() &&
+                !mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 886c5f0..e309c85 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -883,7 +883,7 @@
             } else {
                 // TODO: Show a snack bar with link to settings
                 Toast.makeText(this, getString(R.string.msg_no_phone_permission,
-                        getString(R.string.app_name)), Toast.LENGTH_SHORT).show();
+                        getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show();
             }
         }
         if (mLauncherCallbacks != null) {
@@ -2809,7 +2809,7 @@
         mDragLayer.onAccessibilityStateChanged(enabled);
     }
 
-    public void onDragStarted(View view) {
+    public void onDragStarted() {
         if (isOnCustomContent()) {
             // Custom content screen doesn't participate in drag and drop. If on custom
             // content screen, move to default.
@@ -3952,14 +3952,30 @@
                     pendingInfo.minSpanX = item.minSpanX;
                     pendingInfo.minSpanY = item.minSpanY;
                     Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+
+                    boolean isDirectConfig =
+                            item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
+                    if (isDirectConfig && item.bindOptions != null) {
+                        Bundle newOptions = item.bindOptions.getExtras();
+                        if (options != null) {
+                            newOptions.putAll(options);
+                        }
+                        options = newOptions;
+                    }
                     boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
                             item.appWidgetId, appWidgetInfo, options);
 
+                    // We tried to bind once. If we were not able to bind, we would need to
+                    // go through the permission dialog, which means we cannot skip the config
+                    // activity.
+                    item.bindOptions = null;
+                    item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG;
+
                     // Bind succeeded
                     if (success) {
                         // If the widget has a configure activity, it is still needs to set it up,
                         // otherwise the widget is ready to go.
-                        item.restoreStatus = (appWidgetInfo.configure == null)
+                        item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig
                                 ? LauncherAppWidgetInfo.RESTORE_COMPLETED
                                 : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
                     }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 99210fd..f22c2a4 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 
 import com.android.launcher3.compat.UserHandleCompat;
 
@@ -57,6 +58,12 @@
     public static final int FLAG_ID_ALLOCATED = 16;
 
     /**
+     * Indicates that the widget does not need to show config activity, even if it has a
+     * configuration screen. It can also optionally have some extras which are sent during bind.
+     */
+    public static final int FLAG_DIRECT_CONFIG = 32;
+
+    /**
      * Indicates that the widget hasn't been instantiated yet.
      */
     static final int NO_ID = -1;
@@ -84,6 +91,11 @@
      */
     int installProgress = -1;
 
+    /**
+     * Optional extras sent during widget bind. See {@link #FLAG_DIRECT_CONFIG}.
+     */
+    public Intent bindOptions;
+
     private boolean mHasNotifiedInitialWidgetSizeChanged;
 
     LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
@@ -115,6 +127,8 @@
         values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
         values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString());
         values.put(LauncherSettings.Favorites.RESTORED, restoreStatus);
+        values.put(LauncherSettings.Favorites.INTENT,
+                bindOptions == null ? null : bindOptions.toUri(0));
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 60e07f9..1607a4a 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -2146,7 +2146,7 @@
 
                                             // Id would be valid only if the widget restore broadcast was received.
                                             if (isIdValid) {
-                                                status = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+                                                status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
                                             } else {
                                                 status &= ~LauncherAppWidgetInfo
                                                         .FLAG_PROVIDER_NOT_READY;
@@ -2177,6 +2177,14 @@
                                         appWidgetInfo.installProgress =
                                                 installProgress == null ? 0 : installProgress;
                                     }
+                                    if (appWidgetInfo.hasRestoreFlag(
+                                            LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
+                                        intentDescription = c.getString(intentIndex);
+                                        if (!TextUtils.isEmpty(intentDescription)) {
+                                            appWidgetInfo.bindOptions =
+                                                    Intent.parseUri(intentDescription, 0);
+                                        }
+                                    }
 
                                     appWidgetInfo.id = id;
                                     appWidgetInfo.screenId = c.getInt(screenIndex);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 2758a7c..bea55d2 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1800,6 +1800,7 @@
         case MotionEvent.ACTION_CANCEL:
             if (mTouchState == TOUCH_STATE_SCROLLING) {
                 snapToDestination();
+                onScrollInteractionEnd();
             }
             resetTouchState();
             break;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 6f61688..4c2d4bb 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -69,12 +69,12 @@
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.DragPreviewProvider;
 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.logging.UserEventDispatcher;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutsContainerListener;
@@ -247,8 +247,7 @@
     /** Is the user is dragging an item near the edge of a page? */
     private boolean mInScrollArea = false;
 
-    private HolographicOutlineHelper mOutlineHelper;
-    @Thunk Bitmap mDragOutline = null;
+    private DragPreviewProvider mOutlineProvider = null;
     public static final int DRAG_BITMAP_PADDING = DragPreviewProvider.DRAG_BITMAP_PADDING;
     private boolean mWorkspaceFadeInAdjacentScreens;
 
@@ -343,8 +342,6 @@
     public Workspace(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        mOutlineHelper = HolographicOutlineHelper.obtain(context);
-
         mLauncher = (Launcher) context;
         mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
         final Resources res = getResources();
@@ -417,7 +414,13 @@
             enfoceDragParity("onDragStart", 0, 0);
         }
 
+        if (mOutlineProvider != null) {
+            // The outline is used to visualize where the item will land if dropped
+            mOutlineProvider.generateDragOutline(mCanvas);
+        }
+
         updateChildrenLayersEnabled(false);
+        mLauncher.onDragStarted();
         mLauncher.lockScreenOrientation();
         mLauncher.onInteractionBegin();
         // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
@@ -426,6 +429,27 @@
         if (mAddNewPageOnDrag) {
             mDeferRemoveExtraEmptyScreen = false;
             addExtraEmptyScreenOnDrag();
+
+            if (source != this && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
+                // When dragging a widget from different source, move to a page which has
+                // enough space to place this widget (after rearranging/resizing). We special case
+                // widgets as they cannot be placed inside a folder.
+                // Start at the current page and search right (on LTR) until finding a page with
+                // enough space. Since an empty screen is the furthest right, a page must be found.
+                int currentPage = getPageNearestToCenterOfScreen();
+                for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) {
+                    CellLayout page = (CellLayout) getPageAt(pageIndex);
+                    if (page.hasReorderSolution(info)) {
+                        setCurrentPage(pageIndex);
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
+            // Always enter the spring loaded mode
+            mLauncher.enterSpringLoadedDragMode();
         }
     }
 
@@ -1999,23 +2023,8 @@
                 position[0], position[1], 0, null);
     }
 
-    public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
-        // Find a page that has enough space to place this widget (after rearranging/resizing).
-        // Start at the current page and search right (on LTR) until finding a page with enough
-        // space. Since an empty screen is the furthest right, a page must be found.
-        int currentPageInOverview = getPageNearestToCenterOfScreen();
-        for (int pageIndex = currentPageInOverview; pageIndex < getPageCount(); pageIndex++) {
-            CellLayout page = (CellLayout) getPageAt(pageIndex);
-            if (page.hasReorderSolution(info)) {
-                setCurrentPage(pageIndex);
-                break;
-            }
-        }
-
-        int[] size = estimateItemSize(info, false);
-
-        // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha);
+    public void prepareDragWithProvider(DragPreviewProvider outlineProvider) {
+        mOutlineProvider = outlineProvider;
     }
 
     public void exitWidgetResizeMode() {
@@ -2271,34 +2280,6 @@
         return null;
     }
 
-    /**
-     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
-     * Responsibility for the bitmap is transferred to the caller.
-     */
-    private Bitmap createDragOutline(Bitmap orig, int padding, int w, int h,
-            boolean clipAlpha) {
-        final int outlineColor = getResources().getColor(R.color.outline_color);
-        final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
-        mCanvas.setBitmap(b);
-
-        Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
-        float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
-                (h - padding) / (float) orig.getHeight());
-        int scaledWidth = (int) (scaleFactor * orig.getWidth());
-        int scaledHeight = (int) (scaleFactor * orig.getHeight());
-        Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
-
-        // center the image
-        dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
-
-        mCanvas.drawBitmap(orig, src, dst, null);
-        mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor,
-                clipAlpha);
-        mCanvas.setBitmap(null);
-
-        return b;
-    }
-
     public void startDrag(CellLayout.CellInfo cellInfo) {
         startDrag(cellInfo, false);
     }
@@ -2337,11 +2318,8 @@
             ItemInfo dragObject, DragPreviewProvider previewProvider) {
         child.clearFocus();
         child.setPressed(false);
+        mOutlineProvider = previewProvider;
 
-        // The outline is used to visualize where the item will land if dropped
-        mDragOutline = previewProvider.createDragOutline(mCanvas);
-
-        mLauncher.onDragStarted(child);
         // The drag bitmap follows the touch point around on the screen
         final Bitmap b = previewProvider.createDragBitmap(mCanvas);
         int halfPadding = previewProvider.previewPadding / 2;
@@ -2384,12 +2362,7 @@
                 dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset,
                 dragRect, scale, accessible);
         dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
-
         b.recycle();
-
-        if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
-            mLauncher.enterSpringLoadedDragMode();
-        }
         return dv;
     }
 
@@ -3180,7 +3153,7 @@
                     item.spanY, child, mTargetCell);
 
             if (!nearestDropOccupied) {
-                mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
+                mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
                         mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d);
             } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
                     && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
@@ -3325,7 +3298,7 @@
             }
 
             boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
-            mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
+            mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
                 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragObject);
         }
     }
@@ -3716,7 +3689,7 @@
                 && mDragInfo.cell != null) {
             mDragInfo.cell.setVisibility(VISIBLE);
         }
-        mDragOutline = null;
+        mOutlineProvider = null;
         mDragInfo = null;
 
         if (!isFlingToDelete) {
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index ff70279..0baa8f3 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -25,6 +25,7 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.shortcuts.DeepShortcutView;
 
 import java.util.ArrayList;
 
@@ -46,7 +47,10 @@
     @Override
     public boolean performAction(View host, ItemInfo item, int action) {
         if (action == ADD_TO_WORKSPACE) {
-            final ShortcutInfo info = (ShortcutInfo) item;
+            if (!(host.getParent() instanceof DeepShortcutView)) {
+                return false;
+            }
+            final ShortcutInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
             final int[] coordinates = new int[2];
             final long screenId = findSpaceOnWorkspace(item, coordinates);
             Runnable onComplete = new Runnable() {
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index b90c2fd..e078d9b 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.PreloadIconDrawable;
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.folder.FolderIcon;
 
 /**
@@ -45,6 +46,8 @@
     // The padding added to the drag view during the preview generation.
     public final int previewPadding;
 
+    public Bitmap gerenatedDragOutline;
+
     public DragPreviewProvider(View view) {
         mView = view;
 
@@ -118,6 +121,14 @@
         return b;
     }
 
+    public final void generateDragOutline(Canvas canvas) {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && gerenatedDragOutline != null) {
+            throw new RuntimeException("Drag outline generated twice");
+        }
+
+        gerenatedDragOutline = createDragOutline(canvas);
+    }
+
     /**
      * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
      * Responsibility for the bitmap is transferred to the caller.
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index b3f0c82..0d7ba1e 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -68,6 +68,17 @@
             return thisWorkProfile ? 1 : -1;
         }
 
-        return sCollator.compare(label, another.label);
+        int labelCompare = sCollator.compare(label, another.label);
+        if (labelCompare != 0) {
+            return labelCompare;
+        }
+
+        // If the label is same, put the smaller widget before the larger widget. If the area is
+        // also same, put the widget with smaller height before.
+        int thisArea = spanX * spanY;
+        int otherArea = another.spanX * another.spanY;
+        return thisArea == otherArea
+                ? Integer.compare(spanY, another.spanY)
+                : Integer.compare(thisArea, otherArea);
     }
 }
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 37b6d04..e7fc415 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -21,16 +21,19 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.IconCache;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LogAccelerateInterpolator;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer.UnbadgedShortcutInfo;
 import com.android.launcher3.util.PillRevealOutlineProvider;
 import com.android.launcher3.util.PillWidthRevealOutlineProvider;
 
@@ -48,6 +51,8 @@
     private View mIconView;
     private float mOpenAnimationProgress;
 
+    private UnbadgedShortcutInfo mInfo;
+
     public DeepShortcutView(Context context) {
         this(context, null, 0);
     }
@@ -87,10 +92,36 @@
         mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
     }
 
-    public void applyShortcutInfo(ShortcutInfo info) {
+    /** package private **/
+    void applyShortcutInfo(UnbadgedShortcutInfo info, DeepShortcutsContainer container) {
+        mInfo = info;
         IconCache cache = LauncherAppState.getInstance().getIconCache();
         mBubbleText.applyFromShortcutInfo(info, cache);
         mIconView.setBackground(mBubbleText.getIcon());
+
+        // Use the long label as long as it exists and fits.
+        CharSequence longLabel = info.mDetail.getLongLabel();
+        int availableWidth = mBubbleText.getWidth() - mBubbleText.getTotalPaddingLeft()
+                - mBubbleText.getTotalPaddingRight();
+        boolean usingLongLabel = !TextUtils.isEmpty(longLabel)
+                && mBubbleText.getPaint().measureText(longLabel.toString()) <= availableWidth;
+        mBubbleText.setText(usingLongLabel ? longLabel : info.mDetail.getShortLabel());
+
+        // TODO: Add the click handler to this view directly and not the child view.
+        mBubbleText.setOnClickListener(Launcher.getLauncher(getContext()));
+        mBubbleText.setOnLongClickListener(container);
+        mBubbleText.setOnTouchListener(container);
+    }
+
+    /**
+     * Returns the shortcut info that is suitable to be added on the homescreen
+     */
+    public ShortcutInfo getFinalInfo() {
+        ShortcutInfo badged = new ShortcutInfo(mInfo);
+        // Queue an update task on the worker thread. This ensures that the badged
+        // shortcut eventually gets its icon updated.
+        Launcher.getLauncher(getContext()).getModel().updateShortcutInfo(mInfo.mDetail, badged);
+        return badged;
     }
 
     public View getIconView() {
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index cfeccfc..4d10506 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -183,12 +183,8 @@
                 }
                 for (int i = 0; i < shortcuts.size(); i++) {
                     final ShortcutInfoCompat shortcut = shortcuts.get(i);
-                    final ShortcutInfo launcherShortcutInfo =
-                            new UnbadgedShortcutInfo(shortcut, mLauncher);
-                    CharSequence shortLabel = shortcut.getShortLabel();
-                    CharSequence longLabel = shortcut.getLongLabel();
-                    uiHandler.post(new UpdateShortcutChild(i, launcherShortcutInfo,
-                            shortLabel, longLabel));
+                    uiHandler.post(new UpdateShortcutChild(
+                            i, new UnbadgedShortcutInfo(shortcut, mLauncher)));
                 }
             }
         });
@@ -197,32 +193,17 @@
     /** Updates the child of this container at the given index based on the given shortcut info. */
     private class UpdateShortcutChild implements Runnable {
         private int mShortcutChildIndex;
-        private ShortcutInfo mShortcutChildInfo;
-        private CharSequence mShortLabel;
-        private CharSequence mLongLabel;
+        private UnbadgedShortcutInfo mShortcutChildInfo;
 
-        public UpdateShortcutChild(int shortcutChildIndex, ShortcutInfo shortcutChildInfo,
-                CharSequence shortLabel, CharSequence longLabel) {
+        public UpdateShortcutChild(int shortcutChildIndex, UnbadgedShortcutInfo shortcutChildInfo) {
             mShortcutChildIndex = shortcutChildIndex;
             mShortcutChildInfo = shortcutChildInfo;
-            mShortLabel = shortLabel;
-            mLongLabel = longLabel;
         }
 
         @Override
         public void run() {
-            DeepShortcutView shortcutViewContainer = getShortcutAt(mShortcutChildIndex);
-            shortcutViewContainer.applyShortcutInfo(mShortcutChildInfo);
-            BubbleTextView shortcutView = getShortcutAt(mShortcutChildIndex).getBubbleText();
-            // Use the long label as long as it exists and fits.
-            int availableWidth = shortcutView.getWidth() - shortcutView.getTotalPaddingLeft()
-                    - shortcutView.getTotalPaddingRight();
-            boolean usingLongLabel = !TextUtils.isEmpty(mLongLabel)
-                    && shortcutView.getPaint().measureText(mLongLabel.toString()) <= availableWidth;
-            shortcutView.setText(usingLongLabel ? mLongLabel : mShortLabel);
-            shortcutView.setOnClickListener(mLauncher);
-            shortcutView.setOnLongClickListener(DeepShortcutsContainer.this);
-            shortcutView.setOnTouchListener(DeepShortcutsContainer.this);
+            getShortcutAt(mShortcutChildIndex)
+                    .applyShortcutInfo(mShortcutChildInfo, DeepShortcutsContainer.this);
         }
     }
 
@@ -525,14 +506,7 @@
         // Return if global dragging is not enabled
         if (!mLauncher.isDraggingEnabled()) return false;
 
-        UnbadgedShortcutInfo unbadgedInfo = (UnbadgedShortcutInfo) v.getTag();
-        ShortcutInfo badged = new ShortcutInfo(unbadgedInfo);
-        // Queue an update task on the worker thread. This ensures that the badged
-        // shortcut eventually gets its icon updated.
-        mLauncher.getModel().updateShortcutInfo(unbadgedInfo.mDetail, badged);
-
         // Long clicked on a shortcut.
-
         mDeferContainerRemoval = true;
         DeepShortcutView sv = (DeepShortcutView) v.getParent();
         sv.setWillDrawIcon(false);
@@ -542,7 +516,7 @@
         mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
 
         DragView dv = mLauncher.getWorkspace().beginDragShared(
-                sv.getBubbleText(), this, false, badged,
+                sv.getBubbleText(), this, false, sv.getFinalInfo(),
                 new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift));
         dv.animateShift(-mIconShift.x, -mIconShift.y);
 
@@ -754,8 +728,8 @@
     /**
      * Extension of {@link ShortcutInfo} which does not badge the icons.
      */
-    private static class UnbadgedShortcutInfo extends ShortcutInfo {
-        private final ShortcutInfoCompat mDetail;
+    static class UnbadgedShortcutInfo extends ShortcutInfo {
+        public final ShortcutInfoCompat mDetail;
 
         public UnbadgedShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
             super(shortcutInfo, context);
diff --git a/src/com/android/launcher3/util/MultiStateAlphaController.java b/src/com/android/launcher3/util/MultiStateAlphaController.java
index 2a49361..956fc9e 100644
--- a/src/com/android/launcher3/util/MultiStateAlphaController.java
+++ b/src/com/android/launcher3/util/MultiStateAlphaController.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.util;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
@@ -33,6 +34,7 @@
     private final View mTargetView;
     private final float[] mAlphas;
     private final AccessibilityManager mAm;
+    private int mZeroAlphaListenerCount = 0;
 
     public MultiStateAlphaController(View view, int stateCount) {
         mTargetView = view;
@@ -44,12 +46,20 @@
 
     public void setAlphaAtIndex(float alpha, int index) {
         mAlphas[index] = alpha;
+        updateAlpha();
+    }
+
+    private void updateAlpha() {
+        // Only update the alpha if no zero-alpha animation is running.
+        if (mZeroAlphaListenerCount > 0) {
+            return;
+        }
         float finalAlpha = 1;
         for (float a : mAlphas) {
             finalAlpha = finalAlpha * a;
         }
         mTargetView.setAlpha(finalAlpha);
-        mTargetView.setVisibility(alpha > 0 ? View.VISIBLE
+        mTargetView.setVisibility(finalAlpha > 0 ? View.VISIBLE
                 : (mAm.isEnabled() ? View.GONE : View.INVISIBLE));
     }
 
@@ -58,9 +68,11 @@
      * to {@param finalAlpha}. Alphas at other index are not affected.
      */
     public Animator animateAlphaAtIndex(float finalAlpha, final int index) {
+        final ValueAnimator anim;
+
         if (Float.compare(finalAlpha, mAlphas[index]) == 0) {
             // Return a dummy animator to avoid null checks.
-            return ValueAnimator.ofFloat(0, 0);
+            anim = ValueAnimator.ofFloat(0, 0);
         } else {
             ValueAnimator animator = ValueAnimator.ofFloat(mAlphas[index], finalAlpha);
             animator.addUpdateListener(new AnimatorUpdateListener() {
@@ -70,7 +82,38 @@
                     setAlphaAtIndex(value, index);
                 }
             });
-            return animator;
+            anim = animator;
+        }
+
+        if (Float.compare(finalAlpha, 0f) == 0) {
+            // In case when any channel is animating to 0, and the current alpha is also 0, do not
+            // update alpha of the target view while the animation is running.
+            // We special case '0' because if any channel is set to 0, values of other
+            // channels do not matter.
+            anim.addListener(new ZeroAlphaAnimatorListener());
+        }
+        return anim;
+    }
+
+    private class ZeroAlphaAnimatorListener extends AnimatorListenerAdapter {
+
+        private boolean mStartedAtZero = false;
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mStartedAtZero = Float.compare(mTargetView.getAlpha(), 0f) == 0;
+            if (mStartedAtZero) {
+                mZeroAlphaListenerCount++;
+                mTargetView.setAlpha(0);
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (mStartedAtZero) {
+                mZeroAlphaListenerCount--;
+                updateAlpha();
+            }
         }
     }
 }
diff --git a/src/com/android/launcher3/widget/PendingItemPreviewProvider.java b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
new file mode 100644
index 0000000..8739390
--- /dev/null
+++ b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
@@ -0,0 +1,78 @@
+/*
+ * 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.widget;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.graphics.DragPreviewProvider;
+
+/**
+ * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
+ * dragged from the widget tray.
+ */
+public class PendingItemPreviewProvider extends DragPreviewProvider {
+
+    private final PendingAddItemInfo mAddInfo;
+    private final Bitmap mPreviewBitmap;
+
+    public PendingItemPreviewProvider(View view, PendingAddItemInfo addInfo, Bitmap preview) {
+        super(view);
+        mAddInfo = addInfo;
+        mPreviewBitmap = preview;
+    }
+
+    @Override
+    public Bitmap createDragOutline(Canvas canvas) {
+        Workspace workspace = Launcher.getLauncher(mView.getContext()).getWorkspace();
+        int[] size = workspace.estimateItemSize(mAddInfo, false);
+
+        int w = size[0];
+        int h = size[1];
+        final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+        canvas.setBitmap(b);
+
+        Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight());
+        float scaleFactor = Math.min((w - DRAG_BITMAP_PADDING) / (float) mPreviewBitmap.getWidth(),
+                (h - DRAG_BITMAP_PADDING) / (float) mPreviewBitmap.getHeight());
+        int scaledWidth = (int) (scaleFactor * mPreviewBitmap.getWidth());
+        int scaledHeight = (int) (scaleFactor * mPreviewBitmap.getHeight());
+        Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
+
+        // center the image
+        dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
+
+        canvas.drawBitmap(mPreviewBitmap, src, dst, null);
+
+        // Don't clip alpha values for the drag outline if we're using the default widget preview
+        boolean clipAlpha = !(mAddInfo instanceof PendingAddWidgetInfo &&
+                (((PendingAddWidgetInfo) mAddInfo).previewImage == 0));
+        final int outlineColor = mView.getResources().getColor(R.color.outline_color);
+        HolographicOutlineHelper.obtain(mView.getContext())
+                .applyExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor, clipAlpha);
+        canvas.setBitmap(null);
+
+        return b;
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 34bee1b..352cea4 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -205,7 +205,7 @@
 
         // Compose the drag image
         Bitmap preview;
-        float scale = 1f;
+        final float scale;
         final Rect bounds = image.getBitmapBounds();
 
         if (createItemInfo instanceof PendingAddWidgetInfo) {
@@ -242,19 +242,14 @@
             scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / preview.getWidth();
         }
 
-        // Don't clip alpha values for the drag outline if we're using the default widget preview
-        boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
-                (((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
+        // Since we are not going through the workspace for starting the drag, set drag related
+        // information on the workspace before starting the drag.
+        mLauncher.getWorkspace().prepareDragWithProvider(
+                new PendingItemPreviewProvider(v, createItemInfo, preview));
 
         // Start the drag
-        mLauncher.lockScreenOrientation();
         mDragController.startDrag(image, preview, this, createItemInfo,
                 bounds, DragController.DRAG_ACTION_COPY, scale);
-        // This call expects the extra empty screen to already be created, which is why we call it
-        // after mDragController.startDrag().
-        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
-
-        preview.recycle();
         return true;
     }