@@ -25,6 +25,10 @@ It provides a **Default View** that prompts the user to place a finger to the iP
25
25
</div >
26
26
27
27
### Android Version
28
+ 4.0.0 Prefers the new native Android BiometricPrompt lib on any Android >= v23 (M)
29
+ 4.0.0 also DEPRECATES support for the legacy library that provides support for Samsung & MeiZu phones
30
+
31
+ 3.0.2 and below:
28
32
Using an expandable Android Fingerprint API library, which combines [ Samsung] ( http://developer.samsung.com/galaxy/pass# ) and [ MeiZu] ( http://open-wiki.flyme.cn/index.php?title=%E6%8C%87%E7%BA%B9%E8%AF%86%E5%88%ABAPI ) 's official Fingerprint API.
29
33
30
34
Samsung and MeiZu's Fingerprint SDK supports most devices which system versions less than Android 6.0.
@@ -76,7 +80,7 @@ $ react-native link react-native-fingerprint-scanner
76
80
```
77
81
3 . Insert the following lines inside the dependencies block in ` android/app/build.gradle ` :
78
82
```
79
- compile project(': react-native-fingerprint-scanner ')
83
+ implementation project(': react-native-fingerprint-scanner ')
80
84
```
81
85
82
86
### App Permissions
@@ -86,12 +90,18 @@ Add the following permissions to their respective files:
86
90
#### Android
87
91
In your ` AndroidManifest.xml ` :
88
92
89
- API level 28+ ([ Reference] ( https://developer.android.com/reference/android/Manifest.permission#USE_BIOMETRIC ) )
93
+ API level 28+ (Uses Android native BiometricPrompt) ( [ Reference] ( https://developer.android.com/reference/android/Manifest.permission#USE_BIOMETRIC ) )
90
94
``` xml
91
95
<uses-permission android : name =" android.permission.USE_BIOMETRIC" />
92
96
```
93
97
94
- API level <28 ([ Reference] ( https://developer.android.com/reference/android/Manifest.permission#USE_FINGERPRINT ) )
98
+ API level 23-28 (Uses Android native FingerprintCompat) [ Reference] ( https://developer.android.com/reference/android/Manifest.permission#USE_FINGERPRINT ) )
99
+ ``` xml
100
+ <uses-permission android : name =" android.permission.USE_FINGERPRINT" />
101
+ ```
102
+
103
+ // DEPRECATED in 4.0.0
104
+ API level <23 (Uses device-specific native fingerprinting, if available - Samsung & MeiZu only) [ Reference] ( https://developer.android.com/reference/android/Manifest.permission#USE_FINGERPRINT ) )
95
105
``` xml
96
106
<uses-permission android : name =" android.permission.USE_FINGERPRINT" />
97
107
```
@@ -107,23 +117,26 @@ In your `Info.plist`:
107
117
108
118
1 . Make sure the following versions are all correct in ` android/app/build.gradle `
109
119
```
120
+ // API v29 enables FaceId
110
121
android {
111
- compileSdkVersion 25
112
- buildToolsVersion "25 .0.3 "
122
+ compileSdkVersion 29
123
+ buildToolsVersion "29 .0.2 "
113
124
...
114
125
defaultConfig {
115
- targetSdkVersion 25
126
+ targetSdkVersion 29
116
127
```
117
128
118
129
2. Add necessary rules to `android/app/proguard-rules.pro` if you are using proguard:
119
130
```
120
131
# MeiZu Fingerprint
121
132
133
+ // DEPRECATED in 4.0.0
122
134
-keep class com.fingerprints.service.** { *; }
123
135
-dontwarn com.fingerprints.service.**
124
136
125
137
# Samsung Fingerprint
126
138
139
+ // DEPRECATED in 4.0.0
127
140
-keep class com.samsung.android.sdk.** { *; }
128
141
-dontwarn com.samsung.android.sdk.**
129
142
```
@@ -133,6 +146,7 @@ In your `Info.plist`:
133
146
* For Gradle < 3 you MUST install react-native-fingerprint-scanner at version <= 2.5.0
134
147
* For RN >= 0.57 and/or Gradle >= 3 you MUST install react-native-fingerprint-scanner at version >= 2.6.0
135
148
* For RN >= 0.60 you MUST install react-native-fingerprint-scanner at version >= 3.0.0
149
+ * For Android native Face Unlock, MUST use >= 4.0.0
136
150
137
151
## Example
138
152
@@ -174,54 +188,84 @@ export default FingerprintPopup;
174
188
175
189
** Android Implementation**
176
190
``` javascript
191
+
177
192
import React , { Component } from ' react' ;
178
193
import PropTypes from ' prop-types' ;
179
-
180
194
import {
181
195
Alert ,
182
196
Image ,
183
197
Text ,
184
198
TouchableOpacity ,
185
199
View ,
186
- ViewPropTypes
200
+ ViewPropTypes ,
201
+ Platform ,
187
202
} from ' react-native' ;
188
- import FingerprintScanner from ' react-native-fingerprint-scanner' ;
189
203
190
- import ShakingText from ' ./ShakingText.component ' ;
204
+ import FingerprintScanner from ' react-native-fingerprint-scanner ' ;
191
205
import styles from ' ./FingerprintPopup.component.styles' ;
206
+ import ShakingText from ' ./ShakingText.component' ;
192
207
193
- class FingerprintPopup extends Component {
194
208
209
+ // - this example component supports both the
210
+ // legacy device-specific (Android < v23) and
211
+ // current (Android >= 23) biometric APIs
212
+ // - your lib and implementation may not need both
213
+ class BiometricPopup extends Component {
195
214
constructor (props ) {
196
215
super (props);
197
- this .state = { errorMessage: undefined };
216
+ this .state = {
217
+ errorMessageLegacy: undefined ,
218
+ biometricLegacy: undefined
219
+ };
220
+
221
+ this .description = null ;
198
222
}
199
223
200
224
componentDidMount () {
225
+ if (requiresLegacyAuthentication ()) {
226
+ authLegacy ();
227
+ } else {
228
+ authCurrent ();
229
+ }
230
+ }
231
+
232
+ componentWillUnmount = () => {
233
+ FingerprintScanner .release ();
234
+ }
235
+
236
+ requiresLegacyAuthentication () {
237
+ return Platform .Version < 23 ;
238
+ }
239
+
240
+ authCurrent () {
201
241
FingerprintScanner
202
- .authenticate ({ onAttempt : this . handleAuthenticationAttempted })
242
+ .authenticate ({ description : ' Log in with Biometrics ' })
203
243
.then (() => {
204
- this .props .handlePopupDismissed ();
244
+ this .props .onAuthenticate ();
245
+ });
246
+ }
247
+
248
+ authLegacy () {
249
+ FingerprintScanner
250
+ .authenticate ({ onAttempt: this .handleAuthenticationAttemptedLegacy })
251
+ .then (() => {
252
+ this .props .handlePopupDismissedLegacy ();
205
253
Alert .alert (' Fingerprint Authentication' , ' Authenticated successfully' );
206
254
})
207
255
.catch ((error ) => {
208
- this .setState ({ errorMessage : error .message });
256
+ this .setState ({ errorMessageLegacy : error .message , biometricLegacy : error . biometric });
209
257
this .description .shake ();
210
258
});
211
259
}
212
260
213
- componentWillUnmount () {
214
- FingerprintScanner .release ();
215
- }
216
-
217
- handleAuthenticationAttempted = (error ) => {
218
- this .setState ({ errorMessage: error .message });
261
+ handleAuthenticationAttemptedLegacy = (error ) => {
262
+ this .setState ({ errorMessageLegacy: error .message });
219
263
this .description .shake ();
220
264
};
221
265
222
- render () {
223
- const { errorMessage } = this .state ;
224
- const { style , handlePopupDismissed } = this .props ;
266
+ renderLegacy () {
267
+ const { errorMessageLegacy , biometricLegacy } = this .state ;
268
+ const { style , handlePopupDismissedLegacy } = this .props ;
225
269
226
270
return (
227
271
< View style= {styles .container }>
@@ -233,17 +277,17 @@ class FingerprintPopup extends Component {
233
277
/ >
234
278
235
279
< Text style= {styles .heading }>
236
- Fingerprint {' \n ' }Authentication
280
+ Biometric {' \n ' }Authentication
237
281
< / Text >
238
282
< ShakingText
239
283
ref= {(instance ) => { this .description = instance; }}
240
- style= {styles .description (!! errorMessage )}>
241
- {errorMessage || ' Scan your fingerprint on the\n device scanner to continue' }
284
+ style= {styles .description (!! errorMessageLegacy )}>
285
+ {errorMessageLegacy || ` Scan your ${ biometricLegacy } on the\n device scanner to continue` }
242
286
< / ShakingText>
243
287
244
288
< TouchableOpacity
245
289
style= {styles .buttonContainer }
246
- onPress= {handlePopupDismissed }
290
+ onPress= {handlePopupDismissedLegacy }
247
291
>
248
292
< Text style= {styles .buttonText }>
249
293
BACK TO MAIN
@@ -254,14 +298,25 @@ class FingerprintPopup extends Component {
254
298
< / View>
255
299
);
256
300
}
301
+
302
+
303
+ render = () => {
304
+ if (this .requiresLegacyAuthentication ()) {
305
+ return this .renderLegacy ();
306
+ }
307
+
308
+ // current API UI provided by native BiometricPrompt
309
+ return null ;
310
+ }
257
311
}
258
312
259
- FingerprintPopup .propTypes = {
313
+ BiometricPopup .propTypes = {
314
+ onAuthenticate: PropTypes .func .isRequired ,
315
+ handlePopupDismissedLegacy: PropTypes .func ,
260
316
style: ViewPropTypes .style ,
261
- handlePopupDismissed: PropTypes .func .isRequired ,
262
317
};
263
318
264
- export default FingerprintPopup ;
319
+ export default BiometricPopup ;
265
320
```
266
321
267
322
## API
@@ -271,6 +326,8 @@ Checks if Fingerprint Scanner is able to be used by now.
271
326
272
327
- Returns a ` Promise<string> `
273
328
- ` biometryType: String ` - The type of biometric authentication supported by the device.
329
+ - iOS: biometryType = 'Touch ID', 'Face ID'
330
+ - Android: biometryType = 'Biometrics'
274
331
- ` error: FingerprintScannerError { name, message, biometric } ` - The name and message of failure and the biometric type in use.
275
332
276
333
``` javascript
@@ -304,29 +361,59 @@ componentDidMount() {
304
361
}
305
362
```
306
363
307
- ### ` authenticate({ onAttempt }) ` : (Android)
364
+ ### ` authenticate({ description="Log In", onAttempt=() => (null) }) ` : (Android)
308
365
Starts Fingerprint authentication on Android.
309
366
310
367
- Returns a ` Promise `
368
+ - ` description: String ` the title text to display in the native Android popup
311
369
- ` onAttempt: Function ` - a callback function when users are trying to scan their fingerprint but failed.
312
370
313
371
``` javascript
314
372
componentDidMount () {
373
+ if (requiresLegacyAuthentication ()) {
374
+ authLegacy ();
375
+ } else {
376
+ authCurrent ();
377
+ }
378
+ }
379
+
380
+ componentWillUnmount = () => {
381
+ FingerprintScanner .release ();
382
+ }
383
+
384
+ requiresLegacyAuthentication () {
385
+ return Platform .Version < 23 ;
386
+ }
387
+
388
+ authCurrent () {
315
389
FingerprintScanner
316
- .authenticate ({ onAttempt : this . handleAuthenticationAttempted })
390
+ .authenticate ({ description : ' Log in with Biometrics ' })
317
391
.then (() => {
318
- this .props .handlePopupDismissed ();
392
+ this .props .onAuthenticate ();
393
+ });
394
+ }
395
+
396
+ authLegacy () {
397
+ FingerprintScanner
398
+ .authenticate ({ onAttempt: this .handleAuthenticationAttemptedLegacy })
399
+ .then (() => {
400
+ this .props .handlePopupDismissedLegacy ();
319
401
Alert .alert (' Fingerprint Authentication' , ' Authenticated successfully' );
320
402
})
321
403
.catch ((error ) => {
322
- this .setState ({ errorMessage : error .message });
404
+ this .setState ({ errorMessageLegacy : error .message , biometricLegacy : error . biometric });
323
405
this .description .shake ();
324
406
});
325
407
}
408
+
409
+ handleAuthenticationAttemptedLegacy = (error ) => {
410
+ this .setState ({ errorMessageLegacy: error .message });
411
+ this .description .shake ();
412
+ };
326
413
```
327
414
328
415
### ` release() ` : (Android)
329
- Stops fingerprint scanner listener, releases cache of internal state in native code.
416
+ Stops fingerprint scanner listener, releases cache of internal state in native code, and cancels native prompt if visible .
330
417
331
418
- Returns a ` Void `
332
419
@@ -338,27 +425,32 @@ componentWillUnmount() {
338
425
339
426
### ` Types of Biometrics `
340
427
341
- | Value | OS |
342
- | ---| ---|
343
- | Touch ID | iOS |
344
- | Face ID | iOS |
345
- | Fingerprint | Android |
428
+ | Value | OS | Description |
429
+ | ---| ---| --- |
430
+ | Touch ID | iOS | |
431
+ | Face ID | iOS | |
432
+ | Biometrics | Android | Refers to the biometric set as preferred on the device |
346
433
347
434
### ` Errors `
348
435
349
436
| Name | Message |
350
437
| ---| ---|
351
438
| AuthenticationNotMatch | No match |
352
439
| AuthenticationFailed | Authentication was not successful because the user failed to provide valid credentials |
440
+ | AuthenticationTimeout | Authentication was not successful because the operation timed out |
441
+ | AuthenticationProcessFailed | 'Sensor was unable to process the image. Please try again |
353
442
| UserCancel | Authentication was canceled by the user - e.g. the user tapped Cancel in the dialog |
354
443
| UserFallback | Authentication was canceled because the user tapped the fallback button (Enter Password) |
355
444
| SystemCancel | Authentication was canceled by system - e.g. if another application came to foreground while the authentication dialog was up |
356
445
| PasscodeNotSet | Authentication could not start because the passcode is not set on the device |
357
- | FingerprintScannerNotAvailable | Authentication could not start because Fingerprint Scanner is not available on the device |
358
- | FingerprintScannerNotEnrolled | Authentication could not start because Fingerprint Scanner has no enrolled fingers |
446
+ | DeviceLocked | Authentication was not successful, the device currently in a lockout of 30 seconds |
447
+ | DeviceLockedPermanent | Authentication was not successful, device must be unlocked via password |
448
+ | DeviceOutOfMemory | Authentication could not proceed because there is not enough free memory on the device |
449
+ | HardwareError | A hardware error occurred |
359
450
| FingerprintScannerUnknownError | Could not authenticate for an unknown reason |
360
451
| FingerprintScannerNotSupported | Device does not support Fingerprint Scanner |
361
- | DeviceLocked | Authentication was not successful, the device currently in a lockout of 30 seconds |
452
+ | FingerprintScannerNotEnrolled | Authentication could not start because Fingerprint Scanner has no enrolled fingers |
453
+ | FingerprintScannerNotAvailable | Authentication could not start because Fingerprint Scanner is not available on the device |
362
454
363
455
## License
364
456
0 commit comments