summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt27
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt153
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt4
10 files changed, 298 insertions, 96 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..4a0136c40c14
--- /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 -> MediaHierarchyManager.LOCATION_UNKNOWN
+ }
+ }
+
+ /** 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 -> MediaHierarchyManager.LOCATION_UNKNOWN
+ }
+ }
+
+ /** 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 = MediaHierarchyManager.LOCATION_UNKNOWN
+ override val startLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
+ }
+
+ /** 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 = MediaHierarchyManager.LOCATION_UNKNOWN
+ 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 = MediaHierarchyManager.LOCATION_UNKNOWN,
+ 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 = MediaHierarchyManager.LOCATION_UNKNOWN,
+ 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 0c69dbd5655c..630497998c3e 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..a0fb0bf25c7b 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
@@ -174,19 +175,21 @@ constructor(
* The desired location where we'll be at the end of the transformation. Usually this matches
* the end location, except when we're still waiting on a state update call.
*/
- @MediaLocation private var desiredLocation: Int = -1
+ @MediaLocation private var desiredLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
/**
* The ending location of the view where it ends when all animations and transitions have
* finished
*/
- @MediaLocation @VisibleForTesting var currentEndLocation: Int = -1
+ @MediaLocation
+ @VisibleForTesting
+ var currentEndLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
/**
* The ending location of the view where it ends when all animations and transitions have
* finished
*/
- @MediaLocation private var currentStartLocation: Int = -1
+ @MediaLocation private var currentStartLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
/** The progress of the transition or 1.0 if there is no transition happening */
private var currentTransitionProgress: Float = 1.0f
@@ -726,6 +729,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/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 38cea5b23f78..745ab12c27d6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -64,7 +64,6 @@ import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -161,10 +160,10 @@ constructor(
private var animationStartAlpha = 0.0f
/** The starting location of the cross fade if an animation is running right now. */
- @MediaLocation private var crossFadeAnimationStartLocation = -1
+ @MediaLocation private var crossFadeAnimationStartLocation = LOCATION_UNKNOWN
/** The end location of the cross fade if an animation is running right now. */
- @MediaLocation private var crossFadeAnimationEndLocation = -1
+ @MediaLocation private var crossFadeAnimationEndLocation = LOCATION_UNKNOWN
private var targetBounds: Rect = Rect()
private val mediaFrame
get() = mediaCarouselController.mediaFrame
@@ -191,7 +190,7 @@ constructor(
animationStartBounds,
targetBounds,
boundsProgress,
- result = currentBounds
+ result = currentBounds,
)
resolveClipping(currentClipping)
applyState(currentBounds, currentAlpha, clipBounds = currentClipping)
@@ -233,16 +232,16 @@ constructor(
* The last location where this view was at before going to the desired location. This is useful
* for guided transitions.
*/
- @MediaLocation private var previousLocation = -1
+ @MediaLocation private var previousLocation = LOCATION_UNKNOWN
/** The desired location where the view will be at the end of the transition. */
- @MediaLocation private var desiredLocation = -1
+ @MediaLocation private var desiredLocation = LOCATION_UNKNOWN
/**
* The current attachment location where the view is currently attached. Usually this matches
* the desired location except for animations whenever a view moves to the new desired location,
* during which it is in [IN_OVERLAY].
*/
- @MediaLocation private var currentAttachmentLocation = -1
+ @MediaLocation private var currentAttachmentLocation = LOCATION_UNKNOWN
private var inSplitShade = false
@@ -627,7 +626,7 @@ constructor(
secureSettings.getBoolForUser(
Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
true,
- UserHandle.USER_CURRENT
+ UserHandle.USER_CURRENT,
)
}
}
@@ -635,7 +634,7 @@ constructor(
secureSettings.registerContentObserverForUserSync(
Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
settingsObserver,
- UserHandle.USER_ALL
+ UserHandle.USER_ALL,
)
// Listen to the communal UI state. Make sure that communal UI is showing and hub itself is
@@ -651,7 +650,7 @@ constructor(
shadeInteractor.shadeExpansion
.mapLatest { it < EXPANSION_THRESHOLD }
.distinctUntilChanged(),
- ::Triple
+ ::Triple,
)
.collectLatest { (communalShowing, isDreaming, isShadeExpanding) ->
isCommunalShowing = communalShowing
@@ -689,10 +688,10 @@ constructor(
if (mediaObject.location == desiredLocation) {
// In case we are overriding a view that is already visible, make sure we attach it
// to this new host view in the below call
- desiredLocation = -1
+ desiredLocation = LOCATION_UNKNOWN
}
if (mediaObject.location == currentAttachmentLocation) {
- currentAttachmentLocation = -1
+ currentAttachmentLocation = LOCATION_UNKNOWN
}
updateDesiredLocation()
return viewHost
@@ -734,7 +733,7 @@ constructor(
*/
private fun updateDesiredLocation(
forceNoAnimation: Boolean = false,
- forceStateUpdate: Boolean = false
+ forceStateUpdate: Boolean = false,
) =
traceSection("MediaHierarchyManager#updateDesiredLocation") {
val desiredLocation = calculateLocation()
@@ -758,7 +757,7 @@ constructor(
previousLocation = LOCATION_QQS
}
}
- val isNewView = this.desiredLocation == -1
+ val isNewView = this.desiredLocation == LOCATION_UNKNOWN
this.desiredLocation = desiredLocation
// Let's perform a transition
val animate =
@@ -774,7 +773,7 @@ constructor(
host,
animate,
animDuration,
- delay
+ delay,
)
}
performTransitionToNewLocation(isNewView, animate)
@@ -868,7 +867,7 @@ constructor(
private fun shouldAnimateTransition(
@MediaLocation currentLocation: Int,
- @MediaLocation previousLocation: Int
+ @MediaLocation previousLocation: Int,
): Boolean {
if (isCurrentlyInGuidedTransformation()) {
return false
@@ -990,7 +989,7 @@ constructor(
startBounds: Rect,
endBounds: Rect,
progress: Float,
- result: Rect? = null
+ result: Rect? = null,
): Rect {
val left =
MathUtils.lerp(startBounds.left.toFloat(), endBounds.left.toFloat(), progress).toInt()
@@ -1014,7 +1013,7 @@ constructor(
}
private fun hasValidStartAndEndLocations(): Boolean {
- return previousLocation != -1 && desiredLocation != -1
+ return previousLocation != LOCATION_UNKNOWN && desiredLocation != LOCATION_UNKNOWN
}
/** Calculate the transformation type for the current animation */
@@ -1099,21 +1098,21 @@ constructor(
bounds: Rect,
alpha: Float,
immediately: Boolean = false,
- clipBounds: Rect = EMPTY_RECT
+ clipBounds: Rect = EMPTY_RECT,
) =
traceSection("MediaHierarchyManager#applyState") {
currentBounds.set(bounds)
currentClipping = clipBounds
carouselAlpha = if (isCurrentlyFading()) alpha else 1.0f
val onlyUseEndState = !isCurrentlyInGuidedTransformation() || isCurrentlyFading()
- val startLocation = if (onlyUseEndState) -1 else previousLocation
+ val startLocation = if (onlyUseEndState) LOCATION_UNKNOWN else previousLocation
val progress = if (onlyUseEndState) 1.0f else getTransformationProgress()
val endLocation = resolveLocationForFading()
mediaCarouselController.setCurrentState(
startLocation,
endLocation,
progress,
- immediately
+ immediately,
)
updateHostAttachment()
if (currentAttachmentLocation == IN_OVERLAY) {
@@ -1125,7 +1124,7 @@ constructor(
currentBounds.left,
currentBounds.top,
currentBounds.right,
- currentBounds.bottom
+ currentBounds.bottom,
)
}
}
@@ -1186,7 +1185,7 @@ constructor(
mediaCarouselController.onDesiredLocationChanged(
newLocation,
getHost(newLocation),
- animate = false
+ animate = false,
)
}
}
@@ -1201,7 +1200,7 @@ constructor(
if (isCrossFadeAnimatorRunning) {
// When animating between two hosts with a fade, let's keep ourselves in the old
// location for the first half, and then switch over to the end location
- if (animationCrossFadeProgress > 0.5 || previousLocation == -1) {
+ if (animationCrossFadeProgress > 0.5 || previousLocation == LOCATION_UNKNOWN) {
return crossFadeAnimationEndLocation
} else {
return crossFadeAnimationStartLocation
@@ -1364,6 +1363,9 @@ constructor(
/** Attached at the root of the hierarchy in an overlay */
const val IN_OVERLAY = -1000
+ /** Not attached to any view */
+ const val LOCATION_UNKNOWN = -1
+
/**
* The default transformation type where the hosts transform into each other using a direct
* transition
@@ -1388,8 +1390,8 @@ private val EMPTY_RECT = Rect()
value =
[
MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION,
- MediaHierarchyManager.TRANSFORMATION_TYPE_FADE
- ]
+ MediaHierarchyManager.TRANSFORMATION_TYPE_FADE,
+ ],
)
@Retention(AnnotationRetention.SOURCE)
private annotation class TransformationType
@@ -1403,7 +1405,8 @@ private annotation class TransformationType
MediaHierarchyManager.LOCATION_LOCKSCREEN,
MediaHierarchyManager.LOCATION_DREAM_OVERLAY,
MediaHierarchyManager.LOCATION_COMMUNAL_HUB,
- ]
+ MediaHierarchyManager.LOCATION_UNKNOWN,
+ ],
)
@Retention(AnnotationRetention.SOURCE)
annotation class MediaLocation
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..3928a711f840 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 {
@@ -120,7 +120,7 @@ constructor(
* finished
*/
@MediaLocation
- var currentEndLocation: Int = -1
+ var currentEndLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
set(value) {
if (field != value) {
field = value
@@ -130,7 +130,7 @@ constructor(
}
/** The starting location of the view where it starts for all animations and transitions */
- @MediaLocation private var currentStartLocation: Int = -1
+ @MediaLocation private var currentStartLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
/** The progress of the transition or 1.0 if there is no transition happening */
private var currentTransitionProgress: Float = 1.0f
@@ -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)
@@ -653,7 +653,7 @@ constructor(
logger.logMediaLocation("attach $type", currentStartLocation, currentEndLocation)
this.transitionLayout = transitionLayout
layoutController.attach(transitionLayout)
- if (currentEndLocation == -1) {
+ if (currentEndLocation == MediaHierarchyManager.LOCATION_UNKNOWN) {
return
}
// Set the previously set state immediately to the view, now that it's finally attached
@@ -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()) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index f07f2de08537..4173d2aa272e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -33,6 +33,8 @@ import com.android.systemui.media.controls.domain.pipeline.interactor.MediaContr
import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaControlModel
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.MediaUiEventLogger
@@ -70,7 +72,7 @@ class MediaControlViewModel(
private var isPlaying = false
private var isAnyButtonClicked = false
- private var location = -1
+ @MediaLocation private var location = MediaHierarchyManager.LOCATION_UNKNOWN
private var playerViewModel: MediaPlayerViewModel? = null
fun isNewPlayer(viewModel: MediaPlayerViewModel): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
index a7bce7772270..6bc6b10a1dfd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
@@ -31,6 +31,8 @@ import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecom
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.media.controls.ui.controller.MediaViewController.Companion.GUTS_ANIMATION_DURATION
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
@@ -66,7 +68,7 @@ constructor(
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
- private var location = -1
+ @MediaLocation private var location = MediaHierarchyManager.LOCATION_UNKNOWN
/**
* Called whenever the recommendation has been expired or removed by the user. This method