blob: 912f414551b121c645a3c38b6bad670d229c89fd [file] [log] [blame]
Joe Onorato503007d2010-04-16 09:20:55 -07001/*
2 * Copyright (C) 2008 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
Joe Onorato79de0c52010-05-26 17:03:26 -040017package com.android.systemui.statusbar;
Joe Onorato503007d2010-04-16 09:20:55 -070018
Christoph Studer37fe6932014-05-26 13:10:30 +020019import android.app.Notification;
Chris Wren3ad4e3a2014-09-02 17:23:51 -040020import android.service.notification.NotificationListenerService;
Christoph Studer37fe6932014-05-26 13:10:30 +020021import android.service.notification.NotificationListenerService.Ranking;
Christoph Studerd0694b62014-06-04 16:36:01 +020022import android.service.notification.NotificationListenerService.RankingMap;
John Spurlockde84f0e2013-06-12 12:41:00 -040023import android.service.notification.StatusBarNotification;
Christoph Studerc8db24b2014-07-25 17:50:30 +020024import android.util.ArrayMap;
Joe Onoratoe345fff2010-05-23 15:18:27 -040025import android.view.View;
Joe Onorato503007d2010-04-16 09:20:55 -070026
Selim Cinek25fd4e2b2015-02-20 17:46:07 +010027import com.android.systemui.statusbar.phone.NotificationGroupManager;
28
Christoph Studerc8db24b2014-07-25 17:50:30 +020029import java.io.PrintWriter;
Joe Onoratoe345fff2010-05-23 15:18:27 -040030import java.util.ArrayList;
Christoph Studer37fe6932014-05-26 13:10:30 +020031import java.util.Collections;
John Spurlockde84f0e2013-06-12 12:41:00 -040032import java.util.Comparator;
Joe Onoratoe345fff2010-05-23 15:18:27 -040033
34/**
35 * The list of currently displaying notifications.
36 */
Joe Onorato503007d2010-04-16 09:20:55 -070037public class NotificationData {
Christoph Studerc8db24b2014-07-25 17:50:30 +020038
39 private final Environment mEnvironment;
40
Joe Onoratoe345fff2010-05-23 15:18:27 -040041 public static final class Entry {
Christoph Studer71f18fd2014-05-20 17:02:04 +020042 public String key;
Joe Onoratoe345fff2010-05-23 15:18:27 -040043 public StatusBarNotification notification;
Joe Onorato66b4c5b2010-05-23 15:39:40 -040044 public StatusBarIconView icon;
Chris Wren51c75102013-07-16 20:49:17 -040045 public ExpandableNotificationRow row; // the outer expanded view
Joe Onorato9c1d8232010-05-24 20:02:53 -040046 public View expanded; // the inflated RemoteViews
Dan Sandlera5e0f412014-01-23 15:11:54 -050047 public View expandedPublic; // for insecure lockscreens
Dan Sandler842dd772014-05-15 09:36:47 -040048 public View expandedBig;
Chris Wrenf0048ce2013-08-07 16:43:43 -040049 private boolean interruption;
Jorim Jaggi36b15232014-06-10 17:24:20 +020050 public boolean autoRedacted; // whether the redacted notification was generated by us
51 public boolean legacy; // whether the notification has a legacy, dark background
Jorim Jaggia1eeade2014-09-08 22:34:39 +020052 public int targetSdk;
Jorim Jaggi36b15232014-06-10 17:24:20 +020053
Christoph Studer71f18fd2014-05-20 17:02:04 +020054 public Entry(StatusBarNotification n, StatusBarIconView ic) {
55 this.key = n.getKey();
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040056 this.notification = n;
57 this.icon = ic;
58 }
Chris Wren574a55e2013-07-15 18:48:37 -040059 public void setBigContentView(View bigContentView) {
60 this.expandedBig = bigContentView;
Chris Wren51c75102013-07-16 20:49:17 -040061 row.setExpandable(bigContentView != null);
Chris Wren8fd12652012-05-09 21:25:57 -040062 }
Chris Wren574a55e2013-07-15 18:48:37 -040063 public View getBigContentView() {
64 return expandedBig;
Chris Wren8fd12652012-05-09 21:25:57 -040065 }
Dan Sandlera5e0f412014-01-23 15:11:54 -050066 public View getPublicContentView() { return expandedPublic; }
Chris Wrenf0048ce2013-08-07 16:43:43 -040067
68 public void setInterruption() {
69 interruption = true;
70 }
Chris Wrend04f6ce2014-06-11 17:37:28 -040071
72 public boolean hasInterrupted() {
73 return interruption;
74 }
Christoph Studer22f2ee52014-07-29 22:57:21 +020075
76 /**
77 * Resets the notification entry to be re-used.
78 */
79 public void reset() {
80 // NOTE: Icon needs to be preserved for now.
81 // We should fix this at some point.
82 expanded = null;
83 expandedPublic = null;
84 expandedBig = null;
85 autoRedacted = false;
86 legacy = false;
87 if (row != null) {
88 row.reset();
89 }
90 }
Joe Onoratoe345fff2010-05-23 15:18:27 -040091 }
Christoph Studer71f18fd2014-05-20 17:02:04 +020092
Christoph Studerc8db24b2014-07-25 17:50:30 +020093 private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
94 private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
Selim Cinek25fd4e2b2015-02-20 17:46:07 +010095
96 private NotificationGroupManager mGroupManager;
Christoph Studerc8db24b2014-07-25 17:50:30 +020097
Christoph Studer1d599da2014-06-12 15:25:59 +020098 private RankingMap mRankingMap;
99 private final Ranking mTmpRanking = new Ranking();
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100100
Christoph Studer37fe6932014-05-26 13:10:30 +0200101 private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
Christoph Studer1d599da2014-06-12 15:25:59 +0200102 private final Ranking mRankingA = new Ranking();
103 private final Ranking mRankingB = new Ranking();
104
Christoph Studer37fe6932014-05-26 13:10:30 +0200105 @Override
Daniel Sandler379020a2010-07-29 16:20:06 -0400106 public int compare(Entry a, Entry b) {
Christoph Studer2e731b52014-08-22 16:01:51 +0200107 // Upsort current media notification.
Christoph Studer14921162014-09-03 12:51:26 +0200108 String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();
Christoph Studer2e731b52014-08-22 16:01:51 +0200109 boolean aMedia = a.key.equals(mediaNotification);
110 boolean bMedia = b.key.equals(mediaNotification);
111 if (aMedia != bMedia) {
112 return aMedia ? -1 : 1;
113 }
114
Christoph Studer14921162014-09-03 12:51:26 +0200115 final StatusBarNotification na = a.notification;
116 final StatusBarNotification nb = b.notification;
117
118 // Upsort PRIORITY_MAX system notifications
119 boolean aSystemMax = na.getNotification().priority >= Notification.PRIORITY_MAX &&
120 isSystemNotification(na);
121 boolean bSystemMax = nb.getNotification().priority >= Notification.PRIORITY_MAX &&
122 isSystemNotification(nb);
123 if (aSystemMax != bSystemMax) {
124 return aSystemMax ? -1 : 1;
125 }
126
Christoph Studer2e731b52014-08-22 16:01:51 +0200127 // RankingMap as received from NoMan.
Christoph Studer1d599da2014-06-12 15:25:59 +0200128 if (mRankingMap != null) {
129 mRankingMap.getRanking(a.key, mRankingA);
130 mRankingMap.getRanking(b.key, mRankingB);
131 return mRankingA.getRank() - mRankingB.getRank();
Christoph Studer37fe6932014-05-26 13:10:30 +0200132 }
133
Christoph Studer37fe6932014-05-26 13:10:30 +0200134 int d = nb.getScore() - na.getScore();
John Spurlock362fafe2014-02-24 16:56:42 -0500135 if (a.interruption != b.interruption) {
Christoph Studer37fe6932014-05-26 13:10:30 +0200136 return a.interruption ? -1 : 1;
John Spurlock362fafe2014-02-24 16:56:42 -0500137 } else if (d != 0) {
Chris Wrenf0048ce2013-08-07 16:43:43 -0400138 return d;
139 } else {
Christoph Studer37fe6932014-05-26 13:10:30 +0200140 return (int) (nb.getNotification().when - na.getNotification().when);
Chris Wrenf0048ce2013-08-07 16:43:43 -0400141 }
Daniel Sandler379020a2010-07-29 16:20:06 -0400142 }
143 };
Joe Onorato503007d2010-04-16 09:20:55 -0700144
Christoph Studerc8db24b2014-07-25 17:50:30 +0200145 public NotificationData(Environment environment) {
146 mEnvironment = environment;
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100147 mGroupManager = environment.getGroupManager();
Joe Onoratoe345fff2010-05-23 15:18:27 -0400148 }
Joe Onorato503007d2010-04-16 09:20:55 -0700149
Christoph Studerc8db24b2014-07-25 17:50:30 +0200150 /**
151 * Returns the sorted list of active notifications (depending on {@link Environment}
152 *
153 * <p>
154 * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
155 * when the environment changes.
156 * <p>
157 * Don't hold on to or modify the returned list.
158 */
159 public ArrayList<Entry> getActiveNotifications() {
160 return mSortedAndFiltered;
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400161 }
162
Christoph Studerc8db24b2014-07-25 17:50:30 +0200163 public Entry get(String key) {
164 return mEntries.get(key);
Joe Onorato0e26dff2010-05-24 16:17:02 -0400165 }
166
Christoph Studerd0694b62014-06-04 16:36:01 +0200167 public void add(Entry entry, RankingMap ranking) {
Christoph Studerc8db24b2014-07-25 17:50:30 +0200168 mEntries.put(entry.notification.getKey(), entry);
Christoph Studer37fe6932014-05-26 13:10:30 +0200169 updateRankingAndSort(ranking);
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100170 mGroupManager.onEntryAdded(entry);
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400171 }
172
Christoph Studerd0694b62014-06-04 16:36:01 +0200173 public Entry remove(String key, RankingMap ranking) {
Christoph Studerc8db24b2014-07-25 17:50:30 +0200174 Entry removed = mEntries.remove(key);
175 if (removed == null) return null;
Christoph Studer37fe6932014-05-26 13:10:30 +0200176 updateRankingAndSort(ranking);
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100177 mGroupManager.onEntryRemoved(removed);
Christoph Studerc8db24b2014-07-25 17:50:30 +0200178 return removed;
Joe Onorato503007d2010-04-16 09:20:55 -0700179 }
Joe Onorato20da8f82010-05-24 16:39:29 -0400180
Christoph Studerd0694b62014-06-04 16:36:01 +0200181 public void updateRanking(RankingMap ranking) {
Christoph Studer37fe6932014-05-26 13:10:30 +0200182 updateRankingAndSort(ranking);
183 }
184
185 public boolean isAmbient(String key) {
John Spurlockf079fc52014-08-09 11:10:03 -0400186 if (mRankingMap != null) {
187 mRankingMap.getRanking(key, mTmpRanking);
188 return mTmpRanking.isAmbient();
189 }
190 return false;
Christoph Studer37fe6932014-05-26 13:10:30 +0200191 }
192
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400193 public int getVisibilityOverride(String key) {
194 if (mRankingMap != null) {
195 mRankingMap.getRanking(key, mTmpRanking);
196 return mTmpRanking.getVisibilityOverride();
197 }
198 return NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
199 }
200
Christoph Studerd0694b62014-06-04 16:36:01 +0200201 private void updateRankingAndSort(RankingMap ranking) {
Christoph Studer37fe6932014-05-26 13:10:30 +0200202 if (ranking != null) {
Christoph Studer1d599da2014-06-12 15:25:59 +0200203 mRankingMap = ranking;
Christoph Studer37fe6932014-05-26 13:10:30 +0200204 }
Christoph Studerc8db24b2014-07-25 17:50:30 +0200205 filterAndSort();
Christoph Studer37fe6932014-05-26 13:10:30 +0200206 }
207
Christoph Studerc8db24b2014-07-25 17:50:30 +0200208 // TODO: This should not be public. Instead the Environment should notify this class when
209 // anything changed, and this class should call back the UI so it updates itself.
210 public void filterAndSort() {
211 mSortedAndFiltered.clear();
212
Christoph Studerc8db24b2014-07-25 17:50:30 +0200213 final int N = mEntries.size();
214 for (int i = 0; i < N; i++) {
215 Entry entry = mEntries.valueAt(i);
216 StatusBarNotification sbn = entry.notification;
217
218 if (shouldFilterOut(sbn)) {
219 continue;
Joe Onorato9c1d8232010-05-24 20:02:53 -0400220 }
Christoph Studerc8db24b2014-07-25 17:50:30 +0200221
Christoph Studerc8db24b2014-07-25 17:50:30 +0200222 mSortedAndFiltered.add(entry);
223 }
224
Christoph Studerc8db24b2014-07-25 17:50:30 +0200225 Collections.sort(mSortedAndFiltered, mRankingComparator);
226 }
227
Christoph Studer2f9dbba2014-09-03 17:35:54 +0200228 boolean shouldFilterOut(StatusBarNotification sbn) {
Christoph Studerc8db24b2014-07-25 17:50:30 +0200229 if (!(mEnvironment.isDeviceProvisioned() ||
230 showNotificationEvenIfUnprovisioned(sbn))) {
231 return true;
232 }
233
234 if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) {
235 return true;
236 }
237
238 if (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET &&
239 mEnvironment.shouldHideSensitiveContents(sbn.getUserId())) {
240 return true;
Joe Onorato9c1d8232010-05-24 20:02:53 -0400241 }
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100242
Selim Cinekb5605e52015-02-20 18:21:41 +0100243 if (!BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
244 && mGroupManager.isChildInGroupWithSummary(sbn)) {
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100245 return true;
246 }
Joe Onorato9c1d8232010-05-24 20:02:53 -0400247 return false;
Joe Onorato20da8f82010-05-24 16:39:29 -0400248 }
249
250 /**
Christoph Studerc8db24b2014-07-25 17:50:30 +0200251 * Return whether there are any clearable notifications (that aren't errors).
Joe Onorato20da8f82010-05-24 16:39:29 -0400252 */
Christoph Studerc8db24b2014-07-25 17:50:30 +0200253 public boolean hasActiveClearableNotifications() {
254 for (Entry e : mSortedAndFiltered) {
Daniel Sandler379020a2010-07-29 16:20:06 -0400255 if (e.expanded != null) { // the view successfully inflated
Joe Onorato5dd11692010-09-27 15:34:04 -0700256 if (e.notification.isClearable()) {
Joe Onorato9c1d8232010-05-24 20:02:53 -0400257 return true;
258 }
Joe Onorato20da8f82010-05-24 16:39:29 -0400259 }
260 }
261 return false;
262 }
Christoph Studerc8db24b2014-07-25 17:50:30 +0200263
264 // Q: What kinds of notifications should show during setup?
265 // A: Almost none! Only things coming from the system (package is "android") that also
266 // have special "kind" tags marking them as relevant for setup (see below).
267 public static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
268 return "android".equals(sbn.getPackageName())
269 && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
270 }
271
272 public void dump(PrintWriter pw, String indent) {
273 int N = mSortedAndFiltered.size();
274 pw.print(indent);
275 pw.println("active notifications: " + N);
Christoph Studer11840cd2014-08-21 16:41:45 +0200276 int active;
277 for (active = 0; active < N; active++) {
278 NotificationData.Entry e = mSortedAndFiltered.get(active);
279 dumpEntry(pw, indent, active, e);
Christoph Studerc8db24b2014-07-25 17:50:30 +0200280 }
281
282 int M = mEntries.size();
283 pw.print(indent);
Christoph Studer11840cd2014-08-21 16:41:45 +0200284 pw.println("inactive notifications: " + (M - active));
285 int inactiveCount = 0;
Christoph Studerc8db24b2014-07-25 17:50:30 +0200286 for (int i = 0; i < M; i++) {
287 Entry entry = mEntries.valueAt(i);
288 if (!mSortedAndFiltered.contains(entry)) {
Christoph Studer11840cd2014-08-21 16:41:45 +0200289 dumpEntry(pw, indent, inactiveCount, entry);
290 inactiveCount++;
Christoph Studerc8db24b2014-07-25 17:50:30 +0200291 }
292 }
293 }
294
295 private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) {
296 pw.print(indent);
297 pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon);
298 StatusBarNotification n = e.notification;
299 pw.print(indent);
Christoph Studer11840cd2014-08-21 16:41:45 +0200300 pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" +
301 n.getScore());
Christoph Studerc8db24b2014-07-25 17:50:30 +0200302 pw.print(indent);
303 pw.println(" notification=" + n.getNotification());
304 pw.print(indent);
305 pw.println(" tickerText=\"" + n.getNotification().tickerText + "\"");
306 }
307
Christoph Studer14921162014-09-03 12:51:26 +0200308 private static boolean isSystemNotification(StatusBarNotification sbn) {
309 String sbnPackage = sbn.getPackageName();
310 return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
311 }
312
Christoph Studerc8db24b2014-07-25 17:50:30 +0200313 /**
314 * Provides access to keyguard state and user settings dependent data.
315 */
316 public interface Environment {
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400317 public boolean shouldHideSensitiveContents(int userid);
Christoph Studerc8db24b2014-07-25 17:50:30 +0200318 public boolean isDeviceProvisioned();
319 public boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
Christoph Studer2e731b52014-08-22 16:01:51 +0200320 public String getCurrentMediaNotificationKey();
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100321 public NotificationGroupManager getGroupManager();
Christoph Studerc8db24b2014-07-25 17:50:30 +0200322 }
Joe Onorato503007d2010-04-16 09:20:55 -0700323}