blob: 9018531e3108fdfcc5e16a919cbfd87a313e3425 [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;
Gustav Senntonf892fe92019-01-22 15:31:42 +000038import com.android.systemui.statusbar.notification.logging.NotificationLogger;
Rohan Shah20790b82018-07-02 17:21:04 -070039import com.android.systemui.statusbar.notification.row.NotificationInflater;
Kevind4660b22018-09-27 10:57:35 -070040import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
Rohan Shah20790b82018-07-02 17:21:04 -070041import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090042import com.android.systemui.statusbar.policy.HeadsUpManager;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090043import com.android.systemui.util.leak.LeakDetector;
44
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
47import java.util.ArrayList;
48import java.util.HashMap;
49import java.util.List;
Ned Burnsa5dad552019-01-29 17:59:10 -050050import java.util.Map;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090051
52/**
53 * NotificationEntryManager is responsible for the adding, removing, and updating of notifications.
54 * It also handles tasks such as their inflation and their interaction with other
55 * Notification.*Manager objects.
56 */
Gus Prevas8ba88a82018-12-18 11:13:44 -050057public class NotificationEntryManager implements
58 Dumpable,
59 NotificationInflater.InflationCallback,
60 NotificationUpdateHandler,
Ned Burns01e38212019-01-03 16:32:52 -050061 VisualStabilityManager.Callback {
Julia Reynoldsfc640012018-02-21 12:25:27 -050062 private static final String TAG = "NotificationEntryMgr";
Ned Burnsd35708c2019-01-09 15:38:03 -050063 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Eliot Courtney6c313d32017-12-14 19:57:51 +090064
Ned Burnsd35708c2019-01-09 15:38:03 -050065 @VisibleForTesting
Ned Burnsf81c4c42019-01-07 14:10:43 -050066 protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090067
Ned Burnsa5dad552019-01-29 17:59:10 -050068 private final Map<NotificationEntry, NotificationLifetimeExtender> mRetainedNotifications =
69 new ArrayMap<>();
70
Jason Monk297c04e2018-08-23 17:16:59 -040071 // Lazily retrieved dependencies
72 private NotificationRemoteInputManager mRemoteInputManager;
Gus Prevas8ba88a82018-12-18 11:13:44 -050073 private NotificationRowBinder mNotificationRowBinder;
Eliot Courtney6c313d32017-12-14 19:57:51 +090074
Jason Monk297c04e2018-08-23 17:16:59 -040075 private NotificationPresenter mPresenter;
Ned Burnsdc10c952018-11-21 14:36:21 -050076 private NotificationListenerService.RankingMap mLatestRankingMap;
Ned Burnsd35708c2019-01-09 15:38:03 -050077 @VisibleForTesting
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090078 protected NotificationData mNotificationData;
Ned Burnsf36c6252019-01-09 19:12:33 -050079
Ned Burnsdc10c952018-11-21 14:36:21 -050080 @VisibleForTesting
81 final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
Kevina5ff1fa2018-08-21 16:35:48 -070082 = new ArrayList<>();
Gus Prevasd65c2db2018-12-18 17:13:38 -050083 private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090084
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090085 @Override
86 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
87 pw.println("NotificationEntryManager state:");
88 pw.print(" mPendingNotifications=");
89 if (mPendingNotifications.size() == 0) {
90 pw.println("null");
91 } else {
Ned Burnsf81c4c42019-01-07 14:10:43 -050092 for (NotificationEntry entry : mPendingNotifications.values()) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090093 pw.println(entry.notification);
94 }
95 }
Ned Burnsa5dad552019-01-29 17:59:10 -050096 pw.println(" Lifetime-extended notifications:");
97 if (mRetainedNotifications.isEmpty()) {
98 pw.println(" None");
99 } else {
100 for (Map.Entry<NotificationEntry, NotificationLifetimeExtender> entry
101 : mRetainedNotifications.entrySet()) {
102 pw.println(" " + entry.getKey().notification + " retained by "
103 + entry.getValue().getClass().getName());
104 }
105 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900106 }
107
Eliot Courtney6c313d32017-12-14 19:57:51 +0900108 public NotificationEntryManager(Context context) {
Jason Monk297c04e2018-08-23 17:16:59 -0400109 mNotificationData = new NotificationData();
Jason Monk297c04e2018-08-23 17:16:59 -0400110 }
111
Gus Prevasd65c2db2018-12-18 17:13:38 -0500112 /** Adds a {@link NotificationEntryListener}. */
113 public void addNotificationEntryListener(NotificationEntryListener listener) {
114 mNotificationEntryListeners.add(listener);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900115 }
116
Jason Monk297c04e2018-08-23 17:16:59 -0400117 /**
118 * Our dependencies can have cyclic references, so some need to be lazy
119 */
120 private NotificationRemoteInputManager getRemoteInputManager() {
121 if (mRemoteInputManager == null) {
122 mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
123 }
124 return mRemoteInputManager;
125 }
126
Gus Prevas8ba88a82018-12-18 11:13:44 -0500127 private NotificationRowBinder getRowBinder() {
128 if (mNotificationRowBinder == null) {
129 mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
130 }
131 return mNotificationRowBinder;
132 }
133
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500134 // TODO: Remove this once we can always use a mocked row binder in our tests
135 @VisibleForTesting
136 void setRowBinder(NotificationRowBinder notificationRowBinder) {
137 mNotificationRowBinder = notificationRowBinder;
138 }
139
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900140 public void setUpWithPresenter(NotificationPresenter presenter,
Gus Prevas8621bd22018-12-20 15:04:25 -0500141 NotificationListContainer listContainer,
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900142 HeadsUpManager headsUpManager) {
143 mPresenter = presenter;
Gus Prevas92586462019-01-04 16:06:12 -0500144 mNotificationData.setHeadsUpManager(headsUpManager);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900145 }
146
Gus Prevas5c84abb2018-12-28 16:41:49 -0500147 /** Adds multiple {@link NotificationLifetimeExtender}s. */
148 public void addNotificationLifetimeExtenders(List<NotificationLifetimeExtender> extenders) {
149 for (NotificationLifetimeExtender extender : extenders) {
150 addNotificationLifetimeExtender(extender);
151 }
152 }
153
154 /** Adds a {@link NotificationLifetimeExtender}. */
155 public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
156 mNotificationLifetimeExtenders.add(extender);
157 extender.setCallback(key -> removeNotification(key, mLatestRankingMap));
158 }
159
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900160 public NotificationData getNotificationData() {
161 return mNotificationData;
162 }
163
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900164 @Override
165 public void onReorderingAllowed() {
166 updateNotifications();
167 }
168
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900169 public void performRemoveNotification(StatusBarNotification n) {
Dieter Hsud39f0d52018-04-14 02:08:30 +0800170 final int rank = mNotificationData.getRank(n.getKey());
171 final int count = mNotificationData.getActiveNotifications().size();
Gustav Senntonf892fe92019-01-22 15:31:42 +0000172 NotificationVisibility.NotificationLocation location =
173 NotificationLogger.getNotificationLocation(getNotificationData().get(n.getKey()));
Dieter Hsud39f0d52018-04-14 02:08:30 +0800174 final NotificationVisibility nv = NotificationVisibility.obtain(n.getKey(), rank, count,
Gustav Senntonf892fe92019-01-22 15:31:42 +0000175 true, location);
Gus Prevasca1b6f72018-12-28 10:53:11 -0500176 removeNotificationInternal(
177 n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900178 }
179
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900180 private void abortExistingInflation(String key) {
181 if (mPendingNotifications.containsKey(key)) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500182 NotificationEntry entry = mPendingNotifications.get(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900183 entry.abortTask();
184 mPendingNotifications.remove(key);
185 }
Ned Burnsf81c4c42019-01-07 14:10:43 -0500186 NotificationEntry addedEntry = mNotificationData.get(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900187 if (addedEntry != null) {
188 addedEntry.abortTask();
189 }
190 }
191
Ned Burnsdc10c952018-11-21 14:36:21 -0500192 /**
193 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
194 * about the failure.
195 *
196 * WARNING: this will call back into us. Don't hold any locks.
197 */
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900198 @Override
Ned Burnsdc10c952018-11-21 14:36:21 -0500199 public void handleInflationException(StatusBarNotification n, Exception e) {
Gus Prevasdca2be52018-12-21 11:25:10 -0500200 removeNotificationInternal(
Gus Prevasca1b6f72018-12-28 10:53:11 -0500201 n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */);
202 for (NotificationEntryListener listener : mNotificationEntryListeners) {
203 listener.onInflationError(n, e);
Ned Burnsdc10c952018-11-21 14:36:21 -0500204 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900205 }
206
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900207 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500208 public void onAsyncInflationFinished(NotificationEntry entry,
Kevind4660b22018-09-27 10:57:35 -0700209 @InflationFlag int inflatedFlags) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900210 mPendingNotifications.remove(entry.key);
211 // If there was an async task started after the removal, we don't want to add it back to
212 // the list, otherwise we might get leaks.
Evan Laird94492852018-10-25 13:43:01 -0400213 if (!entry.isRowRemoved()) {
Kevin01a53cb2018-11-09 18:19:54 -0800214 boolean isNew = mNotificationData.get(entry.key) == null;
215 if (isNew) {
Gus Prevasb43dc652018-12-20 13:11:45 -0500216 for (NotificationEntryListener listener : mNotificationEntryListeners) {
217 listener.onEntryInflated(entry, inflatedFlags);
218 }
Ned Burns69760872019-01-03 15:36:38 -0500219 mNotificationData.add(entry);
Ned Burnsf36c6252019-01-09 19:12:33 -0500220 for (NotificationEntryListener listener : mNotificationEntryListeners) {
221 listener.onBeforeNotificationAdded(entry);
222 }
Ned Burns69760872019-01-03 15:36:38 -0500223 updateNotifications();
224 for (NotificationEntryListener listener : mNotificationEntryListeners) {
225 listener.onNotificationAdded(entry);
226 }
Kevin01a53cb2018-11-09 18:19:54 -0800227 } else {
Gus Prevasd65c2db2018-12-18 17:13:38 -0500228 for (NotificationEntryListener listener : mNotificationEntryListeners) {
229 listener.onEntryReinflated(entry);
Ned Burns3d6b3962018-12-07 21:26:00 -0500230 }
Kevin01a53cb2018-11-09 18:19:54 -0800231 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900232 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900233 }
234
235 @Override
236 public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
Gus Prevasdca2be52018-12-21 11:25:10 -0500237 removeNotificationInternal(
Gus Prevasca1b6f72018-12-28 10:53:11 -0500238 key, ranking, null, false /* forceRemove */, false /* removedByUser */);
Kevina5ff1fa2018-08-21 16:35:48 -0700239 }
240
Gus Prevasdca2be52018-12-21 11:25:10 -0500241 private void removeNotificationInternal(
242 String key,
243 @Nullable NotificationListenerService.RankingMap ranking,
Gus Prevasca1b6f72018-12-28 10:53:11 -0500244 @Nullable NotificationVisibility visibility,
Gus Prevasdca2be52018-12-21 11:25:10 -0500245 boolean forceRemove,
246 boolean removedByUser) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500247 final NotificationEntry entry = mNotificationData.get(key);
Ned Burns3d6b3962018-12-07 21:26:00 -0500248
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900249 abortExistingInflation(key);
Gus Prevasf37435a2018-12-20 15:40:01 -0500250
Gus Prevasf37435a2018-12-20 15:40:01 -0500251 boolean lifetimeExtended = false;
252
Gus Prevasd65c2db2018-12-18 17:13:38 -0500253 if (entry != null) {
Gus Prevasf37435a2018-12-20 15:40:01 -0500254 // If a manager needs to keep the notification around for whatever reason, we
255 // keep the notification
256 if (!forceRemove) {
257 for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
258 if (extender.shouldExtendLifetime(entry)) {
259 mLatestRankingMap = ranking;
Ned Burnsa5dad552019-01-29 17:59:10 -0500260 extendLifetime(entry, extender);
Gus Prevasf37435a2018-12-20 15:40:01 -0500261 lifetimeExtended = true;
262 break;
263 }
Kevina5ff1fa2018-08-21 16:35:48 -0700264 }
265 }
Gus Prevasf37435a2018-12-20 15:40:01 -0500266
267 if (!lifetimeExtended) {
268 // At this point, we are guaranteed the notification will be removed
269
270 // Ensure any managers keeping the lifetime extended stop managing the entry
Ned Burnsa5dad552019-01-29 17:59:10 -0500271 cancelLifetimeExtension(entry);
Gus Prevasf37435a2018-12-20 15:40:01 -0500272
Gus Prevasf37435a2018-12-20 15:40:01 -0500273 if (entry.rowExists()) {
274 entry.removeRow();
Gus Prevasf37435a2018-12-20 15:40:01 -0500275 }
276
277 // Let's remove the children if this was a summary
278 handleGroupSummaryRemoved(key);
279
Ned Burns69760872019-01-03 15:36:38 -0500280 mNotificationData.remove(key, ranking);
281 updateNotifications();
282 Dependency.get(LeakDetector.class).trackGarbage(entry);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900283
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500284 for (NotificationEntryListener listener : mNotificationEntryListeners) {
285 listener.onEntryRemoved(entry, visibility, removedByUser);
286 }
287 }
Gus Prevas8621bd22018-12-20 15:04:25 -0500288 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900289 }
290
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900291 /**
292 * Ensures that the group children are cancelled immediately when the group summary is cancelled
293 * instead of waiting for the notification manager to send all cancels. Otherwise this could
294 * lead to flickers.
295 *
296 * This also ensures that the animation looks nice and only consists of a single disappear
297 * animation instead of multiple.
298 * @param key the key of the notification was removed
299 *
300 */
301 private void handleGroupSummaryRemoved(String key) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500302 NotificationEntry entry = mNotificationData.get(key);
Evan Laird94492852018-10-25 13:43:01 -0400303 if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
304 if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900305 // We don't want to remove children for autobundled notifications as they are not
306 // always cancelled. We only remove them if they were dismissed by the user.
307 return;
308 }
Ned Burnsf81c4c42019-01-07 14:10:43 -0500309 List<NotificationEntry> childEntries = entry.getChildren();
Evan Laird94492852018-10-25 13:43:01 -0400310 if (childEntries == null) {
311 return;
312 }
313 for (int i = 0; i < childEntries.size(); i++) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500314 NotificationEntry childEntry = childEntries.get(i);
Evan Laird94492852018-10-25 13:43:01 -0400315 boolean isForeground = (entry.notification.getNotification().flags
Selim Cinek038e2592018-08-16 16:30:45 -0700316 & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Kevina5ff1fa2018-08-21 16:35:48 -0700317 boolean keepForReply =
Jason Monk297c04e2018-08-23 17:16:59 -0400318 getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
319 || getRemoteInputManager().shouldKeepForSmartReplyHistory(childEntry);
Selim Cinek038e2592018-08-16 16:30:45 -0700320 if (isForeground || keepForReply) {
321 // the child is a foreground service notification which we can't remove or it's
322 // a child we're keeping around for reply!
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900323 continue;
324 }
Evan Laird94492852018-10-25 13:43:01 -0400325 entry.setKeepInParent(true);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900326 // we need to set this state earlier as otherwise we might generate some weird
327 // animations
Evan Laird94492852018-10-25 13:43:01 -0400328 entry.removeRow();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900329 }
330 }
331 }
332
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900333 private void addNotificationInternal(StatusBarNotification notification,
Tony Mak628cb932018-06-19 18:30:41 +0100334 NotificationListenerService.RankingMap rankingMap) throws InflationException {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900335 String key = notification.getKey();
Kevina97ea052018-09-11 13:53:18 -0700336 if (DEBUG) {
337 Log.d(TAG, "addNotification key=" + key);
338 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900339
Tony Mak628cb932018-06-19 18:30:41 +0100340 mNotificationData.updateRanking(rankingMap);
341 NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
342 rankingMap.getRanking(key, ranking);
Ned Burns69760872019-01-03 15:36:38 -0500343
Ned Burnsf81c4c42019-01-07 14:10:43 -0500344 NotificationEntry entry = new NotificationEntry(notification, ranking);
Ned Burns69760872019-01-03 15:36:38 -0500345
346 Dependency.get(LeakDetector.class).trackInstance(entry);
347 // Construct the expanded view.
348 getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
349 mNotificationData.get(entry.key) != null);
350
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900351 abortExistingInflation(key);
352
Gus Prevasd65c2db2018-12-18 17:13:38 -0500353 mPendingNotifications.put(key, entry);
354 for (NotificationEntryListener listener : mNotificationEntryListeners) {
355 listener.onPendingEntryAdded(entry);
Ned Burns3d6b3962018-12-07 21:26:00 -0500356 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900357 }
358
359 @Override
360 public void addNotification(StatusBarNotification notification,
361 NotificationListenerService.RankingMap ranking) {
362 try {
363 addNotificationInternal(notification, ranking);
364 } catch (InflationException e) {
365 handleInflationException(notification, e);
366 }
367 }
368
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900369 private void updateNotificationInternal(StatusBarNotification notification,
370 NotificationListenerService.RankingMap ranking) throws InflationException {
371 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
372
373 final String key = notification.getKey();
374 abortExistingInflation(key);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500375 NotificationEntry entry = mNotificationData.get(key);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900376 if (entry == null) {
377 return;
378 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900379
Kevina5ff1fa2018-08-21 16:35:48 -0700380 // Notification is updated so it is essentially re-added and thus alive again. Don't need
Kevine9e938c2018-09-06 13:38:11 -0700381 // to keep its lifetime extended.
Ned Burnsa5dad552019-01-29 17:59:10 -0500382 cancelLifetimeExtension(entry);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900383
Gus Prevas26d91272018-12-21 16:43:28 -0500384 mNotificationData.update(entry, ranking, notification);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900385
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800386 for (NotificationEntryListener listener : mNotificationEntryListeners) {
387 listener.onPreEntryUpdated(entry);
388 }
389
Ned Burnsa13b6f12019-02-11 20:42:58 -0500390 getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
391 mNotificationData.get(entry.key) != null);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900392 updateNotifications();
393
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900394 if (DEBUG) {
395 // Is this for you?
Jason Monk297c04e2018-08-23 17:16:59 -0400396 boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class)
397 .isNotificationForCurrentProfiles(notification);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900398 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
399 }
400
Gus Prevasb43dc652018-12-20 13:11:45 -0500401 for (NotificationEntryListener listener : mNotificationEntryListeners) {
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800402 listener.onPostEntryUpdated(entry);
Gus Prevasb43dc652018-12-20 13:11:45 -0500403 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900404 }
405
406 @Override
407 public void updateNotification(StatusBarNotification notification,
408 NotificationListenerService.RankingMap ranking) {
409 try {
410 updateNotificationInternal(notification, ranking);
411 } catch (InflationException e) {
412 handleInflationException(notification, e);
413 }
414 }
415
416 public void updateNotifications() {
417 mNotificationData.filterAndSort();
Ned Burnsf36c6252019-01-09 19:12:33 -0500418 if (mPresenter != null) {
419 mPresenter.updateNotificationViews();
420 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900421 }
422
Ned Burns6b19b082019-02-12 18:50:47 -0500423 @Override
Tony Mak628cb932018-06-19 18:30:41 +0100424 public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500425 List<NotificationEntry> entries = new ArrayList<>();
Tony Mak628cb932018-06-19 18:30:41 +0100426 entries.addAll(mNotificationData.getActiveNotifications());
427 entries.addAll(mPendingNotifications.values());
428
429 // Has a copy of the current UI adjustments.
430 ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
Gus Prevas33fbc152018-12-04 13:17:32 -0500431 ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500432 for (NotificationEntry entry : entries) {
Tony Mak628cb932018-06-19 18:30:41 +0100433 NotificationUiAdjustment adjustment =
434 NotificationUiAdjustment.extractFromNotificationEntry(entry);
435 oldAdjustments.put(entry.key, adjustment);
Gus Prevas33fbc152018-12-04 13:17:32 -0500436 oldImportances.put(entry.key, entry.importance);
Tony Mak628cb932018-06-19 18:30:41 +0100437 }
438
439 // Populate notification entries from the new rankings.
440 mNotificationData.updateRanking(rankingMap);
441 updateRankingOfPendingNotifications(rankingMap);
442
443 // By comparing the old and new UI adjustments, reinflate the view accordingly.
Ned Burnsf81c4c42019-01-07 14:10:43 -0500444 for (NotificationEntry entry : entries) {
Gus Prevasec9e1f02018-12-18 15:28:12 -0500445 getRowBinder().onNotificationRankingUpdated(
Gus Prevas8ba88a82018-12-18 11:13:44 -0500446 entry,
447 oldImportances.get(entry.key),
448 oldAdjustments.get(entry.key),
449 NotificationUiAdjustment.extractFromNotificationEntry(entry),
450 mNotificationData.get(entry.key) != null);
Tony Mak628cb932018-06-19 18:30:41 +0100451 }
452
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900453 updateNotifications();
454 }
455
Tony Mak628cb932018-06-19 18:30:41 +0100456 private void updateRankingOfPendingNotifications(
457 @Nullable NotificationListenerService.RankingMap rankingMap) {
458 if (rankingMap == null) {
459 return;
460 }
461 NotificationListenerService.Ranking tmpRanking = new NotificationListenerService.Ranking();
Ned Burnsf81c4c42019-01-07 14:10:43 -0500462 for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
Tony Mak628cb932018-06-19 18:30:41 +0100463 rankingMap.getRanking(pendingNotification.key, tmpRanking);
464 pendingNotification.populateFromRanking(tmpRanking);
465 }
466 }
467
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900468 /**
Ned Burns3d6b3962018-12-07 21:26:00 -0500469 * @return An iterator for all "pending" notifications. Pending notifications are newly-posted
470 * notifications whose views have not yet been inflated. In general, the system pretends like
471 * these don't exist, although there are a couple exceptions.
472 */
Ned Burnsf81c4c42019-01-07 14:10:43 -0500473 public Iterable<NotificationEntry> getPendingNotificationsIterator() {
Ned Burns3d6b3962018-12-07 21:26:00 -0500474 return mPendingNotifications.values();
475 }
Ned Burnsa5dad552019-01-29 17:59:10 -0500476
477 private void extendLifetime(NotificationEntry entry, NotificationLifetimeExtender extender) {
478 NotificationLifetimeExtender activeExtender = mRetainedNotifications.get(entry);
479 if (activeExtender != null && activeExtender != extender) {
480 activeExtender.setShouldManageLifetime(entry, false);
481 }
482 mRetainedNotifications.put(entry, extender);
483 extender.setShouldManageLifetime(entry, true);
484 }
485
486 private void cancelLifetimeExtension(NotificationEntry entry) {
487 NotificationLifetimeExtender activeExtender = mRetainedNotifications.remove(entry);
488 if (activeExtender != null) {
489 activeExtender.setShouldManageLifetime(entry, false);
490 }
491 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900492}