Ask password when user adds a new fingerprint including a checkbox to ask that
if they want to use fingerprints in the future and stores it as shared prefernces.
Including some refactoring.
- Keep the purchase button visible after a successful purchase so that users of
the app can test a flow after registering a new fingerprint.
- Fixed broken tests.
- Use end/start instead of left/right for RTL languages.
Bug: 21655960
Change-Id: I9b0015fa5f61048c611ab798afbf96edbc3239eb
diff --git a/security/FingerprintDialog/Application/src/main/AndroidManifest.xml b/security/FingerprintDialog/Application/src/main/AndroidManifest.xml
index 5f754ad..3b19fbe 100644
--- a/security/FingerprintDialog/Application/src/main/AndroidManifest.xml
+++ b/security/FingerprintDialog/Application/src/main/AndroidManifest.xml
@@ -35,7 +35,9 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+
+ <activity
+ android:name=".SettingsActivity"
+ android:label="@string/action_settings" />
</application>
-
-
</manifest>
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.java
index 57c00de..8909f75 100644
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.java
+++ b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.java
@@ -17,6 +17,7 @@
package com.example.android.fingerprintdialog;
import android.app.DialogFragment;
+import android.content.SharedPreferences;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.view.KeyEvent;
@@ -26,6 +27,7 @@
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.ImageView;
import android.widget.TextView;
@@ -44,6 +46,9 @@
private View mFingerprintContent;
private View mBackupContent;
private EditText mPassword;
+ private CheckBox mUseFingerprintFutureCheckBox;
+ private TextView mPasswordDescriptionTextView;
+ private TextView mNewFingerprintEnrolledTextView;
private Stage mStage = Stage.FINGERPRINT;
@@ -52,6 +57,7 @@
@Inject FingerprintUiHelper.FingerprintUiHelperBuilder mFingerprintUiHelperBuilder;
@Inject InputMethodManager mInputMethodManager;
+ @Inject SharedPreferences mSharedPreferences;
@Inject
public FingerprintAuthenticationDialogFragment() {}
@@ -93,6 +99,11 @@
mBackupContent = v.findViewById(R.id.backup_container);
mPassword = (EditText) v.findViewById(R.id.password);
mPassword.setOnEditorActionListener(this);
+ mPasswordDescriptionTextView = (TextView) v.findViewById(R.id.password_description);
+ mUseFingerprintFutureCheckBox = (CheckBox)
+ v.findViewById(R.id.use_fingerprint_in_future_check);
+ mNewFingerprintEnrolledTextView = (TextView)
+ v.findViewById(R.id.new_fingerprint_enrolled_description);
mFingerprintUiHelper = mFingerprintUiHelperBuilder.build(
(ImageView) v.findViewById(R.id.fingerprint_icon),
(TextView) v.findViewById(R.id.fingerprint_status), this);
@@ -114,6 +125,10 @@
}
}
+ public void setStage(Stage stage) {
+ mStage = stage;
+ }
+
@Override
public void onPause() {
super.onPause();
@@ -149,12 +164,25 @@
* let's the activity know about the result.
*/
private void verifyPassword() {
- if (checkPassword(mPassword.getText().toString())) {
- ((MainActivity) getActivity()).onPurchased(false /* without Fingerprint */);
- dismiss();
- } else {
- // assume the password is always correct.
+ if (!checkPassword(mPassword.getText().toString())) {
+ return;
}
+ MainActivity activity = ((MainActivity) getActivity());
+ if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
+ mUseFingerprintFutureCheckBox.isChecked());
+ editor.apply();
+
+ if (mUseFingerprintFutureCheckBox.isChecked()) {
+ // Re-create the key so that fingerprints including new ones are validated.
+ activity.createKey();
+ mStage = Stage.FINGERPRINT;
+ }
+ }
+ mPassword.setText("");
+ ((MainActivity) getActivity()).onPurchased(false /* without Fingerprint */);
+ dismiss();
}
/**
@@ -181,11 +209,18 @@
mFingerprintContent.setVisibility(View.VISIBLE);
mBackupContent.setVisibility(View.GONE);
break;
+ case NEW_FINGERPRINT_ENROLLED:
+ // Intentional fall through
case PASSWORD:
mCancelButton.setText(R.string.cancel);
mSecondDialogButton.setText(R.string.ok);
mFingerprintContent.setVisibility(View.GONE);
mBackupContent.setVisibility(View.VISIBLE);
+ if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) {
+ mPasswordDescriptionTextView.setVisibility(View.GONE);
+ mNewFingerprintEnrolledTextView.setVisibility(View.VISIBLE);
+ mUseFingerprintFutureCheckBox.setVisibility(View.VISIBLE);
+ }
break;
}
}
@@ -215,8 +250,9 @@
/**
* Enumeration to indicate which authentication method the user is trying to authenticate with.
*/
- private enum Stage {
+ public enum Stage {
FINGERPRINT,
+ NEW_FINGERPRINT_ENROLLED,
PASSWORD
}
}
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintModule.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintModule.java
index 16d5067..964e1f6 100644
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintModule.java
+++ b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintModule.java
@@ -18,7 +18,10 @@
import android.app.KeyguardManager;
import android.content.Context;
+import android.content.SharedPreferences;
import android.hardware.fingerprint.FingerprintManager;
+import android.preference.PreferenceManager;
+import android.security.keystore.KeyProperties;
import android.view.inputmethod.InputMethodManager;
import java.security.KeyStore;
@@ -75,7 +78,7 @@
@Provides
public KeyGenerator providesKeyGenerator() {
try {
- return KeyGenerator.getInstance("AES", "AndroidKeyStore");
+ return KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new RuntimeException("Failed to get an instance of KeyGenerator", e);
}
@@ -84,7 +87,9 @@
@Provides
public Cipher providesCipher(KeyStore keyStore) {
try {
- return Cipher.getInstance("AES/CBC/PKCS7Padding");
+ return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ + KeyProperties.BLOCK_MODE_CBC + "/"
+ + KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("Failed to get an instance of Cipher", e);
}
@@ -94,4 +99,9 @@
public InputMethodManager providesInputMethodManager(Context context) {
return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
}
+
+ @Provides
+ public SharedPreferences providesSharedPreferences(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context);
+ }
}
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintUiHelper.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintUiHelper.java
index 6d31ddc..92fcdb1 100644
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintUiHelper.java
+++ b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintUiHelper.java
@@ -82,7 +82,8 @@
}
mCancellationSignal = new CancellationSignal();
mSelfCancelled = false;
- mFingerprintManager.authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null);
+ mFingerprintManager
+ .authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null);
mIcon.setImageResource(R.drawable.ic_fp_40px);
}
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java
index 9d09765..208fd17 100644
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java
+++ b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java
@@ -19,6 +19,8 @@
import android.Manifest;
import android.app.Activity;
import android.app.KeyguardManager;
+import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
@@ -27,6 +29,8 @@
import android.security.keystore.KeyProperties;
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;
@@ -60,23 +64,28 @@
/** Alias for our key in the Android Key Store */
private static final String KEY_NAME = "my_key";
+ private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
+
@Inject KeyguardManager mKeyguardManager;
@Inject FingerprintAuthenticationDialogFragment mFragment;
@Inject KeyStore mKeyStore;
@Inject KeyGenerator mKeyGenerator;
@Inject Cipher mCipher;
+ @Inject SharedPreferences mSharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((InjectedApplication) getApplication()).inject(this);
- requestPermissions(new String[]{Manifest.permission.USE_FINGERPRINT}, 0);
+ requestPermissions(new String[]{Manifest.permission.USE_FINGERPRINT},
+ FINGERPRINT_PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] state) {
- if (requestCode == 0 && state[0] == PackageManager.PERMISSION_GRANTED) {
+ if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE
+ && state[0] == PackageManager.PERMISSION_GRANTED) {
setContentView(R.layout.activity_main);
Button purchaseButton = (Button) findViewById(R.id.purchase_button);
if (!mKeyguardManager.isKeyguardSecure()) {
@@ -86,37 +95,63 @@
+ "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint",
Toast.LENGTH_LONG).show();
purchaseButton.setEnabled(false);
- return;
}
createKey();
purchaseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
+ findViewById(R.id.confirmation_message).setVisibility(View.GONE);
+ findViewById(R.id.encrypted_message).setVisibility(View.GONE);
- // Show the fingerprint dialog. The user has the option to use the fingerprint with
- // crypto, or you can fall back to using a server-side verified password.
- mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher));
- mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
+ // Set up the crypto object for later. The object will be authenticated by use
+ // of the fingerprint.
+ if (initCipher()) {
+
+ // Show the fingerprint dialog. The user has the option to use the fingerprint with
+ // crypto, or you can fall back to using a server-side verified password.
+ mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher));
+ boolean useFingerprintPreference = mSharedPreferences
+ .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
+ true);
+ if (useFingerprintPreference) {
+ mFragment.setStage(
+ FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT);
+ } else {
+ mFragment.setStage(
+ FingerprintAuthenticationDialogFragment.Stage.PASSWORD);
+ }
+ mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
+ } else {
+ // This happens if the lock screen has been disabled or or a fingerprint got
+ // enrolled. Thus show the dialog to authenticate with their password first
+ // and ask the user if they want to authenticate with fingerprints in the
+ // future
+ mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher));
+ mFragment.setStage(
+ FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED);
+ mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
+ }
}
});
-
- // Set up the crypto object for later. The object will be authenticated by use
- // of the fingerprint.
- initCipher();
}
}
- private void initCipher() {
+ /**
+ * Initialize the {@link Cipher} instance with the created key in the {@link #createKey()}
+ * method.
+ *
+ * @return {@code true} if initialization is successful, {@code false} if the lock screen has
+ * been disabled or reset after the key was generated, or if a fingerprint got enrolled after
+ * the key was generated.
+ */
+ private boolean initCipher() {
try {
mKeyStore.load(null);
SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
mCipher.init(Cipher.ENCRYPT_MODE, key);
+ return true;
} catch (KeyPermanentlyInvalidatedException e) {
- // This happens if the lock screen has been disabled or reset after the key was
- // generated, or if a fingerprint got enrolled after the key was generated.
- Toast.makeText(this, "Keys are invalidated after created. Retry the purchase\n"
- + e.getMessage(),
- Toast.LENGTH_LONG).show();
+ return false;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
@@ -124,7 +159,6 @@
}
public void onPurchased(boolean withFingerprint) {
- findViewById(R.id.purchase_button).setVisibility(View.GONE);
if (withFingerprint) {
// If the user has authenticated with fingerprint, verify that using cryptography and
// then show the confirmation message.
@@ -164,7 +198,7 @@
* Creates a symmetric key in the Android Key Store which can only be used after the user has
* authenticated with fingerprint.
*/
- private void createKey() {
+ public void createKey() {
// 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.
@@ -187,4 +221,22 @@
throw new RuntimeException(e);
}
}
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+
+ if (id == R.id.action_settings) {
+ Intent intent = new Intent(this, SettingsActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
}
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/SettingsActivity.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/SettingsActivity.java
new file mode 100644
index 0000000..08b3911
--- /dev/null
+++ b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/SettingsActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 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.Activity;
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+
+public class SettingsActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Display the fragment as the main content.
+ getFragmentManager().beginTransaction().replace(android.R.id.content,
+ new SettingsFragment()).commit();
+ }
+
+ /**
+ * Fragment for settings.
+ */
+ public static class SettingsFragment extends PreferenceFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+ }
+ }
+}
+
+
diff --git a/security/FingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_backup.xml b/security/FingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_backup.xml
index 0b88e33..2be05b1 100644
--- a/security/FingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_backup.xml
+++ b/security/FingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_backup.xml
@@ -16,21 +16,37 @@
-->
<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:layout_width="match_parent"
+ android:layout_height="match_parent"
android:paddingTop="16dp"
android:paddingBottom="8dp">
- <TextView
+ <FrameLayout
+ android:id="@+id/description"
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/description"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
- 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/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"
@@ -46,4 +62,17 @@
android:layout_marginEnd="20dp"
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="16dp"
+ android:layout_marginStart="20dp"
+ android:layout_marginEnd="20dp"
+ 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/Application/src/main/res/layout/fingerprint_dialog_content.xml b/security/FingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_content.xml
index b56ccbb..3929eba 100644
--- a/security/FingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_content.xml
+++ b/security/FingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_content.xml
@@ -19,8 +19,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="8dp"
- android:paddingLeft="24dp"
- android:paddingRight="24dp"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
android:paddingTop="16dp">
<TextView
@@ -50,7 +50,7 @@
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/fingerprint_icon"
android:layout_alignTop="@+id/fingerprint_icon"
- android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_toEndOf="@+id/fingerprint_icon"
android:gravity="center_vertical"
android:text="@string/fingerprint_hint"
diff --git a/security/FingerprintDialog/Application/src/main/res/menu/menu_main.xml b/security/FingerprintDialog/Application/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..73f5e89
--- /dev/null
+++ b/security/FingerprintDialog/Application/src/main/res/menu/menu_main.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 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:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
+ <item android:id="@+id/action_settings" android:title="@string/action_settings"
+ android:orderInCategory="100" android:showAsAction="never" />
+</menu>
diff --git a/security/FingerprintDialog/Application/src/main/res/values/strings.xml b/security/FingerprintDialog/Application/src/main/res/values/strings.xml
index 8a6ecde..9f5a6fd 100644
--- a/security/FingerprintDialog/Application/src/main/res/values/strings.xml
+++ b/security/FingerprintDialog/Application/src/main/res/values/strings.xml
@@ -31,4 +31,8 @@
<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>
</resources>
diff --git a/security/FingerprintDialog/Application/src/main/res/xml/preferences.xml b/security/FingerprintDialog/Application/src/main/res/xml/preferences.xml
new file mode 100644
index 0000000..761391d
--- /dev/null
+++ b/security/FingerprintDialog/Application/src/main/res/xml/preferences.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 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/Application/src/test/java/com/example/android/fingerprintdialog/FingerprintUiHelperTest.java b/security/FingerprintDialog/Application/src/test/java/com/example/android/fingerprintdialog/FingerprintUiHelperTest.java
index 9a68ee7..da9b2a3 100644
--- a/security/FingerprintDialog/Application/src/test/java/com/example/android/fingerprintdialog/FingerprintUiHelperTest.java
+++ b/security/FingerprintDialog/Application/src/test/java/com/example/android/fingerprintdialog/FingerprintUiHelperTest.java
@@ -12,6 +12,7 @@
import android.content.res.Resources;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
+import android.os.Handler;
import android.widget.ImageView;
import android.widget.TextView;
@@ -59,8 +60,9 @@
public void testStartListening_fingerprintAuthAvailable() {
mFingerprintUiHelper.startListening(mockCryptoObject);
- verify(mockFingerprintManager).authenticate(eq(mockCryptoObject), eq(0),
- isA(CancellationSignal.class), eq(mFingerprintUiHelper), any(Handler.class));
+ verify(mockFingerprintManager).authenticate(eq(mockCryptoObject),
+ isA(CancellationSignal.class), eq(0), eq(mFingerprintUiHelper),
+ any(Handler.class));
verify(mockIcon).setImageResource(R.drawable.ic_fp_40px);
}
@@ -71,8 +73,8 @@
mFingerprintUiHelper.startListening(mockCryptoObject);
verify(mockFingerprintManager, never()).authenticate(
- any(FingerprintManager.CryptoObject.class), eq(0),
- any(CancellationSignal.class), any(FingerprintUiHelper.class), any(Handler.class));
+ any(FingerprintManager.CryptoObject.class), any(CancellationSignal.class), eq(0),
+ any(FingerprintUiHelper.class), any(Handler.class));
}
@Test
diff --git a/security/FingerprintDialog/screenshots/4-new-fingerprint-enrolled.png b/security/FingerprintDialog/screenshots/4-new-fingerprint-enrolled.png
new file mode 100644
index 0000000..7dcf080
--- /dev/null
+++ b/security/FingerprintDialog/screenshots/4-new-fingerprint-enrolled.png
Binary files differ
diff --git a/security/FingerprintDialog/template-params.xml b/security/FingerprintDialog/template-params.xml
index 3916c57..ab392ea 100644
--- a/security/FingerprintDialog/template-params.xml
+++ b/security/FingerprintDialog/template-params.xml
@@ -61,6 +61,7 @@
<img>screenshots/1-purchase-screen.png</img>
<img>screenshots/2-fingerprint-dialog.png</img>
<img>screenshots/3-fingerprint-authenticated.png</img>
+ <img>screenshots/4-new-fingerprint-enrolled.png</img>
</screenshots>
<api_refs>