Auto bubble some notifications (behind debug flag)
When the flag is flipped notifications that fulfill any
of these will bubble:
- use MessageStyle
- have remote input
- have an app overlay intent
- have priority >= HIGH
Test: atest SystemUITests (no new tests)
Bug: 111236845
Change-Id: Iff86d78a24ab798ddb681a1ad59e1131136814d4
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 70ee747..53862fb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -22,8 +22,11 @@
import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Point;
+import android.service.notification.StatusBarNotification;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -33,6 +36,7 @@
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -47,7 +51,13 @@
private static final String TAG = "BubbleController";
+ // Enables some subset of notifs to automatically become bubbles
+ public static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
+ // When a bubble is dismissed, recreate it as a notification
+ public static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
+
private Context mContext;
+ private BubbleDismissListener mDismissListener;
private Map<String, BubbleView> mBubbles = new HashMap<>();
private BubbleStackView mStackView;
@@ -56,6 +66,21 @@
// Bubbles get added to the status bar view
private StatusBarWindowController mStatusBarWindowController;
+ /**
+ * Listener to find out about bubble / bubble stack dismissal events.
+ */
+ public interface BubbleDismissListener {
+ /**
+ * Called when the entire stack of bubbles is dismissed by the user.
+ */
+ void onStackDismissed();
+
+ /**
+ * Called when a specific bubble is dismissed by the user.
+ */
+ void onBubbleDismissed(String key);
+ }
+
public BubbleController(Context context) {
mContext = context;
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -65,6 +90,13 @@
}
/**
+ * Set a listener to be notified of bubble dismissal events.
+ */
+ public void setDismissListener(BubbleDismissListener listener) {
+ mDismissListener = listener;
+ }
+
+ /**
* Whether or not there are bubbles present, regardless of them being visible on the
* screen (e.g. if on AOD).
*/
@@ -99,6 +131,9 @@
for (String key: mBubbles.keySet()) {
removeBubble(key);
}
+ if (mDismissListener != null) {
+ mDismissListener.onStackDismissed();
+ }
}
/**
@@ -146,6 +181,38 @@
mStackView.removeBubble(bv);
bv.getEntry().setBubbleDismissed(true);
}
+ if (mDismissListener != null) {
+ mDismissListener.onBubbleDismissed(key);
+ }
+ }
+
+ /**
+ * Sets the visibility of the bubbles, doesn't un-bubble them, just changes visibility.
+ */
+ public void updateVisibility(boolean visible) {
+ if (mStackView == null) {
+ return;
+ }
+ ArrayList<BubbleView> viewsToRemove = new ArrayList<>();
+ for (BubbleView bv : mBubbles.values()) {
+ NotificationData.Entry entry = bv.getEntry();
+ if (entry != null) {
+ if (entry.row.isRemoved() || entry.isBubbleDismissed() || entry.row.isDismissed()) {
+ viewsToRemove.add(bv);
+ }
+ }
+ }
+ 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);
+ }
}
// TODO: factor in PIP location / maybe last place user had it
@@ -166,4 +233,30 @@
return new Point(EDGE_OVERLAP, size);
}
+ /**
+ * Whether the notification should bubble or not.
+ */
+ public static boolean shouldAutoBubble(NotificationData.Entry entry, int priority,
+ boolean canAppOverlay) {
+ if (!DEBUG_ENABLE_AUTO_BUBBLE || entry.isBubbleDismissed()) {
+ return false;
+ }
+ StatusBarNotification n = entry.notification;
+ boolean hasRemoteInput = false;
+ if (n.getNotification().actions != null) {
+ for (Notification.Action action : n.getNotification().actions) {
+ if (action.getRemoteInputs() != null) {
+ hasRemoteInput = true;
+ break;
+ }
+ }
+ }
+ Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle();
+ boolean shouldBubble = priority >= NotificationManager.IMPORTANCE_HIGH
+ || Notification.MessagingStyle.class.equals(style)
+ || Notification.CATEGORY_MESSAGE.equals(n.getNotification().category)
+ || hasRemoteInput
+ || canAppOverlay;
+ return shouldBubble && !entry.isBubbleDismissed();
+ }
}