Merge changes from topic "am-01ff3d34-0eea-44d5-99f7-af2df518afb2" into ub-launcher3-master

* changes:
  [automerger] Fix bug where labels were visible in hotseat / invisible in workspace. am: f1eae802b5
  Fix bug where labels were visible in hotseat / invisible in workspace.
diff --git a/res/layout/gradient_bg.xml b/res/layout/gradient_bg.xml
index db448d7..6c6626c 100644
--- a/res/layout/gradient_bg.xml
+++ b/res/layout/gradient_bg.xml
@@ -20,5 +20,4 @@
     android:id="@+id/gradient_bg"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:visibility="gone"
     launcher:layout_ignoreInsets="true" />
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 485125e..cd72fba 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -42,6 +42,7 @@
 
 import com.android.launcher3.IconCache.IconLoadRequest;
 import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.Launcher.OnResumeCallback;
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.badge.BadgeRenderer;
 import com.android.launcher3.folder.FolderIcon;
@@ -59,7 +60,7 @@
  * because we want to make the bubble taller than the text and TextView's clip is
  * too aggressive.
  */
-public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
+public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback {
 
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
@@ -330,6 +331,13 @@
         refreshDrawableState();
     }
 
+    @Override
+    public void onLauncherResume() {
+        // Reset the pressed state of icon that was locked in the press state while activity
+        // was launching
+        setStayPressed(false);
+    }
+
     void clearPressedBackground() {
         setPressed(false);
         setStayPressed(false);
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 3759300..ffc2b02 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -215,7 +215,7 @@
      * On drop animate the dropView to the icon.
      */
     @Override
-    public void onDrop(final DragObject d) {
+    public void onDrop(final DragObject d, final DragOptions options) {
         final DragLayer dragLayer = mLauncher.getDragLayer();
         final Rect from = new Rect();
         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 7d047d7..2307b89 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -19,6 +19,7 @@
 import android.graphics.Rect;
 
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 
 /**
@@ -106,7 +107,7 @@
     /**
      * Handle an object being dropped on the DropTarget
      */
-    void onDrop(DragObject dragObject);
+    void onDrop(DragObject dragObject, DragOptions options);
 
     void onDragEnter(DragObject dragObject);
 
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index eb6a704..f78cde5 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -49,28 +49,29 @@
     }
 
     @Override
-    public void completeDrop(DragObject d) {
-        DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
-                ? (DropTargetResultCallback) d.dragSource : null;
-        startDetailsActivityForInfo(d.dragInfo, mLauncher, callback);
+    protected ComponentName performDropAction(DragObject d) {
+        return performDropAction(mLauncher, d.dragInfo, null, null);
     }
 
     /**
      * @return Whether the activity was started.
      */
     public static boolean startDetailsActivityForInfo(
-            ItemInfo info, Launcher launcher, DropTargetResultCallback callback) {
-        return startDetailsActivityForInfo(info, launcher, callback, null, null);
+            ItemInfo info, Launcher launcher, Rect sourceBounds, Bundle opts) {
+        return performDropAction(launcher, info, sourceBounds, opts) != null;
     }
 
-    public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher,
-            DropTargetResultCallback callback, Rect sourceBounds, Bundle opts) {
+    /**
+     * Performs the drop action and returns the target component for the dragObject or null if
+     * the action was not performed.
+     */
+    private static ComponentName performDropAction(Context context, ItemInfo info,
+            Rect sourceBounds, Bundle opts) {
         if (info instanceof PromiseAppInfo) {
             PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
-            launcher.startActivity(promiseAppInfo.getMarketIntent());
-            return true;
+            context.startActivity(promiseAppInfo.getMarketIntent());
+            return null;
         }
-        boolean result = false;
         ComponentName componentName = null;
         if (info instanceof AppInfo) {
             componentName = ((AppInfo) info).componentName;
@@ -83,19 +84,15 @@
         }
         if (componentName != null) {
             try {
-                LauncherAppsCompat.getInstance(launcher)
+                LauncherAppsCompat.getInstance(context)
                         .showAppDetailsForProfile(componentName, info.user, sourceBounds, opts);
-                result = true;
+                return componentName;
             } catch (SecurityException | ActivityNotFoundException e) {
-                Toast.makeText(launcher, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+                Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
                 Log.e(TAG, "Unable to launch settings", e);
             }
         }
-
-        if (callback != null) {
-            sendUninstallResult(launcher, result, componentName, info.user, callback);
-        }
-        return result;
+        return null;
     }
 
     @Override
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 0370777..df1eec6 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -27,7 +27,9 @@
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.UserHandle;
@@ -61,6 +63,9 @@
 
 public class InstallShortcutReceiver extends BroadcastReceiver {
 
+    private static final int MSG_ADD_TO_QUEUE = 1;
+    private static final int MSG_FLUSH_QUEUE = 2;
+
     public static final int FLAG_ACTIVITY_PAUSED = 1;
     public static final int FLAG_LOADER_RUNNING = 2;
     public static final int FLAG_DRAG_AND_DROP = 4;
@@ -93,73 +98,98 @@
     public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
     public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
 
-    private static final Object sLock = new Object();
+    private static final Handler sHandler = new Handler(LauncherModel.getWorkerLooper()) {
 
-    private static void addToInstallQueue(
-            SharedPreferences sharedPrefs, PendingInstallShortcutInfo info) {
-        synchronized(sLock) {
-            String encoded = info.encodeToString();
-            if (encoded != null) {
-                Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
-                strings = (strings != null) ? new HashSet<>(strings) : new HashSet<String>(1);
-                strings.add(encoded);
-                sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ADD_TO_QUEUE: {
+                    Pair<Context, PendingInstallShortcutInfo> pair =
+                            (Pair<Context, PendingInstallShortcutInfo>) msg.obj;
+                    String encoded = pair.second.encodeToString();
+                    SharedPreferences prefs = Utilities.getPrefs(pair.first);
+                    Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
+                    strings = (strings != null) ? new HashSet<>(strings) : new HashSet<String>(1);
+                    strings.add(encoded);
+                    prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
+                    return;
+                }
+                case MSG_FLUSH_QUEUE: {
+                    Context context = (Context) msg.obj;
+                    LauncherModel model = LauncherAppState.getInstance(context).getModel();
+                    if (model.getCallback() == null) {
+                        // Launcher not loaded
+                        return;
+                    }
+
+                    ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
+                    SharedPreferences prefs = Utilities.getPrefs(context);
+                    Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
+                    if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
+                    if (strings == null) {
+                        return;
+                    }
+
+                    LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+                    for (String encoded : strings) {
+                        PendingInstallShortcutInfo info = decode(encoded, context);
+                        if (info == null) {
+                            continue;
+                        }
+
+                        String pkg = getIntentPackage(info.launchIntent);
+                        if (!TextUtils.isEmpty(pkg)
+                                && !launcherApps.isPackageEnabledForProfile(pkg, info.user)) {
+                            if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
+                                    + info.launchIntent);
+                            continue;
+                        }
+
+                        // Generate a shortcut info to add into the model
+                        installQueue.add(info.getItemInfo());
+                    }
+                    prefs.edit().remove(APPS_PENDING_INSTALL).apply();
+                    if (!installQueue.isEmpty()) {
+                        model.addAndBindAddedWorkspaceItems(installQueue);
+                    }
+                    return;
+                }
             }
         }
-    }
+    };
 
     public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
             UserHandle user) {
         if (packageNames.isEmpty()) {
             return;
         }
+        Preconditions.assertWorkerThread();
+
         SharedPreferences sp = Utilities.getPrefs(context);
-        synchronized(sLock) {
-            Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
-            if (DBG) {
-                Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
-                        + ", removing packages: " + packageNames);
-            }
-            if (Utilities.isEmpty(strings)) {
-                return;
-            }
-            Set<String> newStrings = new HashSet<>(strings);
-            Iterator<String> newStringsIter = newStrings.iterator();
-            while (newStringsIter.hasNext()) {
-                String encoded = newStringsIter.next();
-                try {
-                    Decoder decoder = new Decoder(encoded, context);
-                    if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) &&
-                            user.equals(decoder.user)) {
-                        newStringsIter.remove();
-                    }
-                } catch (JSONException | URISyntaxException e) {
-                    Log.d(TAG, "Exception reading shortcut to add: " + e);
+        Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
+        if (DBG) {
+            Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
+                    + ", removing packages: " + packageNames);
+        }
+        if (Utilities.isEmpty(strings)) {
+            return;
+        }
+        Set<String> newStrings = new HashSet<>(strings);
+        Iterator<String> newStringsIter = newStrings.iterator();
+        while (newStringsIter.hasNext()) {
+            String encoded = newStringsIter.next();
+            try {
+                Decoder decoder = new Decoder(encoded, context);
+                if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) &&
+                        user.equals(decoder.user)) {
                     newStringsIter.remove();
                 }
+            } catch (JSONException | URISyntaxException e) {
+                Log.d(TAG, "Exception reading shortcut to add: " + e);
+                newStringsIter.remove();
             }
-            sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
         }
-    }
-
-    private static ArrayList<PendingInstallShortcutInfo> getAndClearInstallQueue(Context context) {
-        SharedPreferences sharedPrefs = Utilities.getPrefs(context);
-        synchronized(sLock) {
-            ArrayList<PendingInstallShortcutInfo> infos = new ArrayList<>();
-            Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
-            if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
-            if (strings == null) {
-                return infos;
-            }
-            for (String encoded : strings) {
-                PendingInstallShortcutInfo info = decode(encoded, context);
-                if (info != null) {
-                    infos.add(info);
-                }
-            }
-            sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet<String>()).apply();
-            return infos;
-        }
+        sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
     }
 
     public void onReceive(Context context, Intent data) {
@@ -256,7 +286,7 @@
 
     private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
         // Queue the item up for adding if launcher has not loaded properly yet
-        addToInstallQueue(Utilities.getPrefs(context), info);
+        Message.obtain(sHandler, MSG_ADD_TO_QUEUE, Pair.create(context, info)).sendToTarget();
         flushInstallQueue(context);
     }
 
