Using Android Fingerprint API for User Login and Registration

In this tutorial, we are going to learn hot we can use use Android Fingerprint API for User Registration and Login.

Android Fingerprint API was introduced in Android 6. So this example tutorial will use android min-sdk 23. If you don’t have a device that runs on android 6, you can as well configure your android emulator for testing purpose.

Before we dive deep into using android fingerprint API for user registration and login, there are other options you can use apart from using Fingerprint authentication in android. I have written android tutorials on the following topics. I will suggest you read them if you are looking for something similar.

1. Android Login and Registration with Php and MYSQL

2. Android User Login and Registration with Retrofit

3. Android Firebase Email and Password Login

What we are going to do

We are going to create a Registration Page where the first time users of the application will fill a little bio information and choose options on how the will like to be on authenticated during login.

The options are below.

1. Login with Fingerprint Authentication

2. Login with Fingerprint and password Authentication.

If the login is successful, the user will be taken the UserProfile page and its bio data will be displayed in the page.

If you have not use Android Fingerprint API and you want to learn more before you continue with this tutorial, we have find the doc in android developers guide here.

Before We Start Coding

In other to get a visual understanding of what we are going to create in this android tutorial, I have add below some screen shots from the application.

SOME SCREENSHOT FROM THE APPLICATION

1. Create a new android app

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidFingerPrint

Package: com.inducesmile.androidfingerprint

Select Empty Activity

Name your activity: MainActivity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

2. Update build.gradle

We are going to add some third party plugins we will use in this tutorial. Open the module level gradle.build and add the code below.

apply plugin: 'com.android.application'
apply plugin: 'com.android.application'
android {
    compileSdkVersion 25
    buildToolsVersion "24.0.1"
    defaultConfig {
        applicationId "com.inducesmile.androidfingerprintlogin"
        minSdkVersion 23
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.1'
    compile 'com.intuit.sdp:sdp-android:1.0.3'
    compile 'com.android.support:design:25.0.1'
    compile 'com.google.code.gson:gson:2.6.1'
    compile 'com.android.support:cardview-v7:25.0.1'
    compile 'com.android.support:support-v4:25.0.1'
    testCompile 'junit:junit:4.12'
}

 3. Update AndroidManifest.xml

Since we are going to use Android Fingerprint for user authentication, we will add user permission for android FingerPrint. Also, we are going to use a CustomApplication class which will extend from Application class. Add the code below to your Manifest file.

apply plugin: 'com.android.application'
android {
    compileSdkVersion 25
    buildToolsVersion "24.0.1"
    defaultConfig {
        applicationId "com.inducesmile.androidfingerprintlogin"
        minSdkVersion 23
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.1'
    compile 'com.intuit.sdp:sdp-android:1.0.3'
    compile 'com.android.support:design:25.0.1'
    compile 'com.google.code.gson:gson:2.6.1'
    compile 'com.android.support:cardview-v7:25.0.1'
    compile 'com.android.support:support-v4:25.0.1'
    testCompile 'junit:junit:4.12'
}

4. Update Strings.xml

We are going to update our project strings.xml file located in the values folder inside the res folder. Open the file and add the code below to it.

<resources>
    <string name="app_name">Android FingerPrint Login</string>
    <string name="sign_in">SIGN IN</string>
    <string name="sign_up">SIGN UP</string>
    <string name="authenticate_fingerprint">Error authenticating fingerprint</string>
    <string name="auth_successful">Fingerprint authentication successful</string>
    <string name="Unknown_permission_request">Unrecognized permission request</string>
    <string name="permission_refused">" User refused Fingerprint permission"</string>
    <string name="phone_number_hint">Phone number</string>
    <string name="address_hint">Address</string>
    <string name="password_hint">Password</string>
    <string name="email_hint">Email</string>
    <string name="username_hint">Username</string>
    <string name="with_fingerprint">With Fingerprint</string>
    <string name="with_fingerprint_and_password">With Fingerprint and Password</string>
    <string name="login_with_fingerprint">LOGIN WITH FINGERPRINT</string>
    <string name="login_now">LOGIN</string>
</resources>

 5. Update Colors.xml

Open the colors.xml file in the same location as the strings.xml file and add the code below to the file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#d69803</color>
    <color name="colorPrimaryDark">#9f7102</color>
    <color name="colorAccent">#857300</color>
    <color name="colorBlue">#367e90</color>
    <color name="colorOpposite">#60c002</color>
    <color name="colorWhite">#ffffff</color>
    <color name="colorBlack">#000000</color>
    <color name="colorLight">#7F7F7F</color>
    <color name="colorBorder">#e6e3e2</color>
</resources>

 6. Update activity_main.xml layout file

This is the intro page which will contain two button widgets for login and signup. First, open the layout file and add the code below.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@drawable/bgfinger"
    tools:context="com.inducesmile.androidfingerprintlogin.MainActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:gravity="bottom">
        <Button
            android:id="@+id/sign_in"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginRight="@dimen/_2sdp"
            android:textColor="@color/colorWhite"
            android:text="@string/sign_in"
            android:textSize="@dimen/_10sdp"
            android:background="@color/colorAccent"/>
        <Button
            android:id="@+id/sign_up"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="@color/colorWhite"
            android:layout_marginLeft="@dimen/_2sdp"
            android:text="@string/sign_up"
            android:textSize="@dimen/_10sdp"
            android:background="@color/colorAccent"/>
    </LinearLayout>
</RelativeLayout>

 7. Update MainActivity.java

import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        ActionBar actionBar = getSupportActionBar();
        if(null != actionBar){
            actionBar.hide();
        }
        Button signInButton = (Button)findViewById(R.id.sign_in);
        signInButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent loginIntent = new Intent(MainActivity.this, LoginActivity.class);
                startActivity(loginIntent);
            }
        });
        Button signUpButton = (Button)findViewById(R.id.sign_up);
        signUpButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent signInIntent = new Intent(MainActivity.this, SignUpActivity.class);
                startActivity(signInIntent);
            }
        });
    }
}

 8. Create and update activity_sign_up.xml

