前回、指紋認証 API のサンプルコードをざっと見てみたのですが、そのときに FingerprintManagerCompat クラスが Support Library v4 にあるのを見つけました。
このクラスを使えば Android 6.0 (API level 23 未満) も含め、スッキリと実装できるんじゃないかなと思い調べてみました。
FingerprintManager のインスタンス取得
前回のGoogleのサンプルコードでは、以下のようなコードでインスタンスを取得していました。
FingerprintManager manager = context.getSystemService(FingerprintManager.class);
ところが以下の2点で API level 23 未満では、getSystemService() 経由だと使えません。
Context.getSystemService(Class<T>)
は API level 23から追加されたメソッドContext.getSystemService(String)
だと引数のサービス名に Fingerprint は無い
そのため FingerprintManagerCompat では、別途 Context を使った方法が用意されていました。
// contextはActivityなど
FingerprintManagerCompat managerCompat = FingerprintManagerCompat.from(context);
FingerprintManagerCompat のソースコードを辿って行くと、内部で Build.VERSION.SDK_INT
をチェックしていて、23 以上だと Api23FingerprintManagerCompatImpl
内部クラスを、23 未満だと LegacyFingerprintManagerCompatImpl
内部クラスを、それぞれインスタンス化していました。
指紋センサーの判定など
FingerprintManager では、端末に指紋センサーのハードウェアがあるか?や、指紋の登録があるか?などを判定するメソッドを提供しています。
まったく同じメソッドが、FingerprintManagerCompat クラスにも実装されていますが、どのような挙動になるのか確認してみました。
結論から書くと、API level 23 以上だとちゃんと FingerprintManager 経由で判定し、23 未満だと固定で false を返していました。
API level 23 未満
private static class LegacyFingerprintManagerCompatImpl implements FingerprintManagerCompatImpl { public LegacyFingerprintManagerCompatImpl() { } @Override public boolean hasEnrolledFingerprints(Context context) { return false; } @Override public boolean isHardwareDetected(Context context) { return false; } ... } }
API level 23 以上
private static class Api23FingerprintManagerCompatImpl implements FingerprintManagerCompatImpl { public Api23FingerprintManagerCompatImpl() { } @Override public boolean hasEnrolledFingerprints(Context context) { return FingerprintManagerCompatApi23.hasEnrolledFingerprints(context); } @Override public boolean isHardwareDetected(Context context) { return FingerprintManagerCompatApi23.isHardwareDetected(context); } ...
public final class FingerprintManagerCompatApi23 { private static FingerprintManager getFingerprintManager(Context ctx) { return ctx.getSystemService(FingerprintManager.class); } public static boolean hasEnrolledFingerprints(Context context) { return getFingerprintManager(context).hasEnrolledFingerprints(); } public static boolean isHardwareDetected(Context context) { return getFingerprintManager(context).isHardwareDetected(); } ...
分かりやすくいいですね。
指紋認証
実際に指紋センサーから指紋を読取るときは、 FingerprintManager#authenticate(CryptoObject, CancellationSignal, int, AuthenticationCallback, Handler)
メソッドを使ってましたが、これも先ほどと同じ形でした。
API level 23 以上では、FingerprintManager 経由で処理し、23 未満では何もしません。
API level 23 未満
private static class LegacyFingerprintManagerCompatImpl implements FingerprintManagerCompatImpl { ... @Override public void authenticate(Context context, CryptoObject crypto, int flags, CancellationSignal cancel, AuthenticationCallback callback, Handler handler) { // TODO: Figure out behavior when there is no fingerprint hardware available } }
API level 23 以上
private static class Api23FingerprintManagerCompatImpl implements FingerprintManagerCompatImpl { ... @Override public void authenticate(Context context, CryptoObject crypto, int flags, CancellationSignal cancel, AuthenticationCallback callback, Handler handler) { FingerprintManagerCompatApi23.authenticate(context, wrapCryptoObject(crypto), flags, cancel != null ? cancel.getCancellationSignalObject() : null, wrapCallback(callback), handler); }
public final class FingerprintManagerCompatApi23 { ... public static void authenticate(Context context, CryptoObject crypto, int flags, Object cancel, AuthenticationCallback callback, Handler handler) { getFingerprintManager(context).authenticate(wrapCryptoObject(crypto), (android.os.CancellationSignal) cancel, flags, wrapCallback(callback), handler); }
まとめ
当分 API level 23 以上でアプリを開発出来る状況は来ないので、開発しているアプリに指紋認証を導入したいときは、この FingerprintManagerCompat
を使うのが良さそうです。
今まで割と自力で API level 判定して処理を分けていたようなところも、xxxCompat クラスがいい感じに内部で切り替えてくれるケースが増えてきた気がします。
新しい機能が出てきたら、まずは xxxCompat クラスも一緒に探すと、既存アプリへの導入が楽になりそうですね。