Merge "Topics can now have an Importance."
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index c504ce3..136b810 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -52,6 +52,8 @@
int getTopicVisibilityOverride(String pkg, int uid, in Notification.Topic topic);
void setTopicPriority(String pkg, int uid, in Notification.Topic topic, int priority);
int getTopicPriority(String pkg, int uid, in Notification.Topic topic);
+ void setTopicImportance(String pkg, int uid, in Notification.Topic topic, int importance);
+ int getTopicImportance(String pkg, int uid, in Notification.Topic topic);
// TODO: Remove this when callers have been migrated to the equivalent
// INotificationListener method.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index d741f1a..a7545f2 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -823,9 +823,9 @@
public static final int VISIBILITY_NO_OVERRIDE = -1000;
/**
- * Value signifying thatn the has not expressed an importance.
+ * Value signifying that the user has not expressed an importance.
*
- * This value is for persisting preferences, and should never ve associated with
+ * This value is for persisting preferences, and should never be associated with
* an actual notification.
*/
public static final int IMPORTANCE_UNSPECIFIED = -1000;
@@ -952,6 +952,28 @@
mVisibilityOverride = visibilityOverride;
mSuppressedVisualEffects = suppressedVisualEffects;
}
+
+ /**
+ * {@hide}
+ */
+ public static String importanceToString(int importance) {
+ switch (importance) {
+ case IMPORTANCE_UNSPECIFIED:
+ return "UNSPECIFIED";
+ case IMPORTANCE_NONE:
+ return "NONE";
+ case IMPORTANCE_LOW:
+ return "LOW";
+ case IMPORTANCE_DEFAULT:
+ return "DEFAULT";
+ case IMPORTANCE_HIGH:
+ return "HIGH";
+ case IMPORTANCE_MAX:
+ return "MAX";
+ default:
+ return "UNKNOWN(" + String.valueOf(importance) + ")";
+ }
+ }
}
/**
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 221bb45..decb1ef 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1960,6 +1960,7 @@
<item>com.android.server.notification.TopicPriorityExtractor</item>
<item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
<item>com.android.server.notification.TopicVisibilityExtractor</item>
+ <item>com.android.server.notification.TopicImportanceExtractor</item>
</string-array>
<!-- Flag indicating that this device does not rotate and will always remain in its default
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d6dd842..e5415df 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3954,6 +3954,22 @@
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
<string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
+ <string name="notification_importance_blocked">Blocked: Never show these notifications</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: low importance level description -->
+ <string name="notification_importance_low">Low: Silently show at the bottom of the notification list</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: normal importance level description -->
+ <string name="notification_importance_default">Normal: Silently show these notifications</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: high importance level description -->
+ <string name="notification_importance_high">High: Show at the top of the notifications list and make sound</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
+ <string name="notification_importance_max">Urgent: Peek onto the screen and make sound</string>
+
<!-- Zen mode condition - summary: time duration in minutes. [CHAR LIMIT=NONE] -->
<plurals name="zen_mode_duration_minutes_summary">
<item quantity="one">For one minute (until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 66cc1a5..d92adc2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2086,6 +2086,11 @@
<java-symbol type="array" name="config_system_condition_providers" />
<java-symbol type="string" name="muted_by" />
<java-symbol type="string" name="zen_mode_alarm" />
+ <java-symbol type="string" name="notification_importance_blocked" />
+ <java-symbol type="string" name="notification_importance_low" />
+ <java-symbol type="string" name="notification_importance_default" />
+ <java-symbol type="string" name="notification_importance_high" />
+ <java-symbol type="string" name="notification_importance_max" />
<java-symbol type="string" name="select_day" />
<java-symbol type="string" name="select_year" />
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c2666b8..33f39bc 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1243,6 +1243,20 @@
return mRankingHelper.getTopicVisibilityOverride(pkg, uid, topic);
}
+ @Override
+ public void setTopicImportance(String pkg, int uid, Notification.Topic topic,
+ int importance) {
+ checkCallerIsSystem();
+ mRankingHelper.setTopicImportance(pkg, uid, topic, importance);
+ savePolicyFile();
+ }
+
+ @Override
+ public int getTopicImportance(String pkg, int uid, Notification.Topic topic) {
+ checkCallerIsSystem();
+ return mRankingHelper.getTopicImportance(pkg, uid, topic);
+ }
+
/**
* System-only API for getting a list of current (i.e. not cleared) notifications.
*
@@ -2201,6 +2215,7 @@
}
mRankingHelper.extractSignals(r);
+ savePolicyFile();
// 3. Apply local rules
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 2a7568d..3b7384e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -23,6 +23,7 @@
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import com.android.internal.annotations.VisibleForTesting;
@@ -82,6 +83,7 @@
private int mAuthoritativeRank;
private String mGlobalSortKey;
private int mPackageVisibility;
+ private int mTopicImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
private int mSuppressedVisualEffects = 0;
@@ -106,6 +108,7 @@
mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
mCreationTimeMs = previous.mCreationTimeMs;
mVisibleSinceMs = previous.mVisibleSinceMs;
+ mTopicImportance = previous.mTopicImportance;
// Don't copy mGlobalSortKey, recompute it.
}
@@ -195,6 +198,8 @@
pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive);
pw.println(prefix + " mPackagePriority=" + mPackagePriority);
pw.println(prefix + " mPackageVisibility=" + mPackageVisibility);
+ pw.println(prefix + " mTopicImportance="
+ + NotificationListenerService.Ranking.importanceToString(mTopicImportance));
pw.println(prefix + " mIntercept=" + mIntercept);
pw.println(prefix + " mGlobalSortKey=" + mGlobalSortKey);
pw.println(prefix + " mRankingTimeMs=" + mRankingTimeMs);
@@ -268,6 +273,16 @@
return mPackageVisibility;
}
+ public void setTopicImportance(int importance) {
+ if (importance != NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED) {
+ mTopicImportance = importance;
+ }
+ }
+
+ public int getTopicImportance() {
+ return mTopicImportance;
+ }
+
public boolean setIntercepted(boolean intercept) {
mIntercept = intercept;
return mIntercept;
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 7ee29e4..acdd90a 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -31,4 +31,8 @@
void setTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic,
int visibility);
+
+ void setTopicImportance(String packageName, int uid, Notification.Topic topic, int importance);
+
+ int getTopicImportance(String packageName, int uid, Notification.Topic topic);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 543cd89..5a31c6a 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -23,6 +23,7 @@
import android.os.Message;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -55,12 +56,13 @@
private static final String ATT_UID = "uid";
private static final String ATT_PRIORITY = "priority";
private static final String ATT_VISIBILITY = "visibility";
+ private static final String ATT_IMPORTANCE = "importance";
private static final String ATT_TOPIC_ID = "id";
private static final String ATT_TOPIC_LABEL = "label";
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
- private static final int DEFAULT_VISIBILITY =
- NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
+ private static final int DEFAULT_VISIBILITY = Ranking.VISIBILITY_NO_OVERRIDE;
+ private static final int DEFAULT_IMPORTANCE = Ranking.IMPORTANCE_UNSPECIFIED;
private final NotificationSignalExtractor[] mSignalExtractors;
private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
@@ -197,6 +199,7 @@
if (TAG_TOPIC.equals(tagName)) {
int priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+ int importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
String id = parser.getAttributeValue(null, ATT_TOPIC_ID);
CharSequence label = parser.getAttributeValue(null, ATT_TOPIC_LABEL);
@@ -209,6 +212,9 @@
if (vis != DEFAULT_VISIBILITY) {
topic.visibility = vis;
}
+ if (importance != DEFAULT_IMPORTANCE) {
+ topic.importance = importance;
+ }
r.topics.put(id, topic);
}
}
@@ -267,6 +273,9 @@
if (t.visibility != DEFAULT_VISIBILITY) {
out.attribute(null, ATT_VISIBILITY, Integer.toString(t.visibility));
}
+ if (t.importance != DEFAULT_IMPORTANCE) {
+ out.attribute(null, ATT_IMPORTANCE, Integer.toString(t.importance));
+ }
out.endTag(null, TAG_TOPIC);
}
}
@@ -403,6 +412,20 @@
updateConfig();
}
+ @Override
+ public int getTopicImportance(String packageName, int uid, Notification.Topic topic) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ return getOrCreateTopic(r, topic).importance;
+ }
+
+ @Override
+ public void setTopicImportance(String pkgName, int uid, Notification.Topic topic,
+ int importance) {
+ final Record r = getOrCreateRecord(pkgName, uid);
+ getOrCreateTopic(r, topic).importance = importance;
+ updateConfig();
+ }
+
private Topic getOrCreateTopic(Record r, Notification.Topic topic) {
if (topic == null) {
topic = createDefaultTopic();
@@ -468,6 +491,10 @@
pw.print(" visibility=");
pw.print(Notification.visibilityToString(t.visibility));
}
+ if (t.importance != DEFAULT_IMPORTANCE) {
+ pw.print(" importance=");
+ pw.print(Ranking.importanceToString(t.importance));
+ }
pw.println();
}
}
@@ -512,6 +539,7 @@
Notification.Topic topic;
int priority = DEFAULT_PRIORITY;
int visibility = DEFAULT_VISIBILITY;
+ int importance = DEFAULT_IMPORTANCE;
public Topic(Notification.Topic topic) {
this.topic = topic;
diff --git a/services/core/java/com/android/server/notification/TopicImportanceExtractor.java b/services/core/java/com/android/server/notification/TopicImportanceExtractor.java
new file mode 100644
index 0000000..01770d0
--- /dev/null
+++ b/services/core/java/com/android/server/notification/TopicImportanceExtractor.java
@@ -0,0 +1,56 @@
+/**
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.android.server.notification;
+
+import android.content.Context;
+import android.util.Slog;
+
+/**
+ * Determines the importance of the given notification.
+ */
+public class TopicImportanceExtractor implements NotificationSignalExtractor {
+ private static final String TAG = "ImportantTopicExtractor";
+ private static final boolean DBG = false;
+
+ private RankingConfig mConfig;
+
+ public void initialize(Context ctx, NotificationUsageStats usageStats) {
+ if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ }
+
+ public RankingReconsideration process(NotificationRecord record) {
+ if (record == null || record.getNotification() == null) {
+ if (DBG) Slog.d(TAG, "skipping empty notification");
+ return null;
+ }
+
+ if (mConfig == null) {
+ if (DBG) Slog.d(TAG, "missing config");
+ return null;
+ }
+
+ final int topicImportance = mConfig.getTopicImportance(record.sbn.getPackageName(),
+ record.sbn.getUid(), record.sbn.getNotification().getTopic());
+ record.setTopicImportance(topicImportance);
+
+ return null;
+ }
+
+ @Override
+ public void setConfig(RankingConfig config) {
+ mConfig = config;
+ }
+}
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 b40fd068..d0065cd 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -15,10 +15,17 @@
*/
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+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 static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
+
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import android.app.Notification;
+import android.os.Handler;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.test.AndroidTestCase;
@@ -28,6 +35,7 @@
public class RankingHelperTest extends AndroidTestCase {
@Mock NotificationUsageStats mUsageStats;
+ @Mock Handler handler;
private Notification mNotiGroupGSortA;
private Notification mNotiGroupGSortB;
@@ -46,13 +54,15 @@
MockitoAnnotations.initMocks(this);
UserHandle user = UserHandle.ALL;
- mHelper = new RankingHelper(getContext(), null, mUsageStats, new String[0]);
+ mHelper = new RankingHelper(getContext(), handler, mUsageStats,
+ new String[] {TopicImportanceExtractor.class.getName()});
mNotiGroupGSortA = new Notification.Builder(getContext())
.setContentTitle("A")
.setGroup("G")
.setSortKey("A")
.setWhen(1205)
+ .setTopic(new Notification.Topic("A", "a"))
.build();
mRecordGroupGSortA = new NotificationRecord(new StatusBarNotification(
"package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user), 0);
@@ -62,6 +72,7 @@
.setGroup("G")
.setSortKey("B")
.setWhen(1200)
+ .setTopic(new Notification.Topic("A", "a"))
.build();
mRecordGroupGSortB = new NotificationRecord(new StatusBarNotification(
"package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user), 0);
@@ -69,6 +80,7 @@
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);
@@ -76,6 +88,7 @@
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);
@@ -84,6 +97,7 @@
.setContentTitle("E")
.setWhen(1201)
.setSortKey("A")
+ .setTopic(new Notification.Topic("E", "e"))
.build();
mRecordNoGroupSortA = new NotificationRecord(new StatusBarNotification(
"package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user), 0);
@@ -138,4 +152,30 @@
ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
mHelper.sort(notificationList);
}
+
+ @SmallTest
+ public void testTopicImportanceExtractor() throws Exception {
+ mHelper.setTopicImportance("package", 0, new Notification.Topic("A", "a"),
+ IMPORTANCE_MAX);
+ // There is no B. There never was a b. Moving on...
+ mHelper.setTopicImportance("package", 0, new Notification.Topic("C", "c"),
+ IMPORTANCE_HIGH);
+ mHelper.setTopicImportance("package", 0, new Notification.Topic("D", "d"),
+ IMPORTANCE_LOW);
+ // watch out: different package.
+ mHelper.setTopicImportance("package2", 0, new Notification.Topic("E", "e"),
+ IMPORTANCE_NONE);
+
+ TopicImportanceExtractor validator = mHelper.findExtractor(TopicImportanceExtractor.class);
+ validator.process(mRecordGroupGSortA);
+ validator.process(mRecordGroupGSortB);
+ validator.process(mRecordNoGroup);
+ validator.process(mRecordNoGroup2);
+ validator.process(mRecordNoGroupSortA);
+ assertTrue(mRecordGroupGSortA.getTopicImportance() == IMPORTANCE_MAX);
+ assertTrue(mRecordGroupGSortB.getTopicImportance() == IMPORTANCE_MAX);
+ assertTrue(mRecordNoGroup.getTopicImportance() == IMPORTANCE_HIGH);
+ assertTrue(mRecordNoGroup2.getTopicImportance() == IMPORTANCE_LOW);
+ assertTrue(mRecordNoGroupSortA.getTopicImportance() == IMPORTANCE_UNSPECIFIED);
+ }
}