diff options
15 files changed, 418 insertions, 68 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index b0bd5b71031c..3d6552248658 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -468,6 +468,15 @@ flag { } 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 6e190965d08b..32f4164d509f 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 @@ 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, ) ) ) 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 b12d7c57e1fd..25d5ce50e03f 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 @@ 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, ) ) ) 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 9f40f60db45f..99bda856818e 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 @@ 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() @@ -170,6 +164,62 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() { } @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 572a0c1b1869..183f9016a23b 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 @@ 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,6 +124,53 @@ 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 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 @@ private fun mockGroupEntry( 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 000000000000..a9dbe63e8f07 --- /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 925d4a588f09..4c2512988f4d 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 @@ import dagger.Module * 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 697a6ce52ba9..cff5bef9fe69 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 @@ constructor( // 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 10084517ec19..23da90d426c7 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.NotificationEntry 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 @@ class RenderNotificationListInteractor 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 @@ constructor( 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 @@ constructor( 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 @@ private class ActiveNotificationsStoreBuilder( existingModels.createOrReuse( key = entry.key, summary = summaryModel, - children = childModels + children = childModels, ) ) } @@ -141,6 +155,7 @@ private class ActiveNotificationsStoreBuilder( 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 @@ private fun ActiveNotificationsStore.createOrReuse( key: String, groupKey: String?, whenTime: Long, + isPromoted: Boolean, isAmbient: Boolean, isRowDismissed: Boolean, isSilent: Boolean, @@ -189,6 +205,7 @@ private fun ActiveNotificationsStore.createOrReuse( key = key, groupKey = groupKey, whenTime = whenTime, + isPromoted = isPromoted, isAmbient = isAmbient, isRowDismissed = isRowDismissed, isSilent = isSilent, @@ -212,6 +229,7 @@ private fun ActiveNotificationsStore.createOrReuse( key = key, groupKey = groupKey, whenTime = whenTime, + isPromoted = isPromoted, isAmbient = isAmbient, isRowDismissed = isRowDismissed, isSilent = isSilent, @@ -236,6 +254,7 @@ private fun ActiveNotificationModel.isCurrent( key: String, groupKey: String?, whenTime: Long, + isPromoted: Boolean, isAmbient: Boolean, isRowDismissed: Boolean, isSilent: Boolean, @@ -258,6 +277,7 @@ private fun ActiveNotificationModel.isCurrent( 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 000000000000..4be12bd44a29 --- /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 000000000000..691dc6f5ccac --- /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 cf19938aa533..19a92a2230ba 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 @@ data class ActiveNotificationModel( 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 76bdc0de3d7b..32c582f79ed7 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 @@ fun activeNotificationModel( key: String, groupKey: String? = null, whenTime: Long = 0L, + isPromoted: Boolean = false, isAmbient: Boolean = false, isRowDismissed: Boolean = false, isSilent: Boolean = false, @@ -50,6 +51,7 @@ fun activeNotificationModel( 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 f7acae9846df..067193fb7aa9 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 @@ package com.android.systemui.statusbar.notification.domain.interactor 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 000000000000..a7aa0b41a7aa --- /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() } |