Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 819b290c authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge "Fix CallStyle ranking & ensure correct features are used." into sc-dev

parents 39bcc47c a91713d8
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -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
}
+70 −0
Original line number Diff line number Diff line
@@ -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,
+12 −6
Original line number Diff line number Diff line
@@ -147,15 +147,16 @@ public class NotificationComparator
    }

    private boolean isImportantOngoing(NotificationRecord record) {
        if (!isOngoing(record)) {
        if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
            return false;
        }

        if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
        if (isCallStyle(record)) {
            return true;
        }
        if (!isOngoing(record)) {
            return false;
        }

        return isCall(record) || isMediaNotification(record);
        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 =
+11 −0
Original line number Diff line number Diff line
@@ -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()
+21 −0
Original line number Diff line number Diff line
@@ -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) {