diff options
7 files changed, 254 insertions, 61 deletions
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 f4d9e820ad8f..3d8ca1e96a09 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 @@ -22,7 +22,9 @@ import android.widget.FrameLayout import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.layout.approachLayout import androidx.compose.ui.layout.layout @@ -31,8 +33,12 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.viewinterop.AndroidView import com.android.compose.animation.scene.MovableElementKey import com.android.compose.animation.scene.SceneScope +import com.android.compose.windowsizeclass.LocalWindowSizeClass +import com.android.internal.R.attr.layout +import com.android.systemui.media.controls.ui.composable.MediaCarouselStateLoader.stateForMediaCarouselContent 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 import com.android.systemui.res.R import com.android.systemui.util.animation.MeasurementInput @@ -53,12 +59,20 @@ fun SceneScope.MediaCarousel( modifier: Modifier = Modifier, carouselController: MediaCarouselController, offsetProvider: (() -> IntOffset)? = null, + usingCollapsedLandscapeMedia: Boolean = false, ) { if (!isVisible || carouselController.isLockedAndHidden()) { return } - val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded) + val carouselState = remember { { stateForMediaCarouselContent() } } + val isCollapsed = usingCollapsedLandscapeMedia && isLandscape() + val mediaHeight = + if (isCollapsed && mediaHost.expansion == MediaHostState.COLLAPSED) { + dimensionResource(R.dimen.qs_media_session_height_collapsed) + } else { + dimensionResource(R.dimen.qs_media_session_height_expanded) + } MovableElement( key = MediaCarousel.Elements.Content, @@ -95,6 +109,7 @@ fun SceneScope.MediaCarousel( } }, factory = { context -> + MediaCarouselStateLoader.loadCarouselState(carouselController, carouselState()) FrameLayout(context).apply { layoutParams = FrameLayout.LayoutParams( @@ -103,7 +118,10 @@ fun SceneScope.MediaCarousel( ) } }, - update = { it.setView(carouselController.mediaFrame) }, + update = { + MediaCarouselStateLoader.loadCarouselState(carouselController, carouselState()) + it.setView(carouselController.mediaFrame) + }, onRelease = { it.removeAllViews() }, ) } @@ -117,3 +135,8 @@ private fun ViewGroup.setView(view: View) { (view.parent as? ViewGroup)?.removeView(view) addView(view) } + +@Composable +fun SceneScope.isLandscape(): Boolean { + return LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt new file mode 100644 index 000000000000..b43a5b80b1b6 --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt @@ -0,0 +1,153 @@ +/* + * 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.media.controls.ui.composable + +import com.android.compose.animation.scene.ContentKey +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.SceneScope +import com.android.compose.animation.scene.content.state.TransitionState +import com.android.systemui.media.controls.ui.controller.MediaCarouselController +import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager +import com.android.systemui.media.controls.ui.controller.MediaLocation +import com.android.systemui.scene.shared.model.Overlays +import com.android.systemui.scene.shared.model.Scenes +import kotlin.math.min + +object MediaCarouselStateLoader { + + /** Sets current state for media carousel. */ + fun loadCarouselState(carouselController: MediaCarouselController, state: State) { + if (state is State.Gone) return + + carouselController.setCurrentState( + state.startLocation, + state.endLocation, + state.transitionProgress, + immediately = true, + ) + } + + /** Returns the corresponding media location for the given [scene] */ + @MediaLocation + private fun getMediaLocation(scene: SceneKey): Int { + return when (scene) { + Scenes.QuickSettings -> MediaHierarchyManager.LOCATION_QS + Scenes.Shade -> MediaHierarchyManager.LOCATION_QQS + Scenes.Lockscreen -> MediaHierarchyManager.LOCATION_LOCKSCREEN + Scenes.Communal -> MediaHierarchyManager.LOCATION_COMMUNAL_HUB + else -> -1 + } + } + + /** Returns the corresponding media location for the given [content] */ + @MediaLocation + private fun getMediaLocation(content: ContentKey): Int { + return when (content) { + Overlays.QuickSettingsShade -> MediaHierarchyManager.LOCATION_QS + Overlays.NotificationsShade -> MediaHierarchyManager.LOCATION_QQS + else -> -1 + } + } + + /** State for media carousel. */ + sealed interface State { + val transitionProgress: Float + // TODO b/368368388: implement media squishiness + val squishFraction: () -> Float + @MediaLocation val startLocation: Int + @MediaLocation val endLocation: Int + + /** State when media carousel is not visible on screen. */ + data object Gone : State { + override val transitionProgress: Float = 1.0F + override val squishFraction: () -> Float = { 1.0F } + override val endLocation: Int = -1 + override val startLocation: Int = -1 + } + + /** State when media carousel is moving from one media location to another */ + data class InProgress( + override val transitionProgress: Float, + override val startLocation: Int, + override val endLocation: Int, + ) : State { + override val squishFraction = { 1.0F } + } + + /** State when media carousel reached the end location. */ + data class Idle(override val endLocation: Int) : State { + override val transitionProgress = 1.0F + override val startLocation = -1 + override val squishFraction = { 1.0F } + } + } + + /** Returns the state of media carousel */ + fun SceneScope.stateForMediaCarouselContent(): State { + return when (val transitionState = layoutState.transitionState) { + is TransitionState.Idle -> { + if (MediaContentPicker.contents.contains(transitionState.currentScene)) { + State.Idle(getMediaLocation(transitionState.currentScene)) + } else { + State.Gone + } + } + is TransitionState.Transition.ChangeScene -> + with(transitionState) { + if ( + MediaContentPicker.contents.contains(toScene) && + MediaContentPicker.contents.contains(fromScene) + ) { + State.InProgress( + min(progress, 1.0F), + getMediaLocation(fromScene), + getMediaLocation(toScene), + ) + } else if (MediaContentPicker.contents.contains(toScene)) { + State.InProgress( + transitionProgress = 1.0F, + startLocation = -1, + getMediaLocation(toScene), + ) + } else { + State.Gone + } + } + is TransitionState.Transition.OverlayTransition -> + with(transitionState) { + if ( + MediaContentPicker.contents.contains(toContent) && + MediaContentPicker.contents.contains(fromContent) + ) { + State.InProgress( + min(progress, 1.0F), + getMediaLocation(fromContent), + getMediaLocation(toContent), + ) + } else if (MediaContentPicker.contents.contains(toContent)) { + State.InProgress( + transitionProgress = 1.0F, + startLocation = -1, + getMediaLocation(toContent), + ) + } else { + State.Gone + } + } + } + } +} 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 d91958adaa1b..5d2474c18fd3 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 @@ -48,7 +48,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -85,6 +84,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.ExclusiveActivatable 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.dagger.MediaModule @@ -288,9 +288,7 @@ private fun SceneScope.QuickSettingsScene( // ############# Media ############### val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle() - val mediaInRow = - isMediaVisible && - LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact + val mediaInRow = isMediaVisible && isLandscape() val mediaOffset by animateSceneDpAsState(value = InQS, key = MediaLandscapeTopOffset, canOverflow = false) 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 8a59e204eb23..7f2ee2a8351a 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 @@ -43,7 +43,6 @@ import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -78,7 +77,6 @@ import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.modifiers.padding import com.android.compose.modifiers.thenIf -import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout @@ -89,6 +87,7 @@ import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.media.controls.ui.composable.MediaCarousel import com.android.systemui.media.controls.ui.composable.MediaContentPicker +import com.android.systemui.media.controls.ui.composable.isLandscape import com.android.systemui.media.controls.ui.composable.shouldElevateMedia import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager @@ -197,6 +196,8 @@ constructor( qsMediaHost = qsMediaHost, modifier = modifier, shadeSession = shadeSession, + usingCollapsedLandscapeMedia = + Utils.useCollapsedMediaInLandscape(LocalContext.current.resources), ) init { @@ -223,6 +224,7 @@ private fun SceneScope.ShadeScene( qsMediaHost: MediaHost, modifier: Modifier = Modifier, shadeSession: SaveableSession, + usingCollapsedLandscapeMedia: Boolean, ) { val view = LocalView.current LaunchedEffect(Unit) { @@ -245,6 +247,7 @@ private fun SceneScope.ShadeScene( mediaHost = qqsMediaHost, modifier = modifier, shadeSession = shadeSession, + usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia, ) is ShadeMode.Split -> SplitShade( @@ -275,14 +278,11 @@ private fun SceneScope.SingleShade( mediaHost: MediaHost, modifier: Modifier = Modifier, shadeSession: SaveableSession, + usingCollapsedLandscapeMedia: Boolean, ) { val cutoutLocation = LocalDisplayCutout.current.location val cutoutInsets = WindowInsets.Companion.displayCutout - val isLandscape = LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact - val usingCollapsedLandscapeMedia = - Utils.useCollapsedMediaInLandscape(LocalContext.current.resources) - val isExpanded = !usingCollapsedLandscapeMedia || !isLandscape - mediaHost.expansion = if (isExpanded) EXPANDED else COLLAPSED + mediaHost.expansion = if (usingCollapsedLandscapeMedia && isLandscape()) COLLAPSED else EXPANDED var maxNotifScrimTop by remember { mutableIntStateOf(0) } val tileSquishiness by @@ -298,7 +298,7 @@ private fun SceneScope.SingleShade( layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) || layoutState.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade) // Media is visible and we are in landscape on a small height screen - val mediaInRow = isMediaVisible && isLandscape + val mediaInRow = isMediaVisible && isLandscape() val mediaOffset by animateSceneDpAsState(value = InQQS, key = MediaLandscapeTopOffset, canOverflow = false) @@ -380,6 +380,7 @@ private fun SceneScope.SingleShade( mediaOffsetProvider = mediaOffsetProvider, carouselController = mediaCarouselController, modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Media), + usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia, ) NotificationScrollingStack( @@ -636,6 +637,7 @@ private fun SceneScope.ShadeMediaCarousel( carouselController: MediaCarouselController, mediaOffsetProvider: ShadeMediaOffsetProvider, modifier: Modifier = Modifier, + usingCollapsedLandscapeMedia: Boolean = false, ) { MediaCarousel( modifier = modifier.fillMaxWidth(), @@ -648,5 +650,6 @@ private fun SceneScope.ShadeMediaCarousel( } else { { mediaOffsetProvider.offset } }, + usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia, ) } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index bb9517a14142..c1e28b356ce0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -110,6 +110,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -726,6 +727,13 @@ constructor( ) DiffUtil.calculateDiff(diffUtilCallback).dispatchUpdatesTo(listUpdateCallback) setNewViewModelsList(it) + + // Update host visibility when media changes. + merge( + mediaCarouselViewModel.hasAnyMediaOrRecommendations, + mediaCarouselViewModel.hasActiveMediaOrRecommendations, + ) + .collect { updateHostVisibility() } } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt index e57de09f1063..4d4a5a9780b7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt @@ -91,7 +91,7 @@ constructor( */ enum class TYPE { PLAYER, - RECOMMENDATION + RECOMMENDATION, } companion object { @@ -294,14 +294,14 @@ constructor( object : MediaHostStatesManager.Callback { override fun onHostStateChanged( @MediaLocation location: Int, - mediaHostState: MediaHostState + mediaHostState: MediaHostState, ) { if (location == currentEndLocation || location == currentStartLocation) { setCurrentState( currentStartLocation, currentEndLocation, currentTransitionProgress, - applyImmediately = false + applyImmediately = false, ) } } @@ -442,7 +442,7 @@ constructor( /** Apply squishFraction to a copy of viewState such that the cached version is untouched. */ internal fun squishViewState( viewState: TransitionViewState, - squishFraction: Float + squishFraction: Float, ): TransitionViewState { val squishedViewState = viewState.copy() val squishedHeight = (squishedViewState.measureHeight * squishFraction).toInt() @@ -458,13 +458,13 @@ constructor( MediaViewHolder.expandedBottomActionIds, squishedViewState.measureHeight.toFloat(), squishedViewState, - squishFraction + squishFraction, ) calculateWidgetGroupAlphaForSquishiness( MediaViewHolder.detailIds, squishedViewState.measureHeight.toFloat(), squishedViewState, - squishFraction + squishFraction, ) // recommendation card val titlesTop = @@ -472,13 +472,13 @@ constructor( RecommendationViewHolder.mediaTitlesAndSubtitlesIds, squishedViewState.measureHeight.toFloat(), squishedViewState, - squishFraction + squishFraction, ) calculateWidgetGroupAlphaForSquishiness( RecommendationViewHolder.mediaContainersIds, titlesTop, squishedViewState, - squishFraction + squishFraction, ) return squishedViewState } @@ -517,7 +517,7 @@ constructor( widgetGroupIds: Set<Int>, groupEndPosition: Float, squishedViewState: TransitionViewState, - squishFraction: Float + squishFraction: Float, ): Float { val nonsquishedHeight = squishedViewState.measureHeight var groupTop = squishedViewState.measureHeight.toFloat() @@ -547,7 +547,7 @@ constructor( calculateAlpha( squishFraction, startPosition / nonsquishedHeight, - endPosition / nonsquishedHeight + endPosition / nonsquishedHeight, ) } } @@ -562,10 +562,10 @@ constructor( @VisibleForTesting fun obtainViewState( state: MediaHostState?, - isGutsAnimation: Boolean = false + isGutsAnimation: Boolean = false, ): TransitionViewState? { if (SceneContainerFlag.isEnabled) { - return obtainSceneContainerViewState() + return obtainSceneContainerViewState(state) } if (state == null || state.measurementInput == null) { @@ -606,7 +606,7 @@ constructor( transitionLayout!!.calculateViewState( state.measurementInput!!, constraintSetForExpansion(state.expansion), - TransitionViewState() + TransitionViewState(), ) setGutsViewState(result) @@ -661,7 +661,7 @@ constructor( startLocation = currentStartLocation, endLocation = currentEndLocation, transitionProgress = currentTransitionProgress, - applyImmediately = true + applyImmediately = true, ) } @@ -695,7 +695,7 @@ constructor( Interpolators.EMPHASIZED_DECELERATE, titleText, artistText, - explicitIndicator + explicitIndicator, ) val exit = loadAnimator( @@ -704,7 +704,7 @@ constructor( Interpolators.EMPHASIZED_ACCELERATE, titleText, artistText, - explicitIndicator + explicitIndicator, ) metadataAnimationHandler = MetadataAnimationHandler(exit, enter) @@ -713,7 +713,7 @@ constructor( mediaCard.context, mediaViewHolder, multiRippleController, - turbulenceNoiseController + turbulenceNoiseController, ) // For Turbulence noise. @@ -728,7 +728,7 @@ constructor( object : LoadingEffect.AnimationStateChangedCallback { override fun onStateChanged( oldState: LoadingEffect.AnimationState, - newState: LoadingEffect.AnimationState + newState: LoadingEffect.AnimationState, ) { if (newState === LoadingEffect.AnimationState.NOT_PLAYING) { loadingEffectView.visibility = View.INVISIBLE @@ -755,12 +755,12 @@ constructor( MediaControlViewBinder.setVisibleAndAlpha( expandedLayout, it.scrubbingTotalTimeView.id, - isTimeVisible + isTimeVisible, ) MediaControlViewBinder.setVisibleAndAlpha( expandedLayout, it.scrubbingElapsedTimeView.id, - isTimeVisible + isTimeVisible, ) } @@ -788,7 +788,7 @@ constructor( collapsedLayout, isButtonVisible, notVisibleValue, - showInCollapsed = true + showInCollapsed = true, ) } } @@ -822,7 +822,7 @@ constructor( createTurbulenceNoiseConfig( it.loadingEffectView, it.turbulenceNoiseView, - colorSchemeTransition + colorSchemeTransition, ) } if (Flags.shaderlibLoadingEffectRefactor()) { @@ -832,23 +832,23 @@ constructor( TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE, turbulenceNoiseAnimationConfig, noiseDrawCallback, - stateChangedCallback + stateChangedCallback, ) } colorSchemeTransition.loadingEffect = loadingEffect loadingEffect.play() mainExecutor.executeDelayed( loadingEffect::finish, - MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION + MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION, ) } else { turbulenceNoiseController.play( TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE, - turbulenceNoiseAnimationConfig + turbulenceNoiseAnimationConfig, ) mainExecutor.executeDelayed( turbulenceNoiseController::finish, - MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION + MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION, ) } } @@ -920,7 +920,7 @@ constructor( startViewState, startHostState.disappearParameters, transitionProgress, - tmpState + tmpState, ) } } else if (startHostState != null && !startHostState.visible) { @@ -931,7 +931,7 @@ constructor( endViewState, endHostState.disappearParameters, 1.0f - transitionProgress, - tmpState + tmpState, ) } else if (transitionProgress == 1.0f || startViewState == null) { // We're at the end. Let's use that state @@ -945,13 +945,13 @@ constructor( startViewState, endViewState, transitionProgress, - tmpState + tmpState, ) } logger.logMediaSize( "setCurrentState (progress $transitionProgress)", result.width, - result.height + result.height, ) layoutController.setState( result, @@ -966,7 +966,7 @@ constructor( private fun updateViewStateSize( viewState: TransitionViewState?, location: Int, - outState: TransitionViewState + outState: TransitionViewState, ): TransitionViewState? { var result = viewState?.copy(outState) ?: return null val state = mediaHostStatesManager.mediaHostStates[location] @@ -1020,15 +1020,19 @@ constructor( } /** Get a view state based on the width and height set by the scene */ - private fun obtainSceneContainerViewState(): TransitionViewState? { + private fun obtainSceneContainerViewState(state: MediaHostState?): TransitionViewState? { logger.logMediaSize("scene container", widthInSceneContainerPx, heightInSceneContainerPx) + if (state?.measurementInput == null) { + return null + } + // Similar to obtainViewState: Let's create a new measurement val result = transitionLayout?.calculateViewState( MeasurementInput(widthInSceneContainerPx, heightInSceneContainerPx), - expandedLayout, - TransitionViewState() + if (state.expansion > 0) expandedLayout else collapsedLayout, + TransitionViewState(), ) result?.let { // And then ensure the guts visibility is set correctly @@ -1049,7 +1053,7 @@ constructor( private fun obtainViewStateForLocation(@MediaLocation location: Int): TransitionViewState? { val mediaHostState = mediaHostStatesManager.mediaHostStates[location] ?: return null if (SceneContainerFlag.isEnabled) { - return obtainSceneContainerViewState() + return obtainSceneContainerViewState(mediaHostState) } val viewState = obtainViewState(mediaHostState) @@ -1080,9 +1084,10 @@ constructor( fun refreshState() = traceSection("MediaViewController#refreshState") { if (SceneContainerFlag.isEnabled) { + val hostState = mediaHostStatesManager.mediaHostStates[currentEndLocation] // We don't need to recreate measurements for scene container, since it's a known // size. Just get the view state and update the layout controller - obtainSceneContainerViewState()?.let { + obtainSceneContainerViewState(hostState)?.let { // Get scene container state, then setCurrentState layoutController.setState( state = it, @@ -1106,7 +1111,7 @@ constructor( currentStartLocation, currentEndLocation, currentTransitionProgress, - applyImmediately = true + applyImmediately = true, ) } @@ -1115,7 +1120,7 @@ constructor( context: Context, animId: Int, motionInterpolator: Interpolator?, - vararg targets: View? + vararg targets: View?, ): AnimatorSet { val animators = ArrayList<Animator>() for (target in targets) { @@ -1132,7 +1137,7 @@ constructor( private fun createTurbulenceNoiseConfig( loadingEffectView: LoadingEffectView, turbulenceNoiseView: TurbulenceNoiseView, - colorSchemeTransition: ColorSchemeTransition + colorSchemeTransition: ColorSchemeTransition, ): TurbulenceNoiseAnimationConfig { val targetView: View = if (Flags.shaderlibLoadingEffectRefactor()) { @@ -1163,7 +1168,7 @@ constructor( targetView.context.resources.displayMetrics.density, lumaMatteBlendFactor = 0.26f, lumaMatteOverallBrightness = 0.09f, - shouldInverseNoiseLuminosity = false + shouldInverseNoiseLuminosity = false, ) } @@ -1185,5 +1190,5 @@ private data class CacheKey( var widthMeasureSpec: Int = -1, var heightMeasureSpec: Int = -1, var expansion: Float = 0.0f, - var gutsVisible: Boolean = false + var gutsVisible: Boolean = false, ) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt index e7f7171d5be3..b2137afa05e6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt @@ -56,6 +56,9 @@ constructor( private val mediaLogger: MediaLogger, ) { + val hasAnyMediaOrRecommendations: StateFlow<Boolean> = interactor.hasAnyMediaOrRecommendation + val hasActiveMediaOrRecommendations: StateFlow<Boolean> = + interactor.hasActiveMediaOrRecommendation val mediaItems: StateFlow<List<MediaCommonViewModel>> = interactor.currentMedia .map { sortedItems -> @@ -114,7 +117,7 @@ constructor( qsExpanded: Boolean, visibleIndex: Int, location: Int, - isUpdate: Boolean = false + isUpdate: Boolean = false, ) { // Skip logging if on LS or QQS, and there is no active media card if (!qsExpanded && !interactor.hasActiveMediaOrRecommendation()) return @@ -127,7 +130,7 @@ constructor( val instanceId = commonModel.mediaLoadedModel.instanceId return mediaControlByInstanceId[instanceId]?.copy( immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi, - updateTime = commonModel.updateTime + updateTime = commonModel.updateTime, ) ?: MediaCommonViewModel.MediaControl( instanceId = instanceId, @@ -144,7 +147,7 @@ constructor( }, onUpdated = { onMediaControlAddedOrUpdated(it, commonModel) }, isMediaFromRec = commonModel.isMediaFromRec, - updateTime = commonModel.updateTime + updateTime = commonModel.updateTime, ) .also { mediaControlByInstanceId[instanceId] = it } } @@ -165,7 +168,7 @@ constructor( return mediaRecs?.copy( key = commonModel.recsLoadingModel.key, loadingEnabled = - interactor.isRecommendationActive() || mediaFlags.isPersistentSsCardEnabled() + interactor.isRecommendationActive() || mediaFlags.isPersistentSsCardEnabled(), ) ?: MediaCommonViewModel.MediaRecommendations( key = commonModel.recsLoadingModel.key, @@ -195,7 +198,7 @@ constructor( private fun onMediaControlAddedOrUpdated( commonViewModel: MediaCommonViewModel, - commonModel: MediaCommonModel.MediaControl + commonModel: MediaCommonModel.MediaControl, ) { if (commonModel.canBeRemoved && !Utils.useMediaResumption(applicationContext)) { // This media control is due for removal as it is now paused + timed out, and resumption @@ -222,7 +225,7 @@ constructor( private fun onMediaRecommendationRemoved( commonModel: MediaCommonModel.MediaRecommendations, - immediatelyRemove: Boolean + immediatelyRemove: Boolean, ) { mediaLogger.logMediaRecommendationCardRemoved(commonModel.recsLoadingModel.key) if (immediatelyRemove || isReorderingAllowed()) { |