I noticed something rather odd. The fingerprint authentication function is widely used on iPhone, but it is rarely used on Android phones. After a brief observation, I found that only Alipay, wechat and few apps support the fingerprint authentication function on Android phones, even banking and financial apps are not supported. Many developers don’t even know that Android has an official API for fingerprint authentication.

In fact, Android has supported fingerprint authentication since OS 6.0, but it requires hardware support, and Android hardware is made by a wide variety of manufacturers, so there is no guarantee that all Android phones will support fingerprint authentication like the iPhone. Therefore, many developers may feel that even with fingerprint authentication, it is not compatible with all mobile phones, or with pattern unlock or password functions, so just use pattern and password, once and for all.

That may seem like a reasonable explanation, but it’s the hundreds of millions of Android phone users who are getting hurt. There is an easier and faster way to use, but because the APP does not support it, we have to use a more primitive and clumsy way. In China, the fingerprint authentication function of most Android phones is only used to unlock the phone, and is rarely used in the functional logic of the APP.

In fact, there are many functional scenarios for applying the fingerprint authentication function into the functional logic of apps. For example, financial and banking apps can use fingerprint authentication to quickly log in, APP store apps can use fingerprint authentication to download and install software, and stock and securities apps can use fingerprint authentication to operate and trade, etc.

Despite the application scenario, many developers may be concerned about whether fingerprint authentication will be too complicated to implement. After all, the supported devices are limited, and patterns and passwords are needed to be used. If the implementation is very complicated and only supports part of the devices, the input-output ratio is too low, which may be the reason why many apps refuse to implement fingerprint authentication. I have to say that the Android Demo for fingerprint authentication is quite complex and daunting to watch. But don’t worry, in this article I will take you to implement a minimal version of the fingerprint authentication Demo, directly copy and paste the code in this article into your own projects, you can step integration of fingerprint authentication function.

one

So without further ado, first create a New FingerprintTest project and select to add an Empty Activity. Then modify the code in activity_main.xml as follows:

<? The XML version = "1.0" encoding = "utf-8"? > <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" Android :textSize="18sp" Android :layout_gravity="center" /> </FrameLayout>Copy the code

Here, we modify the layout file in MainActivity and add a TextView that has entered the main interface of the App on the interface. After the fingerprint authentication is passed, the App will jump to this interface.

To start writing the fingerprint authentication interface, create a new fingerprint_dialog.xml and look like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:src="@drawable/ic_fp_40px"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="20dp"
        android:text="请验证指纹解锁"
        android:textColor="#000"
        android:textSize="16sp"
        />

    <TextView
        android:id="@+id/error_msg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="5dp"
        android:maxLines="1"
        android:textSize="12sp"
        android:textColor="#f45"
        />

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_marginTop="10dp"
        android:background="#ccc"
        />

    <TextView
        android:id="@+id/cancel"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="取消"
        android:textColor="#5d7883"
        android:textSize="16sp"
        />

</LinearLayout>
Copy the code

This is a very simple fingerprint authentication interface, I believe there is nothing to explain. The following figure shows the general interface style.

Note that in order to make it clear to users that fingerprint authentication is now required, Google officials recommend using a common fingerprint icon rather than having each APP create its own fingerprint icon. To this end, Google also specially provides a set of fingerprint authentication set of pictures, you can click here to view and download.

two

We then create a FingerprintDialogFragment class, and make it inherit from DialogFragment, used to prompt the user for fingerprint authentication dialog box, the code is as follows:

@TargetApi(23) public class FingerprintDialogFragment extends DialogFragment { private FingerprintManager fingerprintManager; private CancellationSignal mCancellationSignal; private Cipher mCipher; private LoginActivity mActivity; private TextView errorMsg; /** * Indicates whether the user actively cancels the authentication. */ private boolean isSelfCancelled; public void setCipher(Cipher cipher) { mCipher = cipher; } @Override public void onAttach(Context context) { super.onAttach(context); mActivity = (LoginActivity) getActivity(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); fingerprintManager = getContext().getSystemService(FingerprintManager.class); setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fingerprint_dialog, container, false); errorMsg = v.findViewById(R.id.error_msg); TextView cancel = v.findViewById(R.id.cancel); cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); stopListening(); }}); return v; } @Override public void onResume() { super.onResume(); startListening(mCipher); } @Override public void onPause() { super.onPause(); stopListening(); } private void startListening(Cipher cipher) { isSelfCancelled = false; mCancellationSignal = new CancellationSignal(); fingerprintManager.authenticate(new FingerprintManager.CryptoObject(cipher), mCancellationSignal, 0, new FingerprintManager.AuthenticationCallback() { @Override public void onAuthenticationError(int errorCode, CharSequence errString) { if (! isSelfCancelled) { errorMsg.setText(errString); if (errorCode == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) { Toast.makeText(mActivity, errString, Toast.LENGTH_SHORT).show(); dismiss(); } } } @Override public void onAuthenticationHelp(int helpCode, CharSequence helpString) { errorMsg.setText(helpString); } @Override public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { Toast.maketext (mActivity, "Fingerprint authentication succeeded ", toast.length_short).show(); mActivity.onAuthenticated(); } @override public void onAuthenticationFailed() {errorMsg. SetText (" Fingerprint authentication failed, please try again "); } }, null); } private void stopListening() { if (mCancellationSignal ! = null) { mCancellationSignal.cancel(); mCancellationSignal = null; isSelfCancelled = true; }}}Copy the code

The code here is also very simple. Basically, it is the most common implementation of a Fragment class. Below, I take you to simply parse it.

First, the setCipher() method is used to accept a Cipher object, which will be used later in fingerprint authentication.

three

