Loading packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt +31 −5 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.layoutId import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.dimensionResource import com.android.compose.animation.scene.ContentScope import com.android.compose.animation.scene.ElementKey Loading @@ -34,6 +34,13 @@ import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.media.controls.ui.composable.MediaCarousel import com.android.systemui.media.controls.ui.composable.isLandscape import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.controls.ui.view.MediaHostState.Companion.COLLAPSED import com.android.systemui.media.controls.ui.view.MediaHostState.Companion.EXPANDED import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel import com.android.systemui.res.R Loading @@ -42,10 +49,11 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.ui.composable.OverlayShade import com.android.systemui.shade.ui.composable.OverlayShadeHeader import com.android.systemui.shade.ui.composable.SingleShadeMeasurePolicy import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView import com.android.systemui.util.Utils import dagger.Lazy import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.flow.Flow @SysUISingleton Loading @@ -58,6 +66,8 @@ constructor( private val stackScrollView: Lazy<NotificationScrollView>, private val clockSection: DefaultClockSection, private val keyguardClockViewModel: KeyguardClockViewModel, private val mediaCarouselController: MediaCarouselController, @Named(QUICK_QS_PANEL) private val mediaHost: Lazy<MediaHost>, ) : Overlay { override val key = Overlays.NotificationsShade Loading @@ -84,6 +94,11 @@ constructor( viewModel.notificationsPlaceholderViewModelFactory.create() } val usingCollapsedLandscapeMedia = Utils.useCollapsedMediaInLandscape(LocalResources.current) mediaHost.get().expansion = if (usingCollapsedLandscapeMedia && isLandscape()) COLLAPSED else EXPANDED OverlayShade( panelElement = NotificationsShade.Elements.Panel, alignmentOnWideScreens = Alignment.TopStart, Loading @@ -96,9 +111,7 @@ constructor( } OverlayShadeHeader( viewModel = headerViewModel, modifier = Modifier.element(NotificationsShade.Elements.StatusBar) .layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader), modifier = Modifier.element(NotificationsShade.Elements.StatusBar), ) }, ) { Loading @@ -116,6 +129,19 @@ constructor( } } MediaCarousel( isVisible = viewModel.showMedia, mediaHost = mediaHost.get(), carouselController = mediaCarouselController, usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia, modifier = Modifier.padding( top = notificationStackPadding, start = notificationStackPadding, end = notificationStackPadding, ), ) NotificationScrollingStack( shadeSession = shadeSession, stackScrollView = stackScrollView.get(), Loading packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt +37 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.notifications.ui.viewmodel import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest Loading @@ -28,6 +29,8 @@ import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.media.controls.data.repository.mediaFilterRepository import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest import com.android.systemui.power.domain.interactor.powerInteractor Loading @@ -39,10 +42,13 @@ import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.domain.interactor.enableDualShade import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.update import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest Loading @@ -50,6 +56,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper Loading Loading @@ -155,6 +162,36 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() { assertThat(underTest.showClock).isFalse() } @Test fun showMedia_activeMedia_true() = testScope.runTest { kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true)) runCurrent() assertThat(underTest.showMedia).isTrue() } @Test fun showMedia_noActiveMedia_false() = testScope.runTest { kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false)) runCurrent() assertThat(underTest.showMedia).isFalse() } @Test fun showMedia_qsDisabled_false() = testScope.runTest { kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true)) kosmos.fakeDisableFlagsRepository.disableFlags.update { it.copy(disable2 = DISABLE2_QUICK_SETTINGS) } runCurrent() assertThat(underTest.showMedia).isFalse() } private fun TestScope.lockDevice() { val currentScene by collectLastValue(sceneInteractor.currentScene) kosmos.powerInteractor.setAsleepForTest() Loading packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt +22 −0 Original line number Diff line number Diff line Loading @@ -20,12 +20,15 @@ import androidx.compose.runtime.getValue import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.awaitCancellation Loading @@ -33,6 +36,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flowOf /** * Models UI state used to render the content of the notifications shade overlay. Loading @@ -47,6 +51,8 @@ constructor( val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory, val sceneInteractor: SceneInteractor, private val shadeInteractor: ShadeInteractor, disableFlagsInteractor: DisableFlagsInteractor, mediaCarouselInteractor: MediaCarouselInteractor, activeNotificationsInteractor: ActiveNotificationsInteractor, ) : ExclusiveActivatable() { Loading @@ -69,6 +75,22 @@ constructor( ), ) val showMedia: Boolean by hydrator.hydratedStateOf( traceName = "showMedia", initialValue = disableFlagsInteractor.disableFlags.value.isQuickSettingsEnabled() && mediaCarouselInteractor.hasActiveMediaOrRecommendation.value, source = disableFlagsInteractor.disableFlags.flatMapLatestConflated { if (it.isQuickSettingsEnabled()) { mediaCarouselInteractor.hasActiveMediaOrRecommendation } else { flowOf(false) } }, ) override suspend fun onActivated(): Nothing { coroutineScope { launch { hydrator.activate() } Loading packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt +4 −0 Original line number Diff line number Diff line Loading @@ -18,9 +18,11 @@ package com.android.systemui.shade.ui.viewmodel import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory Loading @@ -31,6 +33,8 @@ val Kosmos.notificationsShadeOverlayContentViewModel: notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory, sceneInteractor = sceneInteractor, shadeInteractor = shadeInteractor, disableFlagsInteractor = disableFlagsInteractor, mediaCarouselInteractor = mediaCarouselInteractor, activeNotificationsInteractor = activeNotificationsInteractor, ) } Loading
packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt +31 −5 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.layoutId import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.dimensionResource import com.android.compose.animation.scene.ContentScope import com.android.compose.animation.scene.ElementKey Loading @@ -34,6 +34,13 @@ import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.media.controls.ui.composable.MediaCarousel import com.android.systemui.media.controls.ui.composable.isLandscape import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.controls.ui.view.MediaHostState.Companion.COLLAPSED import com.android.systemui.media.controls.ui.view.MediaHostState.Companion.EXPANDED import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel import com.android.systemui.res.R Loading @@ -42,10 +49,11 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.ui.composable.OverlayShade import com.android.systemui.shade.ui.composable.OverlayShadeHeader import com.android.systemui.shade.ui.composable.SingleShadeMeasurePolicy import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView import com.android.systemui.util.Utils import dagger.Lazy import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.flow.Flow @SysUISingleton Loading @@ -58,6 +66,8 @@ constructor( private val stackScrollView: Lazy<NotificationScrollView>, private val clockSection: DefaultClockSection, private val keyguardClockViewModel: KeyguardClockViewModel, private val mediaCarouselController: MediaCarouselController, @Named(QUICK_QS_PANEL) private val mediaHost: Lazy<MediaHost>, ) : Overlay { override val key = Overlays.NotificationsShade Loading @@ -84,6 +94,11 @@ constructor( viewModel.notificationsPlaceholderViewModelFactory.create() } val usingCollapsedLandscapeMedia = Utils.useCollapsedMediaInLandscape(LocalResources.current) mediaHost.get().expansion = if (usingCollapsedLandscapeMedia && isLandscape()) COLLAPSED else EXPANDED OverlayShade( panelElement = NotificationsShade.Elements.Panel, alignmentOnWideScreens = Alignment.TopStart, Loading @@ -96,9 +111,7 @@ constructor( } OverlayShadeHeader( viewModel = headerViewModel, modifier = Modifier.element(NotificationsShade.Elements.StatusBar) .layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader), modifier = Modifier.element(NotificationsShade.Elements.StatusBar), ) }, ) { Loading @@ -116,6 +129,19 @@ constructor( } } MediaCarousel( isVisible = viewModel.showMedia, mediaHost = mediaHost.get(), carouselController = mediaCarouselController, usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia, modifier = Modifier.padding( top = notificationStackPadding, start = notificationStackPadding, end = notificationStackPadding, ), ) NotificationScrollingStack( shadeSession = shadeSession, stackScrollView = stackScrollView.get(), Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt +37 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.notifications.ui.viewmodel import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest Loading @@ -28,6 +29,8 @@ import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.media.controls.data.repository.mediaFilterRepository import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest import com.android.systemui.power.domain.interactor.powerInteractor Loading @@ -39,10 +42,13 @@ import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.domain.interactor.enableDualShade import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.update import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest Loading @@ -50,6 +56,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper Loading Loading @@ -155,6 +162,36 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() { assertThat(underTest.showClock).isFalse() } @Test fun showMedia_activeMedia_true() = testScope.runTest { kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true)) runCurrent() assertThat(underTest.showMedia).isTrue() } @Test fun showMedia_noActiveMedia_false() = testScope.runTest { kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false)) runCurrent() assertThat(underTest.showMedia).isFalse() } @Test fun showMedia_qsDisabled_false() = testScope.runTest { kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true)) kosmos.fakeDisableFlagsRepository.disableFlags.update { it.copy(disable2 = DISABLE2_QUICK_SETTINGS) } runCurrent() assertThat(underTest.showMedia).isFalse() } private fun TestScope.lockDevice() { val currentScene by collectLastValue(sceneInteractor.currentScene) kosmos.powerInteractor.setAsleepForTest() Loading
packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt +22 −0 Original line number Diff line number Diff line Loading @@ -20,12 +20,15 @@ import androidx.compose.runtime.getValue import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.awaitCancellation Loading @@ -33,6 +36,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flowOf /** * Models UI state used to render the content of the notifications shade overlay. Loading @@ -47,6 +51,8 @@ constructor( val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory, val sceneInteractor: SceneInteractor, private val shadeInteractor: ShadeInteractor, disableFlagsInteractor: DisableFlagsInteractor, mediaCarouselInteractor: MediaCarouselInteractor, activeNotificationsInteractor: ActiveNotificationsInteractor, ) : ExclusiveActivatable() { Loading @@ -69,6 +75,22 @@ constructor( ), ) val showMedia: Boolean by hydrator.hydratedStateOf( traceName = "showMedia", initialValue = disableFlagsInteractor.disableFlags.value.isQuickSettingsEnabled() && mediaCarouselInteractor.hasActiveMediaOrRecommendation.value, source = disableFlagsInteractor.disableFlags.flatMapLatestConflated { if (it.isQuickSettingsEnabled()) { mediaCarouselInteractor.hasActiveMediaOrRecommendation } else { flowOf(false) } }, ) override suspend fun onActivated(): Nothing { coroutineScope { launch { hydrator.activate() } Loading
packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt +4 −0 Original line number Diff line number Diff line Loading @@ -18,9 +18,11 @@ package com.android.systemui.shade.ui.viewmodel import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory Loading @@ -31,6 +33,8 @@ val Kosmos.notificationsShadeOverlayContentViewModel: notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory, sceneInteractor = sceneInteractor, shadeInteractor = shadeInteractor, disableFlagsInteractor = disableFlagsInteractor, mediaCarouselInteractor = mediaCarouselInteractor, activeNotificationsInteractor = activeNotificationsInteractor, ) }