• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.text.TextUtils;
25 import com.android.dialer.common.Assert;
26 import com.android.dialer.common.LogUtil;
27 import com.android.dialer.logging.DialerImpression;
28 import com.android.dialer.logging.Logger;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.List;
33 
34 /**
35  * Utility to ensure that only a certain number of notifications are shown for a particular
36  * notification type. Once the limit is reached, older notifications are cancelled.
37  */
38 /* package */ class NotificationThrottler {
39   /**
40    * For gropued bundled notifications, the system UI will only display the last 8. For grouped
41    * unbundled notifications, the system displays all notifications until a global maximum of 50 is
42    * reached.
43    */
44   private static final int MAX_NOTIFICATIONS_PER_TAG = 10;
45 
46   private static final int HIGH_GLOBAL_NOTIFICATION_COUNT = 45;
47 
48   private static boolean didLogHighGlobalNotificationCountReached;
49 
throttle(@onNull Context context, @NonNull Notification notification)50   /* package */ static void throttle(@NonNull Context context, @NonNull Notification notification) {
51     Assert.isNotNull(context);
52     Assert.isNotNull(notification);
53 
54     // No limiting for non-grouped notifications.
55     String groupKey = notification.getGroup();
56     if (TextUtils.isEmpty(groupKey)) {
57       return;
58     }
59 
60     NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
61     StatusBarNotification[] activeNotifications = notificationManager.getActiveNotifications();
62     if (activeNotifications.length > HIGH_GLOBAL_NOTIFICATION_COUNT
63         && !didLogHighGlobalNotificationCountReached) {
64       LogUtil.i(
65           "NotificationThrottler.throttle",
66           "app has %d notifications, system may suppress future notifications",
67           activeNotifications.length);
68       didLogHighGlobalNotificationCountReached = true;
69       Logger.get(context)
70           .logImpression(DialerImpression.Type.HIGH_GLOBAL_NOTIFICATION_COUNT_REACHED);
71     }
72 
73     // Count the number of notificatons for this group (excluding the summary).
74     int count = 0;
75     for (StatusBarNotification currentNotification : activeNotifications) {
76       if (isNotificationInGroup(currentNotification, groupKey)) {
77         count++;
78       }
79     }
80 
81     if (count > MAX_NOTIFICATIONS_PER_TAG) {
82       LogUtil.i(
83           "NotificationThrottler.throttle",
84           "groupKey: %s is over limit, count: %d, limit: %d",
85           groupKey,
86           count,
87           MAX_NOTIFICATIONS_PER_TAG);
88       List<StatusBarNotification> notifications = getSortedMatchingNotifications(context, groupKey);
89       for (int i = 0; i < (count - MAX_NOTIFICATIONS_PER_TAG); i++) {
90         notificationManager.cancel(notifications.get(i).getTag(), notifications.get(i).getId());
91       }
92     }
93   }
94 
getSortedMatchingNotifications( @onNull Context context, @NonNull String groupKey)95   private static List<StatusBarNotification> getSortedMatchingNotifications(
96       @NonNull Context context, @NonNull String groupKey) {
97     List<StatusBarNotification> notifications = new ArrayList<>();
98     NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
99     for (StatusBarNotification notification : notificationManager.getActiveNotifications()) {
100       if (isNotificationInGroup(notification, groupKey)) {
101         notifications.add(notification);
102       }
103     }
104     Collections.sort(
105         notifications,
106         new Comparator<StatusBarNotification>() {
107           @Override
108           public int compare(StatusBarNotification left, StatusBarNotification right) {
109             return Long.compare(left.getPostTime(), right.getPostTime());
110           }
111         });
112     return notifications;
113   }
114 
isNotificationInGroup( @onNull StatusBarNotification notification, @NonNull String groupKey)115   private static boolean isNotificationInGroup(
116       @NonNull StatusBarNotification notification, @NonNull String groupKey) {
117     // Don't include group summaries.
118     if ((notification.getNotification().flags & Notification.FLAG_GROUP_SUMMARY) != 0) {
119       return false;
120     }
121 
122     return TextUtils.equals(groupKey, notification.getNotification().getGroup());
123   }
124 
NotificationThrottler()125   private NotificationThrottler() {}
126 }
127