The next few lifecycle methods are simple: get an instance of the Activity in onAttach() and a FingerprintManager instance in onCreate(), Loading the fingerprint_dialog.xml layout we just created in the onCreateView() method is a general operation.

StartListening () is called in onResume() to start fingerprint listening, and stopListening() is called in onPause() to stop fingerprint listening. Why do you do that? A fingerprint sensor, like a camera, cannot be used by multiple applications at the same time. Therefore, no application should use the fingerprint sensor resources at any non-foreground time. Therefore, use onPause() to release the resources in a timely manner.

So now all we need to do is focus on the startListening() and stopListening() methods. In the startListening() method, the Authenticate () method of FingerprintManager is called to enable fingerprint fingerprint listening. The Authenticate () method receives five parameters. The first parameter is a CryptoObject object. Here we just need to wrap the Cipher object passed in as a CryptoObject object. The second parameter is the CancellationSignal object, which you can use to cancel the fingerprint authentication operation. The third parameter is optional, and the official recommendation is to simply pass 0. The fourth parameter is used to receive callbacks for fingerprint authentication. In the above code, I have probably prompted all callbacks so that you can observe them. The fifth parameter specifies the Handler to handle the callback, passing NULL to indicate that the callback is to the main thread.

In stopListening(), the logic is much simpler; we simply call cancel() of CancellationSignal to cancel fingerprint authentication.

So we will FingerprintDialogFragment code in all finished, this code can be directly copied to any project as a fingerprint authentication alert dialog box.

four

Finally, we will write a simple login interface, the whole fingerprint authentication process is complete. Create the LoginActivity as follows:

public class LoginActivity extends AppCompatActivity { private static final String DEFAULT_KEY_NAME = "default_key"; KeyStore keyStore; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); if (supportFingerprint()) { initKey(); initCipher(); }} public Boolean supportFingerprint() {if (build.version.sdk_int < 23) {toast.maketext (this, "Your system VERSION is too old, ", toast.length_short).show(); return false; } else { KeyguardManager keyguardManager = getSystemService(KeyguardManager.class); FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class); if (! FingerprintManager. IsHardwareDetected ()) {Toast. MakeText (this, "your phone does not support the fingerprint feature", Toast. LENGTH_SHORT), show (); return false; } else if (! KeyguardManager. IsKeyguardSecure ()) {Toast. MakeText (this, "you have not set up the lock screen, please set the lock screen and add a fingerprint", Toast. LENGTH_SHORT), show (); return false; } else if (! FingerprintManager. HasEnrolledFingerprints ()) {Toast. MakeText (this, "you need at least a fingerprint" is added in the system Settings, Toast. LENGTH_SHORT), show (); return false; } } return true; } @TargetApi(23) private void initKey() { try { keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(DEFAULT_KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7); keyGenerator.init(builder.build()); keyGenerator.generateKey(); } catch (Exception e) { throw new RuntimeException(e); } } @TargetApi(23) private void initCipher() { try { SecretKey key = (SecretKey) keyStore.getKey(DEFAULT_KEY_NAME, null); Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); cipher.init(Cipher.ENCRYPT_MODE, key); showFingerPrintDialog(cipher); } catch (Exception e) { throw new RuntimeException(e); } } private void showFingerPrintDialog(Cipher cipher) { FingerprintDialogFragment fragment = new FingerprintDialogFragment(); fragment.setCipher(cipher); fragment.show(getFragmentManager(), "fingerprint"); } public void onAuthenticated() { Intent intent = new Intent(this, MainActivity.class); startActivity(intent); finish(); }}Copy the code

First, the supportFingerprint() method is called in the onCreate() method to determine whether the current device supports fingerprint authentication. This is important because when the device does not support fingerprint authentication, it is necessary to switch to other authentication methods such as pattern and password in time.

When the device supports fingerprint authentication, it is divided into two steps. The first step is to generate a symmetric encryption Key, and the second step is to generate a Cipher object, which are standard usage requirements of the Android FINGERPRINT authentication API. After got the Cipher object, we create FingerprintDialogFragment instance, and the Cipher object into the, then FingerprintDialogFragment display is ok.

five

After the last of the last fingerprint authentication, when successful, will call in the correction of FingerprintDialogFragment LoginActivity onAuthenticated () method, then the interface will jump to MainActivity, That’s the end of the fingerprint authentication process.

That’s all there is to it, but overall it’s pretty simple, so let’s run it and see what it looks like in action. After opening the app, the fingerprint authentication dialog box will pop up immediately. At this time, use the wrong finger first to authenticate:

When fingerprint authentication fails, an error message is displayed on the interface.

Next use the correct finger for authentication:

OK, the fingerprint verification is successful, and the MainActivity interface is automatically jumped.

This is the simplest version of the fingerprint authentication Demo. If you want to integrate fingerprint authentication into your APP, just copy and paste the code in this article. If you want to download the full Demo source, click here to download it.

At the end of this article I would like to add that although the fingerprint authentication Demo implementation in this article is very simple, it is important to remember that it cannot be used alone. It must be used in conjunction with patterns or other authentication methods, because it is important to provide an alternative authentication method when the device does not support fingerprints.

Unfortunately, although I just wrote this article, FingerprintManager has been deprecated on the latest Android 9.0 OS. Because Android 9.0 offers more powerful biometric authentication features, including fingerprint, face, and even iris recognition, FingerprintManager, which is only used for fingerprint recognition, is no longer enough to meet the demands of the new system.

Don’t worry, though, FingerprintManager is still available, at least for a long time. I’ll be writing another post on Android 9.0 biometrics in the near future, so stay tuned.

Pay attention to my technical public account “Guo Lin”, high-quality technical articles push.