support using biometrics to auth

This commit is contained in:
BinTianqi
2024-05-15 13:48:37 +08:00
parent b7f6eeadb2
commit 405b897754

View File

@@ -10,6 +10,7 @@ import android.widget.Toast
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.AuthenticationCallback
import androidx.biometric.BiometricPrompt.PromptInfo
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -22,12 +23,15 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
import kotlinx.coroutines.*
class AuthFragment: Fragment() {
@OptIn(DelicateCoroutinesApi::class)
@SuppressLint("UnrememberedMutableState")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val sharedPref = context?.getSharedPreferences("data", Context.MODE_PRIVATE)!!
@@ -40,6 +44,11 @@ class AuthFragment: Fragment() {
fragmentTransaction.commit()
}
}
val promptInfo = PromptInfo.Builder()
.setTitle("Auth")
.setSubtitle("Auth OwnDroid with password or biometric")
.setConfirmationRequired(true)
var fallback = false
val callback = object: AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
@@ -48,16 +57,32 @@ class AuthFragment: Fragment() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if(errorCode == BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL) onAuthSucceed()
if(errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) fallback = true
if(errorCode == BiometricPrompt.ERROR_CANCELED) return
Toast.makeText(context, errString, Toast.LENGTH_SHORT).show()
}
}
GlobalScope.launch(Dispatchers.Main) {
while(true){
if(fallback){
val fallbackPromptInfo = PromptInfo.Builder()
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.setTitle("Auth")
.setSubtitle("Auth OwnDroid with password")
.setConfirmationRequired(true)
.build()
authWithBiometricPrompt(requireActivity(), fallbackPromptInfo, callback)
break
}
delay(50)
}
}
return ComposeView(requireContext()).apply {
setContent {
val materialYou = mutableStateOf(sharedPref.getBoolean("material_you",true))
val blackTheme = mutableStateOf(sharedPref.getBoolean("black_theme", false))
OwnDroidTheme(materialYou.value, blackTheme.value) {
Auth(this@AuthFragment.requireActivity(), callback)
Auth(this@AuthFragment.requireActivity(), callback, promptInfo)
}
}
}
@@ -65,7 +90,8 @@ class AuthFragment: Fragment() {
}
@Composable
fun Auth(activity: FragmentActivity, callback: AuthenticationCallback) {
fun Auth(activity: FragmentActivity, callback: AuthenticationCallback, promptInfo: PromptInfo.Builder) {
val context = LocalContext.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
@@ -74,7 +100,19 @@ fun Auth(activity: FragmentActivity, callback: AuthenticationCallback) {
Text(text = "Authenticate", style = MaterialTheme.typography.headlineLarge, color = MaterialTheme.colorScheme.onBackground)
Button(
onClick = {
authWithBiometricPrompt(activity, callback)
val bioManager = BiometricManager.from(context)
when(BiometricManager.BIOMETRIC_SUCCESS){
bioManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) ->
promptInfo
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
.setNegativeButtonText("Use password")
bioManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) ->
promptInfo
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK)
.setNegativeButtonText("Use password")
else -> promptInfo.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
}
authWithBiometricPrompt(activity, promptInfo.build(), callback)
}
){
Text(text = "Start")
@@ -82,14 +120,8 @@ fun Auth(activity: FragmentActivity, callback: AuthenticationCallback) {
}
}
private fun authWithBiometricPrompt(activity: FragmentActivity, callback: AuthenticationCallback) {
private fun authWithBiometricPrompt(activity: FragmentActivity, promptInfo: PromptInfo, callback: AuthenticationCallback) {
val executor = ContextCompat.getMainExecutor(activity.applicationContext)
val biometricPrompt = BiometricPrompt(activity, executor, callback)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.setTitle("Auth")
.setConfirmationRequired(true)
.setSubtitle("Enter password")
.build()
biometricPrompt.authenticate(promptInfo)
}