55
66import android .annotation .SuppressLint ;
77import android .app .Activity ;
8- import android .app .AlertDialog ;
98import android .app .Application ;
10- import android .content .Context ;
11- import android .content .DialogInterface .OnClickListener ;
12- import android .content .Intent ;
139import android .os .Bundle ;
1410import android .os .Handler ;
1511import android .os .Looper ;
16- import android .provider .Settings ;
17- import android .view .ContextThemeWrapper ;
18- import android .view .LayoutInflater ;
19- import android .view .View ;
20- import android .widget .TextView ;
2112import androidx .annotation .NonNull ;
2213import androidx .biometric .BiometricManager ;
2314import androidx .biometric .BiometricPrompt ;
2415import androidx .fragment .app .FragmentActivity ;
2516import androidx .lifecycle .DefaultLifecycleObserver ;
2617import androidx .lifecycle .Lifecycle ;
2718import androidx .lifecycle .LifecycleOwner ;
19+ import io .flutter .plugins .localauth .Messages .AuthResult ;
20+ import io .flutter .plugins .localauth .Messages .AuthResultCode ;
2821import java .util .concurrent .Executor ;
2922
3023/**
@@ -38,14 +31,12 @@ class AuthenticationHelper extends BiometricPrompt.AuthenticationCallback
3831 /** The callback that handles the result of this authentication process. */
3932 interface AuthCompletionHandler {
4033 /** Called when authentication attempt is complete. */
41- void complete (Messages . AuthResult authResult );
34+ void complete (AuthResult authResult );
4235 }
4336
44- // This is null when not using v2 embedding;
4537 private final Lifecycle lifecycle ;
4638 private final FragmentActivity activity ;
4739 private final AuthCompletionHandler completionHandler ;
48- private final boolean useErrorDialogs ;
4940 private final Messages .AuthStrings strings ;
5041 private final BiometricPrompt .PromptInfo promptInfo ;
5142 private final boolean isAuthSticky ;
@@ -65,14 +56,13 @@ interface AuthCompletionHandler {
6556 this .completionHandler = completionHandler ;
6657 this .strings = strings ;
6758 this .isAuthSticky = options .getSticky ();
68- this .useErrorDialogs = options .getUseErrorDialgs ();
6959 this .uiThreadExecutor = new UiThreadExecutor ();
7060
7161 BiometricPrompt .PromptInfo .Builder promptBuilder =
7262 new BiometricPrompt .PromptInfo .Builder ()
7363 .setDescription (strings .getReason ())
7464 .setTitle (strings .getSignInTitle ())
75- .setSubtitle (strings .getBiometricHint ())
65+ .setSubtitle (strings .getSignInHint ())
7666 .setConfirmationRequired (options .getSensitiveTransaction ());
7767
7868 int allowedAuthenticators =
@@ -120,58 +110,69 @@ private void stop() {
120110 @ SuppressLint ("SwitchIntDef" )
121111 @ Override
122112 public void onAuthenticationError (int errorCode , @ NonNull CharSequence errString ) {
113+ AuthResultCode code ;
123114 switch (errorCode ) {
115+ case BiometricPrompt .ERROR_USER_CANCELED :
116+ code = AuthResultCode .USER_CANCELED ;
117+ break ;
118+ case BiometricPrompt .ERROR_NEGATIVE_BUTTON :
119+ code = AuthResultCode .NEGATIVE_BUTTON ;
120+ break ;
124121 case BiometricPrompt .ERROR_NO_DEVICE_CREDENTIAL :
125- if (useErrorDialogs ) {
126- showGoToSettingsDialog (
127- strings .getDeviceCredentialsRequiredTitle (),
128- strings .getDeviceCredentialsSetupDescription ());
129- return ;
130- }
131- completionHandler .complete (Messages .AuthResult .ERROR_NOT_AVAILABLE );
122+ code = AuthResultCode .NO_CREDENTIALS ;
132123 break ;
133- case BiometricPrompt .ERROR_NO_SPACE :
134124 case BiometricPrompt .ERROR_NO_BIOMETRICS :
135- if (useErrorDialogs ) {
136- showGoToSettingsDialog (
137- strings .getBiometricRequiredTitle (), strings .getGoToSettingsDescription ());
138- return ;
139- }
140- completionHandler .complete (Messages .AuthResult .ERROR_NOT_ENROLLED );
125+ code = AuthResultCode .NOT_ENROLLED ;
141126 break ;
142127 case BiometricPrompt .ERROR_HW_UNAVAILABLE :
128+ code = AuthResultCode .HARDWARE_UNAVAILABLE ;
129+ break ;
143130 case BiometricPrompt .ERROR_HW_NOT_PRESENT :
144- completionHandler . complete ( Messages . AuthResult . ERROR_NOT_AVAILABLE ) ;
131+ code = AuthResultCode . NO_HARDWARE ;
145132 break ;
146133 case BiometricPrompt .ERROR_LOCKOUT :
147- completionHandler . complete ( Messages . AuthResult . ERROR_LOCKED_OUT_TEMPORARILY ) ;
134+ code = AuthResultCode . LOCKED_OUT_TEMPORARILY ;
148135 break ;
149136 case BiometricPrompt .ERROR_LOCKOUT_PERMANENT :
150- completionHandler . complete ( Messages . AuthResult . ERROR_LOCKED_OUT_PERMANENTLY ) ;
137+ code = AuthResultCode . LOCKED_OUT_PERMANENTLY ;
151138 break ;
152139 case BiometricPrompt .ERROR_CANCELED :
153140 // If we are doing sticky auth and the activity has been paused,
154141 // ignore this error. We will start listening again when resumed.
155142 if (activityPaused && isAuthSticky ) {
156143 return ;
157- } else {
158- completionHandler .complete (Messages .AuthResult .FAILURE );
159144 }
145+ code = AuthResultCode .SYSTEM_CANCELED ;
146+ break ;
147+ case BiometricPrompt .ERROR_TIMEOUT :
148+ code = AuthResultCode .TIMEOUT ;
149+ break ;
150+ case BiometricPrompt .ERROR_NO_SPACE :
151+ code = AuthResultCode .NO_SPACE ;
152+ break ;
153+ case BiometricPrompt .ERROR_SECURITY_UPDATE_REQUIRED :
154+ code = AuthResultCode .SECURITY_UPDATE_REQUIRED ;
160155 break ;
161156 default :
162- completionHandler .complete (Messages .AuthResult .FAILURE );
157+ code = AuthResultCode .UNKNOWN_ERROR ;
158+ break ;
163159 }
160+ completionHandler .complete (
161+ new AuthResult .Builder ().setCode (code ).setErrorMessage (errString .toString ()).build ());
164162 stop ();
165163 }
166164
167165 @ Override
168166 public void onAuthenticationSucceeded (@ NonNull BiometricPrompt .AuthenticationResult result ) {
169- completionHandler .complete (Messages . AuthResult .SUCCESS );
167+ completionHandler .complete (new AuthResult .Builder (). setCode ( AuthResultCode . SUCCESS ). build () );
170168 stop ();
171169 }
172170
173171 @ Override
174- public void onAuthenticationFailed () {}
172+ public void onAuthenticationFailed () {
173+ // No-op; this is called for incremental failures. Wait for a final
174+ // resolution via the success or error callbacks.
175+ }
175176
176177 /**
177178 * If the activity is paused, we keep track because biometric dialog simply returns "User
@@ -205,34 +206,6 @@ public void onResume(@NonNull LifecycleOwner owner) {
205206 onActivityResumed (null );
206207 }
207208
208- // Suppress inflateParams lint because dialogs do not need to attach to a parent view.
209- @ SuppressLint ("InflateParams" )
210- private void showGoToSettingsDialog (String title , String descriptionText ) {
211- View view = LayoutInflater .from (activity ).inflate (R .layout .go_to_setting , null , false );
212- TextView message = view .findViewById (R .id .fingerprint_required );
213- TextView description = view .findViewById (R .id .go_to_setting_description );
214- message .setText (title );
215- description .setText (descriptionText );
216- Context context = new ContextThemeWrapper (activity , R .style .AlertDialogCustom );
217- OnClickListener goToSettingHandler =
218- (dialog , which ) -> {
219- completionHandler .complete (Messages .AuthResult .FAILURE );
220- stop ();
221- activity .startActivity (new Intent (Settings .ACTION_SECURITY_SETTINGS ));
222- };
223- OnClickListener cancelHandler =
224- (dialog , which ) -> {
225- completionHandler .complete (Messages .AuthResult .FAILURE );
226- stop ();
227- };
228- new AlertDialog .Builder (context )
229- .setView (view )
230- .setPositiveButton (strings .getGoToSettingsButton (), goToSettingHandler )
231- .setNegativeButton (strings .getCancelButton (), cancelHandler )
232- .setCancelable (false )
233- .show ();
234- }
235-
236209 // Unused methods for activity lifecycle.
237210
238211 @ Override
0 commit comments