Create a new activity which will be used for sign up. The layout file is named as above. Open the file once it has been created and add the code below.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_sign_up"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:paddingBottom="@dimen/_4sdp"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@color/colorBorder"
    tools:context="com.inducesmile.androidfingerprintlogin.SignUpActivity">
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="vertical">
            <TextView
                android:id="@+id/login_error"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/_16sdp"
                android:padding="@dimen/_4sdp"
                android:text=""
                android:textColor="@color/colorAccent" />
            <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="1dp"
                card_view:cardElevation="@dimen/_3sdp"
                card_view:cardUseCompatPadding="true">
                <EditText
                    android:id="@+id/username"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/username_hint"
                    android:textColorHint="@color/colorLight"
                    android:inputType="text"
                    android:maxLines="1"
                    android:background="@android:color/transparent"
                    android:textColor="@color/colorBlack"
                    android:padding="@dimen/_12sdp"/>
            </android.support.v7.widget.CardView>
            <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="1dp"
                card_view:cardElevation="@dimen/_3sdp"
                card_view:cardUseCompatPadding="true">
                <EditText
                    android:id="@+id/email"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/email_hint"
                    android:textColorHint="@color/colorLight"
                    android:inputType="textEmailAddress"
                    android:maxLines="1"
                    android:background="@android:color/transparent"
                    android:textColor="@color/colorBlack"
                    android:padding="@dimen/_12sdp" />
            </android.support.v7.widget.CardView>
            <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="1dp"
                card_view:cardElevation="@dimen/_3sdp"
                card_view:cardUseCompatPadding="true">
                <EditText
                    android:id="@+id/password"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/password_hint"
                    android:textColorHint="@color/colorLight"
                    android:inputType="textPassword"
                    android:maxLines="1"
                    android:background="@android:color/transparent"
                    android:textColor="@color/colorBlack"
                    android:padding="@dimen/_12sdp" />
            </android.support.v7.widget.CardView>
            <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="1dp"
                card_view:cardElevation="@dimen/_3sdp"
                card_view:cardUseCompatPadding="true">
                <EditText
                    android:id="@+id/address"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/address_hint"
                    android:textColorHint="@color/colorLight"
                    android:inputType="textPostalAddress"
                    android:maxLines="1"
                    android:background="@android:color/transparent"
                    android:textColor="@color/colorBlack"
                    android:padding="@dimen/_12sdp"/>
            </android.support.v7.widget.CardView>
            <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="1dp"
                card_view:cardElevation="@dimen/_3sdp"
                card_view:cardUseCompatPadding="true">
                <EditText
                    android:id="@+id/phone_number"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/phone_number_hint"
                    android:inputType="phone"
                    android:textColorHint="@color/colorLight"
                    android:maxLines="1"
                    android:background="@android:color/transparent"
                    android:textColor="@color/colorBlack"
                    android:padding="@dimen/_12sdp"/>
            </android.support.v7.widget.CardView>
            <RadioGroup
                android:id="@+id/radio_group"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:layout_marginTop="@dimen/_16sdp"
                android:orientation="horizontal">
                <RadioButton
                    android:id="@+id/with_fingerprint"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="@dimen/_10sdp"
                    android:text="@string/with_fingerprint"/>
                <RadioButton
                    android:id="@+id/with_fingerprint_and_password"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="@dimen/_10sdp"
                    android:text="@string/with_fingerprint_and_password"/>
            </RadioGroup>
            <Button
                android:id="@+id/sign_up_button"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/_24sdp"
                android:paddingBottom="@dimen/_16sdp"
                android:paddingTop="@dimen/_16sdp"
                android:text="@string/sign_up"
                android:textAllCaps="true"
                android:textColor="@color/colorWhite"
                android:background="@color/colorAccent"
                android:textStyle="bold" />
        </LinearLayout>
    </ScrollView>
