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