Android9.0因爲沒法自定義指紋識別框 因此不額外考慮了 bash
本文的場景僅定位於Android6.0以上app
本文實現的登陸流程ide
流程思路啓發於支付寶 登錄界面判斷是否開啓指紋登陸 進入主界面可設置是否開啓指紋登陸post
基本的流程算是整完了哈 接下來就是去瞟一瞟官方的文檔啦(~o ̄3 ̄)~動畫
下面是Android官網中的文檔 指紋識別類ui
這個方法爲指紋識別中的核心方法 用於拉起指紋識別this
關於每一個參數的解釋 加密
crypto: FingerprintManagerCompat.CryptoObject? 密碼對象的包裝類
spa
flags: Int 可選標誌 傳0便可code
callback: FingerprintManagerCompat.AuthenticationCallback 驗證結果的回調
cancel: CancellationSignal 取消操做並向取消監聽器發出信號 用於取消指紋識別時
handler: Handler 能夠直接傳NULL
首先咱們建立一個Cipher形式的密碼對象包裝類 建立密匙時 使用AES + CBC + PADDING_PKCS7進行加密
class CipherHelper {
private var _keystore : KeyStore
init {
_keystore = KeyStore.getInstance(KEYSTORE_NAME)
_keystore.load(null)
}
companion object{
const val KEY_NAME = "MyCipherHelper"
const val KEYSTORE_NAME = "AndroidKeyStore"
const val KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
const val BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC
const val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
const val TRANSFORMATION = "$KEY_ALGORITHM/$BLOCK_MODE/$ENCRYPTION_PADDING"
}
fun createCipher(retry : Boolean) : Cipher{
val key = GetKey()
val cipher = Cipher.getInstance(TRANSFORMATION)
try {
cipher.init(Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE,key)
}catch (e : KeyPermanentlyInvalidatedException){
_keystore.deleteEntry(KEY_NAME)
if(retry){
createCipher(false)
}else{
Exception("Could not create the cipher for fingerprint authentication.",e)
}
}
return cipher
}
private fun GetKey() : Key {
if(!_keystore.isKeyEntry(KEY_NAME)){
CreateKey()
}
return _keystore.getKey(KEY_NAME,null)
}
/** * @description 生成密匙 */
private fun CreateKey(){
val keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME)
val keyGenParameterSpec = KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(ENCRYPTION_PADDING)
.setUserAuthenticationRequired(true)
.build()
keyGenerator.init(keyGenParameterSpec)
keyGenerator.generateKey()
}
}複製代碼
定義一個用於指紋驗證的彈窗
class FingerprintVerifyDialog(context: Activity): Dialog(context, R.style.TransparentDialogStyle) {
private lateinit var onClickListener : OnClickListener
private var tipText : TextView
private var cancelBtn : TextView
init {
setContentView(R.layout.finger_verify_dialog)
setCanceledOnTouchOutside(false)
tipText = findViewById(R.id.finger_verify_tip)
cancelBtn = findViewById(R.id.cancel_finger_verify_btn)
cancelBtn.setOnClickListener { onClickListener.onCancel()
this.dismiss()
}
windowAnim()
}
override fun onBackPressed() {
super.onBackPressed()
onClickListener.onDismiss()
}
fun setOnClickListener(onClickListener: OnClickListener) : FingerprintVerifyDialog{
this.onClickListener = onClickListener
return this
}
fun setTipText(tip : String,color : Int){
tipText.text = tip
tipText.setTextColor(color)
}
interface OnClickListener{
/**
* @description 取消驗證
*/
fun onCancel()
/**
* @description 關閉彈窗
*/
fun onDismiss()
}
private fun windowAnim() {
val window = window!! //獲得對話框
window.setWindowAnimations(R.style.dialogWindowAnim) //設置窗口彈出動畫
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
}
}複製代碼
定義一個SharePrefences 用於存儲用戶打開指紋登陸的標記
class MyPreference<T>(private val name : String, private val default : T) {
private val sharedPreferences : SharedPreferences
by lazy { MyApplication.instance.applicationContext.getSharedPreferences(Constans.SHARE_PREFERENCE_NAME,Context.MODE_PRIVATE)}
operator fun getValue(thisRef : Any?,property : KProperty<*>) : T = getSharedPreferences(name,default)
operator fun setValue(thisRef: Any?,property: KProperty<*>,value: T) = putSharedPreferences(name,value)
private fun putSharedPreferences(name : String,value : T) = with(sharedPreferences.edit()){
when(value){
is Int -> putInt(name,value)
is String -> putString(name,value)
is Float -> putFloat(name,value)
is Long -> putLong(name,value)
is Boolean -> putBoolean(name,value)
else -> throw IllegalArgumentException("SharedPreference can't be save this type")
}.apply()
}
private fun getSharedPreferences(name : String,default: T) : T = with(sharedPreferences){
val res : Any = when(default){
is Int -> getInt(name,default)
is Boolean -> getBoolean(name,default)
is String -> getString(name,default)!!
is Float -> getFloat(name,default)
is Long -> getLong(name,default)
else -> throw IllegalArgumentException("SharedPreference can't be get this type")
}
return res as T
}
}
object DelegatesExt {
fun <T> myPreferences(name : String,default : T) = MyPreference(name,default)
}
class Constans{
companion object{
const val SHARE_PREFERENCE_NAME = "MySharePreference"
const val OPEN_FINGERPRINT_LOGIN = "open_fingerprint_login"
}
}複製代碼
定義一個接口 用於回調監聽
interface FingerprintCallback {
/** * @description 無指紋硬件或硬件不可用 */
fun onHwUnavailable()
/** * @description 未添加指紋 */
fun onNoneEnrolled()
/** * @description 驗證成功 */
fun onSucceeded()
/** * @description 驗證失敗 */
fun onFailed()
/** * @description 取消驗證 */
fun onCancel()
}複製代碼
定義一個擴展類 提早初始初始化加密類 封裝指紋驗證方法 添加監聽 彈出指紋驗證彈窗 對驗證結果回調處理 還有判斷設備是否支持指紋識別方法
class FingerprintImpl {
private lateinit var context: Activity
private lateinit var fingerprintCallback: FingerprintCallback
lateinit var fingerprintVerifyDialog: FingerprintVerifyDialog
private lateinit var cancellationSignal : CancellationSignal
companion object{
private lateinit var cryptoObject : CryptoObject
val instance by lazy {
try {
cryptoObject = CryptoObject(CipherHelper().createCipher(true))
}catch (e : Exception){
e.printStackTrace()
}
FingerprintImpl()
}
}
/** * @description 初始化並調用指紋驗證 */
fun authenticate(context : Activity,callback: FingerprintCallback){
this.context = context
this.fingerprintCallback = callback
cancellationSignal = CancellationSignal()
fingerprintVerifyDialog = FingerprintVerifyDialog(context).setOnClickListener(onClickListener)
cancellationSignal.setOnCancelListener { fingerprintVerifyDialog.dismiss() }
from(context).authenticate(cryptoObject,0,cancellationSignal,authenticationCallback,null)
fingerprintVerifyDialog.show()
}
/** * @description 驗證框監聽 */
private val onClickListener : OnClickListener = object : OnClickListener{
override fun onCancel() {
fingerprintCallback.onCancel()
}
override fun onDismiss() {
cancellationSignal.cancel()
}
}
/** * @description 驗證結果回調 */
private val authenticationCallback = object : AuthenticationCallback(){
override fun onAuthenticationError(errMsgId: Int, errString: CharSequence?) {
super.onAuthenticationError(errMsgId, errString)
if(errMsgId != 5) fingerprintVerifyDialog.setTipText(errString!!.toString(),Color.RED)
}
override fun onAuthenticationSucceeded(result: AuthenticationResult?) {
super.onAuthenticationSucceeded(result)
fingerprintVerifyDialog.setTipText("驗證成功",Color.GREEN)
fingerprintVerifyDialog.dismiss()
fingerprintCallback. ()
}
override fun onAuthenticationHelp(helpMsgId: Int, helpString: CharSequence?) {
super.onAuthenticationHelp(helpMsgId, helpString)
fingerprintVerifyDialog.setTipText(helpString!!.toString(),Color.YELLOW)
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
fingerprintVerifyDialog.setTipText("驗證失敗",Color.RED)
fingerprintCallback.onFailed()
}
}
/** * @description 判斷當前是否支持指紋識別 */
fun canAuthenticate(context: Context,fingerprintCallback: FingerprintCallback) : Boolean{
/** * @description 是否支持指紋識別 */
if(!from(context).isHardwareDetected){
fingerprintCallback.onHwUnavailable()
return false
}
/** * @description 是否已經添加指紋 */
if(!from(context).hasEnrolledFingerprints()){
fingerprintCallback.onNoneEnrolled()
return false
}
return true
}
}複製代碼
最後定義一個管理類 提供了指紋識別給外部調用 以及機器是否支持指紋識別的字段
class FingerprintManager(private var context: Activity, private var callback: FingerprintCallback) {
//是否支持指紋識別
var isSupportFingerLogin : Boolean = false
private var fingerprintImpl : FingerprintImpl = FingerprintImpl.instance
init {
isSupportFingerLogin = fingerprintImpl.canAuthenticate(context,callback)
}
/**
* @description 開始指紋識別
*/
fun autherticate(){
fingerprintImpl.authenticate(context,callback)
}
}複製代碼
到這裏 能夠說 大功告成啦~~~ 哈哈哈哈哈哈哈哈哈哈哈哈 接下來就是收尾階段了
你覺得這就完了嗎 BUG之路纔剛剛開始~
在登陸界面中取出SP中的字段判斷是否打開了指紋登陸
在這裏發生了內存泄漏 緣由是由於當activity銷燬後dialog還存在 和這個bug搏鬥了一個下午
val isOpenFingerLogin by DelegatesExt.myPreferences(Constans.OPEN_FINGERPRINT_LOGIN, false)
login_btn.setOnClickListener {pwlogin(userEdit.text.toString(),passEdit.text.toString()) }
if(isOpenFingerLogin){
login_btn.postDelayed({ fplogin() },1000)
}複製代碼
在主界面中判斷設備是否支持指紋識別 支持就經過拋出彈窗詢問是否打開
manager = FingerprintManager(this,callback)
if(manager.isSupportFingerLogin&&!isOpenFingerLogin){
fingerSwitch.postDelayed({ showFingerprintLoginDialog() },500)
}else if(!manager.isSupportFingerLogin)
{
Toast.makeText(this,"您的設備不支持指紋識別",Toast.LENGTH_SHORT).show()
}複製代碼
設置一個開關 封裝一下邏輯 指紋識別的開關
fingerSwitch.setOnClickListener {
if(isOpenView){
closeFingerprintLogin()
}else{
showFingerprintLoginDialog()
}
}複製代碼
最後就大功告成啦~~~~~ 哈哈哈哈哈哈哈哈哈哈哈哈哈哈
最後感謝你們的瀏覽 有什麼問題能夠留言交流