Merge "Use notification icon views' tags to store NotificationInfo." into ub-launcher3-master
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 95a558a..0e8c313 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -33,7 +33,7 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"വിജറ്റ് തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d വീതിയും %2$d ഉയരവും"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ആപ്പ്‌സ് തിരയുക"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ആപ്പുകളെ തിരയുക"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ആപ്പ്‌സ് ലോഡുചെയ്യുന്നു..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്‌സൊന്നും കണ്ടെത്തിയില്ല"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക"</string>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 809fc6d..19bf51a 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -50,4 +50,7 @@
 
     <!-- Used as a fallback since colorSecondary doesn't exist pre-API 25 -->
     <color name="fallback_secondary_color">#FF37474F</color>
+
+    <color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
+    <color name="legacy_icon_background">#FFE0E0E0</color>
 </resources>
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/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index 58ad449..27aeaba 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -16,14 +16,22 @@
 
 package com.android.launcher3.graphics;
 
+import android.app.Notification;
+import android.content.Context;
 import android.graphics.Color;
 import android.support.v4.graphics.ColorUtils;
+import android.util.Log;
+
+import com.android.launcher3.R;
 
 /**
  * Contains colors based on the dominant color of an icon.
  */
 public class IconPalette {
 
+    private static final boolean DEBUG = false;
+    private static final String TAG = "IconPalette";
+
     public int backgroundColor;
     public int textColor;
     public int secondaryColor;
@@ -36,6 +44,100 @@
         return palette;
     }
 
