Save usercreated bubbles and re-bubble next time; add experiment debug logs

* Maintain a list of user-created bubbles in BubbleController
* Experiment code will allow any notif with a matching key in that list
  to be created as a bubble, does not adjust any channel level setting,
  does not maintain across reboots
* Add some debug logs (turned on by default, only triggers if experiment
  via set prop is running)

This is more of a stop-gap for testing until we have proper way of saving
that this person/notification from app should be bubbled or not.

Test: manual
Bug: 143173197
Change-Id: Id247e361740cdf95ae511287ad58c547825f1e47
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index db1185f..ed21e14 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -31,6 +31,7 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
 import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
+import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_EXPERIMENTS;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -92,6 +93,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -145,6 +147,10 @@
     // Saves notification keys of active bubbles when users are switched.
     private final SparseSetArray<String> mSavedBubbleKeysPerUser;
 
+    // Saves notification keys of user created "fake" bubbles so that we can allow notifications
+    // like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
+    private final HashSet<String> mUserCreatedBubbles;
+
     // Bubbles get added to the status bar view
     private final StatusBarWindowController mStatusBarWindowController;
     private final ZenModeController mZenModeController;
@@ -312,6 +318,8 @@
                     restoreBubbles(newUserId);
                     mCurrentUserId = newUserId;
                 });
+
+        mUserCreatedBubbles = new HashSet<>();
     }
 
     /**
@@ -535,10 +543,13 @@
      * @param entry the notification to show as a bubble.
      */
     public void onUserCreatedBubbleFromNotification(NotificationEntry entry) {
+        if (DEBUG_EXPERIMENTS || DEBUG_BUBBLE_CONTROLLER) {
+            Log.d(TAG, "onUserCreatedBubble: " + entry.getKey());
+        }
         mShadeController.get().collapsePanel(true);
         entry.setFlagBubble(true);
         updateBubble(entry, true /* suppressFlyout */, false /* showInShade */);
-        mBubbleData.getBubbleWithKey(entry.getKey()).setUserCreated(true);
+        mUserCreatedBubbles.add(entry.getKey());
     }
 
     /**
@@ -548,8 +559,19 @@
      * @param entry the notification to no longer show as a bubble.
      */
     public void onUserDemotedBubbleFromNotification(NotificationEntry entry) {
+        if (DEBUG_EXPERIMENTS || DEBUG_BUBBLE_CONTROLLER) {
+            Log.d(TAG, "onUserDemotedBubble: " + entry.getKey());
+        }
         entry.setFlagBubble(false);
         removeBubble(entry.getKey(), DISMISS_BLOCKED);
+        mUserCreatedBubbles.remove(entry.getKey());
+    }
+
+    /**
+     * Whether this bubble was explicitly created by the user via a SysUI affordance.
+     */
+    boolean isUserCreatedBubble(String key) {
+        return mUserCreatedBubbles.contains(key);
     }
 
     /**
@@ -616,7 +638,8 @@
                     mNotificationEntryManager.updateNotifications(
                             "BubbleController.onNotificationRemoveRequested");
                     return true;
-                } else if (!userRemovedNotif && entry != null && !bubble.isUserCreated()) {
+                } else if (!userRemovedNotif && entry != null
+                        && !isUserCreatedBubble(bubble.getKey())) {
                     // This wasn't a user removal so we should remove the bubble as well
                     mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
                     return false;
@@ -676,8 +699,8 @@
     private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
         @Override
         public void onPendingEntryAdded(NotificationEntry entry) {
-            Bubble b = mBubbleData.getBubbleWithKey(entry.getKey());
-            BubbleExperimentConfig.adjustForExperiments(mContext, entry, b);
+            boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
+            BubbleExperimentConfig.adjustForExperiments(mContext, entry, previouslyUserCreated);
 
             if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
                     && canLaunchInActivityView(mContext, entry)) {
@@ -687,8 +710,8 @@
 
         @Override
         public void onPreEntryUpdated(NotificationEntry entry) {
-            Bubble b = mBubbleData.getBubbleWithKey(entry.getKey());
-            BubbleExperimentConfig.adjustForExperiments(mContext, entry, b);
+            boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
+            BubbleExperimentConfig.adjustForExperiments(mContext, entry, previouslyUserCreated);
 
             boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
                     && canLaunchInActivityView(mContext, entry);