前言
现在越来越多的 app 会用到锁屏密码验证或者生物识别(指纹识别、面容识别),最常见的场景就是各大银行登录的时候为了免密登录而设置的验证,目的是为了验证是否是本人操作。
另一种场景就是跟本文所讲述的一样,在用户忘记密码的时候可以通过系统密码(也就是锁屏密码与生物识别)验证身份从而进行密码的修改,不然就是类似于QQ一样,需要申诉或者通过密保找回。
使用到的类
KeyguardManager
这个类是 Android SDK 中自带的,目的是对 Keyguard 进行管理,即对锁屏进行管理,本文主要用到下面几个方法:
-
isDeviceSecure:判断设备是否设置了锁屏密码(包括 PIN 码、解锁图案或数字密码),只能用于
Android 6.0以上。 -
isKeyguardSecure:与 isDeviceSecure 一样,区别在于比 isDeviceSecure 多了一个 SIM 卡锁定的判断,以及对版本的兼容,可以兼容到
Android 3.2以上。 -
createConfirmDeviceCredentialIntent:创建并弹出锁屏密码验证框。
Biometric库
这是Google提供的生物识别的库,文档传送门
在 Android 6.0 的时候SDK引入了 FingerprintManager 作为指纹识别的管理类,但是在 Android 9.0 的时候废弃了,从而使用 Biometric 库来代替,库中已经包含了所有需要添加的权限,所以可以直接引入依赖并使用。
implementation 'androidx.biometric:biometric:1.1.0'
声明身份验证类型:
-
BIOMETRIC_STRONG:使用 Android 兼容性定义页面上定义的 3 类生物识别技术进行身份验证,简单来说就是支持加密密钥使用。
-
BIOMETRIC_WEAK:使用 Android 兼容性定义页面上定义的 2 类生物识别技术进行身份验证,简单来说就是普通的验证,不需要进行加密。
-
DEVICE_CREDENTIAL:使用屏幕锁定凭据(即用户的 PIN 码、解锁图案或密码)进行身份验证。(Android 10 以上可用)
锁屏密码
上面介绍了需要用到的类,以及引入了 Biometric 库的依赖了,那么现在可以开始码代码了。
对于锁屏密码,我们需要先判断设备的 Android 版本(如果大家的项目中的 minSdk 为23或以上的话,可以忽略这部分内容,直接只使用 BiometricPrompt 方式),当 Android 版本小于 10 的话,我们使用 KeyguardManager 来进行锁屏密码验证,当 Android 版本大于 10 的话,我们统一使用 Biometric 库来进行锁屏密码验证,这也是 Google 推荐的方式。
下面我们需要先判断一下用户设备是否有设置锁屏密码:
private fun checkDeviceHasLockPwd(): Boolean {
val keyguardManager = getSystemService(KEYGUARD_SERVICE) as KeyguardManager
return keyguardManager.isKeyguardSecure
}
关于判断是否设置了锁屏密码,这里为了方便,是统一使用了 KeyguardManager 来进行判断,否则的话还需要根据 Android 版本进行判断,稍微麻烦了一点点。
下面我们来写弹锁屏密码验证的代码,xml 布局这里不贴代码了,只是简单的定义了一个 Button 进行点击:
class VerifyActivity : AppCompatActivity() {
private val mBinding by lazy { ActivityVerifyBinding.inflate(layoutInflater) }
private lateinit var mKeyguardManager: KeyguardManager
private lateinit var mScreenLockResultLauncher: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
mKeyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
initListener()
}
private fun initListener() {
mBinding.btnScreenLockAuth.setOnClickListener {
// 根据Android版本判断使用的方式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
showDeviceLockVerify()
} else {
showScreenLockPwd()
}
}
// 由于startActivityForResult已弃用,所以改成registerForActivityResult方式来注册锁屏输入结果的回调监听
mScreenLockResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
Toast.makeText(this, "锁屏密码验证成功", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "锁屏密码验证失败", Toast.LENGTH_SHORT).show()
}
}
}
/**
* 跳转锁屏密码校验页面
*/
private fun showScreenLockPwd() {
val intent = mKeyguardManager.createConfirmDeviceCredentialIntent("锁屏密码", "请输入锁屏密码验证您的身份")
if (intent != null) {
// 打开锁屏密码验证页面
mScreenLockResultLauncher.launch(intent)
} else {
Toast.makeText(this, "当前设备暂未设置锁屏密码", Toast.LENGTH_SHORT).show()
}
}
/**
* 跳转锁屏密码验证页面(Android 10 以上调用)
*/
private fun showDeviceLockVerify() {
val executor = ContextCompat.getMainExecutor(this)
// 创建biometricPrompt以接收结果的回调
val biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Toast.makeText(this@VerifyActivity, "锁屏密码验证成功", Toast.LENGTH_SHORT).show()
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
// 这里的判断是因为当用户点击取消按钮的时候,不需要提示,其余的验证失败都需要提示
if (errorCode != BiometricPrompt.ERROR_NEGATIVE_BUTTON && errString.toString().isNotEmpty()) {
Toast.makeText(this@VerifyActivity, errString.toString(), Toast.LENGTH_SHORT).show()
}
}
})
// 创建锁屏密码输入的弹框以及需要显示的文字
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.setTitle("锁屏密码")
.setConfirmationRequired(true)
.setSubtitle("请输入锁屏密码验证您的身份")
.build()
// 发起验证
biometricPrompt.authenticate(promptInfo)
}
}
上面的注释都有标注得很清楚,主要就是 KeyguardManager 以及 Biometric 库中 BiometricPrompt 的使用,代码运行完之后的截图如下:
由于输入密码涉及到敏感信息,手机禁止截屏,所以只能粗糙的拍下来了....
生物识别
生物识别包含指纹识别以及面容识别,但是由于这个库对国产手机普遍都无法弹出面容识别,目前暂时只发现三星手机还有部分荣耀手机是支持面容识别的,所以这里没有把面容识别考虑进去,但是写法都是一样的不需要修改代码。
先来判断一下用户的设备硬件是否支持生物识别以及是否已经设置了指纹/面容:
private fun checkDeviceHasFingerAuth(): Boolean {
val biometricManager = BiometricManager.from(this)
return biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)
== BiometricManager.BIOMETRIC_SUCCESS
}
在上述的锁屏密码验证的代码基础上新增代码如下:
private fun initListener() {
....
mBinding.btnFingerAuth.setOnClickListener {
showFingerVerify()
}
}
/**
* 跳转指纹识别验证页面
*/
private fun showFingerVerify() {
val executor = ContextCompat.getMainExecutor(this)
val biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Toast.makeText(this@VerifyActivity, "指纹验证成功", Toast.LENGTH_SHORT).show()
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if (errorCode != BiometricPrompt.ERROR_NEGATIVE_BUTTON && errString.toString().isNotEmpty()) {
Toast.makeText(this@VerifyActivity, errString.toString(), Toast.LENGTH_SHORT).show()
}
}
})
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK)
.setTitle("指纹识别")
.setConfirmationRequired(true)
.setSubtitle("请使用您的指纹进行识别")
.setNegativeButtonText("取消")
.build()
biometricPrompt.authenticate(promptInfo)
}
这部分的调用跟上述的锁屏密码调用的写法是基本一致的,区别只有文案以及 PromptInfo 中新增了 setNegativeButtonText("取消") 用于取消识别,而在锁屏密码验证的时候不需要这句代码是因为锁屏密码验证的弹框中已经自带了取消按钮。
代码运行完之后的截图如下:
这是只有指纹识别的弹窗,如果是三星手机的话,还会弹出指纹以及面容识别的弹窗让用户自行选择验证方式:
总结
写这篇文章主要是为了下次再使用的时候可以直接搬运而已,溜了溜了~