/* * Copyright 2015 Marvin Ramin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.mtramin.rxfingerprint; import android.annotation.SuppressLint; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.hardware.fingerprint.FingerprintManager.CryptoObject; import android.os.Build; import android.os.CancellationSignal; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.annotation.RequiresPermission; import com.mtramin.rxfingerprint.data.FingerprintAuthenticationException; import com.mtramin.rxfingerprint.data.FingerprintUnavailableException; import io.reactivex.Emitter; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; import io.reactivex.functions.Cancellable; import static android.Manifest.permission.USE_FINGERPRINT; /** * Base observable for Fingerprint authentication. Provides abstract methods that allow * to alter the input and result of the authentication. */ @SuppressLint("NewApi") // SDK check happens in {@link FingerprintObservable#subscribe} abstract class FingerprintObservable<T> implements ObservableOnSubscribe<T> { private final FingerprintApiWrapper fingerprintApiWrapper; CancellationSignal cancellationSignal; /** * Default constructor for fingerprint authentication * * @param context Context to be used for the fingerprint authentication */ FingerprintObservable(FingerprintApiWrapper fingerprintApiWrapper) { this.fingerprintApiWrapper = fingerprintApiWrapper; } @Override @RequiresPermission(USE_FINGERPRINT) @RequiresApi(Build.VERSION_CODES.M) public void subscribe(ObservableEmitter<T> emitter) throws Exception { if (fingerprintApiWrapper.isUnavailable()) { emitter.onError(new FingerprintUnavailableException("Fingerprint authentication is not available on this device! Ensure that the device has a Fingerprint sensor and enrolled Fingerprints by calling RxFingerprint#isAvailable(Context) first")); return; } AuthenticationCallback callback = createAuthenticationCallback(emitter); cancellationSignal = fingerprintApiWrapper.createCancellationSignal(); CryptoObject cryptoObject = initCryptoObject(emitter); //noinspection MissingPermission fingerprintApiWrapper.getFingerprintManager().authenticate(cryptoObject, cancellationSignal, 0, callback, null); emitter.setCancellable(new Cancellable() { @Override public void cancel() throws Exception { if (cancellationSignal != null && !cancellationSignal.isCanceled()) { cancellationSignal.cancel(); } } }); } private AuthenticationCallback createAuthenticationCallback(final ObservableEmitter<T> emitter) { return new AuthenticationCallback() { @Override public void onAuthenticationError(int errMsgId, CharSequence errString) { if (!emitter.isDisposed()) { emitter.onError(new FingerprintAuthenticationException(errString)); } } @Override public void onAuthenticationFailed() { FingerprintObservable.this.onAuthenticationFailed(emitter); } @Override public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { FingerprintObservable.this.onAuthenticationHelp(emitter, helpMsgId, helpString.toString()); } @Override public void onAuthenticationSucceeded(AuthenticationResult result) { FingerprintObservable.this.onAuthenticationSucceeded(emitter, result); } }; } /** * Method to initialize the {@link FingerprintManager.CryptoObject} * used for the fingerprint authentication. * * @param subscriber current subscriber * @return a {@link FingerprintManager.CryptoObject} * that is to be used in the authentication. May be {@code null}. */ @Nullable protected abstract CryptoObject initCryptoObject(ObservableEmitter<T> subscriber); /** * Action to execute when fingerprint authentication was successful. * Should return the needed result via the given {@link Emitter}. * <p/> * Should call {@link Emitter#onComplete()}. * * @param emitter current subscriber * @param result result of the successful fingerprint authentication */ protected abstract void onAuthenticationSucceeded(ObservableEmitter<T> emitter, AuthenticationResult result); /** * Action to execute when the fingerprint authentication returned a help result. * Should return the needed actions to the subscriber via the given {@link Emitter}. * <p/> * Should <b>not</b> {@link Emitter#onComplete()}. * * @param emitter current subscriber * @param helpMessageId ID of the help message returned from the {@link FingerprintManager} * @param helpString Help message string returned by the {@link FingerprintManager} */ protected abstract void onAuthenticationHelp(ObservableEmitter<T> emitter, int helpMessageId, String helpString); /** * Action to execute when the fingerprint authentication failed. * Should return the needed action to the given {@link Emitter}. * <p/> * Should only call {@link Emitter#onComplete()} when fingerprint authentication should be * canceled due to the failed event. * * @param emitter current subscriber */ protected abstract void onAuthenticationFailed(ObservableEmitter<T> emitter); }