Merge "Implement UMO transitions to & from the communal hub" into main
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index 82d9437..a02c5ce 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -23,6 +23,8 @@
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule
import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
+import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel
+import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModelImpl
import com.android.systemui.communal.widgets.CommunalWidgetModule
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
@@ -47,4 +49,9 @@
fun bindEditWidgetsActivityStarter(
starter: EditWidgetsActivityStarterImpl
): EditWidgetsActivityStarter
+
+ @Binds
+ fun bindCommunalTransitionViewModel(
+ impl: CommunalTransitionViewModelImpl
+ ): CommunalTransitionViewModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
new file mode 100644
index 0000000..eed0aa3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.communal.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.merge
+
+/** View model for transitions related to the communal hub. */
+interface CommunalTransitionViewModel {
+ val isUmoOnCommunal: Flow<Boolean>
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalTransitionViewModelImpl
+@Inject
+constructor(
+ glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel,
+ lockscreenToGlanceableHubTransitionViewModel: LockscreenToGlanceableHubTransitionViewModel,
+ dreamToGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel,
+ glanceableHubToDreamTransitionViewModel: GlanceableHubToDreamingTransitionViewModel,
+) : CommunalTransitionViewModel {
+ /**
+ * Whether UMO location should be on communal. This flow is responsive to transitions so that a
+ * new value is emitted at the right step of a transition to/from communal hub that the location
+ * of UMO should be updated.
+ */
+ override val isUmoOnCommunal: Flow<Boolean> =
+ merge(
+ lockscreenToGlanceableHubTransitionViewModel.showUmo,
+ glanceableHubToLockscreenTransitionViewModel.showUmo,
+ dreamToGlanceableHubTransitionViewModel.showUmo,
+ glanceableHubToDreamTransitionViewModel.showUmo,
+ )
+ .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
index c64f277..5cb2ec9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
@@ -28,6 +28,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
@@ -65,6 +66,15 @@
name = "DREAMING->GLANCEABLE_HUB: dreamOverlayAlpha",
)
+ // Show UMO once the transition starts.
+ val showUmo: Flow<Boolean> =
+ transitionAnimation
+ .sharedFlow(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ onStep = { it },
+ )
+ .map { step -> step != 0f }
+
private companion object {
val TO_GLANCEABLE_HUB_DURATION = 1.seconds
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
index 478c4faa..90be4f9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
@@ -28,6 +28,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
@@ -66,6 +67,15 @@
)
}
+ // Show UMO until transition finishes.
+ val showUmo: Flow<Boolean> =
+ transitionAnimation
+ .sharedFlow(
+ duration = FROM_GLANCEABLE_HUB_DURATION,
+ onStep = { it },
+ )
+ .map { step -> step != 1f }
+
private companion object {
val FROM_GLANCEABLE_HUB_DURATION = 1.seconds
}
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 e5b5964..f81598f 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
@@ -64,6 +64,9 @@
)
.onStart { emit(0f) }
+ // Show UMO as long as keyguard is not visible.
+ val showUmo: Flow<Boolean> = keyguardAlpha.map { alpha -> alpha == 0f }
+
val keyguardTranslationX: Flow<StateToValue> =
configurationInteractor
.dimensionPixelSize(R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index 978e71e..0c33e18 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -63,6 +63,9 @@
)
.onStart { emit(1f) }
+ // Show UMO as long as keyguard is not visible.
+ val showUmo: Flow<Boolean> = keyguardAlpha.map { alpha -> alpha == 0f }
+
val keyguardTranslationX: Flow<StateToValue> =
configurationInteractor
.dimensionPixelSize(R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x)
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 dbd71f3..a4f3e21 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
@@ -36,7 +36,7 @@
import com.android.app.animation.Interpolators
import com.android.app.tracing.traceSection
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -58,7 +58,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.animation.UniqueObjectHostView
-import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -102,7 +101,7 @@
private val mediaManager: MediaDataManager,
private val keyguardViewController: KeyguardViewController,
private val dreamOverlayStateController: DreamOverlayStateController,
- private val communalInteractor: CommunalInteractor,
+ communalTransitionViewModel: CommunalTransitionViewModel,
configurationController: ConfigurationController,
wakefulnessLifecycle: WakefulnessLifecycle,
shadeInteractor: ShadeInteractor,
@@ -587,11 +586,10 @@
// Listen to the communal UI state. Make sure that communal UI is showing and hub itself is
// available, ie. not disabled and able to be shown.
coroutineScope.launch {
- and(communalInteractor.isCommunalShowing, communalInteractor.isCommunalAvailable)
- .collect { value ->
- isCommunalShowing = value
- updateDesiredLocation()
- }
+ communalTransitionViewModel.isUmoOnCommunal.collect { value ->
+ isCommunalShowing = value
+ updateDesiredLocation(forceNoAnimation = true)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 45f49f0..fa28036f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -24,11 +24,8 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.domain.interactor.setCommunalAvailable
-import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.ui.viewmodel.fakeCommunalTransitionViewModel
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -115,10 +112,10 @@
private lateinit var isQsBypassingShade: MutableStateFlow<Boolean>
private lateinit var mediaFrame: ViewGroup
private val configurationController = FakeConfigurationController()
- private val communalInteractor = kosmos.communalInteractor
private val settings = FakeSettings()
private lateinit var testableLooper: TestableLooper
private lateinit var fakeHandler: FakeHandler
+ private var communalTransitionViewModel = kosmos.fakeCommunalTransitionViewModel
@Before
fun setup() {
@@ -142,7 +139,7 @@
mediaDataManager,
keyguardViewController,
dreamOverlayStateController,
- communalInteractor,
+ communalTransitionViewModel,
configurationController,
wakefulnessLifecycle,
shadeInteractor,
@@ -510,11 +507,7 @@
@Test
fun testCommunalLocation() =
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
- kosmos.setCommunalAvailable(true)
- runCurrent()
-
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ communalTransitionViewModel.setIsUmoOnCommunal(true)
runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
@@ -526,7 +519,7 @@
)
clearInvocations(mediaCarouselController)
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+ communalTransitionViewModel.setIsUmoOnCommunal(false)
runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
@@ -541,15 +534,11 @@
@Test
fun testCommunalLocation_showsOverLockscreen() =
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
- kosmos.setCommunalAvailable(true)
- runCurrent()
-
// Device is on lock screen.
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
// UMO goes to communal even over the lock screen.
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ communalTransitionViewModel.setIsUmoOnCommunal(true)
runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
@@ -564,14 +553,10 @@
@Test
fun testCommunalLocation_showsUntilQsExpands() =
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
- kosmos.setCommunalAvailable(true)
- runCurrent()
-
// Device is on lock screen.
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ communalTransitionViewModel.setIsUmoOnCommunal(true)
runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..eaa657b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.communal.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.communalTransitionViewModel by
+ Kosmos.Fixture<CommunalTransitionViewModel> { fakeCommunalTransitionViewModel }
+
+val Kosmos.fakeCommunalTransitionViewModel by Kosmos.Fixture { FakeCommunalTransitionViewModel() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/FakeCommunalTransitionViewModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/FakeCommunalTransitionViewModel.kt
new file mode 100644
index 0000000..409cc1d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/FakeCommunalTransitionViewModel.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.communal.ui.viewmodel
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeCommunalTransitionViewModel : CommunalTransitionViewModel {
+ private val _isUmoOnCommunal = MutableStateFlow(false)
+ override val isUmoOnCommunal: Flow<Boolean> = _isUmoOnCommunal
+
+ fun setIsUmoOnCommunal(value: Boolean) {
+ _isUmoOnCommunal.value = value
+ }
+}