Notification removal with overflow bubbles
Intercept dismissal for overflow bubbles.
Remove notifications for inactive (not in stack and overflow)
bubbles cancelled or suppressed from shade.
Set "isBubble" SysUI flag
- false for inactive bubbles still in shade,
- true for bubbles promoted from overflow.
Account for overflow bubbles in event handling
- onEntryUpdated: remove overflow bubble if no longer a bubble
- onRankingUpdated: remove overflow bubble if blocked
- expandStackAndSelectBubble: promote overflow bubble then expand
- isBubbleNotificationSuppressedFromShade: check overflow bubbles
Update tests
Enable overflow flag
----------------
Fixes: 151104690
Test: manual: cancel oldest bubble => oldest overflow bubble removed
Test: atest SystemUITests
Change-Id: I44bb623f99f9473055787cf10693f7a3bfd1c768
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e488cf2..4b50378 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -123,7 +123,8 @@
@Retention(SOURCE)
@IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
- DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT})
+ DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
+ DISMISS_OVERFLOW_MAX_REACHED})
@Target({FIELD, LOCAL_VARIABLE, PARAMETER})
@interface DismissReason {}
@@ -137,6 +138,7 @@
static final int DISMISS_USER_CHANGED = 8;
static final int DISMISS_GROUP_CANCELLED = 9;
static final int DISMISS_INVALID_INTENT = 10;
+ static final int DISMISS_OVERFLOW_MAX_REACHED = 11;
private final Context mContext;
private final NotificationEntryManager mNotificationEntryManager;
@@ -465,7 +467,6 @@
if (userRemovedNotif) {
return handleDismissalInterception(entry);
}
-
return false;
}
});
@@ -736,18 +737,18 @@
*/
public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) {
String key = entry.getKey();
- boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key)
- && !mBubbleData.getBubbleWithKey(key).showInShade();
+ boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
+ && !mBubbleData.getAnyBubbleWithkey(key).showInShade());
String groupKey = entry.getSbn().getGroupKey();
boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey);
boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey));
-
- return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed;
+ return (isSummary && isSuppressedSummary) || isSuppressedBubble;
}
void promoteBubbleFromOverflow(Bubble bubble) {
bubble.setInflateSynchronously(mInflateSynchronously);
+ setIsBubble(bubble, /* isBubble */ true);
mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
}
@@ -757,11 +758,16 @@
* @param notificationKey the notification key for the bubble to be selected
*/
public void expandStackAndSelectBubble(String notificationKey) {
- Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey);
- if (bubble != null) {
+ Bubble bubble = mBubbleData.getBubbleInStackWithKey(notificationKey);
+ if (bubble == null) {
+ bubble = mBubbleData.getOverflowBubbleWithKey(notificationKey);
+ if (bubble != null) {
+ mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
+ }
+ } else if (bubble.getEntry().isBubble()){
mBubbleData.setSelectedBubble(bubble);
- mBubbleData.setExpanded(true);
}
+ mBubbleData.setExpanded(true);
}
/**
@@ -856,7 +862,7 @@
*/
@MainThread
void removeBubble(NotificationEntry entry, int reason) {
- if (mBubbleData.hasBubbleWithKey(entry.getKey())) {
+ if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
mBubbleData.notificationEntryRemoved(entry, reason);
}
}
@@ -871,7 +877,7 @@
private void onEntryUpdated(NotificationEntry entry) {
boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
&& canLaunchInActivityView(mContext, entry);
- if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
+ if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry, DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
@@ -910,7 +916,7 @@
String key = orderedKeys[i];
NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
rankingMap.getRanking(key, mTmpRanking);
- boolean isActiveBubble = mBubbleData.hasBubbleWithKey(key);
+ boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
if (isActiveBubble && !mTmpRanking.canBubble()) {
mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
} else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
@@ -920,6 +926,19 @@
}
}
+ private void setIsBubble(Bubble b, boolean isBubble) {
+ if (isBubble) {
+ b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE;
+ } else {
+ b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
+ }
+ try {
+ mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0);
+ } catch (RemoteException e) {
+ // Bad things have happened
+ }
+ }
+
@SuppressWarnings("FieldCanBeLocal")
private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
@@ -942,36 +961,36 @@
final Bubble bubble = removed.first;
@DismissReason final int reason = removed.second;
mStackView.removeBubble(bubble);
+
// If the bubble is removed for user switching, leave the notification in place.
- if (reason != DISMISS_USER_CHANGED) {
- if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
- && !bubble.showInShade()) {
+ if (reason == DISMISS_USER_CHANGED) {
+ continue;
+ }
+ if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
+ if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey())
+ && (!bubble.showInShade()
+ || reason == DISMISS_NOTIF_CANCEL
+ || reason == DISMISS_GROUP_CANCELLED)) {
// The bubble is now gone & the notification is hidden from the shade, so
// time to actually remove it
for (NotifCallback cb : mCallbacks) {
cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
}
} else {
- // Update the flag for SysUI
- bubble.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
+ if (bubble.getEntry().isBubble() && bubble.showInShade()) {
+ setIsBubble(bubble, /* isBubble */ false);
+ }
if (bubble.getEntry().getRow() != null) {
bubble.getEntry().getRow().updateBubbleButton();
}
-
- // Update the state in NotificationManagerService
- try {
- mBarService.onNotificationBubbleChanged(bubble.getKey(),
- false /* isBubble */, 0 /* flags */);
- } catch (RemoteException e) {
- }
}
- final String groupKey = bubble.getEntry().getSbn().getGroupKey();
- if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
- // Time to potentially remove the summary
- for (NotifCallback cb : mCallbacks) {
- cb.maybeCancelSummary(bubble.getEntry());
- }
+ }
+ final String groupKey = bubble.getEntry().getSbn().getGroupKey();
+ if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
+ // Time to potentially remove the summary
+ for (NotifCallback cb : mCallbacks) {
+ cb.maybeCancelSummary(bubble.getEntry());
}
}
}
@@ -1020,7 +1039,7 @@
}
Log.d(TAG, "\n[BubbleData] overflow:");
Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getOverflowBubbles(),
- null));
+ null) + "\n");
}
}
};
@@ -1039,21 +1058,19 @@
if (entry == null) {
return false;
}
-
- final boolean interceptBubbleDismissal = mBubbleData.hasBubbleWithKey(entry.getKey())
- && entry.isBubble();
- final boolean interceptSummaryDismissal = isSummaryOfBubbles(entry);
-
- if (interceptSummaryDismissal) {
+ if (isSummaryOfBubbles(entry)) {
handleSummaryDismissalInterception(entry);
- } else if (interceptBubbleDismissal) {
- Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey());
+ } else {
+ Bubble bubble = mBubbleData.getBubbleInStackWithKey(entry.getKey());
+ if (bubble == null || !entry.isBubble()) {
+ bubble = mBubbleData.getOverflowBubbleWithKey(entry.getKey());
+ }
+ if (bubble == null) {
+ return false;
+ }
bubble.setSuppressNotification(true);
bubble.setShowDot(false /* show */);
- } else {
- return false;
}
-
// Update the shade
for (NotifCallback cb : mCallbacks) {
cb.invalidateNotifications("BubbleController.handleDismissalInterception");
@@ -1082,11 +1099,11 @@
if (children != null) {
for (int i = 0; i < children.size(); i++) {
NotificationEntry child = children.get(i);
- if (mBubbleData.hasBubbleWithKey(child.getKey())) {
+ if (mBubbleData.hasAnyBubbleWithKey(child.getKey())) {
// Suppress the bubbled child
// As far as group manager is concerned, once a child is no longer shown
// in the shade, it is essentially removed.
- Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey());
+ Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey());
mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
bubbleChild.setSuppressNotification(true);
bubbleChild.setShowDot(false /* show */);