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