Fix CallStyle ranking & ensure correct features are used.
Fixes: 179178086 Test: atest SystemUITests NotificationComparatorTest Change-Id: I8e0890954300f4fcf31d5927b1919e16e46a7801
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.collection
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager.IMPORTANCE_HIGH
|
||||
import android.app.NotificationManager.IMPORTANCE_MIN
|
||||
import android.service.notification.NotificationListenerService.Ranking
|
||||
@@ -25,6 +26,7 @@ import com.android.systemui.statusbar.NotificationMediaManager
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
|
||||
import com.android.systemui.statusbar.notification.NotificationFilter
|
||||
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
|
||||
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
|
||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
|
||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
|
||||
@@ -33,11 +35,11 @@ import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVI
|
||||
import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
|
||||
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
|
||||
import com.android.systemui.statusbar.notification.stack.PriorityBucket
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager
|
||||
import dagger.Lazy
|
||||
import java.util.Objects
|
||||
import javax.inject.Inject
|
||||
import kotlin.Comparator
|
||||
|
||||
private const val TAG = "NotifRankingManager"
|
||||
|
||||
@@ -77,6 +79,9 @@ open class NotificationRankingManager @Inject constructor(
|
||||
val aIsFsn = a.isColorizedForegroundService()
|
||||
val bIsFsn = b.isColorizedForegroundService()
|
||||
|
||||
val aCall = a.isImportantCall()
|
||||
val bCall = b.isImportantCall()
|
||||
|
||||
val aPersonType = a.getPeopleNotificationType()
|
||||
val bPersonType = b.getPeopleNotificationType()
|
||||
|
||||
@@ -96,6 +101,7 @@ open class NotificationRankingManager @Inject constructor(
|
||||
// Provide consistent ranking with headsUpManager
|
||||
aHeadsUp -> headsUpManager.compare(a, b)
|
||||
aIsFsn != bIsFsn -> if (aIsFsn) -1 else 1
|
||||
aCall != bCall -> if (aCall) -1 else 1
|
||||
usePeopleFiltering && aPersonType != bPersonType ->
|
||||
peopleNotificationIdentifier.compareTo(aPersonType, bPersonType)
|
||||
// Upsort current media notification.
|
||||
@@ -150,11 +156,12 @@ open class NotificationRankingManager @Inject constructor(
|
||||
|
||||
@PriorityBucket
|
||||
private fun getBucketForEntry(entry: NotificationEntry): Int {
|
||||
val isImportantCall = entry.isImportantCall()
|
||||
val isHeadsUp = entry.isRowHeadsUp
|
||||
val isMedia = entry.isImportantMedia()
|
||||
val isSystemMax = entry.isSystemMax()
|
||||
return when {
|
||||
entry.isColorizedForegroundService() -> BUCKET_FOREGROUND_SERVICE
|
||||
entry.isColorizedForegroundService() || isImportantCall -> BUCKET_FOREGROUND_SERVICE
|
||||
usePeopleFiltering && entry.isConversation() -> BUCKET_PEOPLE
|
||||
isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() -> BUCKET_ALERTING
|
||||
else -> BUCKET_SILENT
|
||||
@@ -186,7 +193,7 @@ open class NotificationRankingManager @Inject constructor(
|
||||
}
|
||||
|
||||
private fun NotificationEntry.isImportantMedia() =
|
||||
key == mediaManager.mediaNotificationKey && ranking.importance > IMPORTANCE_MIN
|
||||
key == mediaManager.mediaNotificationKey && importance > IMPORTANCE_MIN
|
||||
|
||||
private fun NotificationEntry.isConversation() = getPeopleNotificationType() != TYPE_NON_PERSON
|
||||
|
||||
@@ -204,6 +211,10 @@ private fun NotificationEntry.isSystemMax() =
|
||||
private fun StatusBarNotification.isSystemNotification() =
|
||||
"android" == packageName || "com.android.systemui" == packageName
|
||||
|
||||
private fun NotificationEntry.isImportantCall() =
|
||||
sbn.notification.extras?.getString(Notification.EXTRA_TEMPLATE) ==
|
||||
"android.app.Notification\$CallStyle" && importance > IMPORTANCE_MIN
|
||||
|
||||
private fun NotificationEntry.isColorizedForegroundService() = sbn.notification.run {
|
||||
isForegroundService && isColorized && importance > IMPORTANCE_MIN
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.app.NotificationChannel
|
||||
import android.app.NotificationManager.IMPORTANCE_DEFAULT
|
||||
import android.app.NotificationManager.IMPORTANCE_HIGH
|
||||
import android.app.NotificationManager.IMPORTANCE_LOW
|
||||
import android.app.PendingIntent
|
||||
import android.app.Person
|
||||
import android.os.SystemClock
|
||||
import android.service.notification.NotificationListenerService.RankingMap
|
||||
import android.testing.AndroidTestingRunner
|
||||
@@ -408,6 +410,74 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
assertThat(b.bucket).isEqualTo(BUCKET_FOREGROUND_SERVICE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSort_importantCall() {
|
||||
whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
|
||||
|
||||
val a = NotificationEntryBuilder()
|
||||
.setImportance(IMPORTANCE_HIGH)
|
||||
.setPkg("pkg")
|
||||
.setOpPkg("pkg")
|
||||
.setTag("tag")
|
||||
.setNotification(
|
||||
Notification.Builder(mContext, "test")
|
||||
.build())
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setUser(mContext.getUser())
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
|
||||
val b = NotificationEntryBuilder()
|
||||
.setImportance(IMPORTANCE_DEFAULT) // high priority
|
||||
.setPkg("pkg2")
|
||||
.setOpPkg("pkg2")
|
||||
.setTag("tag")
|
||||
.setNotification(mock(Notification::class.java).also { notif ->
|
||||
whenever(notif.isForegroundService).thenReturn(true)
|
||||
whenever(notif.isColorized).thenReturn(true)
|
||||
})
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setUser(mContext.getUser())
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
|
||||
val cN = Notification.Builder(mContext, "test")
|
||||
.setStyle(Notification.MessagingStyle(""))
|
||||
.build()
|
||||
val c = NotificationEntryBuilder()
|
||||
.setImportance(IMPORTANCE_HIGH)
|
||||
.setPkg("pkg")
|
||||
.setOpPkg("pkg")
|
||||
.setTag("tag")
|
||||
.setNotification(cN)
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setUser(mContext.user)
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
|
||||
val dN = Notification.Builder(mContext, "test")
|
||||
.setStyle(Notification.CallStyle.forOngoingCall(
|
||||
Person.Builder().setName("caller").build(),
|
||||
mock(PendingIntent::class.java)))
|
||||
.build()
|
||||
val d = NotificationEntryBuilder()
|
||||
.setImportance(IMPORTANCE_DEFAULT) // high priority
|
||||
.setPkg("pkg2")
|
||||
.setOpPkg("pkg2")
|
||||
.setTag("tag")
|
||||
.setNotification(dN)
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setUser(mContext.user)
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
whenever(personNotificationIdentifier.getPeopleNotificationType(a))
|
||||
.thenReturn(TYPE_IMPORTANT_PERSON)
|
||||
|
||||
assertThat(rankingManager.updateRanking(null, listOf(a, b, c, d), "test"))
|
||||
.containsExactly(b, d, c, a)
|
||||
assertThat(d.bucket).isEqualTo(BUCKET_FOREGROUND_SERVICE)
|
||||
}
|
||||
|
||||
internal class TestableNotificationRankingManager(
|
||||
mediaManager: Lazy<NotificationMediaManager>,
|
||||
groupManager: NotificationGroupManagerLegacy,
|
||||
|
||||
@@ -147,15 +147,16 @@ public class NotificationComparator
|
||||
}
|
||||
|
||||
private boolean isImportantOngoing(NotificationRecord record) {
|
||||
if (!isOngoing(record)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isCall(record) || isMediaNotification(record);
|
||||
if (isCallStyle(record)) {
|
||||
return true;
|
||||
}
|
||||
if (!isOngoing(record)) {
|
||||
return false;
|
||||
}
|
||||
return isCallCategory(record) || isMediaNotification(record);
|
||||
}
|
||||
|
||||
protected boolean isImportantPeople(NotificationRecord record) {
|
||||
@@ -181,11 +182,16 @@ public class NotificationComparator
|
||||
return record.getNotification().hasMediaSession();
|
||||
}
|
||||
|
||||
private boolean isCall(NotificationRecord record) {
|
||||
private boolean isCallCategory(NotificationRecord record) {
|
||||
return record.isCategory(Notification.CATEGORY_CALL)
|
||||
&& isDefaultPhoneApp(record.getSbn().getPackageName());
|
||||
}
|
||||
|
||||
private boolean isCallStyle(NotificationRecord record) {
|
||||
return "android.app.Notification$CallStyle".equals(
|
||||
record.getNotification().extras.getString(Notification.EXTRA_TEMPLATE));
|
||||
}
|
||||
|
||||
private boolean isDefaultPhoneApp(String pkg) {
|
||||
if (mDefaultPhoneApp == null) {
|
||||
final TelecomManager telecomm =
|
||||
|
||||
@@ -6570,6 +6570,17 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
if ("android.app.Notification$CallStyle".equals(
|
||||
n.extras.getString(Notification.EXTRA_TEMPLATE))) {
|
||||
boolean isForegroundService = (n.flags & FLAG_FOREGROUND_SERVICE) != 0;
|
||||
boolean hasFullScreenIntent = n.fullScreenIntent != null;
|
||||
if (!isForegroundService && !hasFullScreenIntent) {
|
||||
throw new IllegalArgumentException(r.getKey() + " Not posted."
|
||||
+ " CallStyle notifications must either be for a foreground Service or"
|
||||
+ " use a fullScreenIntent.");
|
||||
}
|
||||
}
|
||||
|
||||
// snoozed apps
|
||||
if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
|
||||
MetricsLogger.action(r.getLogMaker()
|
||||
|
||||
@@ -22,12 +22,15 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Person;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -72,6 +75,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
||||
private NotificationRecord mRecordMinCallNonInterruptive;
|
||||
private NotificationRecord mRecordMinCall;
|
||||
private NotificationRecord mRecordHighCall;
|
||||
private NotificationRecord mRecordHighCallStyle;
|
||||
private NotificationRecord mRecordEmail;
|
||||
private NotificationRecord mRecordInlineReply;
|
||||
private NotificationRecord mRecordSms;
|
||||
@@ -90,9 +94,12 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
||||
int userId = UserHandle.myUserId();
|
||||
|
||||
when(mContext.getResources()).thenReturn(getContext().getResources());
|
||||
when(mContext.getTheme()).thenReturn(getContext().getTheme());
|
||||
when(mContext.getContentResolver()).thenReturn(getContext().getContentResolver());
|
||||
when(mContext.getPackageManager()).thenReturn(mPm);
|
||||
when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
|
||||
when(mContext.getString(anyInt())).thenCallRealMethod();
|
||||
when(mContext.getColor(anyInt())).thenCallRealMethod();
|
||||
when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);
|
||||
final ApplicationInfo legacy = new ApplicationInfo();
|
||||
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
|
||||
@@ -137,6 +144,19 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
||||
new UserHandle(userId), "", 1999), getDefaultChannel());
|
||||
mRecordHighCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
|
||||
|
||||
Notification nHighCallStyle = new Notification.Builder(mContext, TEST_CHANNEL_ID)
|
||||
.setStyle(Notification.CallStyle.forOngoingCall(
|
||||
new Person.Builder().setName("caller").build(),
|
||||
mock(PendingIntent.class)
|
||||
))
|
||||
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
|
||||
.build();
|
||||
mRecordHighCallStyle = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
|
||||
callPkg, 1, "highCallStyle", callUid, callUid, nHighCallStyle,
|
||||
new UserHandle(userId), "", 2000), getDefaultChannel());
|
||||
mRecordHighCallStyle.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
|
||||
mRecordHighCallStyle.setInterruptive(true);
|
||||
|
||||
Notification n4 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
|
||||
.setStyle(new Notification.MessagingStyle("sender!")).build();
|
||||
mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
@@ -236,6 +256,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
||||
final List<NotificationRecord> expected = new ArrayList<>();
|
||||
expected.add(mRecordColorizedCall);
|
||||
expected.add(mRecordColorized);
|
||||
expected.add(mRecordHighCallStyle);
|
||||
expected.add(mRecordHighCall);
|
||||
expected.add(mRecordInlineReply);
|
||||
if (mRecordSms != null) {
|
||||
|
||||
Reference in New Issue
Block a user