+    /**
+     * Resolves a color such that it has enough contrast to be used as the
+     * color of an icon or text on the given background color.
+     *
+     * @return a color of the same hue with enough contrast against the background.
+     *
+     * This was copied from com.android.internal.util.NotificationColorUtil.
+     */
+    public static int resolveContrastColor(Context context, int color, int background) {
+        final int resolvedColor = resolveColor(context, color);
+
+        int contrastingColor = ensureTextContrast(resolvedColor, background);
+
+        if (contrastingColor != resolvedColor) {
+            if (DEBUG){
+                Log.w(TAG, String.format(
+                        "Enhanced contrast of notification for %s " +
+                                "%s (over background) by changing #%s to %s",
+                        context.getPackageName(),
+                        contrastChange(resolvedColor, contrastingColor, background),
+                        Integer.toHexString(resolvedColor), Integer.toHexString(contrastingColor)));
+            }
+        }
+        return contrastingColor;
+    }
+
+    /**
+     * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
+     *
+     * This was copied from com.android.internal.util.NotificationColorUtil.
+     */
+    private static int resolveColor(Context context, int color) {
+        if (color == Notification.COLOR_DEFAULT) {
+            return context.getColor(R.color.notification_icon_default_color);
+        }
+        return color;
+    }
+
+    /** For debugging. This was copied from com.android.internal.util.NotificationColorUtil. */
+    private static String contrastChange(int colorOld, int colorNew, int bg) {
+        return String.format("from %.2f:1 to %.2f:1",
+                ColorUtils.calculateContrast(colorOld, bg),
+                ColorUtils.calculateContrast(colorNew, bg));
+    }
+
+    /**
+     * Finds a text color with sufficient contrast over bg that has the same hue as the original
+     * color.
+     *
+     * This was copied from com.android.internal.util.NotificationColorUtil.
+     */
+    private static int ensureTextContrast(int color, int bg) {
+        return findContrastColor(color, bg, true, 4.5);
+    }
+    /**
+     * Finds a suitable color such that there's enough contrast.
+     *
+     * @param color the color to start searching from.
+     * @param other the color to ensure contrast against. Assumed to be lighter than {@param color}
+     * @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
+     * @param minRatio the minimum contrast ratio required.
+     * @return a color with the same hue as {@param color}, potentially darkened to meet the
+     *          contrast ratio.
+     *
+     * This was copied from com.android.internal.util.NotificationColorUtil.
+     */
+    private static int findContrastColor(int color, int other, boolean findFg, double minRatio) {
+        int fg = findFg ? color : other;
+        int bg = findFg ? other : color;
+        if (ColorUtils.calculateContrast(fg, bg) >= minRatio) {
+            return color;
+        }
+
+        double[] lab = new double[3];
+        ColorUtils.colorToLAB(findFg ? fg : bg, lab);
+
+        double low = 0, high = lab[0];
+        final double a = lab[1], b = lab[2];
+        for (int i = 0; i < 15 && high - low > 0.00001; i++) {
+            final double l = (low + high) / 2;
+            if (findFg) {
+                fg = ColorUtils.LABToColor(l, a, b);
+            } else {
+                bg = ColorUtils.LABToColor(l, a, b);
+            }
+            if (ColorUtils.calculateContrast(fg, bg) > minRatio) {
+                low = l;
+            } else {
+                high = l;
+            }
+        }
+        return ColorUtils.LABToColor(low, a, b);
+    }
+
     private static int getMutedColor(int color) {
         int alpha = (int) (255 * 0.15f);
         return ColorUtils.compositeColors(ColorUtils.setAlphaComponent(color, alpha), Color.WHITE);
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 2ae7a4a..472b913 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -29,10 +29,13 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.PaintDrawable;
+import android.graphics.drawable.ScaleDrawable;
 import android.os.Process;
 import android.os.UserHandle;
+import android.view.Gravity;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.IconCache;
@@ -45,12 +48,12 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 
-import java.lang.reflect.Method;
-
 /**
  * Helper methods for generating various launcher icons
  */
 public class LauncherIcons {
+    // TODO b/33553066 use the constant defined in MaskableIconDrawable
+    private static final float LEGACY_ICON_SCALE = .7f * .6667f;
 
     private static final Rect sOldBounds = new Rect();
     private static final Canvas sCanvas = new Canvas();
@@ -168,7 +171,7 @@
      * @param scale the scale to apply before drawing {@param icon} on the canvas
      */
     public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
-        icon = castToMaskableIconDrawable(icon);
+        icon = wrapToMaskableIconDrawable(context, icon);
         synchronized (sCanvas) {
             final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
 
@@ -224,17 +227,31 @@
         }
     }
 
-    static Drawable castToMaskableIconDrawable(Drawable drawable) {
+    /**
+     * If the platform is running O but the app is not providing MaskableIconDrawable, then
+     * shrink the legacy icon and set it as foreground. Use color drawable as background to
+     * create MaskableIconDrawable.
+     */
+    static Drawable wrapToMaskableIconDrawable(Context context, Drawable drawable) {
         if (!(ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isAtLeastO())) {
             return drawable;
         }
+        int color = context.getResources().getColor(R.color.legacy_icon_background);
+        ColorDrawable colorDrawable = new ColorDrawable(color);
+        ScaleDrawable scaleDrawable = new ScaleDrawable(drawable,
+                Gravity.CENTER, LEGACY_ICON_SCALE, LEGACY_ICON_SCALE);
+        scaleDrawable.setLevel(1);
         try {
             Class clazz = Class.forName("android.graphics.drawable.MaskableIconDrawable");
-            Method method = clazz.getDeclaredMethod("wrap", Drawable.class);
-            return (Drawable) method.invoke(null, drawable);
+            if (!clazz.isAssignableFrom(drawable.getClass())){
+
+                return (Drawable) clazz.getConstructor(Drawable.class, Drawable.class)
+                        .newInstance(colorDrawable, scaleDrawable);
+            }
         } catch (Exception e) {
             return drawable;
         }
+        return drawable;
     }
 
     public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context) {
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index f84ddb1..58789f6 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -58,6 +58,7 @@
 
     LinearLayout.LayoutParams mIconLayoutParams;
     private LinearLayout mIconRow;
+    private int mBackgroundColor;
     private int mTextColor;
 
     public NotificationFooterLayout(Context context) {
@@ -87,7 +88,8 @@
     }
 
     public void applyColors(IconPalette iconPalette) {
-        setBackgroundTintList(ColorStateList.valueOf(iconPalette.backgroundColor));
+        mBackgroundColor = iconPalette.backgroundColor;
+        setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
         findViewById(R.id.divider).setBackgroundColor(iconPalette.secondaryColor);
         mTextColor = iconPalette.textColor;
     }
@@ -126,7 +128,7 @@
 
     private void addNotificationIconForInfo(NotificationInfo info, boolean fromOverflow) {
         View icon = new View(getContext());
-        icon.setBackground(info.iconDrawable);
+        icon.setBackground(info.getIconForBackground(getContext(), mBackgroundColor));
         icon.setOnClickListener(info);
         int addIndex = mIconRow.getChildCount();
         if (fromOverflow) {
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index bf57b2a..af5e817 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -25,6 +25,7 @@
 import android.view.View;
 
 import com.android.launcher3.Launcher;
+import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -41,11 +42,14 @@
     public final String notificationKey;
     public final CharSequence title;
     public final CharSequence text;
-    public final Drawable iconDrawable;
     public final PendingIntent intent;
     public final boolean autoCancel;
     public final boolean dismissable;
 
+    private final Drawable mIconDrawable;
+    private boolean mShouldTintIcon;
+    private int mIconColor;
+
     /**
      * Extracts the data that we need from the StatusBarNotification.
      */
@@ -60,10 +64,12 @@
         Icon icon = notification.getLargeIcon();
         if (icon == null) {
             icon = notification.getSmallIcon();
-            iconDrawable = icon.loadDrawable(context);
-            iconDrawable.setTint(statusBarNotification.getNotification().color);
+            mIconDrawable = icon.loadDrawable(context);
+            mIconColor = statusBarNotification.getNotification().color;
+            mShouldTintIcon = true;
         } else {
-            iconDrawable = icon.loadDrawable(context);
+            mIconDrawable = icon.loadDrawable(context);
+            mShouldTintIcon = false;
         }
         intent = notification.contentIntent;
         autoCancel = (notification.flags & Notification.FLAG_AUTO_CANCEL) != 0;
@@ -83,4 +89,18 @@
         }
         PopupContainerWithArrow.getOpen(launcher).close(true);
     }
+
+    public Drawable getIconForBackground(Context context, int background) {
+        if (!mShouldTintIcon) {
+            return mIconDrawable;
+        }
+        mIconColor = IconPalette.resolveContrastColor(context, mIconColor, background);
+        Drawable icon = mIconDrawable.mutate();
+        // DrawableContainer ignores the color filter if it's already set, so clear it first to
+        // get it set and invalidated properly.
+        icon.setTintList(null);
+        icon.setTint(mIconColor);
+        mShouldTintIcon = false;
+        return icon;
+    }
 }
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index b74cd4e..422722d 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -35,7 +35,9 @@
 import com.android.launcher3.popup.PopupItemView;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import static com.android.launcher3.LauncherAnimUtils.animateViewHeight;
 
@@ -117,7 +119,7 @@
         mHeader.setBackgroundTintList(ColorStateList.valueOf(iconPalette.backgroundColor));
         mHeader.setTextColor(ColorStateList.valueOf(iconPalette.textColor));
         mDivider.setBackgroundColor(iconPalette.secondaryColor);
-        mMainView.setBackgroundColor(iconPalette.backgroundColor);
+        mMainView.applyColors(iconPalette);
         mFooter.applyColors(iconPalette);
     }
 
