1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.example.android.asymmetricfingerprintdialog; 18 19 import com.google.common.annotations.VisibleForTesting; 20 21 import android.hardware.fingerprint.FingerprintManager; 22 import android.os.CancellationSignal; 23 import android.widget.ImageView; 24 import android.widget.TextView; 25 26 import javax.inject.Inject; 27 28 /** 29 * Small helper class to manage text/icon around fingerprint authentication UI. 30 */ 31 public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback { 32 33 @VisibleForTesting static final long ERROR_TIMEOUT_MILLIS = 1600; 34 @VisibleForTesting static final long SUCCESS_DELAY_MILLIS = 1300; 35 36 private final FingerprintManager mFingerprintManager; 37 private final ImageView mIcon; 38 private final TextView mErrorTextView; 39 private final Callback mCallback; 40 private CancellationSignal mCancellationSignal; 41 42 @VisibleForTesting boolean mSelfCancelled; 43 44 /** 45 * Builder class for {@link FingerprintUiHelper} in which injected fields from Dagger 46 * holds its fields and takes other arguments in the {@link #build} method. 47 */ 48 public static class FingerprintUiHelperBuilder { 49 private final FingerprintManager mFingerPrintManager; 50 51 @Inject FingerprintUiHelperBuilder(FingerprintManager fingerprintManager)52 public FingerprintUiHelperBuilder(FingerprintManager fingerprintManager) { 53 mFingerPrintManager = fingerprintManager; 54 } 55 build(ImageView icon, TextView errorTextView, Callback callback)56 public FingerprintUiHelper build(ImageView icon, TextView errorTextView, Callback callback) { 57 return new FingerprintUiHelper(mFingerPrintManager, icon, errorTextView, 58 callback); 59 } 60 } 61 62 /** 63 * Constructor for {@link FingerprintUiHelper}. This method is expected to be called from 64 * only the {@link FingerprintUiHelperBuilder} class. 65 */ FingerprintUiHelper(FingerprintManager fingerprintManager, ImageView icon, TextView errorTextView, Callback callback)66 private FingerprintUiHelper(FingerprintManager fingerprintManager, 67 ImageView icon, TextView errorTextView, Callback callback) { 68 mFingerprintManager = fingerprintManager; 69 mIcon = icon; 70 mErrorTextView = errorTextView; 71 mCallback = callback; 72 } 73 isFingerprintAuthAvailable()74 public boolean isFingerprintAuthAvailable() { 75 return mFingerprintManager.isHardwareDetected() 76 && mFingerprintManager.hasEnrolledFingerprints(); 77 } 78 startListening(FingerprintManager.CryptoObject cryptoObject)79 public void startListening(FingerprintManager.CryptoObject cryptoObject) { 80 if (!isFingerprintAuthAvailable()) { 81 return; 82 } 83 mCancellationSignal = new CancellationSignal(); 84 mSelfCancelled = false; 85 mFingerprintManager 86 .authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null); 87 mIcon.setImageResource(R.drawable.ic_fp_40px); 88 } 89 stopListening()90 public void stopListening() { 91 if (mCancellationSignal != null) { 92 mSelfCancelled = true; 93 mCancellationSignal.cancel(); 94 mCancellationSignal = null; 95 } 96 } 97 98 @Override onAuthenticationError(int errMsgId, CharSequence errString)99 public void onAuthenticationError(int errMsgId, CharSequence errString) { 100 if (!mSelfCancelled) { 101 showError(errString); 102 mIcon.postDelayed(new Runnable() { 103 @Override 104 public void run() { 105 mCallback.onError(); 106 } 107 }, ERROR_TIMEOUT_MILLIS); 108 } 109 } 110 111 @Override onAuthenticationHelp(int helpMsgId, CharSequence helpString)112 public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { 113 showError(helpString); 114 } 115 116 @Override onAuthenticationFailed()117 public void onAuthenticationFailed() { 118 showError(mIcon.getResources().getString( 119 R.string.fingerprint_not_recognized)); 120 } 121 122 @Override onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)123 public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { 124 mErrorTextView.removeCallbacks(mResetErrorTextRunnable); 125 mIcon.setImageResource(R.drawable.ic_fingerprint_success); 126 mErrorTextView.setTextColor( 127 mErrorTextView.getResources().getColor(R.color.success_color, null)); 128 mErrorTextView.setText( 129 mErrorTextView.getResources().getString(R.string.fingerprint_success)); 130 mIcon.postDelayed(new Runnable() { 131 @Override 132 public void run() { 133 mCallback.onAuthenticated(); 134 } 135 }, SUCCESS_DELAY_MILLIS); 136 } 137 showError(CharSequence error)138 private void showError(CharSequence error) { 139 mIcon.setImageResource(R.drawable.ic_fingerprint_error); 140 mErrorTextView.setText(error); 141 mErrorTextView.setTextColor( 142 mErrorTextView.getResources().getColor(R.color.warning_color, null)); 143 mErrorTextView.removeCallbacks(mResetErrorTextRunnable); 144 mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS); 145 } 146 147 @VisibleForTesting 148 Runnable mResetErrorTextRunnable = new Runnable() { 149 @Override 150 public void run() { 151 mErrorTextView.setTextColor( 152 mErrorTextView.getResources().getColor(R.color.hint_color, null)); 153 mErrorTextView.setText( 154 mErrorTextView.getResources().getString(R.string.fingerprint_hint)); 155 mIcon.setImageResource(R.drawable.ic_fp_40px); 156 } 157 }; 158 159 public interface Callback { 160 onAuthenticated()161 void onAuthenticated(); 162 onError()163 void onError(); 164 } 165 } 166