From 26a6f683300922921eacf6e6763923568ab02297 Mon Sep 17 00:00:00 2001 From: Chandru S Date: Mon, 8 May 2023 06:18:13 -0700 Subject: [PATCH] Create BouncerMessageFactory to easily create BouncerMessage data model. Bug: 275600559 Test: pending Change-Id: Ia12e8cd6de851113da718a4bdec6583d8f310160 --- .../keyguard/KeyguardUpdateMonitor.java | 8 + .../data/factory/BouncerMessageFactory.kt} | 165 +++++++++++------- .../shared/model/BouncerMessageModel.kt | 39 +++++ .../data/factory/BouncerMessageFactoryTest.kt | 159 +++++++++++++++++ 4 files changed, 307 insertions(+), 64 deletions(-) rename packages/SystemUI/src/com/android/{keyguard/KeyguardBouncerMessages.kt => systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt} (68%) create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 95e97fffe4449..3a449408ed758 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2622,6 +2622,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mAuthController.isUdfpsSupported(); } + /** + * @return true if the FP sensor is non-UDFPS and the device can be unlocked using fingerprint + * at this moment. + */ + public boolean isFingerprintAllowedInBouncer() { + return !isUdfpsSupported() && isUnlockingWithFingerprintAllowed(); + } + /** * @return true if there's at least one sfps enrollment for the current user. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBouncerMessages.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt similarity index 68% rename from packages/SystemUI/src/com/android/keyguard/KeyguardBouncerMessages.kt rename to packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt index f4145dbef80f2..4085dab393009 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardBouncerMessages.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt @@ -14,9 +14,10 @@ * limitations under the License. */ -package com.android.keyguard +package com.android.systemui.keyguard.bouncer.data.factory import android.annotation.IntDef +import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT @@ -34,6 +35,7 @@ import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST +import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R.string.bouncer_face_not_recognized import com.android.systemui.R.string.keyguard_enter_password import com.android.systemui.R.string.keyguard_enter_pattern @@ -71,10 +73,86 @@ import com.android.systemui.R.string.kg_wrong_input_try_fp_suggestion import com.android.systemui.R.string.kg_wrong_password_try_again import com.android.systemui.R.string.kg_wrong_pattern_try_again import com.android.systemui.R.string.kg_wrong_pin_try_again +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.keyguard.bouncer.shared.model.Message +import javax.inject.Inject -typealias BouncerMessage = Pair +@SysUISingleton +class BouncerMessageFactory +@Inject +constructor( + private val updateMonitor: KeyguardUpdateMonitor, + private val securityModel: KeyguardSecurityModel, +) { -fun emptyBouncerMessage(): BouncerMessage = Pair(0, 0) + fun createFromPromptReason( + @BouncerPromptReason reason: Int, + userId: Int, + ): BouncerMessageModel? { + val pair = + getBouncerMessage( + reason, + securityModel.getSecurityMode(userId), + updateMonitor.isFingerprintAllowedInBouncer + ) + return pair?.let { + BouncerMessageModel( + message = Message(messageResId = pair.first), + secondaryMessage = Message(messageResId = pair.second) + ) + } + } + + fun createFromString( + primaryMsg: String? = null, + secondaryMsg: String? = null + ): BouncerMessageModel = + BouncerMessageModel( + message = primaryMsg?.let { Message(message = it) }, + secondaryMessage = secondaryMsg?.let { Message(message = it) }, + ) + + /** + * Helper method that provides the relevant bouncer message that should be shown for different + * scenarios indicated by [reason]. [securityMode] & [fpAllowedInBouncer] parameters are used to + * provide a more specific message. + */ + private fun getBouncerMessage( + @BouncerPromptReason reason: Int, + securityMode: SecurityMode, + fpAllowedInBouncer: Boolean = false + ): Pair? { + return when (reason) { + PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode) + PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode) + PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode) + PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode) + PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode) + PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode) + PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode) + PROMPT_REASON_FACE_LOCKED_OUT -> faceUnlockUnavailable(securityMode) + PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT -> + if (fpAllowedInBouncer) incorrectSecurityInputWithFingerprint(securityMode) + else incorrectSecurityInput(securityMode) + PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT -> + if (fpAllowedInBouncer) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode) + else nonStrongAuthTimeout(securityMode) + PROMPT_REASON_TRUSTAGENT_EXPIRED -> + if (fpAllowedInBouncer) trustAgentDisabledWithFingerprintAllowed(securityMode) + else trustAgentDisabled(securityMode) + PROMPT_REASON_INCORRECT_FACE_INPUT -> + if (fpAllowedInBouncer) incorrectFaceInputWithFingerprintAllowed(securityMode) + else incorrectFaceInput(securityMode) + PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode) + PROMPT_REASON_DEFAULT -> + if (fpAllowedInBouncer) defaultMessageWithFingerprint(securityMode) + else defaultMessage(securityMode) + PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode) + else -> null + } + } +} @Retention(AnnotationRetention.SOURCE) @IntDef( @@ -97,48 +175,7 @@ fun emptyBouncerMessage(): BouncerMessage = Pair(0, 0) ) annotation class BouncerPromptReason -/** - * Helper method that provides the relevant bouncer message that should be shown for different - * scenarios indicated by [reason]. [securityMode] & [fpAllowedInBouncer] parameters are used to - * provide a more specific message. - */ -@JvmOverloads -fun getBouncerMessage( - @BouncerPromptReason reason: Int, - securityMode: SecurityMode, - fpAllowedInBouncer: Boolean = false -): BouncerMessage { - return when (reason) { - PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode) - PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode) - PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode) - PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode) - PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode) - PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode) - PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode) - PROMPT_REASON_FACE_LOCKED_OUT -> faceUnlockUnavailable(securityMode) - PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT -> - if (fpAllowedInBouncer) incorrectSecurityInputWithFingerprint(securityMode) - else incorrectSecurityInput(securityMode) - PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT -> - if (fpAllowedInBouncer) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode) - else nonStrongAuthTimeout(securityMode) - PROMPT_REASON_TRUSTAGENT_EXPIRED -> - if (fpAllowedInBouncer) trustAgentDisabledWithFingerprintAllowed(securityMode) - else trustAgentDisabled(securityMode) - PROMPT_REASON_INCORRECT_FACE_INPUT -> - if (fpAllowedInBouncer) incorrectFaceInputWithFingerprintAllowed(securityMode) - else incorrectFaceInput(securityMode) - PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode) - PROMPT_REASON_DEFAULT -> - if (fpAllowedInBouncer) defaultMessageWithFingerprint(securityMode) - else defaultMessage(securityMode) - PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode) - else -> emptyBouncerMessage() - } -} - -fun defaultMessage(securityMode: SecurityMode): BouncerMessage { +private fun defaultMessage(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) SecurityMode.Password -> Pair(keyguard_enter_password, 0) @@ -147,7 +184,7 @@ fun defaultMessage(securityMode: SecurityMode): BouncerMessage { } } -fun defaultMessageWithFingerprint(securityMode: SecurityMode): BouncerMessage { +private fun defaultMessageWithFingerprint(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0) SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0) @@ -156,7 +193,7 @@ fun defaultMessageWithFingerprint(securityMode: SecurityMode): BouncerMessage { } } -fun incorrectSecurityInput(securityMode: SecurityMode): BouncerMessage { +private fun incorrectSecurityInput(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0) SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0) @@ -165,7 +202,7 @@ fun incorrectSecurityInput(securityMode: SecurityMode): BouncerMessage { } } -fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): BouncerMessage { +private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion) SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion) @@ -174,7 +211,7 @@ fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): BouncerMe } } -fun incorrectFingerprintInput(securityMode: SecurityMode): BouncerMessage { +private fun incorrectFingerprintInput(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern) SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password) @@ -183,7 +220,7 @@ fun incorrectFingerprintInput(securityMode: SecurityMode): BouncerMessage { } } -fun incorrectFaceInput(securityMode: SecurityMode): BouncerMessage { +private fun incorrectFaceInput(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern) SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password) @@ -192,7 +229,7 @@ fun incorrectFaceInput(securityMode: SecurityMode): BouncerMessage { } } -fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessage { +private fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized) SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized) @@ -201,7 +238,7 @@ fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): Bounce } } -fun biometricLockout(securityMode: SecurityMode): BouncerMessage { +private fun biometricLockout(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern) SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password) @@ -210,7 +247,7 @@ fun biometricLockout(securityMode: SecurityMode): BouncerMessage { } } -fun authRequiredAfterReboot(securityMode: SecurityMode): BouncerMessage { +private fun authRequiredAfterReboot(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern) SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password) @@ -219,7 +256,7 @@ fun authRequiredAfterReboot(securityMode: SecurityMode): BouncerMessage { } } -fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerMessage { +private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock) SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock) @@ -228,7 +265,7 @@ fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerMessage { } } -fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessage { +private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern) SecurityMode.Password -> @@ -238,7 +275,7 @@ fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessage { } } -fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): BouncerMessage { +private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update) SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update) @@ -247,7 +284,7 @@ fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): BouncerMessage } } -fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): BouncerMessage { +private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout) SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout) @@ -256,7 +293,7 @@ fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): BouncerMess } } -fun nonStrongAuthTimeout(securityMode: SecurityMode): BouncerMessage { +private fun nonStrongAuthTimeout(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout) SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout) @@ -265,7 +302,7 @@ fun nonStrongAuthTimeout(securityMode: SecurityMode): BouncerMessage { } } -fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessage { +private fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout) SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout) @@ -274,7 +311,7 @@ fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): Boun } } -fun faceUnlockUnavailable(securityMode: SecurityMode): BouncerMessage { +private fun faceUnlockUnavailable(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out) SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out) @@ -283,7 +320,7 @@ fun faceUnlockUnavailable(securityMode: SecurityMode): BouncerMessage { } } -fun fingerprintUnlockUnavailable(securityMode: SecurityMode): BouncerMessage { +private fun fingerprintUnlockUnavailable(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_fp_locked_out) SecurityMode.Password -> Pair(keyguard_enter_password, kg_fp_locked_out) @@ -292,7 +329,7 @@ fun fingerprintUnlockUnavailable(securityMode: SecurityMode): BouncerMessage { } } -fun trustAgentDisabled(securityMode: SecurityMode): BouncerMessage { +private fun trustAgentDisabled(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled) SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled) @@ -301,7 +338,7 @@ fun trustAgentDisabled(securityMode: SecurityMode): BouncerMessage { } } -fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessage { +private fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled) SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled) @@ -310,7 +347,7 @@ fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): Bounce } } -fun primaryAuthLockedOut(securityMode: SecurityMode): BouncerMessage { +private fun primaryAuthLockedOut(securityMode: SecurityMode): Pair { return when (securityMode) { SecurityMode.Pattern -> Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt new file mode 100644 index 0000000000000..46e88733ae6cb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt @@ -0,0 +1,39 @@ +/* + * 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.keyguard.bouncer.shared.model + +import android.content.res.ColorStateList + +/** + * Represents the message displayed on the bouncer. It has two parts, primary and a secondary + * message + */ +data class BouncerMessageModel(val message: Message? = null, val secondaryMessage: Message? = null) + +/** + * Representation of a single message on the bouncer. It can be either a string or a string resource + * ID + */ +data class Message( + val message: String? = null, + val messageResId: Int? = null, + val colorState: ColorStateList? = null, + /** Any plural formatter arguments that can used to format the [messageResId] */ + var formatterArgs: Map? = null, + /** Specifies whether this text should be animated when it is shown. */ + var animate: Boolean = true, +) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt new file mode 100644 index 0000000000000..9e798490eaffe --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt @@ -0,0 +1,159 @@ +/* + * 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.keyguard.bouncer.data.factory + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN +import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password +import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern +import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT +import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.StringSubject +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class BouncerMessageFactoryTest : SysuiTestCase() { + private lateinit var underTest: BouncerMessageFactory + + @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor + + @Mock private lateinit var securityModel: KeyguardSecurityModel + + private lateinit var testScope: TestScope + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testScope = TestScope() + underTest = BouncerMessageFactory(updateMonitor, securityModel) + } + + @Test + fun bouncerMessages_choosesTheRightMessage_basedOnSecurityModeAndFpAllowedInBouncer() = + testScope.runTest { + primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAllowedInBouncer = false) + .isEqualTo("Enter PIN") + primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAllowedInBouncer = true) + .isEqualTo("Unlock with PIN or fingerprint") + + primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAllowedInBouncer = false) + .isEqualTo("Enter password") + primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAllowedInBouncer = true) + .isEqualTo("Unlock with password or fingerprint") + + primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAllowedInBouncer = false) + .isEqualTo("Draw pattern") + primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAllowedInBouncer = true) + .isEqualTo("Unlock with pattern or fingerprint") + } + + @Test + fun bouncerMessages_setsPrimaryAndSecondaryMessage_basedOnSecurityModeAndFpAllowedInBouncer() = + testScope.runTest { + primaryMessage( + PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, + mode = PIN, + fpAllowedInBouncer = true + ) + .isEqualTo("Wrong PIN. Try again.") + secondaryMessage( + PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, + mode = PIN, + fpAllowedInBouncer = true + ) + .isEqualTo("Or unlock with fingerprint") + + primaryMessage( + PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, + mode = Password, + fpAllowedInBouncer = true + ) + .isEqualTo("Wrong password. Try again.") + secondaryMessage( + PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, + mode = Password, + fpAllowedInBouncer = true + ) + .isEqualTo("Or unlock with fingerprint") + + primaryMessage( + PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, + mode = Pattern, + fpAllowedInBouncer = true + ) + .isEqualTo("Wrong pattern. Try again.") + secondaryMessage( + PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT, + mode = Pattern, + fpAllowedInBouncer = true + ) + .isEqualTo("Or unlock with fingerprint") + } + + private fun primaryMessage( + reason: Int, + mode: KeyguardSecurityModel.SecurityMode, + fpAllowedInBouncer: Boolean + ): StringSubject { + return assertThat( + context.resources.getString( + bouncerMessageModel(mode, fpAllowedInBouncer, reason)!!.message!!.messageResId!! + ) + )!! + } + + private fun secondaryMessage( + reason: Int, + mode: KeyguardSecurityModel.SecurityMode, + fpAllowedInBouncer: Boolean + ): StringSubject { + return assertThat( + context.resources.getString( + bouncerMessageModel(mode, fpAllowedInBouncer, reason)!! + .secondaryMessage!! + .messageResId!! + ) + )!! + } + + private fun bouncerMessageModel( + mode: KeyguardSecurityModel.SecurityMode, + fpAllowedInBouncer: Boolean, + reason: Int + ): BouncerMessageModel? { + whenever(securityModel.getSecurityMode(0)).thenReturn(mode) + whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(fpAllowedInBouncer) + + return underTest.createFromPromptReason(reason, 0) + } +}