29
29
import java .util .concurrent .Executor ;
30
30
31
31
/**
32
- * Authenticates the user with fingerprint and sends corresponding response back to Flutter.
32
+ * Authenticates the user with biometrics and sends corresponding response back to Flutter.
33
33
*
34
34
* <p>One instance per call is generated to ensure readable separation of executable paths across
35
35
* method calls.
36
36
*/
37
37
@ SuppressWarnings ("deprecation" )
38
38
class AuthenticationHelper extends BiometricPrompt .AuthenticationCallback
39
39
implements Application .ActivityLifecycleCallbacks , DefaultLifecycleObserver {
40
-
41
40
/** The callback that handles the result of this authentication process. */
42
41
interface AuthCompletionHandler {
43
-
44
42
/** Called when authentication was successful. */
45
43
void onSuccess ();
46
44
@@ -75,24 +73,32 @@ interface AuthCompletionHandler {
75
73
Lifecycle lifecycle ,
76
74
FragmentActivity activity ,
77
75
MethodCall call ,
78
- AuthCompletionHandler completionHandler ) {
76
+ AuthCompletionHandler completionHandler ,
77
+ boolean allowCredentials ) {
79
78
this .lifecycle = lifecycle ;
80
79
this .activity = activity ;
81
80
this .completionHandler = completionHandler ;
82
81
this .call = call ;
83
82
this .isAuthSticky = call .argument ("stickyAuth" );
84
83
this .uiThreadExecutor = new UiThreadExecutor ();
85
- this .promptInfo =
84
+
85
+ BiometricPrompt .PromptInfo .Builder promptBuilder =
86
86
new BiometricPrompt .PromptInfo .Builder ()
87
87
.setDescription ((String ) call .argument ("localizedReason" ))
88
88
.setTitle ((String ) call .argument ("signInTitle" ))
89
- .setSubtitle ((String ) call .argument ("fingerprintHint" ))
90
- .setNegativeButtonText ((String ) call .argument ("cancelButton" ))
89
+ .setSubtitle ((String ) call .argument ("biometricHint" ))
91
90
.setConfirmationRequired ((Boolean ) call .argument ("sensitiveTransaction" ))
92
- .build ();
91
+ .setConfirmationRequired ((Boolean ) call .argument ("sensitiveTransaction" ));
92
+
93
+ if (allowCredentials ) {
94
+ promptBuilder .setDeviceCredentialAllowed (true );
95
+ } else {
96
+ promptBuilder .setNegativeButtonText ((String ) call .argument ("cancelButton" ));
97
+ }
98
+ this .promptInfo = promptBuilder .build ();
93
99
}
94
100
95
- /** Start the fingerprint listener. */
101
+ /** Start the biometric listener. */
96
102
void authenticate () {
97
103
if (lifecycle != null ) {
98
104
lifecycle .addObserver (this );
@@ -103,15 +109,15 @@ void authenticate() {
103
109
biometricPrompt .authenticate (promptInfo );
104
110
}
105
111
106
- /** Cancels the fingerprint authentication. */
112
+ /** Cancels the biometric authentication. */
107
113
void stopAuthentication () {
108
114
if (biometricPrompt != null ) {
109
115
biometricPrompt .cancelAuthentication ();
110
116
biometricPrompt = null ;
111
117
}
112
118
}
113
119
114
- /** Stops the fingerprint listener. */
120
+ /** Stops the biometric listener. */
115
121
private void stop () {
116
122
if (lifecycle != null ) {
117
123
lifecycle .removeObserver (this );
@@ -125,21 +131,27 @@ private void stop() {
125
131
public void onAuthenticationError (int errorCode , CharSequence errString ) {
126
132
switch (errorCode ) {
127
133
case BiometricPrompt .ERROR_NO_DEVICE_CREDENTIAL :
128
- completionHandler .onError (
129
- "PasscodeNotSet" ,
130
- "Phone not secured by PIN, pattern or password, or SIM is currently locked." );
131
- break ;
134
+ if (call .argument ("useErrorDialogs" )) {
135
+ showGoToSettingsDialog (
136
+ (String ) call .argument ("deviceCredentialsRequired" ),
137
+ (String ) call .argument ("deviceCredentialsSetupDescription" ));
138
+ return ;
139
+ }
140
+ completionHandler .onError ("NotAvailable" , "Security credentials not available." );
132
141
case BiometricPrompt .ERROR_NO_SPACE :
133
142
case BiometricPrompt .ERROR_NO_BIOMETRICS :
143
+ if (promptInfo .isDeviceCredentialAllowed ()) return ;
134
144
if (call .argument ("useErrorDialogs" )) {
135
- showGoToSettingsDialog ();
145
+ showGoToSettingsDialog (
146
+ (String ) call .argument ("biometricRequired" ),
147
+ (String ) call .argument ("goToSettingDescription" ));
136
148
return ;
137
149
}
138
150
completionHandler .onError ("NotEnrolled" , "No Biometrics enrolled on this device." );
139
151
break ;
140
152
case BiometricPrompt .ERROR_HW_UNAVAILABLE :
141
153
case BiometricPrompt .ERROR_HW_NOT_PRESENT :
142
- completionHandler .onError ("NotAvailable" , "Biometrics is not available on this device ." );
154
+ completionHandler .onError ("NotAvailable" , "Security credentials not available." );
143
155
break ;
144
156
case BiometricPrompt .ERROR_LOCKOUT :
145
157
completionHandler .onError (
@@ -176,7 +188,7 @@ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult resul
176
188
public void onAuthenticationFailed () {}
177
189
178
190
/**
179
- * If the activity is paused, we keep track because fingerprint dialog simply returns "User
191
+ * If the activity is paused, we keep track because biometric dialog simply returns "User
180
192
* cancelled" when the activity is paused.
181
193
*/
182
194
@ Override
@@ -215,12 +227,12 @@ public void onResume(@NonNull LifecycleOwner owner) {
215
227
216
228
// Suppress inflateParams lint because dialogs do not need to attach to a parent view.
217
229
@ SuppressLint ("InflateParams" )
218
- private void showGoToSettingsDialog () {
230
+ private void showGoToSettingsDialog (String title , String descriptionText ) {
219
231
View view = LayoutInflater .from (activity ).inflate (R .layout .go_to_setting , null , false );
220
232
TextView message = (TextView ) view .findViewById (R .id .fingerprint_required );
221
233
TextView description = (TextView ) view .findViewById (R .id .go_to_setting_description );
222
- message .setText (( String ) call . argument ( "fingerprintRequired" ) );
223
- description .setText (( String ) call . argument ( "goToSettingDescription" ) );
234
+ message .setText (title );
235
+ description .setText (descriptionText );
224
236
Context context = new ContextThemeWrapper (activity , R .style .AlertDialogCustom );
225
237
OnClickListener goToSettingHandler =
226
238
new OnClickListener () {
0 commit comments