1 /*
2  * Copyright (C) 2017 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.dialer.notification;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.content.Context;
22 import android.service.notification.StatusBarNotification;
23 import android.support.annotation.NonNull;
24 import android.support.annotation.Nullable;
25 import android.support.v4.os.BuildCompat;
26 import android.text.TextUtils;
27 import android.util.Pair;
28 import com.android.dialer.common.Assert;
29 import com.android.dialer.common.LogUtil;
30 import java.util.HashSet;
31 import java.util.Set;
32 
33 /**
34  * Wrapper around the notification manager APIs. The wrapper ensures that channels are set and that
35  * notifications are limited to 10 per group.
36  */
37 public final class DialerNotificationManager {
38 
39   private static final Set<StatusBarNotification> throttledNotificationSet = new HashSet<>();
40 
notify(@onNull Context context, int id, @NonNull Notification notification)41   public static void notify(@NonNull Context context, int id, @NonNull Notification notification) {
42     Assert.isNotNull(context);
43     Assert.isNotNull(notification);
44     throw Assert.createUnsupportedOperationFailException("all notifications must have tags");
45   }
46 
notify( @onNull Context context, @NonNull String tag, int id, @NonNull Notification notification)47   public static void notify(
48       @NonNull Context context, @NonNull String tag, int id, @NonNull Notification notification) {
49     Assert.isNotNull(context);
50     Assert.isNotNull(notification);
51     Assert.checkArgument(!TextUtils.isEmpty(tag));
52 
53     if (BuildCompat.isAtLeastO()) {
54       Assert.checkArgument(!TextUtils.isEmpty(notification.getChannelId()));
55     }
56 
57     getNotificationManager(context).notify(tag, id, notification);
58     throttledNotificationSet.addAll(NotificationThrottler.throttle(context, notification));
59   }
60 
cancel(@onNull Context context, int id)61   public static void cancel(@NonNull Context context, int id) {
62     Assert.isNotNull(context);
63     throw Assert.createUnsupportedOperationFailException(
64         "notification IDs are not unique across the app, a tag must be specified");
65   }
66 
cancel(@onNull Context context, @NonNull String tag, int id)67   public static void cancel(@NonNull Context context, @NonNull String tag, int id) {
68     Assert.isNotNull(context);
69     Assert.checkArgument(!TextUtils.isEmpty(tag));
70 
71     NotificationManager notificationManager = getNotificationManager(context);
72     StatusBarNotification[] notifications = notificationManager.getActiveNotifications();
73 
74     String groupKey = findGroupKey(notifications, tag, id);
75     if (!TextUtils.isEmpty(groupKey)) {
76       Pair<StatusBarNotification, Integer> groupSummaryAndCount =
77           getGroupSummaryAndCount(notifications, groupKey);
78       if (groupSummaryAndCount.first != null && groupSummaryAndCount.second <= 1) {
79         LogUtil.i(
80             "DialerNotificationManager.cancel",
81             "last notification in group (%s) removed, also removing group summary",
82             groupKey);
83         notificationManager.cancel(
84             groupSummaryAndCount.first.getTag(), groupSummaryAndCount.first.getId());
85       }
86     }
87 
88     notificationManager.cancel(tag, id);
89   }
90 
cancelAll(Context context, String prefix)91   public static void cancelAll(Context context, String prefix) {
92     NotificationManager notificationManager = getNotificationManager(context);
93     StatusBarNotification[] notifications = notificationManager.getActiveNotifications();
94     for (StatusBarNotification notification : notifications) {
95       if (notification.getTag() != null && notification.getTag().startsWith(prefix)) {
96         notificationManager.cancel(notification.getTag(), notification.getId());
97       }
98     }
99   }
100 
getActiveNotifications(@onNull Context context)101   public static StatusBarNotification[] getActiveNotifications(@NonNull Context context) {
102     Assert.isNotNull(context);
103     return getNotificationManager(context).getActiveNotifications();
104   }
105 
106   @Nullable
findGroupKey( @onNull StatusBarNotification[] notifications, @NonNull String tag, int id)107   private static String findGroupKey(
108       @NonNull StatusBarNotification[] notifications, @NonNull String tag, int id) {
109     for (StatusBarNotification notification : notifications) {
110       if (TextUtils.equals(tag, notification.getTag()) && id == notification.getId()) {
111         return notification.getNotification().getGroup();
112       }
113     }
114     return null;
115   }
116 
117   @NonNull
getGroupSummaryAndCount( @onNull StatusBarNotification[] notifications, @NonNull String groupKey)118   private static Pair<StatusBarNotification, Integer> getGroupSummaryAndCount(
119       @NonNull StatusBarNotification[] notifications, @NonNull String groupKey) {
120     StatusBarNotification groupSummaryNotification = null;
121     int groupCount = 0;
122     for (StatusBarNotification notification : notifications) {
123       if (TextUtils.equals(groupKey, notification.getNotification().getGroup())) {
124         if ((notification.getNotification().flags & Notification.FLAG_GROUP_SUMMARY) != 0) {
125           groupSummaryNotification = notification;
126         } else {
127           groupCount++;
128         }
129       }
130     }
131     return new Pair<>(groupSummaryNotification, groupCount);
132   }
133 
134   @NonNull
getNotificationManager(@onNull Context context)135   private static NotificationManager getNotificationManager(@NonNull Context context) {
136     return context.getSystemService(NotificationManager.class);
137   }
138 
getThrottledNotificationSet()139   public static Set<StatusBarNotification> getThrottledNotificationSet() {
140     return throttledNotificationSet;
141   }
142 
DialerNotificationManager()143   private DialerNotificationManager() {}
144 }
145