Add biometric lock and rate limit handling
Biometric lock: opt-in Face ID/Touch ID/fingerprint app lock with toggle in ProfileScreen. Locks on background, requires auth on foreground return. Platform implementations: BiometricPrompt (Android), LAContext (iOS). Rate limit: 429 responses parsed with Retry-After header, user-friendly error messages in all 10 locales, retry plugin respects 429. ErrorMessageParser updated for both iOS Swift and KMM.
This commit is contained in:
@@ -5,6 +5,8 @@ import com.tt.honeyDue.storage.TokenManager
|
||||
import com.tt.honeyDue.storage.TokenStorage
|
||||
import com.tt.honeyDue.storage.TaskCacheManager
|
||||
import com.tt.honeyDue.storage.TaskCacheStorage
|
||||
import com.tt.honeyDue.storage.BiometricPreference
|
||||
import com.tt.honeyDue.storage.BiometricPreferenceManager
|
||||
import com.tt.honeyDue.storage.ThemeStorage
|
||||
import com.tt.honeyDue.storage.ThemeStorageManager
|
||||
import com.tt.honeyDue.ui.theme.ThemeManager
|
||||
@@ -20,5 +22,8 @@ fun MainViewController() = ComposeUIViewController {
|
||||
ThemeStorage.initialize(ThemeStorageManager.getInstance())
|
||||
ThemeManager.initialize()
|
||||
|
||||
// Initialize BiometricPreference storage
|
||||
BiometricPreference.initialize(BiometricPreferenceManager.getInstance())
|
||||
|
||||
App()
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.tt.honeyDue.platform
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import kotlinx.cinterop.ExperimentalForeignApi
|
||||
import platform.LocalAuthentication.LAContext
|
||||
import platform.LocalAuthentication.LAPolicyDeviceOwnerAuthenticationWithBiometrics
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* iOS implementation of biometric authentication using LocalAuthentication framework (LAContext).
|
||||
*/
|
||||
@OptIn(ExperimentalForeignApi::class)
|
||||
class IOSBiometricAuthPerformer : BiometricAuthPerformer {
|
||||
|
||||
override fun isBiometricAvailable(): Boolean {
|
||||
val context = LAContext()
|
||||
return context.canEvaluatePolicy(
|
||||
LAPolicyDeviceOwnerAuthenticationWithBiometrics,
|
||||
error = null
|
||||
)
|
||||
}
|
||||
|
||||
override fun authenticate(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
onResult: (BiometricResult) -> Unit
|
||||
) {
|
||||
if (!isBiometricAvailable()) {
|
||||
onResult(BiometricResult.NotAvailable)
|
||||
return
|
||||
}
|
||||
|
||||
val context = LAContext()
|
||||
context.localizedFallbackTitle = ""
|
||||
|
||||
context.evaluatePolicy(
|
||||
LAPolicyDeviceOwnerAuthenticationWithBiometrics,
|
||||
localizedReason = subtitle
|
||||
) { success, error ->
|
||||
MainScope().launch {
|
||||
if (success) {
|
||||
onResult(BiometricResult.Success)
|
||||
} else {
|
||||
val message = error?.localizedDescription ?: "Authentication failed"
|
||||
onResult(BiometricResult.Failed(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
actual fun rememberBiometricAuth(): BiometricAuthPerformer {
|
||||
return remember { IOSBiometricAuthPerformer() }
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.tt.honeyDue.storage
|
||||
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
/**
|
||||
* iOS implementation of biometric preference storage using NSUserDefaults.
|
||||
* Follows the same pattern as ThemeStorageManager.ios.kt.
|
||||
*/
|
||||
actual class BiometricPreferenceManager {
|
||||
private val defaults = NSUserDefaults.standardUserDefaults
|
||||
|
||||
actual fun isBiometricEnabled(): Boolean {
|
||||
return defaults.boolForKey(KEY_BIOMETRIC_ENABLED)
|
||||
}
|
||||
|
||||
actual fun setBiometricEnabled(enabled: Boolean) {
|
||||
defaults.setBool(enabled, forKey = KEY_BIOMETRIC_ENABLED)
|
||||
defaults.synchronize()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_BIOMETRIC_ENABLED = "biometric_enabled"
|
||||
|
||||
private val instance by lazy { BiometricPreferenceManager() }
|
||||
|
||||
fun getInstance(): BiometricPreferenceManager = instance
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user