Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c5149ac9 authored by Chandru S's avatar Chandru S Committed by Android (Google) Code Review
Browse files

Merge "Add keyboard input support for the PIN bouncer" into main

parents 9c0e43e5 a9708c8b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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 ->
+46 −0
Original line number Diff line number Diff line
@@ -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
+19 −2
Original line number Diff line number Diff line
@@ -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,

+46 −0
Original line number Diff line number Diff line
@@ -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. */