summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alejandro Nijamkin <nijamkin@google.com> 2023-08-16 17:16:39 -0700
committer Ale Nijamkin <nijamkin@google.com> 2023-08-21 23:35:22 +0000
commitc73f5616bc7ec67641ad2b4b75bf5cb9a9e329e8 (patch)
tree788eff4260dd8e2179042b153d835f4ad15283d5
parent8a5c6453efe75b481bd400fcd2c44e61bfec1ab8 (diff)
[flexiglass] Lockscreen scene
Adds the standalone KeyguardRootView to LockscreenScene. Note how we remove it from its parent ViewGroup, if there is one, whenever our view factory gets called. This is needed so there is only one KeyguardRootView at all times (or it crashes because the NSSL gets initialized twice). Bug: 280879610 Test: manually verified that the lockscreen scene renders the real lockscreen Test: manually verified that pulling down brings down the shade and pulling up brings up the bouncer Test: manually verified the ability to unlock via PIN bouncer and face unlock and then re-lock the device via power button + timeout - no crash Test: manually verified correct rendering in AOD Change-Id: I54c11bccc7a10e43afea5dbaa09013e72b0a806f
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt1
7 files changed, 92 insertions, 133 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index da48762e1960..0a100babde75 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -14,26 +14,20 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.ui.composable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.material3.Button
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
+import android.view.View
+import android.view.ViewGroup
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -42,6 +36,7 @@ import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.ui.composable.ComposableScene
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@@ -54,6 +49,7 @@ class LockscreenScene
constructor(
@Application private val applicationScope: CoroutineScope,
private val viewModel: LockscreenSceneViewModel,
+ @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
) : ComposableScene {
override val key = SceneKey.Lockscreen
@@ -72,6 +68,7 @@ constructor(
) {
LockscreenScene(
viewModel = viewModel,
+ viewProvider = viewProvider,
modifier = modifier,
)
}
@@ -89,25 +86,22 @@ constructor(
@Composable
private fun LockscreenScene(
viewModel: LockscreenSceneViewModel,
+ viewProvider: () -> View,
modifier: Modifier = Modifier,
) {
- // TODO(b/280879610): implement the real UI.
-
- val lockButtonIcon: Icon by viewModel.lockButtonIcon.collectAsState()
-
- Box(modifier = modifier) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.align(Alignment.Center)
- ) {
- Text("Lockscreen", style = MaterialTheme.typography.headlineMedium)
- Row(
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- Button(onClick = { viewModel.onLockButtonClicked() }) { Icon(lockButtonIcon) }
-
- Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") }
+ AndroidView(
+ factory = { _ ->
+ val keyguardRootView = viewProvider()
+ // Remove the KeyguardRootView from any parent it might already have in legacy code just
+ // in case (a view can't have two parents).
+ (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
+ keyguardRootView
+ },
+ update = { keyguardRootView ->
+ keyguardRootView.requireViewById<View>(R.id.lock_icon_view).setOnClickListener {
+ viewModel.onLockButtonClicked()
}
- }
- }
+ },
+ modifier = modifier,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt
new file mode 100644
index 000000000000..c2d2725f4f06
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 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.keyguard.qualifiers
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KeyguardRootView
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt
new file mode 100644
index 000000000000..c88737e6bd70
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.view
+
+import android.view.View
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.KeyguardViewConfigurator
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import dagger.Module
+import dagger.Provides
+import javax.inject.Provider
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@Module
+object LockscreenSceneModule {
+
+ @Provides
+ @SysUISingleton
+ @KeyguardRootView
+ fun viewProvider(
+ configurator: Provider<KeyguardViewConfigurator>,
+ ): () -> View {
+ return { configurator.get().getKeyguardRootView() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 6d3b7f18e974..93c4902332c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,41 +16,22 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.R
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-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 lockscreen scene. */
@SysUISingleton
class LockscreenSceneViewModel
@Inject
constructor(
- @Application applicationScope: CoroutineScope,
authenticationInteractor: AuthenticationInteractor,
private val bouncerInteractor: BouncerInteractor,
) {
- /** The icon for the "lock" button on the lockscreen. */
- val lockButtonIcon: StateFlow<Icon> =
- authenticationInteractor.isUnlocked
- .map { isUnlocked -> lockIcon(isUnlocked = isUnlocked) }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = lockIcon(isUnlocked = authenticationInteractor.isUnlocked.value),
- )
-
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: Flow<SceneKey> =
authenticationInteractor.isUnlocked.map { isUnlocked ->
@@ -65,31 +46,4 @@ constructor(
fun onLockButtonClicked() {
bouncerInteractor.showOrUnlockDevice()
}
-
- /** Notifies that some content on the lock screen was clicked. */
- fun onContentClicked() {
- bouncerInteractor.showOrUnlockDevice()
- }
-
- private fun upDestinationSceneKey(
- canSwipeToDismiss: Boolean,
- ): SceneKey {
- return if (canSwipeToDismiss) SceneKey.Gone else SceneKey.Bouncer
- }
-
- private fun lockIcon(
- isUnlocked: Boolean,
- ): Icon {
- return if (isUnlocked) {
- Icon.Resource(
- R.drawable.ic_device_lock_off,
- ContentDescription.Resource(R.string.accessibility_unlock_button)
- )
- } else {
- Icon.Resource(
- R.drawable.ic_device_lock_on,
- ContentDescription.Resource(R.string.accessibility_lock_icon)
- )
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 398e64b1981b..714795109454 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.scene
+import com.android.systemui.keyguard.ui.view.LockscreenSceneModule
import com.android.systemui.scene.domain.startable.SceneContainerStartableModule
import com.android.systemui.scene.shared.model.SceneContainerConfigModule
import com.android.systemui.scene.ui.composable.SceneModule
@@ -24,6 +25,7 @@ import dagger.Module
@Module(
includes =
[
+ LockscreenSceneModule::class,
SceneContainerConfigModule::class,
SceneContainerStartableModule::class,
SceneModule::class,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 23f243c8ebda..a9f288d3575f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -17,10 +17,8 @@
package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -48,7 +46,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
private val underTest =
LockscreenSceneViewModel(
- applicationScope = testScope.backgroundScope,
authenticationInteractor = authenticationInteractor,
bouncerInteractor =
utils.bouncerInteractor(
@@ -58,32 +55,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
)
@Test
- fun lockButtonIcon_whenLocked() =
- testScope.runTest {
- val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(false)
-
- assertThat((lockButtonIcon as? Icon.Resource)?.res)
- .isEqualTo(R.drawable.ic_device_lock_on)
- }
-
- @Test
- fun lockButtonIcon_whenUnlocked() =
- testScope.runTest {
- val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(true)
-
- assertThat((lockButtonIcon as? Icon.Resource)?.res)
- .isEqualTo(R.drawable.ic_device_lock_off)
- }
-
- @Test
fun upTransitionSceneKey_canSwipeToUnlock_gone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
@@ -120,32 +91,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
}
@Test
- fun onContentClicked_deviceUnlocked_switchesToGone() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(true)
- runCurrent()
-
- underTest.onContentClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
- @Test
- fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- runCurrent()
-
- underTest.onContentClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- }
-
- @Test
fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 53c04ccbdb38..46cbfacb0044 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -116,7 +116,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
private val lockscreenSceneViewModel =
LockscreenSceneViewModel(
- applicationScope = testScope.backgroundScope,
authenticationInteractor = authenticationInteractor,
bouncerInteractor = bouncerInteractor,
)