1 /* 2 * Copyright 2020 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 androidx.core.app; 18 19 import android.app.NotificationChannel; 20 import android.app.NotificationChannelGroup; 21 import android.content.Intent; 22 import android.os.Build; 23 24 import androidx.annotation.RequiresApi; 25 import androidx.core.util.Preconditions; 26 27 import org.jspecify.annotations.NonNull; 28 import org.jspecify.annotations.Nullable; 29 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.List; 33 34 /** 35 * A grouping of related notification channels. e.g., channels that all belong to a single account. 36 * 37 * Setters return {@code this} to allow chaining. 38 * 39 * This class doesn't do anything on older SDKs which don't support Notification Channels. 40 */ 41 public class NotificationChannelGroupCompat { 42 // These fields are settable through the builder 43 final String mId; 44 CharSequence mName; 45 String mDescription; 46 47 // These fields are read-only 48 private boolean mBlocked; 49 private List<NotificationChannelCompat> mChannels = Collections.emptyList(); 50 51 /** 52 * Builder class for {@link NotificationChannelGroupCompat} objects. 53 */ 54 public static class Builder { 55 final NotificationChannelGroupCompat mGroup; 56 57 /** 58 * Creates a notification channel group. 59 * 60 * @param id The id of the group. Must be unique per package. 61 * The value may be truncated if it is too long. 62 */ Builder(@onNull String id)63 public Builder(@NonNull String id) { 64 mGroup = new NotificationChannelGroupCompat(id); 65 } 66 67 /** 68 * Sets the user visible name of this group. 69 * 70 * You can rename this group when the system locale changes by listening for the 71 * {@link Intent#ACTION_LOCALE_CHANGED} broadcast. 72 * 73 * <p>The recommended maximum length is 40 characters; the value may be truncated if it 74 * is too long. 75 */ setName(@ullable CharSequence name)76 public @NonNull Builder setName(@Nullable CharSequence name) { 77 mGroup.mName = name; 78 return this; 79 } 80 81 /** 82 * Sets the user visible description of this group. 83 * 84 * <p>The recommended maximum length is 300 characters; the value may be truncated if it 85 * is too 86 * long. 87 */ setDescription(@ullable String description)88 public @NonNull Builder setDescription(@Nullable String description) { 89 mGroup.mDescription = description; 90 return this; 91 } 92 93 /** 94 * Creates a {@link NotificationChannelGroupCompat} instance. 95 */ build()96 public @NonNull NotificationChannelGroupCompat build() { 97 return mGroup; 98 } 99 } 100 NotificationChannelGroupCompat(@onNull String id)101 NotificationChannelGroupCompat(@NonNull String id) { 102 mId = Preconditions.checkNotNull(id); 103 } 104 105 @RequiresApi(28) NotificationChannelGroupCompat(@onNull NotificationChannelGroup group)106 NotificationChannelGroupCompat(@NonNull NotificationChannelGroup group) { 107 this(group, Collections.<NotificationChannel>emptyList()); 108 } 109 110 @RequiresApi(26) NotificationChannelGroupCompat(@onNull NotificationChannelGroup group, @NonNull List<NotificationChannel> allChannels)111 NotificationChannelGroupCompat(@NonNull NotificationChannelGroup group, 112 @NonNull List<NotificationChannel> allChannels) { 113 this(Api26Impl.getId(group)); 114 // Populate all builder-editable fields 115 mName = Api26Impl.getName(group); 116 if (Build.VERSION.SDK_INT >= 28) { 117 mDescription = Api28Impl.getDescription(group); 118 } 119 // Populate all read-only fields 120 if (Build.VERSION.SDK_INT >= 28) { 121 mBlocked = Api28Impl.isBlocked(group); 122 mChannels = getChannelsCompat(Api26Impl.getChannels(group)); 123 } else { 124 // On API 26 and 27, the NotificationChannelGroup.getChannels() method was broken, 125 // so we collect this information from the full list of channels at construction. 126 mChannels = getChannelsCompat(allChannels); 127 } 128 } 129 130 @RequiresApi(26) getChannelsCompat(List<NotificationChannel> channels)131 private List<NotificationChannelCompat> getChannelsCompat(List<NotificationChannel> channels) { 132 List<NotificationChannelCompat> channelsCompat = new ArrayList<>(); 133 for (NotificationChannel channel : channels) { 134 if (mId.equals(Api26Impl.getGroup(channel))) { 135 channelsCompat.add(new NotificationChannelCompat(channel)); 136 } 137 } 138 return channelsCompat; 139 } 140 141 /** 142 * Gets the platform notification channel group object. 143 * 144 * Returns {@code null} on older SDKs which don't support Notification Channels. 145 */ getNotificationChannelGroup()146 NotificationChannelGroup getNotificationChannelGroup() { 147 if (Build.VERSION.SDK_INT < 26) { 148 return null; 149 } 150 NotificationChannelGroup group = Api26Impl.createNotificationChannelGroup(mId, mName); 151 if (Build.VERSION.SDK_INT >= 28) { 152 Api28Impl.setDescription(group, mDescription); 153 } 154 return group; 155 } 156 157 /** 158 * Creates a {@link Builder} instance with all the writeable property values of this instance. 159 */ toBuilder()160 public @NonNull Builder toBuilder() { 161 return new Builder(mId) 162 .setName(mName) 163 .setDescription(mDescription); 164 } 165 166 /** 167 * Gets the id of the group. 168 */ getId()169 public @NonNull String getId() { 170 return mId; 171 } 172 173 /** 174 * Gets the user visible name of the group. 175 */ getName()176 public @Nullable CharSequence getName() { 177 return mName; 178 } 179 180 /** 181 * Gets the user visible description of the group. 182 */ getDescription()183 public @Nullable String getDescription() { 184 return mDescription; 185 } 186 187 /** 188 * Returns whether or not notifications posted to {@link NotificationChannelCompat} belonging 189 * to this group are blocked. This value is independent of 190 * {@link NotificationManagerCompat#areNotificationsEnabled()} and 191 * {@link NotificationChannelCompat#getImportance()}. 192 * 193 * <p>This value is always {@code false} before {@link Build.VERSION_CODES#P} 194 * 195 * <p>This is a read-only property which is only valid on instances fetched from the 196 * {@link NotificationManagerCompat}. 197 */ isBlocked()198 public boolean isBlocked() { 199 return mBlocked; 200 } 201 202 /** 203 * Returns the list of channels that belong to this group. 204 * 205 * <p>This is a read-only property which is only valid on instances fetched from the 206 * {@link NotificationManagerCompat}. 207 */ getChannels()208 public @NonNull List<NotificationChannelCompat> getChannels() { 209 return mChannels; 210 } 211 212 /** 213 * A class for wrapping calls to {@link NotificationChannelGroupCompat} methods which 214 * were added in API 26; these calls must be wrapped to avoid performance issues. 215 * See the UnsafeNewApiCall lint rule for more details. 216 */ 217 @RequiresApi(26) 218 static class Api26Impl { Api26Impl()219 private Api26Impl() { } 220 createNotificationChannelGroup(String id, CharSequence name)221 static NotificationChannelGroup createNotificationChannelGroup(String id, 222 CharSequence name) { 223 return new NotificationChannelGroup(id, name); 224 } 225 getId(NotificationChannelGroup notificationChannelGroup)226 static String getId(NotificationChannelGroup notificationChannelGroup) { 227 return notificationChannelGroup.getId(); 228 } 229 getName(NotificationChannelGroup notificationChannelGroup)230 static CharSequence getName(NotificationChannelGroup notificationChannelGroup) { 231 return notificationChannelGroup.getName(); 232 } 233 getChannels( NotificationChannelGroup notificationChannelGroup)234 static List<NotificationChannel> getChannels( 235 NotificationChannelGroup notificationChannelGroup) { 236 return notificationChannelGroup.getChannels(); 237 } 238 getGroup(NotificationChannel notificationChannel)239 static String getGroup(NotificationChannel notificationChannel) { 240 return notificationChannel.getGroup(); 241 } 242 } 243 244 /** 245 * A class for wrapping calls to {@link NotificationChannelGroupCompat} methods which 246 * were added in API 28; these calls must be wrapped to avoid performance issues. 247 * See the UnsafeNewApiCall lint rule for more details. 248 */ 249 @RequiresApi(28) 250 static class Api28Impl { Api28Impl()251 private Api28Impl() { } 252 isBlocked(NotificationChannelGroup notificationChannelGroup)253 static boolean isBlocked(NotificationChannelGroup notificationChannelGroup) { 254 return notificationChannelGroup.isBlocked(); 255 } 256 getDescription(NotificationChannelGroup notificationChannelGroup)257 static String getDescription(NotificationChannelGroup notificationChannelGroup) { 258 return notificationChannelGroup.getDescription(); 259 } 260 setDescription(NotificationChannelGroup notificationChannelGroup, String description)261 static void setDescription(NotificationChannelGroup notificationChannelGroup, 262 String description) { 263 notificationChannelGroup.setDescription(description); 264 } 265 } 266 } 267