aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiem Song <tiem@google.com>2017-11-14 18:17:15 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-11-14 18:17:15 +0000
commit78ec28ceaf0efaf9fdc6e8d74166123b153fd634 (patch)
treec891573fbdf05d8f5832495c3315e0cc530e9c23
parent6f5c750676f0f6371b7f106c0b434825a82c6fa7 (diff)
parent6cb485a0c2cbe9be5f447c7d375725b7f087edfd (diff)
downloadandroid-78ec28ceaf0efaf9fdc6e8d74166123b153fd634.tar.gz
Merge "Create Kotlin version of FingerprintDialog sample" into oc-dev am: 44f2c83565
am: 6cb485a0c2 Change-Id: I2bec02b146fbba64f4934f4903acd6f7186793c8
-rw-r--r--security/FingerprintDialog/kotlinApp/app/build.gradle25
-rw-r--r--security/FingerprintDialog/kotlinApp/app/proguard-project.txt20
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/AndroidManifest.xml48
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/ActivityExtensions.kt24
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/Constants.kt26
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.kt226
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/FingerprintUiHelper.kt112
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/MainActivity.kt332
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/SettingsActivity.kt31
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/SettingsFragment.kt29
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-hdpi/ic_fp_40px.pngbin0 -> 7011 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-mdpi/ic_fp_40px.pngbin0 -> 4001 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-nodpi/android_robot.pngbin0 -> 8288 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xhdpi/ic_fp_40px.pngbin0 -> 10524 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xxhdpi/ic_fp_40px.pngbin0 -> 18565 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xxxhdpi/ic_fp_40px.pngbin0 -> 16535 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/card.xml24
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/ic_fingerprint_error.xml28
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/ic_fingerprint_success.xml28
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/layout/activity_main.xml138
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_backup.xml78
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_container.xml65
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_content.xml59
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/menu/menu_main.xml26
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3266 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2059 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4145 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 6681 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 9444 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/values/colors.xml25
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/values/dimens.xml35
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/values/strings.xml59
-rw-r--r--security/FingerprintDialog/kotlinApp/app/src/main/res/xml/preferences.xml23
-rw-r--r--security/FingerprintDialog/kotlinApp/build.gradle22
-rw-r--r--security/FingerprintDialog/kotlinApp/gradle.properties18
-rw-r--r--security/FingerprintDialog/kotlinApp/gradle/wrapper/gradle-wrapper.jarbin0 -> 49896 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xsecurity/FingerprintDialog/kotlinApp/gradlew164
-rw-r--r--security/FingerprintDialog/kotlinApp/gradlew.bat90
-rw-r--r--security/FingerprintDialog/kotlinApp/screenshots/1-purchase-screen.pngbin0 -> 99587 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/screenshots/2-fingerprint-dialog.pngbin0 -> 104965 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/screenshots/3-fingerprint-authenticated.pngbin0 -> 92101 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/screenshots/4-new-fingerprint-enrolled.pngbin0 -> 103821 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/screenshots/big-icon.pngbin0 -> 35296 bytes
-rw-r--r--security/FingerprintDialog/kotlinApp/settings.gradle1
45 files changed, 1762 insertions, 0 deletions
diff --git a/security/FingerprintDialog/kotlinApp/app/build.gradle b/security/FingerprintDialog/kotlinApp/app/build.gradle
new file mode 100644
index 00000000..58bfe13a
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 27
+ defaultConfig {
+ applicationId "com.example.android.fingerprintdialog"
+ minSdkVersion 24
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation "com.android.support:appcompat-v7:27.0.0"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+} \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/app/proguard-project.txt b/security/FingerprintDialog/kotlinApp/app/proguard-project.txt
new file mode 100644
index 00000000..f2fe1559
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/AndroidManifest.xml b/security/FingerprintDialog/kotlinApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..9dffd1ce
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<manifest package="com.example.android.fingerprintdialog"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.USE_FINGERPRINT" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/application_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat.Light"
+ tools:ignore="GoogleAppIndexingWarning">
+
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/application_name"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".SettingsActivity"
+ android:label="@string/action_settings"
+ android:parentActivityName=".MainActivity"/>
+ </application>
+</manifest>
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/ActivityExtensions.kt b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/ActivityExtensions.kt
new file mode 100644
index 00000000..108ca436
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/ActivityExtensions.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.fingerprintdialog
+
+import android.support.v7.app.AppCompatActivity
+import android.widget.Toast
+
+fun AppCompatActivity.showToast(text: String) {
+ Toast.makeText(this, text, Toast.LENGTH_LONG).show()
+} \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/Constants.kt b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/Constants.kt
new file mode 100644
index 00000000..8f93fb11
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/Constants.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("Constants")
+
+package com.example.android.fingerprintdialog
+
+@JvmField val DEFAULT_KEY_NAME = "default_key"
+
+/**
+ * Enumeration to indicate which authentication method the user is trying to authenticate with.
+ */
+enum class Stage { FINGERPRINT, NEW_FINGERPRINT_ENROLLED, PASSWORD }
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.kt b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.kt
new file mode 100644
index 00000000..8e0fe0bd
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.kt
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.example.android.fingerprintdialog
+
+import android.app.DialogFragment
+import android.content.Context
+import android.content.SharedPreferences
+import android.hardware.fingerprint.FingerprintManager
+import android.os.Bundle
+import android.preference.PreferenceManager
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
+import android.widget.Button
+import android.widget.CheckBox
+import android.widget.EditText
+import android.widget.TextView
+
+/**
+ * A dialog which uses fingerprint APIs to authenticate the user, and falls back to password
+ * authentication if fingerprint is not available.
+ */
+class FingerprintAuthenticationDialogFragment : DialogFragment(),
+ TextView.OnEditorActionListener,
+ FingerprintUiHelper.Callback {
+
+ private lateinit var backupContent: View
+ private lateinit var cancelButton: Button
+ private lateinit var fingerprintContainer: View
+ private lateinit var fingerprintEnrolledTextView: TextView
+ private lateinit var passwordDescriptionTextView: TextView
+ private lateinit var passwordEditText: EditText
+ private lateinit var secondDialogButton: Button
+ private lateinit var useFingerprintFutureCheckBox: CheckBox
+
+ internal lateinit var callback: Callback
+ internal lateinit var cryptoObject: FingerprintManager.CryptoObject
+ private lateinit var fingerprintUiHelper: FingerprintUiHelper
+ private lateinit var inputMethodManager: InputMethodManager
+ private lateinit var sharedPreferences: SharedPreferences
+
+ internal var stage = Stage.FINGERPRINT
+
+ private val showKeyboardRunnable = Runnable {
+ inputMethodManager.showSoftInput(passwordEditText, 0)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // Do not create a new Fragment when the Activity is re-created such as orientation changes.
+ retainInstance = true
+ setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ dialog.setTitle(getString(R.string.sign_in))
+ return inflater.inflate(R.layout.fingerprint_dialog_container, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ backupContent = view.findViewById(R.id.backup_container)
+ cancelButton = view.findViewById(R.id.cancel_button)
+ fingerprintContainer = view.findViewById(R.id.fingerprint_container)
+ fingerprintEnrolledTextView = view.findViewById(R.id.new_fingerprint_enrolled_description)
+ passwordDescriptionTextView = view.findViewById(R.id.password_description)
+ passwordEditText = view.findViewById(R.id.password)
+ secondDialogButton = view.findViewById(R.id.second_dialog_button)
+ useFingerprintFutureCheckBox = view.findViewById(R.id.use_fingerprint_in_future_check)
+
+ cancelButton.setOnClickListener { dismiss() }
+ passwordEditText.setOnEditorActionListener(this)
+ secondDialogButton.setOnClickListener {
+ if (stage == Stage.FINGERPRINT) goToBackup() else verifyPassword()
+ }
+
+ fingerprintUiHelper = FingerprintUiHelper(
+ activity.getSystemService(FingerprintManager::class.java),
+ view.findViewById(R.id.fingerprint_icon),
+ view.findViewById(R.id.fingerprint_status),
+ this
+ )
+ updateStage()
+
+ // If fingerprint authentication is not available, switch immediately to the backup
+ // (password) screen.
+ if (!fingerprintUiHelper.isFingerprintAuthAvailable) {
+ goToBackup()
+ }
+ }
+ override fun onResume() {
+ super.onResume()
+ if (stage == Stage.FINGERPRINT) {
+ fingerprintUiHelper.startListening(cryptoObject)
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+ fingerprintUiHelper.stopListening()
+ }
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ inputMethodManager = context.getSystemService(InputMethodManager::class.java)
+ sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+ }
+
+ /**
+ * Switches to backup (password) screen. This either can happen when fingerprint is not
+ * available or the user chooses to use the password authentication method by pressing the
+ * button. This can also happen when the user has too many invalid fingerprint attempts.
+ */
+ private fun goToBackup() {
+ stage = Stage.PASSWORD
+ updateStage()
+ passwordEditText.run {
+ requestFocus()
+
+ // Show the keyboard.
+ postDelayed(showKeyboardRunnable, 500)
+ }
+
+ // Fingerprint is not used anymore. Stop listening for it.
+ fingerprintUiHelper.stopListening()
+ }
+
+ /**
+ * Checks whether the current entered password is correct, and dismisses the dialog and
+ * informs the activity about the result.
+ */
+ private fun verifyPassword() {
+ if (!checkPassword(passwordEditText.text.toString())) {
+ return
+ }
+ if (stage == Stage.NEW_FINGERPRINT_ENROLLED) {
+ sharedPreferences.edit()
+ .putBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
+ useFingerprintFutureCheckBox.isChecked)
+ .apply()
+
+ if (useFingerprintFutureCheckBox.isChecked) {
+ // Re-create the key so that fingerprints including new ones are validated.
+ callback.createKey(DEFAULT_KEY_NAME)
+ stage = Stage.FINGERPRINT
+ }
+ }
+ passwordEditText.setText("")
+ callback.onPurchased(withFingerprint = false)
+ dismiss()
+ }
+
+ /**
+ * Checks if the given password is valid. Assume that the password is always correct.
+ * In a real world situation, the password needs to be verified via the server.
+ *
+ * @param password The password String
+ *
+ * @return true if `password` is correct, false otherwise
+ */
+ private fun checkPassword(password: String) = password.isNotEmpty()
+
+ private fun updateStage() {
+ when (stage) {
+ Stage.FINGERPRINT -> {
+ cancelButton.setText(R.string.cancel)
+ secondDialogButton.setText(R.string.use_password)
+ fingerprintContainer.visibility = View.VISIBLE
+ backupContent.visibility = View.GONE
+ }
+ Stage.NEW_FINGERPRINT_ENROLLED, // Intentional fall through
+ Stage.PASSWORD -> {
+ cancelButton.setText(R.string.cancel)
+ secondDialogButton.setText(R.string.ok)
+ fingerprintContainer.visibility = View.GONE
+ backupContent.visibility = View.VISIBLE
+ if (stage == Stage.NEW_FINGERPRINT_ENROLLED) {
+ passwordDescriptionTextView.visibility = View.GONE
+ fingerprintEnrolledTextView.visibility = View.VISIBLE
+ useFingerprintFutureCheckBox.visibility = View.VISIBLE
+ }
+ }
+ }
+ }
+
+ override fun onEditorAction(v: TextView, actionId: Int, event: KeyEvent?): Boolean {
+ return if (actionId == EditorInfo.IME_ACTION_GO) { verifyPassword(); true } else false
+ }
+
+ override fun onAuthenticated() {
+ // Callback from FingerprintUiHelper. Let the activity know that authentication succeeded.
+ callback.onPurchased(withFingerprint = true, crypto = cryptoObject)
+ dismiss()
+ }
+
+ override fun onError() {
+ goToBackup()
+ }
+
+ interface Callback {
+ fun onPurchased(withFingerprint: Boolean, crypto: FingerprintManager.CryptoObject? = null)
+ fun createKey(keyName: String, invalidatedByBiometricEnrollment: Boolean = true)
+ }
+}
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/FingerprintUiHelper.kt b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/FingerprintUiHelper.kt
new file mode 100644
index 00000000..ad38abc6
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/FingerprintUiHelper.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.example.android.fingerprintdialog
+
+import android.hardware.fingerprint.FingerprintManager
+import android.os.CancellationSignal
+import android.widget.ImageView
+import android.widget.TextView
+
+/**
+ * Small helper class to manage text/icon around fingerprint authentication UI.
+ */
+class FingerprintUiHelper
+
+/**
+ * Constructor for [FingerprintUiHelper].
+ */
+internal constructor(private val fingerprintMgr: FingerprintManager,
+ private val icon: ImageView,
+ private val errorTextView: TextView,
+ private val callback: Callback
+) : FingerprintManager.AuthenticationCallback() {
+
+ private var cancellationSignal: CancellationSignal? = null
+ private var selfCancelled = false
+
+ val isFingerprintAuthAvailable: Boolean
+ get() = fingerprintMgr.isHardwareDetected && fingerprintMgr.hasEnrolledFingerprints()
+
+ private val resetErrorTextRunnable = Runnable {
+ icon.setImageResource(R.drawable.ic_fp_40px)
+ errorTextView.run {
+ setTextColor(errorTextView.resources.getColor(R.color.hint_color, null))
+ text = errorTextView.resources.getString(R.string.fingerprint_hint)
+ }
+ }
+
+ fun startListening(cryptoObject: FingerprintManager.CryptoObject) {
+ if (!isFingerprintAuthAvailable) return
+ cancellationSignal = CancellationSignal()
+ selfCancelled = false
+ fingerprintMgr.authenticate(cryptoObject, cancellationSignal, 0, this, null)
+ icon.setImageResource(R.drawable.ic_fp_40px)
+ }
+
+ fun stopListening() {
+ cancellationSignal?.also {
+ selfCancelled = true
+ it.cancel()
+ }
+ cancellationSignal = null
+ }
+
+ override fun onAuthenticationError(errMsgId: Int, errString: CharSequence) {
+ if (!selfCancelled) {
+ showError(errString)
+ icon.postDelayed({ callback.onError() }, ERROR_TIMEOUT_MILLIS)
+ }
+ }
+
+ override fun onAuthenticationHelp(helpMsgId: Int, helpString: CharSequence) =
+ showError(helpString)
+
+ override fun onAuthenticationFailed() =
+ showError(icon.resources.getString(R.string.fingerprint_not_recognized))
+
+ override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
+ errorTextView.run {
+ removeCallbacks(resetErrorTextRunnable)
+ setTextColor(errorTextView.resources.getColor(R.color.success_color, null))
+ text = errorTextView.resources.getString(R.string.fingerprint_success)
+ }
+ icon.run {
+ setImageResource(R.drawable.ic_fingerprint_success)
+ postDelayed({ callback.onAuthenticated() }, SUCCESS_DELAY_MILLIS)
+ }
+ }
+
+ private fun showError(error: CharSequence) {
+ icon.setImageResource(R.drawable.ic_fingerprint_error)
+ errorTextView.run {
+ text = error
+ setTextColor(errorTextView.resources.getColor(R.color.warning_color, null))
+ removeCallbacks(resetErrorTextRunnable)
+ postDelayed(resetErrorTextRunnable, ERROR_TIMEOUT_MILLIS)
+ }
+ }
+
+ interface Callback {
+ fun onAuthenticated()
+ fun onError()
+ }
+
+ companion object {
+ val ERROR_TIMEOUT_MILLIS: Long = 1600
+ val SUCCESS_DELAY_MILLIS: Long = 1300
+ }
+}
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/MainActivity.kt b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/MainActivity.kt
new file mode 100644
index 00000000..27bcd250
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/MainActivity.kt
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.example.android.fingerprintdialog
+
+import android.app.KeyguardManager
+import android.content.Intent
+import android.content.SharedPreferences
+import android.hardware.fingerprint.FingerprintManager
+import android.os.Bundle
+import android.preference.PreferenceManager
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyPermanentlyInvalidatedException
+import android.security.keystore.KeyProperties
+import android.security.keystore.KeyProperties.BLOCK_MODE_CBC
+import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_PKCS7
+import android.security.keystore.KeyProperties.KEY_ALGORITHM_AES
+import android.support.v7.app.AppCompatActivity
+import android.util.Base64
+import android.util.Log
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.widget.Button
+import android.widget.TextView
+import android.widget.Toast
+import java.io.IOException
+import java.security.InvalidAlgorithmParameterException
+import java.security.InvalidKeyException
+import java.security.KeyStore
+import java.security.KeyStoreException
+import java.security.NoSuchAlgorithmException
+import java.security.NoSuchProviderException
+import java.security.UnrecoverableKeyException
+import java.security.cert.CertificateException
+import javax.crypto.BadPaddingException
+import javax.crypto.Cipher
+import javax.crypto.IllegalBlockSizeException
+import javax.crypto.KeyGenerator
+import javax.crypto.NoSuchPaddingException
+import javax.crypto.SecretKey
+
+/**
+ * Main entry point for the sample, showing a backpack and "Purchase" button.
+ */
+class MainActivity : AppCompatActivity(),
+ FingerprintAuthenticationDialogFragment.Callback {
+
+ private lateinit var keyStore: KeyStore
+ private lateinit var keyGenerator: KeyGenerator
+ private lateinit var sharedPreferences: SharedPreferences
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ setSupportActionBar(findViewById(R.id.toolbar))
+ setupKeyStoreAndKeyGenerator()
+
+ val (defaultCipher: Cipher, cipherNotInvalidated: Cipher) = setupCiphers()
+ sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
+ setUpPurchaseButtons(cipherNotInvalidated, defaultCipher)
+ }
+
+ /**
+ * Enables or disables purchase buttons and sets the appropriate click listeners.
+ *
+ * @param cipherNotInvalidated cipher for the not invalidated purchase button
+ * @param defaultCipher the default cipher, used for the purchase button
+ */
+ private fun setUpPurchaseButtons(cipherNotInvalidated: Cipher, defaultCipher: Cipher) {
+ val purchaseButton = findViewById<Button>(R.id.purchase_button)
+ val purchaseButtonNotInvalidated =
+ findViewById<Button>(R.id.purchase_button_not_invalidated)
+
+ purchaseButtonNotInvalidated.run {
+ isEnabled = true
+ setOnClickListener(PurchaseButtonClickListener(
+ cipherNotInvalidated, KEY_NAME_NOT_INVALIDATED))
+ }
+
+ val keyguardManager = getSystemService(KeyguardManager::class.java)
+ if (!keyguardManager.isKeyguardSecure) {
+ // Show a message that the user hasn't set up a fingerprint or lock screen.
+ showToast(getString(R.string.setup_lock_screen))
+ purchaseButton.isEnabled = false
+ purchaseButtonNotInvalidated.isEnabled = false
+ return
+ }
+
+ val fingerprintManager = getSystemService(FingerprintManager::class.java)
+ if (!fingerprintManager.hasEnrolledFingerprints()) {
+ purchaseButton.isEnabled = false
+ // This happens when no fingerprints are registered.
+ showToast(getString(R.string.register_fingerprint))
+ return
+ }
+
+ createKey(DEFAULT_KEY_NAME)
+ createKey(KEY_NAME_NOT_INVALIDATED, false)
+ purchaseButton.run {
+ isEnabled = true
+ setOnClickListener(PurchaseButtonClickListener(defaultCipher, DEFAULT_KEY_NAME))
+ }
+ }
+
+ /**
+ * Sets up KeyStore and KeyGenerator
+ */
+ private fun setupKeyStoreAndKeyGenerator() {
+ try {
+ keyStore = KeyStore.getInstance(ANDROID_KEY_STORE)
+ } catch (e: KeyStoreException) {
+ throw RuntimeException("Failed to get an instance of KeyStore", e)
+ }
+
+ try {
+ keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM_AES, ANDROID_KEY_STORE)
+ } catch (e: Exception) {
+ when (e) {
+ is NoSuchAlgorithmException,
+ is NoSuchProviderException ->
+ throw RuntimeException("Failed to get an instance of KeyGenerator", e)
+ else -> throw e
+ }
+ }
+ }
+
+ /**
+ * Sets up default cipher and a non-invalidated cipher
+ */
+ private fun setupCiphers(): Pair<Cipher, Cipher> {
+ val defaultCipher: Cipher
+ val cipherNotInvalidated: Cipher
+ try {
+ val cipherString = "$KEY_ALGORITHM_AES/$BLOCK_MODE_CBC/$ENCRYPTION_PADDING_PKCS7"
+ defaultCipher = Cipher.getInstance(cipherString)
+ cipherNotInvalidated = Cipher.getInstance(cipherString)
+ } catch (e: Exception) {
+ when (e) {
+ is NoSuchAlgorithmException,
+ is NoSuchPaddingException ->
+ throw RuntimeException("Failed to get an instance of Cipher", e)
+ else -> throw e
+ }
+ }
+ return Pair(defaultCipher, cipherNotInvalidated)
+ }
+
+ /**
+ * Initialize the [Cipher] instance with the created key in the [createKey] method.
+ *
+ * @param keyName the key name to init the cipher
+ * @return `true` if initialization succeeded, `false` if the lock screen has been disabled or
+ * reset after key generation, or if a fingerprint was enrolled after key generation.
+ */
+ private fun initCipher(cipher: Cipher, keyName: String): Boolean {
+ try {
+ keyStore.load(null)
+ cipher.init(Cipher.ENCRYPT_MODE, keyStore.getKey(keyName, null) as SecretKey)
+ return true
+ } catch (e: Exception) {
+ when (e) {
+ is KeyPermanentlyInvalidatedException -> return false
+ is KeyStoreException,
+ is CertificateException,
+ is UnrecoverableKeyException,
+ is IOException,
+ is NoSuchAlgorithmException,
+ is InvalidKeyException -> throw RuntimeException("Failed to init Cipher", e)
+ else -> throw e
+ }
+ }
+ }
+
+ /**
+ * Proceed with the purchase operation
+ *
+ * @param withFingerprint `true` if the purchase was made by using a fingerprint
+ * @param crypto the Crypto object
+ */
+ override fun onPurchased(withFingerprint: Boolean, crypto: FingerprintManager.CryptoObject?) {
+ if (withFingerprint) {
+ // If the user authenticated with fingerprint, verify using cryptography and then show
+ // the confirmation message.
+ if (crypto != null) {
+ tryEncrypt(crypto.cipher)
+ }
+ } else {
+ // Authentication happened with backup password. Just show the confirmation message.
+ showConfirmation()
+ }
+ }
+
+ // Show confirmation message. Also show crypto information if fingerprint was used.
+ private fun showConfirmation(encrypted: ByteArray? = null) {
+ findViewById<View>(R.id.confirmation_message).visibility = View.VISIBLE
+ if (encrypted != null) {
+ findViewById<TextView>(R.id.encrypted_message).run {
+ visibility = View.VISIBLE
+ text = Base64.encodeToString(encrypted, 0 /* flags */)
+ }
+ }
+ }
+
+ /**
+ * Tries to encrypt some data with the generated key from [createKey]. This only works if the
+ * user just authenticated via fingerprint.
+ */
+ private fun tryEncrypt(cipher: Cipher) {
+ try {
+ showConfirmation(cipher.doFinal(SECRET_MESSAGE.toByteArray()))
+ } catch (e: Exception) {
+ when (e) {
+ is BadPaddingException,
+ is IllegalBlockSizeException -> {
+ Toast.makeText(this, "Failed to encrypt the data with the generated key. "
+ + "Retry the purchase", Toast.LENGTH_LONG).show()
+ Log.e(TAG, "Failed to encrypt the data with the generated key. ${e.message}")
+ }
+ else -> throw e
+ }
+ }
+ }
+
+ /**
+ * Creates a symmetric key in the Android Key Store which can only be used after the user has
+ * authenticated with a fingerprint.
+ *
+ * @param keyName the name of the key to be created
+ * @param invalidatedByBiometricEnrollment if `false` is passed, the created key will not be
+ * invalidated even if a new fingerprint is enrolled. The default value is `true` - the key will
+ * be invalidated if a new fingerprint is enrolled.
+ */
+ override fun createKey(keyName: String, invalidatedByBiometricEnrollment: Boolean) {
+ // The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
+ // for your flow. Use of keys is necessary if you need to know if the set of enrolled
+ // fingerprints has changed.
+ try {
+ keyStore.load(null)
+
+ val keyProperties = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
+ val builder = KeyGenParameterSpec.Builder(keyName, keyProperties)
+ .setBlockModes(BLOCK_MODE_CBC)
+ .setUserAuthenticationRequired(true)
+ .setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7)
+ .setInvalidatedByBiometricEnrollment(invalidatedByBiometricEnrollment)
+
+ keyGenerator.run {
+ init(builder.build())
+ generateKey()
+ }
+ } catch (e: Exception) {
+ when (e) {
+ is NoSuchAlgorithmException,
+ is InvalidAlgorithmParameterException,
+ is CertificateException,
+ is IOException -> throw RuntimeException(e)
+ else -> throw e
+ }
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu_main, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == R.id.action_settings) {
+ val intent = Intent(this, SettingsActivity::class.java)
+ startActivity(intent)
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ private inner class PurchaseButtonClickListener internal constructor(
+ internal var cipher: Cipher,
+ internal var keyName: String
+ ) : View.OnClickListener {
+
+ override fun onClick(view: View) {
+ findViewById<View>(R.id.confirmation_message).visibility = View.GONE
+ findViewById<View>(R.id.encrypted_message).visibility = View.GONE
+
+ val fragment = FingerprintAuthenticationDialogFragment()
+ fragment.cryptoObject = FingerprintManager.CryptoObject(cipher)
+ fragment.callback = this@MainActivity
+
+ // Set up the crypto object for later, which will be authenticated by fingerprint usage.
+ if (initCipher(cipher, keyName)) {
+
+ // Show the fingerprint dialog. The user has the option to use the fingerprint with
+ // crypto, or can fall back to using a server-side verified password.
+ val useFingerprintPreference = sharedPreferences
+ .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key), true)
+ if (useFingerprintPreference) {
+ fragment.stage = Stage.FINGERPRINT
+ } else {
+ fragment.stage = Stage.PASSWORD
+ }
+ } else {
+ // This happens if the lock screen has been disabled or or a fingerprint was
+ // enrolled. Thus, show the dialog to authenticate with their password first and ask
+ // the user if they want to authenticate with a fingerprint in the future.
+ fragment.stage = Stage.NEW_FINGERPRINT_ENROLLED
+ }
+ fragment.show(fragmentManager, DIALOG_FRAGMENT_TAG)
+ }
+ }
+
+ companion object {
+ private val ANDROID_KEY_STORE = "AndroidKeyStore"
+ private val DIALOG_FRAGMENT_TAG = "myFragment"
+ private val KEY_NAME_NOT_INVALIDATED = "key_not_invalidated"
+ private val SECRET_MESSAGE = "Very secret message"
+ private val TAG = MainActivity::class.java.simpleName
+ }
+}
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/SettingsActivity.kt b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/SettingsActivity.kt
new file mode 100644
index 00000000..6a8dc160
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/SettingsActivity.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.example.android.fingerprintdialog
+
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+
+class SettingsActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ fragmentManager.beginTransaction()
+ .replace(android.R.id.content, SettingsFragment())
+ .commit()
+ }
+
+}
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/SettingsFragment.kt b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/SettingsFragment.kt
new file mode 100644
index 00000000..0681e6d3
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/java/com/example/android/fingerprintdialog/SettingsFragment.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.example.android.fingerprintdialog
+
+import android.os.Bundle
+import android.preference.PreferenceFragment
+
+class SettingsFragment : PreferenceFragment() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ addPreferencesFromResource(R.xml.preferences)
+ }
+
+}
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-hdpi/ic_fp_40px.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-hdpi/ic_fp_40px.png
new file mode 100644
index 00000000..48ebd8ad
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-hdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-mdpi/ic_fp_40px.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-mdpi/ic_fp_40px.png
new file mode 100644
index 00000000..122f4425
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-mdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-nodpi/android_robot.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-nodpi/android_robot.png
new file mode 100644
index 00000000..40bf934b
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-nodpi/android_robot.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xhdpi/ic_fp_40px.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xhdpi/ic_fp_40px.png
new file mode 100644
index 00000000..e1c9590b
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xhdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xxhdpi/ic_fp_40px.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xxhdpi/ic_fp_40px.png
new file mode 100644
index 00000000..f7e87240
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xxhdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xxxhdpi/ic_fp_40px.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xxxhdpi/ic_fp_40px.png
new file mode 100644
index 00000000..0fb85452
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable-xxxhdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/card.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/card.xml
new file mode 100644
index 00000000..cdd2907f
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/card.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <solid
+ android:color="@color/card_solid"/>
+
+ <corners
+ android:radius="@dimen/card_corner" />
+</shape> \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/ic_fingerprint_error.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/ic_fingerprint_error.xml
new file mode 100644
index 00000000..b382543d
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/ic_fingerprint_error.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/fingerprint_square_dimen"
+ android:height="@dimen/fingerprint_square_dimen"
+ android:viewportWidth="@dimen/fingerprint_square_dimen_viewport"
+ android:viewportHeight="@dimen/fingerprint_square_dimen_viewport">
+ <path
+ android:pathData="M20.0,0.0C8.96,0.0 0.0,8.95 0.0,20.0s8.96,20.0 20.0,20.0c11.04,0.0 20.0,-8.95 20.0,-20.0S31.04,0.0 20.0,0.0z"
+ android:fillColor="@color/finerprint_error"/>
+ <path
+ android:pathData="M21.33,29.33l-2.67,0.0l0.0,-2.67l2.67,0.0L21.33,29.33zM21.33,22.67l-2.67,0.0l0.0,-12.0l2.67,0.0L21.33,22.67z"
+ android:fillColor="@color/fingerprint_fill"/>
+</vector>
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/ic_fingerprint_success.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/ic_fingerprint_success.xml
new file mode 100644
index 00000000..0195cd79
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/drawable/ic_fingerprint_success.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/fingerprint_square_dimen"
+ android:height="@dimen/fingerprint_square_dimen"
+ android:viewportWidth="@dimen/fingerprint_square_dimen_viewport"
+ android:viewportHeight="@dimen/fingerprint_square_dimen_viewport">
+ <path
+ android:pathData="M20.0,20.0m-20.0,0.0a20.0,20.0 0.0,1.0 1.0,40.0 0.0a20.0,20.0 0.0,1.0 1.0,-40.0 0.0"
+ android:fillColor="@color/fingerprint_success"/>
+ <path
+ android:pathData="M11.2,21.41l1.63,-1.619999 4.17,4.169998 10.59,-10.589999 1.619999,1.63 -12.209999,12.209999z"
+ android:fillColor="@color/fingerprint_fill"/>
+</vector>
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/activity_main.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..fedfde65
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ android:elevation="@dimen/default_elevation"
+ android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
+ app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
+
+ <ImageView
+ android:layout_width="@dimen/logo_square_dimen"
+ android:layout_height="@dimen/logo_square_dimen"
+ android:layout_marginTop="@dimen/logo_square_margin"
+ android:layout_marginBottom="@dimen/logo_square_margin"
+ android:layout_gravity="center_horizontal"
+ android:scaleType="fitCenter"
+ android:src="@drawable/android_robot"
+ android:contentDescription="@string/description_bugdroid_icon"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/default_margin_small"
+ android:layout_marginStart="@dimen/default_margin_small"
+ android:layout_marginEnd="@dimen/default_margin_small"
+ android:orientation="vertical"
+ android:background="@drawable/card"
+ android:elevation="@dimen/default_elevation"
+ android:paddingTop="@dimen/default_padding"
+ android:paddingBottom="@dimen/default_padding"
+ android:paddingStart="@dimen/default_padding"
+ android:paddingEnd="@dimen/default_padding">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@android:style/TextAppearance.Material.Headline"
+ android:text="@string/item_title"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@android:style/TextAppearance.Material.Body2"
+ android:textColor="?android:attr/colorAccent"
+ android:text="@string/item_price"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/default_margin"
+ android:textAppearance="@android:style/TextAppearance.Material.Body1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:text="@string/item_description"/>
+
+ </LinearLayout>
+
+ <Button style="@android:style/Widget.Material.Button.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/default_margin_small"
+ android:layout_marginEnd="@dimen/default_margin_end"
+ android:layout_gravity="end"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ android:text="@string/purchase"
+ android:id="@+id/purchase_button" />
+
+ <Button style="@android:style/Widget.Material.Button.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/default_margin_small"
+ android:layout_marginEnd="@dimen/default_margin_end"
+ android:layout_gravity="end"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ android:text="@string/purchase_not_invalidated"
+ android:id="@+id/purchase_button_not_invalidated" />
+
+ <TextView
+ android:id="@+id/purchase_button_not_invalidated_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/default_margin_end"
+ android:gravity="end"
+ android:textAlignment="gravity"
+ android:text="@string/purchase_button_not_invalidated_description"/>
+
+ <TextView
+ android:id="@+id/confirmation_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/default_margin"
+ android:paddingStart="@dimen/default_padding"
+ android:paddingEnd="@dimen/default_padding"
+ android:textAppearance="@android:style/TextAppearance.Material.Body2"
+ android:textColor="?android:attr/colorAccent"
+ android:text="@string/purchase_done"
+ android:visibility="gone"/>
+
+ <TextView
+ android:id="@+id/encrypted_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/default_margin"
+ android:paddingStart="@dimen/default_padding"
+ android:paddingEnd="@dimen/default_padding"
+ android:textAppearance="@android:style/TextAppearance.Material.Body2"
+ android:textColor="?android:attr/colorAccent"
+ android:text="@string/purchase_done"
+ android:visibility="gone"/>
+
+ </LinearLayout>
+
+</ScrollView> \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_backup.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_backup.xml
new file mode 100644
index 00000000..9668f504
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_backup.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/backup_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="@dimen/default_padding"
+ android:paddingBottom="@dimen/default_padding_small">
+
+ <FrameLayout
+ android:id="@+id/description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginStart="@dimen/fingerprint_margin"
+ android:layout_marginEnd="@dimen/fingerprint_margin">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/password_description"
+ android:id="@+id/password_description"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/new_fingerprint_enrolled_description"
+ android:id="@+id/new_fingerprint_enrolled_description"
+ android:visibility="gone"
+ android:textColor="?android:attr/textColorSecondary" />
+ </FrameLayout>
+
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword"
+ android:ems="10"
+ android:hint="@string/password"
+ android:imeOptions="actionGo"
+ android:id="@+id/password"
+ android:layout_below="@+id/description"
+ android:layout_marginTop="@dimen/fingerprint_margin_vertical"
+ android:layout_marginStart="@dimen/fingerprint_margin_horizontal"
+ android:layout_marginEnd="@dimen/fingerprint_margin_horizontal"
+ android:layout_alignParentStart="true" />
+
+ <CheckBox
+ android:id="@+id/use_fingerprint_in_future_check"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/password"
+ android:layout_alignParentStart="true"
+ android:layout_marginTop="@dimen/fingerprint_margin_vertical"
+ android:layout_marginStart="@dimen/fingerprint_margin_horizontal"
+ android:layout_marginEnd="@dimen/fingerprint_margin_horizontal"
+ android:checked="true"
+ android:visibility="gone"
+ android:text="@string/use_fingerprint_in_future" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_container.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_container.xml
new file mode 100644
index 00000000..c5881ff8
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_container.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <include layout="@layout/fingerprint_dialog_content" />
+
+ <include
+ layout="@layout/fingerprint_dialog_backup"
+ android:visibility="gone" />
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/fingerprint_padding_horizontal"
+ android:paddingEnd="@dimen/fingerprint_padding_horizontal"
+ android:paddingTop="@dimen/fingerprint_padding_vertical"
+ android:paddingBottom="@dimen/fingerprint_padding_vertical"
+ android:gravity="bottom"
+ style="?android:attr/buttonBarStyle">
+
+ <Space
+ android:id="@+id/spacer"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+ <Button
+ android:id="@+id/cancel_button"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/second_dialog_button"
+ style="?android:attr/buttonBarPositiveButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_content.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_content.xml
new file mode 100644
index 00000000..c68a178d
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/layout/fingerprint_dialog_content.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/fingerprint_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/default_padding_small"
+ android:paddingStart="@dimen/default_padding_large"
+ android:paddingEnd="@dimen/default_padding_large"
+ android:paddingTop="@dimen/default_padding">
+
+ <TextView
+ android:id="@+id/fingerprint_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:text="@string/fingerprint_description"
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <ImageView
+ android:id="@+id/fingerprint_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@+id/fingerprint_description"
+ android:layout_marginTop="@dimen/fingerprint_margin_horizontal"
+ android:src="@drawable/ic_fp_40px"
+ android:contentDescription="@string/description_fingerprint_icon"/>
+
+ <TextView
+ android:id="@+id/fingerprint_status"
+ style="@android:style/TextAppearance.Material.Body1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/fingerprint_icon"
+ android:layout_alignTop="@+id/fingerprint_icon"
+ android:layout_marginStart="@dimen/default_margin"
+ android:layout_toEndOf="@+id/fingerprint_icon"
+ android:gravity="center_vertical"
+ android:text="@string/fingerprint_hint"
+ android:textColor="@color/hint_color" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/menu/menu_main.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 00000000..2adf23cb
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context=".MainActivity">
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:title="@string/action_settings"
+ app:showAsAction="never" />
+</menu>
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-hdpi/ic_launcher.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..68c473a1
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-mdpi/ic_launcher.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..fd7e5f6a
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..106c0d32
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..b319bebe
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..4dc0ddf5
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/values/colors.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..403534ef
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/values/colors.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <color name="warning_color">#f4511e</color>
+ <color name="hint_color">#42000000</color>
+ <color name="success_color">#009688</color>
+ <color name="fingerprint_success">#009688</color>
+ <color name="finerprint_error">#F4511E</color>
+ <color name="fingerprint_fill">#FFFFFF</color>
+ <color name="card_solid">#fefefe</color>
+</resources>
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/values/dimens.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..e2d290b1
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/values/dimens.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <dimen name="fingerprint_square_dimen">40dp</dimen>
+ <dimen name="fingerprint_square_dimen_viewport" type="float">40.0</dimen>
+ <dimen name="card_corner">2dp</dimen>
+ <dimen name="default_elevation">4dp</dimen>
+ <dimen name="logo_square_dimen">150dp</dimen>
+ <dimen name="logo_square_margin">32dp</dimen>
+ <dimen name="default_padding">16dp</dimen>
+ <dimen name="default_margin">16dp</dimen>
+ <dimen name="default_margin_end">4dp</dimen>
+ <dimen name="default_margin_small">8dp</dimen>
+ <dimen name="fingerprint_padding_horizontal">12dp</dimen>
+ <dimen name="fingerprint_padding_vertical">4dp</dimen>
+ <dimen name="fingerprint_margin">24dp</dimen>
+ <dimen name="default_padding_small">8dp</dimen>
+ <dimen name="fingerprint_margin_horizontal">20dp</dimen>
+ <dimen name="fingerprint_margin_vertical">16dp</dimen>
+ <dimen name="default_padding_large">24dp</dimen>
+</resources> \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/values/strings.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..09f0376e
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <!--
+ The similar strings app_name is added in the Application/template directory, but depending
+ on the template directory makes it hard for the sample buildable with Android.mk.
+ Thus defining the application_name with different name from the template.
+ -->
+ <string name="application_name">FingerprintDialog</string>
+ <string name="action_settings">Settings</string>
+ <string name="cancel">Cancel</string>
+ <string name="use_password">Use password</string>
+ <string name="sign_in">Sign in</string>
+ <string name="ok">OK</string>
+ <string name="password">Password</string>
+ <string name="fingerprint_description">Confirm fingerprint to continue</string>
+ <string name="fingerprint_hint">Touch sensor</string>
+ <string name="password_description">Enter your store password to continue</string>
+ <string name="purchase">Purchase</string>
+ <string name="purchase_not_invalidated">Purchase not invalidated</string>
+ <string name="purchase_button_not_invalidated_description">
+ You can proceed to purchase with this button \n even if a new fingerprint is enrolled
+ </string>
+ <string name="fingerprint_not_recognized">Fingerprint not recognized. Try again</string>
+ <string name="fingerprint_success">Fingerprint recognized</string>
+ <string name="item_title">White Mesh Pluto Backpack</string>
+ <string name="item_price">$62.68</string>
+ <string name="item_description">Mesh backpack in white. Black textile trim throughout.</string>
+ <string name="purchase_done">Purchase successful</string>
+ <string name="new_fingerprint_enrolled_description">
+ A new fingerprint was added to this device, so your password is required.
+ </string>
+ <string name="use_fingerprint_in_future">Use fingerprint in the future</string>
+ <string name="use_fingerprint_to_authenticate_title">Use fingerprint to authenticate</string>
+ <string name="use_fingerprint_to_authenticate_key" >use_fingerprint_to_authenticate_key</string>
+ <string name="description_bugdroid_icon">Android bugdroid image</string>
+ <string name="description_fingerprint_icon">Fingerprint icon</string>
+ <string name="register_fingerprint">
+ Go to \'Settings -> Security -> Fingerprint\' and register at least one fingerprint
+ </string>
+ <string name="setup_lock_screen">
+ Secure lock screen hasn\'t set been up.\n
+ Go to \'Settings -> Security -> Fingerprint\' to set up a fingerprint
+ </string>
+</resources>
diff --git a/security/FingerprintDialog/kotlinApp/app/src/main/res/xml/preferences.xml b/security/FingerprintDialog/kotlinApp/app/src/main/res/xml/preferences.xml
new file mode 100644
index 00000000..c9fd74b7
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/app/src/main/res/xml/preferences.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <CheckBoxPreference
+ android:key="@string/use_fingerprint_to_authenticate_key"
+ android:title="@string/use_fingerprint_to_authenticate_title"
+ android:persistent="true"
+ android:defaultValue="true" />
+</PreferenceScreen> \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/build.gradle b/security/FingerprintDialog/kotlinApp/build.gradle
new file mode 100644
index 00000000..624b2312
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/build.gradle
@@ -0,0 +1,22 @@
+buildscript {
+ ext.kotlin_version = '1.1.51'
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.0.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+} \ No newline at end of file
diff --git a/security/FingerprintDialog/kotlinApp/gradle.properties b/security/FingerprintDialog/kotlinApp/gradle.properties
new file mode 100644
index 00000000..89196d13
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
diff --git a/security/FingerprintDialog/kotlinApp/gradle/wrapper/gradle-wrapper.jar b/security/FingerprintDialog/kotlinApp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..8c0fb64a
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/gradle/wrapper/gradle-wrapper.properties b/security/FingerprintDialog/kotlinApp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..152529a1
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Oct 16 16:01:34 PDT 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/security/FingerprintDialog/kotlinApp/gradlew b/security/FingerprintDialog/kotlinApp/gradlew
new file mode 100755
index 00000000..91a7e269
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/security/FingerprintDialog/kotlinApp/gradlew.bat b/security/FingerprintDialog/kotlinApp/gradlew.bat
new file mode 100644
index 00000000..8a0b282a
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/security/FingerprintDialog/kotlinApp/screenshots/1-purchase-screen.png b/security/FingerprintDialog/kotlinApp/screenshots/1-purchase-screen.png
new file mode 100644
index 00000000..0bf03bd0
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/screenshots/1-purchase-screen.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/screenshots/2-fingerprint-dialog.png b/security/FingerprintDialog/kotlinApp/screenshots/2-fingerprint-dialog.png
new file mode 100644
index 00000000..5e681f96
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/screenshots/2-fingerprint-dialog.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/screenshots/3-fingerprint-authenticated.png b/security/FingerprintDialog/kotlinApp/screenshots/3-fingerprint-authenticated.png
new file mode 100644
index 00000000..d485b1dd
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/screenshots/3-fingerprint-authenticated.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/screenshots/4-new-fingerprint-enrolled.png b/security/FingerprintDialog/kotlinApp/screenshots/4-new-fingerprint-enrolled.png
new file mode 100644
index 00000000..7dcf0800
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/screenshots/4-new-fingerprint-enrolled.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/screenshots/big-icon.png b/security/FingerprintDialog/kotlinApp/screenshots/big-icon.png
new file mode 100644
index 00000000..9e323342
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/screenshots/big-icon.png
Binary files differ
diff --git a/security/FingerprintDialog/kotlinApp/settings.gradle b/security/FingerprintDialog/kotlinApp/settings.gradle
new file mode 100644
index 00000000..4c90568a
--- /dev/null
+++ b/security/FingerprintDialog/kotlinApp/settings.gradle
@@ -0,0 +1 @@
+include 'app'