From a73fc6c48f6dd7d34080c1722522163ba3d85a28 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 23 Dec 2023 19:57:55 +0100 Subject: [PATCH] feat: Add biometric authentication check --- .../java/app/myzel394/alibi/db/AppSettings.kt | 12 +--- .../myzel394/alibi/helpers/AppLockHelper.kt | 64 +++++++++++++++++++ .../java/app/myzel394/alibi/ui/Navigation.kt | 30 +-------- 3 files changed, 68 insertions(+), 38 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 4a68400..6657419 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -553,16 +553,8 @@ data class NotificationSettings( @Serializable class AppLockSettings { + // If the object is present, biometric authentication is enabled. + // To disable biometric authentication, set the instance to null. val isEnabled get() = true - - companion object { - fun getDefaultInstance(): AppLockSettings = AppLockSettings() - - fun isSupported(context: Context): Boolean { - val biometricManager = BiometricManager.from(context) - when (biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL)) { - - } - } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt new file mode 100644 index 0000000..b963a14 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt @@ -0,0 +1,64 @@ +package app.myzel394.alibi.helpers + +import android.content.Context +import androidx.biometric.BiometricManager +import androidx.core.content.ContextCompat +import androidx.biometric.BiometricPrompt +import androidx.fragment.app.FragmentActivity +import kotlinx.coroutines.CompletableDeferred + +class AppLockHelper { + enum class SupportType { + AVAILABLE, + UNAVAILABLE, + NONE_ENROLLED, + } + + companion object { + fun isSupported(context: Context): SupportType { + val biometricManager = BiometricManager.from(context) + when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) { + BiometricManager.BIOMETRIC_SUCCESS -> return SupportType.AVAILABLE + BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> return SupportType.NONE_ENROLLED + + else -> return SupportType.UNAVAILABLE + } + } + + fun authenticate( + context: Context, + title: String, + subtitle: String + ): CompletableDeferred { + val deferred = CompletableDeferred() + + val mainExecutor = ContextCompat.getMainExecutor(context) + val biometricPrompt = BiometricPrompt( + context as FragmentActivity, + object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + deferred.completeExceptionally(Exception(errString.toString())) + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + deferred.complete(Unit) + } + + override fun onAuthenticationFailed() { + deferred.completeExceptionally(Exception("Authentication failed")) + } + } + ) + + val promptInfo = BiometricPrompt.PromptInfo.Builder() + .setTitle(title) + .setSubtitle(subtitle) + .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL) + .build() + + biometricPrompt.authenticate(promptInfo) + + return deferred + } + } +} diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index dfb3418..bc1aa2a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -45,6 +45,7 @@ import app.myzel394.alibi.ui.screens.CustomRecordingNotificationsScreen import app.myzel394.alibi.ui.screens.SettingsScreen import app.myzel394.alibi.ui.screens.WelcomeScreen import app.myzel394.alibi.ui.utils.CameraInfo +import app.myzel394.alibi.helpers.AppLockHelper const val SCALE_IN = 1.25f @@ -73,34 +74,7 @@ fun Navigation( LaunchedEffect(Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - val executor = ContextCompat.getMainExecutor(context) - val promptInfo = BiometricPrompt.Builder(context) - .setTitle("Biometric login for my app") - .setSubtitle("Log in using your biometric credential") - .setAllowedAuthenticators( - BIOMETRIC_STRONG or DEVICE_CREDENTIAL - ) - .build() - - // Prompt appears when user clicks "Log in". - // Consider integrating with the keystore to unlock cryptographic operations, - // if needed by your app. - promptInfo.authenticate( - CancellationSignal(), - executor, - object : BiometricPrompt.AuthenticationCallback() { - override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { - super.onAuthenticationError(errorCode, errString) - } - - override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { - super.onAuthenticationSucceeded(result) - } - - override fun onAuthenticationFailed() { - super.onAuthenticationFailed() - } - }) + AppLockHelper.authenticate(context, "Title", "Subtitle") } }