summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff DeCew <jeffdq@google.com> 2022-01-12 20:48:31 +0000
committer Jeff DeCew <jeffdq@google.com> 2022-01-15 00:38:28 +0000
commitfd8687974a3f9df804aab94f98e19c2a57a487f7 (patch)
tree7bca39989e7c6d4bd1e8fb57174cceedf95eee65
parent1439429dd6cbdea912c5746fce9dce7e3ad5bbfa (diff)
Refactor legacy pipeline group alert transfer & group manager
This fixes cases where changes to Group.suppressed or Group.alertOverride were not correctly emitted when the parent was updated before the priority child. Bug: 185680162 Test: manual testing w/ telegram and whatsapp Test: atest NotificationGroupAlertTransferHelperTest Test: atest NotificationGroupManagerLegacyTest Change-Id: I0ee8e9cf6c17a287f7ecbba6318e154ee8889174
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java259
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt294
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java154
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java34
8 files changed, 689 insertions, 127 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index cbc113ba91bf..c3cc97bc14a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -24,6 +24,7 @@ import android.widget.ImageView;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* A util class for various reusable functions
@@ -73,4 +74,20 @@ public class NotificationUtils {
return (int) (dimensionPixelSize * factor);
}
+ /** Get the notification key, reformatted for logging, for the (optional) entry */
+ public static String logKey(NotificationEntry entry) {
+ if (entry == null) {
+ return "null";
+ }
+ return logKey(entry.getKey());
+ }
+
+ /** Removes newlines from the notification key to prettify apps that have these in the tag */
+ public static String logKey(String key) {
+ if (key == null) {
+ return "null";
+ }
+ return key.replace("\n", "");
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index 3b93020f024f..cd2affead92a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar.notification.collection.legacy;
+import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
+
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
@@ -49,6 +53,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeSet;
+import java.util.function.Function;
import javax.inject.Inject;
@@ -79,10 +84,9 @@ public class NotificationGroupManagerLegacy implements
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners =
new ArraySet<>();
- private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
private final Optional<Bubbles> mBubblesOptional;
- private final EventBuffer mEventBuffer = new EventBuffer();
+ private final GroupEventDispatcher mEventDispatcher = new GroupEventDispatcher(mGroupMap::get);
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
@@ -105,7 +109,7 @@ public class NotificationGroupManagerLegacy implements
* Add a listener for changes to groups.
*/
public void registerGroupChangeListener(OnGroupChangeListener listener) {
- mGroupChangeListeners.add(listener);
+ mEventDispatcher.registerGroupChangeListener(listener);
}
@Override
@@ -156,13 +160,15 @@ public class NotificationGroupManagerLegacy implements
*/
public void onEntryRemoved(NotificationEntry removed) {
if (SPEW) {
- Log.d(TAG, "onEntryRemoved: entry=" + removed);
+ Log.d(TAG, "onEntryRemoved: entry=" + logKey(removed));
}
+ mEventDispatcher.openBufferScope();
onEntryRemovedInternal(removed, removed.getSbn());
StatusBarNotification oldSbn = mIsolatedEntries.remove(removed.getKey());
if (oldSbn != null) {
updateSuppression(mGroupMap.get(oldSbn.getGroupKey()));
}
+ mEventDispatcher.closeBufferScope();
}
/**
@@ -190,7 +196,8 @@ public class NotificationGroupManagerLegacy implements
return;
}
if (SPEW) {
- Log.d(TAG, "onEntryRemovedInternal: entry=" + removed + " group=" + group.groupKey);
+ Log.d(TAG, "onEntryRemovedInternal: entry=" + logKey(removed)
+ + " group=" + logGroupKey(group));
}
if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
group.children.remove(removed.getKey());
@@ -201,9 +208,7 @@ public class NotificationGroupManagerLegacy implements
if (group.children.isEmpty()) {
if (group.summary == null) {
mGroupMap.remove(groupKey);
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupRemoved(group, groupKey);
- }
+ mEventDispatcher.notifyGroupRemoved(group);
}
}
}
@@ -213,10 +218,12 @@ public class NotificationGroupManagerLegacy implements
*/
public void onEntryAdded(final NotificationEntry added) {
if (SPEW) {
- Log.d(TAG, "onEntryAdded: entry=" + added);
+ Log.d(TAG, "onEntryAdded: entry=" + logKey(added));
}
+ mEventDispatcher.openBufferScope();
updateIsolation(added);
onEntryAddedInternal(added);
+ mEventDispatcher.closeBufferScope();
}
private void onEntryAddedInternal(final NotificationEntry added) {
@@ -230,19 +237,17 @@ public class NotificationGroupManagerLegacy implements
if (group == null) {
group = new NotificationGroup(groupKey);
mGroupMap.put(groupKey, group);
-
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupCreated(group, groupKey);
- }
+ mEventDispatcher.notifyGroupCreated(group);
}
if (SPEW) {
- Log.d(TAG, "onEntryAddedInternal: entry=" + added + " group=" + group.groupKey);
+ Log.d(TAG, "onEntryAddedInternal: entry=" + logKey(added)
+ + " group=" + logGroupKey(group));
}
if (isGroupChild) {
NotificationEntry existing = group.children.get(added.getKey());
if (existing != null && existing != added) {
Throwable existingThrowable = existing.getDebugThrowable();
- Log.wtf(TAG, "Inconsistent entries found with the same key " + added.getKey()
+ Log.wtf(TAG, "Inconsistent entries found with the same key " + logKey(added)
+ "existing removed: " + existing.isRowRemoved()
+ (existingThrowable != null
? Log.getStackTraceString(existingThrowable) + "\n" : "")
@@ -262,9 +267,7 @@ public class NotificationGroupManagerLegacy implements
for (NotificationEntry child : childrenCopy) {
onEntryBecomingChild(child);
}
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupCreatedFromChildren(group);
- }
+ mEventDispatcher.notifyGroupsChanged();
}
}
}
@@ -323,30 +326,27 @@ public class NotificationGroupManagerLegacy implements
boolean alertOverrideChanged = prevAlertOverride != group.alertOverride;
boolean suppressionChanged = prevSuppressed != group.suppressed;
if (alertOverrideChanged || suppressionChanged) {
- if (DEBUG && alertOverrideChanged) {
- Log.d(TAG, "updateSuppression: alertOverride was=" + prevAlertOverride
- + " now=" + group.alertOverride + " group:\n" + group);
- }
- if (DEBUG && suppressionChanged) {
- Log.d(TAG,
- "updateSuppression: suppressed changed to " + group.suppressed
- + " group:\n" + group);
- }
- if (!mIsUpdatingUnchangedGroup) {
+ if (DEBUG) {
+ Log.d(TAG, "updateSuppression:"
+ + " willNotifyListeners=" + !mIsUpdatingUnchangedGroup
+ + " changes for group:\n" + group);
if (alertOverrideChanged) {
- mEventBuffer.notifyAlertOverrideChanged(group, prevAlertOverride);
+ Log.d(TAG, "updateSuppression: alertOverride was=" + logKey(prevAlertOverride)
+ + " now=" + logKey(group.alertOverride));
}
if (suppressionChanged) {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupSuppressionChanged(group, group.suppressed);
- }
- }
- mEventBuffer.notifyGroupsChanged();
- } else {
- if (DEBUG) {
- Log.d(TAG, group + " did not notify listeners of above change(s)");
+ Log.d(TAG, "updateSuppression: suppressed changed to " + group.suppressed);
}
}
+ if (alertOverrideChanged) {
+ mEventDispatcher.notifyAlertOverrideChanged(group, prevAlertOverride);
+ }
+ if (suppressionChanged) {
+ mEventDispatcher.notifySuppressedChanged(group);
+ }
+ if (!mIsUpdatingUnchangedGroup) {
+ mEventDispatcher.notifyGroupsChanged();
+ }
}
}
@@ -369,13 +369,15 @@ public class NotificationGroupManagerLegacy implements
// but which should be alerting (because priority conversations are isolated), find it.
if (group == null || group.summary == null) {
if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary");
+ Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary"
+ + " group=" + logGroupKey(group));
}
return null;
}
if (isIsolated(group.summary.getKey())) {
if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: isolated group");
+ Log.d(TAG, "getPriorityConversationAlertOverride: isolated group"
+ + " group=" + logGroupKey(group));
}
return null;
}
@@ -386,7 +388,8 @@ public class NotificationGroupManagerLegacy implements
if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
== Notification.GROUP_ALERT_CHILDREN) {
if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: summary == GROUP_ALERT_CHILDREN");
+ Log.d(TAG, "getPriorityConversationAlertOverride: summary == GROUP_ALERT_CHILDREN"
+ + " group=" + logGroupKey(group));
}
return null;
}
@@ -396,7 +399,8 @@ public class NotificationGroupManagerLegacy implements
HashMap<String, NotificationEntry> children = getImportantConversations(group);
if (children == null || children.isEmpty()) {
if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations");
+ Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations"
+ + " group=" + logGroupKey(group));
}
return null;
}
@@ -408,8 +412,8 @@ public class NotificationGroupManagerLegacy implements
if (child.getSbn().getNotification().getGroupAlertBehavior()
!= Notification.GROUP_ALERT_SUMMARY) {
if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: "
- + "child != GROUP_ALERT_SUMMARY");
+ Log.d(TAG, "getPriorityConversationAlertOverride: child != GROUP_ALERT_SUMMARY"
+ + " group=" + logGroupKey(group));
}
return null;
}
@@ -450,13 +454,16 @@ public class NotificationGroupManagerLegacy implements
}
if (newestChild != null && importantChildKeys.contains(newestChild.getKey())) {
if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: result=" + newestChild);
+ Log.d(TAG, "getPriorityConversationAlertOverride:"
+ + " result=" + logKey(newestChild)
+ + " group=" + logGroupKey(group));
}
return newestChild;
}
if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: result=null, newestChild="
- + newestChild);
+ Log.d(TAG, "getPriorityConversationAlertOverride:"
+ + " result=null newestChild=" + logKey(newestChild)
+ + " group=" + logGroupKey(group));
}
return null;
}
@@ -500,7 +507,7 @@ public class NotificationGroupManagerLegacy implements
*/
public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
if (SPEW) {
- Log.d(TAG, "onEntryUpdated: entry=" + entry);
+ Log.d(TAG, "onEntryUpdated: entry=" + logKey(entry));
}
onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
oldNotification.getNotification().isGroupSummary());
@@ -519,6 +526,7 @@ public class NotificationGroupManagerLegacy implements
boolean groupKeysChanged = !oldGroupKey.equals(newGroupKey);
boolean wasGroupChild = isGroupChild(entry.getKey(), oldIsGroup, oldIsGroupSummary);
boolean isGroupChild = isGroupChild(entry.getSbn());
+ mEventDispatcher.openBufferScope();
mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild;
if (mGroupMap.get(getGroupKey(entry.getKey(), oldGroupKey)) != null) {
onEntryRemovedInternal(entry, oldGroupKey, oldIsGroup, oldIsGroupSummary);
@@ -536,6 +544,7 @@ public class NotificationGroupManagerLegacy implements
} else if (!wasGroupChild && isGroupChild) {
onEntryBecomingChild(entry);
}
+ mEventDispatcher.closeBufferScope();
}
/**
@@ -798,7 +807,7 @@ public class NotificationGroupManagerLegacy implements
*/
private void isolateNotification(NotificationEntry entry) {
if (SPEW) {
- Log.d(TAG, "isolateNotification: entry=" + entry);
+ Log.d(TAG, "isolateNotification: entry=" + logKey(entry));
}
// We will be isolated now, so lets update the groups
onEntryRemovedInternal(entry, entry.getSbn());
@@ -811,9 +820,7 @@ public class NotificationGroupManagerLegacy implements
// When the notification gets added afterwards it is already isolated and therefore
// it doesn't lead to an update.
updateSuppression(mGroupMap.get(entry.getSbn().getGroupKey()));
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupsChanged();
- }
+ mEventDispatcher.notifyGroupsChanged();
}
/**
@@ -827,7 +834,7 @@ public class NotificationGroupManagerLegacy implements
// listener may be unable to correctly determine the true state of the group. By delaying
// the alertOverride change until after the add phase, we can ensure that listeners only
// have to handle a consistent state.
- mEventBuffer.startBuffering();
+ mEventDispatcher.openBufferScope();
boolean isIsolated = isIsolated(entry.getSbn().getKey());
if (shouldIsolate(entry)) {
if (!isIsolated) {
@@ -836,7 +843,7 @@ public class NotificationGroupManagerLegacy implements
} else if (isIsolated) {
stopIsolatingNotification(entry);
}
- mEventBuffer.flushAndStopBuffering();
+ mEventDispatcher.closeBufferScope();
}
/**
@@ -846,15 +853,13 @@ public class NotificationGroupManagerLegacy implements
*/
private void stopIsolatingNotification(NotificationEntry entry) {
if (SPEW) {
- Log.d(TAG, "stopIsolatingNotification: entry=" + entry);
+ Log.d(TAG, "stopIsolatingNotification: entry=" + logKey(entry));
}
// not isolated anymore, we need to update the groups
onEntryRemovedInternal(entry, entry.getSbn());
mIsolatedEntries.remove(entry.getKey());
onEntryAddedInternal(entry);
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupsChanged();
- }
+ mEventDispatcher.notifyGroupsChanged();
}
private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
@@ -874,11 +879,11 @@ public class NotificationGroupManagerLegacy implements
pw.println("GroupManagerLegacy state:");
pw.println(" number of groups: " + mGroupMap.size());
for (Map.Entry<String, NotificationGroup> entry : mGroupMap.entrySet()) {
- pw.println("\n key: " + entry.getKey()); pw.println(entry.getValue());
+ pw.println("\n key: " + logKey(entry.getKey())); pw.println(entry.getValue());
}
pw.println("\n isolated entries: " + mIsolatedEntries.size());
for (Map.Entry<String, StatusBarNotification> entry : mIsolatedEntries.entrySet()) {
- pw.print(" "); pw.print(entry.getKey());
+ pw.print(" "); pw.print(logKey(entry.getKey()));
pw.print(", "); pw.println(entry.getValue());
}
}
@@ -888,6 +893,14 @@ public class NotificationGroupManagerLegacy implements
setStatusBarState(newState);
}
+ /** Get the group key, reformatted for logging, for the (optional) group */
+ public static String logGroupKey(NotificationGroup group) {
+ if (group == null) {
+ return "null";
+ }
+ return logKey(group.groupKey);
+ }
+
/**
* A record of a notification being posted, containing the time of the post and the key of the
* notification entry. These are stored in a TreeSet by the NotificationGroup and used to
@@ -977,18 +990,35 @@ public class NotificationGroupManagerLegacy implements
* When buffering, instead of notifying the listeners it will set internal state that will allow
* it to notify listeners of those events later
*/
- private class EventBuffer {
+ static class GroupEventDispatcher {
+ private final Function<String, NotificationGroup> mGroupMapGetter;
+ private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
private final HashMap<String, NotificationEntry> mOldAlertOverrideByGroup = new HashMap<>();
- private boolean mIsBuffering = false;
+ private final HashMap<String, Boolean> mOldSuppressedByGroup = new HashMap<>();
+ private int mBufferScopeDepth = 0;
private boolean mDidGroupsChange = false;
+ GroupEventDispatcher(Function<String, NotificationGroup> groupMapGetter) {
+ mGroupMapGetter = requireNonNull(groupMapGetter);
+ }
+
+ void registerGroupChangeListener(OnGroupChangeListener listener) {
+ mGroupChangeListeners.add(listener);
+ }
+
+ private boolean isBuffering() {
+ return mBufferScopeDepth > 0;
+ }
+
void notifyAlertOverrideChanged(NotificationGroup group,
NotificationEntry oldAlertOverride) {
- if (mIsBuffering) {
+ if (isBuffering()) {
// The value in this map is the override before the event. If there is an entry
// already in the map, then we are effectively coalescing two events, which means
// we need to preserve the original initial value.
- mOldAlertOverrideByGroup.putIfAbsent(group.groupKey, oldAlertOverride);
+ if (!mOldAlertOverrideByGroup.containsKey(group.groupKey)) {
+ mOldAlertOverrideByGroup.put(group.groupKey, oldAlertOverride);
+ }
} else {
for (OnGroupChangeListener listener : mGroupChangeListeners) {
listener.onGroupAlertOverrideChanged(group, oldAlertOverride,
@@ -997,8 +1027,21 @@ public class NotificationGroupManagerLegacy implements
}
}
+ void notifySuppressedChanged(NotificationGroup group) {
+ if (isBuffering()) {
+ // The value in this map is the 'suppressed' before the event. If there is a value
+ // already in the map, then we are effectively coalescing two events, which means
+ // we need to preserve the original initial value.
+ mOldSuppressedByGroup.putIfAbsent(group.groupKey, !group.suppressed);
+ } else {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupSuppressionChanged(group, group.suppressed);
+ }
+ }
+ }
+
void notifyGroupsChanged() {
- if (mIsBuffering) {
+ if (isBuffering()) {
mDidGroupsChange = true;
} else {
for (OnGroupChangeListener listener : mGroupChangeListeners) {
@@ -1007,26 +1050,94 @@ public class NotificationGroupManagerLegacy implements
}
}
- void startBuffering() {
- mIsBuffering = true;
+ void notifyGroupCreated(NotificationGroup group) {
+ // NOTE: given how this event is used, it doesn't need to be buffered right now
+ final String groupKey = group.groupKey;
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupCreated(group, groupKey);
+ }
+ }
+
+ void notifyGroupRemoved(NotificationGroup group) {
+ // NOTE: given how this event is used, it doesn't need to be buffered right now
+ final String groupKey = group.groupKey;
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupRemoved(group, groupKey);
+ }
+ }
+
+ void openBufferScope() {
+ mBufferScopeDepth++;
+ if (SPEW) {
+ Log.d(TAG, "openBufferScope: scopeDepth=" + mBufferScopeDepth);
+ }
+ }
+
+ void closeBufferScope() {
+ mBufferScopeDepth--;
+ if (SPEW) {
+ Log.d(TAG, "closeBufferScope: scopeDepth=" + mBufferScopeDepth);
+ }
+ // Flush buffered events if the last buffer scope has closed
+ if (!isBuffering()) {
+ flushBuffer();
+ }
}
- void flushAndStopBuffering() {
- // stop buffering so that we can call our own helpers
- mIsBuffering = false;
+ private void flushBuffer() {
+ if (SPEW) {
+ Log.d(TAG, "flushBuffer: "
+ + " suppressed.size=" + mOldSuppressedByGroup.size()
+ + " alertOverride.size=" + mOldAlertOverrideByGroup.size()
+ + " mDidGroupsChange=" + mDidGroupsChange);
+ }
+ // alert all group suppressed changes for groups that were not removed
+ for (Map.Entry<String, Boolean> entry : mOldSuppressedByGroup.entrySet()) {
+ NotificationGroup group = mGroupMapGetter.apply(entry.getKey());
+ if (group == null) {
+ // The group can be null if this suppressed changed before the group was
+ // permanently removed, meaning that there's no guarantee that listeners will
+ // that field clear.
+ if (SPEW) {
+ Log.d(TAG, "flushBuffer: suppressed:"
+ + " cannot report for removed group: " + logKey(entry.getKey()));
+ }
+ continue;
+ }
+ boolean oldSuppressed = entry.getValue();
+ if (group.suppressed == oldSuppressed) {
+ // If the final suppressed equals the initial, it means we coalesced two
+ // events which undid the change, so we can drop it entirely.
+ if (SPEW) {
+ Log.d(TAG, "flushBuffer: suppressed:"
+ + " did not change for group: " + logKey(entry.getKey()));
+ }
+ continue;
+ }
+ notifySuppressedChanged(group);
+ }
+ mOldSuppressedByGroup.clear();
// alert all group alert override changes for groups that were not removed
for (Map.Entry<String, NotificationEntry> entry : mOldAlertOverrideByGroup.entrySet()) {
- NotificationGroup group = mGroupMap.get(entry.getKey());
+ NotificationGroup group = mGroupMapGetter.apply(entry.getKey());
if (group == null) {
// The group can be null if this alertOverride changed before the group was
// permanently removed, meaning that there's no guarantee that listeners will
// that field clear.
+ if (SPEW) {
+ Log.d(TAG, "flushBuffer: alertOverride:"
+ + " cannot report for removed group: " + entry.getKey());
+ }
continue;
}
NotificationEntry oldAlertOverride = entry.getValue();
if (group.alertOverride == oldAlertOverride) {
// If the final alertOverride equals the initial, it means we coalesced two
// events which undid the change, so we can drop it entirely.
+ if (SPEW) {
+ Log.d(TAG, "flushBuffer: alertOverride:"
+ + " did not change for group: " + logKey(entry.getKey()));
+ }
continue;
}
notifyAlertOverrideChanged(group, oldAlertOverride);
@@ -1088,14 +1199,6 @@ public class NotificationGroupManagerLegacy implements
@Nullable NotificationEntry newAlertOverride) {}
/**
- * A group of children just received a summary notification and should therefore become
- * children of it.
- *
- * @param group the group created
- */
- default void onGroupCreatedFromChildren(NotificationGroup group) {}
-
- /**
* The groups have changed. This can happen if the isolation of a child has changes or if a
* group became suppressed / unsuppressed
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 7c3399de21cb..5833ec286983 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -93,7 +93,6 @@ import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
@@ -689,11 +688,6 @@ public class NotificationStackScrollLayoutController {
(changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
@Override
- public void onGroupCreatedFromChildren(NotificationGroup group) {
- mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren");
- }
-
- @Override
public void onGroupsChanged() {
mStatusBar.requestNotificationUpdate("onGroupsChanged");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 6632c9cf69e3..8bababfb9d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
+import static com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.logGroupKey;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
@@ -148,7 +151,9 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
@Override
public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
if (DEBUG) {
- Log.d(TAG, "!! onGroupSuppressionChanged: group.summary=" + group.summary
+ Log.d(TAG, "!! onGroupSuppressionChanged:"
+ + " group=" + logGroupKey(group)
+ + " group.summary=" + logKey(group.summary)
+ " suppressed=" + suppressed);
}
NotificationEntry oldAlertOverride = group.alertOverride;
@@ -160,9 +165,11 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
@Nullable NotificationEntry oldAlertOverride,
@Nullable NotificationEntry newAlertOverride) {
if (DEBUG) {
- Log.d(TAG, "!! onGroupAlertOverrideChanged: group.summary=" + group.summary
- + " oldAlertOverride=" + oldAlertOverride
- + " newAlertOverride=" + newAlertOverride);
+ Log.d(TAG, "!! onGroupAlertOverrideChanged:"
+ + " group=" + logGroupKey(group)
+ + " group.summary=" + logKey(group.summary)
+ + " oldAlertOverride=" + logKey(oldAlertOverride)
+ + " newAlertOverride=" + logKey(newAlertOverride));
}
onGroupChanged(group, oldAlertOverride);
}
@@ -208,7 +215,9 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
if (DEBUG) {
- Log.d(TAG, "!! onHeadsUpStateChanged: entry=" + entry + " isHeadsUp=" + isHeadsUp);
+ Log.d(TAG, "!! onHeadsUpStateChanged:"
+ + " entry=" + logKey(entry)
+ + " isHeadsUp=" + isHeadsUp);
}
if (isHeadsUp && entry.getSbn().getNotification().isGroupSummary()) {
// a group summary is alerting; trigger the forward transfer checks
@@ -240,6 +249,9 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
} else if (mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())) {
handleSuppressedSummaryAlerted(summary, oldAlertOverride);
}
+ if (DEBUG) {
+ Log.d(TAG, "checkForForwardAlertTransfer: done");
+ }
}
private final NotificationEntryListener mNotificationEntryListener =
@@ -249,7 +261,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
if (DEBUG) {
- Log.d(TAG, "!! onPendingEntryAdded: entry=" + entry);
+ Log.d(TAG, "!! onPendingEntryAdded: entry=" + logKey(entry));
}
String groupKey = mGroupManager.getGroupKey(entry.getSbn());
GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
@@ -345,7 +357,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
NotificationEntry oldAlertOverride) {
if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + summary);
+ Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + logKey(summary));
}
GroupAlertEntry groupAlertEntry =
mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
@@ -407,7 +419,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
*/
private void handleOverriddenSummaryAlerted(NotificationEntry summary) {
if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + summary);
+ Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + logKey(summary));
}
GroupAlertEntry groupAlertEntry =
mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
@@ -481,7 +493,9 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
}
if (DEBUG) {
- Log.d(TAG, "transferAlertState: fromEntry=" + fromEntry + " toEntry=" + toEntry);
+ Log.d(TAG, "transferAlertState:"
+ + " fromEntry=" + logKey(fromEntry)
+ + " toEntry=" + logKey(toEntry));
}
transferAlertState(fromEntry, toEntry);
}
@@ -583,7 +597,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
if ((params.getContentViews() & contentFlag) == 0) {
if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible: async requestRebind entry=" + entry);
+ Log.d(TAG, "alertNotificationWhenPossible:"
+ + " async requestRebind entry=" + logKey(entry));
}
mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
params.requireContentViews(contentFlag);
@@ -593,6 +608,10 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
if (alertInfo.isStillValid()) {
alertNotificationWhenPossible(entry);
} else {
+ if (DEBUG) {
+ Log.d(TAG, "alertNotificationWhenPossible:"
+ + " markContentViewsFreeable entry=" + logKey(entry));
+ }
// The transfer is no longer valid. Free the content.
mRowContentBindStage.getStageParams(entry).markContentViewsFreeable(
contentFlag);
@@ -604,12 +623,14 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
}
if (mHeadsUpManager.isAlerting(entry.getKey())) {
if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible: continue alerting entry=" + entry);
+ Log.d(TAG, "alertNotificationWhenPossible:"
+ + " continue alerting entry=" + logKey(entry));
}
mHeadsUpManager.updateNotification(entry.getKey(), true /* alert */);
} else {
if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible: start alerting entry=" + entry);
+ Log.d(TAG, "alertNotificationWhenPossible:"
+ + " start alerting entry=" + logKey(entry));
}
mHeadsUpManager.showNotification(entry);
}
@@ -657,7 +678,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
// Notification is aborted due to the transfer being explicitly cancelled
return false;
}
- if (mEntry.getSbn().getGroupKey() != mOriginalNotification.getGroupKey()) {
+ if (!mEntry.getSbn().getGroupKey().equals(mOriginalNotification.getGroupKey())) {
// Groups have changed
return false;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt
new file mode 100644
index 000000000000..c17fe6f8b7e2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/legacy/GroupEventDispatcherTest.kt
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.legacy
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.GroupEventDispatcher
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener
+import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class GroupEventDispatcherTest : SysuiTestCase() {
+ val groupMap = mutableMapOf<String, NotificationGroup>()
+ val groupTestHelper = NotificationGroupTestHelper(mContext)
+
+ private val dispatcher = GroupEventDispatcher(groupMap::get)
+ private val listener: OnGroupChangeListener = mock()
+
+ @Before
+ fun setup() {
+ dispatcher.registerGroupChangeListener(listener)
+ }
+
+ @Test
+ fun testOnGroupsChangedUnbuffered() {
+ dispatcher.notifyGroupsChanged()
+ verify(listener).onGroupsChanged()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testOnGroupsChangedBuffered() {
+ dispatcher.openBufferScope()
+ dispatcher.notifyGroupsChanged()
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope()
+ verify(listener).onGroupsChanged()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testOnGroupsChangedDoubleBuffered() {
+ dispatcher.openBufferScope()
+ dispatcher.notifyGroupsChanged()
+ dispatcher.openBufferScope() // open a nested buffer scope
+ dispatcher.notifyGroupsChanged()
+ dispatcher.closeBufferScope() // should NOT flush events
+ dispatcher.notifyGroupsChanged()
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope() // this SHOULD flush events
+ verify(listener).onGroupsChanged()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testOnGroupsChangedBufferCoalesces() {
+ dispatcher.openBufferScope()
+ dispatcher.notifyGroupsChanged()
+ dispatcher.notifyGroupsChanged()
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope()
+ verify(listener).onGroupsChanged()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testOnGroupCreatedIsNeverBuffered() {
+ val group = addGroup(1)
+
+ dispatcher.openBufferScope()
+ dispatcher.notifyGroupCreated(group)
+ verify(listener).onGroupCreated(group, group.groupKey)
+ verifyNoMoreInteractions(listener)
+
+ dispatcher.closeBufferScope()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testOnGroupRemovedIsNeverBuffered() {
+ val group = addGroup(1)
+
+ dispatcher.openBufferScope()
+ dispatcher.notifyGroupRemoved(group)
+ verify(listener).onGroupRemoved(group, group.groupKey)
+ verifyNoMoreInteractions(listener)
+
+ dispatcher.closeBufferScope()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testAlertOverrideAddedUnbuffered() {
+ val group = addGroup(1)
+ val newAlertEntry = groupTestHelper.createChildNotification()
+ group.alertOverride = newAlertEntry
+ dispatcher.notifyAlertOverrideChanged(group, null)
+ verify(listener).onGroupAlertOverrideChanged(group, null, newAlertEntry)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testAlertOverrideRemovedUnbuffered() {
+ val group = addGroup(1)
+ val oldAlertEntry = groupTestHelper.createChildNotification()
+ dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+ verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, null)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testAlertOverrideChangedUnbuffered() {
+ val group = addGroup(1)
+ val oldAlertEntry = groupTestHelper.createChildNotification()
+ val newAlertEntry = groupTestHelper.createChildNotification()
+ group.alertOverride = newAlertEntry
+ dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+ verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testAlertOverrideChangedBuffered() {
+ dispatcher.openBufferScope()
+ val group = addGroup(1)
+ val oldAlertEntry = groupTestHelper.createChildNotification()
+ val newAlertEntry = groupTestHelper.createChildNotification()
+ group.alertOverride = newAlertEntry
+ dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope()
+ verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testAlertOverrideIgnoredIfRemoved() {
+ dispatcher.openBufferScope()
+ val group = addGroup(1)
+ val oldAlertEntry = groupTestHelper.createChildNotification()
+ val newAlertEntry = groupTestHelper.createChildNotification()
+ group.alertOverride = newAlertEntry
+ dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+ verifyNoMoreInteractions(listener)
+ groupMap.clear()
+ dispatcher.closeBufferScope()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testAlertOverrideMultipleChangesBuffered() {
+ dispatcher.openBufferScope()
+ val group = addGroup(1)
+ val oldAlertEntry = groupTestHelper.createChildNotification()
+ val newAlertEntry = groupTestHelper.createChildNotification()
+ group.alertOverride = null
+ dispatcher.notifyAlertOverrideChanged(group, oldAlertEntry)
+ group.alertOverride = newAlertEntry
+ dispatcher.notifyAlertOverrideChanged(group, null)
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope()
+ verify(listener).onGroupAlertOverrideChanged(group, oldAlertEntry, newAlertEntry)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testAlertOverrideTemporaryValueSwallowed() {
+ dispatcher.openBufferScope()
+ val group = addGroup(1)
+ val stableAlertEntry = groupTestHelper.createChildNotification()
+ group.alertOverride = null
+ dispatcher.notifyAlertOverrideChanged(group, stableAlertEntry)
+ group.alertOverride = stableAlertEntry
+ dispatcher.notifyAlertOverrideChanged(group, null)
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testAlertOverrideTemporaryNullSwallowed() {
+ dispatcher.openBufferScope()
+ val group = addGroup(1)
+ val temporaryAlertEntry = groupTestHelper.createChildNotification()
+ group.alertOverride = temporaryAlertEntry
+ dispatcher.notifyAlertOverrideChanged(group, null)
+ group.alertOverride = null
+ dispatcher.notifyAlertOverrideChanged(group, temporaryAlertEntry)
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testSuppressOnUnbuffered() {
+ val group = addGroup(1)
+ group.suppressed = true
+ dispatcher.notifySuppressedChanged(group)
+ verify(listener).onGroupSuppressionChanged(group, true)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testSuppressOffUnbuffered() {
+ val group = addGroup(1)
+ group.suppressed = false
+ dispatcher.notifySuppressedChanged(group)
+ verify(listener).onGroupSuppressionChanged(group, false)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testSuppressOnBuffered() {
+ dispatcher.openBufferScope()
+ val group = addGroup(1)
+ group.suppressed = false
+ dispatcher.notifySuppressedChanged(group)
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope()
+ verify(listener).onGroupSuppressionChanged(group, false)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testSuppressOnIgnoredIfRemoved() {
+ dispatcher.openBufferScope()
+ val group = addGroup(1)
+ group.suppressed = false
+ dispatcher.notifySuppressedChanged(group)
+ verifyNoMoreInteractions(listener)
+ groupMap.clear()
+ dispatcher.closeBufferScope()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testSuppressOnOffBuffered() {
+ dispatcher.openBufferScope()
+ val group = addGroup(1)
+ group.suppressed = true
+ dispatcher.notifySuppressedChanged(group)
+ group.suppressed = false
+ dispatcher.notifySuppressedChanged(group)
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testSuppressOnOffOnBuffered() {
+ dispatcher.openBufferScope()
+ val group = addGroup(1)
+ group.suppressed = true
+ dispatcher.notifySuppressedChanged(group)
+ group.suppressed = false
+ dispatcher.notifySuppressedChanged(group)
+ group.suppressed = true
+ dispatcher.notifySuppressedChanged(group)
+ verifyNoMoreInteractions(listener)
+ dispatcher.closeBufferScope()
+ verify(listener).onGroupSuppressionChanged(group, true)
+ verifyNoMoreInteractions(listener)
+ }
+
+ private fun addGroup(id: Int): NotificationGroup {
+ val group = NotificationGroup("group:$id")
+ groupMap[group.groupKey] = group
+ return group
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 9898b4b2fdbd..c13b3358a077 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -312,10 +312,11 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
@Test
public void testUpdateChildToSummaryDoesNotTransfer() {
+ final String tag = "fooTag";
NotificationEntry summaryEntry =
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY, 47);
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY, 47, tag);
mockHasHeadsUpContentView(childEntry, false);
mHeadsUpManager.showNotification(summaryEntry);
@@ -327,7 +328,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
StatusBarNotification oldNotification = childEntry.getSbn();
childEntry.setSbn(
mGroupTestHelper.createSummaryNotification(
- Notification.GROUP_ALERT_SUMMARY, 47).getSbn());
+ Notification.GROUP_ALERT_SUMMARY, 47, tag).getSbn());
mGroupManager.onEntryUpdated(childEntry, oldNotification);
assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 6d170b673cc3..5d7b15424fec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -21,14 +21,19 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Log;
import androidx.test.filters.SmallTest;
@@ -64,8 +69,10 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
private final NotificationGroupTestHelper mGroupTestHelper =
new NotificationGroupTestHelper(mContext);
- @Mock PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- @Mock HeadsUpManager mHeadsUpManager;
+ @Mock
+ PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+ @Mock
+ HeadsUpManager mHeadsUpManager;
@Before
public void setup() {
@@ -177,15 +184,25 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
helpTestAlertOverrideWithSiblings(2);
}
+ @Test
+ public void testAlertOverrideWithSiblings_3() {
+ helpTestAlertOverrideWithSiblings(3);
+ }
+
+ @Test
+ public void testAlertOverrideWithSiblings_9() {
+ helpTestAlertOverrideWithSiblings(9);
+ }
+
/**
* Helper for testing various sibling counts
*/
private void helpTestAlertOverrideWithSiblings(int numSiblings) {
helpTestAlertOverride(
/* numSiblings */ numSiblings,
- /* summaryAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* childAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* siblingAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* summaryGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* priorityGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* siblingGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
/* expectAlertOverride */ true);
}
@@ -194,9 +211,9 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
// tests that summary can have GROUP_ALERT_ALL and this still works
helpTestAlertOverride(
/* numSiblings */ 1,
- /* summaryAlert */ Notification.GROUP_ALERT_ALL,
- /* childAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* siblingAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* summaryGroupAlert */ Notification.GROUP_ALERT_ALL,
+ /* priorityGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* siblingGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
/* expectAlertOverride */ true);
}
@@ -205,9 +222,9 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
// Tests that if the summary alerts CHILDREN, there's no alertOverride
helpTestAlertOverride(
/* numSiblings */ 1,
- /* summaryAlert */ Notification.GROUP_ALERT_CHILDREN,
- /* childAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* siblingAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* summaryGroupAlert */ Notification.GROUP_ALERT_CHILDREN,
+ /* priorityGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* siblingGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
/* expectAlertOverride */ false);
}
@@ -216,9 +233,9 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
// Tests that if the children alert ALL, there's no alertOverride
helpTestAlertOverride(
/* numSiblings */ 1,
- /* summaryAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* childAlert */ Notification.GROUP_ALERT_ALL,
- /* siblingAlert */ Notification.GROUP_ALERT_ALL,
+ /* summaryGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
+ /* priorityGroupAlert */ Notification.GROUP_ALERT_ALL,
+ /* siblingGroupAlert */ Notification.GROUP_ALERT_ALL,
/* expectAlertOverride */ false);
}
@@ -229,17 +246,19 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
* 3) when the priority entry is removed, these are reversed
*/
private void helpTestAlertOverride(int numSiblings,
- @Notification.GroupAlertBehavior int summaryAlert,
- @Notification.GroupAlertBehavior int childAlert,
- @Notification.GroupAlertBehavior int siblingAlert,
+ @Notification.GroupAlertBehavior int summaryGroupAlert,
+ @Notification.GroupAlertBehavior int priorityGroupAlert,
+ @Notification.GroupAlertBehavior int siblingGroupAlert,
boolean expectAlertOverride) {
// Create entries in an order so that the priority entry can be deemed the newest child.
NotificationEntry[] siblings = new NotificationEntry[numSiblings];
for (int i = 0; i < numSiblings; i++) {
- siblings[i] = mGroupTestHelper.createChildNotification(siblingAlert);
+ siblings[i] = mGroupTestHelper.createChildNotification(siblingGroupAlert, i, "sibling");
}
- NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(childAlert);
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(summaryAlert);
+ NotificationEntry priorityEntry =
+ mGroupTestHelper.createChildNotification(priorityGroupAlert, 0, "priority");
+ NotificationEntry summaryEntry =
+ mGroupTestHelper.createSummaryNotification(summaryGroupAlert, 0, "summary");
// The priority entry is an important conversation.
when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
@@ -263,11 +282,20 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
assertNull(summaryGroup.alertOverride);
return;
}
+ int max2Siblings = Math.min(2, numSiblings);
// Verify that the summary group has the priority child as its alertOverride
NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
assertEquals(priorityEntry, summaryGroup.alertOverride);
verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+ verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, true);
+ if (numSiblings > 1) {
+ verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, false);
+ }
+ verify(groupChangeListener).onGroupCreated(any(), eq(priorityEntry.getKey()));
+ verify(groupChangeListener).onGroupCreated(any(), eq(summaryEntry.getSbn().getGroupKey()));
+ verify(groupChangeListener, times(max2Siblings + 1)).onGroupsChanged();
+ verifyNoMoreInteractions(groupChangeListener);
// Verify that only the priority notification is isolated from the group
assertEquals(priorityEntry, mGroupManager.getGroupSummary(priorityEntry));
@@ -283,6 +311,92 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
// verify that the alertOverride is removed when the priority notification is
assertNull(summaryGroup.alertOverride);
+ verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, priorityEntry, null);
+ verify(groupChangeListener).onGroupRemoved(any(), eq(priorityEntry.getKey()));
+ verify(groupChangeListener, times(max2Siblings + 2)).onGroupsChanged();
+ if (numSiblings == 0) {
+ verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, false);
+ }
+ verifyNoMoreInteractions(groupChangeListener);
+ }
+
+ @Test
+ public void testAlertOverrideWhenUpdatingSummaryAtEnd() {
+ int numSiblings = 2;
+ int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+ // Create entries in an order so that the priority entry can be deemed the newest child.
+ NotificationEntry[] siblings = new NotificationEntry[numSiblings];
+ for (int i = 0; i < numSiblings; i++) {
+ siblings[i] = mGroupTestHelper.createChildNotification(groupAlert, i, "sibling");
+ }
+ NotificationEntry priorityEntry =
+ mGroupTestHelper.createChildNotification(groupAlert, 0, "priority");
+ NotificationEntry summaryEntry =
+ mGroupTestHelper.createSummaryNotification(groupAlert, 0, "summary");
+
+ // The priority entry is an important conversation.
+ when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
+ .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
+
+ // Register a listener so we can verify that the event is sent.
+ OnGroupChangeListener groupChangeListener = mock(OnGroupChangeListener.class);
+ mGroupManager.registerGroupChangeListener(groupChangeListener);
+
+ // Add all the entries. The order here shouldn't matter.
+ mGroupManager.onEntryAdded(summaryEntry);
+ for (int i = 0; i < numSiblings; i++) {
+ mGroupManager.onEntryAdded(siblings[i]);
+ }
+ mGroupManager.onEntryAdded(priorityEntry);
+
+ int max2Siblings = Math.min(2, numSiblings);
+
+ // Verify that the summary group has the priority child as its alertOverride
+ NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
+ assertEquals(priorityEntry, summaryGroup.alertOverride);
verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+ verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, true);
+ if (numSiblings > 1) {
+ verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, false);
+ }
+ verify(groupChangeListener).onGroupCreated(any(), eq(priorityEntry.getKey()));
+ verify(groupChangeListener).onGroupCreated(any(), eq(summaryEntry.getSbn().getGroupKey()));
+ verify(groupChangeListener, times(max2Siblings + 1)).onGroupsChanged();
+ verifyNoMoreInteractions(groupChangeListener);
+
+ // Verify that only the priority notification is isolated from the group
+ assertEquals(priorityEntry, mGroupManager.getGroupSummary(priorityEntry));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(priorityEntry));
+ // Verify that the siblings are NOT isolated from the group
+ for (int i = 0; i < numSiblings; i++) {
+ assertEquals(summaryEntry, mGroupManager.getGroupSummary(siblings[i]));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(siblings[i]));
+ }
+
+ Log.d("NotificationGroupManagerLegacyTest",
+ "testAlertOverrideWhenUpdatingSummaryAtEnd: About to update summary");
+
+ StatusBarNotification oldSummarySbn = mGroupTestHelper.incrementPost(summaryEntry, 10000);
+ mGroupManager.onEntryUpdated(summaryEntry, oldSummarySbn);
+
+ verify(groupChangeListener, times(max2Siblings + 2)).onGroupsChanged();
+ verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, priorityEntry, null);
+ verifyNoMoreInteractions(groupChangeListener);
+
+ Log.d("NotificationGroupManagerLegacyTest",
+ "testAlertOverrideWhenUpdatingSummaryAtEnd: About to update priority child");
+
+ StatusBarNotification oldPrioritySbn = mGroupTestHelper.incrementPost(priorityEntry, 10000);
+ mGroupManager.onEntryUpdated(priorityEntry, oldPrioritySbn);
+
+ verify(groupChangeListener).onGroupRemoved(any(), eq(priorityEntry.getKey()));
+ verify(groupChangeListener, times(2)).onGroupCreated(any(), eq(priorityEntry.getKey()));
+ verify(groupChangeListener, times(2))
+ .onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+ verify(groupChangeListener, times(max2Siblings + 3)).onGroupsChanged();
+ verifyNoMoreInteractions(groupChangeListener);
+
+ Log.d("NotificationGroupManagerLegacyTest",
+ "testAlertOverrideWhenUpdatingSummaryAtEnd: Done");
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index d405fea78170..ac32b9d6f01a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -23,8 +25,10 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
import com.android.systemui.R;
+import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -44,15 +48,15 @@ public final class NotificationGroupTestHelper {
}
public NotificationEntry createSummaryNotification() {
- return createSummaryNotification(Notification.GROUP_ALERT_ALL, mId++);
+ return createSummaryNotification(Notification.GROUP_ALERT_ALL, mId++, null);
}
public NotificationEntry createSummaryNotification(int groupAlertBehavior) {
- return createSummaryNotification(groupAlertBehavior, mId++);
+ return createSummaryNotification(groupAlertBehavior, mId++, null);
}
- public NotificationEntry createSummaryNotification(int groupAlertBehavior, int id) {
- return createEntry(id, true, groupAlertBehavior);
+ public NotificationEntry createSummaryNotification(int groupAlertBehavior, int id, String tag) {
+ return createEntry(id, tag, true, groupAlertBehavior);
}
public NotificationEntry createChildNotification() {
@@ -60,14 +64,15 @@ public final class NotificationGroupTestHelper {
}
public NotificationEntry createChildNotification(int groupAlertBehavior) {
- return createEntry(mId++, false, groupAlertBehavior);
+ return createEntry(mId++, null, false, groupAlertBehavior);
}
- public NotificationEntry createChildNotification(int groupAlertBehavior, int id) {
- return createEntry(id, false, groupAlertBehavior);
+ public NotificationEntry createChildNotification(int groupAlertBehavior, int id, String tag) {
+ return createEntry(id, tag, false, groupAlertBehavior);
}
- public NotificationEntry createEntry(int id, boolean isSummary, int groupAlertBehavior) {
+ public NotificationEntry createEntry(int id, String tag, boolean isSummary,
+ int groupAlertBehavior) {
Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentTitle("Title")
.setSmallIcon(R.drawable.ic_person)
@@ -80,6 +85,7 @@ public final class NotificationGroupTestHelper {
.setOpPkg(TEST_PACKAGE_NAME)
.setId(id)
.setNotification(notif)
+ .setTag(tag)
.setUser(new UserHandle(ActivityManager.getCurrentUser()))
.build();
@@ -88,4 +94,16 @@ public final class NotificationGroupTestHelper {
when(row.getEntry()).thenReturn(entry);
return entry;
}
+
+ public StatusBarNotification incrementPost(NotificationEntry entry, int increment) {
+ StatusBarNotification oldSbn = entry.getSbn();
+ final long oldPostTime = oldSbn.getPostTime();
+ final long newPostTime = oldPostTime + increment;
+ entry.setSbn(new SbnBuilder(oldSbn)
+ .setPostTime(newPostTime)
+ .build());
+ assertThat(oldSbn.getPostTime()).isEqualTo(oldPostTime);
+ assertThat(entry.getSbn().getPostTime()).isEqualTo(newPostTime);
+ return oldSbn;
+ }
}