diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md index b69e06040307..0c54edc9aa7a 100644 --- a/packages/local_auth/CHANGELOG.md +++ b/packages/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.1 + +* Added ability to stop authentication (For Android). + ## 0.6.0+3 * Remove AndroidX warnings. diff --git a/packages/local_auth/README.md b/packages/local_auth/README.md index 9cb56e619b24..ca2aa49bed23 100644 --- a/packages/local_auth/README.md +++ b/packages/local_auth/README.md @@ -91,6 +91,16 @@ await localAuth.authenticateWithBiometrics( ``` +If needed, you can manually stop authentication for android: + +```dart + +void _cancelAuthentication() { + localAuth.stopAuthentication(); +} + +``` + ### Exceptions There are 6 types of exceptions: PasscodeNotSet, NotEnrolled, NotAvailable, OtherOperatingSystem, LockedOut and PermanentlyLockedOut. diff --git a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java index a4ef01bfa504..99dab6de2975 100644 --- a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java +++ b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java @@ -63,6 +63,7 @@ interface AuthCompletionHandler { private final boolean isAuthSticky; private final UiThreadExecutor uiThreadExecutor; private boolean activityPaused = false; + private BiometricPrompt biometricPrompt; public AuthenticationHelper( FragmentActivity activity, MethodCall call, AuthCompletionHandler completionHandler) { @@ -84,7 +85,16 @@ public AuthenticationHelper( /** Start the fingerprint listener. */ public void authenticate() { activity.getApplication().registerActivityLifecycleCallbacks(this); - new BiometricPrompt(activity, uiThreadExecutor, this).authenticate(promptInfo); + biometricPrompt = new BiometricPrompt(activity, uiThreadExecutor, this); + biometricPrompt.authenticate(promptInfo); + } + + /** Cancels the fingerprint authentication. */ + public void stopAuthentication() { + if (biometricPrompt != null) { + biometricPrompt.cancelAuthentication(); + biometricPrompt = null; + } } /** Stops the fingerprint listener. */ diff --git a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java index ae69942c8229..6a8bd9e20e53 100644 --- a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java +++ b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java @@ -22,6 +22,7 @@ public class LocalAuthPlugin implements MethodCallHandler { private final Registrar registrar; private final AtomicBoolean authInProgress = new AtomicBoolean(false); + private AuthenticationHelper authenticationHelper; /** Plugin registration. */ public static void registerWith(Registrar registrar) { @@ -37,7 +38,7 @@ private LocalAuthPlugin(Registrar registrar) { @Override public void onMethodCall(MethodCall call, final Result result) { if (call.method.equals("authenticateWithBiometrics")) { - if (!authInProgress.compareAndSet(false, true)) { + if (authInProgress.get()) { // Apps should not invoke another authentication request while one is in progress, // so we classify this as an error condition. If we ever find a legitimate use case for // this, we can try to cancel the ongoing auth and start a new one but for now, not worth @@ -59,7 +60,8 @@ public void onMethodCall(MethodCall call, final Result result) { null); return; } - AuthenticationHelper authenticationHelper = + authInProgress.set(true); + authenticationHelper = new AuthenticationHelper( (FragmentActivity) activity, call, @@ -112,8 +114,27 @@ public void onError(String code, String error) { } catch (Exception e) { result.error("no_biometrics_available", e.getMessage(), null); } + } else if (call.method.equals(("stopAuthentication"))) { + stopAuthentication(result); } else { result.notImplemented(); } } + + /* + Stops the authentication if in progress. + */ + private void stopAuthentication(Result result) { + try { + if (authenticationHelper != null && authInProgress.get()) { + authenticationHelper.stopAuthentication(); + authenticationHelper = null; + result.success(true); + return; + } + result.success(false); + } catch (Exception e) { + result.success(false); + } + } } diff --git a/packages/local_auth/example/lib/main.dart b/packages/local_auth/example/lib/main.dart index feeb5789f343..26dccb750289 100644 --- a/packages/local_auth/example/lib/main.dart +++ b/packages/local_auth/example/lib/main.dart @@ -21,6 +21,7 @@ class _MyAppState extends State { bool _canCheckBiometrics; List _availableBiometrics; String _authorized = 'Not Authorized'; + bool _isAuthenticating = false; Future _checkBiometrics() async { bool canCheckBiometrics; @@ -53,20 +54,33 @@ class _MyAppState extends State { Future _authenticate() async { bool authenticated = false; try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); authenticated = await auth.authenticateWithBiometrics( localizedReason: 'Scan your fingerprint to authenticate', useErrorDialogs: true, stickyAuth: true); + setState(() { + _isAuthenticating = false; + _authorized = 'Authenticating'; + }); } on PlatformException catch (e) { print(e); } if (!mounted) return; + final String message = authenticated ? 'Authorized' : 'Not Authorized'; setState(() { - _authorized = authenticated ? 'Authorized' : 'Not Authorized'; + _authorized = message; }); } + void _cancelAuthentication() { + auth.stopAuthentication(); + } + @override Widget build(BuildContext context) { return MaterialApp( @@ -91,8 +105,9 @@ class _MyAppState extends State { ), Text('Current State: $_authorized\n'), RaisedButton( - child: const Text('Authenticate'), - onPressed: _authenticate, + child: Text(_isAuthenticating ? 'Cancel' : 'Authenticate'), + onPressed: + _isAuthenticating ? _cancelAuthentication : _authenticate, ) ])), )); diff --git a/packages/local_auth/lib/local_auth.dart b/packages/local_auth/lib/local_auth.dart index df69a120aa39..31c1a41d0266 100644 --- a/packages/local_auth/lib/local_auth.dart +++ b/packages/local_auth/lib/local_auth.dart @@ -90,6 +90,18 @@ class LocalAuthentication { 'authenticateWithBiometrics', args); } + /// Returns true if auth was cancelled successfully. + /// This api only works for Android. + /// Returns false if there was some error or no auth in progress. + /// + /// Returns [Future] bool true or false: + Future stopAuthentication() { + if (_platform.isAndroid) { + return _channel.invokeMethod('stopAuthentication'); + } + return Future.sync(() => true); + } + /// Returns true if device is capable of checking biometrics /// /// Returns a [Future] bool true or false: diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/pubspec.yaml index 38ed72d23422..13d9f7bed24c 100644 --- a/packages/local_auth/pubspec.yaml +++ b/packages/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS device authentication sensors such as Fingerprint Reader and Touch ID. author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/local_auth -version: 0.6.0+3 +version: 0.6.1 flutter: plugin: @@ -16,7 +16,7 @@ dependencies: sdk: flutter meta: ^1.0.5 intl: ">=0.15.1 <0.17.0" - platform: ^2.0.0 + platform: ^2.0.0 dev_dependencies: flutter_test: