Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aab3eaf7 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Regroup autogrouped notifications into app group on summary is posted" into main

parents 1cd6a0f4 b0a5d576
Loading
Loading
Loading
Loading
+44 −31
Original line number Diff line number Diff line
@@ -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
@@ -694,7 +695,7 @@ public class GroupHelper {
            currentSectionKey = prevSectionKey;
        }

        maybeUngroupWithSections(record, currentSectionKey);
        return maybeUngroupWithSections(record, currentSectionKey);
    }

    /**
@@ -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();
@@ -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);
@@ -761,6 +766,7 @@ public class GroupHelper {
                }
            }
        }
        return wasUnAggregated;
    }

    /**
@@ -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) {
@@ -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);
        }
    }

+0 −6
Original line number Diff line number Diff line
@@ -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) -> {
+183 −2
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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);
            }
        }
    }
}