Make status bar full screen when bubbles are present

* Adds new state 'bubblesShowing' to StatusBarWindowController
* When bubbles are showing status bar will be full screen and
  the touchable region will be updated

Test: manual / existing tests pass (atest SystemUITests)
Bug: 111236845
Change-Id: I6d28d0313104929fba49d326a72209f701eb78d5
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 53862fb..ddb8166 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.bubbles;
 
-import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
@@ -26,6 +26,7 @@
 import android.app.NotificationManager;
 import android.content.Context;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.service.notification.StatusBarNotification;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -58,6 +59,7 @@
 
     private Context mContext;
     private BubbleDismissListener mDismissListener;
+    private BubbleStateChangeListener mStateChangeListener;
 
     private Map<String, BubbleView> mBubbles = new HashMap<>();
     private BubbleStackView mStackView;
@@ -66,6 +68,9 @@
     // Bubbles get added to the status bar view
     private StatusBarWindowController mStatusBarWindowController;
 
+    // Used for determining view rect for touch interaction
+    private Rect mTempRect = new Rect();
+
     /**
      * Listener to find out about bubble / bubble stack dismissal events.
      */
@@ -81,6 +86,16 @@
         void onBubbleDismissed(String key);
     }
 
+    /**
+     * Listener to be notified when some states of the bubbles change.
+     */
+    public interface BubbleStateChangeListener {
+        /**
+         * Called when the stack has bubbles or no longer has bubbles.
+         */
+        void onHasBubblesChanged(boolean hasBubbles);
+    }
+
     public BubbleController(Context context) {
         mContext = context;
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -97,6 +112,13 @@
     }
 
     /**
+     * Set a listener to be notified when some states of the bubbles change.
+     */
+    public void setBubbleStateChangeListener(BubbleStateChangeListener listener) {
+        mStateChangeListener = listener;
+    }
+
+    /**
      * Whether or not there are bubbles present, regardless of them being visible on the
      * screen (e.g. if on AOD).
      */
@@ -124,7 +146,9 @@
      * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
      */
     public void dismissStack() {
-        mStackView.setVisibility(GONE);
+        if (mStackView == null) {
+            return;
+        }
         Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
         // Reset the position of the stack (TODO - or should we save / respect last user position?)
         mStackView.setPosition(startPoint.x, startPoint.y);
@@ -134,6 +158,7 @@
         if (mDismissListener != null) {
             mDismissListener.onStackDismissed();
         }
+        updateBubblesShowing();
     }
 
     /**
@@ -150,25 +175,25 @@
             bubble.setNotif(notif);
             mBubbles.put(bubble.getKey(), bubble);
 
-            boolean setPosition = false;
+            boolean setPosition = mStackView != null && mStackView.getVisibility() != VISIBLE;
             if (mStackView == null) {
                 setPosition = true;
                 mStackView = new BubbleStackView(mContext);
-                ViewGroup sbv = (ViewGroup) mStatusBarWindowController.getStatusBarView();
+                ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
                 // XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim
                 // between bubble and the shade
                 int bubblePosition = sbv.indexOfChild(sbv.findViewById(R.id.scrim_behind)) + 1;
                 sbv.addView(mStackView, bubblePosition,
                         new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
             }
-            mStackView.setVisibility(VISIBLE);
             mStackView.addBubble(bubble);
-
             if (setPosition) {
                 // Need to add the bubble to the stack before we can know the width
                 Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
                 mStackView.setPosition(startPoint.x, startPoint.y);
+                mStackView.setVisibility(VISIBLE);
             }
+            updateBubblesShowing();
         }
     }
 
@@ -177,13 +202,32 @@
      */
     public void removeBubble(String key) {
         BubbleView bv = mBubbles.get(key);
-        if (bv != null) {
+        if (mStackView != null && bv != null) {
             mStackView.removeBubble(bv);
             bv.getEntry().setBubbleDismissed(true);
         }
         if (mDismissListener != null) {
             mDismissListener.onBubbleDismissed(key);
         }
+        updateBubblesShowing();
+    }
+
+    private void updateBubblesShowing() {
+        boolean hasBubblesShowing = false;
+        for (BubbleView bv : mBubbles.values()) {
+            if (!bv.getEntry().isBubbleDismissed()) {
+                hasBubblesShowing = true;
+                break;
+            }
+        }
+        boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
+        mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
+        if (mStackView != null && !hasBubblesShowing) {
+            mStackView.setVisibility(INVISIBLE);
+        }
+        if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
+            mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
+        }
     }
 
     /**
@@ -205,14 +249,25 @@
         for (BubbleView view : viewsToRemove) {
             mBubbles.remove(view.getKey());
             mStackView.removeBubble(view);
-            if (mBubbles.size() == 0) {
-                ((ViewGroup) mStatusBarWindowController.getStatusBarView()).removeView(mStackView);
-                mStackView = null;
-            }
         }
         if (mStackView != null) {
-            mStackView.setVisibility(visible ? VISIBLE : GONE);
+            mStackView.setVisibility(visible ? VISIBLE : INVISIBLE);
+            if (!visible) {
+                collapseStack();
+            }
         }
+        updateBubblesShowing();
+    }
+
+    /**
+     * Rect indicating the touchable region for the bubble stack / expanded stack.
+     */
+    public Rect getTouchableRegion() {
+        if (mStackView == null || mStackView.getVisibility() != VISIBLE) {
+            return null;
+        }
+        mStackView.getBoundsOnScreen(mTempRect);
+        return mTempRect;
     }
 
     // TODO: factor in PIP location / maybe last place user had it