[local_auth] Avoid user confirmation on face unlock (#2047)

* Define a new parameter for signaling that the transaction is sensitive.
* Up the biometric version to beta01.
* Handle no device credential error.
diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md
index 1988028..832efc7 100644
--- a/packages/local_auth/CHANGELOG.md
+++ b/packages/local_auth/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.6.0
+
+* Define a new parameter for signaling that the transaction is sensitive.
+* Up the biometric version to beta01.
+* Handle no device credential error.
+
 ## 0.5.3
 
 * Add face id detection as well by not relying on FingerprintCompat.
diff --git a/packages/local_auth/android/build.gradle b/packages/local_auth/android/build.gradle
index 142b606..74fe6c5 100644
--- a/packages/local_auth/android/build.gradle
+++ b/packages/local_auth/android/build.gradle
@@ -48,6 +48,6 @@
 
 dependencies {
     api "androidx.core:core:1.1.0-beta01"
-    api "androidx.biometric:biometric:1.0.0-alpha04"
+    api "androidx.biometric:biometric:1.0.0-beta01"
     api "androidx.fragment:fragment:1.1.0-alpha06"
 }
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 46d7bf3..a4ef01b 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
@@ -77,6 +77,7 @@
             .setTitle((String) call.argument("signInTitle"))
             .setSubtitle((String) call.argument("fingerprintHint"))
             .setNegativeButtonText((String) call.argument("cancelButton"))
+            .setConfirmationRequired((Boolean) call.argument("sensitiveTransaction"))
             .build();
   }
 
