summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Coco Duan <cocod@google.com> 2025-03-21 09:39:59 -0700
committer Android (Google) Code Review <android-gerrit@google.com> 2025-03-21 09:39:59 -0700
commit90cead419db6089bbcdd7cbde787781bd088ceec (patch)
treef22139641ea64c8ed44a2d292c1d274fe65658ff
parent2c4b90aaa5d4a32e67ac9fc353e8bb4571f00c53 (diff)
parentd142cf112d956e7a011532239bcf687636466300 (diff)
Merge "Fix flickering when swiping back to keyguard from hub in landscape" into main
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt43
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt62
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt229
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt66
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt6
13 files changed, 549 insertions, 15 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 2b8fe39c4870..16a8f987ba83 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -1,5 +1,6 @@
package com.android.systemui.communal.ui.compose
+import android.content.res.Configuration
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
@@ -15,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -26,6 +28,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.disabled
@@ -55,6 +58,7 @@ import com.android.systemui.communal.ui.compose.Dimensions.Companion.SlideOffset
import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.util.CommunalColors
+import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource
@@ -98,6 +102,17 @@ val sceneTransitionsV2 = transitions {
spec = tween(durationMillis = TO_GONE_DURATION.toInt(DurationUnit.MILLISECONDS))
fade(AllElements)
}
+ to(CommunalScenes.Blank, key = CommunalTransitionKeys.SwipeInLandscape) {
+ spec = tween(durationMillis = TO_LOCKSCREEN_DURATION.toInt(DurationUnit.MILLISECONDS))
+ translate(Communal.Elements.Grid, Edge.End)
+ timestampRange(endMillis = 167) {
+ fade(Communal.Elements.Grid)
+ fade(Communal.Elements.IndicationArea)
+ fade(Communal.Elements.LockIcon)
+ fade(Communal.Elements.StatusBar)
+ }
+ timestampRange(startMillis = 167, endMillis = 500) { fade(Communal.Elements.Scrim) }
+ }
to(CommunalScenes.Blank, key = CommunalTransitionKeys.Swipe) {
spec = tween(durationMillis = TransitionDuration.TO_GLANCEABLE_HUB_DURATION_MS)
translate(Communal.Elements.Grid, Edge.End)
@@ -214,6 +229,9 @@ fun CommunalContainer(
val blurRadius = with(LocalDensity.current) { viewModel.blurRadiusPx.toDp() }
+ val swipeFromHubInLandscape by
+ viewModel.swipeFromHubInLandscape.collectAsStateWithLifecycle(false)
+
SceneTransitionLayout(
state = state,
modifier = modifier.fillMaxSize().thenIf(isUiBlurred) { Modifier.blur(blurRadius) },
@@ -241,7 +259,14 @@ fun CommunalContainer(
userActions =
mapOf(
Swipe.End to
- UserActionResult(CommunalScenes.Blank, CommunalTransitionKeys.Swipe)
+ UserActionResult(
+ CommunalScenes.Blank,
+ if (swipeFromHubInLandscape) {
+ CommunalTransitionKeys.SwipeInLandscape
+ } else {
+ CommunalTransitionKeys.Swipe
+ },
+ )
),
) {
CommunalScene(
@@ -258,6 +283,20 @@ fun CommunalContainer(
Box(modifier = Modifier.fillMaxSize().allowGestures(touchesAllowed))
}
+/** Listens to orientation changes on communal scene and reset when scene is disposed. */
+@Composable
+fun ObserveOrientationChange(viewModel: CommunalViewModel) {
+ val configuration = LocalConfiguration.current
+
+ LaunchedEffect(configuration.orientation) {
+ viewModel.onOrientationChange(configuration.orientation)
+ }
+
+ DisposableEffect(Unit) {
+ onDispose { viewModel.onOrientationChange(Configuration.ORIENTATION_UNDEFINED) }
+ }
+}
+
/** Scene containing the glanceable hub UI. */
@Composable
fun ContentScope.CommunalScene(
@@ -269,6 +308,8 @@ fun ContentScope.CommunalScene(
) {
val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
+ // Observe screen rotation while Communal Scene is active.
+ ObserveOrientationChange(viewModel)
Box(
modifier =
Modifier.element(Communal.Elements.Scrim)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
index dc21f0692c9e..7bdac476641b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.communal.domain.interactor
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
@@ -33,11 +35,15 @@ import com.android.systemui.flags.andSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.initialSceneKey
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,9 +52,11 @@ import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
class CommunalSceneInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@@ -70,6 +78,7 @@ class CommunalSceneInteractorTest(flags: FlagsParameterization) : SysuiTestCase(
private val repository = kosmos.communalSceneRepository
private val underTest by lazy { kosmos.communalSceneInteractor }
+ private val keyguardStateController: KeyguardStateController = kosmos.keyguardStateController
@DisableFlags(FLAG_SCENE_CONTAINER)
@Test
@@ -551,4 +560,57 @@ class CommunalSceneInteractorTest(flags: FlagsParameterization) : SysuiTestCase(
transitionState.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
assertThat(isCommunalVisible).isEqualTo(false)
}
+
+ @Test
+ fun willRotateToPortrait_whenKeyguardRotationNotAllowed() =
+ testScope.runTest {
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false)
+ val willRotateToPortrait by collectLastValue(underTest.willRotateToPortrait)
+
+ repository.setCommunalContainerOrientation(ORIENTATION_LANDSCAPE)
+ runCurrent()
+
+ assertThat(willRotateToPortrait).isEqualTo(true)
+
+ repository.setCommunalContainerOrientation(ORIENTATION_PORTRAIT)
+ runCurrent()
+
+ assertThat(willRotateToPortrait).isEqualTo(false)
+ }
+
+ @Test
+ fun willRotateToPortrait_isFalse_whenKeyguardRotationIsAllowed() =
+ testScope.runTest {
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true)
+ val willRotateToPortrait by collectLastValue(underTest.willRotateToPortrait)
+
+ repository.setCommunalContainerOrientation(ORIENTATION_LANDSCAPE)
+ runCurrent()
+
+ assertThat(willRotateToPortrait).isEqualTo(false)
+
+ repository.setCommunalContainerOrientation(ORIENTATION_PORTRAIT)
+ runCurrent()
+
+ assertThat(willRotateToPortrait).isEqualTo(false)
+ }
+
+ @Test
+ fun rotatedToPortrait() =
+ testScope.runTest {
+ val rotatedToPortrait by collectLastValue(underTest.rotatedToPortrait)
+ assertThat(rotatedToPortrait).isEqualTo(false)
+
+ repository.setCommunalContainerOrientation(ORIENTATION_PORTRAIT)
+ runCurrent()
+ assertThat(rotatedToPortrait).isEqualTo(false)
+
+ repository.setCommunalContainerOrientation(ORIENTATION_LANDSCAPE)
+ runCurrent()
+ assertThat(rotatedToPortrait).isEqualTo(false)
+
+ repository.setCommunalContainerOrientation(ORIENTATION_PORTRAIT)
+ runCurrent()
+ assertThat(rotatedToPortrait).isEqualTo(true)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
index 3ab920a46084..cdd093a410df 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
@@ -17,11 +17,20 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.res.Configuration
+import android.content.res.mainResources
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.util.LayoutDirection
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -29,30 +38,53 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.transitions.blurConfig
import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class GlanceableHubToLockscreenTransitionViewModelTest : SysuiTestCase() {
- val kosmos = testKosmos()
+@RunWith(ParameterizedAndroidJunit4::class)
+class GlanceableHubToLockscreenTransitionViewModelTest(flags: FlagsParameterization) :
+ SysuiTestCase() {
+ val kosmos = testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
val testScope = kosmos.testScope
val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
val configurationRepository = kosmos.fakeConfigurationRepository
+ val keyguardStateController: KeyguardStateController = kosmos.keyguardStateController
val underTest by lazy { kosmos.glanceableHubToLockscreenTransitionViewModel }
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(FLAG_GLANCEABLE_HUB_V2)
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
@Test
fun lockscreenFadeIn() =
kosmos.runTest {
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+
val values by collectValues(underTest.keyguardAlpha)
assertThat(values).isEmpty()
@@ -79,6 +111,116 @@ class GlanceableHubToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun lockscreenFadeIn_fromHubInLandscape() =
+ kosmos.runTest {
+ kosmos.setCommunalV2ConfigEnabled(true)
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed).thenReturn(false)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ communalSceneRepository.setCommunalContainerOrientation(
+ Configuration.ORIENTATION_LANDSCAPE
+ )
+
+ val values by collectValues(underTest.keyguardAlpha)
+ assertThat(values).isEmpty()
+
+ // Exit hub to lockscreen
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Communal,
+ toScene = CommunalScenes.Blank,
+ currentScene = flowOf(CommunalScenes.Blank),
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalSceneInteractor.setTransitionState(transitionState)
+ progress.value = .2f
+
+ // Still in landscape
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0.1f),
+ // start here..
+ step(0.5f),
+ ),
+ testScope,
+ )
+
+ // Communal container is rotated to portrait
+ communalSceneRepository.setCommunalContainerOrientation(
+ Configuration.ORIENTATION_PORTRAIT
+ )
+ runCurrent()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0.6f),
+ step(0.7f),
+ // should stop here..
+ step(0.8f),
+ step(1f),
+ ),
+ testScope,
+ )
+ // Scene transition finished.
+ progress.value = 1f
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(step(1f, TransitionState.FINISHED)),
+ testScope,
+ )
+
+ assertThat(values).hasSize(4)
+ // onStart
+ assertThat(values[0]).isEqualTo(0f)
+ assertThat(values[1]).isEqualTo(0f)
+ assertThat(values[2]).isEqualTo(1f)
+ // onFinish
+ assertThat(values[3]).isEqualTo(1f)
+ }
+
+ @Test
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun lockscreenFadeIn_v2FlagDisabledAndFromHubInLandscape() =
+ kosmos.runTest {
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed).thenReturn(false)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ // Rotation is not enabled so communal container is in portrait.
+ communalSceneRepository.setCommunalContainerOrientation(
+ Configuration.ORIENTATION_PORTRAIT
+ )
+
+ val values by collectValues(underTest.keyguardAlpha)
+ assertThat(values).isEmpty()
+
+ // Exit hub to lockscreen
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ // Should start running here...
+ step(0.1f),
+ step(0.2f),
+ step(0.3f),
+ step(0.4f),
+ // ...up to here
+ step(0.5f),
+ step(0.6f),
+ step(0.7f),
+ step(0.8f),
+ step(1f),
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(4)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+ }
+
+ @Test
fun lockscreenTranslationX() =
kosmos.runTest {
val config: Configuration = mock()
@@ -89,6 +231,8 @@ class GlanceableHubToLockscreenTransitionViewModelTest : SysuiTestCase() {
R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
100,
)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+
val values by collectValues(underTest.keyguardTranslationX)
assertThat(values).isEmpty()
@@ -108,6 +252,44 @@ class GlanceableHubToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun lockscreenTranslationX_fromHubInLandscape() =
+ kosmos.runTest {
+ kosmos.setCommunalV2ConfigEnabled(true)
+ val config: Configuration = mock()
+ whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR)
+ configurationRepository.onConfigurationChange(config)
+
+ configurationRepository.setDimensionPixelSize(
+ R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
+ 100,
+ )
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed).thenReturn(false)
+
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ communalSceneRepository.setCommunalContainerOrientation(
+ Configuration.ORIENTATION_LANDSCAPE
+ )
+
+ val values by collectValues(underTest.keyguardTranslationX)
+ assertThat(values).isEmpty()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0.3f),
+ step(0.5f),
+ step(0.7f),
+ step(1f),
+ step(1f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ // no translation-x animation
+ values.forEach { assertThat(it.value).isEqualTo(0f) }
+ }
+
+ @Test
fun lockscreenTranslationX_resetsAfterCancellation() =
kosmos.runTest {
val config: Configuration = mock()
@@ -118,6 +300,9 @@ class GlanceableHubToLockscreenTransitionViewModelTest : SysuiTestCase() {
R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
100,
)
+
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+
val values by collectValues(underTest.keyguardTranslationX)
assertThat(values).isEmpty()
@@ -137,6 +322,42 @@ class GlanceableHubToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun lockscreenTranslationX_resetsAfterCancellation_fromHubInLandscape() =
+ kosmos.runTest {
+ kosmos.setCommunalV2ConfigEnabled(true)
+ val config: Configuration = mock()
+ whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR)
+ configurationRepository.onConfigurationChange(config)
+
+ configurationRepository.setDimensionPixelSize(
+ R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
+ 100,
+ )
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed).thenReturn(false)
+
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ communalSceneRepository.setCommunalContainerOrientation(
+ Configuration.ORIENTATION_LANDSCAPE
+ )
+
+ val values by collectValues(underTest.keyguardTranslationX)
+ assertThat(values).isEmpty()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0.3f),
+ step(0.6f),
+ step(0.9f, TransitionState.CANCELED),
+ ),
+ testScope,
+ )
+ // no translation-x animation
+ values.forEach { assertThat(it.value).isEqualTo(0f) }
+ }
+
+ @Test
@DisableSceneContainer
fun blurBecomesMinValueImmediately() =
kosmos.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index fe213a6ebbf0..71e09d982494 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -17,12 +17,19 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.content.res.Configuration
+import android.content.res.mainResources
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.view.View
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
@@ -35,6 +42,9 @@ import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.sceneContainerRepository
@@ -48,6 +58,7 @@ import com.android.systemui.statusbar.notification.data.repository.activeNotific
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -69,7 +80,8 @@ import platform.test.runner.parameterized.Parameters
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos =
+ testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
private val testScope = kosmos.testScope
private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
@@ -419,6 +431,7 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase()
}
@Test
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
fun alpha_transitionFromHubToLockscreen_isOne() =
testScope.runTest {
val alpha by collectLastValue(underTest.alpha(viewState))
@@ -439,6 +452,84 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase()
}
@Test
+ @DisableSceneContainer
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun alpha_transitionFromHubToLockscreenInLandscape_isOne() =
+ kosmos.runTest {
+ setCommunalV2ConfigEnabled(true)
+ whenever(keyguardStateController.isKeyguardScreenRotationAllowed).thenReturn(false)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ communalSceneRepository.setCommunalContainerOrientation(
+ Configuration.ORIENTATION_LANDSCAPE
+ )
+
+ val alpha by collectLastValue(underTest.alpha(viewState))
+
+ // Transition to the glanceable hub and back.
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GLANCEABLE_HUB,
+ testScope,
+ )
+
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ runCurrent()
+
+ // Exit hub to lockscreen
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow(
+ ObservableTransitionState.Transition(
+ fromScene = CommunalScenes.Communal,
+ toScene = CommunalScenes.Blank,
+ currentScene = flowOf(CommunalScenes.Blank),
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalSceneInteractor.setTransitionState(transitionState)
+ progress.value = .4f
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ value = 0f,
+ ),
+ TransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.RUNNING,
+ value = 0.4f,
+ ),
+ ),
+ testScope,
+ )
+
+ communalSceneRepository.setCommunalContainerOrientation(
+ Configuration.ORIENTATION_PORTRAIT
+ )
+ runCurrent()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED,
+ value = 1f,
+ )
+ ),
+ testScope,
+ )
+
+ assertThat(alpha).isEqualTo(1.0f)
+ }
+
+ @Test
fun alpha_emitsOnShadeExpansion() =
testScope.runTest {
val alpha by collectLastValue(underTest.alpha(viewState))
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
index bf4445ba18db..2b8cf008c0c7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.data.repository
+import android.content.res.Configuration
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.OverlayKey
@@ -49,6 +50,9 @@ interface CommunalSceneRepository {
/** Exposes the transition state of the communal [SceneTransitionLayout]. */
val transitionState: StateFlow<ObservableTransitionState>
+ /** Current orientation of the communal container. */
+ val communalContainerOrientation: StateFlow<Int>
+
/** Updates the requested scene. */
fun changeScene(toScene: SceneKey, transitionKey: TransitionKey? = null)
@@ -64,6 +68,9 @@ interface CommunalSceneRepository {
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
fun setTransitionState(transitionState: Flow<ObservableTransitionState>?)
+
+ /** Set the current orientation of the communal container. */
+ fun setCommunalContainerOrientation(orientation: Int)
}
@SysUISingleton
@@ -89,6 +96,11 @@ constructor(
initialValue = defaultTransitionState,
)
+ private val _communalContainerOrientation =
+ MutableStateFlow(Configuration.ORIENTATION_UNDEFINED)
+ override val communalContainerOrientation: StateFlow<Int> =
+ _communalContainerOrientation.asStateFlow()
+
override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) {
applicationScope.launch {
// SceneTransitionLayout state updates must be triggered on the thread the STL was
@@ -105,6 +117,10 @@ constructor(
}
}
+ override fun setCommunalContainerOrientation(orientation: Int) {
+ _communalContainerOrientation.value = orientation
+ }
+
override suspend fun showHubFromPowerButton() {
// If keyguard is not showing yet, the hub view is not ready and the
// [SceneDataSourceDelegator] will still be using the default [NoOpSceneDataSource]
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index fed99d71fa3b..a112dd25e006 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.domain.interactor
+import android.content.res.Configuration
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
@@ -32,6 +33,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.pairwiseBy
import javax.inject.Inject
@@ -58,6 +60,7 @@ constructor(
private val repository: CommunalSceneRepository,
private val logger: CommunalSceneLogger,
private val sceneInteractor: SceneInteractor,
+ private val keyguardStateController: KeyguardStateController,
) {
private val _isLaunchingWidget = MutableStateFlow(false)
@@ -68,6 +71,30 @@ constructor(
_isLaunchingWidget.value = launching
}
+ /**
+ * Whether screen will be rotated to portrait if transitioned out of hub to keyguard screens.
+ */
+ var willRotateToPortrait: Flow<Boolean> =
+ repository.communalContainerOrientation
+ .map {
+ it == Configuration.ORIENTATION_LANDSCAPE &&
+ !keyguardStateController.isKeyguardScreenRotationAllowed()
+ }
+ .distinctUntilChanged()
+
+ /** Whether communal container is rotated to portrait. Emits an initial value of false. */
+ val rotatedToPortrait: StateFlow<Boolean> =
+ repository.communalContainerOrientation
+ .pairwiseBy(initialValue = false) { old, new ->
+ old == Configuration.ORIENTATION_LANDSCAPE &&
+ new == Configuration.ORIENTATION_PORTRAIT
+ }
+ .stateIn(applicationScope, SharingStarted.Eagerly, false)
+
+ fun setCommunalContainerOrientation(orientation: Int) {
+ repository.setCommunalContainerOrientation(orientation)
+ }
+
fun interface OnSceneAboutToChangeListener {
/** Notifies that the scene is about to change to [toScene]. */
fun onSceneAboutToChange(toScene: SceneKey, keyguardState: KeyguardState?)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
index a84c45732169..49dc59ac0004 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
@@ -33,4 +33,6 @@ object CommunalTransitionKeys {
val FromEditMode = TransitionKey("FromEditMode")
/** Swipes the glanceable hub in/out of view */
val Swipe = TransitionKey("Swipe")
+ /** Swipes out of glanceable hub in landscape orientation */
+ val SwipeInLandscape = TransitionKey("SwipeInLandscape")
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 5a4b0b0e2d24..a6309d1be03d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -386,6 +386,11 @@ constructor(
}
}
+ val swipeFromHubInLandscape: Flow<Boolean> = communalSceneInteractor.willRotateToPortrait
+
+ fun onOrientationChange(orientation: Int) =
+ communalSceneInteractor.setCommunalContainerOrientation(orientation)
+
companion object {
const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 3ad862b761fc..be0cf62b0526 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -251,6 +251,8 @@ constructor(
* Set at 400ms for parity with [FromLockscreenTransitionInteractor]
*/
val DEFAULT_DURATION = 400.milliseconds
+ // To lockscreen duration must be at least 500ms to allow for potential screen rotation
+ // during the transition while the animation begins after 500ms.
val TO_LOCKSCREEN_DURATION = 1.seconds
val TO_BOUNCER_DURATION = 400.milliseconds
val TO_OCCLUDED_DURATION = 450.milliseconds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index bcbe66642d11..fd5783ef7f8e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -19,7 +19,10 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.util.LayoutDirection
import com.android.app.animation.Interpolators.EMPHASIZED
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.shared.model.Edge
@@ -34,21 +37,32 @@ import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/**
* Breaks down GLANCEABLE_HUB->LOCKSCREEN transition into discrete steps for corresponding views to
* consume.
*/
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class GlanceableHubToLockscreenTransitionViewModel
@Inject
constructor(
+ @Application applicationScope: CoroutineScope,
@ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
+ communalSceneInteractor: CommunalSceneInteractor,
+ communalSettingsInteractor: CommunalSettingsInteractor,
private val blurFactory: GlanceableHubBlurComponent.Factory,
) : GlanceableHubTransition, DeviceEntryIconTransition {
private val transitionAnimation =
@@ -59,18 +73,45 @@ constructor(
)
.setupWithoutSceneContainer(edge = Edge.create(from = GLANCEABLE_HUB, to = LOCKSCREEN))
+ // Whether screen rotation will happen with the transition. Only emit when idle so ongoing
+ // animation won't be interrupted when orientation is updated during the transition.
+ private val willRotateToPortraitInTransition: StateFlow<Boolean> =
+ if (!communalSettingsInteractor.isV2FlagEnabled()) {
+ flowOf(false)
+ } else {
+ communalSceneInteractor.isIdleOnCommunal.combineTransform(
+ communalSceneInteractor.willRotateToPortrait
+ ) { isIdle, willRotate ->
+ if (isIdle) emit(willRotate)
+ }
+ }
+ .stateIn(applicationScope, SharingStarted.Eagerly, false)
+
override val windowBlurRadius: Flow<Float> =
blurFactory.create(transitionAnimation).getBlurProvider().exitBlurRadius
val keyguardAlpha: Flow<Float> =
- transitionAnimation.sharedFlow(
- duration = 167.milliseconds,
- startTime = 167.milliseconds,
- onStep = { it },
- onFinish = { 1f },
- onCancel = { 0f },
- name = "GLANCEABLE_HUB->LOCKSCREEN: keyguardAlpha",
- )
+ willRotateToPortraitInTransition.flatMapLatest { willRotate ->
+ transitionAnimation.sharedFlow(
+ duration = 167.milliseconds,
+ // If will rotate, start later to leave time for screen rotation.
+ startTime = if (willRotate) 500.milliseconds else 167.milliseconds,
+ onStep = { step ->
+ if (willRotate) {
+ if (!communalSceneInteractor.rotatedToPortrait.value) {
+ 0f
+ } else {
+ 1f
+ }
+ } else {
+ step
+ }
+ },
+ onFinish = { 1f },
+ onCancel = { 0f },
+ name = "GLANCEABLE_HUB->LOCKSCREEN: keyguardAlpha",
+ )
+ }
// Show UMO as long as keyguard is not visible.
val showUmo: Flow<Boolean> = keyguardAlpha.map { alpha -> alpha == 0f }
@@ -84,7 +125,14 @@ constructor(
.flatMapLatest { translatePx: Int ->
transitionAnimation.sharedFlowWithState(
duration = TO_LOCKSCREEN_DURATION,
- onStep = { value -> -translatePx + value * translatePx },
+ onStep = { value ->
+ // do not animate translation-x if screen rotation will happen
+ if (willRotateToPortraitInTransition.value) {
+ 0f
+ } else {
+ -translatePx + value * translatePx
+ }
+ },
interpolator = EMPHASIZED,
// Move notifications back to their original position since they can be
// accessed from the shade, and also keyguard elements in case the animation
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
index 3f35bb9f3520..38e6c8a0cdea 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
@@ -1,5 +1,6 @@
package com.android.systemui.communal.data.repository
+import android.content.res.Configuration
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
@@ -9,6 +10,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
@@ -48,4 +50,13 @@ class FakeCommunalSceneRepository(
override fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
_transitionState.value = transitionState
}
+
+ private val _communalContainerOrientation =
+ MutableStateFlow(Configuration.ORIENTATION_UNDEFINED)
+ override val communalContainerOrientation: StateFlow<Int> =
+ _communalContainerOrientation.asStateFlow()
+
+ override fun setCommunalContainerOrientation(orientation: Int) {
+ _communalContainerOrientation.value = orientation
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
index 209d1636e380..8834af581e73 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.communal.shared.log.communalSceneLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.statusbar.policy.keyguardStateController
val Kosmos.communalSceneInteractor: CommunalSceneInteractor by
Kosmos.Fixture {
@@ -29,5 +30,6 @@ val Kosmos.communalSceneInteractor: CommunalSceneInteractor by
repository = communalSceneRepository,
logger = communalSceneLogger,
sceneInteractor = sceneInteractor,
+ keyguardStateController = keyguardStateController,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt
index 530981c489e8..02e63a42d87d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt
@@ -17,15 +17,21 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
val Kosmos.glanceableHubToLockscreenTransitionViewModel by Fixture {
GlanceableHubToLockscreenTransitionViewModel(
+ applicationScope = applicationCoroutineScope,
configurationInteractor = configurationInteractor,
animationFlow = keyguardTransitionAnimationFlow,
+ communalSceneInteractor = communalSceneInteractor,
+ communalSettingsInteractor = communalSettingsInteractor,
blurFactory = glanceableHubBlurComponentFactory,
)
}