Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | package com.android.server.notification; |
| 17 | |
| 18 | import android.service.notification.StatusBarNotification; |
| 19 | import android.util.Log; |
| 20 | import android.util.Slog; |
| 21 | |
| 22 | import java.util.ArrayList; |
| 23 | import java.util.HashMap; |
| 24 | import java.util.LinkedHashSet; |
| 25 | import java.util.List; |
| 26 | import java.util.Map; |
| 27 | |
| 28 | /** |
| 29 | * NotificationManagerService helper for auto-grouping notifications. |
| 30 | */ |
| 31 | public class GroupHelper { |
| 32 | private static final String TAG = "GroupHelper"; |
| 33 | private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); |
| 34 | |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 35 | protected static final String AUTOGROUP_KEY = "ranker_group"; |
| 36 | |
| 37 | private final Callback mCallback; |
Adora Zhang | 48dd614a8 | 2018-06-25 19:18:41 -0700 | [diff] [blame^] | 38 | private final int mAutoGroupAtCount; |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 39 | |
| 40 | // Map of user : <Map of package : notification keys>. Only contains notifications that are not |
Julia Reynolds | a13b3e2 | 2017-08-10 16:58:54 -0400 | [diff] [blame] | 41 | // grouped by the app (aka no group or sort key). |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 42 | Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>(); |
| 43 | |
Adora Zhang | 48dd614a8 | 2018-06-25 19:18:41 -0700 | [diff] [blame^] | 44 | public GroupHelper(int autoGroupAtCount, Callback callback) { |
| 45 | mAutoGroupAtCount = autoGroupAtCount; |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 46 | mCallback = callback; |
| 47 | } |
| 48 | |
Julia Reynolds | a13b3e2 | 2017-08-10 16:58:54 -0400 | [diff] [blame] | 49 | public void onNotificationPosted(StatusBarNotification sbn, boolean autogroupSummaryExists) { |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 50 | if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey()); |
| 51 | try { |
| 52 | List<String> notificationsToGroup = new ArrayList<>(); |
| 53 | if (!sbn.isAppGroup()) { |
| 54 | // Not grouped by the app, add to the list of notifications for the app; |
| 55 | // send grouping update if app exceeds the autogrouping limit. |
| 56 | synchronized (mUngroupedNotifications) { |
| 57 | Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser |
| 58 | = mUngroupedNotifications.get(sbn.getUserId()); |
| 59 | if (ungroupedNotificationsByUser == null) { |
| 60 | ungroupedNotificationsByUser = new HashMap<>(); |
| 61 | } |
| 62 | mUngroupedNotifications.put(sbn.getUserId(), ungroupedNotificationsByUser); |
| 63 | LinkedHashSet<String> notificationsForPackage |
| 64 | = ungroupedNotificationsByUser.get(sbn.getPackageName()); |
| 65 | if (notificationsForPackage == null) { |
| 66 | notificationsForPackage = new LinkedHashSet<>(); |
| 67 | } |
| 68 | |
| 69 | notificationsForPackage.add(sbn.getKey()); |
| 70 | ungroupedNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage); |
| 71 | |
Adora Zhang | 48dd614a8 | 2018-06-25 19:18:41 -0700 | [diff] [blame^] | 72 | if (notificationsForPackage.size() >= mAutoGroupAtCount |
Julia Reynolds | a13b3e2 | 2017-08-10 16:58:54 -0400 | [diff] [blame] | 73 | || autogroupSummaryExists) { |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 74 | notificationsToGroup.addAll(notificationsForPackage); |
| 75 | } |
| 76 | } |
| 77 | if (notificationsToGroup.size() > 0) { |
| 78 | adjustAutogroupingSummary(sbn.getUserId(), sbn.getPackageName(), |
| 79 | notificationsToGroup.get(0), true); |
| 80 | adjustNotificationBundling(notificationsToGroup, true); |
| 81 | } |
| 82 | } else { |
| 83 | // Grouped, but not by us. Send updates to un-autogroup, if we grouped it. |
| 84 | maybeUngroup(sbn, false, sbn.getUserId()); |
| 85 | } |
| 86 | } catch (Exception e) { |
| 87 | Slog.e(TAG, "Failure processing new notification", e); |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | public void onNotificationRemoved(StatusBarNotification sbn) { |
| 92 | try { |
| 93 | maybeUngroup(sbn, true, sbn.getUserId()); |
| 94 | } catch (Exception e) { |
| 95 | Slog.e(TAG, "Error processing canceled notification", e); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | /** |
Julia Reynolds | f4af65b | 2016-12-20 17:05:12 -0500 | [diff] [blame] | 100 | * Un-autogroups notifications that are now grouped by the app. |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 101 | */ |
| 102 | private void maybeUngroup(StatusBarNotification sbn, boolean notificationGone, int userId) { |
| 103 | List<String> notificationsToUnAutogroup = new ArrayList<>(); |
| 104 | boolean removeSummary = false; |
| 105 | synchronized (mUngroupedNotifications) { |
Julia Reynolds | f4af65b | 2016-12-20 17:05:12 -0500 | [diff] [blame] | 106 | Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 107 | = mUngroupedNotifications.get(sbn.getUserId()); |
Julia Reynolds | f4af65b | 2016-12-20 17:05:12 -0500 | [diff] [blame] | 108 | if (ungroupedNotificationsByUser == null || ungroupedNotificationsByUser.size() == 0) { |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 109 | return; |
| 110 | } |
| 111 | LinkedHashSet<String> notificationsForPackage |
Julia Reynolds | f4af65b | 2016-12-20 17:05:12 -0500 | [diff] [blame] | 112 | = ungroupedNotificationsByUser.get(sbn.getPackageName()); |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 113 | if (notificationsForPackage == null || notificationsForPackage.size() == 0) { |
| 114 | return; |
| 115 | } |
| 116 | if (notificationsForPackage.remove(sbn.getKey())) { |
| 117 | if (!notificationGone) { |
| 118 | // Add the current notification to the ungrouping list if it still exists. |
| 119 | notificationsToUnAutogroup.add(sbn.getKey()); |
| 120 | } |
Julia Reynolds | f4af65b | 2016-12-20 17:05:12 -0500 | [diff] [blame] | 121 | } |
| 122 | // If the status change of this notification has brought the number of loose |
| 123 | // notifications to zero, remove the summary and un-autogroup. |
| 124 | if (notificationsForPackage.size() == 0) { |
Julia Reynolds | a13b3e2 | 2017-08-10 16:58:54 -0400 | [diff] [blame] | 125 | ungroupedNotificationsByUser.remove(sbn.getPackageName()); |
Julia Reynolds | f4af65b | 2016-12-20 17:05:12 -0500 | [diff] [blame] | 126 | removeSummary = true; |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 127 | } |
| 128 | } |
Julia Reynolds | f4af65b | 2016-12-20 17:05:12 -0500 | [diff] [blame] | 129 | if (removeSummary) { |
| 130 | adjustAutogroupingSummary(userId, sbn.getPackageName(), null, false); |
| 131 | } |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 132 | if (notificationsToUnAutogroup.size() > 0) { |
Julia Reynolds | 8f488d3 | 2016-10-14 10:59:01 -0400 | [diff] [blame] | 133 | adjustNotificationBundling(notificationsToUnAutogroup, false); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | private void adjustAutogroupingSummary(int userId, String packageName, String triggeringKey, |
| 138 | boolean summaryNeeded) { |
| 139 | if (summaryNeeded) { |
| 140 | mCallback.addAutoGroupSummary(userId, packageName, triggeringKey); |
| 141 | } else { |
| 142 | mCallback.removeAutoGroupSummary(userId, packageName); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | private void adjustNotificationBundling(List<String> keys, boolean group) { |
| 147 | for (String key : keys) { |
| 148 | if (DEBUG) Log.i(TAG, "Sending grouping adjustment for: " + key + " group? " + group); |
| 149 | if (group) { |
| 150 | mCallback.addAutoGroup(key); |
| 151 | } else { |
| 152 | mCallback.removeAutoGroup(key); |
| 153 | } |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | protected interface Callback { |
| 158 | void addAutoGroup(String key); |
| 159 | void removeAutoGroup(String key); |
| 160 | void addAutoGroupSummary(int userId, String pkg, String triggeringKey); |
| 161 | void removeAutoGroupSummary(int user, String pkg); |
| 162 | } |
| 163 | } |