@@ -135,7 +137,7 @@
                 @Override
                 public void onIconAnimationEnd(NotificationInfo newMainNotification) {
                     if (newMainNotification != null) {
-                        mMainView.applyNotificationInfo(newMainNotification, mIconView, mIconPalette);
+                        mMainView.applyNotificationInfo(newMainNotification, mIconView, true);
                         // Remove the animated notification from the footer by calling trim
                         // TODO: Remove the notification in NotificationFooterLayout directly
                         // instead of relying on this hack.
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 2997d40..76a84b7 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -41,6 +41,7 @@
     private NotificationInfo mNotificationInfo;
     private TextView mTitleView;
     private TextView mTextView;
+    private IconPalette mIconPalette;
 
     public NotificationMainView(Context context) {
         this(context, null, 0);
@@ -62,35 +63,38 @@
         mTextView = (TextView) findViewById(R.id.text);
     }
 
+    public void applyColors(IconPalette iconPalette) {
+        setBackgroundColor(iconPalette.backgroundColor);
+        mIconPalette = iconPalette;
+    }
+
     public void applyNotificationInfo(NotificationInfo mainNotification, View iconView) {
-        applyNotificationInfo(mainNotification, iconView, null);
+        applyNotificationInfo(mainNotification, iconView, false);
     }
 
     /**
-     * @param iconPalette if not null, indicates that the new info should be animated in,
-     *                    and that part of this animation includes animating the background
-     *                    from iconPalette.secondaryColor to iconPalette.backgroundColor.
+     * Sets the content of this view, animating it after a new icon shifts up if necessary.
      */
     public void applyNotificationInfo(NotificationInfo mainNotification, View iconView,
-            @Nullable IconPalette iconPalette) {
-        boolean animate = iconPalette != null;
+           boolean animate) {
         if (animate) {
             mTitleView.setAlpha(0);
             mTextView.setAlpha(0);
-            setBackgroundColor(iconPalette.secondaryColor);
+            setBackgroundColor(mIconPalette.secondaryColor);
         }
         mNotificationInfo = mainNotification;
         mTitleView.setText(mNotificationInfo.title);
         mTextView.setText(mNotificationInfo.text);
-        iconView.setBackground(mNotificationInfo.iconDrawable);
+        iconView.setBackground(mNotificationInfo.getIconForBackground(
+                getContext(), mIconPalette.backgroundColor));
         setOnClickListener(mNotificationInfo);
         setTranslationX(0);
         if (animate) {
             AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
             Animator textFade = new LauncherViewPropertyAnimator(mTextView).alpha(1);
             Animator titleFade = new LauncherViewPropertyAnimator(mTitleView).alpha(1);
-            ValueAnimator colorChange = ValueAnimator.ofArgb(iconPalette.secondaryColor,
-                    iconPalette.backgroundColor);
+            ValueAnimator colorChange = ValueAnimator.ofArgb(mIconPalette.secondaryColor,
+                    mIconPalette.backgroundColor);
             colorChange.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator valueAnimator) {
diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
index d1cfe42..9aabfeb 100644
--- a/src/com/android/launcher3/util/SQLiteCacheHelper.java
+++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -9,6 +9,9 @@
 import android.database.sqlite.SQLiteOpenHelper;
 import android.util.Log;
 
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.ProviderConfig;
+
 /**
  * An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB.
  * Any exception during write operations are ignored, and any version change causes a DB reset.
@@ -16,12 +19,18 @@
 public abstract class SQLiteCacheHelper {
     private static final String TAG = "SQLiteCacheHelper";
 
+    private static final boolean NO_ICON_CACHE = ProviderConfig.IS_DOGFOOD_BUILD &&
+            Utilities.isPropertyEnabled("MEMORY_ONLY_ICON_CACHE");
+
     private final String mTableName;
     private final MySQLiteOpenHelper mOpenHelper;
 
     private boolean mIgnoreWrites;
 
     public SQLiteCacheHelper(Context context, String name, int version, String tableName) {
+        if (NO_ICON_CACHE) {
+            name = null;
+        }
         mTableName = tableName;
         mOpenHelper = new MySQLiteOpenHelper(context, name, version);
 
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;