taomenggo init

This commit is contained in:
guozhen
2024-08-06 10:30:15 +08:00
committed by xuhuixiang
parent 3e7fd07f4f
commit c929efd05e
3007 changed files with 229844 additions and 77 deletions

View File

@@ -0,0 +1,27 @@
package com.codersun.fingerprintcompat;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.codersun.fingerprintcompat.test", appContext.getPackageName());
}
}

View File

@@ -0,0 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codersun.fingerprintcompat">
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
</manifest>

View File

@@ -0,0 +1,119 @@
package com.codersun.fingerprintcompat;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
/**
* @author codersun
* @time 2019/9/3 15:14
*/
public abstract class AFingerDialog extends DialogFragment
{
private IonDismissListener mDismissListener;
public AFingerDialog()
{
super();
setCancelable(false);
}
/**
* 监听返回键
*
* @param savedInstanceState
* @return
*/
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setOnKeyListener(new DialogInterface.OnKeyListener()
{
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_HOME || keyCode == KeyEvent.KEYCODE_BACK)
{
dismiss();
cancelFingerAuth();
return true;
}
else
{
return false;
}
}
});
return dialog;
}
@Override
public void onDismiss(DialogInterface dialog)
{
super.onDismiss(dialog);
if (mDismissListener != null)
mDismissListener.onDismiss();
}
public void setOnDismissListener(IonDismissListener dismissListener)
{
this.mDismissListener = dismissListener;
}
@Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
dismissAllowingStateLoss();
}
protected void cancelFingerAuth()
{
}
public interface IonDismissListener
{
void onDismiss();
}
public abstract void onSucceed();
/**
* 当识别的手指没有注册时回调,但是可以继续验证
*
* @author codersun
* @time 2019/10/16 10:36
*/
public abstract void onFailed();
/**
* 指纹识别不对,会提示,手指不要大范围移动等信息,可以继续验证
*
* @author codersun
* @time 2019/10/16 10:37
*/
public abstract void onHelp(String help);
/**
* 指纹识别彻底失败,不能继续验证
*
* @author codersun
* @time 2019/10/16 10:37
*/
public abstract void onError(String error);
public abstract void onCancelAuth();
}

View File