@@ -269,17 +299,10 @@
     }
 
     static void flushInstallQueue(Context context) {
-        LauncherModel model = LauncherAppState.getInstance(context).getModel();
-        boolean launcherNotLoaded = model.getCallback() == null;
-        if (sInstallQueueDisabledFlags != 0 || launcherNotLoaded) {
+        if (sInstallQueueDisabledFlags != 0) {
             return;
         }
-
-        ArrayList<PendingInstallShortcutInfo> items = getAndClearInstallQueue(context);
-        if (!items.isEmpty()) {
-            model.addAndBindAddedWorkspaceItems(
-                    new LazyShortcutsProvider(context.getApplicationContext(), items));
-        }
+        Message.obtain(sHandler, MSG_FLUSH_QUEUE, context.getApplicationContext()).sendToTarget();
     }
 
     /**
@@ -601,42 +624,6 @@
         return new PendingInstallShortcutInfo(info, original.mContext);
     }
 
-    private static class LazyShortcutsProvider extends Provider<List<Pair<ItemInfo, Object>>> {
-
-        private final Context mContext;
-        private final ArrayList<PendingInstallShortcutInfo> mPendingItems;
-
-        public LazyShortcutsProvider(Context context, ArrayList<PendingInstallShortcutInfo> items) {
-            mContext = context;
-            mPendingItems = items;
-        }
-
-        /**
-         * This must be called on the background thread as this requires multiple calls to
-         * packageManager and icon cache.
-         */
-        @Override
-        public ArrayList<Pair<ItemInfo, Object>> get() {
-            Preconditions.assertNonUiThread();
-            ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
-            LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
-            for (PendingInstallShortcutInfo pendingInfo : mPendingItems) {
-                // If the intent specifies a package, make sure the package exists
-                String packageName = getIntentPackage(pendingInfo.launchIntent);
-                if (!TextUtils.isEmpty(packageName) && !launcherApps.isPackageEnabledForProfile(
-                        packageName, pendingInfo.user)) {
-                    if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
-                            + pendingInfo.launchIntent);
-                    continue;
-                }
-
-                // Generate a shortcut info to add into the model
-                installQueue.add(pendingInfo.getItemInfo());
-            }
-            return installQueue;
-        }
-    }
-
     private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) {
         Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
         String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ba31926..2945b22 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -119,7 +119,6 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.util.ActivityResultInfo;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -203,7 +202,7 @@
     static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
 
     /** The different states that Launcher can be in. */
-    enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED,
+    enum State { WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED,
         WIDGETS, WIDGETS_SPRING_LOADED }
 
     @Thunk State mState = State.WORKSPACE;
@@ -250,11 +249,6 @@
     // that results in widgets being inflated in the wrong orientation.
     private int mOrientation;
 
-    // We set the state in both onCreate and then onNewIntent in some cases, which causes both
-    // scroll issues (because the workspace may not have been measured yet) and extra work.
-    // Instead, just save the state that we need to restore Launcher to, and commit it in onResume.
-    private State mOnResumeState = State.NONE;
-
     private SpannableStringBuilder mDefaultKeySsb = null;
 
     @Thunk boolean mWorkspaceLoading = true;
@@ -263,7 +257,8 @@
     private boolean mOnResumeNeedsLoad;
 
     private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
-    private final ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<>();
+    private OnResumeCallback mOnResumeCallback;
+
     private ViewOnDrawExecutor mPendingExecutor;
 
     private LauncherModel mModel;
@@ -288,11 +283,6 @@
     // it from the context.
     private SharedPreferences mSharedPrefs;
 
-    // This is set to the view that launched the activity that navigated the user away from
-    // launcher. Since there is no callback for when the activity has finished launching, enable
-    // the press state and keep this reference to reset the press state when we return to launcher.
-    private BubbleTextView mWaitingForResume;
-
     // Exiting spring loaded mode happens with a delay. This runnable object triggers the
     // state transition. If another state transition happened during this delay,
     // simply unregister this runnable.
@@ -484,13 +474,6 @@
 
     private LauncherCallbacks mLauncherCallbacks;
 
-    public void onPostCreate(Bundle savedInstanceState) {
-        super.onPostCreate(savedInstanceState);
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onPostCreate(savedInstanceState);
-        }
-    }
-
     public void onInsetsChanged(Rect insets) {
         mDeviceProfile.updateInsets(insets);
         mDeviceProfile.layout(this, true /* notifyListeners */);
@@ -855,24 +838,10 @@
     @Override
     protected void onResume() {
         TraceHelper.beginSection("ON_RESUME");
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.preOnResume();
-        }
         super.onResume();
         TraceHelper.partitionSection("ON_RESUME", "superCall");
 
         getUserEventDispatcher().resetElapsedSessionMillis();
-
-        // Restore the previous launcher state
-        if (mOnResumeState == State.WORKSPACE) {
-            showWorkspace(false);
-        } else if (mOnResumeState == State.APPS) {
-            showAppsView(false /* animated */);
-        } else if (mOnResumeState == State.WIDGETS) {
-            showWidgetsView(false, false);
-        }
-        mOnResumeState = State.NONE;
-
         mPaused = false;
         if (mOnResumeNeedsLoad) {
             setWorkspaceLoading(true);
@@ -887,22 +856,8 @@
             }
             mBindOnResumeCallbacks.clear();
         }
