summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt46
4 files changed, 113 insertions, 3 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index c19c08e09349..b8f9ca82f072 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -66,6 +66,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
@@ -137,7 +138,7 @@ fun BouncerContent(
// Despite the keyboard only being part of the password bouncer, adding it at this level is
// both necessary to properly handle the keyboard in all layouts and harmless in cases when
// the keyboard isn't used (like the PIN or pattern auth methods).
- modifier = modifier.imePadding(),
+ modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent),
) {
when (layout) {
BouncerSceneLayout.STANDARD_BOUNCER ->
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 256687b56f4e..89bafb952211 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -16,6 +16,12 @@
package com.android.systemui.bouncer.ui.viewmodel
+import android.view.KeyEvent.KEYCODE_0
+import android.view.KeyEvent.KEYCODE_4
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.KEYCODE_DEL
+import android.view.KeyEvent.KEYCODE_NUMPAD_0
+import androidx.compose.ui.input.key.KeyEventType
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.SceneKey
@@ -34,6 +40,8 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlin.random.Random
+import kotlin.random.nextInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -444,6 +452,44 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(pin).hasSize(FakeAuthenticationRepository.HINTING_PIN_LENGTH + 1)
}
+ @Test
+ fun onKeyboardInput_pinInput_isUpdated() =
+ testScope.runTest {
+ val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
+ lockDeviceAndOpenPinBouncer()
+ val random = Random(System.currentTimeMillis())
+ // Generate a random 4 digit PIN
+ val expectedPin =
+ with(random) { arrayOf(nextInt(0..9), nextInt(0..9), nextInt(0..9), nextInt(0..9)) }
+
+ // Enter the PIN using NUM pad and normal number keyboard events
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_0 + expectedPin[0])
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_0 + expectedPin[0])
+
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_NUMPAD_0 + expectedPin[1])
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_NUMPAD_0 + expectedPin[1])
+
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_0 + expectedPin[2])
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_0 + expectedPin[2])
+
+ // Enter an additional digit in between and delete it
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_4)
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_4)
+
+ // Delete that additional digit
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_DEL)
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_DEL)
+
+ // Try entering a non digit character, this should be ignored.
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_A)
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_A)
+
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_NUMPAD_0 + expectedPin[3])
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_NUMPAD_0 + expectedPin[3])
+
+ assertThat(pin).containsExactly(*expectedPin)
+ }
+
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.currentScene)
val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 7c41b75d7105..40a141dcec77 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -20,6 +20,8 @@ import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResources
import android.content.Context
import android.graphics.Bitmap
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.type
import androidx.core.graphics.drawable.toBitmap
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.SceneKey
@@ -326,7 +328,8 @@ class BouncerViewModel(
{ message },
failedAttempts,
remainingAttempts,
- ) ?: message
+ )
+ ?: message
} else {
message
}
@@ -343,7 +346,8 @@ class BouncerViewModel(
.KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
{ message },
failedAttempts,
- ) ?: message
+ )
+ ?: message
} else {
message
}
@@ -377,6 +381,19 @@ class BouncerViewModel(
Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
)
+ /**
+ * Notifies that a key event has occurred.
+ *
+ * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
+ */
+ fun onKeyEvent(keyEvent: KeyEvent): Boolean {
+ return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent(
+ keyEvent.type,
+ keyEvent.nativeKeyEvent.keyCode
+ )
+ ?: false
+ }
+
data class DialogViewModel(
val text: String,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 4c2380c5e4db..aa447ffac154 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -19,6 +19,14 @@
package com.android.systemui.bouncer.ui.viewmodel
import android.content.Context
+import android.view.KeyEvent.KEYCODE_0
+import android.view.KeyEvent.KEYCODE_9
+import android.view.KeyEvent.KEYCODE_DEL
+import android.view.KeyEvent.KEYCODE_NUMPAD_0
+import android.view.KeyEvent.KEYCODE_NUMPAD_9
+import android.view.KeyEvent.isConfirmKey
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
import com.android.keyguard.PinShapeAdapter
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
@@ -196,6 +204,44 @@ class PinBouncerViewModel(
else -> ActionButtonAppearance.Shown
}
}
+
+ /**
+ * Notifies that a key event has occurred.
+ *
+ * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
+ */
+ fun onKeyEvent(type: KeyEventType, keyCode: Int): Boolean {
+ return when (type) {
+ KeyEventType.KeyUp -> {
+ if (isConfirmKey(keyCode)) {
+ onAuthenticateButtonClicked()
+ true
+ } else {
+ false
+ }
+ }
+ KeyEventType.KeyDown -> {
+ when (keyCode) {
+ KEYCODE_DEL -> {
+ onBackspaceButtonClicked()
+ true
+ }
+ in KEYCODE_0..KEYCODE_9 -> {
+ onPinButtonClicked(keyCode - KEYCODE_0)
+ true
+ }
+ in KEYCODE_NUMPAD_0..KEYCODE_NUMPAD_9 -> {
+ onPinButtonClicked(keyCode - KEYCODE_NUMPAD_0)
+ true
+ }
+ else -> {
+ false
+ }
+ }
+ }
+ else -> false
+ }
+ }
}
/** Appearance of pin-pad action buttons. */