Merge "Show only first face error message for coex." into udc-qpr-dev

This commit is contained in:
Joe Bolinger
2023-07-20 01:10:56 +00:00
committed by Android (Google) Code Review
5 changed files with 132 additions and 7 deletions

View File

@@ -479,10 +479,10 @@ private class Spaghetti(
failureReason,
messageAfterError = modalities.asDefaultHelpMessage(applicationContext),
authenticateAfterError = modalities.hasFingerprint,
suppressIf = { currentMessage ->
suppressIf = { currentMessage, history ->
modalities.hasFaceAndFingerprint &&
failedModality == BiometricModality.Face &&
currentMessage.isError
(currentMessage.isError || history.faceFailed)
},
failedModality = failedModality,
)

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.biometrics.ui.viewmodel
import com.android.systemui.biometrics.shared.model.BiometricModality
/** Contains metadata about key events that have occurred while biometric prompt is showing. */
interface PromptHistory {
/** If face authentication has failed at least once. */
val faceFailed: Boolean
/** If fingerprint authentication has failed at least once. */
val fingerprintFailed: Boolean
}
class PromptHistoryImpl : PromptHistory {
private var failures = mutableSetOf<BiometricModality>()
override val faceFailed
get() = failures.contains(BiometricModality.Face)
override val fingerprintFailed
get() = failures.contains(BiometricModality.Fingerprint)
/** Record a failure event. */
fun failure(modality: BiometricModality) {
if (modality != BiometricModality.None) {
failures.add(modality)
}
}
}

View File

@@ -204,6 +204,7 @@ constructor(
}
.distinctUntilChanged()
private val history = PromptHistoryImpl()
private var messageJob: Job? = null
/**
@@ -214,13 +215,13 @@ constructor(
* is set (or via [showHelp] when not set) after the error is dismissed.
*
* The error is ignored if the user has already authenticated or if [suppressIf] is true given
* the currently showing [PromptMessage].
* the currently showing [PromptMessage] and [PromptHistory].
*/
suspend fun showTemporaryError(
message: String,
messageAfterError: String,
authenticateAfterError: Boolean,
suppressIf: (PromptMessage) -> Boolean = { false },
suppressIf: (PromptMessage, PromptHistory) -> Boolean = { _, _ -> false },
hapticFeedback: Boolean = true,
failedModality: BiometricModality = BiometricModality.None,
) = coroutineScope {
@@ -230,7 +231,9 @@ constructor(
_canTryAgainNow.value = supportsRetry(failedModality)
if (suppressIf(_message.value)) {
val suppress = suppressIf(_message.value, history)
history.failure(failedModality)
if (suppress) {
return@coroutineScope
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.biometrics.ui.viewmodel
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@SmallTest
@RunWith(JUnit4::class)
class PromptHistoryImplTest : SysuiTestCase() {
private lateinit var history: PromptHistoryImpl
@Before
fun setup() {
history = PromptHistoryImpl()
}
@Test
fun empty() {
assertThat(history.faceFailed).isFalse()
assertThat(history.fingerprintFailed).isFalse()
}
@Test
fun faceFailed() =
repeat(2) {
history.failure(BiometricModality.None)
history.failure(BiometricModality.Face)
assertThat(history.faceFailed).isTrue()
assertThat(history.fingerprintFailed).isFalse()
}
@Test
fun fingerprintFailed() =
repeat(2) {
history.failure(BiometricModality.None)
history.failure(BiometricModality.Fingerprint)
assertThat(history.faceFailed).isFalse()
assertThat(history.fingerprintFailed).isTrue()
}
@Test
fun coexFailed() =
repeat(2) {
history.failure(BiometricModality.Face)
history.failure(BiometricModality.Fingerprint)
assertThat(history.faceFailed).isTrue()
assertThat(history.fingerprintFailed).isTrue()
history.failure(BiometricModality.None)
}
}

View File

@@ -335,7 +335,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
error,
messageAfterError = "or me",
authenticateAfterError = false,
suppressIf = { _ -> true },
suppressIf = { _, _ -> true },
)
}
}
@@ -364,7 +364,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
error,
messageAfterError = "$error $afterSuffix",
authenticateAfterError = false,
suppressIf = { currentMessage -> suppress && currentMessage.isError },
suppressIf = { currentMessage, _ -> suppress && currentMessage.isError },
)
}
}