Skip to content
Snippets Groups Projects
Commit f320e4b5 authored by Darrell Shi's avatar Darrell Shi Committed by Android (Google) Code Review
Browse files

Merge "Implement UMO transitions to & from the communal hub" into main

parents cd18a51a eb8bf797
No related branches found
No related tags found
No related merge requests found
Showing
with 157 additions and 30 deletions
...@@ -23,6 +23,8 @@ import com.android.systemui.communal.data.repository.CommunalRepositoryModule ...@@ -23,6 +23,8 @@ import com.android.systemui.communal.data.repository.CommunalRepositoryModule
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule
import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule 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.CommunalWidgetModule
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
...@@ -47,4 +49,9 @@ interface CommunalModule { ...@@ -47,4 +49,9 @@ interface CommunalModule {
fun bindEditWidgetsActivityStarter( fun bindEditWidgetsActivityStarter(
starter: EditWidgetsActivityStarterImpl starter: EditWidgetsActivityStarterImpl
): EditWidgetsActivityStarter ): EditWidgetsActivityStarter
@Binds
fun bindCommunalTransitionViewModel(
impl: CommunalTransitionViewModelImpl
): CommunalTransitionViewModel
} }
/*
* 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()
}
...@@ -28,6 +28,7 @@ import kotlin.time.Duration.Companion.seconds ...@@ -28,6 +28,7 @@ import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton @SysUISingleton
...@@ -65,6 +66,15 @@ constructor( ...@@ -65,6 +66,15 @@ constructor(
name = "DREAMING->GLANCEABLE_HUB: dreamOverlayAlpha", 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 { private companion object {
val TO_GLANCEABLE_HUB_DURATION = 1.seconds val TO_GLANCEABLE_HUB_DURATION = 1.seconds
} }
......
...@@ -28,6 +28,7 @@ import kotlin.time.Duration.Companion.seconds ...@@ -28,6 +28,7 @@ import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton @SysUISingleton
...@@ -66,6 +67,15 @@ constructor( ...@@ -66,6 +67,15 @@ constructor(
) )
} }
// 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 { private companion object {
val FROM_GLANCEABLE_HUB_DURATION = 1.seconds val FROM_GLANCEABLE_HUB_DURATION = 1.seconds
} }
......
...@@ -64,6 +64,9 @@ constructor( ...@@ -64,6 +64,9 @@ constructor(
) )
.onStart { emit(0f) } .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> = val keyguardTranslationX: Flow<StateToValue> =
configurationInteractor configurationInteractor
.dimensionPixelSize(R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x) .dimensionPixelSize(R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x)
......
...@@ -63,6 +63,9 @@ constructor( ...@@ -63,6 +63,9 @@ constructor(
) )
.onStart { emit(1f) } .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> = val keyguardTranslationX: Flow<StateToValue> =
configurationInteractor configurationInteractor
.dimensionPixelSize(R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x) .dimensionPixelSize(R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x)
......
...@@ -36,7 +36,7 @@ import androidx.annotation.VisibleForTesting ...@@ -36,7 +36,7 @@ import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators import com.android.app.animation.Interpolators
import com.android.app.tracing.traceSection import com.android.app.tracing.traceSection
import com.android.keyguard.KeyguardViewController 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.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
...@@ -58,7 +58,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController ...@@ -58,7 +58,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.SplitShadeStateController import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
...@@ -102,7 +101,7 @@ constructor( ...@@ -102,7 +101,7 @@ constructor(
private val mediaManager: MediaDataManager, private val mediaManager: MediaDataManager,
private val keyguardViewController: KeyguardViewController, private val keyguardViewController: KeyguardViewController,
private val dreamOverlayStateController: DreamOverlayStateController, private val dreamOverlayStateController: DreamOverlayStateController,
private val communalInteractor: CommunalInteractor, communalTransitionViewModel: CommunalTransitionViewModel,
configurationController: ConfigurationController, configurationController: ConfigurationController,
wakefulnessLifecycle: WakefulnessLifecycle, wakefulnessLifecycle: WakefulnessLifecycle,
shadeInteractor: ShadeInteractor, shadeInteractor: ShadeInteractor,
...@@ -587,11 +586,10 @@ constructor( ...@@ -587,11 +586,10 @@ constructor(
// Listen to the communal UI state. Make sure that communal UI is showing and hub itself is // 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. // available, ie. not disabled and able to be shown.
coroutineScope.launch { coroutineScope.launch {
and(communalInteractor.isCommunalShowing, communalInteractor.isCommunalAvailable) communalTransitionViewModel.isUmoOnCommunal.collect { value ->
.collect { value -> isCommunalShowing = value
isCommunalShowing = value updateDesiredLocation(forceNoAnimation = true)
updateDesiredLocation() }
}
} }
} }
......
...@@ -24,11 +24,8 @@ import android.view.ViewGroup ...@@ -24,11 +24,8 @@ import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController import com.android.keyguard.KeyguardViewController
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.ui.viewmodel.fakeCommunalTransitionViewModel
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle
...@@ -115,10 +112,10 @@ class MediaHierarchyManagerTest : SysuiTestCase() { ...@@ -115,10 +112,10 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
private lateinit var isQsBypassingShade: MutableStateFlow<Boolean> private lateinit var isQsBypassingShade: MutableStateFlow<Boolean>
private lateinit var mediaFrame: ViewGroup private lateinit var mediaFrame: ViewGroup
private val configurationController = FakeConfigurationController() private val configurationController = FakeConfigurationController()
private val communalInteractor = kosmos.communalInteractor
private val settings = FakeSettings() private val settings = FakeSettings()
private lateinit var testableLooper: TestableLooper private lateinit var testableLooper: TestableLooper
private lateinit var fakeHandler: FakeHandler private lateinit var fakeHandler: FakeHandler
private var communalTransitionViewModel = kosmos.fakeCommunalTransitionViewModel
@Before @Before
fun setup() { fun setup() {
...@@ -142,7 +139,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { ...@@ -142,7 +139,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
mediaDataManager, mediaDataManager,
keyguardViewController, keyguardViewController,
dreamOverlayStateController, dreamOverlayStateController,
communalInteractor, communalTransitionViewModel,
configurationController, configurationController,
wakefulnessLifecycle, wakefulnessLifecycle,
shadeInteractor, shadeInteractor,
...@@ -510,11 +507,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { ...@@ -510,11 +507,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Test @Test
fun testCommunalLocation() = fun testCommunalLocation() =
testScope.runTest { testScope.runTest {
mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB) communalTransitionViewModel.setIsUmoOnCommunal(true)
kosmos.setCommunalAvailable(true)
runCurrent()
communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
runCurrent() runCurrent()
verify(mediaCarouselController) verify(mediaCarouselController)
.onDesiredLocationChanged( .onDesiredLocationChanged(
...@@ -526,7 +519,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { ...@@ -526,7 +519,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
) )
clearInvocations(mediaCarouselController) clearInvocations(mediaCarouselController)
communalInteractor.onSceneChanged(CommunalSceneKey.Blank) communalTransitionViewModel.setIsUmoOnCommunal(false)
runCurrent() runCurrent()
verify(mediaCarouselController) verify(mediaCarouselController)
.onDesiredLocationChanged( .onDesiredLocationChanged(
...@@ -541,15 +534,11 @@ class MediaHierarchyManagerTest : SysuiTestCase() { ...@@ -541,15 +534,11 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Test @Test
fun testCommunalLocation_showsOverLockscreen() = fun testCommunalLocation_showsOverLockscreen() =
testScope.runTest { testScope.runTest {
mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
kosmos.setCommunalAvailable(true)
runCurrent()
// Device is on lock screen. // Device is on lock screen.
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
// UMO goes to communal even over the lock screen. // UMO goes to communal even over the lock screen.
communalInteractor.onSceneChanged(CommunalSceneKey.Communal) communalTransitionViewModel.setIsUmoOnCommunal(true)
runCurrent() runCurrent()
verify(mediaCarouselController) verify(mediaCarouselController)
.onDesiredLocationChanged( .onDesiredLocationChanged(
...@@ -564,14 +553,10 @@ class MediaHierarchyManagerTest : SysuiTestCase() { ...@@ -564,14 +553,10 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Test @Test
fun testCommunalLocation_showsUntilQsExpands() = fun testCommunalLocation_showsUntilQsExpands() =
testScope.runTest { testScope.runTest {
mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
kosmos.setCommunalAvailable(true)
runCurrent()
// Device is on lock screen. // Device is on lock screen.
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
communalInteractor.onSceneChanged(CommunalSceneKey.Communal) communalTransitionViewModel.setIsUmoOnCommunal(true)
runCurrent() runCurrent()
verify(mediaCarouselController) verify(mediaCarouselController)
.onDesiredLocationChanged( .onDesiredLocationChanged(
......
/*
* 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() }
/*
* 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
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment