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

Commit 3ea12167 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

[SB][Notifs] Add PromotedNotificationsProvider to auto-promote notifs.

PromotedNotificationsProvider is an interface with a single method,
`shouldPromote(entry: NotificationEntry): Boolean` that lets us
tell which notifications should be promoted. In AOSP, it just promotes
notifications with the FLAG_PROMOTED_ONGOING flag set.

Bug: 364653005
Flag: android.app.ui_rich_ongoing
Test: atest ActiveNotificationsInteractorTest
PromotedNotificationsProviderTest

Change-Id: I1913b65c79eb5a668a2f34e4ed2706595aed97f1
parent 73de83c7
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -467,6 +467,15 @@ flag {
    bug: "361346412"
}

flag {
    name: "status_bar_notification_chips_test"
    namespace: "systemui"
    description: "Flag to enable certain features that let us test the status bar notification "
        "chips with teamfooders. This flag should *never* be released to trunkfood or nextfood."
    bug: "361346412"
}


flag {
    name: "compose_bouncer"
    namespace: "systemui"
+36 −6
Original line number Diff line number Diff line
@@ -66,18 +66,34 @@ class NotifChipsViewModelTest : SysuiTestCase() {
        testScope.runTest {
            val latest by collectLastValue(underTest.chips)

            setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = null)))
            setNotifs(
                listOf(
                    activeNotificationModel(
                        key = "notif",
                        statusBarChipIcon = null,
                        isPromoted = true,
                    )
                )
            )

            assertThat(latest).isEmpty()
        }

    @Test
    fun chips_oneNotif_statusBarIconViewMatches() =
    fun chips_onePromotedNotif_statusBarIconViewMatches() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chips)

            val icon = mock<StatusBarIconView>()
            setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
            setNotifs(
                listOf(
                    activeNotificationModel(
                        key = "notif",
                        statusBarChipIcon = icon,
                        isPromoted = true,
                    )
                )
            )

            assertThat(latest).hasSize(1)
            val chip = latest!![0]
@@ -86,7 +102,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
        }

    @Test
    fun chips_twoNotifs_twoChips() =
    fun chips_onlyForPromotedNotifs() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chips)

@@ -94,8 +110,21 @@ class NotifChipsViewModelTest : SysuiTestCase() {
            val secondIcon = mock<StatusBarIconView>()
            setNotifs(
                listOf(
                    activeNotificationModel(key = "notif1", statusBarChipIcon = firstIcon),
                    activeNotificationModel(key = "notif2", statusBarChipIcon = secondIcon),
                    activeNotificationModel(
                        key = "notif1",
                        statusBarChipIcon = firstIcon,
                        isPromoted = true,
                    ),
                    activeNotificationModel(
                        key = "notif2",
                        statusBarChipIcon = secondIcon,
                        isPromoted = true,
                    ),
                    activeNotificationModel(
                        key = "notif3",
                        statusBarChipIcon = mock<StatusBarIconView>(),
                        isPromoted = false,
                    ),
                )
            )

@@ -118,6 +147,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
                    activeNotificationModel(
                        key = "clickTest",
                        statusBarChipIcon = mock<StatusBarIconView>(),
                        isPromoted = true,
                    )
                )
            )
+46 −12
Original line number Diff line number Diff line
@@ -293,19 +293,27 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
        }

    @Test
    fun chips_singleNotifChip_primaryIsNotifSecondaryIsHidden() =
    fun chips_singlePromotedNotif_primaryIsNotifSecondaryIsHidden() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chips)

            val icon = mock<StatusBarIconView>()
            setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
            setNotifs(
                listOf(
                    activeNotificationModel(
                        key = "notif",
                        statusBarChipIcon = icon,
                        isPromoted = true,
                    )
                )
            )

            assertIsNotifChip(latest!!.primary, icon)
            assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
        }

    @Test
    fun chips_twoNotifChips_primaryAndSecondaryAreNotifsInOrder() =
    fun chips_twoPromotedNotifs_primaryAndSecondaryAreNotifsInOrder() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chips)

@@ -313,8 +321,16 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
            val secondIcon = mock<StatusBarIconView>()
            setNotifs(
                listOf(
                    activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
                    activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
                    activeNotificationModel(
                        key = "firstNotif",
                        statusBarChipIcon = firstIcon,
                        isPromoted = true,
                    ),
                    activeNotificationModel(
                        key = "secondNotif",
                        statusBarChipIcon = secondIcon,
                        isPromoted = true,
                    ),
                )
            )

@@ -323,7 +339,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
        }

    @Test
    fun chips_threeNotifChips_topTwoShown() =
    fun chips_threePromotedNotifs_topTwoShown() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chips)

