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

Commit 6355af29 authored by Juan Sebastian Martinez's avatar Juan Sebastian Martinez
Browse files

Ignoring user touch feedback settings for biometric prompt UDFPS

authentication.

The FLAG_IGONRE_GLOBAL_SETTING will ensure that confirm and reject
haptics always play when the user succeeds or fails to authenticate.

Test: manual: Turn off Touch Feedback on Settings > Sound & Vibration >
  Vibration & haptics. Verified that haptics play on successful and
  failed authetication when a biometric prompt is used from a 3P app.
Test: atest SystemUITests:PromptViewModelTest
Bug: 305949781
Flag: NONE
Change-Id: I4d24e6fc606ccba7350094f6974210a3d0e80f8a
parent 3baae7bc
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -438,9 +438,20 @@ object BiometricViewBinder {

                // Play haptics
                launch {
                    viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
                        if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
                            vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant)
                    viewModel.hapticsToPlay.collect { haptics ->
                        if (haptics.hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
                            if (haptics.flag != null) {
                                vibratorHelper.performHapticFeedback(
                                    view,
                                    haptics.hapticFeedbackConstant,
                                    haptics.flag,
                                )
                            } else {
                                vibratorHelper.performHapticFeedback(
                                    view,
                                    haptics.hapticFeedbackConstant,
                                )
                            }
                            viewModel.clearHaptics()
                        }
                    }
+24 −6
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

/** ViewModel for BiometricPrompt. */
@@ -144,9 +145,10 @@ constructor(
    private val _forceLargeSize = MutableStateFlow(false)
    private val _forceMediumSize = MutableStateFlow(false)

    private val _hapticsToPlay = MutableStateFlow(HapticFeedbackConstants.NO_HAPTICS)
    private val _hapticsToPlay =
        MutableStateFlow(HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, /* flag= */ null))

    /** Event fired to the view indicating a [HapticFeedbackConstants] to be played */
    /** Event fired to the view indicating a [HapticsToPlay] */
    val hapticsToPlay = _hapticsToPlay.asStateFlow()

    /** The current position of the prompt */
@@ -686,16 +688,26 @@ constructor(
    }

    private fun vibrateOnSuccess() {
        _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM
        _hapticsToPlay.value =
            HapticsToPlay(
                HapticFeedbackConstants.CONFIRM,
                HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
            )
    }

    private fun vibrateOnError() {
        _hapticsToPlay.value = HapticFeedbackConstants.REJECT
        _hapticsToPlay.value =
            HapticsToPlay(
                HapticFeedbackConstants.REJECT,
                HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
            )
    }

    /** Clears the [hapticsToPlay] variable by setting it to the NO_HAPTICS default. */
    /** Clears the [hapticsToPlay] variable by setting its constant to the NO_HAPTICS default. */
    fun clearHaptics() {
        _hapticsToPlay.value = HapticFeedbackConstants.NO_HAPTICS
        _hapticsToPlay.update { previous ->
            HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, previous.flag)
        }
    }

    companion object {
@@ -724,3 +736,9 @@ enum class FingerprintStartMode {
    val isStarted: Boolean
        get() = this == Normal || this == Delayed
}

/**
 * The state of haptic feedback to play. It is composed by a [HapticFeedbackConstants] and a
 * [HapticFeedbackConstants] flag.
 */
data class HapticsToPlay(val hapticFeedbackConstant: Int, val flag: Int?)
+26 −12
Original line number Diff line number Diff line
@@ -241,19 +241,27 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa

            viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)

            val confirmConstant by collectLastValue(viewModel.hapticsToPlay)
            assertThat(confirmConstant)
            val confirmHaptics by collectLastValue(viewModel.hapticsToPlay)
            assertThat(confirmHaptics?.hapticFeedbackConstant)
                .isEqualTo(
                    if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
                    else HapticFeedbackConstants.CONFIRM
                )
            assertThat(confirmHaptics?.flag)
                .isEqualTo(
                    if (expectConfirmation) null
                    else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
                )

            if (expectConfirmation) {
                viewModel.confirmAuthenticated()
            }

            val confirmedConstant by collectLastValue(viewModel.hapticsToPlay)
            assertThat(confirmedConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
            val confirmedHaptics by collectLastValue(viewModel.hapticsToPlay)
            assertThat(confirmedHaptics?.hapticFeedbackConstant)
                .isEqualTo(HapticFeedbackConstants.CONFIRM)
            assertThat(confirmedHaptics?.flag)
                .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
        }

    @Test
@@ -265,16 +273,21 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
            viewModel.confirmAuthenticated()
        }

        val currentConstant by collectLastValue(viewModel.hapticsToPlay)
        assertThat(currentConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
        val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
        assertThat(currentHaptics?.hapticFeedbackConstant)
            .isEqualTo(HapticFeedbackConstants.CONFIRM)
        assertThat(currentHaptics?.flag)
            .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
    }

    @Test
    fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
        viewModel.showTemporaryError("test", "messageAfterError", false)

        val currentConstant by collectLastValue(viewModel.hapticsToPlay)
        assertThat(currentConstant).isEqualTo(HapticFeedbackConstants.REJECT)
        val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
        assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
        assertThat(currentHaptics?.flag)
            .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
    }

    @Test
@@ -800,8 +813,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
            hapticFeedback = true,
        )

        val constant by collectLastValue(viewModel.hapticsToPlay)
        assertThat(constant).isEqualTo(HapticFeedbackConstants.REJECT)
        val haptics by collectLastValue(viewModel.hapticsToPlay)
        assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
        assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
    }

    @Test
@@ -813,8 +827,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
            hapticFeedback = false,
        )

        val constant by collectLastValue(viewModel.hapticsToPlay)
        assertThat(constant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
        val haptics by collectLastValue(viewModel.hapticsToPlay)
        assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
    }

    private suspend fun TestScope.showTemporaryErrors(