diff options
| author | 2022-01-12 20:48:31 +0000 | |
|---|---|---|
| committer | 2022-01-15 00:38:28 +0000 | |
| commit | fd8687974a3f9df804aab94f98e19c2a57a487f7 (patch) | |
| tree | 7bca39989e7c6d4bd1e8fb57174cceedf95eee65 | |
| parent | 1439429dd6cbdea912c5746fce9dce7e3ad5bbfa (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
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; + } } |