Local authentication plugin (#151) * Initial commit of local_auth * Make it work with example app. * Analyzer and formatter fixes * Fix review comments
diff --git a/packages/local_auth/.gitignore b/packages/local_auth/.gitignore new file mode 100644 index 0000000..14c7d4c --- /dev/null +++ b/packages/local_auth/.gitignore
@@ -0,0 +1,9 @@ +.DS_Store +.atom/ +.idea +.packages +.pub/ +build/ +ios/.generated/ +packages +pubspec.lock
diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md new file mode 100644 index 0000000..4704b65 --- /dev/null +++ b/packages/local_auth/CHANGELOG.md
@@ -0,0 +1,3 @@ +## [0.0.1] - 6/21/2017 + +* Initial release of local authentication plugin.
diff --git a/packages/local_auth/LICENSE b/packages/local_auth/LICENSE new file mode 100644 index 0000000..c892933 --- /dev/null +++ b/packages/local_auth/LICENSE
@@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium 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/README.md b/packages/local_auth/README.md new file mode 100644 index 0000000..75d4e34 --- /dev/null +++ b/packages/local_auth/README.md
@@ -0,0 +1,104 @@ +# local_auth + +This Flutter plugin provides means to perform local, on-device authentication of +the user. + +This means referring to biometric authentication on iOS (Touch ID or lock code) +and the fingerprint APIs on Android (introduced in Android 6.0). + +## Usage in Dart + +Import the relevant file: + +```dart +import 'package:local_auth/local_auth.dart'; +``` + +We have default dialogs with an 'OK' button to show authentication error +messages for the following 2 cases: + +1. Passcode/PIN/Pattern Not Set. The user has not yet configured a passcode on + iOS or PIN/pattern on Android. +2. Touch ID/Fingerprint Not Enrolled. The user has not enrolled any + fingerprints on the device. + +Which means, if there's no fingerprint on the user's device, a dialog with +instructions will pop up to let the user set up fingerprint. If the user clicks +'OK' button, it will return 'false'. + +Use the exported APIs to trigger local authentication with default dialogs: + +```dart +LocalAuthentication localAuth = new LocalAuthentication(); +bool didAuthenticate = + await localAuth.authenticateWithBiometrics( + localizedReason: 'Please authenticate to show account balance'); +``` + +If you don't want to use the default dialogs, call this API with +'useErrorDialogs = false'. In this case, it will throw the error message back +and you need to handle them in your dart code: + +```dart +bool didAuthenticate = + await localAuth.authenticateWithBiometrics( + localizedReason: 'Please authenticate to show account balance', + useErrorDialogs: false); +``` + +You can use our default dialog messages, or you can use your own messages by +passing in IOSAuthMessages and AndroidAuthMessages: + +```dart +import 'package:local_auth/auth_strings.dart'; + +const iosStrings = const IOSAuthMessages( + cancelButton: 'cancel', + goToSettingsButton: 'settings', + goToSettingsDescription: 'Please set up your Touch ID.', + lockOut: 'Please reenable your Touch ID'); +await localAuth.authenticateWithBiometrics( + localizedReason: 'Please authenticate to show account balance', + useErrorDialogs: false, + iOSAuthStrings: iosStrings); + +``` + +### Exceptions + +There are 4 types of exceptions: PasscodeNotSet, NotEnrolled, NotAvailable and +OtherOperatingSystem. They are wrapped in LocalAuthenticationError class. You can +catch the exception and handle them by different types. For example: + +```dart +import 'package:flutter/services.dart'; +import 'package:local_auth/error_codes.dart' as auth_error; + +try { + bool didAuthenticate = await local_auth.authenticateWithBiometrics( + localizedReason: 'Please authenticate to show account balance'); +} on PlatformException catch (e) { + if (e.code == auth_error.notAvailable) { + // Handle this exception here. + } +} +``` + +## Android integration + +Update your project's `AndroidManifest.xml` file to include the +`USE_FINGERPRINT` permissions: + +``` +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.app"> + <uses-permission android:name="android.permission.USE_FINGERPRINT"/> +<manifest> +``` + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](http://flutter.io/). + +For help on editing plugin code, view the [documentation](https://flutter.io/platform-plugins/#edit-code).
diff --git a/packages/local_auth/android/.gitignore b/packages/local_auth/android/.gitignore new file mode 100644 index 0000000..5c4ef82 --- /dev/null +++ b/packages/local_auth/android/.gitignore
@@ -0,0 +1,12 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures + +/gradle +/gradlew +/gradlew.bat
diff --git a/packages/local_auth/android/build.gradle b/packages/local_auth/android/build.gradle new file mode 100644 index 0000000..f9497c9 --- /dev/null +++ b/packages/local_auth/android/build.gradle
@@ -0,0 +1,39 @@ +group 'io.flutter.plugins.localauth' +version '1.0-SNAPSHOT' + +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:2.3.0' + } +} + +allprojects { + repositories { + jcenter() + maven { + url "https://maven.google.com" + } + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 25 + buildToolsVersion '25.0.3' + + defaultConfig { + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + lintOptions { + disable 'InvalidPackage' + } +} + +dependencies { + compile "com.android.support:support-v4:25.0.0" +} \ No newline at end of file
diff --git a/packages/local_auth/android/gradle.properties b/packages/local_auth/android/gradle.properties new file mode 100644 index 0000000..8bd86f6 --- /dev/null +++ b/packages/local_auth/android/gradle.properties
@@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M
diff --git a/packages/local_auth/android/settings.gradle b/packages/local_auth/android/settings.gradle new file mode 100644 index 0000000..dca8c62 --- /dev/null +++ b/packages/local_auth/android/settings.gradle
@@ -0,0 +1 @@ +rootProject.name = 'local_auth'
diff --git a/packages/local_auth/android/src/main/AndroidManifest.xml b/packages/local_auth/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b0614a1 --- /dev/null +++ b/packages/local_auth/android/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="io.flutter.plugins.localauth" + android:versionCode="1" + android:versionName="0.0.1"> + + <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="25" /> +</manifest>
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 new file mode 100644 index 0000000..d5f26a7 --- /dev/null +++ b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java
@@ -0,0 +1,275 @@ +// Copyright 2017 The Chromium 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 android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Application; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; +import android.support.v4.content.ContextCompat; +import android.support.v4.hardware.fingerprint.FingerprintManagerCompat; +import android.support.v4.os.CancellationSignal; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import io.flutter.plugin.common.MethodCall; + +/** + * Authenticates the user with fingerprint and sends corresponding response back to Flutter. + * + * <p>One instance per call is generated to ensure readable separation of executable paths across + * method calls. + */ +class AuthenticationHelper extends FingerprintManagerCompat.AuthenticationCallback + implements Application.ActivityLifecycleCallbacks { + + /** How long will the fp dialog be delayed to dismiss. */ + private static final long DISMISS_AFTER_MS = 300; + + private static final String CANCEL_BUTTON = "cancelButton"; + + /** Captures the state of the fingerprint dialog. */ + private enum DialogState { + SUCCESS, + FAILURE + } + + /** The callback that handles the result of this authentication process. */ + interface AuthCompletionHandler { + + /** Called when authentication was successful. */ + void onSuccess(); + + /** + * Called when authentication failed due to user. For instance, when user cancels the auth or + * quits the app. + */ + void onFailure(); + + /** + * Called when authentication fails due to non-user related problems such as system errors, + * phone not having a FP reader etc. + * + * @param code The error code to be returned to Flutter app. + * @param error The description of the error. + */ + void onError(String code, String error); + } + + private final Activity activity; + private final AuthCompletionHandler completionHandler; + private final KeyguardManager keyguardManager; + private final FingerprintManagerCompat fingerprintManager; + private final CancellationSignal cancellationSignal; + private final MethodCall call; + + /** + * The prominent UI element during this transaction. It is used to communicate the state of + * authentication to the user. + */ + private AlertDialog fingerprintDialog; + + AuthenticationHelper( + Activity activity, MethodCall call, AuthCompletionHandler completionHandler) { + this.activity = activity; + this.completionHandler = completionHandler; + this.call = call; + this.cancellationSignal = new CancellationSignal(); + this.keyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE); + this.fingerprintManager = FingerprintManagerCompat.from(activity); + } + + void authenticate() { + if (fingerprintManager.isHardwareDetected()) { + if (keyguardManager.isKeyguardSecure() && fingerprintManager.hasEnrolledFingerprints()) { + start(); + } else { + if (call.argument("useErrorDialogs")) { + showGoToSettingsDialog(); + } else if (!keyguardManager.isKeyguardSecure()) { + completionHandler.onError( + "PasscodeNotSet", + "Phone not secured by PIN, pattern or password, or SIM is currently locked."); + } else { + completionHandler.onError("NotEnrolled", "No fingerprint enrolled on this device."); + } + } + } else { + completionHandler.onError("NotAvailable", "Fingerprint is not available on this device."); + } + } + + /** Starts the fingerprint listener and shows the fingerprint dialog. */ + private void start() { + activity.getApplication().registerActivityLifecycleCallbacks(this); + showFingerprintDialog(); + fingerprintManager.authenticate(null, 0, cancellationSignal, this, null); + } + + /** + * Stops the fingerprint listener and dismisses the fingerprint dialog. + * + * @param success If the authentication was successful. + */ + private void stop(boolean success) { + cancellationSignal.cancel(); + if (fingerprintDialog != null && fingerprintDialog.isShowing()) { + fingerprintDialog.dismiss(); + } + activity.getApplication().unregisterActivityLifecycleCallbacks(this); + if (success) { + completionHandler.onSuccess(); + } else { + completionHandler.onFailure(); + } + } + + /** + * If the activity is paused or stopped, we have to stop listening for fingerprint. Otherwise, + * user can still interact with fp reader in the background.. Sigh.. + */ + @Override + public void onActivityPaused(Activity activity) { + stop(false); + } + + @Override + public void onAuthenticationError(int errMsgId, CharSequence errString) { + updateFingerprintDialog(DialogState.FAILURE, errString.toString()); + } + + @Override + public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { + updateFingerprintDialog(DialogState.FAILURE, helpString.toString()); + } + + @Override + public void onAuthenticationFailed() { + updateFingerprintDialog( + DialogState.FAILURE, (String) call.argument("fingerprintNotRecognized")); + } + + @Override + public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) { + updateFingerprintDialog(DialogState.SUCCESS, (String) call.argument("fingerprintSuccess")); + new Handler(Looper.myLooper()) + .postDelayed( + new Runnable() { + @Override + public void run() { + stop(true); + } + }, + DISMISS_AFTER_MS); + } + + private void updateFingerprintDialog(DialogState state, String message) { + if (cancellationSignal.isCanceled() || !fingerprintDialog.isShowing()) { + return; + } + TextView resultInfo = (TextView) fingerprintDialog.findViewById(R.id.fingerprint_status); + ImageView icon = (ImageView) fingerprintDialog.findViewById(R.id.fingerprint_icon); + switch (state) { + case FAILURE: + icon.setImageResource(R.drawable.fingerprint_warning_icon); + resultInfo.setTextColor(ContextCompat.getColor(activity, R.color.warning_color)); + break; + case SUCCESS: + icon.setImageResource(R.drawable.fingerprint_success_icon); + resultInfo.setTextColor(ContextCompat.getColor(activity, R.color.success_color)); + break; + } + resultInfo.setText(message); + } + + // Supress inflateParams lint because dialogs do not need to attach to a parent view. + @SuppressLint("InflateParams") + private void showFingerprintDialog() { + View view = LayoutInflater.from(activity).inflate(R.layout.scan_fp, null, false); + TextView fpDescription = (TextView) view.findViewById(R.id.fingerprint_description); + TextView title = (TextView) view.findViewById(R.id.fingerprint_signin); + TextView status = (TextView) view.findViewById(R.id.fingerprint_status); + fpDescription.setText((String) call.argument("localizedReason")); + title.setText((String) call.argument("signInTitle")); + status.setText((String) call.argument("fingerprintHint")); + Context context = new ContextThemeWrapper(activity, R.style.AlertDialogCustom); + OnClickListener cancelHandler = + new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + stop(false); + } + }; + fingerprintDialog = + new AlertDialog.Builder(context) + .setView(view) + .setNegativeButton((String) call.argument(CANCEL_BUTTON), cancelHandler) + .setCancelable(false) + .show(); + } + + // Supress inflateParams lint because dialogs do not need to attach to a parent view. + @SuppressLint("InflateParams") + private void showGoToSettingsDialog() { + View view = LayoutInflater.from(activity).inflate(R.layout.go_to_setting, null, false); + TextView message = (TextView) view.findViewById(R.id.fingerprint_required); + TextView description = (TextView) view.findViewById(R.id.go_to_setting_description); + message.setText((String) call.argument("fingerprintRequired")); + description.setText((String) call.argument("goToSettingDescription")); + Context context = new ContextThemeWrapper(activity, R.style.AlertDialogCustom); + OnClickListener goToSettingHandler = + new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + activity.startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS)); + stop(false); + } + }; + OnClickListener cancelHandler = + new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + stop(false); + } + }; + new AlertDialog.Builder(context) + .setView(view) + .setPositiveButton((String) call.argument("goToSetting"), goToSettingHandler) + .setNegativeButton((String) call.argument(CANCEL_BUTTON), cancelHandler) + .setCancelable(false) + .show(); + } + + // Unused methods for activity lifecycle. + + @Override + public void onActivityCreated(Activity activity, Bundle bundle) {} + + @Override + public void onActivityStarted(Activity activity) {} + + @Override + public void onActivityResumed(Activity activity) {} + + @Override + public void onActivityStopped(Activity activity) {} + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {} + + @Override + public void onActivityDestroyed(Activity activity) {} +}
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 new file mode 100644 index 0000000..4212487 --- /dev/null +++ b/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java
@@ -0,0 +1,71 @@ +// Copyright 2017 The Chromium 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 android.app.Activity; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugin.common.PluginRegistry.Registrar; +import io.flutter.plugins.localauth.AuthenticationHelper.AuthCompletionHandler; +import java.util.concurrent.atomic.AtomicBoolean; + +/** LocalAuthPlugin */ +public class LocalAuthPlugin implements MethodCallHandler { + private final Activity activity; + private final AtomicBoolean authInProgress = new AtomicBoolean(false); + + /** Plugin registration. */ + public static void registerWith(Registrar registrar) { + final MethodChannel channel = + new MethodChannel(registrar.messenger(), "plugins.flutter.io/local_auth"); + channel.setMethodCallHandler(new LocalAuthPlugin(registrar.activity())); + } + + private LocalAuthPlugin(Activity activity) { + this.activity = activity; + } + + @Override + public void onMethodCall(MethodCall call, final Result result) { + if (call.method.equals("authenticateWithBiometrics")) { + if (!authInProgress.compareAndSet(false, true)) { + // 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 + // the complexity. + result.error("auth_in_progress", "Authentication in progress", null); + return; + } + AuthenticationHelper authenticationHelper = + new AuthenticationHelper( + activity, + call, + new AuthCompletionHandler() { + @Override + public void onSuccess() { + result.success(true); + authInProgress.set(false); + } + + @Override + public void onFailure() { + result.success(false); + authInProgress.set(false); + } + + @Override + public void onError(String code, String error) { + result.error(code, error, null); + authInProgress.set(false); + } + }); + authenticationHelper.authenticate(); + } else { + result.notImplemented(); + } + } +}
diff --git a/packages/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml b/packages/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml new file mode 100644 index 0000000..610f7cd --- /dev/null +++ b/packages/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml
@@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape android:shape="oval"> + <solid android:color="#607D8B"/> + <size + android:width="40dp" + android:height="40dp"/> + </shape> + </item> + <item android:drawable="@drawable/ic_fingerprint_white_24dp" + android:bottom="8dp" + android:left="8dp" + android:right="8dp" + android:top="8dp"/> +</layer-list>
diff --git a/packages/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml b/packages/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml new file mode 100644 index 0000000..78e42a8 --- /dev/null +++ b/packages/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml
@@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape android:shape="oval"> + <solid android:color="@color/success_color"/> + <size + android:width="40dp" + android:height="40dp"/> + </shape> + </item> + <item android:drawable="@drawable/ic_done_white_24dp" + android:bottom="8dp" + android:left="8dp" + android:right="8dp" + android:top="8dp"/> +</layer-list>
diff --git a/packages/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml b/packages/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml new file mode 100644 index 0000000..5020d06 --- /dev/null +++ b/packages/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml
@@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape android:shape="oval"> + <solid android:color="@color/warning_color"/> + <size + android:width="40dp" + android:height="40dp"/> + </shape> + </item> + <item android:drawable="@drawable/ic_priority_high_white_24dp" + android:bottom="8dp" + android:left="8dp" + android:right="8dp" + android:top="8dp"/> +</layer-list>
diff --git a/packages/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml b/packages/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml new file mode 100644 index 0000000..99caef9 --- /dev/null +++ b/packages/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml
@@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/> +</vector>
diff --git a/packages/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml b/packages/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml new file mode 100644 index 0000000..42d8eef --- /dev/null +++ b/packages/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml
@@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M17.81,4.47c-0.08,0 -0.16,-0.02 -0.23,-0.06C15.66,3.42 14,3 12.01,3c-1.98,0 -3.86,0.47 -5.57,1.41 -0.24,0.13 -0.54,0.04 -0.68,-0.2 -0.13,-0.24 -0.04,-0.55 0.2,-0.68C7.82,2.52 9.86,2 12.01,2c2.13,0 3.99,0.47 6.03,1.52 0.25,0.13 0.34,0.43 0.21,0.67 -0.09,0.18 -0.26,0.28 -0.44,0.28zM3.5,9.72c-0.1,0 -0.2,-0.03 -0.29,-0.09 -0.23,-0.16 -0.28,-0.47 -0.12,-0.7 0.99,-1.4 2.25,-2.5 3.75,-3.27C9.98,4.04 14,4.03 17.15,5.65c1.5,0.77 2.76,1.86 3.75,3.25 0.16,0.22 0.11,0.54 -0.12,0.7 -0.23,0.16 -0.54,0.11 -0.7,-0.12 -0.9,-1.26 -2.04,-2.25 -3.39,-2.94 -2.87,-1.47 -6.54,-1.47 -9.4,0.01 -1.36,0.7 -2.5,1.7 -3.4,2.96 -0.08,0.14 -0.23,0.21 -0.39,0.21zM9.75,21.79c-0.13,0 -0.26,-0.05 -0.35,-0.15 -0.87,-0.87 -1.34,-1.43 -2.01,-2.64 -0.69,-1.23 -1.05,-2.73 -1.05,-4.34 0,-2.97 2.54,-5.39 5.66,-5.39s5.66,2.42 5.66,5.39c0,0.28 -0.22,0.5 -0.5,0.5s-0.5,-0.22 -0.5,-0.5c0,-2.42 -2.09,-4.39 -4.66,-4.39 -2.57,0 -4.66,1.97 -4.66,4.39 0,1.44 0.32,2.77 0.93,3.85 0.64,1.15 1.08,1.64 1.85,2.42 0.19,0.2 0.19,0.51 0,0.71 -0.11,0.1 -0.24,0.15 -0.37,0.15zM16.92,19.94c-1.19,0 -2.24,-0.3 -3.1,-0.89 -1.49,-1.01 -2.38,-2.65 -2.38,-4.39 0,-0.28 0.22,-0.5 0.5,-0.5s0.5,0.22 0.5,0.5c0,1.41 0.72,2.74 1.94,3.56 0.71,0.48 1.54,0.71 2.54,0.71 0.24,0 0.64,-0.03 1.04,-0.1 0.27,-0.05 0.53,0.13 0.58,0.41 0.05,0.27 -0.13,0.53 -0.41,0.58 -0.57,0.11 -1.07,0.12 -1.21,0.12zM14.91,22c-0.04,0 -0.09,-0.01 -0.13,-0.02 -1.59,-0.44 -2.63,-1.03 -3.72,-2.1 -1.4,-1.39 -2.17,-3.24 -2.17,-5.22 0,-1.62 1.38,-2.94 3.08,-2.94 1.7,0 3.08,1.32 3.08,2.94 0,1.07 0.93,1.94 2.08,1.94s2.08,-0.87 2.08,-1.94c0,-3.77 -3.25,-6.83 -7.25,-6.83 -2.84,0 -5.44,1.58 -6.61,4.03 -0.39,0.81 -0.59,1.76 -0.59,2.8 0,0.78 0.07,2.01 0.67,3.61 0.1,0.26 -0.03,0.55 -0.29,0.64 -0.26,0.1 -0.55,-0.04 -0.64,-0.29 -0.49,-1.31 -0.73,-2.61 -0.73,-3.96 0,-1.2 0.23,-2.29 0.68,-3.24 1.33,-2.79 4.28,-4.6 7.51,-4.6 4.55,0 8.25,3.51 8.25,7.83 0,1.62 -1.38,2.94 -3.08,2.94s-3.08,-1.32 -3.08,-2.94c0,-1.07 -0.93,-1.94 -2.08,-1.94s-2.08,0.87 -2.08,1.94c0,1.71 0.66,3.31 1.87,4.51 0.95,0.94 1.86,1.46 3.27,1.85 0.27,0.07 0.42,0.35 0.35,0.61 -0.05,0.23 -0.26,0.38 -0.47,0.38z"/> +</vector>
diff --git a/packages/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml b/packages/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml new file mode 100644 index 0000000..f32d4a4 --- /dev/null +++ b/packages/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml
@@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12,19m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M10,3h4v12h-4z"/> +</vector>
diff --git a/packages/local_auth/android/src/main/res/layout/go_to_setting.xml b/packages/local_auth/android/src/main/res/layout/go_to_setting.xml new file mode 100644 index 0000000..8c932d4 --- /dev/null +++ b/packages/local_auth/android/src/main/res/layout/go_to_setting.xml
@@ -0,0 +1,26 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingLeft="24dp" + android:paddingRight="24dp" + android:gravity="center_vertical" + android:orientation="vertical"> + <TextView + android:id="@+id/fingerprint_required" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="24dp" + android:paddingBottom="20dp" + android:gravity="center_vertical" + android:textColor="@color/black_text" + style="@android:style/TextAppearance.DeviceDefault.Medium" + android:textSize="@dimen/huge_text_size"/> + <TextView + android:id="@+id/go_to_setting_description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingBottom="28dp" + android:textColor="@color/grey_text" + android:textStyle="normal" + android:textSize="@dimen/medium_text_size"/> +</LinearLayout>
diff --git a/packages/local_auth/android/src/main/res/layout/scan_fp.xml b/packages/local_auth/android/src/main/res/layout/scan_fp.xml new file mode 100644 index 0000000..a99dd6a --- /dev/null +++ b/packages/local_auth/android/src/main/res/layout/scan_fp.xml
@@ -0,0 +1,47 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingLeft="24dp" + android:paddingRight="24dp" + android:gravity="center_vertical" + android:orientation="vertical"> + <TextView + android:id="@+id/fingerprint_signin" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="24dp" + android:paddingBottom="20dp" + android:gravity="center_vertical" + android:textColor="@color/black_text" + style="@android:style/TextAppearance.DeviceDefault.Medium" + android:textSize="@dimen/huge_text_size"/> + <TextView + android:id="@+id/fingerprint_description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingBottom="28dp" + android:textColor="@color/grey_text" + android:textStyle="normal" + android:textSize="@dimen/medium_text_size"/> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <ImageView + android:id="@+id/fingerprint_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/fingerprint_initial_icon"/> + <TextView + android:id="@+id/fingerprint_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingStart="16dp" + android:paddingEnd="0dp" + android:paddingLeft="16dp" + android:paddingRight="0dp" + android:paddingTop="12dp" + android:paddingBottom="12dp" + android:textColor="@color/hint_color" /> + </LinearLayout> +</LinearLayout>
diff --git a/packages/local_auth/android/src/main/res/values/colors.xml b/packages/local_auth/android/src/main/res/values/colors.xml new file mode 100644 index 0000000..c011fe3 --- /dev/null +++ b/packages/local_auth/android/src/main/res/values/colors.xml
@@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Fingerprint colors --> + <color name="warning_color">#E53935</color> + <color name="hint_color">#BDBDBD</color> + <color name="success_color">#43A047</color> + <color name="black_text">#212121</color> + <color name="grey_text">#757575</color> +</resources>
diff --git a/packages/local_auth/android/src/main/res/values/dimens.xml b/packages/local_auth/android/src/main/res/values/dimens.xml new file mode 100644 index 0000000..678faeb --- /dev/null +++ b/packages/local_auth/android/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@ +<resources> + <dimen name="body_text_size">14sp</dimen> + <dimen name="medium_text_size">16sp</dimen> + <dimen name="huge_text_size">20sp</dimen> +</resources>
diff --git a/packages/local_auth/android/src/main/res/values/styles.xml b/packages/local_auth/android/src/main/res/values/styles.xml new file mode 100644 index 0000000..7a0719f --- /dev/null +++ b/packages/local_auth/android/src/main/res/values/styles.xml
@@ -0,0 +1,9 @@ +<resources> + <!-- Fingerprint dialog theme. --> + <style name="AlertDialogCustom" parent="@android:style/Theme.Material.Dialog.Alert"> + <item name="android:background">#FFFFFFFF</item> + <item name="android:textStyle">bold</item> + <item name="android:textSize">14sp</item> + <item name="android:colorAccent">#FF009688</item> + </style> +</resources>
diff --git a/packages/local_auth/example/.gitignore b/packages/local_auth/example/.gitignore new file mode 100644 index 0000000..eb15c3d --- /dev/null +++ b/packages/local_auth/example/.gitignore
@@ -0,0 +1,10 @@ +.DS_Store +.atom/ +.idea +.packages +.pub/ +build/ +ios/.generated/ +packages +pubspec.lock +.flutter-plugins
diff --git a/packages/local_auth/example/README.md b/packages/local_auth/example/README.md new file mode 100644 index 0000000..da6cf0c --- /dev/null +++ b/packages/local_auth/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](http://flutter.io/).
diff --git a/packages/local_auth/example/android.iml b/packages/local_auth/example/android.iml new file mode 100644 index 0000000..462b903 --- /dev/null +++ b/packages/local_auth/example/android.iml
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$/android"> + <sourceFolder url="file://$MODULE_DIR$/android/app/src/main/java" isTestSource="false" /> + </content> + <orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Flutter for Android" level="project" /> + </component> +</module>
diff --git a/packages/local_auth/example/android/.gitignore b/packages/local_auth/example/android/.gitignore new file mode 100644 index 0000000..1fd9325 --- /dev/null +++ b/packages/local_auth/example/android/.gitignore
@@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +GeneratedPluginRegistrant.java + +/gradle +/gradlew +/gradlew.bat
diff --git a/packages/local_auth/example/android/app/build.gradle b/packages/local_auth/example/android/app/build.gradle new file mode 100644 index 0000000..46e88a1 --- /dev/null +++ b/packages/local_auth/example/android/app/build.gradle
@@ -0,0 +1,45 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withInputStream { stream -> + localProperties.load(stream) + } +} + +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.") +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 25 + buildToolsVersion '25.0.3' + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + applicationId "io.flutter.plugins.localauthexample" + } + + buildTypes { + release { + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + androidTestCompile 'com.android.support:support-annotations:25.4.0' + androidTestCompile 'com.android.support.test:runner:0.5' + androidTestCompile 'com.android.support.test:rules:0.5' +}
diff --git a/packages/local_auth/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..71c48a9 --- /dev/null +++ b/packages/local_auth/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="io.flutter.plugins.localauthexample" + android:versionCode="1" + android:versionName="0.0.1"> + + <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="21" /> + + <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.USE_FINGERPRINT"/> + + <application android:name="io.flutter.app.FlutterApplication" android:label="local_auth_example" android:icon="@mipmap/ic_launcher"> + <activity android:name=".MainActivity" + android:launchMode="singleTop" + android:theme="@android:style/Theme.Black.NoTitleBar" + 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> + </application> +</manifest>
diff --git a/packages/local_auth/example/android/app/src/main/java/io/flutter/plugins/localauthexample/MainActivity.java b/packages/local_auth/example/android/app/src/main/java/io/flutter/plugins/localauthexample/MainActivity.java new file mode 100644 index 0000000..b9a2e11 --- /dev/null +++ b/packages/local_auth/example/android/app/src/main/java/io/flutter/plugins/localauthexample/MainActivity.java
@@ -0,0 +1,17 @@ +// Copyright 2017 The Chromium 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.localauthexample; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class MainActivity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +}
diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/local_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 --- /dev/null +++ b/packages/local_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png Binary files differ
diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/local_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 --- /dev/null +++ b/packages/local_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differ
diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/local_auth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 --- /dev/null +++ b/packages/local_auth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differ
diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/local_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d --- /dev/null +++ b/packages/local_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differ
diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/local_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e --- /dev/null +++ b/packages/local_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png Binary files differ
diff --git a/packages/local_auth/example/android/build.gradle b/packages/local_auth/example/android/build.gradle new file mode 100644 index 0000000..f5004b9 --- /dev/null +++ b/packages/local_auth/example/android/build.gradle
@@ -0,0 +1,32 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:2.2.3' + } +} + +allprojects { + repositories { + jcenter() + maven { + url "https://maven.google.com" + } + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + +task wrapper(type: Wrapper) { + gradleVersion = '2.14.1' +}
diff --git a/packages/local_auth/example/android/gradle.properties b/packages/local_auth/example/android/gradle.properties new file mode 100644 index 0000000..8bd86f6 --- /dev/null +++ b/packages/local_auth/example/android/gradle.properties
@@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M
diff --git a/packages/local_auth/example/android/settings.gradle b/packages/local_auth/example/android/settings.gradle new file mode 100644 index 0000000..115da6c --- /dev/null +++ b/packages/local_auth/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/example/ios/.gitignore b/packages/local_auth/example/ios/.gitignore new file mode 100644 index 0000000..38864ee --- /dev/null +++ b/packages/local_auth/example/ios/.gitignore
@@ -0,0 +1,41 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/app.flx +/Flutter/app.zip +/Flutter/App.framework +/Flutter/Flutter.framework +/Flutter/Generated.xcconfig +/ServiceDefinitions.json + +Pods/
diff --git a/packages/local_auth/example/ios/Flutter/AppFrameworkInfo.plist b/packages/local_auth/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..6c2de80 --- /dev/null +++ b/packages/local_auth/example/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>App</string> + <key>CFBundleIdentifier</key> + <string>io.flutter.flutter.app</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>App</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>arm64</string> + </array> + <key>MinimumOSVersion</key> + <string>8.0</string> +</dict> +</plist>
diff --git a/packages/local_auth/example/ios/Flutter/Debug.xcconfig b/packages/local_auth/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/packages/local_auth/example/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@ +#include "Generated.xcconfig"
diff --git a/packages/local_auth/example/ios/Flutter/Release.xcconfig b/packages/local_auth/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/packages/local_auth/example/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@ +#include "Generated.xcconfig"
diff --git a/packages/local_auth/example/ios/Podfile b/packages/local_auth/example/ios/Podfile new file mode 100644 index 0000000..90b5f65 --- /dev/null +++ b/packages/local_auth/example/ios/Podfile
@@ -0,0 +1,36 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +if ENV['FLUTTER_FRAMEWORK_DIR'] == nil + abort('Please set FLUTTER_FRAMEWORK_DIR to the directory containing Flutter.framework') +end + +target 'Runner' do + # Pods for Runner + + # Flutter Pods + pod 'Flutter', :path => ENV['FLUTTER_FRAMEWORK_DIR'] + + if File.exists? '../.flutter-plugins' + flutter_root = File.expand_path('..') + File.foreach('../.flutter-plugins') { |line| + plugin = line.split(pattern='=') + if plugin.length == 2 + name = plugin[0].strip() + path = plugin[1].strip() + resolved_path = File.expand_path("#{path}/ios", flutter_root) + pod name, :path => resolved_path + else + puts "Invalid plugin specification: #{line}" + end + } + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['ENABLE_BITCODE'] = 'NO' + end + end +end
diff --git a/packages/local_auth/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..3082933 --- /dev/null +++ b/packages/local_auth/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,494 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; + 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; + 9740EEBB1CF902C7004384FC /* app.flx in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB71CF902C7004384FC /* app.flx */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; + 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; + 9740EEB71CF902C7004384FC /* app.flx */ = {isa = PBXFileReference; lastKnownFileType = file; name = app.flx; path = Flutter/app.flx; sourceTree = "<group>"; }; + 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 9740EEB71CF902C7004384FC /* app.flx */, + 3B80C3931E831B6300D905FE /* App.framework */, + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEBA1CF902C7004384FC /* Flutter.framework */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = "<group>"; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + F8CC53B854B121315C7319D2 /* Pods */, + E2D5FA899A019BD3E0DB0917 /* Frameworks */, + ); + sourceTree = "<group>"; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = "<group>"; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = "<group>"; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = "<group>"; + }; + E2D5FA899A019BD3E0DB0917 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + F8CC53B854B121315C7319D2 /* Pods */ = { + isa = PBXGroup; + children = ( + ); + name = Pods; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 98D96A2D1A74AF66E3DD2DBC /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 16CF73924D0A9C13B2100A83 /* [CP] Embed Pods Frameworks */, + A87A71C8D647A16C94C64B4D /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "The Chromium Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = JSJA5AH6K6; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9740EEBB1CF902C7004384FC /* app.flx in Resources */, + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 16CF73924D0A9C13B2100A83 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + 98D96A2D1A74AF66E3DD2DBC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + A87A71C8D647A16C94C64B4D /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = "<group>"; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = JSJA5AH6K6; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.localAuthExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ARCHS = arm64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = JSJA5AH6K6; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.localAuthExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +}
diff --git a/packages/local_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/local_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/packages/local_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "group:Runner.xcodeproj"> + </FileRef> +</Workspace>
diff --git a/packages/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..1c95807 --- /dev/null +++ b/packages/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0830" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "97C146ED1CF9000F007C117D" + BuildableName = "Runner.app" + BlueprintName = "Runner" + ReferencedContainer = "container:Runner.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "97C146ED1CF9000F007C117D" + BuildableName = "Runner.app" + BlueprintName = "Runner" + ReferencedContainer = "container:Runner.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "97C146ED1CF9000F007C117D" + BuildableName = "Runner.app" + BlueprintName = "Runner" + ReferencedContainer = "container:Runner.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "97C146ED1CF9000F007C117D" + BuildableName = "Runner.app" + BlueprintName = "Runner" + ReferencedContainer = "container:Runner.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme>
diff --git a/packages/local_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/local_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/packages/local_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "group:Runner.xcodeproj"> + </FileRef> + <FileRef + location = "group:Pods/Pods.xcodeproj"> + </FileRef> +</Workspace>
diff --git a/packages/local_auth/example/ios/Runner/AppDelegate.h b/packages/local_auth/example/ios/Runner/AppDelegate.h new file mode 100644 index 0000000..d9e18e9 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/AppDelegate.h
@@ -0,0 +1,10 @@ +// Copyright 2017 The Chromium 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 <Flutter/Flutter.h> +#import <UIKit/UIKit.h> + +@interface AppDelegate : FlutterAppDelegate + +@end
diff --git a/packages/local_auth/example/ios/Runner/AppDelegate.m b/packages/local_auth/example/ios/Runner/AppDelegate.m new file mode 100644 index 0000000..f086757 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/AppDelegate.m
@@ -0,0 +1,17 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d22f10b --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png Binary files differ
diff --git a/packages/local_auth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/local_auth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..ebf48f6 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM"> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> + </dependencies> + <scenes> + <!--View Controller--> + <scene sceneID="EHf-IW-A2E"> + <objects> + <viewController id="01J-lp-oVM" sceneMemberID="viewController"> + <layoutGuides> + <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/> + <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/> + </layoutGuides> + <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> + <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> + </view> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="53" y="375"/> + </scene> + </scenes> +</document>
diff --git a/packages/local_auth/example/ios/Runner/Base.lproj/Main.storyboard b/packages/local_auth/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> + </dependencies> + <scenes> + <!--Flutter View Controller--> + <scene sceneID="tne-QT-ifu"> + <objects> + <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController"> + <layoutGuides> + <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> + <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> + </layoutGuides> + <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> + <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> + </view> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> + </objects> + </scene> + </scenes> +</document>
diff --git a/packages/local_auth/example/ios/Runner/Info.plist b/packages/local_auth/example/ios/Runner/Info.plist new file mode 100644 index 0000000..206f43c --- /dev/null +++ b/packages/local_auth/example/ios/Runner/Info.plist
@@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>local_auth_example</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>UILaunchStoryboardName</key> + <string>LaunchScreen</string> + <key>UIMainStoryboardFile</key> + <string>Main</string> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>arm64</string> + </array> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>UISupportedInterfaceOrientations~ipad</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>UIViewControllerBasedStatusBarAppearance</key> + <false/> +</dict> +</plist>
diff --git a/packages/local_auth/example/ios/Runner/main.m b/packages/local_auth/example/ios/Runner/main.m new file mode 100644 index 0000000..dff6597 --- /dev/null +++ b/packages/local_auth/example/ios/Runner/main.m
@@ -0,0 +1,9 @@ +#import <Flutter/Flutter.h> +#import <UIKit/UIKit.h> +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +}
diff --git a/packages/local_auth/example/lib/main.dart b/packages/local_auth/example/lib/main.dart new file mode 100644 index 0000000..e5e3a9e --- /dev/null +++ b/packages/local_auth/example/lib/main.dart
@@ -0,0 +1,59 @@ +// Copyright 2017 The Chromium 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/material.dart'; +import 'package:flutter/services.dart'; +import 'package:local_auth/local_auth.dart'; + +void main() { + runApp(new MyApp()); +} + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => new _MyAppState(); +} + +class _MyAppState extends State<MyApp> { + String _authorized = 'Not Authorized'; + + Future<Null> _authenticate() async { + final LocalAuthentication auth = new LocalAuthentication(); + bool authenticated = false; + try { + authenticated = await auth.authenticateWithBiometrics( + localizedReason: 'Scan your fingerprint to authenticate', + useErrorDialogs: true); + } on PlatformException catch (e) { + print(e); + } + if (!mounted) return; + + setState(() { + _authorized = authenticated ? 'Authorized' : 'Not Authorized'; + }); + } + + @override + Widget build(BuildContext context) { + return new MaterialApp( + home: new Scaffold( + appBar: new AppBar( + title: const Text('Plugin example app'), + ), + body: new ConstrainedBox( + constraints: const BoxConstraints.expand(), + child: new Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: <Widget>[ + new Text('Current State: $_authorized\n'), + new RaisedButton( + child: const Text('Authenticate'), + onPressed: _authenticate, + ) + ])), + )); + } +}
diff --git a/packages/local_auth/example/local_auth_example.iml b/packages/local_auth/example/local_auth_example.iml new file mode 100644 index 0000000..9d5dae1 --- /dev/null +++ b/packages/local_auth/example/local_auth_example.iml
@@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="FLUTTER_MODULE_TYPE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <excludeFolder url="file://$MODULE_DIR$/.idea" /> + <excludeFolder url="file://$MODULE_DIR$/.pub" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/packages" /> + </content> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Dart Packages" level="project" /> + <orderEntry type="library" name="Dart SDK" level="project" /> + </component> +</module> \ No newline at end of file
diff --git a/packages/local_auth/example/local_auth_example_android.iml b/packages/local_auth/example/local_auth_example_android.iml new file mode 100644 index 0000000..462b903 --- /dev/null +++ b/packages/local_auth/example/local_auth_example_android.iml
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$/android"> + <sourceFolder url="file://$MODULE_DIR$/android/app/src/main/java" isTestSource="false" /> + </content> + <orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Flutter for Android" level="project" /> + </component> +</module>
diff --git a/packages/local_auth/example/pubspec.yaml b/packages/local_auth/example/pubspec.yaml new file mode 100644 index 0000000..0bdaee6 --- /dev/null +++ b/packages/local_auth/example/pubspec.yaml
@@ -0,0 +1,11 @@ +name: local_auth_example +description: Demonstrates how to use the local_auth plugin. + +dependencies: + flutter: + sdk: flutter + local_auth: + path: ../ + +flutter: + uses-material-design: true
diff --git a/packages/local_auth/ios/.gitignore b/packages/local_auth/ios/.gitignore new file mode 100644 index 0000000..956c87f --- /dev/null +++ b/packages/local_auth/ios/.gitignore
@@ -0,0 +1,31 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* +
diff --git a/packages/local_auth/ios/Assets/.gitkeep b/packages/local_auth/ios/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/packages/local_auth/ios/Assets/.gitkeep
diff --git a/packages/local_auth/ios/Classes/LocalAuthPlugin.h b/packages/local_auth/ios/Classes/LocalAuthPlugin.h new file mode 100644 index 0000000..2f9b123 --- /dev/null +++ b/packages/local_auth/ios/Classes/LocalAuthPlugin.h
@@ -0,0 +1,8 @@ +// Copyright 2017 The Chromium 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 <Flutter/Flutter.h> + +@interface LocalAuthPlugin : NSObject<FlutterPlugin> +@end
diff --git a/packages/local_auth/ios/Classes/LocalAuthPlugin.m b/packages/local_auth/ios/Classes/LocalAuthPlugin.m new file mode 100644 index 0000000..7cbb389 --- /dev/null +++ b/packages/local_auth/ios/Classes/LocalAuthPlugin.m
@@ -0,0 +1,121 @@ +// Copyright 2017 The Chromium 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 <LocalAuthentication/LocalAuthentication.h> + +#import "LocalAuthPlugin.h" + +@implementation LocalAuthPlugin ++ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar { + FlutterMethodChannel *channel = + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/local_auth" + binaryMessenger:[registrar messenger]]; + LocalAuthPlugin *instance = [[LocalAuthPlugin alloc] init]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { + if ([@"authenticateWithBiometrics" isEqualToString:call.method]) { + [self authenticateWithBiometrics:call.arguments withFlutterResult:result]; + } else { + result(FlutterMethodNotImplemented); + } +} + +#pragma mark Private Methods + +- (void)alertMessage:(NSString *)message + firstButton:(NSString *)firstButton + flutterResult:(FlutterResult)result + additionalButton:(NSString *)secondButton { + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:@"" + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:firstButton + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + result(@NO); + }]; + + [alert addAction:defaultAction]; + if (secondButton != nil) { + UIAlertAction *additionalAction = [UIAlertAction + actionWithTitle:secondButton + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + if (&UIApplicationOpenSettingsURLString != NULL) { + NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; + [[UIApplication sharedApplication] openURL:url]; + result(@NO); + } + }]; + [alert addAction:additionalAction]; + } + [[UIApplication sharedApplication].delegate.window.rootViewController presentViewController:alert + animated:YES + completion:nil]; +} + +- (void)authenticateWithBiometrics:(NSDictionary *)arguments + withFlutterResult:(FlutterResult)result { + LAContext *context = [[LAContext alloc] init]; + NSError *authError = nil; + context.localizedFallbackTitle = @""; + + if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics + error:&authError]) { + [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics + localizedReason:arguments[@"localizedReason"] + reply:^(BOOL success, NSError *error) { + if (success) { + result(@YES); + } else { + switch (error.code) { + case LAErrorPasscodeNotSet: + case LAErrorTouchIDNotAvailable: + case LAErrorTouchIDNotEnrolled: + case LAErrorTouchIDLockout: + [self handleErrors:error + flutterArguments:arguments + withFlutterResult:result]; + return; + } + result(@NO); + } + }]; + } else { + [self handleErrors:authError flutterArguments:arguments withFlutterResult:result]; + } +} + +- (void)handleErrors:(NSError *)authError + flutterArguments:(NSDictionary *)arguments + withFlutterResult:(FlutterResult)result { + NSString *errorCode = @"NotAvailable"; + switch (authError.code) { + case LAErrorPasscodeNotSet: + case LAErrorTouchIDNotEnrolled: + if (arguments[@"useErrorDialogs"]) { + [self alertMessage:arguments[@"goToSettingDescriptionIOS"] + firstButton:arguments[@"okButton"] + flutterResult:result + additionalButton:arguments[@"goToSetting"]]; + return; + } + errorCode = authError.code == LAErrorPasscodeNotSet ? @"PasscodeNotSet" : @"NotEnrolled"; + break; + case LAErrorTouchIDLockout: + [self alertMessage:arguments[@"lockOut"] + firstButton:arguments[@"okButton"] + flutterResult:result + additionalButton:nil]; + return; + } + result([FlutterError errorWithCode:errorCode + message:authError.localizedDescription + details:authError.domain]); +} + +@end
diff --git a/packages/local_auth/ios/local_auth.podspec b/packages/local_auth/ios/local_auth.podspec new file mode 100644 index 0000000..727da45 --- /dev/null +++ b/packages/local_auth/ios/local_auth.podspec
@@ -0,0 +1,21 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'local_auth' + s.version = '0.0.1' + s.summary = 'A new flutter plugin project.' + s.description = <<-DESC +A new flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + + s.ios.deployment_target = '8.0' +end +
diff --git a/packages/local_auth/lib/auth_strings.dart b/packages/local_auth/lib/auth_strings.dart new file mode 100644 index 0000000..8946f57 --- /dev/null +++ b/packages/local_auth/lib/auth_strings.dart
@@ -0,0 +1,130 @@ +// Copyright 2017 The Chromium 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:intl/intl.dart'; + +/// Android side authentication messages. +/// +/// Provides default values for all messages. +class AndroidAuthMessages { + final String fingerprintHint; + final String fingerprintNotRecognized; + final String fingerprintSuccess; + final String cancelButton; + final String signInTitle; + final String fingerprintRequiredTitle; + final String goToSettingsButton; + final String goToSettingsDescription; + + const AndroidAuthMessages({ + this.fingerprintHint, + this.fingerprintNotRecognized, + this.fingerprintSuccess, + this.cancelButton, + this.signInTitle, + this.fingerprintRequiredTitle, + this.goToSettingsButton, + this.goToSettingsDescription, + }); + + Map<String, String> get args { + return <String, String>{ + 'fingerprintHint': fingerprintHint ?? androidFingerprintHint, + 'fingerprintNotRecognized': + fingerprintNotRecognized ?? androidFingerprintNotRecognized, + 'fingerprintSuccess': fingerprintSuccess ?? androidFingerprintSuccess, + 'cancelButton': cancelButton ?? androidCancelButton, + 'signInTitle': signInTitle ?? androidSignInTitle, + 'fingerprintRequired': + fingerprintRequiredTitle ?? androidFingerprintRequiredTitle, + 'goToSetting': goToSettingsButton ?? goToSettings, + 'goToSettingDescription': + goToSettingsDescription ?? androidGoToSettingsDescription, + }; + } +} + +/// iOS side authentication messages. +/// +/// Provides default values for all messages. +class IOSAuthMessages { + final String lockOut; + final String goToSettingsButton; + final String goToSettingsDescription; + final String cancelButton; + + const IOSAuthMessages({ + this.lockOut, + this.goToSettingsButton, + this.goToSettingsDescription, + this.cancelButton, + }); + + Map<String, String> get args { + return <String, String>{ + 'lockOut': lockOut ?? iOSLockOut, + 'goToSetting': goToSettingsButton ?? goToSettings, + 'goToSettingDescriptionIOS': + goToSettingsDescription ?? iOSGoToSettingsDescription, + 'okButton': cancelButton ?? iOSOkButton, + }; + } +} + +// Strings for local_authentication plugin. Currently supports English. +// Intl.message must be string literals. +String get androidFingerprintHint => Intl.message('Touch sensor', + desc: 'Hint message advising the user how to scan their fingerprint. It is ' + 'used on Android side. Maximum 60 characters.'); + +String get androidFingerprintNotRecognized => + Intl.message('Fingerprint not recognized. Try again.', + desc: 'Message to let the user know that authentication was failed. It ' + 'is used on Android side. Maximum 60 characters.'); + +String get androidFingerprintSuccess => Intl.message('Fingerprint recognized.', + desc: 'Message to let the user know that authentication was successful. It ' + 'is used on Android side. Maximum 60 characters.'); + +String get androidCancelButton => Intl.message('Cancel', + desc: 'Message showed on a button that the user can click to leave the ' + 'current dialog. It is used on Andorid side. Maxium 30 characters.'); + +String get androidSignInTitle => Intl.message('Fingerprint Authentication', + desc: 'Message showed as a title in a dialog which indicates the user ' + 'that they need to scan fingerprint to continue. It is used on ' + 'Android side. Maximum 60 characters.'); + +String get androidFingerprintRequiredTitle { + return Intl.message('Fingerprint required', + desc: 'Message showed as a title in a dialog which indicates the user ' + 'fingerprint is not set up yet on their device. It is used on Android' + ' side. Maximum 60 characters.'); +} + +String get goToSettings => Intl.message('Go to settings', + desc: 'Message showed on a button that the user can click to go to ' + 'settings pages from the current dialog. It is used on both Android ' + 'and iOS side. Maximum 30 characters.'); + +String get androidGoToSettingsDescription => Intl.message( + 'Fingerprint is not set up on your device. Go to ' + '\'Settings > Security\' to add your fingerprint.', + desc: 'Message advising the user to go to the settings and configure ' + 'fingerprint on their device. It shows in a dialog on Android side.'); + +String get iOSLockOut => Intl.message( + 'Touch ID is disabled. Please lock and unlock your screen to enable it.', + desc: 'Message advising the user to re-enable Touch ID on their device. It ' + 'shows in a dialog on iOS side.'); + +String get iOSGoToSettingsDescription => Intl.message( + 'Touch ID is not set up on your device. Go to \'Settings > Touch ID & ' + 'Passcode\' to add your fingerprint.', + desc: 'Message advising the user to go to the settings and configure Touch ' + 'ID on their device. It shows in a dialog on iOS side.'); + +String get iOSOkButton => Intl.message('OK', + desc: 'Message showed on a button that the user can click to leave the ' + 'current dialog. It is used on iOS side. Maximum 30 characters.');
diff --git a/packages/local_auth/lib/error_codes.dart b/packages/local_auth/lib/error_codes.dart new file mode 100644 index 0000000..2d226ed --- /dev/null +++ b/packages/local_auth/lib/error_codes.dart
@@ -0,0 +1,19 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Exception codes for `PlatformException` returned by +// `authenticateWithBiometrics`. + +/// Indicates that the user has not yet configured a passcode (iOS) or +/// PIN/pattern/password (Android) on the device. +const String passcodeNotSet = 'PasscodeNotSet'; + +/// Indicates the user has not enrolled any fingerprints on the device. +const String notEnrolled = 'NotEnrolled'; + +/// Indicates the device does not have a Touch ID/fingerprint scanner. +const String notAvailable = 'NotAvailable'; + +/// Indicates the device operating system is not iOS or Android. +const String otherOperatingSystem = 'OtherOperatingSystem';
diff --git a/packages/local_auth/lib/local_auth.dart b/packages/local_auth/lib/local_auth.dart new file mode 100644 index 0000000..20600b5 --- /dev/null +++ b/packages/local_auth/lib/local_auth.dart
@@ -0,0 +1,66 @@ +// Copyright 2017 The Chromium 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 'dart:io'; + +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart'; + +import 'auth_strings.dart'; +import 'error_codes.dart'; + +const MethodChannel _channel = + const MethodChannel('plugins.flutter.io/local_auth'); + +/// A Flutter plugin for authenticating the user identity locally. +class LocalAuthentication { + /// Authenticates the user with biometrics available on the device. + /// + /// Returns a [Future] holding true, if the user successfully authenticated, + /// false otherwise. + /// + /// [localizedReason] is the message to show to user while prompting them + /// for authentication. This is typically along the lines of: 'Please scan + /// your finger to access MyApp.' + /// + /// useErrorDialogs = true means the system will attempt to handle user + /// fixable issues encountered while authenticating. For instance, if + /// fingerprint reader exists on the phone but there's no fingerprint + /// registered, the plugin will attempt to take the user to settings to add + /// one. Anything that is not user fixable, such as no biometric sensor on + /// device, will be returned as a [PlatformException]. + /// + /// Construct [AndroidAuthStrings] and [IOSAuthStrings] if you want to + /// customize messages in the dialogs. + /// + /// 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 + /// simulator. + Future<bool> authenticateWithBiometrics({ + @required String localizedReason, + bool useErrorDialogs: true, + AndroidAuthMessages androidAuthStrings: const AndroidAuthMessages(), + IOSAuthMessages iOSAuthStrings: const IOSAuthMessages(), + }) { + assert(localizedReason != null); + final Map<String, Object> args = <String, Object>{ + 'localizedReason': localizedReason, + 'useErrorDialogs': useErrorDialogs, + }; + if (Platform.isIOS) { + args.addAll(iOSAuthStrings.args); + } else if (Platform.isAndroid) { + args.addAll(androidAuthStrings.args); + } else { + throw new PlatformException( + code: otherOperatingSystem, + message: 'Local authentication does not support non-Android/iOS ' + 'operating systems.', + details: 'Your operating system is ${Platform.operatingSystem}'); + } + return _channel.invokeMethod('authenticateWithBiometrics', args); + } +}
diff --git a/packages/local_auth/local_auth.iml b/packages/local_auth/local_auth.iml new file mode 100644 index 0000000..9d5dae1 --- /dev/null +++ b/packages/local_auth/local_auth.iml
@@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="FLUTTER_MODULE_TYPE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <excludeFolder url="file://$MODULE_DIR$/.idea" /> + <excludeFolder url="file://$MODULE_DIR$/.pub" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/packages" /> + </content> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Dart Packages" level="project" /> + <orderEntry type="library" name="Dart SDK" level="project" /> + </component> +</module> \ No newline at end of file
diff --git a/packages/local_auth/local_auth_android.iml b/packages/local_auth/local_auth_android.iml new file mode 100644 index 0000000..462b903 --- /dev/null +++ b/packages/local_auth/local_auth_android.iml
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$/android"> + <sourceFolder url="file://$MODULE_DIR$/android/app/src/main/java" isTestSource="false" /> + </content> + <orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Flutter for Android" level="project" /> + </component> +</module>
diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/pubspec.yaml new file mode 100644 index 0000000..9e81a17 --- /dev/null +++ b/packages/local_auth/pubspec.yaml
@@ -0,0 +1,19 @@ +name: local_auth +description: A plugin that uses local sensors to authenticate users (such as Fingerprint Reader/Touch ID). +version: 0.0.1 +author: Flutter Team <flutter-dev@googlegroups.com> +homepage: https://github.com/flutter/plugins/tree/master/packages/local_auth + +flutter: + plugin: + androidPackage: io.flutter.plugins.localauth + pluginClass: LocalAuthPlugin + +dependencies: + flutter: + sdk: flutter + meta: ^1.0.5 + intl: '>=0.14.0 <0.15.0' + +environment: + sdk: ">=1.8.0 <2.0.0"