-        if (mOnResumeCallbacks.size() > 0) {
-            for (int i = 0; i < mOnResumeCallbacks.size(); i++) {
-                mOnResumeCallbacks.get(i).run();
-            }
-            mOnResumeCallbacks.clear();
-        }
 
-        // Reset the pressed state of icons that were locked in the press state while activities
-        // were launching
-        if (mWaitingForResume != null) {
-            // Resets the previous workspace icon press state
-            mWaitingForResume.setStayPressed(false);
-        }
-
-        updateInteraction(Workspace.State.NORMAL, mWorkspace.getState());
-
+        setOnResumeCallback(null);
         // Process any items that were added while Launcher was away.
         InstallShortcutReceiver.disableAndFlushInstallQueue(
                 InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
@@ -1086,8 +1041,10 @@
         State[] stateValues = State.values();
         State state = (stateOrdinal >= 0 && stateOrdinal < stateValues.length)
                 ? stateValues[stateOrdinal] : State.WORKSPACE;
-        if (state == State.APPS || state == State.WIDGETS) {
-            mOnResumeState = state;
+        if (state == State.APPS) {
+            showAppsView(false /* animated */);
+        } else if (state == State.WIDGETS) {
+            showWidgetsView(false, false);
         }
 
         PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS);
@@ -1499,15 +1456,7 @@
 
             // In all these cases, only animate if we're already on home
             AbstractFloatingView.closeAllOpenViews(this, alreadyOnHome);
-            exitSpringLoadedDragMode();
-
-            // If we are already on home, then just animate back to the workspace,
-            // otherwise, just wait until onResume to set the state back to Workspace
-            if (alreadyOnHome) {
-                showWorkspace(true);
-            } else {
-                mOnResumeState = State.WORKSPACE;
-            }
+            showWorkspace(alreadyOnHome /* animated */);
 
             final View v = getWindow().peekDecorView();
             if (v != null && v.getWindowToken() != null) {
@@ -1538,11 +1487,7 @@
         // as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
         // animation.
         if (isActionMain) {
-            boolean callbackAllowsMoveToDefaultScreen =
-                mLauncherCallbacks == null || mLauncherCallbacks
-                    .shouldMoveToDefaultScreenOnHomeIntent();
-            if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()
-                    && callbackAllowsMoveToDefaultScreen) {
+            if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()) {
 
                 mWorkspace.post(new Runnable() {
                     @Override
@@ -1749,25 +1694,11 @@
     }
 
     private void setWorkspaceLoading(boolean value) {
-        boolean isLocked = isWorkspaceLocked();
         mWorkspaceLoading = value;
-        if (isLocked != isWorkspaceLocked()) {
-            onWorkspaceLockedChanged();
-        }
     }
 
     public void setWaitingForResult(PendingRequestArgs args) {
-        boolean isLocked = isWorkspaceLocked();
         mPendingRequestArgs = args;
-        if (isLocked != isWorkspaceLocked()) {
-            onWorkspaceLockedChanged();
-        }
-    }
-
-    protected void onWorkspaceLockedChanged() {
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onWorkspaceLockedChanged();
-        }
     }
 
     void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
@@ -2127,11 +2058,7 @@
     private void startMarketIntentForPackage(View v, String packageName) {
         ItemInfo item = (ItemInfo) v.getTag();
         Intent intent = PackageManagerHelper.getMarketIntent(packageName);
-        boolean success = startActivitySafely(v, intent, item);
-        if (success && v instanceof BubbleTextView) {
-            mWaitingForResume = (BubbleTextView) v;
-            mWaitingForResume.setStayPressed(true);
-        }
+        startActivitySafely(v, intent, item);
     }
 
     /**
@@ -2201,13 +2128,8 @@
         if (intent == null) {
             throw new IllegalArgumentException("Input must have a valid intent");
         }
-        boolean success = startActivitySafely(v, intent, item);
+        startActivitySafely(v, intent, item);
         getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
-
-        if (success && v instanceof BubbleTextView) {
-            mWaitingForResume = (BubbleTextView) v;
-            mWaitingForResume.setStayPressed(true);
-        }
     }
 
     /**
@@ -2292,44 +2214,6 @@
         mDragLayer.onAccessibilityStateChanged(enabled);
     }
 
-    /**
-     * Called when the user stops interacting with the launcher.
-     * This implies that the user is now on the homescreen and is not doing housekeeping.
-     */
-    protected void onInteractionEnd() {
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onInteractionEnd();
-        }
-    }
-
-    /**
-     * Called when the user starts interacting with the launcher.
-     * The possible interactions are:
-     *  - open all apps
-     *  - reorder an app shortcut, or a widget
-     *  - open the overview mode.
-     * This is a good time to stop doing things that only make sense
-     * when the user is on the homescreen and not doing housekeeping.
-     */
-    protected void onInteractionBegin() {
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onInteractionBegin();
-        }
-    }
-
-    /** Updates the interaction state. */
-    public void updateInteraction(Workspace.State fromState, Workspace.State toState) {
-        // Only update the interacting state if we are transitioning to/from a view with an
-        // overlay
-        boolean fromStateWithOverlay = fromState != Workspace.State.NORMAL;
-        boolean toStateWithOverlay = toState != Workspace.State.NORMAL;
-        if (toStateWithOverlay) {
-            onInteractionBegin();
-        } else if (fromStateWithOverlay) {
-            onInteractionEnd();
-        }
-    }
-
     private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
         try {
             StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
@@ -2438,6 +2322,16 @@
                 LauncherAppsCompat.getInstance(this).startActivityForProfile(
                         intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
             }
+
+            if (v instanceof BubbleTextView) {
+                // This is set to the view that launched the activity that navigated the user away
+                // from launcher. Since there is no callback for when the activity has finished
+                // launching, enable the press state and keep this reference to reset the press
+                // state when we return to launcher.
+                BubbleTextView btv = (BubbleTextView) v;
+                btv.setStayPressed(true);
+                setOnResumeCallback(btv);
+            }
             return true;
         } catch (ActivityNotFoundException|SecurityException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
@@ -2551,11 +2445,11 @@
     }
 
     public boolean isAppsViewVisible() {
-        return (mState == State.APPS) || (mOnResumeState == State.APPS);
+        return mState == State.APPS;
     }
 
     public boolean isWidgetsViewVisible() {
-        return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS);
+        return mState == State.WIDGETS;
     }
 
     @Override
