Handle suppressed bubbles

Hide or show bubble based on suppression signals from shell.
If this is the only bubble, hides or shows the bubble bar.

Bug: 273316505
Test: have a single bubble in the bubble bar, suppress the bubble,
  observe that bubble is hidden and bubble bar is not shown,
  unsuppress the bubble, check that bubble bar and bubble is shown
  again, perform check with transient and persistent taskbar
Test: have multiple bubbles, suppress one bubble, check that this bubble
  is not shown, but the bubble bar remains visible with other bubbles,
  perform the check with transient and persistent taskbar
Test: have a single bubble in bubble bar that is suppressed, receive
  notifications to trigger new bubbles, observe bubble bar is shown for
  new bubbles, check that after unsuppressing bubble, it is added back,
  perform the checks with tranient and persistent taskbar
Test: suppress a bubble and rotate the device to trigger launcher
  recreation, check that after unsuppress, the bubble is added back

Flag: com.android.wm.shell.enable_bubble_bar

Change-Id: I17e5cec750a2666feecc62213a3e8fc204ee510a
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index a64dab1..f4a7d68 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -77,6 +77,8 @@
 
     public List<BubbleInfo> bubbleInfoItems;
 
+    public List<BubbleInfo> suppressedBubbleInfoItems;
+
     /** Returns whether there are a saved bubbles. */
     public boolean hasSavedBubbles() {
         return bubbleInfoItems != null && !bubbleInfoItems.isEmpty();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 5b3c233..3065d48 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -107,6 +107,7 @@
     private final Context mContext;
     private final BubbleBarView mBarView;
     private final ArrayMap<String, BubbleBarBubble> mBubbles = new ArrayMap<>();
+    private final ArrayMap<String, BubbleBarBubble> mSuppressedBubbles = new ArrayMap<>();
 
     private static final Executor BUBBLE_STATE_EXECUTOR = Executors.newSingleThreadExecutor(
             new SimpleThreadFactory("BubbleStateUpdates-", THREAD_PRIORITY_BACKGROUND));
@@ -188,6 +189,10 @@
             }
         });
         mSharedState.bubbleInfoItems = Arrays.asList(bubbleInfoItems);
+        mSharedState.suppressedBubbleInfoItems = new ArrayList<>(mSuppressedBubbles.size());
+        for (int i = 0; i < mSuppressedBubbles.size(); i++) {
+            mSharedState.suppressedBubbleInfoItems.add(mSuppressedBubbles.valueAt(i).getInfo());
+        }
     }
 
     /** Initializes controllers. */
@@ -290,7 +295,11 @@
         if (sharedState.bubbleBarLocation != null) {
             updateBubbleBarLocationInternal(sharedState.bubbleBarLocation);
         }
-        List<BubbleInfo> bubbleInfos = sharedState.bubbleInfoItems;
+        restoreSavedBubbles(sharedState.bubbleInfoItems);
+        restoreSuppressed(sharedState.suppressedBubbleInfoItems);
+    }
+
+    private void restoreSavedBubbles(List<BubbleInfo> bubbleInfos) {
         if (bubbleInfos == null || bubbleInfos.isEmpty()) return;
         // Iterate in reverse because new bubbles are added in front and the list is in order.
         for (int i = bubbleInfos.size() - 1; i >= 0; i--) {
@@ -304,6 +313,18 @@
         }
     }
 
+    private void restoreSuppressed(List<BubbleInfo> bubbleInfos) {
+        if (bubbleInfos == null || bubbleInfos.isEmpty()) return;
+        for (BubbleInfo bubbleInfo : bubbleInfos.reversed()) {
+            BubbleBarBubble bb = mBubbleCreator.populateBubble(mContext, bubbleInfo,
+                    mBarView, /* existingBubble= */
+                    null);
+            if (bb != null) {
+                mSuppressedBubbles.put(bb.getKey(), bb);
+            }
+        }
+    }
+
     private void applyViewChanges(BubbleBarViewUpdate update) {
         final boolean isCollapsed = (update.expandedChanged && !update.expanded)
                 || (!update.expandedChanged && !mBubbleBarViewController.isExpanded());
@@ -375,9 +396,7 @@
 
         // if a bubble was updated upstream, but removed before the update was received, add it back
         if (update.updatedBubble != null && !mBubbles.containsKey(update.updatedBubble.getKey())) {
-            mBubbles.put(update.updatedBubble.getKey(), update.updatedBubble);
-            mBubbleBarViewController.addBubble(
-                    update.updatedBubble, isExpanding, suppressAnimation);
+            addBubbleInternally(update.updatedBubble, isExpanding, suppressAnimation);
         }
 
         if (update.addedBubble != null && isCollapsed) {
@@ -405,6 +424,24 @@
             mBubbleBarViewController.showOverflow(true);
         }
 
+        if (update.suppressedBubbleKey != null) {
+            BubbleBarBubble bb = mBubbles.remove(update.suppressedBubbleKey);
+            if (bb != null) {
+                mSuppressedBubbles.put(update.suppressedBubbleKey, bb);
+                mBubbleBarViewController.removeBubble(bb);
+            }
+        }
+        if (update.unsuppressedBubbleKey != null) {
+            BubbleBarBubble bb = mSuppressedBubbles.remove(update.unsuppressedBubbleKey);
+            if (bb != null) {
+                // Unsuppressing an existing bubble should not cause the bar to expand or animate
+                addBubbleInternally(bb, /* isExpanding= */ false, /* suppressAnimation= */ true);
+                if (mBubbleBarViewController.isHiddenForNoBubbles()) {
+                    mBubbleBarViewController.setHiddenForBubbles(false);
+                }
+            }
+        }
+
         // Update the visibility if this is the initial state or if there are no bubbles.
         // If this is the initial bubble, the bubble bar will become visible as part of the
         // animation.
@@ -439,12 +476,6 @@
                 mBubbleBarViewController.reorderBubbles(newOrder);
             }
         }
-        if (update.suppressedBubbleKey != null) {
-            // TODO: (b/273316505) handle suppression
-        }
-        if (update.unsuppressedBubbleKey != null) {
-            // TODO: (b/273316505) handle suppression
-        }
         if (update.selectedBubbleKey != null) {
             if (mSelectedBubble == null
                     || !update.selectedBubbleKey.equals(mSelectedBubble.getKey())) {