[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
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index b0bd5b7..3d65522 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -468,6 +468,15 @@
}
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"
description: "Use the new compose bouncer in SystemUI"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 6e19096..32f4164 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -66,18 +66,34 @@
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 @@
}
@Test
- fun chips_twoNotifs_twoChips() =
+ fun chips_onlyForPromotedNotifs() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
@@ -94,8 +110,21 @@
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 @@
activeNotificationModel(
key = "clickTest",
statusBarChipIcon = mock<StatusBarIconView>(),
+ isPromoted = true,
)
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index b12d7c5..25d5ce5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -293,19 +293,27 @@
}
@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 @@
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 @@
}
@Test
- fun chips_threeNotifChips_topTwoShown() =
+ fun chips_threePromotedNotifs_topTwoShown() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
@@ -332,9 +348,21 @@
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 @@
}
@Test
- fun chips_callAndNotifs_primaryIsCallSecondaryIsNotif() =
+ fun chips_callAndPromotedNotifs_primaryIsCallSecondaryIsNotif() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
@@ -351,10 +379,15 @@
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 @@
}
@Test
- fun chips_screenRecordAndCallAndNotifs_notifsNotShown() =
+ fun chips_screenRecordAndCallAndPromotedNotifs_notifsNotShown() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
@@ -375,6 +408,7 @@
activeNotificationModel(
key = "notif",
statusBarChipIcon = mock<StatusBarIconView>(),
+ isPromoted = true,
)
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
index 9f40f60..99bda85 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
@@ -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 @@
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 @@
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 @@
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()
@@ -170,6 +164,62 @@
}
@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 {
val areAnyNotificationsPresent by collectLastValue(underTest.areAnyNotificationsPresent)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index 572a0c1..183f901 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -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 @@
@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 @@
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,6 +124,53 @@
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 mockNotificationEntry(
+ key: String,
+ rank: Int = 0,
+ flag: Int? = null,
+ ): NotificationEntry {
+ val nBuilder = Notification.Builder(context, "a")
+ if (flag != null) {
+ nBuilder.setFlag(flag, true)
+ }
+ val notification = nBuilder.build()
+
+ val mockSbn =
+ mock<StatusBarNotification>() {
+ whenever(this.notification).thenReturn(notification)
+ whenever(packageName).thenReturn("com.android")
+ }
+ return mock<NotificationEntry> {
+ whenever(this.key).thenReturn(key)
+ whenever(this.icons).thenReturn(mock())
+ whenever(this.representativeEntry).thenReturn(this)
+ whenever(this.ranking).thenReturn(RankingBuilder().setRank(rank).build())
+ whenever(this.sbn).thenReturn(mockSbn)
+ }
+ }
}
private fun mockGroupEntry(
@@ -139,19 +184,3 @@
whenever(this.children).thenReturn(children)
}
}
-
-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(packageName).thenReturn("com.android")
- }
- return mock<NotificationEntry> {
- whenever(this.key).thenReturn(key)
- whenever(this.icons).thenReturn(mock())
- whenever(this.representativeEntry).thenReturn(this)
- whenever(this.ranking).thenReturn(RankingBuilder().setRank(rank).build())
- whenever(this.sbn).thenReturn(mockSbn)
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderTest.kt
new file mode 100644
index 0000000..a9dbe63
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 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.systemui.statusbar.notification.promoted
+
+import android.app.Notification
+import android.app.Notification.FLAG_PROMOTED_ONGOING
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+
+@SmallTest
+class PromotedNotificationsProviderTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ private val underTest = kosmos.promotedNotificationsProvider
+
+ @Test
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun shouldPromote_uiFlagOff_false() {
+ val entry = createNotification(FLAG_PROMOTED_ONGOING)
+
+ assertThat(underTest.shouldPromote(entry)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun shouldPromote_uiFlagOn_notifDoesNotHaveFlag_false() {
+ val entry = createNotification(flag = null)
+
+ assertThat(underTest.shouldPromote(entry)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun shouldPromote_uiFlagOn_notifHasFlag_true() {
+ val entry = createNotification(FLAG_PROMOTED_ONGOING)
+
+ assertThat(underTest.shouldPromote(entry)).isTrue()
+ }
+
+ private fun createNotification(flag: Int? = null): NotificationEntry {
+ val n = Notification.Builder(context, "a")
+ if (flag != null) {
+ n.setFlag(flag, true)
+ }
+
+ return NotificationEntryBuilder().setNotification(n.build()).build()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt
index 925d4a5..4c25129 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.dagger
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsModule
import com.android.systemui.statusbar.notification.row.NotificationRowModule
import dagger.Module
@@ -23,5 +24,12 @@
* A module that includes the standard notifications classes that most SysUI variants need. Variants
* are free to not include this module and instead write a custom notifications module.
*/
-@Module(includes = [NotificationsModule::class, NotificationRowModule::class])
+@Module(
+ includes =
+ [
+ NotificationsModule::class,
+ NotificationRowModule::class,
+ PromotedNotificationsModule::class,
+ ]
+)
object ReferenceNotificationsModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 697a6ce..cff5bef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -83,6 +83,9 @@
// TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow
// instead of being separate.
topLevelRepresentativeNotifications
+ .map { notifs -> notifs.filter { it.isPromoted } }
+ .distinctUntilChanged()
+ .flowOn(backgroundDispatcher)
} else {
flowOf(emptyList())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index 1008451..23da90d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider
import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -50,6 +51,7 @@
constructor(
private val repository: ActiveNotificationListRepository,
private val sectionStyleProvider: SectionStyleProvider,
+ private val promotedNotificationsProvider: PromotedNotificationsProvider,
) {
/**
* Sets the current list of rendered notification entries as displayed in the notification list.
@@ -57,7 +59,11 @@
fun setRenderedList(entries: List<ListEntry>) {
traceSection("RenderNotificationListInteractor.setRenderedList") {
repository.activeNotifications.update { existingModels ->
- buildActiveNotificationsStore(existingModels, sectionStyleProvider) {
+ buildActiveNotificationsStore(
+ existingModels,
+ sectionStyleProvider,
+ promotedNotificationsProvider,
+ ) {
entries.forEach(::addListEntry)
setRankingsMap(entries)
}
@@ -69,13 +75,21 @@
private fun buildActiveNotificationsStore(
existingModels: ActiveNotificationsStore,
sectionStyleProvider: SectionStyleProvider,
- block: ActiveNotificationsStoreBuilder.() -> Unit
+ promotedNotificationsProvider: PromotedNotificationsProvider,
+ block: ActiveNotificationsStoreBuilder.() -> Unit,
): ActiveNotificationsStore =
- ActiveNotificationsStoreBuilder(existingModels, sectionStyleProvider).apply(block).build()
+ ActiveNotificationsStoreBuilder(
+ existingModels,
+ sectionStyleProvider,
+ promotedNotificationsProvider,
+ )
+ .apply(block)
+ .build()
private class ActiveNotificationsStoreBuilder(
private val existingModels: ActiveNotificationsStore,
private val sectionStyleProvider: SectionStyleProvider,
+ private val promotedNotificationsProvider: PromotedNotificationsProvider,
) {
private val builder = ActiveNotificationsStore.Builder()
@@ -96,7 +110,7 @@
existingModels.createOrReuse(
key = entry.key,
summary = summaryModel,
- children = childModels
+ children = childModels,
)
)
}
@@ -141,6 +155,7 @@
key = key,
groupKey = sbn.groupKey,
whenTime = sbn.notification.`when`,
+ isPromoted = promotedNotificationsProvider.shouldPromote(this),
isAmbient = sectionStyleProvider.isMinimized(this),
isRowDismissed = isRowDismissed,
isSilent = sectionStyleProvider.isSilent(this),
@@ -166,6 +181,7 @@
key: String,
groupKey: String?,
whenTime: Long,
+ isPromoted: Boolean,
isAmbient: Boolean,
isRowDismissed: Boolean,
isSilent: Boolean,
@@ -189,6 +205,7 @@
key = key,
groupKey = groupKey,
whenTime = whenTime,
+ isPromoted = isPromoted,
isAmbient = isAmbient,
isRowDismissed = isRowDismissed,
isSilent = isSilent,
@@ -212,6 +229,7 @@
key = key,
groupKey = groupKey,
whenTime = whenTime,
+ isPromoted = isPromoted,
isAmbient = isAmbient,
isRowDismissed = isRowDismissed,
isSilent = isSilent,
@@ -236,6 +254,7 @@
key: String,
groupKey: String?,
whenTime: Long,
+ isPromoted: Boolean,
isAmbient: Boolean,
isRowDismissed: Boolean,
isSilent: Boolean,
@@ -258,6 +277,7 @@
key != this.key -> false
groupKey != this.groupKey -> false
whenTime != this.whenTime -> false
+ isPromoted != this.isPromoted -> false
isAmbient != this.isAmbient -> false
isRowDismissed != this.isRowDismissed -> false
isSilent != this.isSilent -> false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsModule.kt
new file mode 100644
index 0000000..4be12bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsModule.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 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.systemui.statusbar.notification.promoted
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class PromotedNotificationsModule {
+ @Binds
+ @SysUISingleton
+ abstract fun bindPromotedNotificationsProvider(
+ impl: PromotedNotificationsProviderImpl
+ ): PromotedNotificationsProvider
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt
new file mode 100644
index 0000000..691dc6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 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.systemui.statusbar.notification.promoted
+
+import android.app.Notification.FLAG_PROMOTED_ONGOING
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import javax.inject.Inject
+
+/** A provider for making decisions on which notifications should be promoted. */
+interface PromotedNotificationsProvider {
+ /** Returns true if the given notification should be promoted and false otherwise. */
+ fun shouldPromote(entry: NotificationEntry): Boolean
+}
+
+@SysUISingleton
+open class PromotedNotificationsProviderImpl @Inject constructor() : PromotedNotificationsProvider {
+ override fun shouldPromote(entry: NotificationEntry): Boolean {
+ if (!PromotedNotificationUi.isEnabled) {
+ return false
+ }
+ return (entry.sbn.notification.flags and FLAG_PROMOTED_ONGOING) != 0
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index cf19938..19a92a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -36,6 +36,8 @@
val groupKey: String?,
/** When this notification was posted. */
val whenTime: Long,
+ /** True if this notification should be promoted and false otherwise. */
+ val isPromoted: Boolean,
/** Is this entry in the ambient / minimized section (lowest priority)? */
val isAmbient: Boolean,
/**
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
index 76bdc0d..32c582f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
@@ -28,6 +28,7 @@
key: String,
groupKey: String? = null,
whenTime: Long = 0L,
+ isPromoted: Boolean = false,
isAmbient: Boolean = false,
isRowDismissed: Boolean = false,
isSilent: Boolean = false,
@@ -50,6 +51,7 @@
key = key,
groupKey = groupKey,
whenTime = whenTime,
+ isPromoted = isPromoted,
isAmbient = isAmbient,
isRowDismissed = isRowDismissed,
isSilent = isSilent,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
index f7acae9..067193f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
@@ -19,8 +19,13 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.notification.collection.provider.sectionStyleProvider
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.promoted.promotedNotificationsProvider
val Kosmos.renderNotificationListInteractor by
Kosmos.Fixture {
- RenderNotificationListInteractor(activeNotificationListRepository, sectionStyleProvider)
+ RenderNotificationListInteractor(
+ activeNotificationListRepository,
+ sectionStyleProvider,
+ promotedNotificationsProvider,
+ )
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt
new file mode 100644
index 0000000..a7aa0b4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 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.systemui.statusbar.notification.promoted
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.promotedNotificationsProvider by Kosmos.Fixture { PromotedNotificationsProviderImpl() }