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

Commit f3e400a8 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Stop requesting focus to the input text field if authentication was successful" into main

parents a8a1fbb0 cd7db757
Loading
Loading
Loading
Loading
+39 −10
Original line number Diff line number Diff line
@@ -310,6 +310,41 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
                .isEqualTo(displayId)
        }

    @Test
    fun afterSuccessfulAuthentication_focusIsNotRequested() =
        testScope.runTest {
            val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
            val textInputFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
            lockDeviceAndOpenPasswordBouncer()

            // remove focus from text field
            underTest.onTextFieldFocusChanged(false)
            runCurrent()

            // focus should be requested
            assertThat(textInputFocusRequested).isTrue()

            // simulate text field getting focus
            underTest.onTextFieldFocusChanged(true)
            runCurrent()

            // focus should not be requested anymore
            assertThat(textInputFocusRequested).isFalse()

            // authenticate successfully.
            underTest.onPasswordInputChanged("password")
            underTest.onAuthenticateKeyPressed()
            runCurrent()

            assertThat(authResult).isTrue()

            // remove focus from text field
            underTest.onTextFieldFocusChanged(false)
            runCurrent()
            // focus should not be requested again
            assertThat(textInputFocusRequested).isFalse()
        }

    private fun TestScope.switchToScene(toScene: SceneKey) {
        val currentScene by collectLastValue(sceneInteractor.currentScene)
        val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
@@ -327,10 +362,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
        switchToScene(Scenes.Bouncer)
    }

    private suspend fun TestScope.setLockout(
        isLockedOut: Boolean,
        failedAttemptCount: Int = 5,
    ) {
    private suspend fun TestScope.setLockout(isLockedOut: Boolean, failedAttemptCount: Int = 5) {
        if (isLockedOut) {
            repeat(failedAttemptCount) {
                kosmos.fakeAuthenticationRepository.reportAuthenticationAttempt(false)
@@ -350,7 +382,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
        kosmos.fakeUserRepository.selectedUser.value =
            SelectedUserModel(
                userInfo = userInfo,
                selectionStatus = SelectionStatus.SELECTION_COMPLETE
                selectionStatus = SelectionStatus.SELECTION_COMPLETE,
            )
        advanceTimeBy(PasswordBouncerViewModel.DELAY_TO_FETCH_IMES)
    }
@@ -374,7 +406,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
            subtypes =
                List(auxiliarySubtypes + nonAuxiliarySubtypes) {
                    InputMethodModel.Subtype(subtypeId = it, isAuxiliary = it < auxiliarySubtypes)
                }
                },
        )
    }

@@ -383,9 +415,6 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
        private const val WRONG_PASSWORD = "Wrong password"

        private val USER_INFOS =
            listOf(
                UserInfo(100, "First user", 0),
                UserInfo(101, "Second user", 0),
            )
            listOf(UserInfo(100, "First user", 0), UserInfo(101, "Second user", 0))
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -86,6 +86,9 @@ sealed class AuthMethodBouncerViewModel(

            _animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
            clearInput()
            if (authenticationResult == AuthenticationResult.SUCCEEDED) {
                onSuccessfulAuthentication()
            }
        }
        awaitCancellation()
    }
@@ -116,6 +119,9 @@ sealed class AuthMethodBouncerViewModel(
    /** Returns the input entered so far. */
    protected abstract fun getInput(): List<Any>

    /** Invoked after a successful authentication. */
    protected open fun onSuccessfulAuthentication() = Unit

    /** Perform authentication result haptics */
    private fun performAuthenticationHapticFeedback(result: AuthenticationResult) {
        if (result == AuthenticationResult.SKIPPED) return
+51 −38
Original line number Diff line number Diff line
@@ -81,8 +81,10 @@ constructor(
    val selectedUserId: StateFlow<Int> = _selectedUserId.asStateFlow()

    private val requests = Channel<Request>(Channel.BUFFERED)
    private var wasSuccessfullyAuthenticated = false

    override suspend fun onActivated(): Nothing {
        try {
            coroutineScope {
                launch { super.onActivated() }
                launch {
@@ -102,23 +104,26 @@ constructor(
                }
                launch {
                    combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus ->
                        hasInput && !hasFocus
                            hasInput && !hasFocus && !wasSuccessfullyAuthenticated
                        }
                        .collect { _isTextFieldFocusRequested.value = it }
                }
            launch { selectedUserInteractor.selectedUser.collect { _selectedUserId.value = it } }
                launch {
                    selectedUserInteractor.selectedUser.collect { _selectedUserId.value = it }
                }
                launch {
                    // Re-fetch the currently-enabled IMEs whenever the selected user changes, and
                    // whenever
                    // the UI subscribes to the `isImeSwitcherButtonVisible` flow.
                    combine(
                        // InputMethodManagerService sometimes takes some time to update its
                        // internal
                        // state when the selected user changes. As a workaround, delay fetching the
                        // IME
                        // info.
                        selectedUserInteractor.selectedUser.onEach { delay(DELAY_TO_FETCH_IMES) },
                        _isImeSwitcherButtonVisible.onSubscriberAdded()
                            // InputMethodManagerService sometimes takes
                            // some time to update its internal state when the
                            // selected user changes.
                            // As a workaround, delay fetching the IME info.
                            selectedUserInteractor.selectedUser.onEach {
                                delay(DELAY_TO_FETCH_IMES)
                            },
                            _isImeSwitcherButtonVisible.onSubscriberAdded(),
                        ) { selectedUserId, _ ->
                            inputMethodInteractor.hasMultipleEnabledImesOrSubtypes(selectedUserId)
                        }
@@ -126,6 +131,10 @@ constructor(
                }
                awaitCancellation()
            }
        } finally {
            // reset whenever the view model is "deactivated"
            wasSuccessfullyAuthenticated = false
        }
    }

    override fun onHidden() {
@@ -141,6 +150,10 @@ constructor(
        return _password.value.toCharArray().toList()
    }

    override fun onSuccessfulAuthentication() {
        wasSuccessfullyAuthenticated = true
    }

    /** Notifies that the user has changed the password input. */
    fun onPasswordInputChanged(newPassword: String) {
        if (newPassword.isNotEmpty()) {