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

Commit e67fd838 authored by Valentin Iftime's avatar Valentin Iftime
Browse files

Autogroup bundled notifications immediately

 - Create autogroup summary for bundle sections after the first notification is added
 - Allow conversation notifications to autogroup if bundled

Flag: android.service.notification.notification_classification

Test: atest GroupHelperTest

Bug: 415267690
Change-Id: I6c40d9cdcfdd1db6a64c9a6cd4cb1318a89ec36d
parent 9efc269b
Loading
Loading
Loading
Loading
+67 −26
Original line number Diff line number Diff line
@@ -90,6 +90,9 @@ public class GroupHelper {
    //  with less than this value, they will be forced grouped
    private static final int MIN_CHILD_COUNT_TO_AVOID_FORCE_GROUPING = 3;

    private static final int AUTOGROUP_AT_COUNT_DEFAULT = 2;
    private static final int AUTOGROUP_BUNDLES_AT_COUNT_DEFAULT = 1;

    // Regrouping needed because the channel was updated, ie. importance changed
    static final int REGROUP_REASON_CHANNEL_UPDATE = 0;
    // Regrouping needed because of notification bundling
@@ -147,24 +150,30 @@ public class GroupHelper {
            mAggregatedNotifications = new ArrayMap<>();

    private static List<NotificationSectioner> NOTIFICATION_SHADE_SECTIONS =
            getNotificationShadeSections();
            getNotificationShadeSections(AUTOGROUP_AT_COUNT_DEFAULT,
                AUTOGROUP_BUNDLES_AT_COUNT_DEFAULT);

    private static List<NotificationSectioner> NOTIFICATION_BUNDLE_SECTIONS;

    private static List<NotificationSectioner> getNotificationShadeSections() {
    private static List<NotificationSectioner> getNotificationShadeSections(int autogroupAtCount,
            int autogroupBundlesAtCount) {
        ArrayList<NotificationSectioner> sectionsList = new ArrayList<>();
        if (android.service.notification.Flags.notificationClassification()) {
            sectionsList.addAll(List.of(
                new NotificationSectioner("PromotionsSection", 0, (record) ->
                new NotificationSectioner("PromotionsSection", 0,
                        autogroupBundlesAtCount, (record) ->
                        NotificationChannel.PROMOTIONS_ID.equals(record.getChannel().getId())
                        && record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT),
                new NotificationSectioner("SocialSection", 0, (record) ->
                new NotificationSectioner("SocialSection", 0,
                        autogroupBundlesAtCount, (record) ->
                        NotificationChannel.SOCIAL_MEDIA_ID.equals(record.getChannel().getId())
                        && record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT),
                new NotificationSectioner("NewsSection", 0, (record) ->
                new NotificationSectioner("NewsSection", 0,
                        autogroupBundlesAtCount, (record) ->
                        NotificationChannel.NEWS_ID.equals(record.getChannel().getId())
                        && record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT),
                new NotificationSectioner("RecsSection", 0, (record) ->
                new NotificationSectioner("RecsSection", 0,
                        autogroupBundlesAtCount, (record) ->
                        NotificationChannel.RECS_ID.equals(record.getChannel().getId())
                        && record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT)
                ));
@@ -174,30 +183,32 @@ public class GroupHelper {

        if (Flags.notificationForceGroupConversations()) {
            // add priority people section
            sectionsList.add(new NotificationSectioner("PeopleSection(priority)", 1, (record) ->
            sectionsList.add(new NotificationSectioner("PeopleSection(priority)", 1,
                    autogroupAtCount, (record) ->
                    record.isConversation() && record.getChannel().isImportantConversation()));

            // add non-priority people section
            sectionsList.add(new NotificationSectioner("PeopleSection", 0,
            sectionsList.add(new NotificationSectioner("PeopleSection", 0, autogroupAtCount,
                    NotificationRecord::isConversation));
        }

        sectionsList.addAll(List.of(
            new NotificationSectioner("AlertingSection", 0, (record) ->
            new NotificationSectioner("AlertingSection", 0, autogroupAtCount, (record) ->
                    record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT),
            new NotificationSectioner("SilentSection", 1, (record) ->
            new NotificationSectioner("SilentSection", 1, autogroupAtCount, (record) ->
                    record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT)));
        return sectionsList;
    }

    public GroupHelper(Context context, PackageManager packageManager, int autoGroupAtCount,
            int autoGroupSparseGroupsAtCount, Callback callback) {
            int autoGroupBundlesAtCount, int autoGroupSparseGroupsAtCount, Callback callback) {
        mAutoGroupAtCount = autoGroupAtCount;
        mCallback =  callback;
        mContext = context;
        mPackageManager = packageManager;
        mAutogroupSparseGroupsAtCount = autoGroupSparseGroupsAtCount;
        NOTIFICATION_SHADE_SECTIONS = getNotificationShadeSections();
        NOTIFICATION_SHADE_SECTIONS = getNotificationShadeSections(autoGroupAtCount,
                autoGroupBundlesAtCount);
    }

    void setTestHarnessExempted(boolean isExempted) {
@@ -610,11 +621,10 @@ public class GroupHelper {
            mUngroupedAbuseNotifications.put(fullAggregateGroupKey, ungrouped);

            // scenario 0: ungrouped notifications
            if (ungrouped.size() >= mAutoGroupAtCount || autogroupSummaryExists) {
            if (ungrouped.size() >= sectioner.mAutogroupAtCount || autogroupSummaryExists) {
                if (DEBUG) {
                    if (ungrouped.size() >= mAutoGroupAtCount) {
                        Slog.i(TAG,
                            "Found >=" + mAutoGroupAtCount
                    if (ungrouped.size() >= sectioner.mAutogroupAtCount) {
                        Slog.i(TAG, "Found >=" + sectioner.mAutogroupAtCount
                                + " ungrouped notifications => force grouping");
                    } else {
                        Slog.i(TAG, "Found aggregate summary => force grouping");
@@ -860,10 +870,11 @@ public class GroupHelper {
        //  or if aggregate group exists
        boolean hasSummary = !mAggregatedNotifications.getOrDefault(fullAggregateGroupKey,
                new ArrayMap<>()).isEmpty();
        if (ungrouped.size() >= mAutoGroupAtCount || hasSummary) {

        if (ungrouped.size() >= sectioner.mAutogroupAtCount || hasSummary) {
            if (DEBUG) {
                if (ungrouped.size() >= mAutoGroupAtCount) {
                    Slog.i(TAG, "Found >=" + mAutoGroupAtCount
                if (ungrouped.size() >= sectioner.mAutogroupAtCount) {
                    Slog.i(TAG, "Found >=" + sectioner.mAutogroupAtCount
                            + " ungrouped notifications => force grouping");
                } else {
                    Slog.i(TAG, "Found aggregate summary => force grouping");
@@ -1245,7 +1256,9 @@ public class GroupHelper {
                String pkgName, ArrayMap<String, NotificationRecord> notificationsToCheck) {
        final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
        for (NotificationRecord record : notificationsToCheck.values()) {
            if (isChildOfValidAppGroup(record)) {
            // Valid app grouped child notification or ungrouped notifications from previous invalid
            // sections (ie. conversations) can be moved to bundle sections
            if (isChildOfValidAppGroup(record) || isNotAppGroupPrevInvalidSection(record)) {
                // Check if section changes to a bundle section
                NotificationSectioner sectioner = getSection(record);
                if (sectioner != null && NOTIFICATION_BUNDLE_SECTIONS.contains(sectioner)) {
@@ -1468,9 +1481,11 @@ public class GroupHelper {
            NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record;
            boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary;
            //Group needs to be created/updated
            if (ungrouped.size() >= mAutoGroupAtCount
                    || (hasSummary && !aggregatedNotificationsAttrs.isEmpty())) {
            NotificationSectioner sectioner = getSection(triggeringNotification);
            final int autogroupAtCount =
                    sectioner != null ? sectioner.mAutogroupAtCount : mAutoGroupAtCount;
            if (ungrouped.size() >= autogroupAtCount
                    || (hasSummary && !aggregatedNotificationsAttrs.isEmpty())) {
                if (sectioner == null) {
                    continue;
                }
@@ -1553,6 +1568,23 @@ public class GroupHelper {
        return true;
    }

    private boolean isNotAppGroupPrevInvalidSection(NotificationRecord record) {
        final StatusBarNotification sbn = record.getSbn();
        if (sbn.isGroup()) {
            return false;
        }

        if (record.isCanceled) {
            return false;
        }

        if (getPreviousValidSectionKey(record) != null) {
            return false;
        }

        return true;
    }

    private static int getNumChildrenForGroup(@NonNull final String groupKey,
            final List<NotificationRecord> notificationList) {
        //TODO (b/349072751): track grouping state in GroupHelper -> do not use notificationList
@@ -1975,12 +2007,14 @@ public class GroupHelper {
    protected static class NotificationSectioner {
        final String mName;
        final int mSummaryId;
        final int mAutogroupAtCount;
        private final Predicate<NotificationRecord> mSectionChecker;

        private NotificationSectioner(String name, int summaryId,
        private NotificationSectioner(String name, int summaryId, int autogroupAtCount,
                Predicate<NotificationRecord> sectionChecker) {
            mName = name;
            mSummaryId = summaryId;
            mAutogroupAtCount = autogroupAtCount;
            mSectionChecker = sectionChecker;
        }

@@ -1991,9 +2025,16 @@ public class GroupHelper {
        private boolean isNotificationGroupable(final NotificationRecord record) {
            if (!Flags.notificationForceGroupConversations()) {
                if (record.isConversation()) {
                    // Bundled conversations are groupable
                    if (android.service.notification.Flags.notificationClassification()) {
                        if (!NOTIFICATION_BUNDLE_SECTIONS.contains(this)) {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
            }

            Notification notification = record.getSbn().getNotification();
            boolean isColorizedFGS = notification.isForegroundService()
+4 −1
Original line number Diff line number Diff line
@@ -651,6 +651,8 @@ public class NotificationManagerService extends SystemService {
    // Minium number of sparse groups for a package before autogrouping them
    private static final int AUTOGROUP_SPARSE_GROUPS_AT_COUNT = 6;
    // Minimum number notifications in a bundle section before autogrouping them
    private static final int AUTOGROUP_BUNDLE_SECTIONS_AT_COUNT = 1;
    private static final Duration ZEN_BROADCAST_DELAY = Duration.ofMillis(250);
@@ -3199,7 +3201,8 @@ public class NotificationManagerService extends SystemService {
        mAutoGroupAtCount =
                getContext().getResources().getInteger(R.integer.config_autoGroupAtCount);
        return new GroupHelper(getContext(), getContext().getPackageManager(),
                mAutoGroupAtCount, AUTOGROUP_SPARSE_GROUPS_AT_COUNT, new GroupHelper.Callback() {
                mAutoGroupAtCount, AUTOGROUP_BUNDLE_SECTIONS_AT_COUNT,
                AUTOGROUP_SPARSE_GROUPS_AT_COUNT, new GroupHelper.Callback() {
            @Override
            public void addAutoGroup(String key, String groupName, boolean requestSort) {
                synchronized (mNotificationLock) {
+265 −50

File changed.

Preview size limit exceeded, changes collapsed.