@@ -2720,18 +2614,6 @@
         return true;
     }
 
-    /**
-     * Updates the workspace and interaction state on state change, and return the animation to this
-     * new state.
-     */
-    public Animator startWorkspaceStateChangeAnimation(Workspace.State toState,
-            boolean animated, AnimationLayerSet layerViews) {
-        Workspace.State fromState = mWorkspace.getState();
-        Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews);
-        updateInteraction(fromState, toState);
-        return anim;
-    }
-
     public void enterSpringLoadedDragMode() {
         if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name()));
         if (isStateSpringLoaded()) {
@@ -2831,8 +2713,11 @@
         }
     }
 
-    public void addOnResumeCallback(Runnable run) {
-        mOnResumeCallbacks.add(run);
+    public void setOnResumeCallback(OnResumeCallback callback) {
+        if (mOnResumeCallback != null) {
+            mOnResumeCallback.onLauncherResume();
+        }
+        mOnResumeCallback = callback;
     }
 
     /**
@@ -3307,10 +3192,6 @@
                 InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
 
         NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.finishBindingItems(false);
-        }
         TraceHelper.endSection("finishBindingItems");
     }
 
@@ -3710,4 +3591,12 @@
             }
         }
     }
+
+    /**
+     * Callback for listening for onResume
+     */
+    public interface OnResumeCallback {
+
+        void onLauncherResume();
+    }
 }
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 2c9a23f..78d753a 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -39,14 +39,12 @@
      */
     void preOnCreate();
     void onCreate(Bundle savedInstanceState);
-    void preOnResume();
     void onResume();
     void onStart();
     void onStop();
     void onPause();
     void onDestroy();
     void onSaveInstanceState(Bundle outState);