@@ -332,9 +348,21 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
            val thirdIcon = mock<StatusBarIconView>()
            setNotifs(
                listOf(
                    activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
                    activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
                    activeNotificationModel(key = "thirdNotif", statusBarChipIcon = thirdIcon),
                    activeNotificationModel(
                        key = "firstNotif",
                        statusBarChipIcon = firstIcon,
                        isPromoted = true,
                    ),
                    activeNotificationModel(
                        key = "secondNotif",
                        statusBarChipIcon = secondIcon,
                        isPromoted = true,
                    ),
                    activeNotificationModel(
                        key = "thirdNotif",
                        statusBarChipIcon = thirdIcon,
                        isPromoted = true,
                    ),
                )
            )

@@ -343,7 +371,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
        }

    @Test
    fun chips_callAndNotifs_primaryIsCallSecondaryIsNotif() =
    fun chips_callAndPromotedNotifs_primaryIsCallSecondaryIsNotif() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chips)

@@ -351,10 +379,15 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
            val firstIcon = mock<StatusBarIconView>()
            setNotifs(
                listOf(
                    activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
                    activeNotificationModel(
                        key = "firstNotif",
                        statusBarChipIcon = firstIcon,
                        isPromoted = true,
                    ),
                    activeNotificationModel(
                        key = "secondNotif",
                        statusBarChipIcon = mock<StatusBarIconView>(),
                        isPromoted = true,
                    ),
                )
            )
@@ -364,7 +397,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
        }

    @Test
    fun chips_screenRecordAndCallAndNotifs_notifsNotShown() =
    fun chips_screenRecordAndCallAndPromotedNotifs_notifsNotShown() =
        testScope.runTest {
            val latest by collectLastValue(underTest.chips)

@@ -375,6 +408,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
                    activeNotificationModel(
                        key = "notif",
                        statusBarChipIcon = mock<StatusBarIconView>(),
                        isPromoted = true,
                    )
                )
            )
+63 −13
Original line number Diff line number Diff line
@@ -18,11 +18,14 @@

package com.android.systemui.statusbar.notification.domain.interactor

import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
@@ -44,7 +47,7 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
    private val testScope = kosmos.testScope
    private val activeNotificationListRepository = kosmos.activeNotificationListRepository

    private val underTest = kosmos.activeNotificationsInteractor
    private val underTest by lazy { kosmos.activeNotificationsInteractor }

    @Test
    fun testAllNotificationsCount() =
