综合技术

Biometrics – AndroidX

微信扫一扫,分享到朋友圈

Biometrics – AndroidX
0

Android has supported fingerprint sensors since API 23 and previously we looked at the new APIs to handle them usingBiometricPrompt andBiometricManager. For the latter we looked at how to create a compat wrapper so that this works back to API 23. But we didn’t implement one for BiometricPrompt . Since writing those articles, I have discovered that an AndroidX implementation of BiometricPrompt exists. In this article we’ll look at this and port the app that we created previously to use the AndroidX version, and be backwardly compatible to API 23 also.

Image:
free icons from pngtree.com

BiometricPrompt is a mechanism provided by the Android Framework which greatly simplifies the process of performing biometric authentication. Typically it is used as in-app security forcing the user to authenticate before they are allowed to access more secure data within the app. The older version is FingerprintManager which performs the actual authentication but requires us to implement to UI for scanning ourselves – so BiometricPrompt simplifies this enormously. The AndroidX biometric library is a compat wrapper around both of these APIs, but included the necessary UI components for the legacy FingerprintManager implementation. It is also a slightly simplified version of the Android framework implementation of BiometricPrompt , so it really does make sense to use it.

The project that we used previously can be found here . We need to make only minor changes to get it backwardly compatible to API 23.

The first thing that we need to do is include the AndroidX biometric library which, at the time of writing, is 1.0.0-alpha04 :

.
.
.
dependencies {
    implementation 'androidx.biometric:biometric:1.0.0-alpha04'
}
.
.
.

There are only two files which touch BiometricPrompt . The first is AuthenticationResult.kt for which we only need to change the import to use the AndroidX version of BiometricPrompt :

package com.stylingandroid.biometrics
 
import androidx.biometric.BiometricPrompt
 
internal sealed class AuthenticationResult {
    internal data class Success(val cryptoObject: BiometricPrompt.CryptoObject?) :
        AuthenticationResult()
    internal data class RecoverableError(val code: Int, val message: CharSequence) :
        AuthenticationResult()
    internal data class UnrecoverableError(val code: Int, val message: CharSequence) :
        AuthenticationResult()
    internal object Failure : AuthenticationResult()
    internal object Cancelled : AuthenticationResult()
}

The majority of the changes are required in Authenticator.kt with the result actually being a smaller file – the previous version was a total of 74 lines, but after the changes it is 58 lines. We need to change the import as we did for AuthenticationResult – I won’t bother showing that here.

The Android framework implementation of BiometricPrompt uses a builder which is used to define the characteristics of the bottom sheet that gets displayed when prompting for a biometric authentication. However, because the AndroidX implementation provides its own UI for the legacy wrapper, this gets abstracted in to a separate class BiometricPrompt.PromptInfo which has its own builder class, and this is simplified because there is much less that we need to do:

    private val promptInfo = BiometricPrompt.PromptInfo.Builder()
        .setTitle(fragmentActivity.getString(R.string.biometric_prompt_title))
        .setNegativeButtonText(fragmentActivity.getString(R.string.biometric_prompt_negative_text))
        .build()

The BiometricPrompt.AuthenticationCallback interface is simplified in the AndroidX implementation and we no longer need to implement the onAuthenticationHelp() method, but otherwise it is identical:

private val authCallback = object : BiometricPrompt.AuthenticationCallback() {
 
        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            super.onAuthenticationError(errorCode, errString)
            Timber.d("Error: $errorCode: $errString")
            callback(AuthenticationResult.UnrecoverableError(errorCode, errString))
        }
 
        override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()
            Timber.d("Failed")
            callback(AuthenticationResult.Failure)
        }
 
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            super.onAuthenticationSucceeded(result)
            Timber.d("Success")
            callback(AuthenticationResult.Success(result.cryptoObject))
        }
    }

Next we need to make a couple of small changes to the class itself as the AndroidX implementation of BiometricPrompt has some slightly different requirements:

internal class Authenticator(
    private val fragmentActivity: FragmentActivity,
    private val callback: (AuthenticationResult) -> Unit,
    private val biometricChecker: BiometricChecker = BiometricChecker.getInstance(fragmentActivity)
) {
 
    private val handler = Handler()
    .
    .
    .
}

We now have everything we need to create the AndroidX BiometricPrompt instance:

    private val biometricPrompt = BiometricPrompt(
        fragmentActivity,
        { runnable -> handler.post(runnable) },
        authCallback
    )

The final thing we need to change is the authenticate() method because the method signature of the AndroidX BiometricPrompt#authenticate method is slightly different:

    fun authenticate() {
        if (!biometricChecker.hasBiometrics) {
            callback(AuthenticationResult.UnrecoverableError(
                0,
                fragmentActivity.getString(R.string.biometric_prompt_no_hardware)
            ))
        } else {
            biometricPrompt.authenticate(promptInfo)
        }
    }

It may feel like we need to do some additional work because the constrictor signature of Authenticator has changed but actually we don’t. The previous version required a more generic Context whereas this new version requires a FragmentActivity , but this is actually being called from a FragmentActivity instance which passes this as the first argument, so everything still works quite nicely.

I have made some other small changes to the project, either to update to newer versions of AndroidX libraries, or allow the app itself to work on API 23. For example: with minSdkVersion="28" we could assume Adaptive Icon support, but that no longer applies with minSdkVersion="23" . I haven’t bothered detailing those changes here as they’re not relevant to the purpose of this post, but you can see them in the source.

When we run this, the behaviour on a device running Pie or later is identical to what we had before, but we get this on an Oreo emulator:


That shows most of the possible flows and the UI is slightly different from the Pie version. But it by no means a degradation in UX, just a slightly different UI which still works well.

Adapting the Android framework implementation of BiometricPrompt to the AndroidX one requires a work, but the advantages make that effort worthwhile. Adapting an older FingerprintManager implementation to the AndroidX BiometricManager may require a little more effort, the advantages would be much greater, and could potentially reduce your code size quite considerably because you no longer need to implement the UI yourself.

The source code for this article is available here .

© 2019,Mark Allison. All rights reserved.

阅读原文...


微信扫一扫,分享到朋友圈

Biometrics – AndroidX
0

Styling Android

乐视网:董事、总经理及财务总监张巍因个人原因辞职

上一篇

Remembering Andy Baker

下一篇

评论已经被关闭。

插入图片

热门分类

往期推荐

Biometrics – AndroidX

长按储存图像,分享给朋友