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
This commit is contained in:
Jeff DeCew
2022-01-12 20:48:31 +00:00
parent 1439429dd6
commit fd8687974a
8 changed files with 689 additions and 127 deletions

View File

@@ -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", "");
}
}

View File

@@ -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 flushAndStopBuffering() {
// stop buffering so that we can call our own helpers
mIsBuffering = false;
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();
}
}
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);
@@ -1087,14 +1198,6 @@ public class NotificationGroupManagerLegacy implements
@Nullable NotificationEntry oldAlertOverride,
@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

View File

@@ -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;
@@ -688,11 +687,6 @@ public class NotificationStackScrollLayoutController {
groupManager.registerGroupExpansionChangeListener(
(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");

View File

@@ -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;
}

View File

@@ -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
}
}

View File

@@ -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));

View File

@@ -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");
}
}

View File

@@ -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;
}
}