Cleanup to make loading bubble info in background easier

* Moved all the image creation code into BubbleIconFactory & document it
  possibly this is where image caching logic will occur in the future
* BubbleController owns BubbleIconFactory now
* Simplifies some logic in BubbleExpandedView

Test: atest SystemUITests
Bug: 144719337
Change-Id: I91656478e2e542fa943ca00535091ada227ee8ef
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index a6a3ce0..6e03441 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -16,16 +16,13 @@
 package com.android.systemui.bubbles;
 
 import android.annotation.Nullable;
-import android.app.Notification;
 import android.content.Context;
-import android.content.pm.LauncherApps;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Path;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
 import android.util.AttributeSet;
 import android.util.PathParser;
 import android.widget.ImageView;
@@ -283,8 +280,8 @@
             return;
         }
 
-        Drawable bubbleDrawable = getBubbleDrawable(mContext);
-        BitmapInfo badgeBitmapInfo = mBubbleIconFactory.getBadgedBitmap(mBubble);
+        Drawable bubbleDrawable = mBubbleIconFactory.getBubbleDrawable(mBubble, mContext);
+        BitmapInfo badgeBitmapInfo = mBubbleIconFactory.getBadgeBitmap(mBubble);
         BitmapInfo bubbleBitmapInfo = mBubbleIconFactory.getBubbleBitmap(bubbleDrawable,
                 badgeBitmapInfo);
         setImageBitmap(bubbleBitmapInfo.icon);
@@ -307,17 +304,4 @@
 
         animateDot();
     }
-
-    Drawable getBubbleDrawable(Context context) {
-        if (mBubble.getShortcutInfo() != null && mBubble.usingShortcutInfo()) {
-            LauncherApps launcherApps =
-                    (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
-            int density = getContext().getResources().getConfiguration().densityDpi;
-            return launcherApps.getShortcutIconDrawable(mBubble.getShortcutInfo(), density);
-        } else {
-            Notification.BubbleMetadata metadata = mBubble.getEntry().getBubbleMetadata();
-            Icon ic = metadata.getIcon();
-            return ic.loadDrawable(context);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1277736..85a3959 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -103,7 +103,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
@@ -151,6 +150,7 @@
 
     private BubbleData mBubbleData;
     @Nullable private BubbleStackView mStackView;
+    private BubbleIconFactory mBubbleIconFactory;
 
     // Tracks the id of the current (foreground) user.
     private int mCurrentUserId;
@@ -353,6 +353,7 @@
         mUserBlockedBubbles = new HashSet<>();
 
         mScreenshotHelper = new ScreenshotHelper(context);
+        mBubbleIconFactory = new BubbleIconFactory(context);
     }
 
     /**
@@ -416,13 +417,21 @@
 
     @Override
     public void onUiModeChanged() {
-        if (mStackView != null) {
-            mStackView.onThemeChanged();
-        }
+        updateForThemeChanges();
     }
 
     @Override
     public void onOverlayChanged() {
+        updateForThemeChanges();
+    }
+
+    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();
         }
@@ -509,14 +518,10 @@
         return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed;
     }
 
-    void selectBubble(Bubble bubble) {
-        mBubbleData.setSelectedBubble(bubble);
-    }
-
     @VisibleForTesting
     void selectBubble(String key) {
         Bubble bubble = mBubbleData.getBubbleWithKey(key);
-        selectBubble(bubble);
+        mBubbleData.setSelectedBubble(bubble);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 63d036d..c1705db 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -99,7 +99,6 @@
     private int mExpandedViewTouchSlop;
 
     private Bubble mBubble;
-    private String mAppName;
 
     private BubbleController mBubbleController = Dependency.get(BubbleController.class);
     private WindowManager mWindowManager;
@@ -339,26 +338,41 @@
         }
     }
 
-    /**
-     * Sets the bubble used to populate this view.
-     */
-    public void setBubble(Bubble bubble, BubbleStackView stackView) {
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "setBubble: bubble=" + (bubble != null ? bubble.getKey() : "null"));
-        }
+    void setStackView(BubbleStackView stackView) {
         mStackView = stackView;
-        mBubble = bubble;
-        mAppName = bubble.getAppName();
-
-        applyThemeAttrs();
-        showSettingsIcon();
-        updateExpandedView();
     }
 
     /**
-     * Lets activity view know it should be shown / populated.
+     * Sets the bubble used to populate this view.
      */
-    public void populateExpandedView() {
+    void update(Bubble bubble) {
+        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
+            Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
+        }
+        boolean isNew = mBubble == null;
+        if (isNew || bubble.getKey().equals(mBubble.getKey())) {
+            mBubble = bubble;
+            mSettingsIcon.setContentDescription(getResources().getString(
+                    R.string.bubbles_settings_button_description, bubble.getAppName()));
+
+            if (isNew) {
+                mBubbleIntent = mBubble.getBubbleIntent();
+                if (mBubbleIntent != null) {
+                    setContentVisibility(false);
+                    mActivityView.setVisibility(VISIBLE);
+                }
+            }
+            applyThemeAttrs();
+        } else {
+            Log.w(TAG, "Trying to update entry with different key, new bubble: "
+                    + bubble.getKey() + " old bubble: " + bubble.getKey());
+        }
+    }
+
+    /**
+     * Lets activity view know it should be shown / populated with activity content.
+     */
+    void populateExpandedView() {
         if (DEBUG_BUBBLE_EXPANDED_VIEW) {
             Log.d(TAG, "populateExpandedView: "
                     + "bubble=" + getBubbleKey());
@@ -371,38 +385,6 @@
         }
     }
 
-    /**
-     * Updates the bubble backing this view. This will not re-populate ActivityView, it will
-     * only update the deep-links in the title, and the height of the view.
-     */
-    public void update(Bubble bubble) {
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
-        }
-        if (bubble.getKey().equals(mBubble.getKey())) {
-            mBubble = bubble;
-            updateSettingsContentDescription();
-            updateHeight();
-        } else {
-            Log.w(TAG, "Trying to update entry with different key, new bubble: "
-                    + bubble.getKey() + " old bubble: " + bubble.getKey());
-        }
-    }
-
-    private void updateExpandedView() {
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "updateExpandedView: bubble="
-                    + getBubbleKey());
-        }
-
-        mBubbleIntent = mBubble.getBubbleIntent();
-        if (mBubbleIntent != null) {
-            setContentVisibility(false);
-            mActivityView.setVisibility(VISIBLE);
-        }
-        updateView();
-    }
-
     boolean performBackPressIfNeeded() {
         if (!usingActivityView()) {
             return false;
@@ -490,16 +472,6 @@
         }
     }
 