@@ -65,14 +68,8 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {

            val normalNotifs =
                listOf(
                    activeNotificationModel(
                        key = "notif1",
                        callType = CallType.None,
                    ),
                    activeNotificationModel(
                        key = "notif2",
                        callType = CallType.None,
                    )
                    activeNotificationModel(key = "notif1", callType = CallType.None),
                    activeNotificationModel(key = "notif2", callType = CallType.None),
                )

            activeNotificationListRepository.activeNotifications.value =
@@ -129,10 +126,7 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
            val latest by collectLastValue(underTest.ongoingCallNotification)

            val ongoingNotif =
                activeNotificationModel(
                    key = "ongoingNotif",
                    callType = CallType.Ongoing,
                )
                activeNotificationModel(key = "ongoingNotif", callType = CallType.Ongoing)

            activeNotificationListRepository.activeNotifications.value =
                ActiveNotificationsStore.Builder()
@@ -169,6 +163,62 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
            assertThat(latest).isEqualTo(earlierOngoingNotif)
        }

    @Test
    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
    fun promotedOngoingNotifications_flagOff_empty() =
        testScope.runTest {
            val latest by collectLastValue(underTest.promotedOngoingNotifications)

            val promoted1 = activeNotificationModel(key = "notif1", isPromoted = true)
            val notPromoted2 = activeNotificationModel(key = "notif2", isPromoted = false)

            activeNotificationListRepository.activeNotifications.value =
                ActiveNotificationsStore.Builder()
                    .apply { addIndividualNotif(promoted1) }
                    .apply { addIndividualNotif(notPromoted2) }
                    .build()

            assertThat(latest!!).isEmpty()
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun promotedOngoingNotifications_nonePromoted_empty() =
        testScope.runTest {
            val latest by collectLastValue(underTest.promotedOngoingNotifications)

            activeNotificationListRepository.activeNotifications.value =
                ActiveNotificationsStore.Builder()
                    .apply { activeNotificationModel(key = "notif1", isPromoted = false) }
                    .apply { activeNotificationModel(key = "notif2", isPromoted = false) }
                    .apply { activeNotificationModel(key = "notif3", isPromoted = false) }
                    .build()

            assertThat(latest!!).isEmpty()
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun promotedOngoingNotifications_somePromoted_hasOnlyPromoted() =
        testScope.runTest {
            val latest by collectLastValue(underTest.promotedOngoingNotifications)

            val promoted1 = activeNotificationModel(key = "notif1", isPromoted = true)
            val notPromoted2 = activeNotificationModel(key = "notif2", isPromoted = false)
            val notPromoted3 = activeNotificationModel(key = "notif3", isPromoted = false)
            val promoted4 = activeNotificationModel(key = "notif4", isPromoted = true)

            activeNotificationListRepository.activeNotifications.value =
                ActiveNotificationsStore.Builder()
                    .apply { addIndividualNotif(promoted1) }
                    .apply { addIndividualNotif(notPromoted2) }
                    .apply { addIndividualNotif(notPromoted3) }
                    .apply { addIndividualNotif(promoted4) }
                    .build()

            assertThat(latest!!).containsExactly(promoted1, promoted4)
        }

    @Test
    fun areAnyNotificationsPresent_isTrue() =
        testScope.runTest {
+60 −31
Original line number Diff line number Diff line
@@ -16,22 +16,25 @@
package com.android.systemui.statusbar.notification.domain.interactor

import android.app.Notification
import android.os.Bundle
import android.app.Notification.FLAG_PROMOTED_ONGOING
import android.platform.test.annotations.EnableFlags
import android.service.notification.StatusBarNotification
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
import com.android.systemui.statusbar.notification.promoted.promotedNotificationsProvider
import com.android.systemui.statusbar.notification.shared.byKey
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,16 +42,16 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class RenderNotificationsListInteractorTest : SysuiTestCase() {
    private val backgroundDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(backgroundDispatcher)
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope

    private val notifsRepository = ActiveNotificationListRepository()
    private val notifsInteractor =
        ActiveNotificationsInteractor(notifsRepository, backgroundDispatcher)
    private val notifsRepository = kosmos.activeNotificationListRepository
    private val notifsInteractor = kosmos.activeNotificationsInteractor
    private val underTest =
        RenderNotificationListInteractor(
            notifsRepository,
            sectionStyleProvider = mock(),
            promotedNotificationsProvider = kosmos.promotedNotificationsProvider,
        )

    @Test
@@ -85,12 +88,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {

            assertThat(ranks)
                .containsExactlyEntriesIn(
                    mapOf(
                        "single" to 0,
                        "summary" to 1,
                        "child0" to 2,
                        "child1" to 3,
                    )
                    mapOf("single" to 0, "summary" to 1, "child0" to 2, "child1" to 3)
                )
        }

@@ -126,25 +124,43 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {

            assertThat(actual).containsAtLeastEntriesIn(expected)
        }

    @Test
    @EnableFlags(PromotedNotificationUi.FLAG_NAME)
    fun setRenderList_setsPromotionStatus() =
        testScope.runTest {
            val actual by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)

            val notPromoted1 = mockNotificationEntry("key1", flag = null)
            val promoted2 = mockNotificationEntry("key2", flag = FLAG_PROMOTED_ONGOING)

            underTest.setRenderedList(listOf(notPromoted1, promoted2))

            assertThat(actual!!.size).isEqualTo(2)

            val first = actual!![0]
            assertThat(first.key).isEqualTo("key1")
            assertThat(first.isPromoted).isFalse()

            val second = actual!![1]
            assertThat(second.key).isEqualTo("key2")
            assertThat(second.isPromoted).isTrue()
        }

private fun mockGroupEntry(
    private fun mockNotificationEntry(
        key: String,
    summary: NotificationEntry?,
    children: List<NotificationEntry>,
): GroupEntry {
    return mock<GroupEntry> {
        whenever(this.key).thenReturn(key)
        whenever(this.summary).thenReturn(summary)
        whenever(this.children).thenReturn(children)
    }
        rank: Int = 0,
        flag: Int? = null,
    ): NotificationEntry {
        val nBuilder = Notification.Builder(context, "a")
        if (flag != null) {
            nBuilder.setFlag(flag, true)
        }
        val notification = nBuilder.build()

private fun mockNotificationEntry(key: String, rank: Int = 0): NotificationEntry {
    val mockNotification = mock<Notification> { this.extras = Bundle() }
        val mockSbn =
            mock<StatusBarNotification>() {
            whenever(notification).thenReturn(mockNotification)
                whenever(this.notification).thenReturn(notification)
                whenever(packageName).thenReturn("com.android")
            }
        return mock<NotificationEntry> {
@@ -155,3 +171,16 @@ private fun mockNotificationEntry(key: String, rank: Int = 0): NotificationEntry
            whenever(this.sbn).thenReturn(mockSbn)
        }
    }
}

private fun mockGroupEntry(
    key: String,
    summary: NotificationEntry?,
    children: List<NotificationEntry>,
): GroupEntry {
    return mock<GroupEntry> {
        whenever(this.key).thenReturn(key)
        whenever(this.summary).thenReturn(summary)
        whenever(this.children).thenReturn(children)
    }
}
Loading