@@ -95,13 +96,11 @@
   @Override
   public void onAuthenticationError(int errorCode, CharSequence errString) {
     switch (errorCode) {
-        // TODO(mehmetf): Re-enable when biometric alpha05 is released.
-        // https://developer.android.com/jetpack/androidx/releases/biometric
-        // case BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL:
-        //   completionHandler.onError(
-        //       "PasscodeNotSet",
-        //       "Phone not secured by PIN, pattern or password, or SIM is currently locked.");
-        //   break;
+      case BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL:
+        completionHandler.onError(
+            "PasscodeNotSet",
+            "Phone not secured by PIN, pattern or password, or SIM is currently locked.");
+        break;
       case BiometricPrompt.ERROR_NO_SPACE:
       case BiometricPrompt.ERROR_NO_BIOMETRICS:
         if (call.argument("useErrorDialogs")) {
diff --git a/packages/local_auth/lib/local_auth.dart b/packages/local_auth/lib/local_auth.dart
index d5f2ac7..df69a12 100644
--- a/packages/local_auth/lib/local_auth.dart
+++ b/packages/local_auth/lib/local_auth.dart
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:io';
 
 import 'package:flutter/services.dart';
 import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
 
 import 'auth_strings.dart';
 import 'error_codes.dart';
@@ -15,6 +15,13 @@
 
 const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth');
 
+Platform _platform = const LocalPlatform();
+
+@visibleForTesting
+void setMockPathProviderPlatform(Platform platform) {
+  _platform = platform;
+}
+
 /// A Flutter plugin for authenticating the user identity locally.
 class LocalAuthentication {
   /// Authenticates the user with biometrics available on the device.
@@ -44,6 +51,11 @@
   /// Construct [AndroidAuthStrings] and [IOSAuthStrings] if you want to
   /// customize messages in the dialogs.
   ///
+  /// Setting [sensitiveTransaction] to true enables platform specific
+  /// precautions. For instance, on face unlock, Android opens a confirmation
+  /// dialog after the face is recognized to make sure the user meant to unlock
+  /// their phone.
+  ///
   /// Throws an [PlatformException] if there were technical problems with local
   /// authentication (e.g. lack of relevant hardware). This might throw
   /// [PlatformException] with error code [otherOperatingSystem] on the iOS
@@ -54,23 +66,25 @@
     bool stickyAuth = false,
     AndroidAuthMessages androidAuthStrings = const AndroidAuthMessages(),
     IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(),
+    bool sensitiveTransaction = true,
   }) async {
     assert(localizedReason != null);
     final Map<String, Object> args = <String, Object>{
       'localizedReason': localizedReason,
       'useErrorDialogs': useErrorDialogs,
       'stickyAuth': stickyAuth,
+      'sensitiveTransaction': sensitiveTransaction,
     };
-    if (Platform.isIOS) {
+    if (_platform.isIOS) {
       args.addAll(iOSAuthStrings.args);
-    } else if (Platform.isAndroid) {
+    } else if (_platform.isAndroid) {
       args.addAll(androidAuthStrings.args);
     } else {
       throw PlatformException(
           code: otherOperatingSystem,
           message: 'Local authentication does not support non-Android/iOS '
               'operating systems.',
-          details: 'Your operating system is ${Platform.operatingSystem}');
+          details: 'Your operating system is ${_platform.operatingSystem}');
     }
     return await _channel.invokeMethod<bool>(
         'authenticateWithBiometrics', args);
diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/pubspec.yaml
index 286d7aa..a78f628 100644
--- a/packages/local_auth/pubspec.yaml
+++ b/packages/local_auth/pubspec.yaml
@@ -3,7 +3,7 @@
   such as Fingerprint Reader and Touch ID.
 author: Flutter Team <flutter-dev@googlegroups.com>
 homepage: https://github.com/flutter/plugins/tree/master/packages/local_auth
-version: 0.5.3
+version: 0.6.0
 
 flutter:
   plugin:
@@ -16,6 +16,11 @@
     sdk: flutter
   meta: ^1.0.5
   intl: ^0.15.1
+  platform: ^2.0.0 
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
 
 environment:
   sdk: ">=2.0.0-dev.28.0 <3.0.0"
diff --git a/packages/local_auth/test/local_auth_test.dart b/packages/local_auth/test/local_auth_test.dart
new file mode 100644
index 0000000..205c5f7
--- /dev/null
+++ b/packages/local_auth/test/local_auth_test.dart
@@ -0,0 +1,90 @@
+// Copyright 2019 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:local_auth/auth_strings.dart';
+import 'package:local_auth/local_auth.dart';
+import 'package:platform/platform.dart';
+
+void main() {
+  TestWidgetsFlutterBinding.ensureInitialized();
+
+  group('LocalAuth', () {
+    const MethodChannel channel = MethodChannel(
+      'plugins.flutter.io/local_auth',
+    );
+
+    final List<MethodCall> log = <MethodCall>[];
+    LocalAuthentication localAuthentication;
+
+    setUp(() {
+      channel.setMockMethodCallHandler((MethodCall methodCall) {
+        log.add(methodCall);
+        return Future<dynamic>.value(true);
+      });
+      localAuthentication = LocalAuthentication();
+      log.clear();
+    });
+
+    test('authenticate with no args on Android.', () async {
+      setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android'));
+      await localAuthentication.authenticateWithBiometrics(
+          localizedReason: 'Needs secure');
+      expect(
+        log,
+        <Matcher>[
+          isMethodCall('authenticateWithBiometrics',
+              arguments: <String, dynamic>{
+                'localizedReason': 'Needs secure',
+                'useErrorDialogs': true,
+                'stickyAuth': false,
+                'sensitiveTransaction': true,
+              }..addAll(const AndroidAuthMessages().args)),
+        ],
+      );
+    });
+
+    test('authenticate with no args on iOS.', () async {
+      setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios'));
+      await localAuthentication.authenticateWithBiometrics(
+          localizedReason: 'Needs secure');
+      expect(
+        log,
+        <Matcher>[
+          isMethodCall('authenticateWithBiometrics',
+              arguments: <String, dynamic>{
+                'localizedReason': 'Needs secure',
+                'useErrorDialogs': true,
+                'stickyAuth': false,
+                'sensitiveTransaction': true,
+              }..addAll(const IOSAuthMessages().args)),
+        ],
+      );
+    });
+
+    test('authenticate with no sensitive transaction.', () async {
+      setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android'));
+      await localAuthentication.authenticateWithBiometrics(
+        localizedReason: 'Insecure',
+        sensitiveTransaction: false,
+        useErrorDialogs: false,
+      );
+      expect(
+        log,
+        <Matcher>[
+          isMethodCall('authenticateWithBiometrics',
+              arguments: <String, dynamic>{
+                'localizedReason': 'Insecure',
+                'useErrorDialogs': false,
+                'stickyAuth': false,
+                'sensitiveTransaction': false,
+              }..addAll(const AndroidAuthMessages().args)),
+        ],
+      );
+    });
+  });
+}