-    private void updateSettingsContentDescription() {
-        mSettingsIcon.setContentDescription(getResources().getString(
-                R.string.bubbles_settings_button_description, mAppName));
-    }
-
-    void showSettingsIcon() {
-        updateSettingsContentDescription();
-        mSettingsIcon.setVisibility(VISIBLE);
-    }
-
     /**
      * Update appearance of the expanded view being displayed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index 9ff033c..093fd0d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -15,11 +15,14 @@
  */
 package com.android.systemui.bubbles;
 
+import android.app.Notification;
 import android.content.Context;
+import android.content.pm.LauncherApps;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapInfo;
@@ -42,8 +45,27 @@
         return mContext.getResources().getDimensionPixelSize(
                 com.android.launcher3.icons.R.dimen.profile_badge_size);
     }
+    /**
+     * Returns the drawable that the developer has provided to display in the bubble.
+     */
+    Drawable getBubbleDrawable(Bubble b, Context context) {
+        if (b.getShortcutInfo() != null && b.usingShortcutInfo()) {
+            LauncherApps launcherApps =
+                    (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
+            int density = context.getResources().getConfiguration().densityDpi;
+            return launcherApps.getShortcutIconDrawable(b.getShortcutInfo(), density);
+        } else {
+            Notification.BubbleMetadata metadata = b.getEntry().getBubbleMetadata();
+            Icon ic = metadata.getIcon();
+            return ic.loadDrawable(context);
+        }
+    }
 
-    BitmapInfo getBadgedBitmap(Bubble b) {
+    /**
+     * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
+     * will include the workprofile indicator on the badge if appropriate.
+     */
+    BitmapInfo getBadgeBitmap(Bubble b) {
         Bitmap userBadgedBitmap = createIconBitmap(
                 b.getUserBadgedAppIcon(), 1f, getBadgeSize());
 
@@ -51,10 +73,12 @@
         ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
         c.setBitmap(userBadgedBitmap);
         shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
-        BitmapInfo bitmapInfo = createIconBitmap(userBadgedBitmap);
-        return bitmapInfo;
+        return createIconBitmap(userBadgedBitmap);
     }
 
+    /**
+     * Returns a {@link BitmapInfo} for the entire bubble icon including the badge.
+     */
     BitmapInfo getBubbleBitmap(Drawable bubble, BitmapInfo badge) {
         BitmapInfo bubbleIconInfo = createBadgedIconBitmap(bubble,
                 null /* user */,
@@ -64,5 +88,4 @@
                 new BitmapDrawable(mContext.getResources(), badge.icon));
         return bubbleIconInfo;
     }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 245b232..8987683 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -516,14 +516,7 @@
      * Handle theme changes.
      */
     public void onThemeChanged() {
-        // Recreate icon factory to update default adaptive icon scale.
-        mBubbleIconFactory = new BubbleIconFactory(mContext);
         setUpFlyout();
-        for (Bubble b: mBubbleData.getBubbles()) {
-            b.getIconView().setBubbleIconFactory(mBubbleIconFactory);
-            b.getIconView().updateViews();
-            b.getExpandedView().applyThemeAttrs();
-        }
     }
 
     /** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
@@ -749,10 +742,6 @@
             mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
         }
 
-        bubble.setBubbleIconFactory(mBubbleIconFactory);
-        bubble.inflate(mInflater, this);
-        bubble.getIconView().updateViews();
-
         // Set the dot position to the opposite of the side the stack is resting on, since the stack
         // resting slightly off-screen would result in the dot also being off-screen.
         bubble.getIconView().setDotPosition(
@@ -1567,9 +1556,6 @@
 
         mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         if (mIsExpanded) {
-            // First update the view so that it calculates a new height (ensuring the y position
-            // calculation is correct)
-            mExpandedBubble.getExpandedView().updateView();
             final float y = getExpandedViewY();
             if (!mExpandedViewYAnim.isRunning()) {
                 // We're not animating so set the value