summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Danny Burakov <burakov@google.com> 2023-11-15 03:18:27 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-11-15 03:18:27 +0000
commitb6f66a70e838bb626316834c20cf1b8f7f057208 (patch)
treee44f9e934b45d47cc22669601c5263f80195f179
parent1d806343600dc52c108d22664c5719a2f467541b (diff)
parentf6387084729d391e401e84ec2a1869e7f5c1da51 (diff)
Merge "[flexiglass] Clear bouncer input when it's dismissed." into main
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt9
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt72
7 files changed, 97 insertions, 87 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 53e437a2944f..0b1338305076 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -28,6 +28,7 @@ import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -63,11 +64,13 @@ internal fun PasswordBouncer(
val isImeVisible by rememberUpdatedState(WindowInsets.imeAnimationTarget.getBottom(density) > 0)
LaunchedEffect(isImeVisible) { viewModel.onImeVisibilityChanged(isImeVisible) }
- LaunchedEffect(Unit) {
+ DisposableEffect(Unit) {
+ viewModel.onShown()
+
// When the UI comes up, request focus on the TextField to bring up the software keyboard.
focusRequester.requestFocus()
- // Also, report that the UI is shown to let the view-model run some logic.
- viewModel.onShown()
+
+ onDispose { viewModel.onHidden() }
}
LaunchedEffect(animateFailure) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 03efbe0fe1ff..2bbe9b8fc20a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -26,6 +26,7 @@ import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -65,8 +66,10 @@ internal fun PatternBouncer(
viewModel: PatternBouncerViewModel,
modifier: Modifier = Modifier,
) {
- // Report that the UI is shown to let the view-model run some logic.
- LaunchedEffect(Unit) { viewModel.onShown() }
+ DisposableEffect(Unit) {
+ viewModel.onShown()
+ onDispose { viewModel.onHidden() }
+ }
val colCount = viewModel.columnCount
val rowCount = viewModel.rowCount
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 243751fafe5d..59617c9022ab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -69,8 +70,10 @@ fun PinPad(
viewModel: PinBouncerViewModel,
modifier: Modifier = Modifier,
) {
- // Report that the UI is shown to let the view-model run some logic.
- LaunchedEffect(Unit) { viewModel.onShown() }
+ DisposableEffect(Unit) {
+ viewModel.onShown()
+ onDispose { viewModel.onHidden() }
+ }
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index f46574ca5bbe..80248744c25a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -62,6 +62,13 @@ sealed class AuthMethodBouncerViewModel(
/** Notifies that the UI has been shown to the user. */
fun onShown() {
+ interactor.resetMessage()
+ }
+
+ /**
+ * Notifies that the UI has been hidden from the user (after any transitions have completed).
+ */
+ fun onHidden() {
clearInput()
interactor.resetMessage()
}
@@ -113,8 +120,6 @@ sealed class AuthMethodBouncerViewModel(
}
_animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
- // TODO(b/291528545): On success, this should only be cleared after the view is animated
- // away).
clearInput()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index c498edf0e971..9b1e9585979a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -78,12 +78,28 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
lockDeviceAndOpenPasswordBouncer()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
- assertThat(password).isEqualTo("")
+ assertThat(password).isEmpty()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Password)
}
@Test
+ fun onHidden_resetsPasswordInputAndMessage() =
+ testScope.runTest {
+ val message by collectLastValue(bouncerViewModel.message)
+ val password by collectLastValue(underTest.password)
+ lockDeviceAndOpenPasswordBouncer()
+
+ underTest.onPasswordInputChanged("password")
+ assertThat(message?.text).isNotEqualTo(ENTER_YOUR_PASSWORD)
+ assertThat(password).isNotEmpty()
+
+ underTest.onHidden()
+ assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
+ assertThat(password).isEmpty()
+ }
+
+ @Test
fun onPasswordInputChanged() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
@@ -121,7 +137,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
underTest.onPasswordInputChanged("wrong")
underTest.onAuthenticateKeyPressed()
- assertThat(password).isEqualTo("")
+ assertThat(password).isEmpty()
assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
}
@@ -134,14 +150,13 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
AuthenticationMethodModel.Password
)
utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- underTest.onShown()
- // Enter nothing.
+ switchToScene(SceneKey.Bouncer)
+
+ // No input entered.
underTest.onAuthenticateKeyPressed()
- assertThat(password).isEqualTo("")
+ assertThat(password).isEmpty()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
}
@@ -182,32 +197,33 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
assertThat(password).isEqualTo("password")
// The user doesn't confirm the password, but navigates back to the lockscreen instead.
- sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+ switchToScene(SceneKey.Lockscreen)
// The user navigates to the bouncer again.
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-
- underTest.onShown()
+ switchToScene(SceneKey.Bouncer)
// Ensure the previously-entered password is not shown.
assertThat(password).isEmpty()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
+ private fun TestScope.switchToScene(toScene: SceneKey) {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
+ val bouncerHidden = currentScene?.key == SceneKey.Bouncer && toScene != SceneKey.Bouncer
+ sceneInteractor.changeScene(SceneModel(toScene), "reason")
+ sceneInteractor.onSceneChanged(SceneModel(toScene), "reason")
+ if (bouncerShown) underTest.onShown()
+ if (bouncerHidden) underTest.onHidden()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(SceneModel(toScene))
+ }
+
private fun TestScope.lockDeviceAndOpenPasswordBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Password)
utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
- .isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
+ switchToScene(SceneKey.Bouncer)
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 3f5ddba23165..125fe680db21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -373,15 +373,23 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
)
}
+ private fun TestScope.switchToScene(toScene: SceneKey) {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
+ val bouncerHidden = currentScene?.key == SceneKey.Bouncer && toScene != SceneKey.Bouncer
+ sceneInteractor.changeScene(SceneModel(toScene), "reason")
+ sceneInteractor.onSceneChanged(SceneModel(toScene), "reason")
+ if (bouncerShown) underTest.onShown()
+ if (bouncerHidden) underTest.onHidden()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(SceneModel(toScene))
+ }
+
private fun TestScope.lockDeviceAndOpenPatternBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pattern)
utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
- .isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
+ switchToScene(SceneKey.Bouncer)
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 52844cf7f79a..c30e405ab911 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -76,20 +76,12 @@ class PinBouncerViewModelTest : SysuiTestCase() {
@Test
fun onShown() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-
- underTest.onShown()
+ lockDeviceAndOpenPinBouncer()
assertThat(message?.text).ignoringCase().isEqualTo(ENTER_YOUR_PIN)
assertThat(pin).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Pin)
}
@@ -142,29 +134,19 @@ class PinBouncerViewModelTest : SysuiTestCase() {
@Test
fun onPinButtonClicked() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
+ lockDeviceAndOpenPinBouncer()
underTest.onPinButtonClicked(1)
assertThat(message?.text).isEmpty()
assertThat(pin).containsExactly(1)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onBackspaceButtonClicked() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
lockDeviceAndOpenPinBouncer()
@@ -176,7 +158,6 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(pin).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
@@ -224,9 +205,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
collectLastValue(authenticationInteractor.authenticationChallengeResult)
lockDeviceAndOpenPinBouncer()
- FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
- underTest.onPinButtonClicked(digit)
- }
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
underTest.onAuthenticateButtonClicked()
@@ -274,9 +253,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(authResult).isFalse()
// Enter the correct PIN:
- FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
- underTest.onPinButtonClicked(digit)
- }
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
assertThat(message?.text).isEmpty()
underTest.onAuthenticateButtonClicked()
@@ -292,9 +269,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
collectLastValue(authenticationInteractor.authenticationChallengeResult)
lockDeviceAndOpenPinBouncer()
- FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
- underTest.onPinButtonClicked(digit)
- }
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
assertThat(authResult).isTrue()
}
@@ -323,31 +298,21 @@ class PinBouncerViewModelTest : SysuiTestCase() {
@Test
fun onShown_againAfterSceneChange_resetsPin() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
lockDeviceAndOpenPinBouncer()
// The user types a PIN.
- FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
- underTest.onPinButtonClicked(digit)
- }
+ FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
assertThat(pin).isNotEmpty()
// The user doesn't confirm the PIN, but navigates back to the lockscreen instead.
- sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+ switchToScene(SceneKey.Lockscreen)
// The user navigates to the bouncer again.
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-
- underTest.onShown()
+ switchToScene(SceneKey.Bouncer)
// Ensure the previously-entered PIN is not shown.
assertThat(pin).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
@@ -414,16 +379,23 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(isAnimationEnabled).isTrue()
}
+ private fun TestScope.switchToScene(toScene: SceneKey) {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
+ val bouncerHidden = currentScene?.key == SceneKey.Bouncer && toScene != SceneKey.Bouncer
+ sceneInteractor.changeScene(SceneModel(toScene), "reason")
+ sceneInteractor.onSceneChanged(SceneModel(toScene), "reason")
+ if (bouncerShown) underTest.onShown()
+ if (bouncerHidden) underTest.onHidden()
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(SceneModel(toScene))
+ }
+
private fun TestScope.lockDeviceAndOpenPinBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.deviceEntryRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
- .isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
+ switchToScene(SceneKey.Bouncer)
}
companion object {