Loading services/core/java/com/android/server/notification/GroupHelper.java +44 −31 Original line number Diff line number Diff line Loading @@ -681,8 +681,9 @@ public class GroupHelper { /** * A notification was added that is app-grouped. * @return true if the notification was previously auto-grouped */ private void maybeUngroupOnAppGrouped(NotificationRecord record) { private boolean maybeUngroupOnAppGrouped(NotificationRecord record) { FullyQualifiedGroupKey currentSectionKey = getSectionGroupKeyWithFallback(record); // The notification was part of a different section => trigger regrouping Loading @@ -694,7 +695,7 @@ public class GroupHelper { currentSectionKey = prevSectionKey; } maybeUngroupWithSections(record, currentSectionKey); return maybeUngroupWithSections(record, currentSectionKey); } /** Loading @@ -709,16 +710,19 @@ public class GroupHelper { * This method implements autogrouping with sections support. * * And updates the internal state of un-app-grouped notifications and their flags. * * @return true if the notification was previously auto-grouped */ private void maybeUngroupWithSections(NotificationRecord record, private boolean maybeUngroupWithSections(NotificationRecord record, @Nullable FullyQualifiedGroupKey fullAggregateGroupKey) { boolean wasUnAggregated = false; if (fullAggregateGroupKey == null) { if (DEBUG) { Slog.i(TAG, "Skipping maybeUngroupWithSections for " + record + " no valid section found."); } return; return false; } final StatusBarNotification sbn = record.getSbn(); Loading @@ -738,6 +742,7 @@ public class GroupHelper { if (aggregatedNotificationsAttrs.containsKey(record.getKey())) { aggregatedNotificationsAttrs.remove(sbn.getKey()); mAggregatedNotifications.put(fullAggregateGroupKey, aggregatedNotificationsAttrs); wasUnAggregated = true; if (DEBUG) { Slog.i(TAG, "maybeUngroup removeAutoGroup: " + record); Loading @@ -761,6 +766,7 @@ public class GroupHelper { } } } return wasUnAggregated; } /** Loading Loading @@ -795,6 +801,10 @@ public class GroupHelper { return; } // If any formerly-ungrouped or autogrouped notifications will be grouped by this summary, // update grouping onGroupSummaryAdded(record, notificationList); final NotificationSectioner sectioner = getSection(record); if (sectioner == null) { if (DEBUG) { Loading Loading @@ -1097,45 +1107,48 @@ public class GroupHelper { } /** * Called when a group summary is posted. If there are any ungrouped notifications that are * in that group, remove them as they are no longer candidates for autogrouping. * Called when a group summary is posted. * If there are any ungrouped or force-grouped notifications from that group, * remove them from the ungrouped list or autogroup * and regroup them into the app-provided group. * * @param summaryRecord the NotificationRecord for the newly posted group summary * @param notificationList the full notification list from NotificationManagerService */ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING) protected void onGroupSummaryAdded(final NotificationRecord summaryRecord, private void onGroupSummaryAdded(final NotificationRecord summaryRecord, final List<NotificationRecord> notificationList) { String groupKey = summaryRecord.getSbn().getGroup(); synchronized (mAggregatedNotifications) { final NotificationSectioner sectioner = getSection(summaryRecord); if (sectioner == null) { Slog.w(TAG, "onGroupSummaryAdded " + summaryRecord + ": no valid section found"); if (!summaryRecord.getNotification().isGroupSummary()) { return; } FullyQualifiedGroupKey aggregateGroupKey = FullyQualifiedGroupKey.forRecord( summaryRecord, sectioner); ArrayMap<String, NotificationAttributes> ungrouped = mUngroupedAbuseNotifications.getOrDefault(aggregateGroupKey, new ArrayMap<>()); if (ungrouped.isEmpty()) { // don't bother looking through the notification list if there are no pending // ungrouped notifications in this section (likely to be the most common case) if (GroupHelper.isAggregatedGroup(summaryRecord)) { return; } synchronized (mAggregatedNotifications) { if (DEBUG) { Log.i(TAG, "onGroupSummaryAdded: " + summaryRecord); } final String summaryGroupKey = summaryRecord.getGroupKey(); // Look through full notification list for any notifications belonging to this group; // remove from ungrouped map if needed, as the presence of the summary means they will // now be grouped for (NotificationRecord r : notificationList) { final String oldGroupKey = GroupHelper.getFullAggregateGroupKey( r.getSbn().getPackageName(), r.getOriginalGroupKey(), r.getUserId()); if (!r.getNotification().isGroupSummary() && groupKey.equals(r.getSbn().getGroup()) && ungrouped.containsKey(r.getKey())) { ungrouped.remove(r.getKey()); && (r.mOriginalFlags & FLAG_GROUP_SUMMARY) == 0 && summaryGroupKey.equals(oldGroupKey)) { final NotificationSectioner sectioner = getSection(r); if (sectioner == null || NOTIFICATION_BUNDLE_SECTIONS.contains(sectioner)) { if (DEBUG) { Slog.i(TAG, "onGroupSummaryAdded skip bundled child: " + r); } continue; } if (maybeUngroupOnAppGrouped(r)) { // Cleanup override group key here so that onNotificationPostedWithDelay // has the right state synchronously r.setOverrideGroupKey(null); } } } mUngroupedAbuseNotifications.put(aggregateGroupKey, ungrouped); } } Loading services/core/java/com/android/server/notification/NotificationManagerService.java +0 −6 Original line number Diff line number Diff line Loading @@ -10413,12 +10413,6 @@ public class NotificationManagerService extends SystemService { } if (isSummary) { mSummaryByGroupKey.put(group, r); if (notificationForceGrouping()) { // If any formerly-ungrouped notifications will be grouped by this summary, update // accordingly. mGroupHelper.onGroupSummaryAdded(r, mNotificationList); } } FlagChecker childrenFlagChecker = (flags) -> { Loading services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java +183 −2 Original line number Diff line number Diff line Loading @@ -4176,7 +4176,6 @@ public class GroupHelperTest extends UiServiceTestCase { mUser, "specialGroup", true, IMPORTANCE_DEFAULT); notifList.add(groupSummary); summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary); mGroupHelper.onGroupSummaryAdded(groupSummary, notifList); mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey); // One more notification posted to the group; because its summary already exists, it should Loading Loading @@ -4222,7 +4221,6 @@ public class GroupHelperTest extends UiServiceTestCase { "summaryGroup", true, IMPORTANCE_DEFAULT); notifList.add(summary); summaryByGroupKey.put(summary.getSbn().getKey(), summary); mGroupHelper.onGroupSummaryAdded(summary, notifList); mGroupHelper.onNotificationPostedWithDelay(summary, notifList, summaryByGroupKey); // all of the above posted notifications should be autogrouped Loading @@ -4234,4 +4232,187 @@ public class GroupHelperTest extends UiServiceTestCase { verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(expectedGroupKey), anyBoolean()); } @Test @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) public void testGroupSummaryAdded_unAggregatesAutogroupedNotifications() { // Scenario: // * child notifications posted before summary and force-grouped // * summary posted => remove child notifications from autogroup and regroup into app group List<NotificationRecord> notifList = new ArrayList<>(); Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>(); final String groupName = "testGrp"; final int numChildNotif = AUTOGROUP_AT_COUNT; for (int i = 0; i < numChildNotif; i++) { NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, groupName, false, IMPORTANCE_DEFAULT); notifList.add(child); mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey); } // Check that notifications were autogrouped final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(mPkg, AGGREGATE_GROUP_KEY + "AlertingSection", mUser.getIdentifier()); verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(mPkg), anyString(), eq(expectedGroupKey_alerting), anyInt(), eq(getNotificationAttributes(BASE_FLAGS))); verify(mCallback, times(numChildNotif)).addAutoGroup(anyString(), eq(expectedGroupKey_alerting), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { record.setOverrideGroupKey(expectedGroupKey_alerting); } } // Post group summary Mockito.reset(mCallback); NotificationRecord groupSummary = getNotificationRecord(mPkg, 4242, "", mUser, groupName, true, IMPORTANCE_DEFAULT); notifList.add(groupSummary); summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary); mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey); // Check that the updated notification was removed from the autogroup verify(mCallback, times(numChildNotif)).removeAutoGroup(anyString()); verify(mCallback, times(numChildNotif - 1)).updateAutogroupSummary(anyInt(), anyString(), eq(expectedGroupKey_alerting), any()); verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(mPkg), eq(expectedGroupKey_alerting)); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { assertThat(record.getGroupKey()).isEqualTo(groupSummary.getGroupKey()); } } } @Test @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) public void testGroupSummaryAddedDifferentChannel_unAggregatesAutogroupedNotifications() { // Scenario: // * child notifications posted before summary and force-grouped // * summary posted in different channel/section // => remove child notifications from autogroup and regroup into app group List<NotificationRecord> notifList = new ArrayList<>(); Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>(); final String groupName = "testGrp"; final int numChildNotif = AUTOGROUP_AT_COUNT; for (int i = 0; i < numChildNotif; i++) { NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, groupName, false, IMPORTANCE_DEFAULT); notifList.add(child); mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey); } // Check that notifications were autogrouped final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(mPkg, AGGREGATE_GROUP_KEY + "AlertingSection", mUser.getIdentifier()); verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(mPkg), anyString(), eq(expectedGroupKey_alerting), anyInt(), eq(getNotificationAttributes(BASE_FLAGS))); verify(mCallback, times(numChildNotif)).addAutoGroup(anyString(), eq(expectedGroupKey_alerting), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { record.setOverrideGroupKey(expectedGroupKey_alerting); } } // Post group summary Mockito.reset(mCallback); NotificationRecord groupSummary = getNotificationRecord(mPkg, 4242, "", mUser, groupName, true, IMPORTANCE_LOW); notifList.add(groupSummary); summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary); mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey); // Check that the updated notification was removed from the autogroup verify(mCallback, times(numChildNotif)).removeAutoGroup(anyString()); verify(mCallback, times(numChildNotif - 1)).updateAutogroupSummary(anyInt(), anyString(), eq(expectedGroupKey_alerting), any()); verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(mPkg), eq(expectedGroupKey_alerting)); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { assertThat(record.getGroupKey()).isEqualTo(groupSummary.getGroupKey()); } } } @Test @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_CLASSIFICATION}) public void testGroupSummaryAdded_doesnNotUnAggregateBundledNotifications() { // Scenario: // * child notifications posted before summary and classified (bundled) and force-grouped // * summary posted => child notifications are not removed from bundle autogroup List<NotificationRecord> notifList = new ArrayList<>(); Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>(); final String groupName = "testGrp"; final int numChildNotif = AUTOGROUP_AT_COUNT; final NotificationChannel socialChannel = new NotificationChannel( NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID, IMPORTANCE_LOW); for (int i = 0; i < numChildNotif; i++) { NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, groupName, false, IMPORTANCE_DEFAULT); notifList.add(child); child.updateNotificationChannel(socialChannel); mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey); } // Check that notifications were autogrouped in the social section final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(mPkg, AGGREGATE_GROUP_KEY + "SocialSection", mUser.getIdentifier()); final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes( BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, NotificationChannel.SOCIAL_MEDIA_ID); verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(mPkg), anyString(), eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social)); verify(mCallback, times(numChildNotif)).addAutoGroup(anyString(), eq(expectedGroupKey_social), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); verify(mCallback, times(numChildNotif - AUTOGROUP_BUNDLES_AT_COUNT)) .updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { record.setOverrideGroupKey(expectedGroupKey_social); } } // Post group summary Mockito.reset(mCallback); NotificationRecord groupSummary = getNotificationRecord(mPkg, 4242, "", mUser, groupName, true, IMPORTANCE_DEFAULT); notifList.add(groupSummary); summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary); mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey); // Check that the updated notification were not removed from the autogroup verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { assertThat(record.getGroupKey()).isEqualTo(expectedGroupKey_social); } } } } Loading
services/core/java/com/android/server/notification/GroupHelper.java +44 −31 Original line number Diff line number Diff line Loading @@ -681,8 +681,9 @@ public class GroupHelper { /** * A notification was added that is app-grouped. * @return true if the notification was previously auto-grouped */ private void maybeUngroupOnAppGrouped(NotificationRecord record) { private boolean maybeUngroupOnAppGrouped(NotificationRecord record) { FullyQualifiedGroupKey currentSectionKey = getSectionGroupKeyWithFallback(record); // The notification was part of a different section => trigger regrouping Loading @@ -694,7 +695,7 @@ public class GroupHelper { currentSectionKey = prevSectionKey; } maybeUngroupWithSections(record, currentSectionKey); return maybeUngroupWithSections(record, currentSectionKey); } /** Loading @@ -709,16 +710,19 @@ public class GroupHelper { * This method implements autogrouping with sections support. * * And updates the internal state of un-app-grouped notifications and their flags. * * @return true if the notification was previously auto-grouped */ private void maybeUngroupWithSections(NotificationRecord record, private boolean maybeUngroupWithSections(NotificationRecord record, @Nullable FullyQualifiedGroupKey fullAggregateGroupKey) { boolean wasUnAggregated = false; if (fullAggregateGroupKey == null) { if (DEBUG) { Slog.i(TAG, "Skipping maybeUngroupWithSections for " + record + " no valid section found."); } return; return false; } final StatusBarNotification sbn = record.getSbn(); Loading @@ -738,6 +742,7 @@ public class GroupHelper { if (aggregatedNotificationsAttrs.containsKey(record.getKey())) { aggregatedNotificationsAttrs.remove(sbn.getKey()); mAggregatedNotifications.put(fullAggregateGroupKey, aggregatedNotificationsAttrs); wasUnAggregated = true; if (DEBUG) { Slog.i(TAG, "maybeUngroup removeAutoGroup: " + record); Loading @@ -761,6 +766,7 @@ public class GroupHelper { } } } return wasUnAggregated; } /** Loading Loading @@ -795,6 +801,10 @@ public class GroupHelper { return; } // If any formerly-ungrouped or autogrouped notifications will be grouped by this summary, // update grouping onGroupSummaryAdded(record, notificationList); final NotificationSectioner sectioner = getSection(record); if (sectioner == null) { if (DEBUG) { Loading Loading @@ -1097,45 +1107,48 @@ public class GroupHelper { } /** * Called when a group summary is posted. If there are any ungrouped notifications that are * in that group, remove them as they are no longer candidates for autogrouping. * Called when a group summary is posted. * If there are any ungrouped or force-grouped notifications from that group, * remove them from the ungrouped list or autogroup * and regroup them into the app-provided group. * * @param summaryRecord the NotificationRecord for the newly posted group summary * @param notificationList the full notification list from NotificationManagerService */ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING) protected void onGroupSummaryAdded(final NotificationRecord summaryRecord, private void onGroupSummaryAdded(final NotificationRecord summaryRecord, final List<NotificationRecord> notificationList) { String groupKey = summaryRecord.getSbn().getGroup(); synchronized (mAggregatedNotifications) { final NotificationSectioner sectioner = getSection(summaryRecord); if (sectioner == null) { Slog.w(TAG, "onGroupSummaryAdded " + summaryRecord + ": no valid section found"); if (!summaryRecord.getNotification().isGroupSummary()) { return; } FullyQualifiedGroupKey aggregateGroupKey = FullyQualifiedGroupKey.forRecord( summaryRecord, sectioner); ArrayMap<String, NotificationAttributes> ungrouped = mUngroupedAbuseNotifications.getOrDefault(aggregateGroupKey, new ArrayMap<>()); if (ungrouped.isEmpty()) { // don't bother looking through the notification list if there are no pending // ungrouped notifications in this section (likely to be the most common case) if (GroupHelper.isAggregatedGroup(summaryRecord)) { return; } synchronized (mAggregatedNotifications) { if (DEBUG) { Log.i(TAG, "onGroupSummaryAdded: " + summaryRecord); } final String summaryGroupKey = summaryRecord.getGroupKey(); // Look through full notification list for any notifications belonging to this group; // remove from ungrouped map if needed, as the presence of the summary means they will // now be grouped for (NotificationRecord r : notificationList) { final String oldGroupKey = GroupHelper.getFullAggregateGroupKey( r.getSbn().getPackageName(), r.getOriginalGroupKey(), r.getUserId()); if (!r.getNotification().isGroupSummary() && groupKey.equals(r.getSbn().getGroup()) && ungrouped.containsKey(r.getKey())) { ungrouped.remove(r.getKey()); && (r.mOriginalFlags & FLAG_GROUP_SUMMARY) == 0 && summaryGroupKey.equals(oldGroupKey)) { final NotificationSectioner sectioner = getSection(r); if (sectioner == null || NOTIFICATION_BUNDLE_SECTIONS.contains(sectioner)) { if (DEBUG) { Slog.i(TAG, "onGroupSummaryAdded skip bundled child: " + r); } continue; } if (maybeUngroupOnAppGrouped(r)) { // Cleanup override group key here so that onNotificationPostedWithDelay // has the right state synchronously r.setOverrideGroupKey(null); } } } mUngroupedAbuseNotifications.put(aggregateGroupKey, ungrouped); } } Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +0 −6 Original line number Diff line number Diff line Loading @@ -10413,12 +10413,6 @@ public class NotificationManagerService extends SystemService { } if (isSummary) { mSummaryByGroupKey.put(group, r); if (notificationForceGrouping()) { // If any formerly-ungrouped notifications will be grouped by this summary, update // accordingly. mGroupHelper.onGroupSummaryAdded(r, mNotificationList); } } FlagChecker childrenFlagChecker = (flags) -> { Loading
services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java +183 −2 Original line number Diff line number Diff line Loading @@ -4176,7 +4176,6 @@ public class GroupHelperTest extends UiServiceTestCase { mUser, "specialGroup", true, IMPORTANCE_DEFAULT); notifList.add(groupSummary); summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary); mGroupHelper.onGroupSummaryAdded(groupSummary, notifList); mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey); // One more notification posted to the group; because its summary already exists, it should Loading Loading @@ -4222,7 +4221,6 @@ public class GroupHelperTest extends UiServiceTestCase { "summaryGroup", true, IMPORTANCE_DEFAULT); notifList.add(summary); summaryByGroupKey.put(summary.getSbn().getKey(), summary); mGroupHelper.onGroupSummaryAdded(summary, notifList); mGroupHelper.onNotificationPostedWithDelay(summary, notifList, summaryByGroupKey); // all of the above posted notifications should be autogrouped Loading @@ -4234,4 +4232,187 @@ public class GroupHelperTest extends UiServiceTestCase { verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(expectedGroupKey), anyBoolean()); } @Test @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) public void testGroupSummaryAdded_unAggregatesAutogroupedNotifications() { // Scenario: // * child notifications posted before summary and force-grouped // * summary posted => remove child notifications from autogroup and regroup into app group List<NotificationRecord> notifList = new ArrayList<>(); Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>(); final String groupName = "testGrp"; final int numChildNotif = AUTOGROUP_AT_COUNT; for (int i = 0; i < numChildNotif; i++) { NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, groupName, false, IMPORTANCE_DEFAULT); notifList.add(child); mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey); } // Check that notifications were autogrouped final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(mPkg, AGGREGATE_GROUP_KEY + "AlertingSection", mUser.getIdentifier()); verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(mPkg), anyString(), eq(expectedGroupKey_alerting), anyInt(), eq(getNotificationAttributes(BASE_FLAGS))); verify(mCallback, times(numChildNotif)).addAutoGroup(anyString(), eq(expectedGroupKey_alerting), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { record.setOverrideGroupKey(expectedGroupKey_alerting); } } // Post group summary Mockito.reset(mCallback); NotificationRecord groupSummary = getNotificationRecord(mPkg, 4242, "", mUser, groupName, true, IMPORTANCE_DEFAULT); notifList.add(groupSummary); summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary); mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey); // Check that the updated notification was removed from the autogroup verify(mCallback, times(numChildNotif)).removeAutoGroup(anyString()); verify(mCallback, times(numChildNotif - 1)).updateAutogroupSummary(anyInt(), anyString(), eq(expectedGroupKey_alerting), any()); verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(mPkg), eq(expectedGroupKey_alerting)); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { assertThat(record.getGroupKey()).isEqualTo(groupSummary.getGroupKey()); } } } @Test @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) public void testGroupSummaryAddedDifferentChannel_unAggregatesAutogroupedNotifications() { // Scenario: // * child notifications posted before summary and force-grouped // * summary posted in different channel/section // => remove child notifications from autogroup and regroup into app group List<NotificationRecord> notifList = new ArrayList<>(); Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>(); final String groupName = "testGrp"; final int numChildNotif = AUTOGROUP_AT_COUNT; for (int i = 0; i < numChildNotif; i++) { NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, groupName, false, IMPORTANCE_DEFAULT); notifList.add(child); mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey); } // Check that notifications were autogrouped final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(mPkg, AGGREGATE_GROUP_KEY + "AlertingSection", mUser.getIdentifier()); verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(mPkg), anyString(), eq(expectedGroupKey_alerting), anyInt(), eq(getNotificationAttributes(BASE_FLAGS))); verify(mCallback, times(numChildNotif)).addAutoGroup(anyString(), eq(expectedGroupKey_alerting), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { record.setOverrideGroupKey(expectedGroupKey_alerting); } } // Post group summary Mockito.reset(mCallback); NotificationRecord groupSummary = getNotificationRecord(mPkg, 4242, "", mUser, groupName, true, IMPORTANCE_LOW); notifList.add(groupSummary); summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary); mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey); // Check that the updated notification was removed from the autogroup verify(mCallback, times(numChildNotif)).removeAutoGroup(anyString()); verify(mCallback, times(numChildNotif - 1)).updateAutogroupSummary(anyInt(), anyString(), eq(expectedGroupKey_alerting), any()); verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(mPkg), eq(expectedGroupKey_alerting)); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { assertThat(record.getGroupKey()).isEqualTo(groupSummary.getGroupKey()); } } } @Test @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_CLASSIFICATION}) public void testGroupSummaryAdded_doesnNotUnAggregateBundledNotifications() { // Scenario: // * child notifications posted before summary and classified (bundled) and force-grouped // * summary posted => child notifications are not removed from bundle autogroup List<NotificationRecord> notifList = new ArrayList<>(); Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>(); final String groupName = "testGrp"; final int numChildNotif = AUTOGROUP_AT_COUNT; final NotificationChannel socialChannel = new NotificationChannel( NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID, IMPORTANCE_LOW); for (int i = 0; i < numChildNotif; i++) { NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, groupName, false, IMPORTANCE_DEFAULT); notifList.add(child); child.updateNotificationChannel(socialChannel); mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey); } // Check that notifications were autogrouped in the social section final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(mPkg, AGGREGATE_GROUP_KEY + "SocialSection", mUser.getIdentifier()); final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes( BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, NotificationChannel.SOCIAL_MEDIA_ID); verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(mPkg), anyString(), eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social)); verify(mCallback, times(numChildNotif)).addAutoGroup(anyString(), eq(expectedGroupKey_social), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); verify(mCallback, times(numChildNotif - AUTOGROUP_BUNDLES_AT_COUNT)) .updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { record.setOverrideGroupKey(expectedGroupKey_social); } } // Post group summary Mockito.reset(mCallback); NotificationRecord groupSummary = getNotificationRecord(mPkg, 4242, "", mUser, groupName, true, IMPORTANCE_DEFAULT); notifList.add(groupSummary); summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary); mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey); // Check that the updated notification were not removed from the autogroup verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); for (NotificationRecord record: notifList) { if (record.getNotification().isGroupChild()) { assertThat(record.getGroupKey()).isEqualTo(expectedGroupKey_social); } } } }