From 8d903362c811f092e60ce8c9fdf38d8a75c068d7 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 31 Oct 2023 19:22:43 +0100 Subject: [PATCH] Feature/fga/biometric unlock (#1702) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Biometric unlock : refactor a bit existing classes * Biometric unlock : first implementation * Biometric: add ui for biometric setup * Biometric unlock : use localazy strings * Biometric unlock setup : branch skip/allow events * Biometric : fix tests * Biometrics: add small test * Biometric : clean up * Update screenshots * Biometric unlock : address some PR review * Biometric : improve a bit edge cases * Fix lint issues --------- Co-authored-by: ganfra Co-authored-by: ElementBot Co-authored-by: Jorge Martín --- .../io/element/android/x/MainActivity.kt | 4 +- app/src/main/res/values-night/themes.xml | 2 +- app/src/main/res/values/themes.xml | 2 +- .../android/appconfig/LockScreenConfig.kt | 17 +- features/lockscreen/impl/build.gradle.kts | 1 + .../impl/DefaultLockScreenService.kt | 20 ++- .../lockscreen/impl/LockScreenFlowNode.kt | 4 +- .../impl/biometric/BiometricUnlock.kt | 134 ++++++++++++++++ .../impl/biometric/BiometricUnlockError.kt | 38 +++++ .../impl/biometric/BiometricUnlockManager.kt | 38 +++++ .../DefaultBiometricUnlockCallback.kt | 14 +- .../DefaultBiometricUnlockManager.kt | 148 ++++++++++++++++++ .../impl/pin/DefaultPinCodeManager.kt | 32 ++-- .../impl/settings/LockScreenSettingsEvents.kt | 2 +- .../settings/LockScreenSettingsFlowNode.kt | 24 ++- .../settings/LockScreenSettingsPresenter.kt | 16 +- .../impl/settings/LockScreenSettingsState.kt | 1 + .../LockScreenSettingsStateProvider.kt | 2 + .../impl/settings/LockScreenSettingsView.kt | 12 +- .../impl/setup/LockScreenSetupFlowNode.kt | 115 ++++++++++++++ .../setup/biometric/SetupBiometricEvents.kt | 22 +++ .../setup/biometric/SetupBiometricNode.kt | 59 +++++++ .../biometric/SetupBiometricPresenter.kt | 61 ++++++++ .../setup/biometric/SetupBiometricState.kt | 22 +++ .../biometric/SetupBiometricStateProvider.kt | 33 ++++ .../setup/biometric/SetupBiometricView.kt | 105 +++++++++++++ .../impl/setup/{ => pin}/SetupPinEvents.kt | 2 +- .../impl/setup/{ => pin}/SetupPinNode.kt | 2 +- .../impl/setup/{ => pin}/SetupPinPresenter.kt | 6 +- .../impl/setup/{ => pin}/SetupPinState.kt | 4 +- .../setup/{ => pin}/SetupPinStateProvider.kt | 4 +- .../impl/setup/{ => pin}/SetupPinView.kt | 4 +- .../{ => pin}/validation/PinValidator.kt | 2 +- .../{ => pin}/validation/SetupPinFailure.kt | 2 +- .../storage/EncryptedPinCodeStorage.kt | 2 +- .../LockScreenStore.kt} | 18 ++- .../PreferencesLockScreenStore.kt} | 21 ++- .../lockscreen/impl/unlock/PinUnlockEvents.kt | 1 + .../impl/unlock/PinUnlockPresenter.kt | 21 ++- .../lockscreen/impl/unlock/PinUnlockState.kt | 14 ++ .../impl/unlock/PinUnlockStateProvider.kt | 6 + .../lockscreen/impl/unlock/PinUnlockView.kt | 20 ++- .../impl/src/main/res/values/localazy.xml | 3 + .../biometric/FakeBiometricUnlockManager.kt | 41 +++++ .../impl/fixtures/LockScreenConfig.kt | 10 +- .../impl/fixtures/PinCodeManager.kt | 12 +- .../impl/pin/DefaultPinCodeManagerTest.kt | 10 +- ...odeStore.kt => InMemoryLockScreenStore.kt} | 15 +- .../LockScreenSettingsPresenterTest.kt | 7 +- .../biometric/SetupBiometricPresenterTest.kt | 74 +++++++++ .../setup/{ => pin}/SetupPinPresenterTest.kt | 6 +- .../impl/unlock/PinUnlockPresenterTest.kt | 14 +- gradle/libs.versions.toml | 3 +- .../cryptography/api/SecretKeyRepository.kt | 38 +++++ .../cryptography/impl/CryptographyModule.kt | 37 +++++ ...ider.kt => KeyStoreSecretKeyRepository.kt} | 26 +-- ...ovider.kt => SimpleSecretKeyRepository.kt} | 10 +- .../src/main/res/values/localazy.xml | 2 + ...etricView-D-2_2_null_0,NEXUS_5,1.0,en].png | 3 + ...etricView-N-2_3_null_0,NEXUS_5,1.0,en].png | 3 + ...pPinView-D-3_3_null_0,NEXUS_5,1.0,en].png} | 0 ...pPinView-D-3_3_null_1,NEXUS_5,1.0,en].png} | 0 ...pPinView-D-3_3_null_2,NEXUS_5,1.0,en].png} | 0 ...pPinView-D-3_3_null_3,NEXUS_5,1.0,en].png} | 0 ...pPinView-D-3_3_null_4,NEXUS_5,1.0,en].png} | 0 ...pPinView-N-3_4_null_0,NEXUS_5,1.0,en].png} | 0 ...pPinView-N-3_4_null_1,NEXUS_5,1.0,en].png} | 0 ...pPinView-N-3_4_null_2,NEXUS_5,1.0,en].png} | 0 ...pPinView-N-3_4_null_3,NEXUS_5,1.0,en].png} | 0 ...pPinView-N-3_4_null_4,NEXUS_5,1.0,en].png} | 0 ..._PinKeypad-D-5_5_null,NEXUS_5,1.0,en].png} | 0 ..._PinKeypad-N-5_6_null,NEXUS_5,1.0,en].png} | 0 ...lockView-D-4_4_null_0,NEXUS_5,1.0,en].png} | 0 ...lockView-D-4_4_null_1,NEXUS_5,1.0,en].png} | 0 ...lockView-D-4_4_null_2,NEXUS_5,1.0,en].png} | 0 ...lockView-D-4_4_null_3,NEXUS_5,1.0,en].png} | 0 ...nlockView-D-4_4_null_4,NEXUS_5,1.0,en].png | 3 + ...lockView-D-4_4_null_5,NEXUS_5,1.0,en].png} | 0 ...lockView-D-4_4_null_6,NEXUS_5,1.0,en].png} | 0 ...lockView-N-4_5_null_0,NEXUS_5,1.0,en].png} | 0 ...lockView-N-4_5_null_1,NEXUS_5,1.0,en].png} | 0 ...lockView-N-4_5_null_2,NEXUS_5,1.0,en].png} | 0 ...lockView-N-4_5_null_3,NEXUS_5,1.0,en].png} | 0 ...nlockView-N-4_5_null_4,NEXUS_5,1.0,en].png | 3 + ...lockView-N-4_5_null_5,NEXUS_5,1.0,en].png} | 0 ...lockView-N-4_5_null_6,NEXUS_5,1.0,en].png} | 0 86 files changed, 1270 insertions(+), 107 deletions(-) create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricUnlock.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricUnlockError.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricUnlockManager.kt rename libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyProvider.kt => features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/DefaultBiometricUnlockCallback.kt (66%) create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/DefaultBiometricUnlockManager.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricEvents.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenter.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricState.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricStateProvider.kt create mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/{ => pin}/SetupPinEvents.kt (92%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/{ => pin}/SetupPinNode.kt (96%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/{ => pin}/SetupPinPresenter.kt (94%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/{ => pin}/SetupPinState.kt (87%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/{ => pin}/SetupPinStateProvider.kt (93%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/{ => pin}/SetupPinView.kt (97%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/{ => pin}/validation/PinValidator.kt (94%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/{ => pin}/validation/SetupPinFailure.kt (90%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/{pin => }/storage/EncryptedPinCodeStorage.kt (95%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/{pin/storage/PinCodeStore.kt => storage/LockScreenStore.kt} (71%) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/{pin/storage/PreferencesPinCodeStore.kt => storage/PreferencesLockScreenStore.kt} (82%) create mode 100644 features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/biometric/FakeBiometricUnlockManager.kt rename features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/{InMemoryPinCodeStore.kt => InMemoryLockScreenStore.kt} (73%) create mode 100644 features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenterTest.kt rename features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/{ => pin}/SetupPinPresenterTest.kt (96%) create mode 100644 libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyRepository.kt create mode 100644 libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/CryptographyModule.kt rename libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/{KeyStoreSecretKeyProvider.kt => KeyStoreSecretKeyRepository.kt} (75%) rename libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/{SimpleSecretKeyProvider.kt => SimpleSecretKeyRepository.kt} (78%) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_null_SetupBiometricView-D-2_2_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.setup.biometric_null_SetupBiometricView-N-2_3_null_0,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-D-2_2_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-D-3_3_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-D-2_2_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-D-3_3_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-D-2_2_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-D-3_3_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-D-2_2_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-D-3_3_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-D-2_2_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-D-3_3_null_4,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-N-2_3_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-N-3_4_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-N-2_3_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-N-3_4_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-N-2_3_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-N-3_4_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-N-2_3_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-N-3_4_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.setup_null_SetupPinView-N-2_3_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.setup.pin_null_SetupPinView-N-3_4_null_4,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock.keypad_null_PinKeypad-D-4_4_null,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock.keypad_null_PinKeypad-D-5_5_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock.keypad_null_PinKeypad-N-4_5_null,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock.keypad_null_PinKeypad-N-5_6_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-3_3_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-4_4_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-3_3_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-4_4_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-3_3_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-4_4_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-3_3_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-4_4_null_3,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-4_4_null_4,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-3_3_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-4_4_null_5,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-3_3_null_5,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-D-4_4_null_6,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-3_4_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-4_5_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-3_4_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-4_5_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-3_4_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-4_5_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-3_4_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-4_5_null_3,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-4_5_null_4,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-3_4_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-4_5_null_5,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-3_4_null_5,NEXUS_5,1.0,en].png => ui_S_t[f.lockscreen.impl.unlock_null_PinUnlockView-N-4_5_null_6,NEXUS_5,1.0,en].png} (100%) diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index 3b35a0ebf4..7109743052 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -30,7 +30,7 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat import com.bumble.appyx.core.integration.NodeHost -import com.bumble.appyx.core.integrationpoint.NodeComponentActivity +import com.bumble.appyx.core.integrationpoint.NodeActivity import com.bumble.appyx.core.plugin.NodeReadyObserver import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.log.logger.LoggerTag @@ -42,7 +42,7 @@ import timber.log.Timber private val loggerTag = LoggerTag("MainActivity") -class MainActivity : NodeComponentActivity() { +class MainActivity : NodeActivity() { private lateinit var mainNode: MainNode diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index a6572451d7..fad45b6def 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -22,5 +22,5 @@ @style/Theme.ElementX - -