BubbleData [7/n]: BubbleData impl and tests!!

This adds the full implementation of BubbleData along with
test cases developed against the designed behavior of Bubbles.

See: go/bubble-order for the full policy and rules behind this.

Since BubbleData.Listener#onOrderChanged is not yet connected,
the behavior is not yet applied to UI. This will happen in a
following change (along with new BubbleStackView tests).

Bug: 123542488
Test: atest BubbleControllerTest BubbleDataTest
Change-Id: I79d4452c196945735f081d6ae0fdc673de7ae102
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index ef383ad..d071363 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -41,6 +41,7 @@
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
+import android.util.Log;
 import android.view.Display;
 import android.view.IPinnedStackController;
 import android.view.IPinnedStackListener;
@@ -83,6 +84,7 @@
 public class BubbleController implements ConfigurationController.ConfigurationListener {
 
     private static final String TAG = "BubbleController";
+    private static final boolean DEBUG = true;
 
     @Retention(SOURCE)
     @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
@@ -203,6 +205,9 @@
 
         configurationController.addCallback(this /* configurationListener */);
 
+        mBubbleData = data;
+        mBubbleData.setListener(mBubbleDataListener);
+
         mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
 
@@ -219,9 +224,6 @@
         } catch (RemoteException e) {
             e.printStackTrace();
         }
-
-        mBubbleData = data;
-        mBubbleData.setListener(mBubbleDataListener);
         mSurfaceSynchronizer = synchronizer;
 
         mBarService = IStatusBarService.Stub.asInterface(
@@ -482,7 +484,7 @@
         }
 
         @Override
-        public void onSelectionChanged(Bubble selectedBubble) {
+        public void onSelectionChanged(@Nullable Bubble selectedBubble) {
             if (mStackView != null) {
                 mStackView.setSelectedBubble(selectedBubble);
             }
@@ -506,6 +508,18 @@
         public void apply() {
             mNotificationEntryManager.updateNotifications();
             updateVisibility();
+
+            if (DEBUG) {
+                Log.d(TAG, "[BubbleData]");
+                Log.d(TAG, formatBubblesString(mBubbleData.getBubbles(),
+                        mBubbleData.getSelectedBubble()));
+
+                if (mStackView != null) {
+                    Log.d(TAG, "[BubbleStackView]");
+                    Log.d(TAG, formatBubblesString(mStackView.getBubblesOnScreen(),
+                            mStackView.getExpandedBubble()));
+                }
+            }
         }
     };
 
@@ -623,6 +637,23 @@
         entry.setShowInShadeWhenBubble(!suppressNotification);
     }
 
+    static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
+        StringBuilder sb = new StringBuilder();
+        for (Bubble bubble : bubbles) {
+            if (bubble == null) {
+                sb.append("   <null> !!!!!\n");
+            } else {
+                boolean isSelected = (bubble == selected);
+                sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
+                        ((isSelected) ? "->" : "  "),
+                        bubble.getLastActivity(),
+                        (bubble.isOngoing() ? 1 : 0),
+                        bubble.getKey()));
+            }
+        }
+        return sb.toString();
+    }
+
     /**
      * Return true if the applications with the package name is running in foreground.
      *