Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +14 −3 Original line number Diff line number Diff line Loading @@ -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() } } Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +24 −6 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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 */ Loading Loading @@ -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 { Loading Loading @@ -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?) packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +26 −12 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading @@ -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( Loading Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +14 −3 Original line number Diff line number Diff line Loading @@ -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() } } Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +24 −6 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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 */ Loading Loading @@ -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 { Loading Loading @@ -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?)
packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +26 −12 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading @@ -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( Loading