</LinearLayout>

 9. Update SignUpActivity.java

In this class, all the user input values for registration are obtained and stored in a shared preference.

Open the class and add the code below.

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.google.gson.Gson;
public class SignUpActivity extends AppCompatActivity {
    private static final String TAG = SignUpActivity.class.getSimpleName();
    private TextView displayError;
    private EditText username;
    private EditText email;
    private EditText password;
    private EditText address;
    private EditText phoneNumber;
    private RadioGroup radioGroup;
    private boolean loginOption;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sign_up);
        setTitle("Android Fingerprint Registration");
        username = (EditText)findViewById(R.id.username);
        email = (EditText)findViewById(R.id.email);
        password = (EditText)findViewById(R.id.password);
        address = (EditText)findViewById(R.id.address);
        phoneNumber = (EditText)findViewById(R.id.phone_number);
        radioGroup = (RadioGroup)findViewById(R.id.radio_group);
        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int id) {
                if(id == R.id.with_fingerprint){
                    loginOption = false;
                }
                if(id == R.id.with_fingerprint_and_password){
                    loginOption = true;
                }
            }
        });
        Button signUpButton = (Button) findViewById(R.id.sign_up_button);
        signUpButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String usernameValue = username.getText().toString();
                String emailValue = email.getText().toString();
                String passwordValue = password.getText().toString();
                String addressValue = address.getText().toString();
                String phonenumberValue = phoneNumber.getText().toString();
                int selectedButtonId = radioGroup.getCheckedRadioButtonId();
                if(TextUtils.isEmpty(usernameValue) || TextUtils.isEmpty(emailValue)|| TextUtils.isEmpty(passwordValue)
                        || TextUtils.isEmpty(addressValue) || TextUtils.isEmpty(phonenumberValue)){
                    Toast.makeText(SignUpActivity.this, "All input fields must be filled", Toast.LENGTH_LONG).show();
                }else if(selectedButtonId == -1){
                    Toast.makeText(SignUpActivity.this, "Login option must be selected", Toast.LENGTH_LONG).show();
                }else{
                    Gson gson = ((CustomApplication)getApplication()).getGsonObject();
                    UserObject userData = new UserObject(usernameValue, emailValue, passwordValue, addressValue, phonenumberValue, loginOption);
                    String userDataString = gson.toJson(userData);
                    CustomSharedPreference pref = ((CustomApplication)getApplication()).getShared();
                    pref.setUserData(userDataString);
                    username.setText("");
                    email.setText("");
                    password.setText("");
                    address.setText("");
                    phoneNumber.setText("");
                    Intent loginIntent = new Intent(SignUpActivity.this, LoginActivity.class);
                    startActivity(loginIntent);
                }
            }
        });
    }
}

 10. Create the LoginActivity and activity_login.xm file

