Merge "use importance instead of score"
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index a7545f2..d7fcd7d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -863,6 +863,8 @@
private boolean mMatchesInterruptionFilter;
private int mVisibilityOverride;
private int mSuppressedVisualEffects;
+ private int mImportance;
+ private CharSequence mImportanceExplanation;
public Ranking() {}
@@ -928,7 +930,7 @@
* @return the rank of the notification
*/
public int getImportance() {
- return IMPORTANCE_DEFAULT; // TODO implement;
+ return mImportance;
}
/**
@@ -939,18 +941,21 @@
* @return the explanation for the importance, or null if it is the natural importance
*/
public CharSequence getImportanceExplanation() {
- return null; // TODO implement
+ return mImportanceExplanation;
}
private void populate(String key, int rank, boolean isAmbient,
boolean matchesInterruptionFilter, int visibilityOverride,
- int suppressedVisualEffects) {
+ int suppressedVisualEffects, int importance,
+ CharSequence explanation) {
mKey = key;
mRank = rank;
mIsAmbient = isAmbient;
mMatchesInterruptionFilter = matchesInterruptionFilter;
mVisibilityOverride = visibilityOverride;
mSuppressedVisualEffects = suppressedVisualEffects;
+ mImportance = importance;
+ mImportanceExplanation = explanation;
}
/**
@@ -990,6 +995,8 @@
private ArraySet<Object> mIntercepted;
private ArrayMap<String, Integer> mVisibilityOverrides;
private ArrayMap<String, Integer> mSuppressedVisualEffects;
+ private ArrayMap<String, Integer> mImportance;
+ private ArrayMap<String, String> mImportanceExplanation;
private RankingMap(NotificationRankingUpdate rankingUpdate) {
mRankingUpdate = rankingUpdate;
@@ -1015,7 +1022,8 @@
public boolean getRanking(String key, Ranking outRanking) {
int rank = getRank(key);
outRanking.populate(key, rank, isAmbient(key), !isIntercepted(key),
- getVisibilityOverride(key), getSuppressedVisualEffects(key));
+ getVisibilityOverride(key), getSuppressedVisualEffects(key),
+ getImportance(key), getImportanceExplanation(key));
return rank >= 0;
}
@@ -1073,6 +1081,28 @@
return suppressed.intValue();
}
+ private int getImportance(String key) {
+ synchronized (this) {
+ if (mImportance == null) {
+ buildImportanceLocked();
+ }
+ }
+ Integer importance = mImportance.get(key);
+ if (importance == null) {
+ return Ranking.IMPORTANCE_DEFAULT;
+ }
+ return importance.intValue();
+ }
+
+ private String getImportanceExplanation(String key) {
+ synchronized (this) {
+ if (mImportanceExplanation == null) {
+ buildImportanceExplanationLocked();
+ }
+ }
+ return mImportanceExplanation.get(key);
+ }
+
// Locked by 'this'
private void buildRanksLocked() {
String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1107,6 +1137,25 @@
mSuppressedVisualEffects.put(key, suppressedBundle.getInt(key));
}
}
+ // Locked by 'this'
+ private void buildImportanceLocked() {
+ String[] orderedKeys = mRankingUpdate.getOrderedKeys();
+ int[] importance = mRankingUpdate.getImportance();
+ mImportance = new ArrayMap<>(orderedKeys.length);
+ for (int i = 0; i < orderedKeys.length; i++) {
+ String key = orderedKeys[i];
+ mImportance.put(key, importance[i]);
+ }
+ }
+
+ // Locked by 'this'
+ private void buildImportanceExplanationLocked() {
+ Bundle explanationBundle = mRankingUpdate.getImportanceExplanation();
+ mImportanceExplanation = new ArrayMap<>(explanationBundle.size());
+ for (String key: explanationBundle.keySet()) {
+ mImportanceExplanation.put(key, explanationBundle.getString(key));
+ }
+ }
// ----------- Parcelable
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 1282fb1..0d42ffb 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -29,14 +29,19 @@
private final int mFirstAmbientIndex;
private final Bundle mVisibilityOverrides;
private final Bundle mSuppressedVisualEffects;
+ private final int[] mImportance;
+ private final Bundle mImportanceExplanation;
public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
- Bundle visibilityOverrides, int firstAmbientIndex, Bundle suppressedVisualEffects) {
+ Bundle visibilityOverrides, int firstAmbientIndex, Bundle suppressedVisualEffects,
+ int[] importance, Bundle explanation) {
mKeys = keys;
mFirstAmbientIndex = firstAmbientIndex;
mInterceptedKeys = interceptedKeys;
mVisibilityOverrides = visibilityOverrides;
mSuppressedVisualEffects = suppressedVisualEffects;
+ mImportance = importance;
+ mImportanceExplanation = explanation;
}
public NotificationRankingUpdate(Parcel in) {
@@ -45,6 +50,9 @@
mInterceptedKeys = in.readStringArray();
mVisibilityOverrides = in.readBundle();
mSuppressedVisualEffects = in.readBundle();
+ mImportance = new int[mKeys.length];
+ in.readIntArray(mImportance);
+ mImportanceExplanation = in.readBundle();
}
@Override
@@ -59,6 +67,8 @@
out.writeStringArray(mInterceptedKeys);
out.writeBundle(mVisibilityOverrides);
out.writeBundle(mSuppressedVisualEffects);
+ out.writeIntArray(mImportance);
+ out.writeBundle(mImportanceExplanation);
}
public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -91,4 +101,12 @@
public Bundle getSuppressedVisualEffects() {
return mSuppressedVisualEffects;
}
+
+ public int[] getImportance() {
+ return mImportance;
+ }
+
+ public Bundle getImportanceExplanation() {
+ return mImportanceExplanation;
+ }
}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 2cab914..198e43d 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -42,7 +42,6 @@
private final UserHandle user;
private final long postTime;
- private final int score;
private Context mContext; // used for inflation & icon expansion
/** @hide */
@@ -64,7 +63,6 @@
this.tag = tag;
this.uid = uid;
this.initialPid = initialPid;
- this.score = score;
this.notification = notification;
this.user = user;
this.postTime = postTime;
@@ -83,7 +81,6 @@
}
this.uid = in.readInt();
this.initialPid = in.readInt();
- this.score = in.readInt();
this.notification = new Notification(in);
this.user = UserHandle.readFromParcel(in);
this.postTime = in.readLong();
@@ -120,7 +117,6 @@
}
out.writeInt(this.uid);
out.writeInt(this.initialPid);
- out.writeInt(this.score);
this.notification.writeToParcel(out, flags);
user.writeToParcel(out, flags);
@@ -153,14 +149,14 @@
this.notification.cloneInto(no, false); // light copy
return new StatusBarNotification(this.pkg, this.opPkg,
this.id, this.tag, this.uid, this.initialPid,
- this.score, no, this.user, this.postTime);
+ 0, no, this.user, this.postTime);
}
@Override
public StatusBarNotification clone() {
return new StatusBarNotification(this.pkg, this.opPkg,
this.id, this.tag, this.uid, this.initialPid,
- this.score, this.notification.clone(), this.user, this.postTime);
+ 0, this.notification.clone(), this.user, this.postTime);
}
@Override
@@ -168,7 +164,7 @@
return String.format(
"StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
this.pkg, this.user, this.id, this.tag,
- this.score, this.key, this.notification);
+ 0, this.key, this.notification);
}
/** Convenience method to check the notification's flags for
@@ -247,11 +243,6 @@
return postTime;
}
- /** @hide */
- public int getScore() {
- return score;
- }
-
/**
* A unique instance key for this notification record.
*/
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e5415df..ca249b9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4102,4 +4102,7 @@
</plurals>
<string name="default_notification_topic_label">Miscellaneous</string>
+
+ <string name="importance_from_topic">You set the importance of these notifications.</string>
+ <string name="importance_from_person">This is important because of the people involved.</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dd81f89..7470331 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2379,4 +2379,6 @@
<java-symbol type="dimen" name="notification_content_margin_end" />
<java-symbol type="dimen" name="notification_content_picture_margin" />
<java-symbol type="dimen" name="notification_content_margin_top" />
+ <java-symbol type="string" name="importance_from_topic" />
+ <java-symbol type="string" name="importance_from_person" />
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index cc6a29a..8b94e54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
@@ -2102,11 +2104,11 @@
mNotificationData.updateRanking(ranking);
boolean applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
- boolean shouldInterrupt = shouldInterrupt(entry, notification);
+ boolean shouldPeek = shouldPeek(entry, notification);
boolean alertAgain = alertAgain(entry, n);
if (DEBUG) {
Log.d(TAG, "applyInPlace=" + applyInPlace
- + " shouldInterrupt=" + shouldInterrupt
+ + " shouldPeek=" + shouldPeek
+ " alertAgain=" + alertAgain);
}
@@ -2153,7 +2155,7 @@
entry.icon.set(ic);
inflateViews(entry, mStackScroller);
}
- updateHeadsUp(key, entry, shouldInterrupt, alertAgain);
+ updateHeadsUp(key, entry, shouldPeek, alertAgain);
updateNotifications();
// Update the veto button accordingly (and as a result, whether this row is
@@ -2169,7 +2171,7 @@
setAreThereNotifications();
}
- protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
+ protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
boolean alertAgain);
private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
@@ -2212,49 +2214,54 @@
|| (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
}
- protected boolean shouldInterrupt(Entry entry) {
- return shouldInterrupt(entry, entry.notification);
+ protected boolean shouldPeek(Entry entry) {
+ return shouldPeek(entry, entry.notification);
}
- protected boolean shouldInterrupt(Entry entry, StatusBarNotification sbn) {
+ protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
if (mNotificationData.shouldFilterOut(sbn)) {
- if (DEBUG) {
- Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
- }
+ if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
return false;
}
if (isSnoozedPackage(sbn)) {
+ if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
return false;
}
- Notification notification = sbn.getNotification();
- // some predicates to make the boolean logic legible
- boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
- || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
- || notification.sound != null
- || notification.vibrate != null;
- boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
- boolean isFullscreen = notification.fullScreenIntent != null;
- boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
- boolean accessibilityForcesLaunch = isFullscreen
- && mAccessibilityManager.isTouchExplorationEnabled();
- boolean justLaunchedFullScreenIntent = entry.hasJustLaunchedFullScreenIntent();
- boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
- && !accessibilityForcesLaunch
- && !justLaunchedFullScreenIntent
- && mPowerManager.isScreenOn()
+ if (entry.hasJustLaunchedFullScreenIntent()) {
+ if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+ return false;
+ }
+
+ if (sbn.getNotification().fullScreenIntent != null
+ && mAccessibilityManager.isTouchExplorationEnabled()) {
+ if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
+ return false;
+ }
+
+
+ if (mNotificationData.shouldSuppressPeek(sbn.getKey())) {
+ if (DEBUG) Log.d(TAG, "No peeking: suppressed by manager: " + sbn.getKey());
+ return false;
+ }
+
+ if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_MAX) {
+ if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+ return false;
+ }
+
+ boolean inUse = mPowerManager.isScreenOn()
&& (!mStatusBarKeyguardViewManager.isShowing()
|| mStatusBarKeyguardViewManager.isOccluded())
- && !mStatusBarKeyguardViewManager.isInputRestricted()
- && !mNotificationData.shouldSuppressPeek(sbn.getKey());
+ && !mStatusBarKeyguardViewManager.isInputRestricted();
try {
- interrupt = interrupt && !mDreamManager.isDreaming();
+ inUse = inUse && !mDreamManager.isDreaming();
} catch (RemoteException e) {
Log.d(TAG, "failed to query dream manager", e);
}
- if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
- return interrupt;
+ if (DEBUG) Log.d(TAG, "peek if device in use: " + inUse);
+ return inUse;
}
protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 89edae3..fa41215 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -188,22 +188,33 @@
public int compare(Entry a, Entry b) {
final StatusBarNotification na = a.notification;
final StatusBarNotification nb = b.notification;
- final int aPriority = na.getNotification().priority;
- final int bPriority = nb.getNotification().priority;
+ int aImportance = Ranking.IMPORTANCE_DEFAULT;
+ int bImportance = Ranking.IMPORTANCE_DEFAULT;
+ int aRank = 0;
+ int bRank = 0;
+
+ if (mRankingMap != null) {
+ // RankingMap as received from NoMan
+ mRankingMap.getRanking(a.key, mRankingA);
+ mRankingMap.getRanking(b.key, mRankingB);
+ aImportance = mRankingA.getImportance();
+ bImportance = mRankingB.getImportance();
+ aRank = mRankingA.getRank();
+ bRank = mRankingB.getRank();
+ }
String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();
// PRIORITY_MIN media streams are allowed to drift to the bottom
final boolean aMedia = a.key.equals(mediaNotification)
- && aPriority > Notification.PRIORITY_MIN;
+ && aImportance > Ranking.IMPORTANCE_LOW;
final boolean bMedia = b.key.equals(mediaNotification)
- && bPriority > Notification.PRIORITY_MIN;
+ && bImportance > Ranking.IMPORTANCE_LOW;
- boolean aSystemMax = aPriority >= Notification.PRIORITY_MAX &&
+ boolean aSystemMax = aImportance >= Ranking.IMPORTANCE_MAX &&
isSystemNotification(na);
- boolean bSystemMax = bPriority >= Notification.PRIORITY_MAX &&
+ boolean bSystemMax = bImportance >= Ranking.IMPORTANCE_MAX &&
isSystemNotification(nb);
- int d = nb.getScore() - na.getScore();
boolean isHeadsUp = a.row.isHeadsUp();
if (isHeadsUp != b.row.isHeadsUp()) {
@@ -217,13 +228,8 @@
} else if (aSystemMax != bSystemMax) {
// Upsort PRIORITY_MAX system notifications
return aSystemMax ? -1 : 1;
- } else if (mRankingMap != null) {
- // RankingMap as received from NoMan
- mRankingMap.getRanking(a.key, mRankingA);
- mRankingMap.getRanking(b.key, mRankingB);
- return mRankingA.getRank() - mRankingB.getRank();
- } if (d != 0) {
- return d;
+ } else if (aRank != bRank) {
+ return aRank - bRank;
} else {
return (int) (nb.getNotification().when - na.getNotification().when);
}
@@ -295,6 +301,14 @@
return false;
}
+ public int getImportance(String key) {
+ if (mRankingMap != null) {
+ mRankingMap.getRanking(key, mTmpRanking);
+ return mTmpRanking.getImportance();
+ }
+ return Ranking.IMPORTANCE_UNSPECIFIED;
+ }
+
private void updateRankingAndSort(RankingMap ranking) {
if (ranking != null) {
mRankingMap = ranking;
@@ -390,12 +404,13 @@
}
private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) {
+ mRankingMap.getRanking(e.key, mTmpRanking);
pw.print(indent);
pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon);
StatusBarNotification n = e.notification;
pw.print(indent);
- pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" +
- n.getScore());
+ pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" +
+ mTmpRanking.getImportance());
pw.print(indent);
pw.println(" notification=" + n.getNotification());
pw.print(indent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 181e6aa..b854c80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1266,7 +1266,7 @@
if (shadeEntry == null) {
return;
}
- boolean isHeadsUped = mUseHeadsUp && shouldInterrupt(shadeEntry);
+ boolean isHeadsUped = mUseHeadsUp && shouldPeek(shadeEntry);
if (isHeadsUped) {
mHeadsUpManager.showNotification(shadeEntry);
// Mark as seen immediately
@@ -2111,17 +2111,17 @@
}
- protected void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
+ protected void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
boolean alertAgain) {
final boolean wasHeadsUp = isHeadsUp(key);
if (wasHeadsUp) {
- if (!shouldInterrupt) {
+ if (!shouldPeek) {
// We don't want this to be interrupting anymore, lets remove it
mHeadsUpManager.removeNotification(key);
} else {
mHeadsUpManager.updateNotification(entry, alertAgain);
}
- } else if (shouldInterrupt && alertAgain) {
+ } else if (shouldPeek && alertAgain) {
// This notification was updated to be a heads-up, show it!
mHeadsUpManager.showNotification(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 39a2986..856a774 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -175,7 +175,7 @@
}
@Override
- protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldInterrupt,
+ protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
boolean alertAgain) {
}
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index ec81fd2..32db000 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -32,11 +32,11 @@
return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
}
- final int leftScore = left.sbn.getScore();
- final int rightScore = right.sbn.getScore();
- if (leftScore != rightScore) {
+ final int leftImportance = left.getImportance();
+ final int rightImportance = right.getImportance();
+ if (leftImportance != rightImportance) {
// by priority, high to low
- return -1 * Integer.compare(leftScore, rightScore);
+ return -1 * Integer.compare(leftImportance, rightImportance);
}
final float leftPeople = left.getContactAffinity();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c83012c..dd5baec 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -34,6 +34,7 @@
import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_PEEK;
import static android.service.notification.NotificationListenerService.TRIM_FULL;
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -181,16 +182,6 @@
static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
- static final boolean SCORE_ONGOING_HIGHER = false;
-
- static final int JUNK_SCORE = -1000;
- static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
- static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
-
- // Notifications with scores below this will not interrupt the user, either via LED or
- // sound or vibration
- static final int SCORE_INTERRUPTION_THRESHOLD =
- Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
static final boolean ENABLE_BLOCKED_TOASTS = true;
@@ -2158,31 +2149,15 @@
synchronized (mNotificationList) {
- // === Scoring ===
-
- // 0. Sanitize inputs
+ // Sanitize inputs
notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
Notification.PRIORITY_MAX);
- // Migrate notification flags to scores
- if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
- if (notification.priority < Notification.PRIORITY_MAX) {
- notification.priority = Notification.PRIORITY_MAX;
- }
- } else if (SCORE_ONGOING_HIGHER &&
- 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
- if (notification.priority < Notification.PRIORITY_HIGH) {
- notification.priority = Notification.PRIORITY_HIGH;
- }
- }
- // 1. initial score: buckets of 10, around the app [-20..20]
- final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
-
- // 2. extract ranking signals from the notification data
+ // setup local book-keeping
final StatusBarNotification n = new StatusBarNotification(
- pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
+ pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
user);
- NotificationRecord r = new NotificationRecord(n, score);
+ NotificationRecord r = new NotificationRecord(getContext(), n);
NotificationRecord old = mNotificationsByKey.get(n.getKey());
if (old != null) {
// Retain ranking information from previous record
@@ -2217,23 +2192,16 @@
mRankingHelper.extractSignals(r);
savePolicyFile();
- // 3. Apply local rules
-
// blocked apps
if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
if (!isSystemNotification) {
- r.score = JUNK_SCORE;
Slog.e(TAG, "Suppressing notification from package " + pkg
+ " by user request.");
mUsageStats.registerBlocked(r);
+ return;
}
}
- if (r.score < SCORE_DISPLAY_THRESHOLD) {
- // Notification will be blocked because the score is too low.
- return;
- }
-
int index = indexOfNotificationLocked(n.getKey());
if (index < 0) {
mNotificationList.add(r);
@@ -2385,7 +2353,7 @@
final Notification notification = record.sbn.getNotification();
// Should this notification make noise, vibe, or use the LED?
- final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
+ final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_HIGH;
final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
if (DBG || record.isIntercepted())
Slog.v(TAG,
@@ -3251,24 +3219,29 @@
final int N = mNotificationList.size();
ArrayList<String> keys = new ArrayList<String>(N);
ArrayList<String> interceptedKeys = new ArrayList<String>(N);
+ ArrayList<Integer> importance = new ArrayList<>(N);
Bundle visibilityOverrides = new Bundle();
Bundle suppressedVisualEffects = new Bundle();
+ Bundle explanation = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
continue;
}
- keys.add(record.sbn.getKey());
+ final String key = record.sbn.getKey();
+ keys.add(key);
+ importance.add(record.getImportance());
+ if (record.getImportanceExplanation() != null) {
+ explanation.putCharSequence(key, record.getImportanceExplanation());
+ }
if (record.isIntercepted()) {
- interceptedKeys.add(record.sbn.getKey());
+ interceptedKeys.add(key);
}
- suppressedVisualEffects.putInt(
- record.sbn.getKey(), record.getSuppressedVisualEffects());
+ suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
if (record.getPackageVisibilityOverride()
!= NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
- visibilityOverrides.putInt(record.sbn.getKey(),
- record.getPackageVisibilityOverride());
+ visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
}
// Find first min-prio notification for speedbump placement.
if (speedBumpIndex == -1 &&
@@ -3283,10 +3256,15 @@
speedBumpIndex = keys.size() - 1;
}
}
- String[] keysAr = keys.toArray(new String[keys.size()]);
+ final int M = keys.size();
+ String[] keysAr = keys.toArray(new String[M]);
String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
+ int[] importanceAr = new int[M];
+ for (int i = 0; i < M; i++) {
+ importanceAr[i] = importance.get(i);
+ }
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
- speedBumpIndex, suppressedVisualEffects);
+ speedBumpIndex, suppressedVisualEffects, importanceAr, explanation);
}
private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 3b7384e..463c0b0 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -15,6 +15,11 @@
*/
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_LOW;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
+
import android.app.Notification;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -49,10 +54,10 @@
public final class NotificationRecord {
final StatusBarNotification sbn;
final int mOriginalFlags;
+ private final Context mContext;
NotificationUsageStats.SingleNotificationStats stats;
boolean isCanceled;
- int score;
/** Whether the notification was seen by the user via one of the notification listeners. */
boolean mIsSeen;
@@ -84,18 +89,71 @@
private String mGlobalSortKey;
private int mPackageVisibility;
private int mTopicImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ private int mImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ private CharSequence mImportanceExplanation = null;
private int mSuppressedVisualEffects = 0;
+ private String mTopicExplanation;
+ private String mPeopleExplanation;
@VisibleForTesting
- public NotificationRecord(StatusBarNotification sbn, int score)
+ public NotificationRecord(Context context, StatusBarNotification sbn)
{
this.sbn = sbn;
- this.score = score;
mOriginalFlags = sbn.getNotification().flags;
mRankingTimeMs = calculateRankingTimeMs(0L);
mCreationTimeMs = sbn.getPostTime();
mUpdateTimeMs = mCreationTimeMs;
+ mContext = context;
+ mImportance = defaultImportance();
+ }
+
+ private int defaultImportance() {
+ final Notification n = sbn.getNotification();
+ int importance = IMPORTANCE_DEFAULT;
+
+ // Migrate notification flags to scores
+ if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
+ n.priority = Notification.PRIORITY_MAX;
+ }
+
+ switch (n.priority) {
+ case Notification.PRIORITY_MIN:
+ case Notification.PRIORITY_LOW:
+ importance = IMPORTANCE_LOW;
+ break;
+ case Notification.PRIORITY_DEFAULT:
+ importance = IMPORTANCE_DEFAULT;
+ break;
+ case Notification.PRIORITY_HIGH:
+ importance = IMPORTANCE_HIGH;
+ break;
+ case Notification.PRIORITY_MAX:
+ importance = IMPORTANCE_MAX;
+ break;
+ }
+
+ boolean isNoisy = (n.defaults & Notification.DEFAULT_SOUND) != 0
+ || (n.defaults & Notification.DEFAULT_VIBRATE) != 0
+ || n.sound != null
+ || n.vibrate != null;
+ if (!isNoisy && importance > IMPORTANCE_DEFAULT) {
+ importance = IMPORTANCE_DEFAULT;
+ }
+ // maybe only do this for target API < N?
+ if (isNoisy) {
+ if (importance == IMPORTANCE_HIGH) {
+ importance = IMPORTANCE_MAX;
+ } else {
+ importance = IMPORTANCE_HIGH;
+ }
+ }
+
+ if (n.fullScreenIntent != null) {
+ importance = IMPORTANCE_MAX;
+ }
+
+ return importance;
}
// copy any notes that the ranking system may have made before the update
@@ -109,6 +167,8 @@
mCreationTimeMs = previous.mCreationTimeMs;
mVisibleSinceMs = previous.mVisibleSinceMs;
mTopicImportance = previous.mTopicImportance;
+ mImportance = previous.mImportance;
+ mImportanceExplanation = previous.mImportanceExplanation;
// Don't copy mGlobalSortKey, recompute it.
}
@@ -129,7 +189,7 @@
pw.println(prefix + this);
pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
pw.println(prefix + " icon=" + iconStr);
- pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore());
+ pw.println(prefix + " pri=" + notification.priority);
pw.println(prefix + " key=" + sbn.getKey());
pw.println(prefix + " seen=" + mIsSeen);
pw.println(prefix + " groupKey=" + getGroupKey());
@@ -158,6 +218,7 @@
action.actionIntent.toString()
));
}
+ }
pw.println(prefix + " }");
}
if (notification.extras != null && notification.extras.size() > 0) {
@@ -200,6 +261,9 @@
pw.println(prefix + " mPackageVisibility=" + mPackageVisibility);
pw.println(prefix + " mTopicImportance="
+ NotificationListenerService.Ranking.importanceToString(mTopicImportance));
+ pw.println(prefix + " mImportance="
+ + NotificationListenerService.Ranking.importanceToString(mImportance));
+ pw.println(prefix + " mImportanceExplanation=" + mImportanceExplanation);
pw.println(prefix + " mIntercept=" + mIntercept);
pw.println(prefix + " mGlobalSortKey=" + mGlobalSortKey);
pw.println(prefix + " mRankingTimeMs=" + mRankingTimeMs);
@@ -234,15 +298,19 @@
@Override
public final String toString() {
return String.format(
- "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
+ "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s: %s)",
System.identityHashCode(this),
this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
- this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
+ this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
this.sbn.getNotification());
}
public void setContactAffinity(float contactAffinity) {
mContactAffinity = contactAffinity;
+ if (mImportance < IMPORTANCE_DEFAULT &&
+ mContactAffinity > ValidateNotificationPeople.VALID_CONTACT) {
+ setImportance(IMPORTANCE_DEFAULT, getPeopleExplanation());
+ }
}
public float getContactAffinity() {
@@ -276,6 +344,30 @@
public void setTopicImportance(int importance) {
if (importance != NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED) {
mTopicImportance = importance;
+ applyTopicImportance();
+ }
+ }
+
+ private String getTopicExplanation() {
+ if (mTopicExplanation == null) {
+ mTopicExplanation =
+ mContext.getString(com.android.internal.R.string.importance_from_topic);
+ }
+ return mTopicExplanation;
+ }
+
+ private String getPeopleExplanation() {
+ if (mPeopleExplanation == null) {
+ mPeopleExplanation =
+ mContext.getString(com.android.internal.R.string.importance_from_person);
+ }
+ return mPeopleExplanation;
+ }
+
+ private void applyTopicImportance() {
+ if (mTopicImportance != NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED) {
+ mImportance = mTopicImportance;
+ mImportanceExplanation = getTopicExplanation();
}
}
@@ -283,6 +375,22 @@
return mTopicImportance;
}
+ public void setImportance(int importance, CharSequence explanation) {
+ if (importance != NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED) {
+ mImportance = importance;
+ mImportanceExplanation = explanation;
+ }
+ applyTopicImportance();
+ }
+
+ public int getImportance() {
+ return mImportance;
+ }
+
+ public CharSequence getImportanceExplanation() {
+ return mImportanceExplanation;
+ }
+
public boolean setIntercepted(boolean intercept) {
mIntercept = intercept;
return mIntercept;
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index d0065cd..df7b412 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -64,8 +64,8 @@
.setWhen(1205)
.setTopic(new Notification.Topic("A", "a"))
.build();
- mRecordGroupGSortA = new NotificationRecord(new StatusBarNotification(
- "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user), 0);
+ mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user));
mNotiGroupGSortB = new Notification.Builder(getContext())
.setContentTitle("B")
@@ -74,24 +74,24 @@
.setWhen(1200)
.setTopic(new Notification.Topic("A", "a"))
.build();
- mRecordGroupGSortB = new NotificationRecord(new StatusBarNotification(
- "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user), 0);
+ mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user));
mNotiNoGroup = new Notification.Builder(getContext())
.setContentTitle("C")
.setWhen(1201)
.setTopic(new Notification.Topic("C", "c"))
.build();
- mRecordNoGroup = new NotificationRecord(new StatusBarNotification(
- "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user), 0);
+ mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user));
mNotiNoGroup2 = new Notification.Builder(getContext())
.setContentTitle("D")
.setWhen(1202)
.setTopic(new Notification.Topic("D", "d"))
.build();
- mRecordNoGroup2 = new NotificationRecord(new StatusBarNotification(
- "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user), 0);
+ mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user));
mNotiNoGroupSortA = new Notification.Builder(getContext())
.setContentTitle("E")
@@ -99,8 +99,8 @@
.setSortKey("A")
.setTopic(new Notification.Topic("E", "e"))
.build();
- mRecordNoGroupSortA = new NotificationRecord(new StatusBarNotification(
- "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user), 0);
+ mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user));
}
@SmallTest