Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +65 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,9 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.runTest import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.remedia.data.repository.mediaPipelineRepository import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.scene.shared.flag.SceneContainerFlag Loading Loading @@ -163,6 +166,68 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas assertThat(shouldShowEmptyShadeView).isFalse() } @Test @EnableSceneContainer fun shouldShowEmptyShadeView_dualShade_falseWhenNoNotifsWithMedia() = kosmos.runTest { enableDualShade() runCurrent() val hasActiveMedia by collectLastValue(mediaCarouselInteractor.hasActiveMedia) val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView.map { it.value }) // WHEN has no notifs but has media activeNotificationListRepository.setActiveNotifs(count = 0) val userMedia = MediaData(active = true) mediaPipelineRepository.addCurrentUserMediaEntry(userMedia) runCurrent() // THEN empty shade is not visible assertThat(hasActiveMedia).isTrue() assertThat(shouldShowEmptyShadeView).isFalse() // WHEN media is dismissed mediaPipelineRepository.addCurrentUserMediaEntry(userMedia.copy(active = false)) runCurrent() // THEN empty shade is visible assertThat(hasActiveMedia).isFalse() assertThat(shouldShowEmptyShadeView).isTrue() } @Test @EnableSceneContainer fun shouldShowEmptyShadeView_singleShade_trueWhenNoNotifsWithMedia() = kosmos.runTest { enableSingleShade() runCurrent() val hasActiveMedia by collectLastValue(mediaCarouselInteractor.hasActiveMedia) val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView.map { it.value }) // WHEN has no notifs but has media activeNotificationListRepository.setActiveNotifs(count = 0) val userMedia = MediaData(active = true) mediaPipelineRepository.addCurrentUserMediaEntry(userMedia) runCurrent() // THEN empty shade is visible assertThat(hasActiveMedia).isTrue() assertThat(shouldShowEmptyShadeView).isTrue() // WHEN media is dismissed mediaPipelineRepository.addCurrentUserMediaEntry(userMedia.copy(active = false)) runCurrent() // THEN empty shade is still visible assertThat(hasActiveMedia).isFalse() assertThat(shouldShowEmptyShadeView).isTrue() } @Test fun shouldShowEmptyShadeView_falseWhenQsExpandedDefault() = kosmos.runTest { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt +15 −11 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.SuppressLint import android.graphics.RectF import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Overlays Loading @@ -42,6 +43,7 @@ import com.android.systemui.util.kotlin.sample import com.android.systemui.util.ui.AnimatableEvent import com.android.systemui.util.ui.AnimatedValue import com.android.systemui.util.ui.toAnimatedValueFlow import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow Loading @@ -52,7 +54,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import javax.inject.Inject /** * ViewModel for the list of notifications, including child elements like the Clear all/Manage Loading @@ -74,6 +75,7 @@ constructor( activeNotificationsInteractor: ActiveNotificationsInteractor, notificationStackInteractor: NotificationStackInteractor, private val headsUpNotificationInteractor: HeadsUpNotificationInteractor, private val mediaCarouselInteractor: MediaCarouselInteractor, remoteInputInteractor: RemoteInputInteractor, shadeInteractor: ShadeInteractor, shadeModeInteractor: ShadeModeInteractor, Loading Loading @@ -111,23 +113,25 @@ constructor( val shouldShowEmptyShadeView: Flow<AnimatedValue<Boolean>> by lazy { combine( activeNotificationsInteractor.areAnyNotificationsPresent, mediaCarouselInteractor.hasActiveMedia, shadeInteractor.qsExpansion .map { it >= QS_EXPANSION_THRESHOLD } .distinctUntilChanged(), shadeModeInteractor.shadeMode.map { @Suppress("DEPRECATION") // to handle split shade it == ShadeMode.Split }, shadeModeInteractor.shadeMode, notificationStackInteractor.isShowingOnLockscreen, ) { hasNotifications, qsExpandedEnough, splitShade, isShowingOnLockscreen -> ) { hasNotifications, hasMedia, qsExpandedEnough, shadeMode, isShowingOnLockscreen -> val dualShade = shadeMode is ShadeMode.Dual val singleShade = shadeMode is ShadeMode.Single when { hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION // Hide the empty shade when QS is close to being full screen. We use this // instead of isQsFullscreen to avoid some flickering. qsExpandedEnough && !splitShade -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION // In Dual Shade, media is treated as a notification. Only show the empty shade // view when both notifications and media are absent. hasMedia && dualShade -> VisibilityChange.DISAPPEAR_WITH_ANIMATION // Hide the empty shade when QS is close to being full screen (in single shade). // We use this instead of isQsFullscreen to avoid some flickering. qsExpandedEnough && singleShade -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION // Do not show the empty shade if the lockscreen is visible (including AOD // b/228790482 and bouncer b/267060171), except if the shade is opened on // top. // b/228790482 and bouncer b/267060171), except if the shade is opened on top. isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION else -> VisibilityChange.APPEAR_WITH_ANIMATION } Loading packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.testDispatcher import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.domain.interactor.shadeModeInteractor Loading Loading @@ -47,6 +48,7 @@ val Kosmos.notificationListViewModel by Fixture { activeNotificationsInteractor = activeNotificationsInteractor, notificationStackInteractor = notificationStackInteractor, headsUpNotificationInteractor = headsUpNotificationInteractor, mediaCarouselInteractor = mediaCarouselInteractor, remoteInputInteractor = remoteInputInteractor, shadeInteractor = shadeInteractor, shadeModeInteractor = shadeModeInteractor, Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +65 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,9 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.runTest import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.remedia.data.repository.mediaPipelineRepository import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.scene.shared.flag.SceneContainerFlag Loading Loading @@ -163,6 +166,68 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas assertThat(shouldShowEmptyShadeView).isFalse() } @Test @EnableSceneContainer fun shouldShowEmptyShadeView_dualShade_falseWhenNoNotifsWithMedia() = kosmos.runTest { enableDualShade() runCurrent() val hasActiveMedia by collectLastValue(mediaCarouselInteractor.hasActiveMedia) val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView.map { it.value }) // WHEN has no notifs but has media activeNotificationListRepository.setActiveNotifs(count = 0) val userMedia = MediaData(active = true) mediaPipelineRepository.addCurrentUserMediaEntry(userMedia) runCurrent() // THEN empty shade is not visible assertThat(hasActiveMedia).isTrue() assertThat(shouldShowEmptyShadeView).isFalse() // WHEN media is dismissed mediaPipelineRepository.addCurrentUserMediaEntry(userMedia.copy(active = false)) runCurrent() // THEN empty shade is visible assertThat(hasActiveMedia).isFalse() assertThat(shouldShowEmptyShadeView).isTrue() } @Test @EnableSceneContainer fun shouldShowEmptyShadeView_singleShade_trueWhenNoNotifsWithMedia() = kosmos.runTest { enableSingleShade() runCurrent() val hasActiveMedia by collectLastValue(mediaCarouselInteractor.hasActiveMedia) val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView.map { it.value }) // WHEN has no notifs but has media activeNotificationListRepository.setActiveNotifs(count = 0) val userMedia = MediaData(active = true) mediaPipelineRepository.addCurrentUserMediaEntry(userMedia) runCurrent() // THEN empty shade is visible assertThat(hasActiveMedia).isTrue() assertThat(shouldShowEmptyShadeView).isTrue() // WHEN media is dismissed mediaPipelineRepository.addCurrentUserMediaEntry(userMedia.copy(active = false)) runCurrent() // THEN empty shade is still visible assertThat(hasActiveMedia).isFalse() assertThat(shouldShowEmptyShadeView).isTrue() } @Test fun shouldShowEmptyShadeView_falseWhenQsExpandedDefault() = kosmos.runTest { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt +15 −11 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.SuppressLint import android.graphics.RectF import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Overlays Loading @@ -42,6 +43,7 @@ import com.android.systemui.util.kotlin.sample import com.android.systemui.util.ui.AnimatableEvent import com.android.systemui.util.ui.AnimatedValue import com.android.systemui.util.ui.toAnimatedValueFlow import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow Loading @@ -52,7 +54,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import javax.inject.Inject /** * ViewModel for the list of notifications, including child elements like the Clear all/Manage Loading @@ -74,6 +75,7 @@ constructor( activeNotificationsInteractor: ActiveNotificationsInteractor, notificationStackInteractor: NotificationStackInteractor, private val headsUpNotificationInteractor: HeadsUpNotificationInteractor, private val mediaCarouselInteractor: MediaCarouselInteractor, remoteInputInteractor: RemoteInputInteractor, shadeInteractor: ShadeInteractor, shadeModeInteractor: ShadeModeInteractor, Loading Loading @@ -111,23 +113,25 @@ constructor( val shouldShowEmptyShadeView: Flow<AnimatedValue<Boolean>> by lazy { combine( activeNotificationsInteractor.areAnyNotificationsPresent, mediaCarouselInteractor.hasActiveMedia, shadeInteractor.qsExpansion .map { it >= QS_EXPANSION_THRESHOLD } .distinctUntilChanged(), shadeModeInteractor.shadeMode.map { @Suppress("DEPRECATION") // to handle split shade it == ShadeMode.Split }, shadeModeInteractor.shadeMode, notificationStackInteractor.isShowingOnLockscreen, ) { hasNotifications, qsExpandedEnough, splitShade, isShowingOnLockscreen -> ) { hasNotifications, hasMedia, qsExpandedEnough, shadeMode, isShowingOnLockscreen -> val dualShade = shadeMode is ShadeMode.Dual val singleShade = shadeMode is ShadeMode.Single when { hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION // Hide the empty shade when QS is close to being full screen. We use this // instead of isQsFullscreen to avoid some flickering. qsExpandedEnough && !splitShade -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION // In Dual Shade, media is treated as a notification. Only show the empty shade // view when both notifications and media are absent. hasMedia && dualShade -> VisibilityChange.DISAPPEAR_WITH_ANIMATION // Hide the empty shade when QS is close to being full screen (in single shade). // We use this instead of isQsFullscreen to avoid some flickering. qsExpandedEnough && singleShade -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION // Do not show the empty shade if the lockscreen is visible (including AOD // b/228790482 and bouncer b/267060171), except if the shade is opened on // top. // b/228790482 and bouncer b/267060171), except if the shade is opened on top. isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION else -> VisibilityChange.APPEAR_WITH_ANIMATION } Loading
packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.testDispatcher import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.domain.interactor.shadeModeInteractor Loading Loading @@ -47,6 +48,7 @@ val Kosmos.notificationListViewModel by Fixture { activeNotificationsInteractor = activeNotificationsInteractor, notificationStackInteractor = notificationStackInteractor, headsUpNotificationInteractor = headsUpNotificationInteractor, mediaCarouselInteractor = mediaCarouselInteractor, remoteInputInteractor = remoteInputInteractor, shadeInteractor = shadeInteractor, shadeModeInteractor = shadeModeInteractor, Loading