summaryrefslogtreecommitdiffstats
path: root/samples/browseable/FingerprintDialog/src
diff options
context:
space:
mode:
Diffstat (limited to 'samples/browseable/FingerprintDialog/src')
-rw-r--r--samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java48
-rw-r--r--samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintModule.java14
-rw-r--r--samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintUiHelper.java3
-rw-r--r--samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/MainActivity.java101
-rw-r--r--samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/SettingsActivity.java47
5 files changed, 185 insertions, 28 deletions
diff --git a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java
index 57c00de08..8909f752f 100644
--- a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java
+++ b/samples/browseable/FingerprintDialog/src/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.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.ImageView;
import android.widget.TextView;
@@ -44,6 +46,9 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
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 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
@Inject FingerprintUiHelper.FingerprintUiHelperBuilder mFingerprintUiHelperBuilder;
@Inject InputMethodManager mInputMethodManager;
+ @Inject SharedPreferences mSharedPreferences;
@Inject
public FingerprintAuthenticationDialogFragment() {}
@@ -93,6 +99,11 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
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 class FingerprintAuthenticationDialogFragment extends DialogFragment
}
}
+ public void setStage(Stage stage) {
+ mStage = stage;
+ }
+
@Override
public void onPause() {
super.onPause();
@@ -149,12 +164,25 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
* 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 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
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 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
/**
* 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/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintModule.java b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintModule.java
index 16d5067ea..964e1f6d8 100644
--- a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintModule.java
+++ b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintModule.java
@@ -18,7 +18,10 @@ package com.example.android.fingerprintdialog;
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 @@ public class FingerprintModule {
@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 @@ public class FingerprintModule {
@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 class FingerprintModule {
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/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintUiHelper.java b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintUiHelper.java
index ab7570ca7..92fcdb1d0 100644
--- a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintUiHelper.java
+++ b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintUiHelper.java
@@ -82,7 +82,8 @@ public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallba
}
mCancellationSignal = new CancellationSignal();
mSelfCancelled = false;
- mFingerprintManager.authenticate(cryptoObject, mCancellationSignal, this, 0 /* flags */);
+ mFingerprintManager
+ .authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null);
mIcon.setImageResource(R.drawable.ic_fp_40px);
}
diff --git a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/MainActivity.java b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/MainActivity.java
index 9d09765eb..c954bfa7b 100644
--- a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/MainActivity.java
+++ b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/MainActivity.java
@@ -19,6 +19,8 @@ package com.example.android.fingerprintdialog;
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.KeyPermanentlyInvalidatedException;
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,29 @@ public class MainActivity extends Activity {
/** 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 FingerprintManager mFingerprintManager;
@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()) {
@@ -88,35 +98,71 @@ public class MainActivity extends Activity {
purchaseButton.setEnabled(false);
return;
}
+ if (!mFingerprintManager.hasEnrolledFingerprints()) {
+ purchaseButton.setEnabled(false);
+ // This happens when no fingerprints are registered.
+ Toast.makeText(this,
+ "Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
createKey();
+ purchaseButton.setEnabled(true);
purchaseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
-
- // 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);
+ findViewById(R.id.confirmation_message).setVisibility(View.GONE);
+ findViewById(R.id.encrypted_message).setVisibility(View.GONE);
+
+ // 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 +170,6 @@ public class MainActivity extends Activity {
}
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 +209,7 @@ public class MainActivity extends Activity {
* 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 +232,22 @@ public class MainActivity extends Activity {
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/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/SettingsActivity.java b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/SettingsActivity.java
new file mode 100644
index 000000000..08b391140
--- /dev/null
+++ b/samples/browseable/FingerprintDialog/src/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);
+ }
+ }
+}
+
+