• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.notification;
18 
19 import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
20 import static android.app.NotificationManager.IMPORTANCE_NONE;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.app.INotificationManager;
25 import android.app.NotificationChannel;
26 import android.app.NotificationChannelGroup;
27 import android.service.notification.Flags;
28 import android.util.ArrayMap;
29 
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 
36 /**
37  * NotificationChannelGroupHelper contains helper methods for associating channels with the groups
38  * they belong to, matching by ID.
39  */
40 public class NotificationChannelGroupsHelper {
41     /**
42      * Set of parameters passed into
43      * {@link NotificationChannelGroupsHelper#getGroupsWithChannels(Collection, Map, Params)}.
44      *
45      * @param includeDeleted Whether to include deleted channels.
46      * @param includeNonGrouped Whether to include channels that are not associated with a group.
47      * @param includeEmpty Whether to include groups containing no channels.
48      * @param includeAllBlockedWithFilter Whether to include channels that are blocked from
49      *                                    sending notifications along with channels specified by
50      *                                    the filter. This setting only takes effect when
51      *                                    channelFilter is not {@code null}, and if true will
52      *                                    include all blocked channels in the output (regardless
53      *                                    of whether they are included in the filter).
54      * @param channelFilter If non-null, a specific set of channels to include. If a channel
55      *                      matching this filter is blocked, it will still be included even
56      *                      if includeAllBlockedWithFilter=false.
57      */
Params( boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty, boolean includeAllBlockedWithFilter, Set<String> channelFilter )58     public record Params(
59             boolean includeDeleted,
60             boolean includeNonGrouped,
61             boolean includeEmpty,
62             boolean includeAllBlockedWithFilter,
63             Set<String> channelFilter
64     ) {
65         /**
66          * Default set of parameters used to specify the behavior of
67          * {@link INotificationManager#getNotificationChannelGroups(String)}. This will include
68          * output for all groups, including those without channels, but not any ungrouped channels.
69          */
70         public static Params forAllGroups() {
71             return new Params(
72                     /* includeDeleted= */ false,
73                     /* includeNonGrouped= */ false,
74                     /* includeEmpty= */ true,
75                     /* includeAllBlockedWithFilter= */ true,
76                     /* channelFilter= */ null);
77         }
78 
79         /**
80          * Parameters to get groups for all channels, including those not associated with any groups
81          * and optionally including deleted channels as well. Channels not associated with a group
82          * are returned inside a group with id {@code null}.
83          *
84          * @param includeDeleted Whether to include deleted channels.
85          */
86         public static Params forAllChannels(boolean includeDeleted) {
87             return new Params(
88                     includeDeleted,
89                     /* includeNonGrouped= */ true,
90                     /* includeEmpty= */ false,
91                     /* includeAllBlockedWithFilter= */ true,
92                     /* channelFilter= */ null);
93         }
94 
95         /**
96          * Parameters to collect groups only for channels specified by the channel filter, as well
97          * as any blocked channels (independent of whether they exist in the filter).
98          * @param channelFilter Specific set of channels to return.
99          */
100         public static Params onlySpecifiedOrBlockedChannels(Set<String> channelFilter) {
101             return new Params(
102                     /* includeDeleted= */ false,
103                     /* includeNonGrouped= */ true,
104                     /* includeEmpty= */ false,
105                     /* includeAllBlockedWithFilter= */ true,
106                     channelFilter);
107         }
108     }
109 
110     /**
111      * Retrieve the {@link NotificationChannelGroup} object specified by the given groupId, if it
112      * exists, with the list of channels filled in from the provided available channels.
113      *
114      * @param groupId The ID of the group to return.
115      * @param allChannels A list of all channels associated with the package.
116      * @param allGroups A map of group ID -> NotificationChannelGroup objects.
117      */
getGroupWithChannels(@onNull String groupId, @NonNull Collection<NotificationChannel> allChannels, @NonNull Map<String, NotificationChannelGroup> allGroups, boolean includeDeleted)118     public static @Nullable NotificationChannelGroup getGroupWithChannels(@NonNull String groupId,
119             @NonNull Collection<NotificationChannel> allChannels,
120             @NonNull Map<String, NotificationChannelGroup> allGroups,
121             boolean includeDeleted) {
122         NotificationChannelGroup group = null;
123         if (allGroups.containsKey(groupId)) {
124             group = allGroups.get(groupId).clone();
125             group.setChannels(new ArrayList<>());
126             for (NotificationChannel nc : allChannels) {
127                 if (includeDeleted || !nc.isDeleted()) {
128                     if (groupId.equals(nc.getGroup())) {
129                         group.addChannel(nc);
130                     }
131                 }
132             }
133         }
134         return group;
135     }
136 
137     /**
138      * Returns a list of groups with their associated channels filled in.
139      *
140      * @param allChannels All available channels that may be associated with these groups.
141      * @param allGroups Map of group ID -> {@link NotificationChannelGroup} objects.
142      * @param params Params indicating which channels and which groups to include.
143      */
getGroupsWithChannels( @onNull Collection<NotificationChannel> allChannels, @NonNull Map<String, NotificationChannelGroup> allGroups, Params params)144     public static @NonNull List<NotificationChannelGroup> getGroupsWithChannels(
145             @NonNull Collection<NotificationChannel> allChannels,
146             @NonNull Map<String, NotificationChannelGroup> allGroups,
147             Params params) {
148         Map<String, NotificationChannelGroup> outputGroups = new ArrayMap<>();
149         NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
150         for (NotificationChannel nc : allChannels) {
151             boolean includeChannel = (params.includeDeleted || !nc.isDeleted())
152                     && (params.channelFilter == null
153                             || (params.includeAllBlockedWithFilter
154                                     && nc.getImportance() == IMPORTANCE_NONE)
155                             || params.channelFilter.contains(nc.getId()))
156                     && (!Flags.notificationClassification()
157                             || !SYSTEM_RESERVED_IDS.contains(nc.getId()));
158             if (includeChannel) {
159                 if (nc.getGroup() != null) {
160                     if (allGroups.get(nc.getGroup()) != null) {
161                         NotificationChannelGroup ncg = outputGroups.get(nc.getGroup());
162                         if (ncg == null) {
163                             ncg = allGroups.get(nc.getGroup()).clone();
164                             ncg.setChannels(new ArrayList<>());
165                             outputGroups.put(nc.getGroup(), ncg);
166                         }
167                         ncg.addChannel(nc);
168                     }
169                 } else {
170                     nonGrouped.addChannel(nc);
171                 }
172             }
173         }
174         if (params.includeNonGrouped && nonGrouped.getChannels().size() > 0) {
175             outputGroups.put(null, nonGrouped);
176         }
177         if (params.includeEmpty) {
178             for (NotificationChannelGroup group : allGroups.values()) {
179                 if (!outputGroups.containsKey(group.getId())) {
180                     outputGroups.put(group.getId(), group);
181                 }
182             }
183         }
184         return new ArrayList<>(outputGroups.values());
185     }
186 }
187