blob: 4ed9ae4d5142e5447d951a33e0ed9fee297efb6a [file] [log] [blame]
Eliot Courtneya6d8cf22017-10-20 13:26:58 +09001/*
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 */
Rohan Shah20790b82018-07-02 17:21:04 -070016package com.android.systemui.statusbar.notification;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090017
Tony Mak628cb932018-06-19 18:30:41 +010018import android.annotation.Nullable;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090019import android.app.Notification;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090020import android.content.Context;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090021import android.service.notification.NotificationListenerService;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090022import android.service.notification.StatusBarNotification;
Tony Mak628cb932018-06-19 18:30:41 +010023import android.util.ArrayMap;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090024import android.util.Log;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090025
Julia Reynolds91590062018-04-02 16:24:11 -040026import com.android.internal.annotations.VisibleForTesting;
Dieter Hsud39f0d52018-04-14 02:08:30 +080027import com.android.internal.statusbar.NotificationVisibility;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090028import com.android.systemui.Dependency;
29import com.android.systemui.Dumpable;
Kevin38ce6fa2018-10-17 16:00:14 -070030import com.android.systemui.statusbar.NotificationLifetimeExtender;
Rohan Shah20790b82018-07-02 17:21:04 -070031import com.android.systemui.statusbar.NotificationPresenter;
32import com.android.systemui.statusbar.NotificationRemoteInputManager;
33import com.android.systemui.statusbar.NotificationUiAdjustment;
34import com.android.systemui.statusbar.NotificationUpdateHandler;
Ned Burnsf81c4c42019-01-07 14:10:43 -050035import com.android.systemui.statusbar.notification.collection.NotificationData;
36import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
37import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Ned Burnsc5864672019-02-20 12:57:29 -050038import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
Gustav Senntonf892fe92019-01-22 15:31:42 +000039import com.android.systemui.statusbar.notification.logging.NotificationLogger;
Ned Burns1a5e22f2019-02-14 15:11:52 -050040import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
41import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
Rohan Shah20790b82018-07-02 17:21:04 -070042import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090043import com.android.systemui.statusbar.policy.HeadsUpManager;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090044import com.android.systemui.util.leak.LeakDetector;
45
46import java.io.FileDescriptor;
47import java.io.PrintWriter;
48import java.util.ArrayList;
49import java.util.HashMap;
50import java.util.List;
Ned Burnsa5dad552019-01-29 17:59:10 -050051import java.util.Map;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090052
53/**
54 * NotificationEntryManager is responsible for the adding, removing, and updating of notifications.
55 * It also handles tasks such as their inflation and their interaction with other
56 * Notification.*Manager objects.
57 */
Gus Prevas8ba88a82018-12-18 11:13:44 -050058public class NotificationEntryManager implements
59 Dumpable,
Ned Burns1a5e22f2019-02-14 15:11:52 -050060 NotificationContentInflater.InflationCallback,
Gus Prevas8ba88a82018-12-18 11:13:44 -050061 NotificationUpdateHandler,
Ned Burns01e38212019-01-03 16:32:52 -050062 VisualStabilityManager.Callback {
Julia Reynoldsfc640012018-02-21 12:25:27 -050063 private static final String TAG = "NotificationEntryMgr";
Ned Burnsd35708c2019-01-09 15:38:03 -050064 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Eliot Courtney6c313d32017-12-14 19:57:51 +090065
Ned Burnsd35708c2019-01-09 15:38:03 -050066 @VisibleForTesting
Ned Burnsf81c4c42019-01-07 14:10:43 -050067 protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090068
Ned Burnsa5dad552019-01-29 17:59:10 -050069 private final Map<NotificationEntry, NotificationLifetimeExtender> mRetainedNotifications =
70 new ArrayMap<>();
71
Jason Monk297c04e2018-08-23 17:16:59 -040072 // Lazily retrieved dependencies
73 private NotificationRemoteInputManager mRemoteInputManager;
Gus Prevas8ba88a82018-12-18 11:13:44 -050074 private NotificationRowBinder mNotificationRowBinder;
Eliot Courtney6c313d32017-12-14 19:57:51 +090075
Jason Monk297c04e2018-08-23 17:16:59 -040076 private NotificationPresenter mPresenter;
Ned Burnsdc10c952018-11-21 14:36:21 -050077 private NotificationListenerService.RankingMap mLatestRankingMap;
Ned Burnsd35708c2019-01-09 15:38:03 -050078 @VisibleForTesting
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090079 protected NotificationData mNotificationData;
Ned Burnsf36c6252019-01-09 19:12:33 -050080
Ned Burnsdc10c952018-11-21 14:36:21 -050081 @VisibleForTesting
82 final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
Kevina5ff1fa2018-08-21 16:35:48 -070083 = new ArrayList<>();
Gus Prevasd65c2db2018-12-18 17:13:38 -050084 private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090085
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090086 @Override
87 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
88 pw.println("NotificationEntryManager state:");
89 pw.print(" mPendingNotifications=");
90 if (mPendingNotifications.size() == 0) {
91 pw.println("null");
92 } else {
Ned Burnsf81c4c42019-01-07 14:10:43 -050093 for (NotificationEntry entry : mPendingNotifications.values()) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090094 pw.println(entry.notification);
95 }
96 }
Ned Burnsa5dad552019-01-29 17:59:10 -050097 pw.println(" Lifetime-extended notifications:");
98 if (mRetainedNotifications.isEmpty()) {
99 pw.println(" None");
100 } else {
101 for (Map.Entry<NotificationEntry, NotificationLifetimeExtender> entry
102 : mRetainedNotifications.entrySet()) {
103 pw.println(" " + entry.getKey().notification + " retained by "
104 + entry.getValue().getClass().getName());
105 }
106 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900107 }
108
Eliot Courtney6c313d32017-12-14 19:57:51 +0900109 public NotificationEntryManager(Context context) {
Jason Monk297c04e2018-08-23 17:16:59 -0400110 mNotificationData = new NotificationData();
Jason Monk297c04e2018-08-23 17:16:59 -0400111 }
112
Gus Prevasd65c2db2018-12-18 17:13:38 -0500113 /** Adds a {@link NotificationEntryListener}. */
114 public void addNotificationEntryListener(NotificationEntryListener listener) {
115 mNotificationEntryListeners.add(listener);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900116 }
117
Jason Monk297c04e2018-08-23 17:16:59 -0400118 /**
119 * Our dependencies can have cyclic references, so some need to be lazy
120 */
121 private NotificationRemoteInputManager getRemoteInputManager() {
122 if (mRemoteInputManager == null) {
123 mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
124 }
125 return mRemoteInputManager;
126 }
127
Ned Burnsc5864672019-02-20 12:57:29 -0500128 public void setRowBinder(NotificationRowBinder notificationRowBinder) {
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500129 mNotificationRowBinder = notificationRowBinder;
130 }
131
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900132 public void setUpWithPresenter(NotificationPresenter presenter,
Gus Prevas8621bd22018-12-20 15:04:25 -0500133 NotificationListContainer listContainer,
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900134 HeadsUpManager headsUpManager) {
135 mPresenter = presenter;
Gus Prevas92586462019-01-04 16:06:12 -0500136 mNotificationData.setHeadsUpManager(headsUpManager);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900137 }
138
Gus Prevas5c84abb2018-12-28 16:41:49 -0500139 /** Adds multiple {@link NotificationLifetimeExtender}s. */
140 public void addNotificationLifetimeExtenders(List<NotificationLifetimeExtender> extenders) {
141 for (NotificationLifetimeExtender extender : extenders) {
142 addNotificationLifetimeExtender(extender);
143 }
144 }
145
146 /** Adds a {@link NotificationLifetimeExtender}. */
147 public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
148 mNotificationLifetimeExtenders.add(extender);
149 extender.setCallback(key -> removeNotification(key, mLatestRankingMap));
150 }
151
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900152 public NotificationData getNotificationData() {
153 return mNotificationData;
154 }
155
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900156 @Override
157 public void onReorderingAllowed() {
158 updateNotifications();
159 }
160
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900161 public void performRemoveNotification(StatusBarNotification n) {
Dieter Hsud39f0d52018-04-14 02:08:30 +0800162 final int rank = mNotificationData.getRank(n.getKey());
163 final int count = mNotificationData.getActiveNotifications().size();
Gustav Senntonf892fe92019-01-22 15:31:42 +0000164 NotificationVisibility.NotificationLocation location =
165 NotificationLogger.getNotificationLocation(getNotificationData().get(n.getKey()));
Dieter Hsud39f0d52018-04-14 02:08:30 +0800166 final NotificationVisibility nv = NotificationVisibility.obtain(n.getKey(), rank, count,
Gustav Senntonf892fe92019-01-22 15:31:42 +0000167 true, location);
Gus Prevasca1b6f72018-12-28 10:53:11 -0500168 removeNotificationInternal(
169 n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900170 }
171
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900172 private void abortExistingInflation(String key) {
173 if (mPendingNotifications.containsKey(key)) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500174 NotificationEntry entry = mPendingNotifications.get(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900175 entry.abortTask();
176 mPendingNotifications.remove(key);
177 }
Ned Burnsf81c4c42019-01-07 14:10:43 -0500178 NotificationEntry addedEntry = mNotificationData.get(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900179 if (addedEntry != null) {
180 addedEntry.abortTask();
181 }
182 }
183
Ned Burnsdc10c952018-11-21 14:36:21 -0500184 /**
185 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
186 * about the failure.
187 *
188 * WARNING: this will call back into us. Don't hold any locks.
189 */
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900190 @Override
Ned Burnsdc10c952018-11-21 14:36:21 -0500191 public void handleInflationException(StatusBarNotification n, Exception e) {
Gus Prevasdca2be52018-12-21 11:25:10 -0500192 removeNotificationInternal(
Gus Prevasca1b6f72018-12-28 10:53:11 -0500193 n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */);
194 for (NotificationEntryListener listener : mNotificationEntryListeners) {
195 listener.onInflationError(n, e);
Ned Burnsdc10c952018-11-21 14:36:21 -0500196 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900197 }
198
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900199 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500200 public void onAsyncInflationFinished(NotificationEntry entry,
Kevind4660b22018-09-27 10:57:35 -0700201 @InflationFlag int inflatedFlags) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900202 mPendingNotifications.remove(entry.key);
203 // If there was an async task started after the removal, we don't want to add it back to
204 // the list, otherwise we might get leaks.
Evan Laird94492852018-10-25 13:43:01 -0400205 if (!entry.isRowRemoved()) {
Kevin01a53cb2018-11-09 18:19:54 -0800206 boolean isNew = mNotificationData.get(entry.key) == null;
207 if (isNew) {
Gus Prevasb43dc652018-12-20 13:11:45 -0500208 for (NotificationEntryListener listener : mNotificationEntryListeners) {
209 listener.onEntryInflated(entry, inflatedFlags);
210 }
Ned Burns69760872019-01-03 15:36:38 -0500211 mNotificationData.add(entry);
Ned Burnsf36c6252019-01-09 19:12:33 -0500212 for (NotificationEntryListener listener : mNotificationEntryListeners) {
213 listener.onBeforeNotificationAdded(entry);
214 }
Ned Burns69760872019-01-03 15:36:38 -0500215 updateNotifications();
216 for (NotificationEntryListener listener : mNotificationEntryListeners) {
217 listener.onNotificationAdded(entry);
218 }
Kevin01a53cb2018-11-09 18:19:54 -0800219 } else {
Gus Prevasd65c2db2018-12-18 17:13:38 -0500220 for (NotificationEntryListener listener : mNotificationEntryListeners) {
221 listener.onEntryReinflated(entry);
Ned Burns3d6b3962018-12-07 21:26:00 -0500222 }
Kevin01a53cb2018-11-09 18:19:54 -0800223 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900224 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900225 }
226
227 @Override
228 public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
Gus Prevasdca2be52018-12-21 11:25:10 -0500229 removeNotificationInternal(
Gus Prevasca1b6f72018-12-28 10:53:11 -0500230 key, ranking, null, false /* forceRemove */, false /* removedByUser */);
Kevina5ff1fa2018-08-21 16:35:48 -0700231 }
232
Gus Prevasdca2be52018-12-21 11:25:10 -0500233 private void removeNotificationInternal(
234 String key,
235 @Nullable NotificationListenerService.RankingMap ranking,
Gus Prevasca1b6f72018-12-28 10:53:11 -0500236 @Nullable NotificationVisibility visibility,
Gus Prevasdca2be52018-12-21 11:25:10 -0500237 boolean forceRemove,
238 boolean removedByUser) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500239 final NotificationEntry entry = mNotificationData.get(key);
Ned Burns3d6b3962018-12-07 21:26:00 -0500240
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900241 abortExistingInflation(key);
Gus Prevasf37435a2018-12-20 15:40:01 -0500242
Gus Prevasf37435a2018-12-20 15:40:01 -0500243 boolean lifetimeExtended = false;
244
Gus Prevasd65c2db2018-12-18 17:13:38 -0500245 if (entry != null) {
Gus Prevasf37435a2018-12-20 15:40:01 -0500246 // If a manager needs to keep the notification around for whatever reason, we
247 // keep the notification
248 if (!forceRemove) {
249 for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
250 if (extender.shouldExtendLifetime(entry)) {
251 mLatestRankingMap = ranking;
Ned Burnsa5dad552019-01-29 17:59:10 -0500252 extendLifetime(entry, extender);
Gus Prevasf37435a2018-12-20 15:40:01 -0500253 lifetimeExtended = true;
254 break;
255 }
Kevina5ff1fa2018-08-21 16:35:48 -0700256 }
257 }
Gus Prevasf37435a2018-12-20 15:40:01 -0500258
259 if (!lifetimeExtended) {
260 // At this point, we are guaranteed the notification will be removed
261
262 // Ensure any managers keeping the lifetime extended stop managing the entry
Ned Burnsa5dad552019-01-29 17:59:10 -0500263 cancelLifetimeExtension(entry);
Gus Prevasf37435a2018-12-20 15:40:01 -0500264
Gus Prevasf37435a2018-12-20 15:40:01 -0500265 if (entry.rowExists()) {
266 entry.removeRow();
Gus Prevasf37435a2018-12-20 15:40:01 -0500267 }
268
269 // Let's remove the children if this was a summary
270 handleGroupSummaryRemoved(key);
271
Ned Burns69760872019-01-03 15:36:38 -0500272 mNotificationData.remove(key, ranking);
273 updateNotifications();
274 Dependency.get(LeakDetector.class).trackGarbage(entry);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900275
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500276 for (NotificationEntryListener listener : mNotificationEntryListeners) {
277 listener.onEntryRemoved(entry, visibility, removedByUser);
278 }
279 }
Gus Prevas8621bd22018-12-20 15:04:25 -0500280 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900281 }
282
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900283 /**
284 * Ensures that the group children are cancelled immediately when the group summary is cancelled
285 * instead of waiting for the notification manager to send all cancels. Otherwise this could
286 * lead to flickers.
287 *
288 * This also ensures that the animation looks nice and only consists of a single disappear
289 * animation instead of multiple.
290 * @param key the key of the notification was removed
291 *
292 */
293 private void handleGroupSummaryRemoved(String key) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500294 NotificationEntry entry = mNotificationData.get(key);
Evan Laird94492852018-10-25 13:43:01 -0400295 if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
296 if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900297 // We don't want to remove children for autobundled notifications as they are not
298 // always cancelled. We only remove them if they were dismissed by the user.
299 return;
300 }
Ned Burnsf81c4c42019-01-07 14:10:43 -0500301 List<NotificationEntry> childEntries = entry.getChildren();
Evan Laird94492852018-10-25 13:43:01 -0400302 if (childEntries == null) {
303 return;
304 }
305 for (int i = 0; i < childEntries.size(); i++) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500306 NotificationEntry childEntry = childEntries.get(i);
Evan Laird94492852018-10-25 13:43:01 -0400307 boolean isForeground = (entry.notification.getNotification().flags
Selim Cinek038e2592018-08-16 16:30:45 -0700308 & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Kevina5ff1fa2018-08-21 16:35:48 -0700309 boolean keepForReply =
Jason Monk297c04e2018-08-23 17:16:59 -0400310 getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
311 || getRemoteInputManager().shouldKeepForSmartReplyHistory(childEntry);
Selim Cinek038e2592018-08-16 16:30:45 -0700312 if (isForeground || keepForReply) {
313 // the child is a foreground service notification which we can't remove or it's
314 // a child we're keeping around for reply!
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900315 continue;
316 }
Evan Laird94492852018-10-25 13:43:01 -0400317 entry.setKeepInParent(true);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900318 // we need to set this state earlier as otherwise we might generate some weird
319 // animations
Evan Laird94492852018-10-25 13:43:01 -0400320 entry.removeRow();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900321 }
322 }
323 }
324
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900325 private void addNotificationInternal(StatusBarNotification notification,
Tony Mak628cb932018-06-19 18:30:41 +0100326 NotificationListenerService.RankingMap rankingMap) throws InflationException {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900327 String key = notification.getKey();
Kevina97ea052018-09-11 13:53:18 -0700328 if (DEBUG) {
329 Log.d(TAG, "addNotification key=" + key);
330 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900331
Tony Mak628cb932018-06-19 18:30:41 +0100332 mNotificationData.updateRanking(rankingMap);
333 NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
334 rankingMap.getRanking(key, ranking);
Ned Burns69760872019-01-03 15:36:38 -0500335
Ned Burnsf81c4c42019-01-07 14:10:43 -0500336 NotificationEntry entry = new NotificationEntry(notification, ranking);
Ned Burns69760872019-01-03 15:36:38 -0500337
338 Dependency.get(LeakDetector.class).trackInstance(entry);
339 // Construct the expanded view.
Ned Burnsc5864672019-02-20 12:57:29 -0500340 requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
Ned Burns69760872019-01-03 15:36:38 -0500341
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900342 abortExistingInflation(key);
343
Gus Prevasd65c2db2018-12-18 17:13:38 -0500344 mPendingNotifications.put(key, entry);
345 for (NotificationEntryListener listener : mNotificationEntryListeners) {
346 listener.onPendingEntryAdded(entry);
Ned Burns3d6b3962018-12-07 21:26:00 -0500347 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900348 }
349
350 @Override
351 public void addNotification(StatusBarNotification notification,
352 NotificationListenerService.RankingMap ranking) {
353 try {
354 addNotificationInternal(notification, ranking);
355 } catch (InflationException e) {
356 handleInflationException(notification, e);
357 }
358 }
359
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900360 private void updateNotificationInternal(StatusBarNotification notification,
361 NotificationListenerService.RankingMap ranking) throws InflationException {
362 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
363
364 final String key = notification.getKey();
365 abortExistingInflation(key);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500366 NotificationEntry entry = mNotificationData.get(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900367 if (entry == null) {
368 return;
369 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900370
Kevina5ff1fa2018-08-21 16:35:48 -0700371 // Notification is updated so it is essentially re-added and thus alive again. Don't need
Kevine9e938c2018-09-06 13:38:11 -0700372 // to keep its lifetime extended.
Ned Burnsa5dad552019-01-29 17:59:10 -0500373 cancelLifetimeExtension(entry);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900374
Gus Prevas26d91272018-12-21 16:43:28 -0500375 mNotificationData.update(entry, ranking, notification);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900376
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800377 for (NotificationEntryListener listener : mNotificationEntryListeners) {
378 listener.onPreEntryUpdated(entry);
379 }
380
Ned Burnsc5864672019-02-20 12:57:29 -0500381 requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900382 updateNotifications();
383
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900384 if (DEBUG) {
385 // Is this for you?
Jason Monk297c04e2018-08-23 17:16:59 -0400386 boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class)
387 .isNotificationForCurrentProfiles(notification);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900388 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
389 }
390
Gus Prevasb43dc652018-12-20 13:11:45 -0500391 for (NotificationEntryListener listener : mNotificationEntryListeners) {
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800392 listener.onPostEntryUpdated(entry);
Gus Prevasb43dc652018-12-20 13:11:45 -0500393 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900394 }
395
396 @Override
397 public void updateNotification(StatusBarNotification notification,
398 NotificationListenerService.RankingMap ranking) {
399 try {
400 updateNotificationInternal(notification, ranking);
401 } catch (InflationException e) {
402 handleInflationException(notification, e);
403 }
404 }
405
406 public void updateNotifications() {
407 mNotificationData.filterAndSort();
Ned Burnsf36c6252019-01-09 19:12:33 -0500408 if (mPresenter != null) {
409 mPresenter.updateNotificationViews();
410 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900411 }
412
Ned Burns6b19b082019-02-12 18:50:47 -0500413 @Override
Tony Mak628cb932018-06-19 18:30:41 +0100414 public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500415 List<NotificationEntry> entries = new ArrayList<>();
Tony Mak628cb932018-06-19 18:30:41 +0100416 entries.addAll(mNotificationData.getActiveNotifications());
417 entries.addAll(mPendingNotifications.values());
418
419 // Has a copy of the current UI adjustments.
420 ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
Gus Prevas33fbc152018-12-04 13:17:32 -0500421 ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500422 for (NotificationEntry entry : entries) {
Tony Mak628cb932018-06-19 18:30:41 +0100423 NotificationUiAdjustment adjustment =
424 NotificationUiAdjustment.extractFromNotificationEntry(entry);
425 oldAdjustments.put(entry.key, adjustment);
Gus Prevas33fbc152018-12-04 13:17:32 -0500426 oldImportances.put(entry.key, entry.importance);
Tony Mak628cb932018-06-19 18:30:41 +0100427 }
428
429 // Populate notification entries from the new rankings.
430 mNotificationData.updateRanking(rankingMap);
431 updateRankingOfPendingNotifications(rankingMap);
432
433 // By comparing the old and new UI adjustments, reinflate the view accordingly.
Ned Burnsf81c4c42019-01-07 14:10:43 -0500434 for (NotificationEntry entry : entries) {
Ned Burnsc5864672019-02-20 12:57:29 -0500435 requireBinder().onNotificationRankingUpdated(
Gus Prevas8ba88a82018-12-18 11:13:44 -0500436 entry,
437 oldImportances.get(entry.key),
438 oldAdjustments.get(entry.key),
Ned Burnsf529ec22019-02-12 19:33:50 -0500439 NotificationUiAdjustment.extractFromNotificationEntry(entry));
Tony Mak628cb932018-06-19 18:30:41 +0100440 }
441
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900442 updateNotifications();
443 }
444
Tony Mak628cb932018-06-19 18:30:41 +0100445 private void updateRankingOfPendingNotifications(
446 @Nullable NotificationListenerService.RankingMap rankingMap) {
447 if (rankingMap == null) {
448 return;
449 }
450 NotificationListenerService.Ranking tmpRanking = new NotificationListenerService.Ranking();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500451 for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
Tony Mak628cb932018-06-19 18:30:41 +0100452 rankingMap.getRanking(pendingNotification.key, tmpRanking);
453 pendingNotification.populateFromRanking(tmpRanking);
454 }
455 }
456
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900457 /**
Ned Burns3d6b3962018-12-07 21:26:00 -0500458 * @return An iterator for all "pending" notifications. Pending notifications are newly-posted
459 * notifications whose views have not yet been inflated. In general, the system pretends like
460 * these don't exist, although there are a couple exceptions.
461 */
Ned Burnsf81c4c42019-01-07 14:10:43 -0500462 public Iterable<NotificationEntry> getPendingNotificationsIterator() {
Ned Burns3d6b3962018-12-07 21:26:00 -0500463 return mPendingNotifications.values();
464 }
Ned Burnsa5dad552019-01-29 17:59:10 -0500465
466 private void extendLifetime(NotificationEntry entry, NotificationLifetimeExtender extender) {
467 NotificationLifetimeExtender activeExtender = mRetainedNotifications.get(entry);
468 if (activeExtender != null && activeExtender != extender) {
469 activeExtender.setShouldManageLifetime(entry, false);
470 }
471 mRetainedNotifications.put(entry, extender);
472 extender.setShouldManageLifetime(entry, true);
473 }
474
475 private void cancelLifetimeExtension(NotificationEntry entry) {
476 NotificationLifetimeExtender activeExtender = mRetainedNotifications.remove(entry);
477 if (activeExtender != null) {
478 activeExtender.setShouldManageLifetime(entry, false);
479 }
480 }
Ned Burnsc5864672019-02-20 12:57:29 -0500481
482 private NotificationRowBinder requireBinder() {
483 if (mNotificationRowBinder == null) {
484 throw new RuntimeException("You must initialize NotificationEntryManager by calling"
485 + "setRowBinder() before using.");
486 }
487 return mNotificationRowBinder;
488 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900489}