-    void onPostCreate(Bundle savedInstanceState);
     void onNewIntent(Intent intent);
     void onActivityResult(int requestCode, int resultCode, Intent data);
     void onRequestPermissionsResult(int requestCode, String[] permissions,
@@ -64,13 +62,7 @@
      * Extension points for providing custom behavior on certain user interactions.
      */
     void onLauncherProviderChange();
-    void finishBindingItems(final boolean upgradePath);
     void bindAllApplications(ArrayList<AppInfo> apps);
-    void onInteractionBegin();
-    void onInteractionEnd();
-
-    @Deprecated
-    void onWorkspaceLockedChanged();
 
     /**
      * Starts a search with {@param initialQuery}. Return false if search was not started.
@@ -81,6 +73,5 @@
     /*
      * Extensions points for adding / replacing some other aspects of the Launcher experience.
      */
-    boolean shouldMoveToDefaultScreenOnHomeIntent();
     boolean hasSettings();
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 74a5bac..3e22366 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -192,9 +192,8 @@
     /**
      * Adds the provided items to the workspace.
      */
-    public void addAndBindAddedWorkspaceItems(
-            Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
-        enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
+    public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
+        enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
     }
 
     public ModelWriter getWriter(boolean hasVerticalHotseat) {
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index e247490..be0ed0c 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -361,8 +361,8 @@
             AnimationLayerSet layerViews) {
         // Create the workspace animation.
         // NOTE: this call apparently also sets the state for the workspace if !animated
-        Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
-                animated, layerViews);
+        Animator workspaceAnim = mLauncher.getWorkspace().
+                setStateWithAnimation(toWorkspaceState, animated, layerViews);
 
         if (animated && initialized) {
             // Play the workspace animation
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 3f7de06..e8c4528 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -12,9 +12,13 @@
 import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.View;
 import android.widget.Toast;
 
+import com.android.launcher3.Launcher.OnResumeCallback;
 import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
 import java.net.URISyntaxException;
 
@@ -91,98 +95,110 @@
     }
 
     @Override
-    public void onDrop(DragObject d) {
-        // Differ item deletion
-        if (d.dragSource instanceof DropTargetSource) {
-            ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
+    public void onDrop(DragObject d, DragOptions options) {
+        // Defer onComplete
+        if (options.deferCompleteForUninstall) {
+            d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
         }
-        super.onDrop(d);
+        super.onDrop(d, options);
     }
 
     @Override
     public void completeDrop(final DragObject d) {
-        DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
-                ? (DropTargetResultCallback) d.dragSource : null;
-        startUninstallActivity(mLauncher, d.dragInfo, callback);
+        ComponentName target = performDropAction(d);
+        if (d.dragSource instanceof DeferredOnComplete) {
+            DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
+            if (target != null) {
+                deferred.mPackageName = target.getPackageName();
+                mLauncher.setOnResumeCallback(deferred);
+            } else {
+                deferred.sendFailure();
+            }
+        }
     }
 
-    public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
-        return startUninstallActivity(launcher, info, null);
+    /**
+     * Performs the drop action and returns the target component for the dragObject or null if
+     * the action was not performed.
+     */
+    protected ComponentName performDropAction(DragObject d) {
+        return performDropAction(mLauncher, d.dragInfo);
     }
 
-    public static boolean startUninstallActivity(
-            final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
-        final ComponentName cn = getUninstallTarget(launcher, info);
-
-        boolean canUninstall;
+    /**
+     * Performs the drop action and returns the target component for the dragObject or null if
+     * the action was not performed.
+     */
+    private static ComponentName performDropAction(Context context, ItemInfo info) {
+        ComponentName cn = getUninstallTarget(context, info);
         if (cn == null) {
             // System applications cannot be installed. For now, show a toast explaining that.
             // We may give them the option of disabling apps this way.
-            Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
-            canUninstall = false;
-        } else {
-            try {
-                Intent i = Intent.parseUri(launcher.getString(R.string.delete_package_intent), 0)
-                        .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
-                        .putExtra(Intent.EXTRA_USER, info.user);
-                launcher.startActivity(i);
-                canUninstall = true;
-            } catch (URISyntaxException e) {
-                Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
-                canUninstall = false;
+            Toast.makeText(context, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
+            return null;
+        }
+        try {
+            Intent i = Intent.parseUri(context.getString(R.string.delete_package_intent), 0)
+                    .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
+                    .putExtra(Intent.EXTRA_USER, info.user);
+            context.startActivity(i);
+            return cn;
+        } catch (URISyntaxException e) {
+            Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
+            return null;
+        }
+    }
+
+    public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
+        return performDropAction(launcher, info) != null;
+    }
+
+    /**
+     * A wrapper around {@link DragSource} which delays the {@link #onDropCompleted} action until
+     * {@link #onLauncherResume}
+     */
+    private class DeferredOnComplete implements DragSource, OnResumeCallback {
+
+        private final DragSource mOriginal;
+        private final Context mContext;
+
+        private String mPackageName;
+        private DragObject mDragObject;
+
+        public DeferredOnComplete(DragSource original, Context context) {
+            mOriginal = original;
+            mContext = context;
+        }
+
+        @Override
+        public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
+                boolean success) {
+            mDragObject = d;
+        }
+
+        @Override
+        public void fillInLogContainerData(View v, ItemInfo info, Target target,
+                Target targetParent) {
+            mOriginal.fillInLogContainerData(v, info, target, targetParent);
+        }
+
+        @Override
+        public void onLauncherResume() {
+            // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
+            if (LauncherAppsCompat.getInstance(mContext)
+                    .getApplicationInfo(mPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                            mDragObject.dragInfo.user) == null) {
+                mDragObject.dragSource = mOriginal;
+                mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false, true);
+            } else {
+                sendFailure();
             }
         }
-        if (callback != null) {
-            sendUninstallResult(launcher, canUninstall, cn, info.user, callback);
+
+        public void sendFailure() {
+            mDragObject.dragSource = mOriginal;
+            mDragObject.cancelled = true;
+            mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false, false);
         }
-        return canUninstall;
-    }
-
-    /**
-     * Notifies the {@param callback} whether the uninstall was successful or not.
-     *
-     * Since there is no direct callback for an uninstall request, we check the package existence
-     * when the launch resumes next time. This assumes that the uninstall activity will finish only
-     * after the task is completed
-     */
-    protected static void sendUninstallResult(
-            final Launcher launcher, boolean activityStarted,
-            final ComponentName cn, final UserHandle user,
-            final DropTargetResultCallback callback) {
-        if (activityStarted)  {
-            final Runnable checkIfUninstallWasSuccess = new Runnable() {
-                @Override
-                public void run() {
-                    // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
-                    boolean uninstallSuccessful = LauncherAppsCompat.getInstance(launcher)
-                            .getApplicationInfo(cn.getPackageName(),
-                                    PackageManager.MATCH_UNINSTALLED_PACKAGES, user) == null;
-                    callback.onDragObjectRemoved(uninstallSuccessful);
-                }
-            };
-            launcher.addOnResumeCallback(checkIfUninstallWasSuccess);
-        } else {
-            callback.onDragObjectRemoved(false);
-        }
-    }
-
-    public interface DropTargetResultCallback {
-        /**
-         * A drag operation was complete.
-         * @param isRemoved true if the drag object should be removed, false otherwise.
-         */
-        void onDragObjectRemoved(boolean isRemoved);
-    }
-
-    /**
-     * Interface defining an object that can provide uninstallable drag objects.
-     */
-    public interface DropTargetSource extends DropTargetResultCallback {
-
-        /**
-         * Indicates that an uninstall request are made and the actual result may come
-         * after some time.
-         */
-        void deferCompleteDropAfterUninstallActivity();
     }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index fff075e..1f26965 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -55,7 +55,6 @@
 
 import com.android.launcher3.Launcher.LauncherOverlay;
 import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
-import com.android.launcher3.UninstallDropTarget.DropTargetSource;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.accessibility.OverviewAccessibilityDelegate;
 import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
@@ -99,7 +98,7 @@
 public class Workspace extends PagedView
         implements DropTarget, DragSource, View.OnTouchListener,
         DragController.DragListener, ViewGroup.OnHierarchyChangeListener,
-        Insettable, DropTargetSource {
+        Insettable {
     private static final String TAG = "Launcher.Workspace";
 
     /** The value that {@link #mTransitionProgress} must be greater than for
@@ -280,10 +279,6 @@
     private float mCurrentScale;
     private float mTransitionProgress;
 
-    @Thunk Runnable mDeferredAction;
-    private boolean mDeferDropAfterUninstall;
-    private boolean mUninstallSuccessful;
-
     // State related to Launcher Overlay
     LauncherOverlay mLauncherOverlay;
     boolean mScrollInteractionBegan;
@@ -407,7 +402,6 @@
 
         updateChildrenLayersEnabled();
         mLauncher.lockScreenOrientation();
-        mLauncher.onInteractionBegin();
         // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
         InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
 
@@ -466,9 +460,7 @@
                 InstallShortcutReceiver.FLAG_DRAG_AND_DROP, getContext());
 
         mOutlineProvider = null;
-        mDragInfo = null;
         mDragSourceInternal = null;
-        mLauncher.onInteractionEnd();
     }
 
     /**
@@ -1752,6 +1744,7 @@
                 }
             });
         }
+        options.deferCompleteForUninstall = true;
 
         beginDragShared(child, this, options);
     }
@@ -2035,7 +2028,7 @@
         if (dropOverView instanceof FolderIcon) {
             FolderIcon fi = (FolderIcon) dropOverView;
             if (fi.acceptDrop(d.dragInfo)) {
-                fi.onDrop(d);
+                fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
 
                 // if the drag started here, we need to remove it from the workspace
                 if (!external) {
@@ -2050,7 +2043,7 @@
     @Override
     public void prepareAccessibilityDrop() { }
 
-    public void onDrop(final DragObject d) {
+    public void onDrop(final DragObject d, DragOptions options) {
         mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
         CellLayout dropTargetLayout = mDropToLayout;
 
@@ -3093,21 +3086,8 @@
      */
     public void onDropCompleted(final View target, final DragObject d,
             final boolean isFlingToDelete, final boolean success) {
-        if (mDeferDropAfterUninstall) {
-            final CellLayout.CellInfo dragInfo = mDragInfo;
-            mDeferredAction = new Runnable() {
-                public void run() {
-                    mDragInfo = dragInfo; // Restore the drag info that was cleared in onDragEnd()
-                    onDropCompleted(target, d, isFlingToDelete, success);
-                    mDeferredAction = null;
-                }
-            };
-            return;
-        }
 
-        boolean beingCalledAfterUninstall = mDeferredAction != null;
-
-        if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
+        if (success) {
             if (target != this && mDragInfo != null) {
                 removeWorkspaceItem(mDragInfo.cell);
             }
@@ -3121,8 +3101,7 @@
                         + "Workspace#onDropCompleted. Please file a bug. ");
             }
         }
-        if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
-                && mDragInfo != null && mDragInfo.cell != null) {
+        if (d.cancelled && mDragInfo != null && mDragInfo.cell != null) {
             mDragInfo.cell.setVisibility(VISIBLE);
         }
         mDragInfo = null;
@@ -3169,21 +3148,6 @@
         });
     }
 
-    @Override
-    public void deferCompleteDropAfterUninstallActivity() {
-        mDeferDropAfterUninstall = true;
-    }
-
-    /// maybe move this into a smaller part
-    @Override
-    public void onDragObjectRemoved(boolean success) {
-        mDeferDropAfterUninstall = false;
-        mUninstallSuccessful = success;
-        if (mDeferredAction != null) {
-            mDeferredAction.run();
-        }
-    }
-
     public boolean isDropEnabled() {
         return true;
     }
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index c695758..583492e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -152,7 +152,7 @@
             DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host);
             return true;
         } else if (action == INFO) {
-            InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null);
+            InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null, null);
             return true;
         } else if (action == UNINSTALL) {
             return UninstallDropTarget.startUninstallActivity(mLauncher, item);
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 2cc0781..b844ba3 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -275,8 +275,7 @@
     private void updateAllAppsBg(float progress) {
         // gradient
         if (mGradientView == null) {
-            mGradientView = (GradientView) mLauncher.findViewById(R.id.gradient_bg);
-            mGradientView.setVisibility(View.VISIBLE);
+            mGradientView = mLauncher.findViewById(R.id.gradient_bg);
         }
         mGradientView.setProgress(progress);
     }
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index a7ed87f..db8f903 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -575,7 +575,7 @@
                 if (flingAnimation != null) {
                     flingAnimation.run();
                 } else if (!mIsInPreDrag) {
-                    dropTarget.onDrop(mDragObject);
+                    dropTarget.onDrop(mDragObject, mOptions);
                 }
                 accepted = true;
             }
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index f108f8b..550f948 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -38,6 +38,12 @@
     public float intrinsicIconScaleFactor = 1f;
 
     /**
+     * Whether or not to defer {@link com.android.launcher3.DragSource#onDropCompleted} until
+     * uninstall result is available when dropped on uninstall drop target.
+     */
+    public boolean deferCompleteForUninstall = false;
+
+    /**
      * Specifies a condition that must be met before DragListener#onDragStart() is called.
      * By default, there is no condition and onDragStart() is called immediately following
      * DragController#startDrag().
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index e182da1..b930ad3 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -58,7 +58,6 @@
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.UninstallDropTarget.DropTargetSource;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
@@ -84,8 +83,7 @@
  */
 public class Folder extends AbstractFloatingView implements DragSource, View.OnClickListener,
         View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
