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