I've implemented the fingerprint feature into my Android app and I'm using jetpack compose. This is my Biometric code.
import android.content.Context
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import sahalwallet.app.R
import sahalwallet.app.theme.bu
import sahalwallet.app.theme.col
import sahalwallet.app.theme.ui
class Biometric:FragmentActivity() {
fun status(con: Context): Boolean {
var result = false
val biometricManager = BiometricManager.from(con)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
BiometricManager.BIOMETRIC_SUCCESS -> result = true
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> result = false
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> result = false
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> result = false
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED ->
result = true
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED ->
result = true
BiometricManager.BIOMETRIC_STATUS_UNKNOWN ->
result = false
}
} else {
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
BiometricManager.BIOMETRIC_SUCCESS -> result = true
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> result = false
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> result = false
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> result = false
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED ->
result = true
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED ->
result = true
BiometricManager.BIOMETRIC_STATUS_UNKNOWN ->
result = false
}
}
return result
}
fun authenticate(
activity: FragmentActivity,
title: String,
otherOption: String,
onError: (Int, CharSequence) -> Unit,
onSuccess: (BiometricPrompt.AuthenticationResult) -> Unit,
onFailed: () -> Unit,
) {
Log.d("activity is =>",activity.toString())
val executor =ContextCompat.getMainExecutor(activity)
val biometricPrompt = BiometricPrompt(activity, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int,
errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
onError(errorCode, errString)
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
onSuccess(result)
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
onFailed()
}
})
val promptInfo: BiometricPrompt.PromptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(title)
.setNegativeButtonText(otherOption)
.build()
biometricPrompt.authenticate(promptInfo)
}
@Composable
fun BiometricUI() {
val context = LocalContext.current
if (status(context)) {
bu.Icon(
iconEnd = R.drawable.fingerprint,
round = ui.tinyHeight,
bg = col.whiteBrush,
height = 90,
action = {
/**TODO
* We need to pass an object of FragmentActivity into the activity parameter
*/
authenticate(
activity = this@Biometric,
title = "Authentication Required",
otherOption = "Password",
onSuccess = {
runOnUiThread {
Toast.makeText(
context,
"Authenticated successfully",
Toast.LENGTH_SHORT
)
.show() // isAuthenticated.value = true
}
},
onError = { _, _ ->
/*
On Clicking on Password
*/
},
onFailed = {
runOnUiThread {
Toast.makeText(
context,
"Authentication failed",
Toast.LENGTH_SHORT
)
.show()
}
}
)
}
)
}
} }
val fingerprint = Biometric()
And in my code I'm calling fingerprint.BiometricUI()
And I'm using this into some Composable in my code and this is also showing the biometric icon so when I click this the issue is coming:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.concurrent.Executor android.content.Context.getMainExecutor()' on a null object reference
I tried to activity.mainExecutor
and ContextCompt.getMainexecutor
but both are giving the same issue!
java.util.concurrent.Executor android.content.Context.getMainExecutor()