This class and its layout file will be used for user fingerprint and or password authentication.

Open the activity_login.xml and add the code below.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_finger_print"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:padding="24dp"
    tools:context="com.inducesmile.androidfingerprintlogin.LoginActivity">
    <ImageView
        android:id="@+id/fingerprint_image"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:contentDescription="@string/app_name"
        android:src="@drawable/ic_fp_40px"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:text="@string/login_with_fingerprint"
        android:textStyle="bold"
        android:layout_marginTop="@dimen/_24sdp"/>
</LinearLayout>

Open the LoginActivity.java and add the code below.

import android.Manifest;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.gson.Gson;
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 LoginActivity extends AppCompatActivity {
    private static final String TAG = LoginActivity.class.getSimpleName();
    private FingerprintManager fingerprintManager;
    private KeyguardManager keyguardManager;
    private KeyStore keyStore;
    private KeyGenerator keyGenerator;
    private Cipher cipher;
    private FingerprintManager.CryptoObject cryptoObject;
    private FingerprintHandler fingerprintHandler;
    private static final String FINGERPRINT_KEY = "key_name";
    private static final int REQUEST_USE_FINGERPRINT = 300;
    protected static Gson mGson;
    protected static CustomSharedPreference mPref;
    private static UserObject mUser;
    private static String userString;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        setTitle("Android Fingerprint Login");
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        mGson = ((CustomApplication)getApplication()).getGsonObject();
        mPref = ((CustomApplication)getApplication()).getShared();
        fingerprintHandler = new FingerprintHandler(this);
        fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
        keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
        // check support for android fingerprint on device
        checkDeviceFingerprintSupport();
        //generate fingerprint keystore
        generateFingerprintKeyStore();
        //instantiate Cipher class
        Cipher mCipher = instantiateCipher();
        if(mCipher != null){
            cryptoObject = new FingerprintManager.CryptoObject(mCipher);
        }
        ImageView fingerprintImage = (ImageView)findViewById(R.id.fingerprint_image);
        fingerprintImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                fingerprintHandler.completeFingerAuthentication(fingerprintManager, cryptoObject);
            }
        });
    }
    private void checkDeviceFingerprintSupport() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.USE_FINGERPRINT}, REQUEST_USE_FINGERPRINT);
        } else {
            if (!fingerprintManager.isHardwareDetected()) {
                Toast.makeText(LoginActivity.this, "Fingerprint is not supported in this device", Toast.LENGTH_LONG).show();
            }
            if (!fingerprintManager.hasEnrolledFingerprints()) {
                Toast.makeText(LoginActivity.this, "Fingerprint not yet configured", Toast.LENGTH_LONG).show();
            }
            if (!keyguardManager.isKeyguardSecure()) {
                Toast.makeText(LoginActivity.this, "Screen lock is not secure and enable", Toast.LENGTH_LONG).show();
            }
            return;
        }
    }
    private void generateFingerprintKeyStore(){
        try {
            keyStore = KeyStore.getInstance("AndroidKeyStore");
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        try {
            keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        }
        try {
            keyGenerator.init(new KeyGenParameterSpec.Builder(FINGERPRINT_KEY, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                    .setUserAuthenticationRequired(true)
                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                    .build());
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        keyGenerator.generateKey();
    }
    private Cipher instantiateCipher(){
        try {
            cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
            keyStore.load(null);
            SecretKey secretKey = (SecretKey)keyStore.getKey(FINGERPRINT_KEY, null);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return cipher;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | UnrecoverableKeyException |
                CertificateException | IOException | KeyStoreException | InvalidKeyException e) {
            throw new RuntimeException("Failed to instantiate Cipher class");
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if(requestCode == REQUEST_USE_FINGERPRINT){
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                // check support for android fingerprint on device
                checkDeviceFingerprintSupport();
                //generate fingerprint keystore
                generateFingerprintKeyStore();
                //instantiate Cipher class
                Cipher mCipher = instantiateCipher();
                if(mCipher != null){
                    cryptoObject = new FingerprintManager.CryptoObject(mCipher);
                }
            }
            else{
                Toast.makeText(this, R.string.permission_refused, Toast.LENGTH_LONG).show();
            }
        }else{
            Toast.makeText(this, getString(R.string.Unknown_permission_request), Toast.LENGTH_LONG).show();
        }
    }
    public static class FingerprintHandler extends FingerprintManager.AuthenticationCallback{
        private static final String TAG = FingerprintHandler.class.getSimpleName();
        private Context context;
        public FingerprintHandler(Context context){
            this.context = context;
        }
        @Override
        public void onAuthenticationError(int errorCode, CharSequence errString) {
            super.onAuthenticationError(errorCode, errString);
            Log.d(TAG, "Error message " + errorCode + ": " + errString);
            Toast.makeText(context, context.getString(R.string.authenticate_fingerprint), Toast.LENGTH_LONG).show();
        }
        @Override
        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
            super.onAuthenticationHelp(helpCode, helpString);
            Toast.makeText(context, R.string.auth_successful, Toast.LENGTH_LONG).show();
        }
        @Override
        public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
            super.onAuthenticationSucceeded(result);
            userString = mPref.getUserData();
            mUser = mGson.fromJson(userString, UserObject.class);
            if(mUser != null){
                Toast.makeText(context, context.getString(R.string.auth_successful), Toast.LENGTH_LONG).show();
                if(mUser.isLoginOption()){
                    // login with fingerprint and password
                    showPasswordAuthentication(context);
                }
                else{
                    // login with only fingerprint
                    Intent userIntent = new Intent(context, UserProfileActivity.class);
                    userIntent.putExtra("USER_BIO", userString);
                    context.startActivity(userIntent);
                }
            }else{
                Toast.makeText(context, "You must register before login with fingerprint", Toast.LENGTH_LONG).show();
            }
        }
        @Override
        public void onAuthenticationFailed() {
            super.onAuthenticationFailed();
        }
        public void completeFingerAuthentication(FingerprintManager fingerprintManager, FingerprintManager.CryptoObject cryptoObject){
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            try{
                fingerprintManager.authenticate(cryptoObject, new CancellationSignal(), 0, this, null);
            }catch (SecurityException ex) {
                Log.d(TAG, "An error occurred:\n" + ex.getMessage());
            } catch (Exception ex) {
                Log.d(TAG, "An error occurred\n" + ex.getMessage());
            }
        }
    }
    private static void showPasswordAuthentication(Context context){
        final Dialog openDialog = new Dialog(context);
        openDialog.setContentView(R.layout.password_layout);
        openDialog.setTitle("Enter Password");
        final EditText passwordDialog = (EditText)openDialog.findViewById(R.id.password);
        Button loginWithPasswordButton = (Button)openDialog.findViewById(R.id.login_button);
        loginWithPasswordButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String authPassword = passwordDialog.getText().toString();
                if(TextUtils.isEmpty(authPassword)){
                    Toast.makeText(view.getContext(), "Password field must be filled", Toast.LENGTH_LONG).show();
                    return;
                }
                if(mUser.getPassword().equals(authPassword)){
                    Intent userIntent = new Intent(view.getContext(), UserProfileActivity.class);
                    userIntent.putExtra("USER_BIO", userString);
                    view.getContext().startActivity(userIntent);
                }else{
                    Toast.makeText(view.getContext(), "Incorrect password! Try again", Toast.LENGTH_LONG).show();
                    return;
                }
                openDialog.dismiss();
            }
        });
        openDialog.show();
    }
}

 11. Create the following helper classes

