[local_auth] Add native Android implementation of platform interface (#4700)
diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml
index dbffb4c..baf5d46 100644
--- a/packages/local_auth/local_auth/pubspec.yaml
+++ b/packages/local_auth/local_auth/pubspec.yaml
@@ -17,8 +17,7 @@
plugin:
platforms:
android:
- package: io.flutter.plugins.localauth
- pluginClass: LocalAuthPlugin
+ default_package: local_auth_android
ios:
default_package: local_auth_ios
@@ -27,7 +26,9 @@
sdk: flutter
flutter_plugin_android_lifecycle: ^2.0.1
intl: ^0.17.0
- # Temporary path dependencies to allow moving Android and iOS implementations.
+# Temporary path dependencies to allow moving Android and iOS implementations.
+ local_auth_android:
+ path: ../local_auth_android
local_auth_ios:
path: ../local_auth_ios
local_auth_platform_interface: ^1.0.1
diff --git a/packages/local_auth/local_auth_android/AUTHORS b/packages/local_auth/local_auth_android/AUTHORS
new file mode 100644
index 0000000..d569469
--- /dev/null
+++ b/packages/local_auth/local_auth_android/AUTHORS
@@ -0,0 +1,67 @@
+# Below is a list of people and organizations that have contributed
+# to the Flutter project. Names should be added to the list like so:
+#
+# Name/Organization <email address>
+
+Google Inc.
+The Chromium Authors
+German Saprykin <saprykin.h@gmail.com>
+Benjamin Sauer <sauer.benjamin@gmail.com>
+larsenthomasj@gmail.com
+Ali Bitek <alibitek@protonmail.ch>
+Pol Batlló <pol.batllo@gmail.com>
+Anatoly Pulyaevskiy
+Hayden Flinner <haydenflinner@gmail.com>
+Stefano Rodriguez <hlsroddy@gmail.com>
+Salvatore Giordano <salvatoregiordanoo@gmail.com>
+Brian Armstrong <brian@flutter.institute>
+Paul DeMarco <paulmdemarco@gmail.com>
+Fabricio Nogueira <feufeu@gmail.com>
+Simon Lightfoot <simon@devangels.london>
+Ashton Thomas <ashton@acrinta.com>
+Thomas Danner <thmsdnnr@gmail.com>
+Diego Velásquez <diego.velasquez.lopez@gmail.com>
+Hajime Nakamura <nkmrhj@gmail.com>
+Tuyển Vũ Xuân <netsoft1985@gmail.com>
+Miguel Ruivo <miguel@miguelruivo.com>
+Sarthak Verma <sarthak@artiosys.com>
+Mike Diarmid <mike@invertase.io>
+Invertase <oss@invertase.io>
+Elliot Hesp <elliot@invertase.io>
+Vince Varga <vince.varga@smaho.com>
+Aawaz Gyawali <awazgyawali@gmail.com>
+EUI Limited <ian.evans3@admiralgroup.co.uk>
+Katarina Sheremet <katarina@sheremet.ch>
+Thomas Stockx <thomas@stockxit.com>
+Sarbagya Dhaubanjar <sarbagyastha@gmail.com>
+Ozkan Eksi <ozeksi@gmail.com>
+Rishab Nayak <rishab@bu.edu>
+ko2ic <ko2ic.dev@gmail.com>
+Jonathan Younger <jonathan@daikini.com>
+Jose Sanchez <josesm82@gmail.com>
+Debkanchan Samadder <debu.samadder@gmail.com>
+Audrius Karosevicius <audrius.karosevicius@gmail.com>
+Lukasz Piliszczuk <lukasz@intheloup.io>
+SoundReply Solutions GmbH <ch@soundreply.com>
+Rafal Wachol <rwachol@gmail.com>
+Pau Picas <pau.picas@gmail.com>
+Christian Weder <chrstian.weder@yapeal.ch>
+Alexandru Tuca <salexandru.tuca@outlook.com>
+Christian Weder <chrstian.weder@yapeal.ch>
+Rhodes Davis Jr. <rody.davis.jr@gmail.com>
+Luigi Agosti <luigi@tengio.com>
+Quentin Le Guennec <quentin@tengio.com>
+Koushik Ravikumar <koushik@tengio.com>
+Nissim Dsilva <nissim@tengio.com>
+Giancarlo Rocha <giancarloiff@gmail.com>
+Ryo Miyake <ryo@miyake.id>
+Théo Champion <contact.theochampion@gmail.com>
+Kazuki Yamaguchi <y.kazuki0614n@gmail.com>
+Eitan Schwartz <eshvartz@gmail.com>
+Chris Rutkowski <chrisrutkowski89@gmail.com>
+Juan Alvarez <juan.alvarez@resideo.com>
+Aleksandr Yurkovskiy <sanekyy@gmail.com>
+Anton Borries <mail@antonborri.es>
+Alex Li <google@alexv525.com>
+Rahul Raj <64.rahulraj@gmail.com>
+Bodhi Mulders <info@bemacized.net>
diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md
new file mode 100644
index 0000000..7f198f2
--- /dev/null
+++ b/packages/local_auth/local_auth_android/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 1.0.0
+
+* Initial release from migration to federated architecture.
diff --git a/packages/local_auth/local_auth_android/LICENSE b/packages/local_auth/local_auth_android/LICENSE
new file mode 100644
index 0000000..c6823b8
--- /dev/null
+++ b/packages/local_auth/local_auth_android/LICENSE
@@ -0,0 +1,25 @@
+Copyright 2013 The Flutter Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/local_auth/local_auth_android/README.md b/packages/local_auth/local_auth_android/README.md
new file mode 100644
index 0000000..0724491
--- /dev/null
+++ b/packages/local_auth/local_auth_android/README.md
@@ -0,0 +1,11 @@
+# local\_auth\_android
+
+The Android implementation of [`local_auth`][1].
+
+## Usage
+
+This package is [endorsed][2], which means you can simply use `local_auth`
+normally. This package will be automatically included in your app when you do.
+
+[1]: https://pub.dev/packages/local_auth
+[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin
\ No newline at end of file
diff --git a/packages/local_auth/local_auth/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle
similarity index 100%
rename from packages/local_auth/local_auth/android/build.gradle
rename to packages/local_auth/local_auth_android/android/build.gradle
diff --git a/packages/local_auth/local_auth/android/lint-baseline.xml b/packages/local_auth/local_auth_android/android/lint-baseline.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/lint-baseline.xml
rename to packages/local_auth/local_auth_android/android/lint-baseline.xml
diff --git a/packages/local_auth/local_auth/android/settings.gradle b/packages/local_auth/local_auth_android/android/settings.gradle
similarity index 100%
rename from packages/local_auth/local_auth/android/settings.gradle
rename to packages/local_auth/local_auth_android/android/settings.gradle
diff --git a/packages/local_auth/local_auth/android/src/main/AndroidManifest.xml b/packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/AndroidManifest.xml
rename to packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml
diff --git a/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
rename to packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
diff --git a/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java
similarity index 99%
rename from packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java
rename to packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java
index a63e22a..49a6b78 100644
--- a/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java
+++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java
@@ -40,7 +40,7 @@
*/
@SuppressWarnings("deprecation")
public class LocalAuthPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware {
- private static final String CHANNEL_NAME = "plugins.flutter.io/local_auth";
+ private static final String CHANNEL_NAME = "plugins.flutter.io/local_auth_android";
private static final int LOCK_REQUEST_CODE = 221;
private Activity activity;
private final AtomicBoolean authInProgress = new AtomicBoolean(false);
diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_initial_icon.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_initial_icon.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_success_icon.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_success_icon.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_warning_icon.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_warning_icon.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_done_white_24dp.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_done_white_24dp.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_priority_high_white_24dp.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_priority_high_white_24dp.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/layout/go_to_setting.xml b/packages/local_auth/local_auth_android/android/src/main/res/layout/go_to_setting.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/layout/go_to_setting.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/layout/go_to_setting.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/layout/scan_fp.xml b/packages/local_auth/local_auth_android/android/src/main/res/layout/scan_fp.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/layout/scan_fp.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/layout/scan_fp.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/values/colors.xml b/packages/local_auth/local_auth_android/android/src/main/res/values/colors.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/values/colors.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/values/colors.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/values/dimens.xml b/packages/local_auth/local_auth_android/android/src/main/res/values/dimens.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/values/dimens.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/values/dimens.xml
diff --git a/packages/local_auth/local_auth/android/src/main/res/values/styles.xml b/packages/local_auth/local_auth_android/android/src/main/res/values/styles.xml
similarity index 100%
rename from packages/local_auth/local_auth/android/src/main/res/values/styles.xml
rename to packages/local_auth/local_auth_android/android/src/main/res/values/styles.xml
diff --git a/packages/local_auth/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java
similarity index 100%
rename from packages/local_auth/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java
rename to packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java
diff --git a/packages/local_auth/local_auth_android/example/README.md b/packages/local_auth/local_auth_android/example/README.md
new file mode 100644
index 0000000..a4a6091
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/README.md
@@ -0,0 +1,8 @@
+# local_auth_example
+
+Demonstrates how to use the local_auth plugin.
+
+## Getting Started
+
+For help getting started with Flutter, view our online
+[documentation](https://flutter.dev/).
diff --git a/packages/local_auth/local_auth_android/example/android/app/build.gradle b/packages/local_auth/local_auth_android/example/android/app/build.gradle
new file mode 100644
index 0000000..d1cef4b
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/build.gradle
@@ -0,0 +1,58 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 31
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+
+ defaultConfig {
+ applicationId "io.flutter.plugins.localauthexample"
+ minSdkVersion 16
+ targetSdkVersion 28
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+}
diff --git a/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..186b715
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java
new file mode 100644
index 0000000..0f4298d
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java
@@ -0,0 +1,14 @@
+// Copyright 2013 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.
+
+package io.flutter.plugins;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface DartIntegrationTest {}
diff --git a/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java
new file mode 100644
index 0000000..68c2237
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java
@@ -0,0 +1,20 @@
+// Copyright 2013 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.
+
+package io.flutter.plugins.localauth;
+
+import androidx.test.rule.ActivityTestRule;
+import dev.flutter.plugins.integration_test.FlutterTestRunner;
+import io.flutter.embedding.android.FlutterFragmentActivity;
+import io.flutter.plugins.DartIntegrationTest;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+@DartIntegrationTest
+@RunWith(FlutterTestRunner.class)
+public class FlutterFragmentActivityTest {
+ @Rule
+ public ActivityTestRule<FlutterFragmentActivity> rule =
+ new ActivityTestRule<>(FlutterFragmentActivity.class);
+}
diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8c09177
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="io.flutter.plugins.localauthexample">
+
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
+
+ <application android:label="local_auth_example" android:icon="@mipmap/ic_launcher">
+ <activity android:name="io.flutter.embedding.android.FlutterFragmentActivity"
+ android:launchMode="singleTop"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <meta-data android:name="flutterEmbedding" android:value="2"/>
+ </application>
+</manifest>
diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/local_auth/local_auth_android/example/android/build.gradle b/packages/local_auth/local_auth_android/example/android/build.gradle
new file mode 100644
index 0000000..54c9436
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.1'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/packages/local_auth/local_auth_android/example/android/gradle.properties b/packages/local_auth/local_auth_android/example/android/gradle.properties
new file mode 100644
index 0000000..7fe61a7
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1024m
+android.useAndroidX=true
+android.enableJetifier=true
+android.enableR8=true
diff --git a/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..cd9fe1c
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Jan 03 14:07:08 CST 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
diff --git a/packages/local_auth/local_auth_android/example/android/settings.gradle b/packages/local_auth/local_auth_android/example/android/settings.gradle
new file mode 100644
index 0000000..115da6c
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/settings.gradle
@@ -0,0 +1,15 @@
+include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+ pluginsFile.withInputStream { stream -> plugins.load(stream) }
+}
+
+plugins.each { name, path ->
+ def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+ include ":$name"
+ project(":$name").projectDir = pluginDirectory
+}
diff --git a/packages/local_auth/local_auth_android/example/android/settings_aar.gradle b/packages/local_auth/local_auth_android/example/android/settings_aar.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/android/settings_aar.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart b/packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart
new file mode 100644
index 0000000..1dfc0ae
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart
@@ -0,0 +1,19 @@
+// Copyright 2013 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 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+
+import 'package:local_auth_android/local_auth_android.dart';
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ testWidgets('canCheckBiometrics', (WidgetTester tester) async {
+ expect(
+ LocalAuthAndroid().getEnrolledBiometrics(),
+ completion(isList),
+ );
+ });
+}
diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart
new file mode 100644
index 0000000..4c04521
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/lib/main.dart
@@ -0,0 +1,238 @@
+// Copyright 2013 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.
+
+// ignore_for_file: public_member_api_docs
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:local_auth_android/local_auth_android.dart';
+import 'package:local_auth_platform_interface/local_auth_platform_interface.dart';
+
+void main() {
+ runApp(MyApp());
+}
+
+class MyApp extends StatefulWidget {
+ @override
+ _MyAppState createState() => _MyAppState();
+}
+
+class _MyAppState extends State<MyApp> {
+ _SupportState _supportState = _SupportState.unknown;
+ bool? _canCheckBiometrics;
+ List<BiometricType>? _availableBiometrics;
+ String _authorized = 'Not Authorized';
+ bool _isAuthenticating = false;
+
+ @override
+ void initState() {
+ super.initState();
+ LocalAuthPlatform.instance.isDeviceSupported().then(
+ (bool isSupported) => setState(() => _supportState = isSupported
+ ? _SupportState.supported
+ : _SupportState.unsupported),
+ );
+ }
+
+ Future<void> _checkBiometrics() async {
+ late bool canCheckBiometrics;
+ try {
+ canCheckBiometrics =
+ (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty;
+ } on PlatformException catch (e) {
+ canCheckBiometrics = false;
+ print(e);
+ }
+ if (!mounted) {
+ return;
+ }
+
+ setState(() {
+ _canCheckBiometrics = canCheckBiometrics;
+ });
+ }
+
+ Future<void> _getEnrolledBiometrics() async {
+ late List<BiometricType> availableBiometrics;
+ try {
+ availableBiometrics =
+ await LocalAuthPlatform.instance.getEnrolledBiometrics();
+ } on PlatformException catch (e) {
+ availableBiometrics = <BiometricType>[];
+ print(e);
+ }
+ if (!mounted) {
+ return;
+ }
+
+ setState(() {
+ _availableBiometrics = availableBiometrics;
+ });
+ }
+
+ Future<void> _authenticate() async {
+ bool authenticated = false;
+ try {
+ setState(() {
+ _isAuthenticating = true;
+ _authorized = 'Authenticating';
+ });
+ authenticated = await LocalAuthPlatform.instance.authenticate(
+ localizedReason: 'Let OS determine authentication method',
+ authMessages: <AuthMessages>[const AndroidAuthMessages()],
+ options: const AuthenticationOptions(
+ useErrorDialogs: true,
+ stickyAuth: true,
+ ),
+ );
+ setState(() {
+ _isAuthenticating = false;
+ });
+ } on PlatformException catch (e) {
+ print(e);
+ setState(() {
+ _isAuthenticating = false;
+ _authorized = 'Error - ${e.message}';
+ });
+ return;
+ }
+ if (!mounted) {
+ return;
+ }
+
+ setState(
+ () => _authorized = authenticated ? 'Authorized' : 'Not Authorized');
+ }
+
+ Future<void> _authenticateWithBiometrics() async {
+ bool authenticated = false;
+ try {
+ setState(() {
+ _isAuthenticating = true;
+ _authorized = 'Authenticating';
+ });
+ authenticated = await LocalAuthPlatform.instance.authenticate(
+ localizedReason:
+ 'Scan your fingerprint (or face or whatever) to authenticate',
+ authMessages: <AuthMessages>[const AndroidAuthMessages()],
+ options: const AuthenticationOptions(
+ useErrorDialogs: true,
+ stickyAuth: true,
+ biometricOnly: true,
+ ),
+ );
+ setState(() {
+ _isAuthenticating = false;
+ _authorized = 'Authenticating';
+ });
+ } on PlatformException catch (e) {
+ print(e);
+ setState(() {
+ _isAuthenticating = false;
+ _authorized = 'Error - ${e.message}';
+ });
+ return;
+ }
+ if (!mounted) {
+ return;
+ }
+
+ final String message = authenticated ? 'Authorized' : 'Not Authorized';
+ setState(() {
+ _authorized = message;
+ });
+ }
+
+ Future<void> _cancelAuthentication() async {
+ await LocalAuthPlatform.instance.stopAuthentication();
+ setState(() => _isAuthenticating = false);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ home: Scaffold(
+ appBar: AppBar(
+ title: const Text('Plugin example app'),
+ ),
+ body: ListView(
+ padding: const EdgeInsets.only(top: 30),
+ children: <Widget>[
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: <Widget>[
+ if (_supportState == _SupportState.unknown)
+ const CircularProgressIndicator()
+ else if (_supportState == _SupportState.supported)
+ const Text('This device is supported')
+ else
+ const Text('This device is not supported'),
+ const Divider(height: 100),
+ Text('Can check biometrics: $_canCheckBiometrics\n'),
+ ElevatedButton(
+ child: const Text('Check biometrics'),
+ onPressed: _checkBiometrics,
+ ),
+ const Divider(height: 100),
+ Text('Available biometrics: $_availableBiometrics\n'),
+ ElevatedButton(
+ child: const Text('Get available biometrics'),
+ onPressed: _getEnrolledBiometrics,
+ ),
+ const Divider(height: 100),
+ Text('Current State: $_authorized\n'),
+ if (_isAuthenticating)
+ ElevatedButton(
+ onPressed: _cancelAuthentication,
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: const <Widget>[
+ Text('Cancel Authentication'),
+ Icon(Icons.cancel),
+ ],
+ ),
+ )
+ else
+ Column(
+ children: <Widget>[
+ ElevatedButton(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: const <Widget>[
+ Text('Authenticate'),
+ Icon(Icons.perm_device_information),
+ ],
+ ),
+ onPressed: _authenticate,
+ ),
+ ElevatedButton(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: <Widget>[
+ Text(_isAuthenticating
+ ? 'Cancel'
+ : 'Authenticate: biometrics only'),
+ const Icon(Icons.fingerprint),
+ ],
+ ),
+ onPressed: _authenticateWithBiometrics,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+enum _SupportState {
+ unknown,
+ supported,
+ unsupported,
+}
diff --git a/packages/local_auth/local_auth_android/example/pubspec.yaml b/packages/local_auth/local_auth_android/example/pubspec.yaml
new file mode 100644
index 0000000..c07a81d
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/pubspec.yaml
@@ -0,0 +1,28 @@
+name: local_auth_android_example
+description: Demonstrates how to use the local_auth_android plugin.
+publish_to: none
+
+environment:
+ sdk: ">=2.14.0 <3.0.0"
+ flutter: ">=2.8.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ local_auth_android:
+ # When depending on this package from a real application you should use:
+ # local_auth_android: ^x.y.z
+ # See https://dart.dev/tools/pub/dependencies#version-constraints
+ # The example app is bundled with the plugin so we use a path dependency on
+ # the parent directory to use the current plugin's version.
+ path: ../
+ local_auth_platform_interface: ^1.0.0
+
+dev_dependencies:
+ flutter_driver:
+ sdk: flutter
+ integration_test:
+ sdk: flutter
+
+flutter:
+ uses-material-design: true
diff --git a/packages/local_auth/local_auth_android/example/test_driver/integration_test.dart b/packages/local_auth/local_auth_android/example/test_driver/integration_test.dart
new file mode 100644
index 0000000..4f10f2a
--- /dev/null
+++ b/packages/local_auth/local_auth_android/example/test_driver/integration_test.dart
@@ -0,0 +1,7 @@
+// Copyright 2013 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 'package:integration_test/integration_test_driver.dart';
+
+Future<void> main() => integrationDriver();
diff --git a/packages/local_auth/local_auth_android/lib/local_auth_android.dart b/packages/local_auth/local_auth_android/lib/local_auth_android.dart
new file mode 100644
index 0000000..a3f314e
--- /dev/null
+++ b/packages/local_auth/local_auth_android/lib/local_auth_android.dart
@@ -0,0 +1,87 @@
+// Copyright 2013 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 'package:flutter/services.dart';
+import 'package:local_auth_android/types/auth_messages_android.dart';
+import 'package:local_auth_platform_interface/local_auth_platform_interface.dart';
+import 'package:local_auth_platform_interface/types/auth_messages.dart';
+import 'package:local_auth_platform_interface/types/auth_options.dart';
+import 'package:local_auth_platform_interface/types/biometric_type.dart';
+
+export 'package:local_auth_android/types/auth_messages_android.dart';
+export 'package:local_auth_platform_interface/types/auth_messages.dart';
+export 'package:local_auth_platform_interface/types/auth_options.dart';
+export 'package:local_auth_platform_interface/types/biometric_type.dart';
+
+const MethodChannel _channel =
+ MethodChannel('plugins.flutter.io/local_auth_android');
+
+/// The implementation of [LocalAuthPlatform] for Android.
+class LocalAuthAndroid extends LocalAuthPlatform {
+ /// Registers this class as the default instance of [LocalAuthPlatform].
+ static void registerWith() {
+ LocalAuthPlatform.instance = LocalAuthAndroid();
+ }
+
+ @override
+ Future<bool> authenticate({
+ required String localizedReason,
+ required Iterable<AuthMessages> authMessages,
+ AuthenticationOptions options = const AuthenticationOptions(),
+ }) async {
+ assert(localizedReason.isNotEmpty);
+ final Map<String, Object> args = <String, Object>{
+ 'localizedReason': localizedReason,
+ 'useErrorDialogs': options.useErrorDialogs,
+ 'stickyAuth': options.stickyAuth,
+ 'sensitiveTransaction': options.sensitiveTransaction,
+ 'biometricOnly': options.biometricOnly,
+ };
+ args.addAll(const AndroidAuthMessages().args);
+ for (final AuthMessages messages in authMessages) {
+ if (messages is AndroidAuthMessages) {
+ args.addAll(messages.args);
+ }
+ }
+ return (await _channel.invokeMethod<bool>('authenticate', args)) ?? false;
+ }
+
+ @override
+ Future<bool> deviceSupportsBiometrics() async {
+ return (await getEnrolledBiometrics()).isNotEmpty;
+ }
+
+ @override
+ Future<List<BiometricType>> getEnrolledBiometrics() async {
+ final List<String> result = (await _channel.invokeListMethod<String>(
+ 'getAvailableBiometrics',
+ )) ??
+ <String>[];
+ final List<BiometricType> biometrics = <BiometricType>[];
+ for (final String value in result) {
+ switch (value) {
+ case 'face':
+ biometrics.add(BiometricType.face);
+ break;
+ case 'fingerprint':
+ biometrics.add(BiometricType.fingerprint);
+ break;
+ case 'iris':
+ biometrics.add(BiometricType.iris);
+ break;
+ case 'undefined':
+ break;
+ }
+ }
+ return biometrics;
+ }
+
+ @override
+ Future<bool> isDeviceSupported() async =>
+ (await _channel.invokeMethod<bool>('isDeviceSupported')) ?? false;
+
+ @override
+ Future<bool> stopAuthentication() async =>
+ await _channel.invokeMethod<bool>('stopAuthentication') ?? false;
+}
diff --git a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart
new file mode 100644
index 0000000..ea61a4b
--- /dev/null
+++ b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart
@@ -0,0 +1,191 @@
+// Copyright 2013 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 'package:flutter/foundation.dart';
+import 'package:intl/intl.dart';
+import 'package:local_auth_platform_interface/types/auth_messages.dart';
+
+/// Android side authentication messages.
+///
+/// Provides default values for all messages.
+@immutable
+class AndroidAuthMessages extends AuthMessages {
+ /// Constructs a new instance.
+ const AndroidAuthMessages({
+ this.biometricHint,
+ this.biometricNotRecognized,
+ this.biometricRequiredTitle,
+ this.biometricSuccess,
+ this.cancelButton,
+ this.deviceCredentialsRequiredTitle,
+ this.deviceCredentialsSetupDescription,
+ this.goToSettingsButton,
+ this.goToSettingsDescription,
+ this.signInTitle,
+ });
+
+ /// Hint message advising the user how to authenticate with biometrics.
+ /// Maximum 60 characters.
+ final String? biometricHint;
+
+ /// Message to let the user know that authentication was failed.
+ /// Maximum 60 characters.
+ final String? biometricNotRecognized;
+
+ /// Message shown as a title in a dialog which indicates the user
+ /// has not set up biometric authentication on their device.
+ /// Maximum 60 characters.
+ final String? biometricRequiredTitle;
+
+ /// Message to let the user know that authentication was successful.
+ /// Maximum 60 characters
+ final String? biometricSuccess;
+
+ /// Message shown on a button that the user can click to leave the
+ /// current dialog.
+ /// Maximum 30 characters.
+ final String? cancelButton;
+
+ /// Message shown as a title in a dialog which indicates the user
+ /// has not set up credentials authentication on their device.
+ /// Maximum 60 characters.
+ final String? deviceCredentialsRequiredTitle;
+
+ /// Message advising the user to go to the settings and configure
+ /// device credentials on their device.
+ final String? deviceCredentialsSetupDescription;
+
+ /// Message shown on a button that the user can click to go to settings pages
+ /// from the current dialog.
+ /// Maximum 30 characters.
+ final String? goToSettingsButton;
+
+ /// Message advising the user to go to the settings and configure
+ /// biometric on their device.
+ final String? goToSettingsDescription;
+
+ /// Message shown as a title in a dialog which indicates the user
+ /// that they need to scan biometric to continue.
+ /// Maximum 60 characters.
+ final String? signInTitle;
+
+ @override
+ Map<String, String> get args {
+ return <String, String>{
+ 'biometricHint': biometricHint ?? androidBiometricHint,
+ 'biometricNotRecognized':
+ biometricNotRecognized ?? androidBiometricNotRecognized,
+ 'biometricSuccess': biometricSuccess ?? androidBiometricSuccess,
+ 'biometricRequired':
+ biometricRequiredTitle ?? androidBiometricRequiredTitle,
+ 'cancelButton': cancelButton ?? androidCancelButton,
+ 'deviceCredentialsRequired': deviceCredentialsRequiredTitle ??
+ androidDeviceCredentialsRequiredTitle,
+ 'deviceCredentialsSetupDescription': deviceCredentialsSetupDescription ??
+ androidDeviceCredentialsSetupDescription,
+ 'goToSetting': goToSettingsButton ?? goToSettings,
+ 'goToSettingDescription':
+ goToSettingsDescription ?? androidGoToSettingsDescription,
+ 'signInTitle': signInTitle ?? androidSignInTitle,
+ };
+ }
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is AndroidAuthMessages &&
+ runtimeType == other.runtimeType &&
+ biometricHint == other.biometricHint &&
+ biometricNotRecognized == other.biometricNotRecognized &&
+ biometricRequiredTitle == other.biometricRequiredTitle &&
+ biometricSuccess == other.biometricSuccess &&
+ cancelButton == other.cancelButton &&
+ deviceCredentialsRequiredTitle ==
+ other.deviceCredentialsRequiredTitle &&
+ deviceCredentialsSetupDescription ==
+ other.deviceCredentialsSetupDescription &&
+ goToSettingsButton == other.goToSettingsButton &&
+ goToSettingsDescription == other.goToSettingsDescription &&
+ signInTitle == other.signInTitle;
+
+ @override
+ int get hashCode =>
+ biometricHint.hashCode ^
+ biometricNotRecognized.hashCode ^
+ biometricRequiredTitle.hashCode ^
+ biometricSuccess.hashCode ^
+ cancelButton.hashCode ^
+ deviceCredentialsRequiredTitle.hashCode ^
+ deviceCredentialsSetupDescription.hashCode ^
+ goToSettingsButton.hashCode ^
+ goToSettingsDescription.hashCode ^
+ signInTitle.hashCode;
+}
+
+// Default strings for AndroidAuthMessages. Currently supports English.
+// Intl.message must be string literals.
+
+/// Message shown on a button that the user can click to go to settings pages
+/// from the current dialog.
+String get goToSettings => Intl.message('Go to settings',
+ desc: 'Message shown on a button that the user can click to go to '
+ 'settings pages from the current dialog. Maximum 30 characters.');
+
+/// Hint message advising the user how to authenticate with biometrics.
+String get androidBiometricHint => Intl.message('Verify identity',
+ desc: 'Hint message advising the user how to authenticate with biometrics. '
+ 'Maximum 60 characters.');
+
+/// Message to let the user know that authentication was failed.
+String get androidBiometricNotRecognized =>
+ Intl.message('Not recognized. Try again.',
+ desc: 'Message to let the user know that authentication was failed. '
+ 'Maximum 60 characters.');
+
+/// Message to let the user know that authentication was successful. It
+String get androidBiometricSuccess => Intl.message('Success',
+ desc: 'Message to let the user know that authentication was successful. '
+ 'Maximum 60 characters.');
+
+/// Message shown on a button that the user can click to leave the
+/// current dialog.
+String get androidCancelButton => Intl.message('Cancel',
+ desc: 'Message shown on a button that the user can click to leave the '
+ 'current dialog. Maximum 30 characters.');
+
+/// Message shown as a title in a dialog which indicates the user
+/// that they need to scan biometric to continue.
+String get androidSignInTitle => Intl.message('Authentication required',
+ desc: 'Message shown as a title in a dialog which indicates the user '
+ 'that they need to scan biometric to continue. Maximum 60 characters.');
+
+/// Message shown as a title in a dialog which indicates the user
+/// has not set up biometric authentication on their device.
+String get androidBiometricRequiredTitle => Intl.message('Biometric required',
+ desc: 'Message shown as a title in a dialog which indicates the user '
+ 'has not set up biometric authentication on their device. '
+ 'Maximum 60 characters.');
+
+/// Message shown as a title in a dialog which indicates the user
+/// has not set up credentials authentication on their device.
+String get androidDeviceCredentialsRequiredTitle =>
+ Intl.message('Device credentials required',
+ desc: 'Message shown as a title in a dialog which indicates the user '
+ 'has not set up credentials authentication on their device. '
+ 'Maximum 60 characters.');
+
+/// Message advising the user to go to the settings and configure
+/// device credentials on their device.
+String get androidDeviceCredentialsSetupDescription =>
+ Intl.message('Device credentials required',
+ desc: 'Message advising the user to go to the settings and configure '
+ 'device credentials on their device.');
+
+/// Message advising the user to go to the settings and configure
+/// biometric on their device.
+String get androidGoToSettingsDescription => Intl.message(
+ 'Biometric authentication is not set up on your device. Go to '
+ '\'Settings > Security\' to add biometric authentication.',
+ desc: 'Message advising the user to go to the settings and configure '
+ 'biometric on their device.');
diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml
new file mode 100644
index 0000000..ec2991d
--- /dev/null
+++ b/packages/local_auth/local_auth_android/pubspec.yaml
@@ -0,0 +1,29 @@
+name: local_auth_android
+description: Android implementation of the local_auth plugin.
+repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android
+issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22
+version: 1.0.0
+
+environment:
+ sdk: ">=2.14.0 <3.0.0"
+ flutter: ">=2.8.0"
+
+flutter:
+ plugin:
+ implements: local_auth
+ platforms:
+ android:
+ package: io.flutter.plugins.localauth
+ pluginClass: LocalAuthPlugin
+ dartPluginClass: LocalAuthAndroid
+
+dependencies:
+ flutter:
+ sdk: flutter
+ flutter_plugin_android_lifecycle: ^2.0.1
+ intl: ^0.17.0
+ local_auth_platform_interface: ^1.0.0
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
\ No newline at end of file
diff --git a/packages/local_auth/local_auth_android/test/local_auth_test.dart b/packages/local_auth/local_auth_android/test/local_auth_test.dart
new file mode 100644
index 0000000..31f5e57
--- /dev/null
+++ b/packages/local_auth/local_auth_android/test/local_auth_test.dart
@@ -0,0 +1,181 @@
+// Copyright 2013 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_android/local_auth_android.dart';
+import 'package:local_auth_platform_interface/types/auth_messages.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ group('LocalAuth', () {
+ const MethodChannel channel = MethodChannel(
+ 'plugins.flutter.io/local_auth_android',
+ );
+
+ final List<MethodCall> log = <MethodCall>[];
+ late LocalAuthAndroid localAuthentication;
+
+ setUp(() {
+ channel.setMockMethodCallHandler((MethodCall methodCall) {
+ log.add(methodCall);
+ switch (methodCall.method) {
+ case 'getAvailableBiometrics':
+ return Future<List<String>>.value(
+ <String>['face', 'fingerprint', 'iris', 'undefined']);
+ default:
+ return Future<dynamic>.value(true);
+ }
+ });
+ localAuthentication = LocalAuthAndroid();
+ log.clear();
+ });
+
+ test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async {
+ final bool result = await localAuthentication.deviceSupportsBiometrics();
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('getAvailableBiometrics', arguments: null),
+ ],
+ );
+ expect(result, true);
+ });
+
+ test('getEnrolledBiometrics calls platform', () async {
+ final List<BiometricType> result =
+ await localAuthentication.getEnrolledBiometrics();
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('getAvailableBiometrics', arguments: null),
+ ],
+ );
+ expect(result, <BiometricType>[
+ BiometricType.face,
+ BiometricType.fingerprint,
+ BiometricType.iris
+ ]);
+ });
+
+ test('isDeviceSupported calls platform', () async {
+ await localAuthentication.isDeviceSupported();
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('isDeviceSupported', arguments: null),
+ ],
+ );
+ });
+
+ test('stopAuthentication calls platform', () async {
+ await localAuthentication.stopAuthentication();
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('stopAuthentication', arguments: null),
+ ],
+ );
+ });
+
+ group('With device auth fail over', () {
+ test('authenticate with no args.', () async {
+ await localAuthentication.authenticate(
+ authMessages: <AuthMessages>[const AndroidAuthMessages()],
+ localizedReason: 'Needs secure',
+ options: const AuthenticationOptions(biometricOnly: true),
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('authenticate',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Needs secure',
+ 'useErrorDialogs': true,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': true,
+ 'biometricOnly': true,
+ }..addAll(const AndroidAuthMessages().args)),
+ ],
+ );
+ });
+
+ test('authenticate with no sensitive transaction.', () async {
+ await localAuthentication.authenticate(
+ authMessages: <AuthMessages>[const AndroidAuthMessages()],
+ localizedReason: 'Insecure',
+ options: const AuthenticationOptions(
+ sensitiveTransaction: false,
+ useErrorDialogs: false,
+ biometricOnly: true,
+ ),
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('authenticate',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Insecure',
+ 'useErrorDialogs': false,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': false,
+ 'biometricOnly': true,
+ }..addAll(const AndroidAuthMessages().args)),
+ ],
+ );
+ });
+ });
+
+ group('With biometrics only', () {
+ test('authenticate with no args.', () async {
+ await localAuthentication.authenticate(
+ authMessages: <AuthMessages>[const AndroidAuthMessages()],
+ localizedReason: 'Needs secure',
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('authenticate',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Needs secure',
+ 'useErrorDialogs': true,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': true,
+ 'biometricOnly': false,
+ }..addAll(const AndroidAuthMessages().args)),
+ ],
+ );
+ });
+
+ test('authenticate with no sensitive transaction.', () async {
+ await localAuthentication.authenticate(
+ authMessages: <AuthMessages>[const AndroidAuthMessages()],
+ localizedReason: 'Insecure',
+ options: const AuthenticationOptions(
+ sensitiveTransaction: false,
+ useErrorDialogs: false,
+ ),
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('authenticate',
+ arguments: <String, dynamic>{
+ 'localizedReason': 'Insecure',
+ 'useErrorDialogs': false,
+ 'stickyAuth': false,
+ 'sensitiveTransaction': false,
+ 'biometricOnly': false,
+ }..addAll(const AndroidAuthMessages().args)),
+ ],
+ );
+ });
+ });
+ });
+}