Inflate & make bitmaps for bubble views in background

* Simple AsyncTask to load bubble content
* BubbleController finds out about a notification being posted, if it
  is a bubble, BubbleController creates the bubble & tells it to "load"
  once it's done, BubbleData does its thing as per usual
* Anywhere we need to "reload" the bubbles (e.g. theme change) should
  use the async task
* Updates tests to work with these changes

Test: atest SystemUITests
Bug: 144719337
Change-Id: If55f27a517bff0c1f467722966a7b3b7075e9403
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 85a3959..ef8041b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -184,6 +184,8 @@
     /** Last known orientation, used to detect orientation changes in {@link #onConfigChanged}. */
     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
 
+    private boolean mInflateSynchronously;
+
     /**
      * Listener to be notified when some states of the bubbles change.
      */
@@ -357,6 +359,15 @@
     }
 
     /**
+     * Sets whether to perform inflation on the same thread as the caller. This method should only
+     * be used in tests, not in production.
+     */
+    @VisibleForTesting
+    void setInflateSynchronously(boolean inflateSynchronously) {
+        mInflateSynchronously = inflateSynchronously;
+    }
+
+    /**
      * BubbleStackView is lazily created by this method the first time a Bubble is added. This
      * method initializes the stack view and adds it to the StatusBar just above the scrim.
      */
@@ -426,15 +437,14 @@
     }
 
     private void updateForThemeChanges() {
-        mBubbleIconFactory = new BubbleIconFactory(mContext);
-        for (Bubble b: mBubbleData.getBubbles()) {
-            b.getIconView().setBubbleIconFactory(mBubbleIconFactory);
-            b.getIconView().updateViews();
-            b.getExpandedView().applyThemeAttrs();
-        }
         if (mStackView != null) {
             mStackView.onThemeChanged();
         }
+        mBubbleIconFactory = new BubbleIconFactory(mContext);
+        for (Bubble b: mBubbleData.getBubbles()) {
+            // Reload each bubble
+            b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory);
+        }
     }
 
     @Override
@@ -568,11 +578,19 @@
     }
 
     void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) {
+        if (mStackView == null) {
+            // Lazy init stack view when a bubble is created
+            ensureStackViewCreated();
+        }
         // If this is an interruptive notif, mark that it's interrupted
         if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
             notif.setInterruption();
         }
-        mBubbleData.notificationEntryUpdated(notif, suppressFlyout, showInShade);
+        Bubble bubble = mBubbleData.getOrCreateBubble(notif);
+        bubble.setInflateSynchronously(mInflateSynchronously);
+        bubble.inflate(
+                b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
+                mContext, mStackView, mBubbleIconFactory);
     }
 
     /**
@@ -789,16 +807,6 @@
 
         @Override
         public void applyUpdate(BubbleData.Update update) {
-            if (mStackView == null && update.addedBubble != null) {
-                // Lazy init stack view when the first bubble is added.
-                ensureStackViewCreated();
-            }
-
-            // If not yet initialized, ignore all other changes.
-            if (mStackView == null) {
-                return;
-            }
-
             if (update.addedBubble != null) {
                 mStackView.addBubble(update.addedBubble);
             }