@@ -0,0 +1,23 @@
package com.codersun.fingerprintcompat;
import android.content.Context;
import android.os.Build;
import androidx.annotation.RequiresApi;
/**
* @author codersun
* @time 2019/9/8 16:36
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public abstract class AonFingerChangeCallback
{
void onChange(Context context)
{
SharePreferenceUtil.saveData(context, SharePreferenceUtil.KEY_IS_FINGER_CHANGE, "1");
onFingerDataChange();
}
protected abstract void onFingerDataChange();
}

View File

@@ -0,0 +1,138 @@
package com.codersun.fingerprintcompat;
import android.app.Activity;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.CancellationSignal;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import javax.crypto.Cipher;
/**
* @author codersun
* @time 2019/10/15 20:48
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public class BiometricPromptImpl23 implements IBiometricPromptImpl
{
private Cipher mCipher;
private Activity mActivity;
private boolean mSelfCanceled;
private AFingerDialog mFingerDialog;
private IonFingerCallback mCallback;
private AonFingerChangeCallback mFingerChangeCallback;
private FingerManagerController mFingerManagerController;
BiometricPromptImpl23(Activity activity, AFingerDialog fingerDialog,
FingerManagerController fingerManagerController)
{
this.mActivity = activity;
mFingerManagerController = fingerManagerController;
mCipher = CipherHelper.getInstance().createCipher();
mFingerChangeCallback = fingerManagerController.getFingerChangeCallback();
this.mFingerDialog = fingerDialog == null ? DefaultFingerDialog.newInstance(fingerManagerController) : fingerDialog;
}
@Override
public void authenticate(@NonNull final CancellationSignal cancel)
{
this.mCallback = mFingerManagerController.getFingerCheckCallback();
mSelfCanceled = false;
if (CipherHelper.getInstance().initCipher(mCipher) || SharePreferenceUtil.isFingerDataChange(mActivity))
{
mFingerChangeCallback.onChange(mActivity);
return;
}
mFingerDialog.setOnDismissListener(new AFingerDialog.IonDismissListener()
{
@Override
public void onDismiss()
{
mSelfCanceled = !cancel.isCanceled();
if (mSelfCanceled)
{
cancel.cancel();
//如果使用的是默认弹窗,就使用cancel回调,否则交给开发者自行处理
if (mFingerDialog.getClass() == DefaultFingerDialog.class)
{
mCallback.onCancel();
}
}
}
});
if (!mFingerDialog.isAdded())
mFingerDialog.show(mActivity.getFragmentManager(), mFingerDialog.getClass().getSimpleName());
FingerprintManager fingerprintManager = (FingerprintManager) mActivity.getSystemService(FingerprintManager.class);
fingerprintManager.authenticate(new FingerprintManager.CryptoObject(mCipher), cancel, 0,
new FingerprintManager.AuthenticationCallback()
{
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString)
{
super.onAuthenticationError(errMsgId, errString);
if (!mSelfCanceled)
{
//当指纹识别结算手动调用cancel,用于后面判断是手动取消还是自动取消识别的
cancel.cancel();
mFingerDialog.onError(errString.toString());
mCallback.onError(errString.toString());
}
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString)
{
super.onAuthenticationHelp(helpMsgId, helpString);
mFingerDialog.onHelp(helpString.toString());
mCallback.onHelp(helpString.toString());
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)
{
super.onAuthenticationSucceeded(result);
Cipher cipher = result.getCryptoObject().getCipher();
if (cipher != null)
{
try
{
byte[] bytes = cipher.doFinal();
////当指纹识别结算手动调用cancel,用于后面判断是手动取消还是自动取消识别的
cancel.cancel();
mFingerDialog.onSucceed();
mCallback.onSucceed();
}
catch (Exception e)
{
e.printStackTrace();
mFingerChangeCallback.onChange(mActivity);
}
}
}
@Override
public void onAuthenticationFailed()
{
super.onAuthenticationFailed();
mFingerDialog.onFailed();
mCallback.onFailed();
}
}, null);
}
}

View File

@@ -0,0 +1,100 @@
package com.codersun.fingerprintcompat;
import android.app.Activity;
import android.content.DialogInterface;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Build;
import android.os.CancellationSignal;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import javax.crypto.Cipher;
/**
* @author codersun
* @time 2019/10/15 20:48
*/
@RequiresApi(Build.VERSION_CODES.P)
public class BiometricPromptImpl28 implements IBiometricPromptImpl {
private Activity mActivity;
private BiometricPrompt mBiometricPrompt;
private CancellationSignal mCancellationSignal;
private Cipher cipher;
private IonFingerCallback mCallback;
private boolean userCancel;
private AonFingerChangeCallback mFingerChangeCallback;
@RequiresApi(Build.VERSION_CODES.P)
BiometricPromptImpl28(Activity activity,
FingerManagerController fingerManagerController) {
this.mActivity = activity;
mCallback = fingerManagerController.getFingerCheckCallback();
mFingerChangeCallback = fingerManagerController.getFingerChangeCallback();
mBiometricPrompt = new BiometricPrompt
.Builder(activity)
.setTitle(fingerManagerController.getTitle())
.setDescription(fingerManagerController.getDes())
.setNegativeButton(fingerManagerController.getNegativeText(),
activity.getMainExecutor(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mCallback.onCancel();
userCancel = true;
mCancellationSignal.cancel();
}
})
.build();
cipher = CipherHelper.getInstance().createCipher();
}
@RequiresApi(Build.VERSION_CODES.P)
@Override
public void authenticate(@Nullable CancellationSignal cancel) {
userCancel = false;
mCancellationSignal = cancel;
if (CipherHelper.getInstance().initCipher(cipher) || SharePreferenceUtil.isFingerDataChange(mActivity)) {
mFingerChangeCallback.onChange(mActivity);
return;
}
mBiometricPrompt.authenticate(new BiometricPrompt.CryptoObject(cipher),
mCancellationSignal, mActivity.getMainExecutor(), new BiometricPromptCallbackImpl());
}
@RequiresApi(Build.VERSION_CODES.P)
private class BiometricPromptCallbackImpl extends BiometricPrompt.AuthenticationCallback {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
mCancellationSignal.cancel();
if (!userCancel) {
mCallback.onError(errString.toString());
}
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
super.onAuthenticationHelp(helpCode, helpString);
}
@Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
mCancellationSignal.cancel();
mCallback.onSucceed();
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
mCallback.onFailed();
}
}
}

View File

