Updating the preview generation logic so that it aligns better with
the drag source image

> Using common code for pending item drag (WidgetContainerView and PinItemDragListener)
> Adding a shortcut-circuit in Workspace when a pendingItem can create a shortcut
  directly. Previously the multi-window drop was routing through onActivityResult which
  was causing some state information to be lost.

Bug: 33584624
Change-Id: I0259870032185713caa9bff27092dbae6ce91199
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 85b08d1..8d69fe3 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -229,7 +229,7 @@
             }
         };
         dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
-                mLauncher.getDragController().isExternalDrag() ? 1 : DRAG_VIEW_DROP_DURATION,
+                DRAG_VIEW_DROP_DURATION,
                 new DecelerateInterpolator(2),
                 new LinearInterpolator(), onAnimationEndRunnable,
                 DragLayer.ANIMATION_END_DISAPPEAR, null);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a1aafb8..11db9a0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -101,7 +101,6 @@
 import com.android.launcher3.dynamicui.ExtractedColors;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.keyboard.CustomActionsPopup;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.logging.FileLog;
@@ -111,7 +110,6 @@
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -836,7 +834,7 @@
     }
 
     @Override
-    public void onActivityResult(
+    protected void onActivityResult(
             final int requestCode, final int resultCode, final Intent data) {
         handleActivityResult(requestCode, resultCode, data);
         if (mLauncherCallbacks != null) {
@@ -1459,21 +1457,8 @@
 
         ShortcutInfo info = null;
         if (Utilities.isAtLeastO()) {
-            PinItemRequestCompat request = PinItemRequestCompat.getPinItemRequest(data);
-            // request.accept will initiate a shortcutChanged callback. To ensure that the model is
-            // consistent, that callback must be processed by the model, after the ShortcutInfo is
-            // added to the model. This is guaranteed here the callback comes on the UI thread, and
-            // we will add the shortcut on the UI thread as well.
-            if (request != null &&
-                    request.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT &&
-                    request.isValid() && request.accept()) {
-                ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
-                info = new ShortcutInfo(compat, this);
-                // Apply the unbadged icon and fetch the actual icon asynchronously.
-                info.iconBitmap = LauncherIcons
-                        .createShortcutIcon(compat, this, false /* badged */);
-                getModel().updateAndBindShortcutInfo(info, compat);
-            }
+            info = LauncherAppsCompat.createShortcutInfoFromPinItemRequest(
+                    this, PinItemRequestCompat.getPinItemRequest(data));
         }
 
         if (info == null) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index cd1d8d9..72dff66 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2523,7 +2523,7 @@
         if (d.dragSource != this) {
             final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
                     (int) mDragViewVisualCenter[1] };
-            onDropExternal(touchXY, d.dragInfo, dropTargetLayout, d);
+            onDropExternal(touchXY, dropTargetLayout, d);
         } else if (mDragInfo != null) {
             final View cell = mDragInfo.cell;
             boolean droppedOnOriginalCellDuringTransition = false;
@@ -3226,8 +3226,7 @@
      * NOTE: This can also be called when we are outside of a drag event, when we want
      * to add an item to one of the workspace screens.
      */
-    private void onDropExternal(final int[] touchXY, final ItemInfo dragInfo,
-            final CellLayout cellLayout, DragObject d) {
+    private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
         final Runnable exitSpringLoadedRunnable = new Runnable() {
             @Override
             public void run() {
@@ -3236,7 +3235,15 @@
             }
         };
 
-        ItemInfo info = dragInfo;
+        if (d.dragInfo instanceof PendingAddShortcutInfo) {
+            ShortcutInfo si = ((PendingAddShortcutInfo) d.dragInfo)
+                    .activityInfo.createShortcutInfo();
+            if (si != null) {
+                d.dragInfo = si;
+            }
+        }
+
+        ItemInfo info = d.dragInfo;
         int spanX = info.spanX;
         int spanY = info.spanY;
         if (mDragInfo != null) {
@@ -3255,7 +3262,7 @@
         }
 
         if (info instanceof PendingAddItemInfo) {
-            final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
+            final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) info;
 
             boolean findNearestVacantCell = true;
             if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
@@ -3318,7 +3325,7 @@
 
             int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
             if (isWidget && ((PendingAddWidgetInfo) pendingInfo).info != null &&
-                    ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
+                    ((PendingAddWidgetInfo) pendingInfo).getHandler().needsConfigure()) {
                 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
             }
             animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
@@ -3433,21 +3440,32 @@
                 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
         resetTransitionTransform(layout);
 
-        float dragViewScaleX = 1f;
-        float dragViewScaleY = 1f;
         if (scale) {
-            dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
-            dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
+            float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
+            float dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
+
+            // The animation will scale the dragView about its center, so we need to center about
+            // the final location.
+            loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2
+                    - Math.ceil(layout.getUnusedHorizontalSpace() / 2f);
+            loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
+            scaleXY[0] = dragViewScaleX * cellLayoutScale;
+            scaleXY[1] = dragViewScaleY * cellLayoutScale;
+        } else {
+            // Since we are not cross-fading the dragView, align the drag view to the
+            // final cell position.
+            float dragScale = dragView.getInitialScale() * cellLayoutScale;
+            loc[0] += (dragScale - 1) * dragView.getWidth() / 2;
+            loc[1] += (dragScale - 1) * dragView.getHeight() / 2;
+            scaleXY[0] = scaleXY[1] = dragScale;
+
+            // If a dragRegion was provided, offset the final position accordingly.
+            Rect dragRegion = dragView.getDragRegion();
+            if (dragRegion != null) {
+                loc[0] += cellLayoutScale * dragRegion.left;
+                loc[1] += cellLayoutScale * dragRegion.top;
+            }
         }
-
-        // The animation will scale the dragView about its center, so we need to center about
-        // the final location.
-        loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2
-                - Math.ceil(layout.getUnusedHorizontalSpace() / 2f);
-        loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
-
-        scaleXY[0] = dragViewScaleX * cellLayoutScale;
-        scaleXY[1] = dragViewScaleY * cellLayoutScale;
     }
 
     public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, final DragView dragView,
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 281069a..b9142ed 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -23,8 +23,12 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.support.annotation.Nullable;
 
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 
 import java.util.List;
@@ -75,4 +79,38 @@
     public abstract boolean isActivityEnabledForProfile(ComponentName component,
             UserHandle user);
     public abstract List<ShortcutConfigActivityInfo> getCustomShortcutActivityList();
+
+    /**
+     * request.accept() will initiate the following flow:
+     *      -> go-to-system-process for actual processing (a)
+     *      -> callback-to-launcher on UI thread (b)
+     *      -> post callback on the worker thread (c)
+     *      -> Update model and unpin (in system) any shortcut not in out model. (d)
+     *
+     * Note that (b) will take at-least one frame as it involves posting callback from binder
+     * thread to UI thread.
+     * If (d) happens before we add this shortcut to our model, we will end up unpinning
+     * the shortcut in the system.
+     * Here its the caller's responsibility to add the newly created ShortcutInfo immediately
+     * to the model (which may involves a single post-to-worker-thread). That will guarantee
+     * that (d) happens after model is updated.
+     */
+    @Nullable
+    public static ShortcutInfo createShortcutInfoFromPinItemRequest(
+            Context context, PinItemRequestCompat request) {
+        if (request != null &&
+                request.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT &&
+                request.isValid() && request.accept()) {
+            ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
+            ShortcutInfo info = new ShortcutInfo(compat, context);
+            // Apply the unbadged icon and fetch the actual icon asynchronously.
+            info.iconBitmap = LauncherIcons
+                    .createShortcutIcon(compat, context, false /* badged */);
+            LauncherAppState.getInstance(context).getModel()
+                    .updateAndBindShortcutInfo(info, compat);
+            return info;
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/compat/PinItemRequestCompat.java b/src/com/android/launcher3/compat/PinItemRequestCompat.java
index 481310a..f76b776 100644
--- a/src/com/android/launcher3/compat/PinItemRequestCompat.java
+++ b/src/com/android/launcher3/compat/PinItemRequestCompat.java
@@ -94,10 +94,6 @@
         parcel.writeParcelable(mObject, i);
     }
 
-    public Intent toIntent() {
-        return new Intent().putExtra(EXTRA_PIN_ITEM_REQUEST, mObject);
-    }
-
     public static final Parcelable.Creator<PinItemRequestCompat> CREATOR =
             new Parcelable.Creator<PinItemRequestCompat>() {
                 public PinItemRequestCompat createFromParcel(Parcel source) {
diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
index 1cfbd20..ebe95d6 100644
--- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
@@ -26,18 +26,15 @@
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
-import android.os.Bundle;
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
 import android.widget.Toast;
 
 import com.android.launcher3.IconCache;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.ShortcutInfo;
 
 import java.lang.reflect.Method;
 
@@ -68,7 +65,15 @@
 
     public abstract Drawable getFullResIcon(IconCache cache);
 
-    public boolean startConfigActivity(Launcher activity, int requestCode) {
+    /**
+     * Return a shortcut info, if it can be created directly on drop, without requiring any
+     * {@link #startConfigActivity(Activity, int)}.
+     */
+    public ShortcutInfo createShortcutInfo() {
+        return null;
+    }
+
+    public boolean startConfigActivity(Activity activity, int requestCode) {
         Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT)
                 .setComponent(getComponent());
         try {
@@ -137,7 +142,7 @@
         }
 
         @Override
-        public boolean startConfigActivity(Launcher activity, int requestCode) {
+        public boolean startConfigActivity(Activity activity, int requestCode) {
             if (getUser().equals(Process.myUserHandle())) {
                 return super.startConfigActivity(activity, requestCode);
             }
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 423fdab..b80baf3 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -117,11 +117,19 @@
     public boolean onLongClick(View view) {
         // Find the position of the preview relative to the touch location.
         WidgetImageView img = mWidgetCell.getWidgetView();
+
+        // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
+        // we abort the drag.
+        if (img.getBitmap() == null) {
+            return false;
+        }
+
         Rect bounds = img.getBitmapBounds();
         bounds.offset(img.getLeft() - (int) mLastTouchPos.x, img.getTop() - (int) mLastTouchPos.y);
 
         // Start home and pass the draw request params
-        PinItemDragListener listener = new PinItemDragListener(mRequest, bounds);
+        PinItemDragListener listener = new PinItemDragListener(mRequest, bounds,
+                img.getBitmap().getWidth(), img.getWidth());
         Intent homeIntent = new Intent(Intent.ACTION_MAIN)
                 .addCategory(Intent.CATEGORY_HOME)
                 .setPackage(getPackageName())
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 5a5e7d0..e31c8ff 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -121,28 +121,6 @@
     /**
      * Starts a drag.
      *
-     * @param v The view that is being dragged
-     * @param bmp The bitmap that represents the view being dragged
-     * @param source An object representing where the drag originated
-     * @param dragInfo The data associated with the object that is being dragged
-     * @param viewImageBounds the position of the image inside the view
-     */
-    public void startDrag(View v, Bitmap bmp, DragSource source, ItemInfo dragInfo,
-            Rect viewImageBounds, float initialDragViewScale, DragOptions options) {
-        int[] loc = mCoordinatesTemp;
-        mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
-        int dragLayerX = loc[0] + viewImageBounds.left
-                + (int) ((initialDragViewScale * bmp.getWidth() - bmp.getWidth()) / 2);
-        int dragLayerY = loc[1] + viewImageBounds.top
-                + (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2);
-
-        startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, null,
-                null, initialDragViewScale, options);
-    }
-
-    /**
-     * Starts a drag.
-     *
      * @param b The bitmap to display as the drag image.  It will be re-scaled to the
      *          enlarged size.
      * @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
@@ -259,10 +237,6 @@
         return mDragDriver != null || (mOptions != null && mOptions.isAccessibleDrag);
     }
 
-    public boolean isExternalDrag() {
-        return (mOptions != null && mOptions.systemDndStartPoint != null);
-    }
-
     /**
      * Stop dragging without dropping.
      */
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index de416e3..1be40f7 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -673,15 +673,11 @@
 
         // If duration < 0, this is a cue to compute the duration based on the distance
         if (duration < 0) {
-            if (mDragController != null && mDragController.isExternalDrag()) {
-                duration = 1;
-            } else {
-                duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
-                if (dist < maxDist) {
-                    duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
-                }
-                duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
+            duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
+            if (dist < maxDist) {
+                duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
             }
+            duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
         }
 
         // Fall back to cubic ease out interpolator for the animation if none is specified
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index c946235..3a98ae7 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -392,4 +392,8 @@
     public int getBlurSizeOutline() {
         return mBlurSizeOutline;
     }
+
+    public float getInitialScale() {
+        return mInitialScale;
+    }
 }
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 9770d40..6e5318f 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -17,9 +17,6 @@
 package com.android.launcher3.dragndrop;
 
 import android.content.ClipDescription;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
@@ -32,30 +29,25 @@
 import android.view.View;
 
 import com.android.launcher3.DeleteDropTarget;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.compat.PinItemRequestCompat;
 import com.android.launcher3.folder.Folder;
-import com.android.launcher3.graphics.LauncherIcons;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
-import com.android.launcher3.widget.PendingItemPreviewProvider;
+import com.android.launcher3.widget.PendingItemDragHelper;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
-import com.android.launcher3.widget.WidgetHostViewLoader;
 
 import java.util.UUID;
 
 /**
- * {@link DragSource} for handling drop from from a different window. This object is initialized
+ * {@link DragSource} for handling drop from a different window. This object is initialized
  * in the source window and is passed on to the Launcher activity as an Intent extra.
  */
 public class PinItemDragListener
@@ -71,6 +63,9 @@
     // Position of preview relative to the touch location
     private final Rect mPreviewRect;
 
+    private final int mPreviewBitmapWidth;
+    private final int mPreviewViewWidth;
+
     // Randomly generated id used to verify the drag event.
     private final String mId;
 
@@ -78,15 +73,20 @@
     private DragController mDragController;
     private long mDragStartTime;
 
-    public PinItemDragListener(PinItemRequestCompat request, Rect previewRect) {
+    public PinItemDragListener(PinItemRequestCompat request, Rect previewRect,
+            int previewBitmapWidth, int previewViewWidth) {
         mRequest = request;
         mPreviewRect = previewRect;
+        mPreviewBitmapWidth = previewBitmapWidth;
+        mPreviewViewWidth = previewViewWidth;
         mId = UUID.randomUUID().toString();
     }
 
     private PinItemDragListener(Parcel parcel) {
         mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel);
         mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
+        mPreviewBitmapWidth = parcel.readInt();
+        mPreviewViewWidth = parcel.readInt();
         mId = parcel.readString();
     }
 
@@ -103,6 +103,8 @@
     public void writeToParcel(Parcel parcel, int i) {
         mRequest.writeToParcel(parcel, i);
         mPreviewRect.writeToParcel(parcel, i);
+        parcel.writeInt(mPreviewBitmapWidth);
+        parcel.writeInt(mPreviewViewWidth);
         parcel.writeString(mId);
     }
 
@@ -139,26 +141,9 @@
         }
 
         final PendingAddItemInfo item;
-        final Bitmap preview;
-        final View view = new View(mLauncher);
-
-        Point dragShift = new Point(mPreviewRect.left, mPreviewRect.top);
         if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
             item = new PendingAddShortcutInfo(
                     new PinShortcutRequestActivityInfo(mRequest, mLauncher));
-
-            ShortcutInfoCompat compat = new ShortcutInfoCompat(mRequest.getShortcutInfo());
-            Bitmap icon = LauncherIcons.createShortcutIcon(compat, mLauncher, false /* badged */);
-
-            // Create a preview same as the workspace cell size and draw the icon at the
-            // appropriate position.
-            int[] size = mLauncher.getWorkspace().estimateItemSize(item, true, false);
-            preview = Bitmap.createBitmap(size[0], size[1], Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(preview);
-            DeviceProfile dp = mLauncher.getDeviceProfile();
-            c.drawBitmap(icon, (size[0] - icon.getWidth()) / 2,
-                    (size[1] - icon.getHeight() - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2,
-                    new Paint(Paint.FILTER_BITMAP_FLAG));
         } else {
             // mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_APPWIDGET
             LauncherAppWidgetProviderInfo providerInfo =
@@ -166,46 +151,28 @@
                             mLauncher, mRequest.getAppWidgetProviderInfo(mLauncher));
             final PinWidgetFlowHandler flowHandler =
                     new PinWidgetFlowHandler(providerInfo, mRequest);
-            PendingAddWidgetInfo info = new PendingAddWidgetInfo(providerInfo) {
+            item = new PendingAddWidgetInfo(providerInfo) {
                 @Override
                 public WidgetAddFlowHandler getHandler() {
                     return flowHandler;
                 }
             };
-            int[] size = mLauncher.getWorkspace().estimateItemSize(info, true, false);
-
-            float minScale = 1.25f;
-            int maxWidth = Math.min((int) (mPreviewRect.width() * minScale), size[0]);
-            int[] previewSizeBeforeScale = new int[1];
-            preview = LauncherAppState.getInstance(mLauncher).getWidgetCache()
-                    .generateWidgetPreview(mLauncher, info.info, maxWidth, null,
-                            previewSizeBeforeScale);
-
-            dragShift.offset(
-                    (mPreviewRect.width() - preview.getWidth()) / 2,
-                    (mPreviewRect.height() - preview.getHeight()) / 2);
-            item = info;
-
-            view.setTag(info);
-            mDragController.addDragListener(new WidgetHostViewLoader(mLauncher, view));
         }
-
-        PendingItemPreviewProvider previewProvider =
-                new PendingItemPreviewProvider(view, item, preview);
-
-        // 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(previewProvider);
+        View view = new View(mLauncher);
+        view.setTag(item);
 
         Point downPos = new Point((int) event.getX(), (int) event.getY());
         DragOptions options = new DragOptions();
         options.systemDndStartPoint = downPos;
         options.preDragCondition = this;
 
-        int x = downPos.x + dragShift.x;
-        int y = downPos.y + dragShift.y;
-        mDragController.startDrag(
-                preview, x, y, this, item, null, null, 1f, options);
+        // We use drag event position as the screenPos for the preview image. Since mPreviewRect
+        // already includes the view position relative to the drag event on the source window,
+        // and the absolute position (position relative to the screen) of drag event is same
+        // across windows, using drag position here give a good estimate for relative position
+        // to source window.
+        new PendingItemDragHelper(view).startDrag(new Rect(mPreviewRect),
+                mPreviewBitmapWidth, mPreviewViewWidth, downPos,  this, options);
         mDragStartTime = SystemClock.uptimeMillis();
         return true;
     }
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index 2121b43..6a8c19f 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -26,8 +26,8 @@
 import android.os.Build;
 
 import com.android.launcher3.IconCache;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PinItemRequestCompat;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
 
@@ -66,9 +66,13 @@
     }
 
     @Override
-    public boolean startConfigActivity(Launcher activity, int requestCode) {
-        activity.onActivityResult(requestCode, Activity.RESULT_OK, mRequest.toIntent());
-        return true;
+    public com.android.launcher3.ShortcutInfo createShortcutInfo() {
+        return LauncherAppsCompat.createShortcutInfoFromPinItemRequest(mContext, mRequest);
+    }
+
+    @Override
+    public boolean startConfigActivity(Activity activity, int requestCode) {
+        return false;
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
new file mode 100644
index 0000000..a4698c2
--- /dev/null
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -0,0 +1,191 @@
+/*
+ * 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.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.graphics.HolographicOutlineHelper;
+import com.android.launcher3.graphics.LauncherIcons;
+
+/**
+ * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
+ * dragged from the widget tray.
+ */
+public class PendingItemDragHelper extends DragPreviewProvider {
+
+    private static final float MAX_WIDGET_SCALE = 1.25f;
+
+    private final PendingAddItemInfo mAddInfo;
+
+    private Bitmap mPreviewBitmap;
+
+    public PendingItemDragHelper(View view) {
+        super(view);
+        mAddInfo = (PendingAddItemInfo) view.getTag();
+    }
+
+    /**
+     * Starts the drag for the pending item associated with the view.
+     *
+     * @param previewBounds The bounds where the image was displayed,
+     *                      {@link WidgetImageView#getBitmapBounds()}
+     * @param previewBitmapWidth The actual width of the bitmap displayed in the view.
+     * @param previewViewWidth The width of {@link WidgetImageView} displaying the preview
+     * @param screenPos Position of {@link WidgetImageView} on the screen
+     */
+    public void startDrag(Rect previewBounds, int previewBitmapWidth, int previewViewWidth,
+            Point screenPos, DragSource source, DragOptions options) {
+        final Launcher launcher = Launcher.getLauncher(mView.getContext());
+        LauncherAppState app = LauncherAppState.getInstance(launcher);
+
+        final Bitmap preview;
+        final float scale;
+        final Point dragOffset;
+        final Rect dragRegion;
+
+
+        if (mAddInfo instanceof PendingAddWidgetInfo) {
+            PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) mAddInfo;
+            int[] size = launcher.getWorkspace().estimateItemSize(createWidgetInfo, true, false);
+
+            int maxWidth = Math.min((int) (previewBitmapWidth * MAX_WIDGET_SCALE), size[0]);
+
+            int[] previewSizeBeforeScale = new int[1];
+            preview = app.getWidgetCache() .generateWidgetPreview(
+                    launcher, createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale);
+
+            if (previewSizeBeforeScale[0] < previewBitmapWidth) {
+                // The icon has extra padding around it.
+                int padding = (previewBitmapWidth - previewSizeBeforeScale[0]) / 2;
+                if (previewBitmapWidth > previewViewWidth) {
+                    padding = padding * previewViewWidth / previewBitmapWidth;
+                }
+
+                previewBounds.left += padding;
+                previewBounds.right -= padding;
+            }
+            scale = previewBounds.width() / (float) preview.getWidth();
+            launcher.getDragController().addDragListener(new WidgetHostViewLoader(launcher, mView));
+
+            dragOffset = null;
+            dragRegion = null;
+        } else {
+            PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
+            Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
+            preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher);
+            mAddInfo.spanX = mAddInfo.spanY = 1;
+            scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth();
+
+            dragOffset = new Point(previewPadding / 2, previewPadding / 2);
+
+            // Create a preview same as the workspace cell size and draw the icon at the
+            // appropriate position.
+            int[] size = launcher.getWorkspace().estimateItemSize(mAddInfo, false, true);
+            DeviceProfile dp = launcher.getDeviceProfile();
+            int iconSize = dp.iconSizePx;
+
+            dragRegion = new Rect();
+            dragRegion.left = (size[0] - iconSize) / 2;
+            dragRegion.right = dragRegion.left + iconSize;
+            dragRegion.top = (size[1] - iconSize - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2;
+            dragRegion.bottom = dragRegion.top + iconSize;
+        }
+
+        // Since we are not going through the workspace for starting the drag, set drag related
+        // information on the workspace before starting the drag.
+        launcher.getWorkspace().prepareDragWithProvider(this);
+
+        int dragLayerX = screenPos.x + previewBounds.left
+                + (int) ((scale * preview.getWidth() - preview.getWidth()) / 2);
+        int dragLayerY = screenPos.y + previewBounds.top
+                + (int) ((scale * preview.getHeight() - preview.getHeight()) / 2);
+
+        mPreviewBitmap = preview;
+        // Start the drag
+        launcher.getDragController().startDrag(preview, dragLayerX, dragLayerY, source, mAddInfo,
+                dragOffset, dragRegion, scale, options);
+    }
+
+
+    @Override
+    public Bitmap createDragOutline(Canvas canvas) {
+        if (mAddInfo instanceof PendingAddShortcutInfo) {
+            int width = mPreviewBitmap.getWidth();
+            int height = mPreviewBitmap.getHeight();
+            Bitmap b = Bitmap.createBitmap(width + blurSizeOutline, height + blurSizeOutline,
+                    Bitmap.Config.ALPHA_8);
+            canvas.setBitmap(b);
+
+            Launcher launcher = Launcher.getLauncher(mView.getContext());
+            int size = launcher.getDeviceProfile().iconSizePx;
+
+            Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight());
+            Rect dst = new Rect(0, 0, size, size);
+            dst.offset(blurSizeOutline / 2, blurSizeOutline / 2);
+            canvas.drawBitmap(mPreviewBitmap, src, dst, new Paint(Paint.FILTER_BITMAP_FLAG));
+
+            HolographicOutlineHelper.getInstance(mView.getContext())
+                    .applyExpensiveOutlineWithBlur(b, canvas);
+
+            canvas.setBitmap(null);
+            return b;
+        }
+
+        Workspace workspace = Launcher.getLauncher(mView.getContext()).getWorkspace();
+        int[] size = workspace.estimateItemSize(mAddInfo, false, false);
+
+        int w = size[0];
+        int h = size[1];
+        final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
+        canvas.setBitmap(b);
+
+        Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight());
+        float scaleFactor = Math.min((w - blurSizeOutline) / (float) mPreviewBitmap.getWidth(),
+                (h - blurSizeOutline) / (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));
+        HolographicOutlineHelper.getInstance(mView.getContext())
+                .applyExpensiveOutlineWithBlur(b, canvas, clipAlpha);
+        canvas.setBitmap(null);
+
+        return b;
+    }
+}
diff --git a/src/com/android/launcher3/widget/PendingItemPreviewProvider.java b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
deleted file mode 100644
index 3a49a6c..0000000
--- a/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.graphics.HolographicOutlineHelper;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.PendingAddItemInfo;
-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, false);
-
-        int w = size[0];
-        int h = size[1];
-        final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
-        canvas.setBitmap(b);
-
-        Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight());
-        float scaleFactor = Math.min((w - blurSizeOutline) / (float) mPreviewBitmap.getWidth(),
-                (h - blurSizeOutline) / (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));
-        HolographicOutlineHelper.getInstance(mView.getContext())
-                .applyExpensiveOutlineWithBlur(b, canvas, 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 d1421e0..b2321a7 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -17,9 +17,7 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+import android.graphics.Point;
 import android.support.v7.widget.LinearLayoutManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -35,17 +33,13 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
 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.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;
@@ -71,9 +65,6 @@
     /* Touch handling related member variables. */
     private Toast mWidgetInstructionToast;
 
-    /* Rendering related. */
-    private WidgetPreviewLoader mWidgetPreviewLoader;
-
     public WidgetsContainerView(Context context) {
         this(context, null);
     }
@@ -174,7 +165,6 @@
     private boolean beginDraggingWidget(WidgetCell v) {
         // Get the widget preview as the drag representation
         WidgetImageView image = (WidgetImageView) v.findViewById(R.id.widget_preview);
-        PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
 
         // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
         // we abort the drag.
@@ -182,55 +172,12 @@
             return false;
         }
 
-        // Compose the drag image
-        Bitmap preview;
-        final float scale;
-        final Rect bounds = image.getBitmapBounds();
+        int[] loc = new int[2];
+        mLauncher.getDragLayer().getLocationInDragLayer(image, loc);
 
-        if (createItemInfo instanceof PendingAddWidgetInfo) {
-            // This can happen in some weird cases involving multi-touch. We can't start dragging
-            // the widget if this is null, so we break out.
-
-            PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo;
-            int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true, false);
-
-            Bitmap icon = image.getBitmap();
-            float minScale = 1.25f;
-            int maxWidth = Math.min((int) (icon.getWidth() * minScale), size[0]);
-
-            int[] previewSizeBeforeScale = new int[1];
-            preview = getWidgetPreviewLoader().generateWidgetPreview(mLauncher,
-                    createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale);
-
-            if (previewSizeBeforeScale[0] < icon.getWidth()) {
-                // The icon has extra padding around it.
-                int padding = (icon.getWidth() - previewSizeBeforeScale[0]) / 2;
-                if (icon.getWidth() > image.getWidth()) {
-                    padding = padding * image.getWidth() / icon.getWidth();
-                }
-
-                bounds.left += padding;
-                bounds.right -= padding;
-            }
-            scale = bounds.width() / (float) preview.getWidth();
-
-            mLauncher.getDragController().addDragListener(new WidgetHostViewLoader(mLauncher, v));
-        } else {
-            PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
-            Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(mIconCache);
-            preview = LauncherIcons.createIconBitmap(icon, mLauncher);
-            createItemInfo.spanX = createItemInfo.spanY = 1;
-            scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / preview.getWidth();
-        }
-
-        // 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
-        mDragController.startDrag(image, preview, this, createItemInfo,
-                bounds, scale, new DragOptions());
+        new PendingItemDragHelper(v).startDrag(
+                image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(),
+                new Point(loc[0], loc[1]), this, new DragOptions());
         return true;
     }
 
@@ -294,13 +241,6 @@
         return mAdapter.getItemCount() == 0;
     }
 
-    private WidgetPreviewLoader getWidgetPreviewLoader() {
-        if (mWidgetPreviewLoader == null) {
-            mWidgetPreviewLoader = LauncherAppState.getInstance(getContext()).getWidgetCache();
-        }
-        return mWidgetPreviewLoader;
-    }
-
     @Override
     public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
         targetParent.containerType = ContainerType.WIDGETS;