mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Add support for Face ID/Touch ID to app lock. (#1966)
* Fix biometrics with low grace period and backgrounding before unlocked. * Trigger permissions alert when enabling Face ID.
This commit is contained in:
@@ -46,11 +46,7 @@ class AppLockScreenViewModel: AppLockScreenViewModelType, AppLockScreenViewModel
|
||||
|
||||
switch viewAction {
|
||||
case .submitPINCode:
|
||||
guard appLockService.unlock(with: state.bindings.pinCode) else {
|
||||
handleInvalidPIN()
|
||||
return
|
||||
}
|
||||
actionsSubject.send(.appUnlocked)
|
||||
submitPINCode()
|
||||
case .clearPINCode:
|
||||
state.bindings.pinCode = ""
|
||||
case .forgotPIN:
|
||||
@@ -60,6 +56,14 @@ class AppLockScreenViewModel: AppLockScreenViewModelType, AppLockScreenViewModel
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func submitPINCode() {
|
||||
guard appLockService.unlock(with: state.bindings.pinCode) else {
|
||||
handleInvalidPIN()
|
||||
return
|
||||
}
|
||||
actionsSubject.send(.appUnlocked)
|
||||
}
|
||||
|
||||
private func handleForgotPIN() {
|
||||
state.bindings.alertInfo = .init(id: .confirmResetPIN,
|
||||
title: L10n.screenAppLockSignoutAlertTitle,
|
||||
|
||||
+27
-6
@@ -39,13 +39,34 @@ class AppLockSetupBiometricsScreenViewModel: AppLockSetupBiometricsScreenViewMod
|
||||
|
||||
switch viewAction {
|
||||
case .allow:
|
||||
MXLog.info("Enable biometric unlock.")
|
||||
appLockService.biometricUnlockEnabled = true
|
||||
actionsSubject.send(.continue)
|
||||
Task { await enableBiometricUnlock() }
|
||||
case .skip:
|
||||
MXLog.info("Disable biometric unlock.")
|
||||
appLockService.biometricUnlockEnabled = false
|
||||
actionsSubject.send(.continue)
|
||||
disableBiometricUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func enableBiometricUnlock() async {
|
||||
guard case .success = appLockService.enableBiometricUnlock() else {
|
||||
MXLog.error("Enabling biometric unlock failed.")
|
||||
return
|
||||
}
|
||||
MXLog.info("Biometric unlock enabled.")
|
||||
|
||||
// Attempt unlock to trigger Face ID permissions alert.
|
||||
if appLockService.biometryType == .faceID,
|
||||
await !appLockService.unlockWithBiometrics() {
|
||||
MXLog.info("Confirmation failed. Disabling biometric unlock.")
|
||||
appLockService.disableBiometricUnlock()
|
||||
}
|
||||
|
||||
actionsSubject.send(.continue)
|
||||
}
|
||||
|
||||
private func disableBiometricUnlock() {
|
||||
appLockService.disableBiometricUnlock()
|
||||
MXLog.info("Biometric unlock disabled.")
|
||||
actionsSubject.send(.continue)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ struct AppLockSetupSettingsScreenViewState: BindableState {
|
||||
let biometryType: LABiometryType
|
||||
var bindings: AppLockSetupSettingsScreenViewStateBindings
|
||||
|
||||
var supportsBiometry: Bool { biometryType != .none }
|
||||
var enableBiometryTitle: String { L10n.screenAppLockSetupBiometricUnlockAllowTitle(biometryType.localizedString) }
|
||||
var supportsBiometrics: Bool { biometryType != .none }
|
||||
var enableBiometricsTitle: String { L10n.screenAppLockSetupBiometricUnlockAllowTitle(biometryType.localizedString) }
|
||||
}
|
||||
|
||||
struct AppLockSetupSettingsScreenViewStateBindings {
|
||||
|
||||
+23
-1
@@ -45,12 +45,34 @@ class AppLockSetupSettingsScreenViewModel: AppLockSetupSettingsScreenViewModelTy
|
||||
case .disable:
|
||||
showRemovePINAlert()
|
||||
case .enableBiometricsChanged:
|
||||
appLockService.biometricUnlockEnabled = state.bindings.enableBiometrics
|
||||
Task { await toggleBiometrics() }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func toggleBiometrics() async {
|
||||
if state.bindings.enableBiometrics {
|
||||
guard case .success = appLockService.enableBiometricUnlock() else {
|
||||
MXLog.error("Enabling biometric unlock failed.")
|
||||
state.bindings.enableBiometrics = false
|
||||
return
|
||||
}
|
||||
MXLog.info("Biometric unlock enabled.")
|
||||
|
||||
// Attempt unlock to trigger Face ID permissions alert.
|
||||
if appLockService.biometryType == .faceID,
|
||||
await !appLockService.unlockWithBiometrics() {
|
||||
MXLog.info("Confirmation failed. Disabling biometric unlock.")
|
||||
state.bindings.enableBiometrics = false
|
||||
appLockService.disableBiometricUnlock()
|
||||
}
|
||||
} else {
|
||||
appLockService.disableBiometricUnlock()
|
||||
MXLog.info("Biometric unlock disabled.")
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows a confirmation alert to the user before removing their PIN code.
|
||||
private func showRemovePINAlert() {
|
||||
state.bindings.alertInfo = .init(id: .confirmRemovePINCode,
|
||||
|
||||
+2
-2
@@ -32,9 +32,9 @@ struct AppLockSetupSettingsScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
if context.viewState.supportsBiometry {
|
||||
if context.viewState.supportsBiometrics {
|
||||
Section {
|
||||
ListRow(label: .plain(title: context.viewState.enableBiometryTitle),
|
||||
ListRow(label: .plain(title: context.viewState.enableBiometricsTitle),
|
||||
kind: .toggle($context.enableBiometrics))
|
||||
.onChange(of: context.enableBiometrics) { _ in
|
||||
context.send(viewAction: .enableBiometricsChanged)
|
||||
|
||||
@@ -22,7 +22,7 @@ extension LABiometryType {
|
||||
var systemSymbol: SFSymbol {
|
||||
switch self {
|
||||
case .none:
|
||||
MXLog.error("Invalid presentation: Biometry not supported.")
|
||||
MXLog.error("Invalid presentation: Biometrics not supported.")
|
||||
return .viewfinder
|
||||
case .touchID:
|
||||
return .touchid
|
||||
@@ -40,7 +40,7 @@ extension LABiometryType {
|
||||
var localizedString: String {
|
||||
switch self {
|
||||
case .none:
|
||||
MXLog.error("Invalid presentation: Biometry not supported.")
|
||||
MXLog.error("Invalid presentation: Biometrics not supported.")
|
||||
return L10n.screenAppLockBiometricUnlock
|
||||
case .touchID:
|
||||
return L10n.commonTouchIdIos
|
||||
|
||||
Reference in New Issue
Block a user