-        View.OnFocusChangeListener, DragListener, DropTargetSource,
-        ExtendedEditText.OnBackKeyListener {
+        View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
     private static final String TAG = "Launcher.Folder";
 
     /**
@@ -172,10 +170,6 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mDestroyed;
 
-    @Thunk Runnable mDeferredAction;
-    private boolean mDeferDropAfterUninstall;
-    private boolean mUninstallSuccessful;
-
     // Folder scrolling
     private int mScrollAreaOffset;
 
@@ -289,6 +283,7 @@
                     }
                 });
             }
+            options.deferCompleteForUninstall = true;
 
             mLauncher.getWorkspace().beginDragShared(v, this, options);
         }
@@ -856,22 +851,8 @@
 
     public void onDropCompleted(final View target, final DragObject d,
             final boolean isFlingToDelete, final boolean success) {
-        if (mDeferDropAfterUninstall) {
-            Log.d(TAG, "Deferred handling drop because waiting for uninstall.");
-            mDeferredAction = new Runnable() {
-                    public void run() {
-                        onDropCompleted(target, d, isFlingToDelete, success);
-                        mDeferredAction = null;
-                    }
-                };
-            return;
-        }
 
-        boolean beingCalledAfterUninstall = mDeferredAction != null;
-        boolean successfulDrop =
-                success && (!beingCalledAfterUninstall || mUninstallSuccessful);
-
-        if (successfulDrop) {
+        if (success) {
             if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) {
                 replaceFolderWithFinalItem();
             }
@@ -886,14 +867,14 @@
             mItemsInvalidated = true;
 
             try (SuppressInfoChanges s = new SuppressInfoChanges()) {
-                mFolderIcon.onDrop(d);
+                mFolderIcon.onDrop(d, true /* itemReturnedOnFailedDrop */);
             }
         }
 
         if (target != this) {
             if (mOnExitAlarm.alarmPending()) {
                 mOnExitAlarm.cancelAlarm();
-                if (!successfulDrop) {
+                if (!success) {
                     mSuppressFolderDeletion = true;
                 }
                 mScrollPauseAlarm.cancelAlarm();
@@ -920,25 +901,11 @@
 
         if (!isFlingToDelete) {
             // Fling to delete already exits spring loaded mode after the animation finishes.
-            mLauncher.exitSpringLoadedDragModeDelayed(successfulDrop,
+            mLauncher.exitSpringLoadedDragModeDelayed(success,
                     Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
         }
     }
 
-    @Override
-    public void deferCompleteDropAfterUninstallActivity() {
-        mDeferDropAfterUninstall = true;
-    }
-
-    @Override
-    public void onDragObjectRemoved(boolean success) {
-        mDeferDropAfterUninstall = false;
-        mUninstallSuccessful = success;
-        if (mDeferredAction != null) {
-            mDeferredAction.run();
-        }
-    }
-
     private void updateItemLocationsInDatabaseBatch() {
         ArrayList<View> list = getItemsInReadingOrder();
         ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
@@ -1190,7 +1157,7 @@
         }
     }
 
-    public void onDrop(DragObject d) {
+    public void onDrop(DragObject d, DragOptions options) {
         Runnable cleanUpRunnable = null;
 
         // If we are coming from All Apps space, we defer removing the extra empty screen
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index bb0a726..3998888 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -100,6 +100,7 @@
     ClippedFolderIconLayoutRule mPreviewLayoutRule;
     private PreviewItemManager mPreviewItemManager;
     private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+    private List<BubbleTextView> mCurrentPreviewItems = new ArrayList<>();
 
     boolean mAnimating = false;
     private Rect mTempBounds = new Rect();
@@ -198,7 +199,7 @@
     private void setFolder(Folder folder) {
         mFolder = folder;
         mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
-        mPreviewItemManager.updateItemDrawingParams(false);
+        updatePreviewItems(false);
     }
 
     private boolean willAcceptItem(ItemInfo item) {
@@ -262,7 +263,8 @@
                 .start();
 
         // This will animate the dragView (srcView) into the new folder
-        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
+        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable,
+                false /* itemReturnedOnFailedDrop */);
     }
 
     public void performDestroyAnimation(Runnable onCompleteRunnable) {
@@ -277,7 +279,8 @@
     }
 
     private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
-            float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
+            float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable,
+            boolean itemReturnedOnFailedDrop) {
         item.cellX = -1;
         item.cellY = -1;
 
@@ -305,21 +308,25 @@
                 workspace.resetTransitionTransform((CellLayout) getParent().getParent());
             }
 
+            int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1);
             boolean itemAdded = false;
-            if (index >= MAX_NUM_ITEMS_IN_PREVIEW) {
-                List<BubbleTextView> oldPreviewItems = getPreviewItemsOnPage(0);
+            if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
+                List<BubbleTextView> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
                 addItem(item, false);
-                List<BubbleTextView> newPreviewItems = getPreviewItemsOnPage(0);
+                mCurrentPreviewItems.clear();
+                mCurrentPreviewItems.addAll(getPreviewItems());
 
-                if (!oldPreviewItems.containsAll(newPreviewItems)) {
-                    for (int i = 0; i < newPreviewItems.size(); ++i) {
-                        if (newPreviewItems.get(i).getTag().equals(item)) {
+                if (!oldPreviewItems.equals(mCurrentPreviewItems)) {
+                    for (int i = 0; i < mCurrentPreviewItems.size(); ++i) {
+                        if (mCurrentPreviewItems.get(i).getTag().equals(item)) {
                             // If the item dropped is going to be in the preview, we update the
                             // index here to reflect its position in the preview.
                             index = i;
                         }
                     }
-                    mPreviewItemManager.onDrop(oldPreviewItems, newPreviewItems, item);
+
+                    mPreviewItemManager.hidePreviewItem(index, true);
+                    mPreviewItemManager.onDrop(oldPreviewItems, mCurrentPreviewItems, item);
                     itemAdded = true;
                 } else {
                     removeItem(item, false);
@@ -331,7 +338,7 @@
             }
 
             int[] center = new int[2];
-            float scale = getLocalCenterForIndex(index, index + 1, center);
+            float scale = getLocalCenterForIndex(index, numItemsInPreview, center);
             center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
             center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
 
@@ -362,7 +369,7 @@
         }
     }
 
-    public void onDrop(DragObject d) {
+    public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) {
         ShortcutInfo item;
         if (d.dragInfo instanceof AppInfo) {
             // Came from all apps -- make a copy
@@ -374,7 +381,8 @@
             item = (ShortcutInfo) d.dragInfo;
         }
         mFolder.notifyDrop();
-        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
+        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable,
+                itemReturnedOnFailedDrop);
     }
 
     public void setBadgeInfo(FolderBadgeInfo badgeInfo) {
@@ -545,11 +553,17 @@
 
     @Override
     public void onItemsChanged(boolean animate) {
-        mPreviewItemManager.updateItemDrawingParams(animate);
+        updatePreviewItems(animate);
         invalidate();
         requestLayout();
     }
 
+    private void updatePreviewItems(boolean animate) {
+        mPreviewItemManager.updatePreviewItems(animate);
+        mCurrentPreviewItems.clear();
+        mCurrentPreviewItems.addAll(getPreviewItems());
+    }
+
     @Override
     public void prepareAutoUpdate() {
     }
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 5d40010..06d3eb1 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -110,7 +110,7 @@
             mIcon.mPreviewLayoutRule.init(mIcon.mBackground.previewSize, mIntrinsicIconSize,
                     Utilities.isRtl(mIcon.getResources()));
 
-            updateItemDrawingParams(false);
+            updatePreviewItems(false);
         }
     }
 
@@ -185,6 +185,11 @@
     }
 
     public void hidePreviewItem(int index, boolean hidden) {
+        // If there are more params than visible in the preview, they are used for enter/exit
+        // animation purposes and they were added to the front of the list.
+        // To index the params properly, we need to skip these params.
+        index = index + Math.max(mFirstPageParams.size() - MAX_NUM_ITEMS_IN_PREVIEW, 0);
+
         PreviewItemDrawingParams params = index < mFirstPageParams.size() ?
                 mFirstPageParams.get(index) : null;
         if (params != null) {
@@ -266,7 +271,7 @@
         }
     }
 
-    void updateItemDrawingParams(boolean animate) {
+    void updatePreviewItems(boolean animate) {
         buildParamsForPage(0, mFirstPageParams, animate);
     }
 
@@ -310,8 +315,8 @@
             int prevIndex = newParams.indexOf(moveIn.get(i));
             PreviewItemDrawingParams p = params.get(prevIndex);
             computePreviewItemDrawingParams(prevIndex, numItems, p);
-            updateTransitionParam(p, moveIn.get(i), ENTER_INDEX,
-                    newParams.indexOf(moveIn.get(i)));
+            updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newParams.indexOf(moveIn.get(i)),
+                    numItems);
         }
 
         // Items that are moving into new positions within the preview.
