diff options
| author | 2023-05-05 16:26:32 -0700 | |
|---|---|---|
| committer | 2023-05-09 14:52:00 -0700 | |
| commit | 1240a66fe07626fe9e5720a8220512d9f981bca3 (patch) | |
| tree | e2c9a3d749a46a79eba0651d480ae21ba3eba796 | |
| parent | 63561ed2df4493f852ebb16b927a77a96d5da477 (diff) | |
[flexiglass] Foundation for placeholder shade scene.
Foundational parts of the shade scene including application state
(which is still not hooked up to real sources of truth), business logic,
and a view-model that's only good enough for placeholder scene UI.
Bug: 280887022
Test: unit tests included. Manually tested with the entire relation
chain in the Compose Gallery testdbed app.
Change-Id: If1a64a9346343ad2242c39049d1a57ba7c8d76a8
2 files changed, 208 insertions, 0 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt new file mode 100644 index 000000000000..dcae258ed76f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 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.shade.ui.viewmodel + +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.LockScreenSceneInteractor +import com.android.systemui.scene.shared.model.SceneKey +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +/** Models UI state and handles user input for the shade scene. */ +class ShadeSceneViewModel +@AssistedInject +constructor( + @Application private val applicationScope: CoroutineScope, + lockScreenSceneInteractorFactory: LockScreenSceneInteractor.Factory, + @Assisted private val containerName: String, +) { + private val lockScreenInteractor: LockScreenSceneInteractor = + lockScreenSceneInteractorFactory.create(containerName) + + /** The key of the scene we should switch to when swiping up. */ + val upDestinationSceneKey: StateFlow<SceneKey> = + lockScreenInteractor.isDeviceLocked + .map { isLocked -> upDestinationSceneKey(isLocked = isLocked) } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = + upDestinationSceneKey( + isLocked = lockScreenInteractor.isDeviceLocked.value, + ), + ) + + /** Notifies that some content in the shade was clicked. */ + fun onContentClicked() { + lockScreenInteractor.dismissLockScreen() + } + + private fun upDestinationSceneKey( + isLocked: Boolean, + ): SceneKey { + return if (isLocked) SceneKey.LockScreen else SceneKey.Gone + } + + @AssistedFactory + interface Factory { + fun create( + containerName: String, + ): ShadeSceneViewModel + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt new file mode 100644 index 000000000000..688cce83a55d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2023 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.shade.ui.viewmodel + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.AuthenticationRepositoryImpl +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor +import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.bouncer.data.repo.BouncerRepository +import com.android.systemui.bouncer.domain.interactor.BouncerInteractor +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.domain.interactor.LockScreenSceneInteractor +import com.android.systemui.scene.data.repository.fakeSceneContainerRepository +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class ShadeSceneViewModelTest : SysuiTestCase() { + + private val testScope = TestScope() + private val sceneInteractor = + SceneInteractor( + repository = fakeSceneContainerRepository(), + ) + private val mAuthenticationInteractor = + AuthenticationInteractor( + applicationScope = testScope.backgroundScope, + repository = AuthenticationRepositoryImpl(), + ) + + private val underTest = + ShadeSceneViewModel( + applicationScope = testScope.backgroundScope, + lockScreenSceneInteractorFactory = + object : LockScreenSceneInteractor.Factory { + override fun create(containerName: String): LockScreenSceneInteractor { + return LockScreenSceneInteractor( + applicationScope = testScope.backgroundScope, + authenticationInteractor = mAuthenticationInteractor, + bouncerInteractorFactory = + object : BouncerInteractor.Factory { + override fun create(containerName: String): BouncerInteractor { + return BouncerInteractor( + applicationScope = testScope.backgroundScope, + applicationContext = context, + repository = BouncerRepository(), + authenticationInteractor = mAuthenticationInteractor, + sceneInteractor = sceneInteractor, + containerName = containerName, + ) + } + }, + sceneInteractor = sceneInteractor, + containerName = CONTAINER_NAME, + ) + } + }, + containerName = CONTAINER_NAME + ) + + @Test + fun upTransitionSceneKey_deviceLocked_lockScreen() = + testScope.runTest { + val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) + mAuthenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + mAuthenticationInteractor.lockDevice() + + assertThat(upTransitionSceneKey).isEqualTo(SceneKey.LockScreen) + } + + @Test + fun upTransitionSceneKey_deviceUnlocked_gone() = + testScope.runTest { + val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) + mAuthenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + mAuthenticationInteractor.unlockDevice() + + assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun onContentClicked_deviceUnlocked_switchesToGone() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + mAuthenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + mAuthenticationInteractor.unlockDevice() + runCurrent() + + underTest.onContentClicked() + + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + } + + @Test + fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + mAuthenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + mAuthenticationInteractor.lockDevice() + runCurrent() + + underTest.onContentClicked() + + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + } + + companion object { + private const val CONTAINER_NAME = "container1" + } +} |