├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values-zh
│ │ │ │ └── strings.xml
│ │ │ ├── values-zh-rCN
│ │ │ │ └── strings.xml
│ │ │ ├── values-zh-rTW
│ │ │ │ └── strings.xml
│ │ │ ├── values-en
│ │ │ │ └── strings.xml
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── wei
│ │ │ └── android
│ │ │ └── lib
│ │ │ └── fingerprintidentify
│ │ │ └── demo
│ │ │ └── MainActivity.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── wei
│ │ │ └── android
│ │ │ └── lib
│ │ │ └── fingerprintidentify
│ │ │ └── demo
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── wei
│ │ └── android
│ │ └── lib
│ │ └── fingerprintidentify
│ │ └── demo
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── FingerprintIdentifyLib
├── .gitignore
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── wei
│ │ │ └── android
│ │ │ └── lib
│ │ │ └── fingerprintidentify
│ │ │ ├── impl
│ │ │ ├── MeiZuFingerprint.java
│ │ │ ├── AndroidFingerprint.java
│ │ │ └── SamsungFingerprint.java
│ │ │ ├── FingerprintIdentify.java
│ │ │ ├── aosp
│ │ │ ├── FingerprintManagerCompatApi23.java
│ │ │ └── FingerprintManagerCompat.java
│ │ │ └── base
│ │ │ └── BaseFingerprint.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── wei
│ │ │ └── android
│ │ │ └── lib
│ │ │ └── fingerprintidentify
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── wei
│ │ └── android
│ │ └── lib
│ │ └── fingerprintidentify
│ │ └── ExampleInstrumentedTest.java
├── libs
│ ├── MeiZu_Fingerprint.jar
│ ├── Samsung_Fingerprint_Sdk-v1.0.0.jar
│ └── Samsung_Fingerprint_Pass-v1.2.6.jar
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── other
├── demo.apk
├── QRCode_en.png
├── QRCode_zh.png
└── README_ZH.md
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── LICENSE
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/FingerprintIdentifyLib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':FingerprintIdentifyLib'
2 |
--------------------------------------------------------------------------------
/other/demo.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uccmawei/FingerprintIdentify/HEAD/other/demo.apk
--------------------------------------------------------------------------------
/other/QRCode_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uccmawei/FingerprintIdentify/HEAD/other/QRCode_en.png
--------------------------------------------------------------------------------
/other/QRCode_zh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uccmawei/FingerprintIdentify/HEAD/other/QRCode_zh.png
--------------------------------------------------------------------------------
/FingerprintIdentifyLib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | *
20 | * The above copyright notice and this permission notice shall be included in all 21 | * copies or substantial portions of the Software. 22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | * SOFTWARE. 30 | *
31 | * Created by Awei on 2017/2/9. 32 | */ 33 | public class MeiZuFingerprint extends BaseFingerprint { 34 | 35 | private FingerprintManager mMeiZuFingerprintManager; 36 | 37 | public MeiZuFingerprint(Context context, ExceptionListener exceptionListener) { 38 | super(context, exceptionListener); 39 | 40 | try { 41 | mMeiZuFingerprintManager = FingerprintManager.open(); 42 | if (mMeiZuFingerprintManager != null) { 43 | setHardwareEnable(isMeiZuDevice(Build.MANUFACTURER)); 44 | int[] fingerprintIds = mMeiZuFingerprintManager.getIds(); 45 | setRegisteredFingerprint(fingerprintIds != null && fingerprintIds.length > 0); 46 | } 47 | } catch (Throwable e) { 48 | onCatchException(e); 49 | } 50 | 51 | releaseMBack(); 52 | } 53 | 54 | @Override 55 | protected void doIdentify() { 56 | try { 57 | mMeiZuFingerprintManager = FingerprintManager.open(); 58 | mMeiZuFingerprintManager.startIdentify(new FingerprintManager.IdentifyCallback() { 59 | @Override 60 | public void onIdentified(int i, boolean b) { 61 | onSucceed(); 62 | } 63 | 64 | @Override 65 | public void onNoMatch() { 66 | onNotMatch(); 67 | } 68 | }, mMeiZuFingerprintManager.getIds()); 69 | } catch (Throwable e) { 70 | onCatchException(e); 71 | onFailed(false); 72 | } 73 | } 74 | 75 | @Override 76 | protected void doCancelIdentify() { 77 | releaseMBack(); 78 | } 79 | 80 | private void releaseMBack() { 81 | try { 82 | if (mMeiZuFingerprintManager != null) { 83 | mMeiZuFingerprintManager.release(); 84 | } 85 | } catch (Throwable e) { 86 | onCatchException(e); 87 | } 88 | } 89 | 90 | private boolean isMeiZuDevice(String manufacturer) { 91 | return !TextUtils.isEmpty(manufacturer) && manufacturer.toUpperCase().contains("MEIZU"); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/com/wei/android/lib/fingerprintidentify/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.wei.android.lib.fingerprintidentify.demo; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.ScrollView; 8 | import android.widget.TextView; 9 | import android.widget.Toast; 10 | 11 | import com.wei.android.lib.fingerprintidentify.FingerprintIdentify; 12 | import com.wei.android.lib.fingerprintidentify.base.BaseFingerprint; 13 | 14 | import androidx.appcompat.app.AppCompatActivity; 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | 18 | private TextView mTvTips; 19 | private ScrollView mScrollView; 20 | private FingerprintIdentify mFingerprintIdentify; 21 | 22 | private static final int MAX_AVAILABLE_TIMES = 3; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_main); 28 | mTvTips = findViewById(R.id.mTvTips); 29 | mScrollView = findViewById(R.id.mScrollView); 30 | 31 | long time = System.currentTimeMillis(); 32 | append("new FingerprintIdentify().init() "); 33 | mFingerprintIdentify = new FingerprintIdentify(getApplicationContext()); 34 | mFingerprintIdentify.setSupportAndroidL(true); 35 | mFingerprintIdentify.setExceptionListener(new BaseFingerprint.ExceptionListener() { 36 | @Override 37 | public void onCatchException(Throwable exception) { 38 | append("\nException:" + exception.getLocalizedMessage()); 39 | } 40 | }); 41 | mFingerprintIdentify.init(); 42 | 43 | append("\n" + getString(R.string.time) + (System.currentTimeMillis() - time) + "ms"); 44 | append("\nisHardwareEnable() " + mFingerprintIdentify.isHardwareEnable()); 45 | append("\nisRegisteredFingerprint() " + mFingerprintIdentify.isRegisteredFingerprint()); 46 | append("\nisFingerprintEnable() " + mFingerprintIdentify.isFingerprintEnable()); 47 | 48 | if (!mFingerprintIdentify.isFingerprintEnable()) { 49 | append("\n" + getString(R.string.not_support)); 50 | return; 51 | } 52 | 53 | append("\n" + getString(R.string.click_to_start)); 54 | } 55 | 56 | private void append(String msg) { 57 | mTvTips.append(msg); 58 | mScrollView.post(new Runnable() { 59 | @Override 60 | public void run() { 61 | mScrollView.fullScroll(View.FOCUS_DOWN); 62 | } 63 | }); 64 | } 65 | 66 | public void start(View view) { 67 | append("\n" + getString(R.string.start)); 68 | mFingerprintIdentify.startIdentify(MAX_AVAILABLE_TIMES, new BaseFingerprint.IdentifyListener() { 69 | @Override 70 | public void onSucceed() { 71 | append("\n" + getString(R.string.succeed)); 72 | } 73 | 74 | @Override 75 | public void onNotMatch(int availableTimes) { 76 | append("\n" + getString(R.string.not_match, availableTimes)); 77 | } 78 | 79 | @Override 80 | public void onFailed(boolean isDeviceLocked) { 81 | append("\n" + getString(R.string.failed) + " " + isDeviceLocked); 82 | } 83 | 84 | @Override 85 | public void onStartFailedByDeviceLocked() { 86 | append("\n" + getString(R.string.start_failed)); 87 | } 88 | }); 89 | } 90 | 91 | public void copy(View view) { 92 | String text = mTvTips.getText().toString(); 93 | ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); 94 | clipboardManager.setPrimaryClip(ClipData.newPlainText(text, text)); 95 | Toast.makeText(this, getString(R.string.copied), Toast.LENGTH_SHORT).show(); 96 | } 97 | 98 | @Override 99 | protected void onPause() { 100 | super.onPause(); 101 | mFingerprintIdentify.cancelIdentify(); 102 | append("\n" + getString(R.string.stopped_by_life_callback)); 103 | } 104 | 105 | @Override 106 | protected void onDestroy() { 107 | super.onDestroy(); 108 | mFingerprintIdentify.cancelIdentify(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /FingerprintIdentifyLib/src/main/java/com/wei/android/lib/fingerprintidentify/impl/AndroidFingerprint.java: -------------------------------------------------------------------------------- 1 | package com.wei.android.lib.fingerprintidentify.impl; 2 | 3 | import android.content.Context; 4 | import android.hardware.fingerprint.FingerprintManager; 5 | import android.os.Build; 6 | 7 | import com.wei.android.lib.fingerprintidentify.aosp.FingerprintManagerCompat; 8 | import com.wei.android.lib.fingerprintidentify.base.BaseFingerprint; 9 | 10 | import androidx.core.os.CancellationSignal; 11 | 12 | /** 13 | * Copyright (c) 2017 Awei 14 | *
15 | * Permission is hereby granted, free of charge, to any person obtaining a copy 16 | * of this software and associated documentation files (the "Software"), to deal 17 | * in the Software without restriction, including without limitation the rights 18 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | * copies of the Software, and to permit persons to whom the Software is 20 | * furnished to do so, subject to the following conditions: 21 | *
22 | * The above copyright notice and this permission notice shall be included in all 23 | * copies or substantial portions of the Software. 24 | *
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | * SOFTWARE. 32 | *
33 | * Created by Awei on 2017/2/9. 34 | */ 35 | public class AndroidFingerprint extends BaseFingerprint { 36 | 37 | private CancellationSignal mCancellationSignal; 38 | private FingerprintManagerCompat mFingerprintManagerCompat; 39 | 40 | public AndroidFingerprint(Context context, ExceptionListener exceptionListener, boolean iSupportAndroidL) { 41 | super(context, exceptionListener); 42 | 43 | if (!iSupportAndroidL) { 44 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 45 | return; 46 | } 47 | } 48 | 49 | try { 50 | mFingerprintManagerCompat = FingerprintManagerCompat.from(mContext); 51 | setHardwareEnable(mFingerprintManagerCompat.isHardwareDetected()); 52 | setRegisteredFingerprint(mFingerprintManagerCompat.hasEnrolledFingerprints()); 53 | } catch (Throwable e) { 54 | onCatchException(e); 55 | } 56 | } 57 | 58 | @Override 59 | protected void doIdentify() { 60 | try { 61 | mCancellationSignal = new CancellationSignal(); 62 | mFingerprintManagerCompat.authenticate(null, 0, mCancellationSignal, new FingerprintManagerCompat.AuthenticationCallback() { 63 | @Override 64 | public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) { 65 | super.onAuthenticationSucceeded(result); 66 | onSucceed(); 67 | } 68 | 69 | @Override 70 | public void onAuthenticationFailed() { 71 | super.onAuthenticationFailed(); 72 | onNotMatch(); 73 | } 74 | 75 | @Override 76 | public void onAuthenticationError(int errMsgId, CharSequence errString) { 77 | super.onAuthenticationError(errMsgId, errString); 78 | 79 | if (errMsgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED || 80 | errMsgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED) { 81 | return; 82 | } 83 | 84 | onFailed(errMsgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT || 85 | errMsgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT); 86 | } 87 | }, null); 88 | } catch (Throwable e) { 89 | onCatchException(e); 90 | onFailed(false); 91 | } 92 | } 93 | 94 | @Override 95 | protected void doCancelIdentify() { 96 | try { 97 | if (mCancellationSignal != null) { 98 | mCancellationSignal.cancel(); 99 | } 100 | } catch (Throwable e) { 101 | onCatchException(e); 102 | } 103 | } 104 | 105 | @Override 106 | protected boolean needToCallDoIdentifyAgainAfterNotMatch() { 107 | return false; 108 | } 109 | } -------------------------------------------------------------------------------- /FingerprintIdentifyLib/src/main/java/com/wei/android/lib/fingerprintidentify/FingerprintIdentify.java: -------------------------------------------------------------------------------- 1 | package com.wei.android.lib.fingerprintidentify; 2 | 3 | import android.content.Context; 4 | 5 | import com.wei.android.lib.fingerprintidentify.base.BaseFingerprint; 6 | import com.wei.android.lib.fingerprintidentify.impl.AndroidFingerprint; 7 | import com.wei.android.lib.fingerprintidentify.impl.MeiZuFingerprint; 8 | import com.wei.android.lib.fingerprintidentify.impl.SamsungFingerprint; 9 | 10 | /** 11 | * Copyright (c) 2017 Awei 12 | *
13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | *
20 | * The above copyright notice and this permission notice shall be included in all 21 | * copies or substantial portions of the Software. 22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | * SOFTWARE. 30 | *
31 | * Created by Awei on 2017/2/8.
32 | */
33 | public class FingerprintIdentify {
34 |
35 | protected Context mContext;
36 | protected BaseFingerprint.ExceptionListener mExceptionListener;
37 |
38 | protected boolean mIsSupportAndroidL = false;
39 |
40 | protected BaseFingerprint mFingerprint;
41 | protected BaseFingerprint mSubFingerprint;
42 |
43 | public FingerprintIdentify(Context context) {
44 | mContext = context;
45 | }
46 |
47 | public void setSupportAndroidL(boolean supportAndroidL) {
48 | mIsSupportAndroidL = supportAndroidL;
49 | }
50 |
51 | public void setExceptionListener(BaseFingerprint.ExceptionListener exceptionListener) {
52 | mExceptionListener = exceptionListener;
53 | }
54 |
55 | public void init() {
56 | AndroidFingerprint androidFingerprint = new AndroidFingerprint(mContext, mExceptionListener, mIsSupportAndroidL);
57 | if (androidFingerprint.isHardwareEnable()) {
58 | mSubFingerprint = androidFingerprint;
59 | if (androidFingerprint.isRegisteredFingerprint()) {
60 | mFingerprint = androidFingerprint;
61 | return;
62 | }
63 | }
64 |
65 | SamsungFingerprint samsungFingerprint = new SamsungFingerprint(mContext, mExceptionListener);
66 | if (samsungFingerprint.isHardwareEnable()) {
67 | mSubFingerprint = samsungFingerprint;
68 | if (samsungFingerprint.isRegisteredFingerprint()) {
69 | mFingerprint = samsungFingerprint;
70 | return;
71 | }
72 | }
73 |
74 | MeiZuFingerprint meiZuFingerprint = new MeiZuFingerprint(mContext, mExceptionListener);
75 | if (meiZuFingerprint.isHardwareEnable()) {
76 | mSubFingerprint = meiZuFingerprint;
77 | if (meiZuFingerprint.isRegisteredFingerprint()) {
78 | mFingerprint = meiZuFingerprint;
79 | }
80 | }
81 | }
82 |
83 | // DO
84 | public void startIdentify(int maxAvailableTimes, BaseFingerprint.IdentifyListener listener) {
85 | if (!isFingerprintEnable()) {
86 | return;
87 | }
88 |
89 | mFingerprint.startIdentify(maxAvailableTimes, listener);
90 | }
91 |
92 | public void cancelIdentify() {
93 | if (mFingerprint != null) {
94 | mFingerprint.cancelIdentify();
95 | }
96 | }
97 |
98 | public void resumeIdentify() {
99 | if (!isFingerprintEnable()) {
100 | return;
101 | }
102 |
103 | mFingerprint.resumeIdentify();
104 | }
105 |
106 | // GET & SET
107 | public boolean isFingerprintEnable() {
108 | return mFingerprint != null && mFingerprint.isEnable();
109 | }
110 |
111 | public boolean isHardwareEnable() {
112 | return isFingerprintEnable() || (mSubFingerprint != null && mSubFingerprint.isHardwareEnable());
113 | }
114 |
115 | public boolean isRegisteredFingerprint() {
116 | return isFingerprintEnable() || (mSubFingerprint != null && mSubFingerprint.isRegisteredFingerprint());
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/other/README_ZH.md:
--------------------------------------------------------------------------------
1 | # FingerprintIdentify
2 |
3 | 这是一个可拓展的Android指纹识别API兼容库,目前集成了以下API:
4 |
5 | 安卓API:最低支持安卓**6.0**系统 [(查看详细介绍)](https://developer.android.com/reference/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.html)
6 |
7 | 三星SDK:最低支持安卓**4.2**系统 [(查看详细介绍)](http://developer.samsung.com/galaxy/pass#)
8 |
9 | 魅族SDK:最低支持安卓**5.1**系统 [(查看详细介绍)](http://open-wiki.flyme.cn/index.php?title=%E6%8C%87%E7%BA%B9%E8%AF%86%E5%88%ABAPI)
10 |
11 | API调用优先级:安卓API > 三星SDK > 魅族SDK
12 |
13 | [](https://github.com/uccmawei/FingerprintIdentify/raw/master/other/demo.apk)
14 |
15 | **1. Gradle**
16 |
17 | allprojects {
18 | repositories {
19 | ...
20 | maven { url 'https://jitpack.io' }
21 | }
22 | }
23 | dependencies {
24 | implementation 'com.github.uccmawei:FingerprintIdentify:1.2.6'
25 | }
26 |
27 | **2. AndroidManifest 添加权限**
28 |
29 |
13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | *
20 | * The above copyright notice and this permission notice shall be included in all 21 | * copies or substantial portions of the Software. 22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | * SOFTWARE. 30 | *
31 | * Created by Awei on 2017/2/9.
32 | */
33 | public class SamsungFingerprint extends BaseFingerprint {
34 |
35 | private int mResultCode = -1;
36 | private SpassFingerprint mSpassFingerprint;
37 |
38 | public SamsungFingerprint(Context context, ExceptionListener exceptionListener) {
39 | super(context, exceptionListener);
40 |
41 | try {
42 | Spass spass = new Spass();
43 | spass.initialize(mContext);
44 | mSpassFingerprint = new SpassFingerprint(mContext);
45 | setHardwareEnable(spass.isFeatureEnabled(Spass.DEVICE_FINGERPRINT));
46 | setRegisteredFingerprint(mSpassFingerprint.hasRegisteredFinger());
47 | } catch (Throwable e) {
48 | onCatchException(e);
49 | }
50 | }
51 |
52 | @Override
53 | protected void doIdentify() {
54 | runOnUiThread(new Runnable() {
55 | @Override
56 | public void run() {
57 | try {
58 | mSpassFingerprint.startIdentify(new SpassFingerprint.IdentifyListener() {
59 | @Override
60 | public void onFinished(int i) {
61 | mResultCode = i;
62 | }
63 |
64 | @Override
65 | public void onReady() {
66 |
67 | }
68 |
69 | @Override
70 | public void onStarted() {
71 |
72 | }
73 |
74 | @Override
75 | public void onCompleted() {
76 | switch (mResultCode) {
77 | case SpassFingerprint.STATUS_AUTHENTIFICATION_SUCCESS:
78 | case SpassFingerprint.STATUS_AUTHENTIFICATION_PASSWORD_SUCCESS:
79 | onSucceed();
80 | break;
81 |
82 | case SpassFingerprint.STATUS_SENSOR_FAILED:
83 | case SpassFingerprint.STATUS_OPERATION_DENIED:
84 | case SpassFingerprint.STATUS_TIMEOUT_FAILED:
85 | case SpassFingerprint.STATUS_BUTTON_PRESSED:
86 | case SpassFingerprint.STATUS_QUALITY_FAILED:
87 | case SpassFingerprint.STATUS_USER_CANCELLED_BY_TOUCH_OUTSIDE:
88 | case SpassFingerprint.STATUS_AUTHENTIFICATION_FAILED:
89 | onNotMatch();
90 | break;
91 |
92 | case SpassFingerprint.STATUS_USER_CANCELLED:
93 | // do nothing
94 | break;
95 |
96 | default:
97 | onFailed(false);
98 | break;
99 | }
100 | }
101 | });
102 | } catch (Throwable e) {
103 | if (e instanceof SpassInvalidStateException) {
104 | SpassInvalidStateException stateException = (SpassInvalidStateException) e;
105 | if (stateException.getType() == 1) {
106 | onFailed(true);
107 | } else {
108 | onCatchException(e);
109 | onFailed(false);
110 | }
111 | } else {
112 | onCatchException(e);
113 | onFailed(false);
114 | }
115 | }
116 | }
117 | });
118 | }
119 |
120 | @Override
121 | protected void doCancelIdentify() {
122 | runOnUiThread(new Runnable() {
123 | @Override
124 | public void run() {
125 | try {
126 | if (mSpassFingerprint != null) {
127 | mSpassFingerprint.cancelIdentify();
128 | }
129 | } catch (Throwable e) {
130 | onCatchException(e);
131 | }
132 | }
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | *
17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | *
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | *
28 | * Created by Awei on 2017/2/9. 29 | */ 30 | public abstract class BaseFingerprint { 31 | 32 | protected Context mContext; 33 | 34 | private Handler mHandler; 35 | private IdentifyListener mIdentifyListener; 36 | private ExceptionListener mExceptionListener; 37 | 38 | private int mNumberOfFailures = 0; // number of failures 39 | private int mMaxAvailableTimes = 3; // the most available times 40 | 41 | private boolean mIsHardwareEnable = false; // if the phone equipped fingerprint hardware 42 | private boolean mIsRegisteredFingerprint = false; // if the phone has any fingerprints 43 | 44 | private boolean mIsCalledStartIdentify = false; // if started identify 45 | private boolean mIsCanceledIdentify = false; // if canceled identify 46 | 47 | public BaseFingerprint(Context context, ExceptionListener exceptionListener) { 48 | mContext = context; 49 | mExceptionListener = exceptionListener; 50 | mHandler = new Handler(Looper.getMainLooper()); 51 | } 52 | 53 | // DO 54 | public void startIdentify(int maxAvailableTimes, IdentifyListener identifyListener) { 55 | mMaxAvailableTimes = maxAvailableTimes; 56 | mIdentifyListener = identifyListener; 57 | mIsCalledStartIdentify = true; 58 | mIsCanceledIdentify = false; 59 | mNumberOfFailures = 0; 60 | 61 | doIdentify(); 62 | } 63 | 64 | public void resumeIdentify() { 65 | if (mIsCalledStartIdentify && mIdentifyListener != null && mNumberOfFailures < mMaxAvailableTimes) { 66 | mIsCanceledIdentify = false; 67 | doIdentify(); 68 | } 69 | } 70 | 71 | public void cancelIdentify() { 72 | mIsCanceledIdentify = true; 73 | doCancelIdentify(); 74 | } 75 | 76 | // IMPL 77 | protected abstract void doIdentify(); 78 | 79 | protected abstract void doCancelIdentify(); 80 | 81 | // CALLBACK 82 | protected void onSucceed() { 83 | if (mIsCanceledIdentify) { 84 | return; 85 | } 86 | 87 | mNumberOfFailures = mMaxAvailableTimes; 88 | 89 | if (mIdentifyListener != null) { 90 | runOnUiThread(new Runnable() { 91 | @Override 92 | public void run() { 93 | mIdentifyListener.onSucceed(); 94 | } 95 | }); 96 | } 97 | 98 | cancelIdentify(); 99 | } 100 | 101 | protected void onNotMatch() { 102 | if (mIsCanceledIdentify) { 103 | return; 104 | } 105 | 106 | if (++mNumberOfFailures < mMaxAvailableTimes) { 107 | if (mIdentifyListener != null) { 108 | final int chancesLeft = mMaxAvailableTimes - mNumberOfFailures; 109 | runOnUiThread(new Runnable() { 110 | @Override 111 | public void run() { 112 | mIdentifyListener.onNotMatch(chancesLeft); 113 | } 114 | }); 115 | } 116 | 117 | if (needToCallDoIdentifyAgainAfterNotMatch()) { 118 | doIdentify(); 119 | } 120 | 121 | return; 122 | } 123 | 124 | onFailed(false); 125 | } 126 | 127 | protected void onFailed(final boolean isDeviceLocked) { 128 | if (mIsCanceledIdentify) { 129 | return; 130 | } 131 | 132 | final boolean isStartFailedByDeviceLocked = isDeviceLocked && mNumberOfFailures == 0; 133 | 134 | mNumberOfFailures = mMaxAvailableTimes; 135 | 136 | if (mIdentifyListener != null) { 137 | runOnUiThread(new Runnable() { 138 | @Override 139 | public void run() { 140 | if (isStartFailedByDeviceLocked) { 141 | mIdentifyListener.onStartFailedByDeviceLocked(); 142 | } else { 143 | mIdentifyListener.onFailed(isDeviceLocked); 144 | } 145 | } 146 | }); 147 | } 148 | 149 | cancelIdentify(); 150 | } 151 | 152 | protected void onCatchException(Throwable exception) { 153 | if (mExceptionListener != null && exception != null) { 154 | mExceptionListener.onCatchException(exception); 155 | } 156 | } 157 | 158 | // GET & SET 159 | public boolean isEnable() { 160 | return mIsHardwareEnable && mIsRegisteredFingerprint; 161 | } 162 | 163 | public boolean isHardwareEnable() { 164 | return mIsHardwareEnable; 165 | } 166 | 167 | protected void setHardwareEnable(boolean hardwareEnable) { 168 | mIsHardwareEnable = hardwareEnable; 169 | } 170 | 171 | public boolean isRegisteredFingerprint() { 172 | return mIsRegisteredFingerprint; 173 | } 174 | 175 | protected void setRegisteredFingerprint(boolean registeredFingerprint) { 176 | mIsRegisteredFingerprint = registeredFingerprint; 177 | } 178 | 179 | // OTHER 180 | protected void runOnUiThread(Runnable runnable) { 181 | mHandler.post(runnable); 182 | } 183 | 184 | protected boolean needToCallDoIdentifyAgainAfterNotMatch() { 185 | return true; 186 | } 187 | 188 | public interface IdentifyListener { 189 | void onSucceed(); 190 | 191 | void onNotMatch(int availableTimes); 192 | 193 | void onFailed(boolean isDeviceLocked); 194 | 195 | void onStartFailedByDeviceLocked(); 196 | } 197 | 198 | public interface ExceptionListener { 199 | void onCatchException(Throwable exception); 200 | } 201 | } -------------------------------------------------------------------------------- /FingerprintIdentifyLib/src/main/java/com/wei/android/lib/fingerprintidentify/aosp/FingerprintManagerCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | 17 | package com.wei.android.lib.fingerprintidentify.aosp; 18 | 19 | import android.content.Context; 20 | import android.os.Handler; 21 | 22 | import java.security.Signature; 23 | 24 | import javax.crypto.Cipher; 25 | import javax.crypto.Mac; 26 | 27 | import androidx.annotation.NonNull; 28 | import androidx.annotation.Nullable; 29 | import androidx.core.os.CancellationSignal; 30 | 31 | public final class FingerprintManagerCompat { 32 | 33 | private Context mContext; 34 | 35 | public static FingerprintManagerCompat from(Context context) { 36 | return new FingerprintManagerCompat(context); 37 | } 38 | 39 | private FingerprintManagerCompat(Context context) { 40 | mContext = context; 41 | } 42 | 43 | private static final FingerprintManagerCompatImpl IMPL; 44 | 45 | static { 46 | IMPL = new Api23FingerprintManagerCompatImpl(); 47 | } 48 | 49 | public boolean hasEnrolledFingerprints() { 50 | return IMPL.hasEnrolledFingerprints(mContext); 51 | } 52 | 53 | public boolean isHardwareDetected() { 54 | return IMPL.isHardwareDetected(mContext); 55 | } 56 | 57 | public void authenticate(@Nullable CryptoObject crypto, int flags, 58 | @Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback, 59 | @Nullable Handler handler) { 60 | IMPL.authenticate(mContext, crypto, flags, cancel, callback, handler); 61 | } 62 | 63 | public static class CryptoObject { 64 | 65 | private final Signature mSignature; 66 | private final Cipher mCipher; 67 | private final Mac mMac; 68 | 69 | public CryptoObject(Signature signature) { 70 | mSignature = signature; 71 | mCipher = null; 72 | mMac = null; 73 | 74 | } 75 | 76 | public CryptoObject(Cipher cipher) { 77 | mCipher = cipher; 78 | mSignature = null; 79 | mMac = null; 80 | } 81 | 82 | public CryptoObject(Mac mac) { 83 | mMac = mac; 84 | mCipher = null; 85 | mSignature = null; 86 | } 87 | 88 | public Signature getSignature() { 89 | return mSignature; 90 | } 91 | 92 | public Cipher getCipher() { 93 | return mCipher; 94 | } 95 | 96 | public Mac getMac() { 97 | return mMac; 98 | } 99 | } 100 | 101 | public static final class AuthenticationResult { 102 | private CryptoObject mCryptoObject; 103 | 104 | public AuthenticationResult(CryptoObject crypto) { 105 | mCryptoObject = crypto; 106 | } 107 | 108 | public CryptoObject getCryptoObject() { 109 | return mCryptoObject; 110 | } 111 | } 112 | 113 | public static abstract class AuthenticationCallback { 114 | 115 | public void onAuthenticationError(int errMsgId, CharSequence errString) { 116 | } 117 | 118 | public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { 119 | } 120 | 121 | public void onAuthenticationSucceeded(AuthenticationResult result) { 122 | } 123 | 124 | public void onAuthenticationFailed() { 125 | } 126 | } 127 | 128 | private interface FingerprintManagerCompatImpl { 129 | boolean hasEnrolledFingerprints(Context context); 130 | 131 | boolean isHardwareDetected(Context context); 132 | 133 | void authenticate(Context context, CryptoObject crypto, int flags, CancellationSignal cancel, AuthenticationCallback callback, Handler handler); 134 | } 135 | 136 | private static class Api23FingerprintManagerCompatImpl implements FingerprintManagerCompatImpl { 137 | 138 | public Api23FingerprintManagerCompatImpl() { 139 | } 140 | 141 | @Override 142 | public boolean hasEnrolledFingerprints(Context context) { 143 | return FingerprintManagerCompatApi23.hasEnrolledFingerprints(context); 144 | } 145 | 146 | @Override 147 | public boolean isHardwareDetected(Context context) { 148 | return FingerprintManagerCompatApi23.isHardwareDetected(context); 149 | } 150 | 151 | @Override 152 | public void authenticate(Context context, CryptoObject crypto, int flags, CancellationSignal cancel, 153 | AuthenticationCallback callback, Handler handler) { 154 | FingerprintManagerCompatApi23.authenticate(context, wrapCryptoObject(crypto), flags, 155 | cancel != null ? cancel.getCancellationSignalObject() : null, wrapCallback(callback), handler); 156 | } 157 | 158 | private static FingerprintManagerCompatApi23.CryptoObject wrapCryptoObject(CryptoObject cryptoObject) { 159 | if (cryptoObject == null) { 160 | return null; 161 | } else if (cryptoObject.getCipher() != null) { 162 | return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getCipher()); 163 | } else if (cryptoObject.getSignature() != null) { 164 | return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getSignature()); 165 | } else if (cryptoObject.getMac() != null) { 166 | return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getMac()); 167 | } else { 168 | return null; 169 | } 170 | } 171 | 172 | static CryptoObject unwrapCryptoObject(FingerprintManagerCompatApi23.CryptoObject cryptoObject) { 173 | if (cryptoObject == null) { 174 | return null; 175 | } else if (cryptoObject.getCipher() != null) { 176 | return new CryptoObject(cryptoObject.getCipher()); 177 | } else if (cryptoObject.getSignature() != null) { 178 | return new CryptoObject(cryptoObject.getSignature()); 179 | } else if (cryptoObject.getMac() != null) { 180 | return new CryptoObject(cryptoObject.getMac()); 181 | } else { 182 | return null; 183 | } 184 | } 185 | 186 | private static FingerprintManagerCompatApi23.AuthenticationCallback wrapCallback(final AuthenticationCallback callback) { 187 | return new FingerprintManagerCompatApi23.AuthenticationCallback() { 188 | @Override 189 | public void onAuthenticationError(int errMsgId, CharSequence errString) { 190 | callback.onAuthenticationError(errMsgId, errString); 191 | } 192 | 193 | @Override 194 | public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { 195 | callback.onAuthenticationHelp(helpMsgId, helpString); 196 | } 197 | 198 | @Override 199 | public void onAuthenticationSucceeded(FingerprintManagerCompatApi23.AuthenticationResultInternal result) { 200 | callback.onAuthenticationSucceeded(new AuthenticationResult(unwrapCryptoObject(result.getCryptoObject()))); 201 | } 202 | 203 | @Override 204 | public void onAuthenticationFailed() { 205 | callback.onAuthenticationFailed(); 206 | } 207 | }; 208 | } 209 | } 210 | } 211 | --------------------------------------------------------------------------------