CustomApplication.java

import android.app.Application;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class CustomApplication extends Application {
    private Gson gson;
    private GsonBuilder builder;
    private CustomSharedPreference shared;
    @Override
    public void onCreate() {
        super.onCreate();
        builder = new GsonBuilder();
        gson = builder.create();
        shared = new CustomSharedPreference(getApplicationContext());
    }
    public CustomSharedPreference getShared(){
        return shared;
    }
    public Gson getGsonObject(){
        return gson;
    }
}

 CustomSharedPreference.java

import android.content.Context;
import android.content.SharedPreferences;
public class CustomSharedPreference {
    private SharedPreferences sharedPref;
    public CustomSharedPreference(Context context) {
        sharedPref = context.getSharedPreferences("SHARED_PREF", Context.MODE_PRIVATE);
    }
    public SharedPreferences getInstanceOfSharedPreference(){
        return sharedPref;
    }
    //Save user information
    public void setUserData(String userData){
        sharedPref.edit().putString("USER", userData).apply();
    }
    public String getUserData(){
        return sharedPref.getString("USER", "");
    }
}

 UserObject.java

public class UserObject {
    private String username;
    private String email;
    private String password;
    private String address;
    private String phone;
    private boolean loginOption;
    public UserObject(String username, String email, String password, String address, String phone, boolean loginOption) {
        this.username = username;
        this.email = email;
        this.password = password;
        this.address = address;
        this.phone = phone;
        this.loginOption = loginOption;
    }
    public String getUsername() {
        return username;
    }
    public String getEmail() {
        return email;
    }
    public String getPassword() {
        return password;
    }
    public String getAddress() {
        return address;
    }
    public String getPhone() {
        return phone;
    }
    public boolean isLoginOption() {
        return loginOption;
    }
}

