main fest
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
activity main xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="START AUTHENTICATION"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
The code for the dialog_fingerprint.xml
is given below:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="24dp"
android:paddingTop="24dp"
android:paddingRight="24dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<TextView
android:id="@+id/titleTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Fingerprint Dialog"
android:textAppearance="?android:attr/textAppearanceLarge"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/subtitleTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Confirm fingerprint to continue."
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleTextView" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/subtitleTextView"
app:srcCompat="@drawable/ic_fingerprint_white_24dp" />
<TextView
android:id="@+id/errorTextView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:gravity="center_vertical"
android:text="Touch sensor"
app:layout_constraintBottom_toBottomOf="@id/fab"
app:layout_constraintLeft_toRightOf="@id/fab"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/fab" />
<LinearLayout
android:id="@+id/buttons"
style="?android:attr/buttonBarStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="end"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/fab">
<Button
android:id="@+id/btnCancel"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancel" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
FingerprintHelper.java
package com.journaldev.androidfingerprintapi;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import android.support.v4.app.ActivityCompat;
public class FingerprintHelper extends FingerprintManager.AuthenticationCallback {
private Context mContext;
private FingerprintManager mFingerprintManager;
private CancellationSignal mCancellationSignal;
private Callback mCallback;
public FingerprintHelper(FingerprintManager fingerprintManager, Context context, Callback callback) {
mContext = context;
mFingerprintManager = fingerprintManager;
mCallback = callback;
}
public boolean isFingerprintAuthAvailable() {
return mFingerprintManager.isHardwareDetected()
&& mFingerprintManager.hasEnrolledFingerprints();
}
public void startAuthentication(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) {
if (!isFingerprintAuthAvailable())
return;
mCancellationSignal = new CancellationSignal();
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
return;
}
manager.authenticate(cryptoObject, mCancellationSignal, 0, this, null);
}
public void stopListening() {
if (mCancellationSignal != null) {
mCancellationSignal.cancel();
mCancellationSignal = null;
}
}
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
mCallback.onError(errString.toString());
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
mCallback.onHelp(helpString.toString());
}
@Override
public void onAuthenticationFailed() {
mCallback.onAuthenticated(false);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
mCallback.onAuthenticated(true);
}
public interface Callback {
void onAuthenticated(boolean b);
void onError(String s);
void onHelp(String s);
}
}
manager.authenticate(cryptoObject, mCancellationSignal, 0, this, null);
MainActivity.java
package com.journaldev.androidfingerprintapi;
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button button;
FingerprintManagerCompat managerCompat;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(this);
}
private void showFingerPrintDialog() {
FingerprintDialog fragment = new FingerprintDialog();
fragment.setContext(this);
fragment.show(getSupportFragmentManager(), "");
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
managerCompat = FingerprintManagerCompat.from(MainActivity.this);
if (managerCompat.isHardwareDetected() && managerCompat.hasEnrolledFingerprints()) {
showFingerPrintDialog();
} else {
Toast.makeText(getApplicationContext(), "Fingerprint not supported", Toast.LENGTH_SHORT).show();
}
break;
}
}
}
FingerprintDialog.java
package com.journaldev.androidfingerprintapi;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
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.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
public class FingerprintDialog extends DialogFragment
implements FingerprintHelper.Callback {
Button mCancelButton;
public static final String DEFAULT_KEY_NAME = "default_key";
FingerprintManager mFingerprintManager;
private FingerprintManager.CryptoObject mCryptoObject;
private FingerprintHelper mFingerprintHelper;
KeyStore mKeyStore = null;
KeyGenerator mKeyGenerator = null;
KeyguardManager mKeyguardManager;
private Context mContext;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog);
try {
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (KeyStoreException e) {
e.printStackTrace();
}
try {
mKeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
Cipher defaultCipher;
try {
defaultCipher = 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);
}
mKeyguardManager = getContext().getSystemService(KeyguardManager.class);
mFingerprintManager = getContext().getSystemService(FingerprintManager.class);
mFingerprintHelper = new FingerprintHelper(mFingerprintManager, getContext(), this);
if (!mKeyguardManager.isKeyguardSecure()) {
Toast.makeText(getContext(),
"Lock screen not set up.\n"
+ "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint",
Toast.LENGTH_LONG).show();
return;
}
createKey(DEFAULT_KEY_NAME);
if (initCipher(defaultCipher, DEFAULT_KEY_NAME)) {
mCryptoObject = new FingerprintManager.CryptoObject(defaultCipher);
}
}
private boolean initCipher(Cipher cipher, String keyName) {
try {
mKeyStore.load(null);
SecretKey key = (SecretKey) mKeyStore.getKey(keyName, null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyPermanentlyInvalidatedException e) {
Toast.makeText(mContext, "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) {
Toast.makeText(mContext, "Failed to init cipher", Toast.LENGTH_LONG).show();
return false;
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_fingerprint, container, false);
mCancelButton = v.findViewById(R.id.btnCancel);
mCancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismiss();
}
});
return v;
}
@Override
public void onResume() {
super.onResume();
if (mCryptoObject != null) {
mFingerprintHelper.startAuthentication(mFingerprintManager, mCryptoObject);
}
}
@Override
public void onPause() {
super.onPause();
mFingerprintHelper.stopListening();
}
public void setContext(Context context) {
mContext = context;
}
public void createKey(String keyName) {
try {
mKeyStore.load(null);
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
mKeyGenerator.init(builder.build());
mKeyGenerator.generateKey();
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
| CertificateException | IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void onAuthenticated(boolean b) {
if (b) {
Toast.makeText(mContext.getApplicationContext(), "Auth success", Toast.LENGTH_LONG).show();
dismiss();
} else
Toast.makeText(mContext.getApplicationContext(), "Auth failed", Toast.LENGTH_LONG).show();
}
@Override
public void onError(String s) {
Toast.makeText(mContext.getApplicationContext(), s, Toast.LENGTH_LONG).show();
}
@Override
public void onHelp(String s) {
Toast.makeText(mContext.getApplicationContext(), "Auth help message:" + s, Toast.LENGTH_LONG).show();
}
}