@@ -0,0 +1,150 @@
package com.codersun.fingerprintcompat;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.RequiresApi;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
/**
* @author codersun
* @time 2019/10/15 20:48
*/
@RequiresApi(api = Build.VERSION_CODES.M)
class CipherHelper {
private static CipherHelper instance;
private static final String DEFAULT_KEY_NAME = "defaultKey";
private static final String KEYSTORE_ALIAS = "keyStoreAlias";
private static final String HAS_FINGER_KEY = "hasFingerKey";
private KeyGenerator mKeyGenerator;
private KeyStore keyStore;
private CipherHelper() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (KeyStoreException e) {
e.printStackTrace();
}
try {
mKeyGenerator = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new RuntimeException("Failed to get an instance of KeyGenerator", e);
}
}
static CipherHelper getInstance() {
if (instance == null) {
synchronized (CipherHelper.class) {
if (instance == null) {
instance = new CipherHelper();
}
}
}
return instance;
}
/**
* @des 创建cipher
* @author codersun
* @date 2019/7/3 14:24
*/
Cipher createCipher() {
try {
return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* @des 初始化Cipher ,并判断指纹库是否发生了变化
* @author codersun
* @date 2019/7/3 14:24
*/
boolean initCipher(Cipher cipher) {
try {
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEYSTORE_ALIAS, null);
if (cipher == null) {
cipher = createCipher();
}
cipher.init(Cipher.ENCRYPT_MODE, key);
return false;
} catch (KeyPermanentlyInvalidatedException e) {
return true;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}
}
/**
* @param context
* @param createNewKey 是否创建新的密钥
* @des 根据当前指纹库创建一个密钥
* @author codersun
* @date 2019/7/3 10:35
*/
void createKey(Context context, boolean createNewKey) {
if (context == null) {
throw new RuntimeException("context can not be null");
}
SharedPreferences sharedPreferences = context.getSharedPreferences(DEFAULT_KEY_NAME, Context.MODE_PRIVATE);
try {
//首先通过标志位判断用户之前是否创建了密钥,如果已经创建过了并且不需要重新创建就跳过
if (TextUtils.isEmpty(sharedPreferences.getString(HAS_FINGER_KEY, "")) || createNewKey) {
//创建新密钥
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(KEYSTORE_ALIAS,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.setInvalidatedByBiometricEnrollment(true);
}
mKeyGenerator.init(builder.build());
mKeyGenerator.generateKey();
sharedPreferences.edit().putString(HAS_FINGER_KEY, "KEY").apply();
}
} catch (InvalidAlgorithmParameterException e) {
// throw new RuntimeException(e);
Log.d("cipherHelper", "message:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,136 @@
package com.codersun.fingerprintcompat;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
/**
* @author codersun
* @time 2019/9/8 16:35
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public class DefaultFingerDialog extends AFingerDialog implements View.OnClickListener
{
private static final String TITLE = "title";
private static final String DES = "des";
private static final String NEGATIVE_TEXT = "negativeText";
private FingerManagerController mFingerManagerController;
private ObjectAnimator animator;
private TextView titleTv;
private TextView desTv;
public static DefaultFingerDialog newInstance(FingerManagerController fingerManagerController)
{
DefaultFingerDialog defaultFingerDialog = new DefaultFingerDialog();
Bundle bundle = new Bundle();
bundle.putString(TITLE, fingerManagerController.getTitle());
bundle.putString(DES, fingerManagerController.getDes());
bundle.putString(NEGATIVE_TEXT, fingerManagerController.getNegativeText());
defaultFingerDialog.setArguments(bundle);
return defaultFingerDialog;
}
@SuppressLint("ValidFragment")
private DefaultFingerDialog()
{
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState)
{
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.dialog_finger, null);
titleTv = view.findViewById(R.id.finger_dialog_title_tv);
desTv = view.findViewById(R.id.finger_dialog_des_tv);
TextView cancelTv = view.findViewById(R.id.finger_dialog_cancel_tv);
cancelTv.setOnClickListener(this);
if (getArguments() != null)
{
titleTv.setText(getArguments().getString(TITLE));
desTv.setText(getArguments().getString(DES));
cancelTv.setText(getArguments().getString(NEGATIVE_TEXT));
}
animator = ObjectAnimator.ofFloat(desTv, View.TRANSLATION_X, 20, -20);
animator.setRepeatCount(1);
animator.setDuration(500);
return view;
}
@Override
public void onSucceed()
{
dismiss();
}
@Override
public void onFailed()
{
titleTv.setText("请重试");
desTv.setText("换个手指试试");
desTv.setVisibility(View.VISIBLE);
if (!animator.isRunning())
{
animator.start();
}
}
@Override
public void onHelp(String help)
{
titleTv.setVisibility(View.VISIBLE);
desTv.setVisibility(View.VISIBLE);
titleTv.setText("请重试");
desTv.setText("换个手指试试");
if (!TextUtils.isEmpty(help))
{
if (!desTv.getText().toString().trim().equals(help.trim()))
{
if (!animator.isRunning())
animator.start();
}
}
}
@Override
public void onError(String error)
{
dismissAllowingStateLoss();
}
@Override
public void onCancelAuth()
{
}
@Override
public void onClick(View v)
{
dismiss();
}
}

View File

@@ -0,0 +1,125 @@
package com.codersun.fingerprintcompat;
import android.app.Activity;
import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.CancellationSignal;
import androidx.annotation.RequiresApi;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
@RequiresApi(api = Build.VERSION_CODES.M)
public class FingerManager {
private static FingerManager fingerManager;
private static FingerManagerController mFingerManagerController;
private CancellationSignal cancellationSignal;
private IBiometricPromptImpl biometricPrompt;
public enum SupportResult {
DEVICE_UNSUPPORTED,//设备不支持指纹识别
SUPPORT_WITHOUT_DATA,//设备支持指纹识别但是没有指纹数据
SUPPORT//设备支持且有指纹数据
}
private static FingerManager getInstance() {
if (fingerManager == null) {
synchronized (FingerManager.class) {
if (fingerManager == null) {
fingerManager = new FingerManager();
}
}
}
return fingerManager;
}
static FingerManager getInstance(FingerManagerController fingerManagerController) {
mFingerManagerController = fingerManagerController;
return getInstance();
}
private FingerManager() {
}
private void createImp(Activity activity, AFingerDialog fingerDialog) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
biometricPrompt = new BiometricPromptImpl28(activity, mFingerManagerController);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
biometricPrompt = new BiometricPromptImpl23(activity, fingerDialog, mFingerManagerController);
}
}
/**
* 检查设别是否支持指纹识别
*
* @return
*/
public static SupportResult checkSupport(Context context) {
// FingerprintManagerCompat fingerprintManager = FingerprintManagerCompat.from(context);
FingerprintManager fingerprintManager = context.getSystemService(FingerprintManager.class);
if (fingerprintManager == null) {
return SupportResult.DEVICE_UNSUPPORTED;
}
if (fingerprintManager.isHardwareDetected()) {
if (fingerprintManager.hasEnrolledFingerprints()) {
return SupportResult.SUPPORT;
} else {
return SupportResult.SUPPORT_WITHOUT_DATA;
}
} else {
return SupportResult.DEVICE_UNSUPPORTED;
}
}
/**
* 检查设备是否有指纹数据
*
* @return
*/
public static boolean hasFingerprintData(Context context) {
FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(context);
return fingerprintManagerCompat.hasEnrolledFingerprints();
}
/**
* 开始监听指纹识别器
*/
@RequiresApi(api = Build.VERSION_CODES.M)
private void startListener() {
CipherHelper.getInstance().createKey(mFingerManagerController.getApplication(), false);
if (cancellationSignal == null) {
cancellationSignal = new CancellationSignal();
}
if (cancellationSignal.isCanceled()) {
cancellationSignal = new CancellationSignal();
}
biometricPrompt.authenticate(cancellationSignal);
}
public void startListener(Activity activity) {
createImp(activity, mFingerManagerController.getFingerDialogApi23());
startListener();
}
/**
* 同步指纹数据,解除 指纹数据变化 问题
*
* @param context
*/
public static void updateFingerData(Context context) {
SharePreferenceUtil.saveData(context, SharePreferenceUtil.KEY_IS_FINGER_CHANGE, "0");
CipherHelper.getInstance().createKey(context, true);
}
public static FingerManagerController build() {
return new FingerManagerController();
}
}

View File

@@ -0,0 +1,124 @@
package com.codersun.fingerprintcompat;
import android.app.Application;
import android.os.Build;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
/**
* @author codersun
* @time 2019/9/3 17:41
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public class FingerManagerController
{
private Application mApplication;
//弹窗标题
private String mTitle;
//弹窗描述
private String mDes;
//取消按钮话术
private String mNegativeText;
//Android P 以下版本的指纹识别弹窗(如需自定义样式就设置)
private AFingerDialog mFingerDialogApi23;
//指纹识别回调
private IonFingerCallback mFingerCheckCallback;
//指纹库发生变化时的回调
private AonFingerChangeCallback mFingerChangeCallback;
public AonFingerChangeCallback getFingerChangeCallback()
{
return mFingerChangeCallback;
}
public FingerManagerController setFingerChangeCallback(AonFingerChangeCallback fingerChangeCallback)
{
this.mFingerChangeCallback = fingerChangeCallback;
return this;
}
public IonFingerCallback getFingerCheckCallback()
{
return mFingerCheckCallback;
}
public FingerManagerController setFingerCheckCallback(IonFingerCallback fingerCheckCallback)
{
this.mFingerCheckCallback = fingerCheckCallback;
return this;
}
public FingerManagerController setDes(String des)
{
this.mDes = des;
return this;
}
public FingerManagerController setNegativeText(String negativeText)
{
this.mNegativeText = negativeText;
return this;
}
public FingerManagerController setTitle(String title)
{
mTitle = title;
return this;
}
public FingerManagerController setApplication(Application application)
{
mApplication = application;
return this;
}
public FingerManagerController setFingerDialogApi23(@Nullable AFingerDialog fingerDialogApi23)
{
this.mFingerDialogApi23 = fingerDialogApi23;
return this;
}
public Application getApplication()
{
return mApplication;
}
public String getTitle()
{
return mTitle;
}
public String getDes()
{
return mDes;
}
public String getNegativeText()
{
return mNegativeText;
}
public AFingerDialog getFingerDialogApi23()
{
return mFingerDialogApi23;
}
public FingerManager create()
{
if (mFingerCheckCallback == null){
throw new RuntimeException("CompatFingerManager : FingerCheckCallback can not be null");
}
return FingerManager.getInstance(this);
}
}

View File

@@ -0,0 +1,9 @@
package com.codersun.fingerprintcompat;
import android.os.CancellationSignal;
import androidx.annotation.NonNull;
public interface IBiometricPromptImpl {
void authenticate(@NonNull CancellationSignal cancel);
}

View File

@@ -0,0 +1,15 @@
package com.codersun.fingerprintcompat;
public interface IonFingerCallback
{
void onSucceed();
void onFailed();
void onHelp(String help);
void onError(String error);
void onCancel();
}

View File

@@ -0,0 +1,54 @@
package com.codersun.fingerprintcompat;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import static android.text.TextUtils.isEmpty;
class SharePreferenceUtil
{
private static final String DEFAULT_NAME = "finger";
public static String KEY_IS_FINGER_CHANGE = "is_finger_change";//指纹是否变化了
private static SharedPreferences.Editor getSharePreferenceEditor(Context context, String fileName) {
return getSharedPreferences(context,fileName).edit();
}
private static SharedPreferences getSharedPreferences(Context context, String fileName) {
return context.getSharedPreferences(isEmpty(fileName) ? DEFAULT_NAME : fileName, Context.MODE_PRIVATE);
}
public static void saveData(Context context, String key, String value) {
saveData(context, key, value, null);
}
public static void saveData(Context context, String key, String value, String fileName) {
SharedPreferences.Editor editor = getSharePreferenceEditor(context, fileName);
editor.putString(key, value);
editor.commit();
}
public static String getValue(Context context,String key) {
if (null == key)
return "";
SharedPreferences sharedPreferences = getSharedPreferences(context, "");
return sharedPreferences.getString(key, "");
}
public static boolean isFingerDataChange(Context context){
String value = getValue(context, KEY_IS_FINGER_CHANGE);
boolean result = false;
if (TextUtils.isEmpty(value)){
result = false;
}else {
result = Integer.parseInt(value) == 1;
}
return result;
}
}

View File

@@ -0,0 +1,21 @@
package com.codersun.fingerprintcompat;
/**
* @author codersun
* @time 2019/9/8 16:36
*/
public abstract class SimpleFingerCheckCallback implements IonFingerCallback
{
@Override
public void onHelp(String help)
{
}
@Override
public void onFailed()
{
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<corners android:radius="5dip"/>
<solid android:color="#aaaaaa"/>
</shape>
</item>
<item>
<shape>
<corners android:radius="5dip"/>
<solid android:color="#909090"/>
</shape>
</item>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners
android:topLeftRadius="50dp"
android:topRightRadius="50dp"/>
<solid android:color="#00ffffff"/>
</shape>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="205dp"
android:layout_height="wrap_content"
android:background="@drawable/top_round_white_bg"
android:orientation="vertical">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:src="@drawable/ic_fingerprint"/>
<TextView
android:id="@+id/finger_dialog_title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/icon"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold"/>
<RelativeLayout
android:id="@+id/finger_dialog_bottom_layout_rl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/finger_dialog_title_tv">
<TextView
android:id="@+id/finger_dialog_des_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:textColor="#000"
android:textSize="15sp"/>
<RelativeLayout
android:id="@+id/finger_dialog_button_layout_rl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/finger_dialog_des_tv"
android:layout_marginTop="30dp">
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@drawable/gray_round_bg"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/finger_dialog_cancel_tv"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="取消"/>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Fingerprint Compat</string>
</resources>

View File

@@ -0,0 +1,17 @@
package com.codersun.fingerprintcompat;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}