Password_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/_16sdp"
    android:orientation="vertical">
    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="1dp"
        card_view:cardElevation="@dimen/_3sdp"
        card_view:cardUseCompatPadding="true">
        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/password_hint"
            android:textColorHint="@color/colorLight"
            android:inputType="textPassword"
            android:maxLines="1"
            android:background="@android:color/transparent"
            android:textColor="@color/colorBlack"
            android:padding="@dimen/_12sdp" />
    </android.support.v7.widget.CardView>
    <Button
        android:id="@+id/login_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/_24sdp"
        android:paddingBottom="@dimen/_16sdp"
        android:paddingTop="@dimen/_16sdp"
        android:text="@string/login_now"
        android:textAllCaps="true"
        android:textColor="@color/colorWhite"
        android:background="@color/colorAccent"
        android:textStyle="bold" />
</LinearLayout>

12. Create and update UserProfileActivity and its layout file

If the user authentication is successful, the user will be redirect to this activity page and all user bio data will be displayed.

Open the activity_user_profile.xml and add the code below.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_user_profile"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.inducesmile.androidfingerprintlogin.UserProfileActivity">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorAccent"
        android:id="@+id/user_bio"/>
</LinearLayout>

Open UserProfileActivity.java and add the code below.

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.google.gson.Gson;
public class UserProfileActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_profile);
        setTitle("Profile Page");
        String userBio = getIntent().getExtras().getString("USER_BIO");
        Gson gson = ((CustomApplication)getApplication()).getGsonObject();
        UserObject mUserObject = gson.fromJson(userBio, UserObject.class);
        String bio = mUserObject.getUsername() + "\n" +
                mUserObject.getEmail() + "\n" +
                mUserObject.getPhone() + "\n" +
                mUserObject.getAddress() + "\n" +
                mUserObject.getPassword();
        TextView userTextValue = (TextView)findViewById(R.id.user_bio);
        userTextValue.setText(bio);
    }
}

This brings us to the end of this tutorial. I hope that you have learn something. Run your app and see for yourself.

You can download the code for this tutorial above. If you are having hard time downloading the tutorial, kindly contact me.

Remember to subscribe with your email address to be among the first to receive my new android blog post once it is published.

10 Comments

  1. Inducesmile
    • Inducesmile
    • Inducesmile

Add a Comment