使用指紋
說明 : 指紋解鎖在23 的時候,官方就已經給出了api ,但是由於Android市場複雜,無法形成統一,硬件由不同的廠商開發,導致相同版本的軟件系統,搭載的硬件千變萬化,導致由的機型不支持指紋識別,但是,這也擋不住指紋識別在接下來的時間中進入Android市場的趨勢,因爲它相比較輸入密碼或圖案,它更加簡單,相比較密碼或者圖案,它更炫酷 ,本文Demo 使用最新的28 支持的androidx 庫中的API及最近火熱的kotlin語言完成的
需要知道的
- FingerprintManager : 指紋管理工具類
- FingerprintManager.AuthenticationCallback :使用驗證的時候傳入該接口,通過該接口進行驗證結果回調
- FingerprintManager.CryptoObject: FingerprintManager 支持的分裝加密對象的類
以上是28以下API 中使用的類 在Android 28版本中google 宣佈使用Androidx 庫代替Android庫,所以在28版本中Android 推薦使用androidx庫中的類 所以在本文中我 使用的是推薦是用的FingerprintManagerCompat 二者的使用的方式基本相似
如何使用指紋
- 開始驗證 ,系統默認的每段時間驗證指紋次數爲5次 次數用完之後自動關閉驗證,並且30秒之內不允行在使用驗證
驗證的方法是authenticate()
/**
*
*@param crypto object associated with the call or null if none required.
* @param flags optional flags; should be 0
* @param cancel an object that can be used to cancel authentication
* @param callback an object to receive authentication events
* @param handler an optional handler for events
**/
@RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
public void authenticate(@Nullable CryptoObject crypto, int flags,
@Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback,
@Nullable Handler handler) {
if (Build.VERSION.SDK_INT >= 23) {
final FingerprintManager fp = getFingerprintManagerOrNull(mContext);
if (fp != null) {
android.os.CancellationSignal cancellationSignal = cancel != null
? (android.os.CancellationSignal) cancel.getCancellationSignalObject()
: null;
fp.authenticate(
wrapCryptoObject(crypto),
cancellationSignal,
flags,
wrapCallback(callback),
handler);
}
}
}
arg1: 用於通過指紋驗證取出AndroidKeyStore中key的值
arg2: 系統建議爲0
arg3: 取消指紋驗證 手動關閉驗證 可以調用該參數的cancel方法
arg4:返回驗證結果
arg5: Handler fingerprint 中的
消息都是通過handler來傳遞的 如果不需要則傳null 會自動默認創建一個主線程的handler來傳遞消息
使用指紋識別的條件
通過零碎的知識完成一個Demo
- 添加權限(這個權限不需要在6.0中做處理)
- 判斷硬件是否支持
- 是否已經設置了鎖屏 並且已經有一個被錄入的指紋
- 判斷是否至少存在一條指紋信息
指紋識別通過之後跳轉到 指定頁面
進入之後首先彈出對話框,進行指紋驗證
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/fingerprint" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="驗證指紋" />
<TextView
android:id="@+id/fingerprint_error_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:maxLines="1" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="10dp"
android:layout_marginRight="5dp"
android:background="#696969" />
<TextView
android:id="@+id/fingerprint_cancel_tv"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_gravity="center"
android:gravity="center"
android:text="取消"
android:textSize="16sp" />
</LinearLayout>
使用DialogFragment 完成對話框 新建一個DialogFragment 並且初始化相關的api
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//獲取fingerprintManagerCompat對象
fingerprintManagerCompat = FingerprintManagerCompat.from(context!!)
setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog)
}
在界面顯示在前臺的時候開始掃描
override fun onResume() {
super.onResume()
startListening()
}
@SuppressLint("MissingPermission")
private fun startListening() {
isSelfCancelled = false
mCancellationSignal = CancellationSignal()
fingerprintManagerCompat.authenticate(FingerprintManagerCompat.CryptoObject(mCipher), 0, mCancellationSignal, object : FingerprintManagerCompat.AuthenticationCallback() {
//驗證錯誤
override fun onAuthenticationError(errMsgId: Int, errString: CharSequence?) {
if (!isSelfCancelled) {
errorMsg.text = errString
if (errMsgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) {
Toast.makeText(mActivity, errString, Toast.LENGTH_SHORT).show()
dismiss()
mActivity.finish()
}
}
}
//成功
override fun onAuthenticationSucceeded(result: FingerprintManagerCompat.AuthenticationResult?) {
MainActivity.startActivity(mActivity, true)
}
//錯誤時提示幫助,比如說指紋錯誤,我們將顯示在界面上 讓用戶知道情況
override fun onAuthenticationHelp(helpMsgId: Int, helpString: CharSequence?) {
errorMsg.text = helpString
}
//驗證失敗
override fun onAuthenticationFailed() {
errorMsg.text = "指紋驗證失敗,請重試"
}
}, null)
}
在不可見的時候停止驗證
if (null != mCancellationSignal) {
mCancellationSignal.cancel()
isSelfCancelled = true
}
在MainActivity 中首先判斷是否驗證成功 是 跳轉到目標頁 否則的話需要進行驗證
在這個過程中我們需要做的就是判斷是否支持,判斷是否滿足指紋驗證的條件(條件在上面)
if (intent.getBooleanExtra("isSuccess", false)) {
WelcomeActivity.startActivity(this)
finish()
} else {
//判斷是否支持該功能
if (supportFingerprint()) {
initKey() //生成一個對稱加密的key
initCipher() //生成一個Cipher對象
}
}
驗證條件
if (Build.VERSION.SDK_INT < 23) {
Toast.makeText(this, "系統不支持指紋功能", Toast.LENGTH_SHORT).show()
return false
} else {
val keyguardManager = getSystemService(KeyguardManager::class.java)
val managerCompat = FingerprintManagerCompat.from(this)
if (!managerCompat.isHardwareDetected) {
Toast.makeText(this, "系統不支持指紋功能", Toast.LENGTH_SHORT).show()
return false
} else if (!keyguardManager.isKeyguardSecure) {
Toast.makeText(this, "屏幕未設置鎖屏 請先設置鎖屏並添加一個指紋", Toast.LENGTH_SHORT).show()
return false
} else if (!managerCompat.hasEnrolledFingerprints()) {
Toast.makeText(this, "至少在系統中添加一個指紋", Toast.LENGTH_SHORT).show()
return false
}
}
必須生成一個加密的key 和一個Cipher對象
//生成Cipher
private fun initCipher() {
val key = keyStore.getKey(DEFAULT_KEY_NAME, null) as SecretKey
val cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
cipher.init(Cipher.ENCRYPT_MODE, key)
showFingerPrintDialog(cipher)
}
//生成一個key
private fun initKey() {
keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val builder = KeyGenParameterSpec.Builder(DEFAULT_KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
keyGenerator.init(builder.build())
keyGenerator.generateKey()
}
驗證成功就可