@@ -319,7 +324,7 @@
             int oldIndex = oldParams.indexOf(newParams.get(newIndex));
             if (oldIndex >= 0 && newIndex != oldIndex) {
                 PreviewItemDrawingParams p = params.get(newIndex);
-                updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex);
+                updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex, numItems);
             }
         }
 
@@ -330,7 +335,7 @@
             BubbleTextView item = moveOut.get(i);
             int oldIndex = oldParams.indexOf(item);
             PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null);
-            updateTransitionParam(p, item, oldIndex, EXIT_INDEX);
+            updateTransitionParam(p, item, oldIndex, EXIT_INDEX, numItems);
             params.add(0, p); // We want these items first so that they are on drawn last.
         }
 
@@ -342,7 +347,7 @@
     }
 
     private void updateTransitionParam(final PreviewItemDrawingParams p, BubbleTextView btv,
-            int prevIndex, int newIndex) {
+            int prevIndex, int newIndex, int numItems) {
         p.drawable = btv.getCompoundDrawables()[1];
         if (!mIcon.mFolder.isOpen()) {
             // Set the callback to FolderIcon as it is responsible to drawing the icon. The
@@ -350,9 +355,8 @@
             p.drawable.setCallback(mIcon);
         }
 
-        FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex,
-                MAX_NUM_ITEMS_IN_PREVIEW, newIndex, MAX_NUM_ITEMS_IN_PREVIEW,
-                DROP_IN_ANIMATION_DURATION, null);
+        FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex, numItems,
+                newIndex, numItems, DROP_IN_ANIMATION_DURATION, null);
         if (p.anim != null && !p.anim.hasEqualFinalState(anim)) {
             p.anim.cancel();
         }
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 42926fa..a33a039 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -38,7 +38,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.ManagedProfileHeuristic.UserFolderInfo;
-import com.android.launcher3.util.Provider;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -47,19 +46,18 @@
  */
 public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
 
