Merge "Removing static state management from the install shortcut queue" into ub-launcher3-master
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 53b65e8..51a9dfe 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -24,7 +24,6 @@
 import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
-import static com.android.launcher3.InstallShortcutReceiver.FLAG_DRAG_AND_DROP;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
@@ -42,6 +41,9 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP;
 import static com.android.launcher3.logging.StatsLogManager.containerTypeToAtomState;
+import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
+import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
+import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -119,6 +121,7 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.ModelUtils;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.data.AppInfo;
@@ -911,8 +914,8 @@
         getUserEventDispatcher().startSession();
 
         // Process any items that were added while Launcher was away.
-        InstallShortcutReceiver.disableAndFlushInstallQueue(
-                InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
+        ItemInstallQueue.INSTANCE.get(this)
+                .resumeModelPush(FLAG_ACTIVITY_PAUSED);
 
         // Refresh shortcuts if the permission changed.
         mModel.validateModelDataOnResume();
@@ -1006,7 +1009,7 @@
         if (state == SPRING_LOADED) {
             // Prevent any Un/InstallShortcutReceivers from updating the db while we are
             // not on homescreen
-            InstallShortcutReceiver.enableInstallQueue(FLAG_DRAG_AND_DROP);
+            ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_DRAG_AND_DROP);
             getRotationHelper().setCurrentStateRequest(REQUEST_LOCK);
 
             mWorkspace.showPageIndicatorAtCurrentScroll();
@@ -1031,7 +1034,8 @@
 
         if (state == NORMAL) {
             // Re-enable any Un/InstallShortcutReceiver and now process any queued items
-            InstallShortcutReceiver.disableAndFlushInstallQueue(FLAG_DRAG_AND_DROP, this);
+            ItemInstallQueue.INSTANCE.get(this)
+                    .resumeModelPush(FLAG_DRAG_AND_DROP);
 
             // Clear any rotation locks when going to normal state
             getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
@@ -1065,7 +1069,7 @@
     @Override
     protected void onPause() {
         // Ensure that items added to Launcher are queued until Launcher returns
-        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
+        ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
 
         super.onPause();
         mDragController.cancelDrag();
@@ -2420,8 +2424,8 @@
             mPendingActivityResult = null;
         }
 
-        InstallShortcutReceiver.disableAndFlushInstallQueue(
-                InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
+        ItemInstallQueue.INSTANCE.get(this)
+                .resumeModelPush(FLAG_LOADER_RUNNING);
 
         // When undoing the removal of the last item on a page, return to that page.
         // Since we are just resetting the current page without user interaction,
@@ -2448,8 +2452,8 @@
 
     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
         ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v)
-                .setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
-        bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
+                .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION);
+        bounceAnim.setStartDelay(i * ItemInstallQueue.NEW_SHORTCUT_STAGGER_DELAY);
         bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION));
         return bounceAnim;
     }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f58e0b4..c51a84e 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -43,6 +43,7 @@
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.CacheDataUpdatedTask;
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.LoaderResults;
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDelegate;
@@ -328,7 +329,8 @@
      */
     public boolean startLoader() {
         // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
-        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
+        ItemInstallQueue.INSTANCE.get(mApp.getContext())
+                .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
         synchronized (mLock) {
             // Don't bother to start the thread if we know it's not going to do anything
             final Callbacks[] callbacksList = getCallbacks();
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index e48ffb9..007e5f5 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -25,6 +25,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.pm.InstallSessionHelper;
 
 /**
@@ -59,7 +60,8 @@
             return;
         }
 
-        InstallShortcutReceiver.queueApplication(info.getAppPackageName(), user, context);
+        ItemInstallQueue.INSTANCE.get(context)
+                .queueItem(info.getAppPackageName(), user);
     }
 
     public static boolean isEnabled(Context context) {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 0df6713..2d625c5 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -43,13 +43,13 @@
 import android.view.View.OnTouchListener;
 
 import com.android.launcher3.BaseActivity;
-import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetHost;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.pm.PinRequestHelper;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -249,7 +249,7 @@
      */
     public void onPlaceAutomaticallyClick(View v) {
         if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
-            InstallShortcutReceiver.queueShortcut(mRequest.getShortcutInfo(), this);
+            ItemInstallQueue.INSTANCE.get(this).queueItem(mRequest.getShortcutInfo());
             logCommand(Action.Command.CONFIRM);
             mRequest.accept();
             finish();
@@ -270,7 +270,8 @@
     }
 
     private void acceptWidget(int widgetId) {
-        InstallShortcutReceiver.queueWidget(mRequest.getAppWidgetProviderInfo(this), widgetId, this);
+        ItemInstallQueue.INSTANCE.get(this)
+                .queueItem(mRequest.getAppWidgetProviderInfo(this), widgetId);
         mWidgetOptions.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
         mRequest.accept(mWidgetOptions);
         logCommand(Action.Command.CONFIRM);
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 140342f..7e45021 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -31,7 +31,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 
-import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Workspace;
@@ -298,7 +297,7 @@
                         .filter(wi -> wi.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                         .map(ShortcutKey::fromItemInfo),
                     // Pending shortcuts
-                    InstallShortcutReceiver.getPendingShortcuts(context)
+                    ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts()
                         .stream().filter(si -> si.user.equals(user)))
                 .collect(groupingBy(ShortcutKey::getPackageName,
                         mapping(ShortcutKey::getId, Collectors.toSet())));
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/model/ItemInstallQueue.java
similarity index 80%
rename from src/com/android/launcher3/InstallShortcutReceiver.java
rename to src/com/android/launcher3/model/ItemInstallQueue.java
index d2b05c5..6238db3 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.model;
 
 import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
 
@@ -42,13 +42,19 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
 
 import org.json.JSONException;
@@ -63,16 +69,15 @@
 import java.util.List;
 import java.util.Set;
 
-public class InstallShortcutReceiver {
+/**
+ * Class to maintain a queue of pending items to be added to the workspace.
+ */
+public class ItemInstallQueue {
 
     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;
 
-    // Determines whether to defer installing shortcuts immediately until
-    // processAllPendingInstalls() is called.
-    private static int sInstallQueueDisabledFlags = 0;
-
     private static final String TAG = "InstallShortcutReceiver";
     private static final boolean DBG = false;
 
@@ -82,10 +87,23 @@
     public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
     public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
 
+    public static MainThreadInitializedObject<ItemInstallQueue> INSTANCE =
+            new MainThreadInitializedObject<>(ItemInstallQueue::new);
+
+    private final Context mContext;
+
+    // Determines whether to defer installing shortcuts immediately until
+    // processAllPendingInstalls() is called.
+    private int mInstallQueueDisabledFlags = 0;
+
+    private ItemInstallQueue(Context context) {
+        mContext = context;
+    }
+
     @WorkerThread
-    private static void addToQueue(Context context, PendingInstallShortcutInfo info) {
-        String encoded = info.encodeToString(context);
-        SharedPreferences prefs = Utilities.getPrefs(context);
+    private void addToQueue(PendingInstallShortcutInfo info) {
+        String encoded = info.encodeToString(mContext);
+        SharedPreferences prefs = Utilities.getPrefs(mContext);
         Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
         strings = (strings != null) ? new HashSet<>(strings) : new HashSet<>(1);
         strings.add(encoded);
@@ -93,14 +111,15 @@
     }
 
     @WorkerThread
-    private static void flushQueueInBackground(Context context) {
-        if (Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null) {
+    private void flushQueueInBackground() {
+        Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
+        if (launcher == null) {
             // Launcher not loaded
             return;
         }
 
         ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
-        SharedPreferences prefs = Utilities.getPrefs(context);
+        SharedPreferences prefs = Utilities.getPrefs(mContext);
         Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
         if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
         if (strings == null) {
@@ -108,29 +127,31 @@
         }
 
         for (String encoded : strings) {
-            PendingInstallShortcutInfo info = decode(encoded, context);
+            PendingInstallShortcutInfo info = decode(encoded, mContext);
             if (info == null) {
                 continue;
             }
 
             // Generate a shortcut info to add into the model
-            installQueue.add(info.getItemInfo(context));
+            installQueue.add(info.getItemInfo(mContext));
         }
         prefs.edit().remove(APPS_PENDING_INSTALL).apply();
         if (!installQueue.isEmpty()) {
-            LauncherAppState.getInstance(context).getModel()
-                    .addAndBindAddedWorkspaceItems(installQueue);
+            launcher.getModel().addAndBindAddedWorkspaceItems(installQueue);
         }
     }
 
-    public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
-            UserHandle user) {
+    /**
+     * Removes previously added items from the queue.
+     */
+    @WorkerThread
+    public void removeFromInstallQueue(HashSet<String> packageNames, UserHandle user) {
         if (packageNames.isEmpty()) {
             return;
         }
         Preconditions.assertWorkerThread();
 
-        SharedPreferences sp = Utilities.getPrefs(context);
+        SharedPreferences sp = Utilities.getPrefs(mContext);
         Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
         if (DBG) {
             Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
@@ -144,7 +165,7 @@
         while (newStringsIter.hasNext()) {
             String encoded = newStringsIter.next();
             try {
-                Decoder decoder = new Decoder(encoded, context);
+                Decoder decoder = new Decoder(encoded, mContext);
                 if (packageNames.contains(getIntentPackage(decoder.intent))
                         && user.equals(decoder.user)) {
                     newStringsIter.remove();
@@ -157,30 +178,42 @@
         sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
     }
 
-    public static void queueShortcut(ShortcutInfo info, Context context) {
-        queuePendingShortcutInfo(new PendingInstallShortcutInfo(info), context);
+    /**
+     * Adds an item to the install queue
+     */
+    public void queueItem(ShortcutInfo info) {
+        queuePendingShortcutInfo(new PendingInstallShortcutInfo(info));
     }
 
-    public static void queueWidget(AppWidgetProviderInfo info, int widgetId, Context context) {
-        queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId), context);
+    /**
+     * Adds an item to the install queue
+     */
+    public void queueItem(AppWidgetProviderInfo info, int widgetId) {
+        queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId));
     }
 
-    public static void queueApplication(
-            String packageName, UserHandle userHandle, Context context) {
-        queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle), context);
+    /**
+     * Adds an item to the install queue
+     */
+    public void queueItem(String packageName, UserHandle userHandle) {
+        queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle));
     }
 
-    public static HashSet<ShortcutKey> getPendingShortcuts(Context context) {
+    /**
+     * Returns all pending shorts in the queue
+     */
+    @WorkerThread
+    public HashSet<ShortcutKey> getPendingShortcuts() {
         HashSet<ShortcutKey> result = new HashSet<>();
 
-        Set<String> strings = Utilities.getPrefs(context).getStringSet(APPS_PENDING_INSTALL, null);
+        Set<String> strings = Utilities.getPrefs(mContext).getStringSet(APPS_PENDING_INSTALL, null);
         if (strings == null || ((Collection) strings).isEmpty()) {
             return result;
         }
 
         for (String encoded : strings) {
             try {
-                Decoder decoder = new Decoder(encoded, context);
+                Decoder decoder = new Decoder(encoded, mContext);
                 if (decoder.optInt(Favorites.ITEM_TYPE, -1) == ITEM_TYPE_DEEP_SHORTCUT) {
                     result.add(ShortcutKey.fromIntent(decoder.intent, decoder.user));
                 }
@@ -191,28 +224,35 @@
         return result;
     }
 
-    private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
+    private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
         // Queue the item up for adding if launcher has not loaded properly yet
-        MODEL_EXECUTOR.post(() -> addToQueue(context, info));
-        flushInstallQueue(context);
+        MODEL_EXECUTOR.post(() -> addToQueue(info));
+        flushInstallQueue();
     }
 
-    public static void enableInstallQueue(int flag) {
-        sInstallQueueDisabledFlags |= flag;
-    }
-    public static void disableAndFlushInstallQueue(int flag, Context context) {
-        sInstallQueueDisabledFlags &= ~flag;
-        flushInstallQueue(context);
+    /**
+     * Pauses the push-to-model flow until unpaused. All items are held in the queue and
+     * not added to the model.
+     */
+    public void pauseModelPush(int flag) {
+        mInstallQueueDisabledFlags |= flag;
     }
 
-    static void flushInstallQueue(Context context) {
-        if (sInstallQueueDisabledFlags != 0) {
+    /**
+     * Adds all the queue items to the model if the use is completely resumed.
+     */
+    public void resumeModelPush(int flag) {
+        mInstallQueueDisabledFlags &= ~flag;
+        flushInstallQueue();
+    }
+
+    private void flushInstallQueue() {
+        if (mInstallQueueDisabledFlags != 0) {
             return;
         }
-        MODEL_EXECUTOR.post(() -> flushQueueInBackground(context));
+        MODEL_EXECUTOR.post(this::flushQueueInBackground);
     }
 
-
     private static class PendingInstallShortcutInfo extends ItemInfo {
 
         final Intent intent;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index c0ae6f9..896bfb6 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -28,7 +28,6 @@
 import android.os.UserManager;
 import android.util.Log;
 
-import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.config.FeatureFlags;
@@ -320,7 +319,8 @@
             deleteAndBindComponentsRemoved(removeMatch);
 
             // Remove any queued items from the install queue
-            InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
+            ItemInstallQueue.INSTANCE.get(context)
+                    .removeFromInstallQueue(removedPackages, mUser);
         }
 
         if (mOp == OP_ADD) {
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index d546013..753a6dd 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -32,11 +32,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
-import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.LooperExecutor;
@@ -213,8 +213,8 @@
                 && !mPromiseIconIds.contains(sessionInfo.getSessionId())
                 && new PackageManagerHelper(mAppContext).getApplicationInfo(
                         sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), 0) == null) {
-            InstallShortcutReceiver.queueApplication(
-                    sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), mAppContext);
+            ItemInstallQueue.INSTANCE.get(mAppContext)
+                    .queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
 
             mPromiseIconIds.add(sessionInfo.getSessionId());
             updatePromiseIconPrefs();