aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/utils/BiometricUtils.kt
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2019-04-21 20:20:21 -0400
committerAllan Wang <me@allanwang.ca>2019-04-21 20:20:21 -0400
commit576cc1a451a16f2d82ee1e41e83c420a85ded47e (patch)
tree848a3ffdd1fb237796c606bdd410953927d0407c /app/src/main/kotlin/com/pitchedapps/frost/utils/BiometricUtils.kt
parentf0f95295bdb2b853aa6262ec4bed2353ce326eee (diff)
downloadfrost-576cc1a451a16f2d82ee1e41e83c420a85ded47e.tar.gz
frost-576cc1a451a16f2d82ee1e41e83c420a85ded47e.tar.bz2
frost-576cc1a451a16f2d82ee1e41e83c420a85ded47e.zip
Add initial biometric test
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/utils/BiometricUtils.kt')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/BiometricUtils.kt89
1 files changed, 89 insertions, 0 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/BiometricUtils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/BiometricUtils.kt
new file mode 100644
index 00000000..476e490d
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/BiometricUtils.kt
@@ -0,0 +1,89 @@
+package com.pitchedapps.frost.utils
+
+import android.content.Context
+import android.hardware.fingerprint.FingerprintManager
+import android.os.Build
+import androidx.biometric.BiometricPrompt
+import androidx.fragment.app.FragmentActivity
+import ca.allanwang.kau.utils.string
+import com.pitchedapps.frost.R
+import kotlinx.coroutines.CompletableDeferred
+import java.util.concurrent.Executor
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+
+typealias BiometricDeferred = CompletableDeferred<BiometricPrompt.CryptoObject?>
+
+/**
+ * Container for [BiometricPrompt]
+ * Inspired by coroutine's CommonPool
+ */
+object BiometricUtils {
+
+ private val executor: Executor
+ get() = pool ?: getOrCreatePoolSync()
+
+ @Volatile
+ private var pool: ExecutorService? = null
+
+ /**
+ * Checks if biometric authentication is possible
+ * Currently, this means checking for enrolled fingerprints
+ */
+ @Suppress("DEPRECATION")
+ fun isSupported(context: Context): Boolean {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false
+ val fingerprintManager = context.getSystemService(FingerprintManager::class.java) ?: return false
+ return fingerprintManager.isHardwareDetected && fingerprintManager.hasEnrolledFingerprints()
+ }
+
+ private fun getOrCreatePoolSync(): Executor =
+ pool ?: Executors.newSingleThreadExecutor().also { pool = it }
+
+ private fun shouldPrompt(context: Context): Boolean {
+ return true
+ }
+
+ fun authenticate(activity: FragmentActivity): BiometricDeferred {
+ val deferred: BiometricDeferred = CompletableDeferred()
+ if (!shouldPrompt(activity)) {
+ deferred.complete(null)
+ return deferred
+ }
+ val info = BiometricPrompt.PromptInfo.Builder()
+ .setTitle(activity.string(R.string.biometrics_prompt_title))
+ .setNegativeButtonText(activity.string(R.string.kau_cancel))
+ .build()
+ BiometricPrompt(activity, executor, Callback(activity, deferred)).authenticate(info)
+ return deferred
+ }
+
+ private class Callback(val activity: FragmentActivity, val deferred: BiometricDeferred) :
+ BiometricPrompt.AuthenticationCallback() {
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
+ deferred.cancel()
+ activity.finish()
+ }
+
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
+ deferred.complete(result.cryptoObject)
+ }
+
+ override fun onAuthenticationFailed() {
+ deferred.cancel()
+ activity.finish()
+ }
+ }
+
+ /**
+ * For completeness we provide a shutdown function.
+ * In practice, we initialize the executor only when it is first used,
+ * and keep it alive throughout the app lifecycle, as it will be used an arbitrary number of times,
+ * with unknown frequency
+ */
+ @Synchronized
+ fun shutdown() {
+ pool?.shutdown()
+ pool = null
+ }
+} \ No newline at end of file