-    private final Provider<List<Pair<ItemInfo, Object>>> mAppsProvider;
+    private final List<Pair<ItemInfo, Object>> mItemList;
 
     /**
-     * @param appsProvider items to add on the workspace
+     * @param itemList items to add on the workspace
      */
-    public AddWorkspaceItemsTask(Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
-        mAppsProvider = appsProvider;
+    public AddWorkspaceItemsTask(List<Pair<ItemInfo, Object>> itemList) {
+        mItemList = itemList;
     }
 
     @Override
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-        List<Pair<ItemInfo, Object>> workspaceApps = mAppsProvider.get();
-        if (workspaceApps.isEmpty()) {
+        if (mItemList.isEmpty()) {
             return;
         }
         Context context = app.getContext();
@@ -75,7 +73,7 @@
         synchronized(dataModel) {
 
             List<ItemInfo> filteredItems = new ArrayList<>();
-            for (Pair<ItemInfo, Object> entry : workspaceApps) {
+            for (Pair<ItemInfo, Object> entry : mItemList) {
                 ItemInfo item = entry.first;
                 if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                         item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
diff --git a/src/com/android/launcher3/notification/NotificationGroup.java b/src/com/android/launcher3/notification/NotificationGroup.java
new file mode 100644
index 0000000..bce2117
--- /dev/null
+++ b/src/com/android/launcher3/notification/NotificationGroup.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Contains data related to a group of notifications, like the group summary key and the child keys.
+ */
+public class NotificationGroup {
+    private String mGroupSummaryKey;
+    private Set<String> mChildKeys;
+
+    public NotificationGroup() {
+        mChildKeys = new HashSet<>();
+    }
+
+    public void setGroupSummaryKey(String groupSummaryKey) {
+        mGroupSummaryKey = groupSummaryKey;
+    }
+
+    public String getGroupSummaryKey() {
+        return mGroupSummaryKey;
+    }
+
+    public void addChildKey(String childKey) {
+        mChildKeys.add(childKey);
+    }
+
+    public void removeChildKey(String childKey) {
+        mChildKeys.remove(childKey);
+    }
+
+    public boolean isEmpty() {
+        return mChildKeys.isEmpty();
+    }
+}
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 9126626..7b70df7 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -38,7 +38,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
@@ -66,6 +68,8 @@
     private final Handler mWorkerHandler;
     private final Handler mUiHandler;
     private final Ranking mTempRanking = new Ranking();
+    /** Maps groupKey's to the corresponding group of notifications. */
+    private final Map<String, NotificationGroup> mNotificationGroupMap = new HashMap<>();
 
     private SettingsObserver mNotificationBadgingObserver;
 
@@ -227,6 +231,15 @@
                         NotificationKeyData.fromNotification(sbn));
         mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey)
                 .sendToTarget();
+
+        NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
+        if (notificationGroup != null) {
+            notificationGroup.removeChildKey(sbn.getKey());
+            if (notificationGroup.isEmpty()) {
+                cancelNotification(notificationGroup.getGroupSummaryKey());
+                mNotificationGroupMap.remove(sbn.getGroupKey());
+            }
+        }
     }
 
     /** This makes a potentially expensive binder call and should be run on a background thread. */
@@ -264,18 +277,34 @@
     }
 
     private boolean shouldBeFilteredOut(StatusBarNotification sbn) {
+        Notification notification = sbn.getNotification();
+
+        boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
+        if (sbn.isGroup()) {
+            // Maintain group info so we can cancel the summary when the last child is canceled.
+            NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
+            if (notificationGroup == null) {
+                notificationGroup = new NotificationGroup();
+                mNotificationGroupMap.put(sbn.getGroupKey(), notificationGroup);
+            }
+            if (isGroupHeader) {
+                notificationGroup.setGroupSummaryKey(sbn.getKey());
+            } else {
+                notificationGroup.addChildKey(sbn.getKey());
+            }
+        }
+
         getCurrentRanking().getRanking(sbn.getKey(), mTempRanking);
         if (!mTempRanking.canShowBadge()) {
             return true;
         }
-        Notification notification = sbn.getNotification();
         if (mTempRanking.getChannel().getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
             // Special filtering for the default, legacy "Miscellaneous" channel.
             if ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0) {
                 return true;
             }
         }
-        boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
+
         CharSequence title = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
         CharSequence text = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
         boolean missingTitleAndText = TextUtils.isEmpty(title) && TextUtils.isEmpty(text);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index a342500..3f7bf42 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -92,7 +92,7 @@
                 public void onClick(View view) {
                     Rect sourceBounds = launcher.getViewBounds(view);
                     Bundle opts = launcher.getActivityLaunchOptions(view);
-                    InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, null, sourceBounds, opts);
+                    InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, sourceBounds, opts);
                     launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                             ControlType.APPINFO_TARGET, view);
                 }
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index a1a4d75..c40e1fb 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -36,10 +36,6 @@
         }
 
         @Override
-        public void preOnResume() {
-        }
-
-        @Override
         public void onResume() {
         }
 
@@ -64,10 +60,6 @@
         }
 
         @Override
-        public void onPostCreate(Bundle savedInstanceState) {
-        }
-
-        @Override
         public void onNewIntent(Intent intent) {
         }
 
@@ -111,37 +103,16 @@
         }
 
         @Override
-        public void finishBindingItems(boolean upgradePath) {
-        }
-
-        @Override
         public void bindAllApplications(ArrayList<AppInfo> apps) {
         }
 
         @Override
-        public void onWorkspaceLockedChanged() {
-        }
-
-        @Override
-        public void onInteractionBegin() {
-        }
-
-        @Override
-        public void onInteractionEnd() {
-        }
-
-        @Override
         public boolean startSearch(String initialQuery, boolean selectInitialQuery,
                 Bundle appSearchData) {
             return false;
         }
 
         @Override
-        public boolean shouldMoveToDefaultScreenOnHomeIntent() {
-            return true;
-        }
-
-        @Override
         public boolean hasSettings() {
             return false;
         }
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 432efa7..7aa50a4 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -107,7 +107,6 @@
         onWidgetsBound();
 
         mLauncher.getDragLayer().addView(mGradientBackground);
-        mGradientBackground.setVisibility(VISIBLE);
         mLauncher.getDragLayer().addView(this);
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         setTranslationY(mTranslationYClosed);
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index 82f34e4..a486ceb 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -55,7 +55,7 @@
         for (ItemInfo item : items) {
             list.add(Pair.create(item, null));
         }
-        return new AddWorkspaceItemsTask(Provider.of(list)) {
+        return new AddWorkspaceItemsTask(list) {
 
             @Override
             protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { }