NotificationData.java revision f079fc52f3c8e07cc2b5cc07a4518e0638c64b69
1/* 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 17package com.android.systemui.statusbar; 18 19import android.app.Notification; 20import android.service.notification.NotificationListenerService.Ranking; 21import android.service.notification.NotificationListenerService.RankingMap; 22import android.service.notification.StatusBarNotification; 23import android.util.ArrayMap; 24import android.util.ArraySet; 25import android.view.View; 26 27import java.io.PrintWriter; 28import java.util.ArrayList; 29import java.util.Collections; 30import java.util.Comparator; 31 32/** 33 * The list of currently displaying notifications. 34 */ 35public class NotificationData { 36 37 private final Environment mEnvironment; 38 39 public static final class Entry { 40 public String key; 41 public StatusBarNotification notification; 42 public StatusBarIconView icon; 43 public ExpandableNotificationRow row; // the outer expanded view 44 public View expanded; // the inflated RemoteViews 45 public View expandedPublic; // for insecure lockscreens 46 public View expandedBig; 47 private boolean interruption; 48 public boolean autoRedacted; // whether the redacted notification was generated by us 49 public boolean legacy; // whether the notification has a legacy, dark background 50 51 public Entry(StatusBarNotification n, StatusBarIconView ic) { 52 this.key = n.getKey(); 53 this.notification = n; 54 this.icon = ic; 55 } 56 public void setBigContentView(View bigContentView) { 57 this.expandedBig = bigContentView; 58 row.setExpandable(bigContentView != null); 59 } 60 public View getBigContentView() { 61 return expandedBig; 62 } 63 public View getPublicContentView() { return expandedPublic; } 64 65 public void setInterruption() { 66 interruption = true; 67 } 68 69 public boolean hasInterrupted() { 70 return interruption; 71 } 72 73 /** 74 * Resets the notification entry to be re-used. 75 */ 76 public void reset() { 77 // NOTE: Icon needs to be preserved for now. 78 // We should fix this at some point. 79 expanded = null; 80 expandedPublic = null; 81 expandedBig = null; 82 autoRedacted = false; 83 legacy = false; 84 if (row != null) { 85 row.reset(); 86 } 87 } 88 } 89 90 private final ArrayMap<String, Entry> mEntries = new ArrayMap<>(); 91 private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>(); 92 93 private RankingMap mRankingMap; 94 private final Ranking mTmpRanking = new Ranking(); 95 private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() { 96 private final Ranking mRankingA = new Ranking(); 97 private final Ranking mRankingB = new Ranking(); 98 99 @Override 100 public int compare(Entry a, Entry b) { 101 if (mRankingMap != null) { 102 mRankingMap.getRanking(a.key, mRankingA); 103 mRankingMap.getRanking(b.key, mRankingB); 104 return mRankingA.getRank() - mRankingB.getRank(); 105 } 106 107 final StatusBarNotification na = a.notification; 108 final StatusBarNotification nb = b.notification; 109 int d = nb.getScore() - na.getScore(); 110 if (a.interruption != b.interruption) { 111 return a.interruption ? -1 : 1; 112 } else if (d != 0) { 113 return d; 114 } else { 115 return (int) (nb.getNotification().when - na.getNotification().when); 116 } 117 } 118 }; 119 120 public NotificationData(Environment environment) { 121 mEnvironment = environment; 122 } 123 124 /** 125 * Returns the sorted list of active notifications (depending on {@link Environment} 126 * 127 * <p> 128 * This call doesn't update the list of active notifications. Call {@link #filterAndSort()} 129 * when the environment changes. 130 * <p> 131 * Don't hold on to or modify the returned list. 132 */ 133 public ArrayList<Entry> getActiveNotifications() { 134 return mSortedAndFiltered; 135 } 136 137 public Entry get(String key) { 138 return mEntries.get(key); 139 } 140 141 public void add(Entry entry, RankingMap ranking) { 142 mEntries.put(entry.notification.getKey(), entry); 143 updateRankingAndSort(ranking); 144 } 145 146 public Entry remove(String key, RankingMap ranking) { 147 Entry removed = mEntries.remove(key); 148 if (removed == null) return null; 149 updateRankingAndSort(ranking); 150 return removed; 151 } 152 153 public void updateRanking(RankingMap ranking) { 154 updateRankingAndSort(ranking); 155 } 156 157 public boolean isAmbient(String key) { 158 if (mRankingMap != null) { 159 mRankingMap.getRanking(key, mTmpRanking); 160 return mTmpRanking.isAmbient(); 161 } 162 return false; 163 } 164 165 private void updateRankingAndSort(RankingMap ranking) { 166 if (ranking != null) { 167 mRankingMap = ranking; 168 } 169 filterAndSort(); 170 } 171 172 // TODO: This should not be public. Instead the Environment should notify this class when 173 // anything changed, and this class should call back the UI so it updates itself. 174 public void filterAndSort() { 175 mSortedAndFiltered.clear(); 176 177 ArraySet<String> groupsWithSummaries = null; 178 final int N = mEntries.size(); 179 for (int i = 0; i < N; i++) { 180 Entry entry = mEntries.valueAt(i); 181 StatusBarNotification sbn = entry.notification; 182 183 if (shouldFilterOut(sbn)) { 184 continue; 185 } 186 187 if (sbn.getNotification().isGroupSummary()) { 188 if (groupsWithSummaries == null) { 189 groupsWithSummaries = new ArraySet<>(); 190 } 191 groupsWithSummaries.add(sbn.getGroupKey()); 192 } 193 mSortedAndFiltered.add(entry); 194 } 195 196 // Second pass: Filter out group children with summary. 197 if (groupsWithSummaries != null) { 198 final int M = mSortedAndFiltered.size(); 199 for (int i = M - 1; i >= 0; i--) { 200 Entry ent = mSortedAndFiltered.get(i); 201 StatusBarNotification sbn = ent.notification; 202 if (sbn.getNotification().isGroupChild() && 203 groupsWithSummaries.contains(sbn.getGroupKey())) { 204 mSortedAndFiltered.remove(i); 205 } 206 } 207 } 208 209 Collections.sort(mSortedAndFiltered, mRankingComparator); 210 } 211 212 private boolean shouldFilterOut(StatusBarNotification sbn) { 213 if (!(mEnvironment.isDeviceProvisioned() || 214 showNotificationEvenIfUnprovisioned(sbn))) { 215 return true; 216 } 217 218 if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) { 219 return true; 220 } 221 222 if (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET && 223 mEnvironment.shouldHideSensitiveContents(sbn.getUserId())) { 224 return true; 225 } 226 return false; 227 } 228 229 /** 230 * Return whether there are any clearable notifications (that aren't errors). 231 */ 232 public boolean hasActiveClearableNotifications() { 233 for (Entry e : mSortedAndFiltered) { 234 if (e.expanded != null) { // the view successfully inflated 235 if (e.notification.isClearable()) { 236 return true; 237 } 238 } 239 } 240 return false; 241 } 242 243 // Q: What kinds of notifications should show during setup? 244 // A: Almost none! Only things coming from the system (package is "android") that also 245 // have special "kind" tags marking them as relevant for setup (see below). 246 public static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) { 247 return "android".equals(sbn.getPackageName()) 248 && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP); 249 } 250 251 public void dump(PrintWriter pw, String indent) { 252 int N = mSortedAndFiltered.size(); 253 pw.print(indent); 254 pw.println("active notifications: " + N); 255 for (int i = 0; i < N; i++) { 256 NotificationData.Entry e = mSortedAndFiltered.get(i); 257 dumpEntry(pw, indent, i, e); 258 } 259 260 int M = mEntries.size(); 261 pw.print(indent); 262 pw.println("inactive notifications: " + M); 263 for (int i = 0; i < M; i++) { 264 Entry entry = mEntries.valueAt(i); 265 if (!mSortedAndFiltered.contains(entry)) { 266 dumpEntry(pw, indent, i, entry); 267 } 268 } 269 } 270 271 private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) { 272 pw.print(indent); 273 pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); 274 StatusBarNotification n = e.notification; 275 pw.print(indent); 276 pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" + n 277 .getScore()); 278 pw.print(indent); 279 pw.println(" notification=" + n.getNotification()); 280 pw.print(indent); 281 pw.println(" tickerText=\"" + n.getNotification().tickerText + "\""); 282 } 283 284 /** 285 * Provides access to keyguard state and user settings dependent data. 286 */ 287 public interface Environment { 288 public boolean shouldHideSensitiveContents(int userId); 289 public boolean isDeviceProvisioned(); 290 public boolean isNotificationForCurrentProfiles(StatusBarNotification sbn); 291 } 292} 293