User wants to login only with FaceId,But when I code like
executor = ContextCompat.getMainExecutor(requireContext())
biometricPrompt = BiometricPrompt(this, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int,
errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
moveToLogin()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
}
})
promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric login of ${getString(R.string.app_name)}")
.setSubtitle("Log in using your biometric credential")
.setNegativeButtonText("Use account password")
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK)
.build()
biometricPrompt.authenticate(promptInfo)
This shows both Fingerprint and FaceId, But i need to show only faceID Eventhough both authentication is enabled in system level.
Related
I have this function :
fun createBiometricPrompt(
activity: AppCompatActivity,
processSuccess: (BiometricPrompt.AuthenticationResult) -> Unit,
callback: BiometricCallback
): BiometricPrompt {
var nbFailure = 0
val executor = ContextCompat.getMainExecutor(activity)
val callback = object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errCode: Int, errString: CharSequence) {
super.onAuthenticationError(errCode, errString)
Log.d(TAG, "errCode is $errCode and errString is: $errString")
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Log.d(TAG, "User biometric rejected.")
nbFailure++;
if(nbFailure == MAX_BIOMETRICS_FAILURE){
nbFailure = 0
callback.onFailure()
}
}
which is using a callback callback: BiometricCallback, it's only for now using it for notifying that a failure happened by sending callback.onFailure()
Callback is called in the calling Fragment by doing this:
Fragment.kt
biometricPrompt =
BiometricPromptUtils.createBiometricPrompt(requireActivity() as AppCompatActivity, ::encryptAndStoreServerToken, object: BiometricCallback {
override fun onFailure() {
biometricPrompt.cancelAuthentication()
viewModel.loginError.set("Error Biometrics")
}
})
The onFailure is used then to cancel the process and display an error.
The Callback interface is defined as :
interface BiometricCallback {
fun onFailure()
}
I have been asked to use a suspend function instead of a callback but I have no clue how to do it properly. Should I use LiveData ? If any idea, please help
Thanks
you can use suspendCancellableCoroutine or suspendCoroutine to convert any callback based Api in direct style to make it more kotlin friendly plus it provides more encapsulation you will be just returned with the result all complexity is handled inside the function only
suspend fun authenticate():BiomatricPrompt.AuthenticationResult? {
return suspendCancelableCoroutine { continuation ->
biometricPrompt = BiometricPrompt(this, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int,
errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
continuation.resume(null,null)
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
continuation.resume(result,null)
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
continuation.resume(null,null)
}
})
promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric login for my app")
.setSubtitle("Log in using your biometric credential")
.setNegativeButtonText("Use account password")
.build()
biometricPrompt.authenticate(promptInfo)
}
}
usage
val authenticationResult = authenticate()
if(authenticationResult == null){
//authentication failed
}else{
//authenticated successfully
}
you can use custom object as result type to handle more use cases
more information
I have tried to setup fingerprint, pattern/password/pin, faceUnlock for my app. But Biometric doesn't works it always showing fingerprint with use pattern dialog. Is Still Biometrics not support FaceUnlock? If Not supported Biometric dependency means which library I should use in my app which contains fingerprint, pattern/password/pin and faceUnlock.
class LoginActivity : AppCompatActivity(){
private lateinit var biometricPrompt: BiometricPrompt
private lateinit var executor: Executor
private lateinit var callBack: BiometricPrompt.AuthenticationCallback
lateinit var prompt: BiometricPrompt.PromptInfo
private var keyguardManager: KeyguardManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityLoginBinding>(this, R.layout.activity_login)
executor = ContextCompat.getMainExecutor(this)
biometricPrompt = BiometricPrompt(this, executor, object: BiometricPrompt.AuthenticationCallback(){
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
tvAuthStatus.text = "Authentication Failed"
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
startActivity(Intent(this#LoginActivity, MainActivity::class.java))
finish()
tvAuthStatus.text = "Authentication Success"
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
tvAuthStatus.text = "Error" + errString
}
})
prompt = BiometricPrompt.PromptInfo.Builder()
.setTitle("RoomDB FingerPrint Login")
.setNegativeButtonText("Login using fingerprint or face")
.setNegativeButtonText("Cancel")
.build()
btnAuth.setOnClickListener {
biometricPrompt.authenticate(prompt)
}
}
}
Maybe, in your case, the reason is your test device is released with the face authentication feature before the same API for face authentication appears in the official Android SDK. It makes third-party applications in your test device cannot use face authentication. Check this library, it said about this thing in README.md "I have a device that can be unlocked using Fingerprint/Face/Iris and(or) I can use this biometric type in pre-installed apps. But it doesn't work on 3rd party apps. Can you help?"
https://github.com/sergeykomlach/AdvancedBiometricPromptCompat#i-have-a-device-that-can-be-unlocked-using-fingerprintfaceiris-andor-i-can-use-this-biometric-type-in-pre-installed-apps-but-it-doesnt-work-on-3rd-party-apps-can--you-help
Hope it can help.
I'm new to Android development and not sure if this should be asked here or in stack exchange.
Let's say for example, I want to build a simple app that is ad hoc. Meaning for that specific purpose only and not long term. I want to save time building/setting up my own authentication and perhaps for user's convenience of remembering another password. Is there a way where my app can utilize the phone's password authentication if it's set-up by the user. It could be pin, password, pattern. Everytime user opens the app, the phone prompts the lock screen, sort of, before I can use it.
In manifest file
<uses-permission android:name="android.permission.USE_BIOMETRIC" /> //this is for SDK_INT >= 28
<uses-feature android:name="android.software.secure_lock_screen"/>//this is for below
In your activity, probably you should use a blank view activity for this
val km = getSystemService(KEYGUARD_SERVICE) as KeyguardManager
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
val executor = ContextCompat.getMainExecutor(this)
val biometricPrompt = BiometricPrompt(this, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int,
errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Toast.makeText(applicationContext,
"Authentication error: $errString", Toast.LENGTH_SHORT)
.show()
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Toast.makeText(applicationContext,
"Authentication succeeded!", Toast.LENGTH_SHORT)
.show()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Toast.makeText(applicationContext, "Authentication failed",
Toast.LENGTH_SHORT)
.show()
}
})
promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric login for my app")
.setSubtitle("Log in using your biometric credential")
.setNegativeButtonText("Use account password")
.build()
biometricPrompt.authenticate(promptInfo)
}else{
#Suppress("DEPRECATION")
//Added in API level 21
//Deprecated in API level 29 as per android doc
val i = km.createConfirmDeviceCredentialIntent("title", "description")
startActivityForResult(i, 1000)
}
Then in on activity result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode==1000&&resultCode==Activity.RESULT_OK){
//verified open next activity
}
}
I am working on authenticating my my via BiometricPrompt
The UI shown by system varies
on Motorola (stock Android) I get BottomSheet
on OnePlus I get Full screen activity
How can I know what type of UI would be shown Or how can I control the ui
My problem
Facing a big issue in Activity transition
In BottomSheet UI I can see the my app screen UI elements which I don't want
I can't guess how GooglePay(Tez) is doing
MyCode
val executor = ContextCompat.getMainExecutor(activity)
val biometricPrompt = BiometricPrompt(activity, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if (errorCode == BiometricConstants.ERROR_USER_CANCELED) {
listener.onAuthCancelled()
} else {
listener.onAuthFailed()
}
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
listener.onAuthSucceeded()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
listener.onAuthFailed()
}
})
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Title")
.setSubtitle("Sub title ")
.setConfirmationRequired(false)
.setDeviceCredentialAllowed(true)
.build()
biometricPrompt.authenticate(promptInfo)
It looks like BiometricPrompt 1.0.0 has a bug where it stays in invisible state throwing that exception
ill suggest a workaround in the answer
EDIT(thanks to #Isai Damier):
Way to reproduce:
open the BiometricPrompt
press back button - close the prompt
press back again and exit the app
return to the app - try to open the prompt again
this fix was introduces when biometric prompt version was 1.0.0. this issue dosent reproduces in 1.0.1
//-------------------------------------------------------------------------
i came up with a work around - call 'cancelAuthentication' on the prompt upon user cancelation.
full code is as follows (used in a react native app - thats why the promise are for):
private const val E_BIOMETRIC_ERR = "E_FINGER_PRINT_ERR"
private const val OPENED = "success"
private var bp: BiometricPrompt? = null
private var mAuthenticationPromise: Promise? = null
fun authenticate(activity: AppCompatActivity, title: String,
subTitle: String, description: String, authenticationPromise: Promise) {
mAuthenticationPromise = authenticationPromise
val executor = Executors.newSingleThreadExecutor()
bp?.cancelAuthentication()
bp = getBiometricPrompt(activity, executor)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(title)
.setSubtitle(subTitle)
.setDescription(description)
.setDeviceCredentialAllowed(true)
.build()
bp!!.authenticate(promptInfo)
}
private fun getBiometricPrompt(activity: AppCompatActivity, executor: Executor): BiometricPrompt {
return BiometricPrompt(activity, executor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
mAuthenticationPromise?.resolve(OPENED)
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
bp!!.cancelAuthentication()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
bp!!.cancelAuthentication()
}
})
}
I hope it helps anyone - cheers