diff options
7 files changed, 94 insertions, 69 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt index dae120cca981..556bbbe9f997 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt @@ -17,21 +17,14 @@ package com.android.systemui.keyguard.ui.composable.section import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.dimensionResource import com.android.compose.animation.scene.SceneScope import com.android.systemui.keyguard.ui.viewmodel.MediaCarouselViewModel import com.android.systemui.media.controls.ui.composable.MediaCarousel import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule -import com.android.systemui.res.R -import com.android.systemui.util.animation.MeasurementInput import javax.inject.Inject import javax.inject.Named @@ -43,29 +36,19 @@ constructor( private val mediaCarouselViewModel: MediaCarouselViewModel, ) { - @Composable - fun SceneScope.MediaCarousel(modifier: Modifier = Modifier) { - if (!mediaCarouselViewModel.isMediaVisible) { - return - } - + private fun isVisible(): Boolean { if (mediaCarouselController.mediaFrame == null) { - return + return false } + return mediaCarouselViewModel.isMediaVisible + } - val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded) - // TODO(b/312714128): MediaPlayer background size is not as expected. + @Composable + fun SceneScope.KeyguardMediaCarousel() { MediaCarousel( - modifier = - modifier.height(mediaHeight).fillMaxWidth().onSizeChanged { size -> - // Notify controller to size the carousel for the - // current space - mediaHost.measurementInput = MeasurementInput(size.width, size.height) - mediaCarouselController.setSceneContainerSize(size.width, size.height) - }, + isVisible = ::isVisible, mediaHost = mediaHost, - layoutWidth = 0, // Layout width is not used. - layoutHeight = with(LocalDensity.current) { mediaHeight.toPx() }.toInt(), + modifier = Modifier.fillMaxWidth(), carouselController = mediaCarouselController, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt index e0540bf3175f..722032c19553 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt @@ -125,7 +125,7 @@ constructor( onTopChanged = burnIn.onSmartspaceTopChanged, ) } - with(mediaCarouselSection) { MediaCarousel() } + with(mediaCarouselSection) { KeyguardMediaCarousel() } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt index f0d356c889a9..241c171f7862 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt @@ -18,14 +18,20 @@ package com.android.systemui.media.controls.ui.composable import android.view.ViewGroup import android.widget.FrameLayout +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.layout +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.viewinterop.AndroidView import androidx.core.view.contains import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost +import com.android.systemui.res.R import com.android.systemui.util.animation.MeasurementInput private object MediaCarousel { @@ -36,18 +42,40 @@ private object MediaCarousel { @Composable fun SceneScope.MediaCarousel( + isVisible: () -> Boolean, mediaHost: MediaHost, modifier: Modifier = Modifier, - layoutWidth: Int, - layoutHeight: Int, carouselController: MediaCarouselController, ) { + if (!isVisible()) { + return + } + + val density = LocalDensity.current + val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded) + + val layoutWidth = 0 + val layoutHeight = with(density) { mediaHeight.toPx() }.toInt() + // Notify controller to size the carousel for the current space mediaHost.measurementInput = MeasurementInput(layoutWidth, layoutHeight) carouselController.setSceneContainerSize(layoutWidth, layoutHeight) AndroidView( - modifier = modifier.element(MediaCarousel.Elements.Content), + modifier = + modifier + .element(MediaCarousel.Elements.Content) + .height(mediaHeight) + .fillMaxWidth() + .layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + + // Notify controller to size the carousel for the current space + mediaHost.measurementInput = MeasurementInput(placeable.width, placeable.height) + carouselController.setSceneContainerSize(placeable.width, placeable.height) + + layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) } + }, factory = { context -> FrameLayout(context).apply { val mediaFrame = carouselController.mediaFrame diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index f6575dc9768b..62619f56b4b4 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -67,6 +67,10 @@ import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.media.controls.ui.composable.MediaCarousel +import com.android.systemui.media.controls.ui.controller.MediaCarouselController +import com.android.systemui.media.controls.ui.view.MediaHost +import com.android.systemui.media.dagger.MediaModule import com.android.systemui.notifications.ui.composable.NotificationScrollingStack import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel @@ -82,6 +86,7 @@ import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.ui.StatusBarIconController import com.android.systemui.statusbar.phone.ui.TintedIconManager import javax.inject.Inject +import javax.inject.Named import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted @@ -98,6 +103,8 @@ constructor( private val tintedIconManagerFactory: TintedIconManager.Factory, private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory, private val statusBarIconController: StatusBarIconController, + private val mediaCarouselController: MediaCarouselController, + @Named(MediaModule.QS_PANEL) private val mediaHost: MediaHost, ) : ComposableScene { override val key = Scenes.QuickSettings @@ -118,6 +125,8 @@ constructor( createTintedIconManager = tintedIconManagerFactory::create, createBatteryMeterViewController = batteryMeterViewControllerFactory::create, statusBarIconController = statusBarIconController, + mediaCarouselController = mediaCarouselController, + mediaHost = mediaHost, modifier = modifier, ) } @@ -130,6 +139,8 @@ private fun SceneScope.QuickSettingsScene( createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager, createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController, statusBarIconController: StatusBarIconController, + mediaCarouselController: MediaCarouselController, + mediaHost: MediaHost, modifier: Modifier = Modifier, ) { val brightnessMirrorShowing by viewModel.brightnessMirrorViewModel.isShowing.collectAsState() @@ -282,6 +293,13 @@ private fun SceneScope.QuickSettingsScene( isSplitShade = false, modifier = Modifier.sysuiResTag("expanded_qs_scroll_view"), ) + + MediaCarousel( + isVisible = viewModel::isMediaVisible, + mediaHost = mediaHost, + modifier = Modifier.fillMaxWidth(), + carouselController = mediaCarouselController, + ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index 91a9d2af7959..cda8059669a8 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -50,11 +50,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.Layout -import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.LowestZIndexScenePicker @@ -85,7 +83,6 @@ import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.ui.StatusBarIconController import com.android.systemui.statusbar.phone.ui.TintedIconManager -import com.android.systemui.util.animation.MeasurementInput import javax.inject.Inject import javax.inject.Named import kotlin.math.roundToInt @@ -243,10 +240,11 @@ private fun SceneScope.SingleShade( ) } - MediaIfVisible( - viewModel = viewModel, - mediaCarouselController = mediaCarouselController, + MediaCarousel( + isVisible = viewModel::isMediaVisible, mediaHost = mediaHost, + modifier = Modifier.fillMaxWidth(), + carouselController = mediaCarouselController, ) Spacer(modifier = Modifier.height(16.dp)) @@ -406,11 +404,11 @@ private fun SceneScope.SplitShade( ) } - MediaIfVisible( - viewModel = viewModel, - mediaCarouselController = mediaCarouselController, + MediaCarousel( + isVisible = viewModel::isMediaVisible, mediaHost = mediaHost, modifier = Modifier.fillMaxWidth(), + carouselController = mediaCarouselController, ) } FooterActionsWithAnimatedVisibility( @@ -437,34 +435,3 @@ private fun SceneScope.SplitShade( } } } - -@Composable -private fun SceneScope.MediaIfVisible( - viewModel: ShadeSceneViewModel, - mediaCarouselController: MediaCarouselController, - mediaHost: MediaHost, - modifier: Modifier = Modifier, -) { - if (viewModel.isMediaVisible()) { - val density = LocalDensity.current - val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded) - - MediaCarousel( - modifier = - modifier.height(mediaHeight).fillMaxWidth().layout { measurable, constraints -> - val placeable = measurable.measure(constraints) - - // Notify controller to size the carousel for the - // current space - mediaHost.measurementInput = MeasurementInput(placeable.width, placeable.height) - mediaCarouselController.setSceneContainerSize(placeable.width, placeable.height) - - layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) } - }, - mediaHost = mediaHost, - layoutWidth = 0, - layoutHeight = with(density) { mediaHeight.toPx() }.toInt(), - carouselController = mediaCarouselController, - ) - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index d2a458c8a055..179ba42cc772 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope +import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.qs.FooterActionsController import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter @@ -78,6 +79,8 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { private val sceneBackInteractor = kosmos.sceneBackInteractor private val sceneContainerStartable = kosmos.sceneContainerStartable + private val mediaDataManager = mock<MediaDataManager>() + private lateinit var underTest: QuickSettingsSceneViewModel @OptIn(ExperimentalCoroutinesApi::class) @@ -97,6 +100,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { footerActionsViewModelFactory = footerActionsViewModelFactory, footerActionsController = footerActionsController, sceneBackInteractor = sceneBackInteractor, + mediaDataManager = mediaDataManager, ) } @@ -230,4 +234,22 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { verify(footerActionsController, times(1)).init() } + + @Test + fun hasMedia_mediaVisible() { + testScope.runTest { + whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true) + + assertThat(underTest.isMediaVisible()).isTrue() + } + } + + @Test + fun doesNotHaveMedia_mediaNotVisible() { + testScope.runTest { + whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(false) + + assertThat(underTest.isMediaVisible()).isFalse() + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt index 22146ce3a18f..257c4d58f569 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt @@ -29,6 +29,7 @@ import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor +import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.qs.FooterActionsController import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.qs.ui.adapter.QSSceneAdapter @@ -62,6 +63,7 @@ constructor( private val footerActionsViewModelFactory: FooterActionsViewModel.Factory, private val footerActionsController: FooterActionsController, sceneBackInteractor: SceneBackInteractor, + val mediaDataManager: MediaDataManager, ) { private val backScene: StateFlow<SceneKey> = sceneBackInteractor.backScene @@ -141,4 +143,9 @@ constructor( } return footerActionsViewModelFactory.create(lifecycleOwner) } + + fun isMediaVisible(): Boolean { + // TODO(b/328207006): use new pipeline to handle updates while visible + return mediaDataManager.hasAnyMediaOrRecommendation() + } } |