0

I'm trying to figure out how to modify (if it's possible) the normal behavior of biometricPrompt, in particular i want to display Gandalf, when the authentication fails. I'm currently displaying it with a custom alertDialog, but it remains in background, with the biometricPrompt fragment on foreground exactly like this, and it loses all of its dumbness... The best solution would probably be to display both, alertDialog and biometricPrompt, on foreground, displaying the image only in the upper half of the screen, but at the moment I've no idea of how to do it, or better, I have no idea how to link layouts together to manage size / margins and everything else.

The other thing I was thinking is to remove the biometricPrompt, so the alert dialog will be put on foreground, but any solution I've tried has failed miserably.

Any type of help/ideas will be welcome.

Anyway, here's the code:

class BiometricPromptManager(private val activity: FragmentActivity) {

private val cryptoManager = CryptoManager(activity)

fun authenticateAndDecrypt(failedAction: () -> Unit, successAction: (String) -> Unit) {

    //  display biometric prompt, if the user is authenticated, the decryption will start
    // if biometric related decryption gives positives results, the successAction will start services data decryption

    val executor = Executors.newSingleThreadExecutor()
    val biometricPrompt = BiometricPrompt(activity, executor, object : BiometricPrompt.AuthenticationCallback() {

        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            super.onAuthenticationSucceeded(result)

            cryptoManager.startDecrypt(failedAction,successAction)
        }

        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            super.onAuthenticationError(errorCode, errString)

            activity.runOnUiThread { failedAction() }
        }

        override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()

            activity.runOnUiThread { failedAction() }
        }
    })

    val promptInfo = biometricPromptInfo()
    biometricPrompt.authenticate(promptInfo)
}

    private fun biometricPromptInfo(): BiometricPrompt.PromptInfo {
        return BiometricPrompt.PromptInfo.Builder()
            .setTitle("Fingerprint Authenticator")
            .setNegativeButtonText(activity.getString(android.R.string.cancel))
            .build()
    }
}

Open biometric auth from activity :

private fun openBiometricAuth(){

    if(sharedPreferences.getBoolean("fingerPrintEnabled",false)) {
        if (BiometricManager.from(this).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) { // check for hardware/permission
            biometric.visibility = View.VISIBLE
            BiometricPromptManager(this).authenticateAndDecrypt(::failure, ::callDecryption)
        }
    }
}

What to do when the user is not recognized :

private fun failure(){

    val view = layoutInflater.inflate(R.layout.gandalf, null)

    val builder = AlertDialog.Builder(this)

    builder.setView(view)

    builder.setPositiveButton("Dismiss") { dialog: DialogInterface, id: Int -> dialog.cancel() }

    val alertDialog = builder.create()

    alertDialog.show()
}

1 Answers1

0

The Biometric API itself handles failed attempts at authentication the following way:

  1. For each failed attempt the onAuthenticationFailed() callback is invoked.
  2. The user gets 5 tries, after the 5th attempt fails the onAuthenticationError() callback receives the error code ERROR_LOCKOUT and the user must wait for 30 seconds to try again.

Showing your dialog inside onAuthenticationFailed() may be too eager and may result in poor user experience. And so a good place might be after you get the ERROR_LOCKOUT. The way the AndroidX Biometric Library works is that it dismisses the BiometricPrompt when the error is sent. Therefore you should have little problem showing your own dialog at that point.

In any case -- i.e. beyond these opinions -- the more general approach is to call cancelAuthentication() to dismiss the prompt and then proceed to showing your own dialog that way.

Also please follow blogpost1 and blogpost2 for recommended design pattern for BiometricPrompt.

Isai Damier
  • 976
  • 6
  • 8