概要
生体認証ダイアログを表示する Android Developers
・公式サイトにもあるような、生体認証ダイアログを用いた認証を作成します。
このダイアログでは
- 指紋認証
- 顔認証
- 虹彩認証
など、端末のロック解除方法に登録してあるものを使用して、ユーザ認証が行えます。
・公式サイト内ではAndroid Viewを使用していますが、今回はせっかくなのでJetpack Composeで作成します。
開発環境
・Macbook Pro (Apple M2 Pro)
・macOS Sonoma
・Android Studio Ladybug 2024.2.1 Patch2
実行端末
・Google Pixel9 (Android 14)
エミュレータではテストができないため、Android実機端末が必要です
※完成画像などが使用できていないのもこのためです。実機のスクショがうまく出来ませんでした...
実装方法
プロジェクトの作成などは各自で行ってください。
1.必要なライブラリを追加する
・認証機能の実装には、androidx.biometricが必要なので、これを追加します。
build.gradle.kts
implementation("androidx.biometric:biometric-ktx:1.4.0-alpha02")
※2024/12/02時点の最新版(安定版は1.1.0)
2.認証ダイアログの作成
Composableで作成します
@Composable
fun BiometricAuthenticationDialog(
onAuthSuccess: () -> Unit,
onAuthError: (String) -> Unit
) {
val context = LocalContext.current
val executor = ContextCompat.getMainExecutor(context)
val biometricPrompt = remember {
BiometricPrompt(
context as FragmentActivity,
executor,
object : BiometricPrompt.AuthenticationCallback() {
//if auth is successful.
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
onAuthSuccess()
}
//if auth is error.
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
onAuthError(errString.toString())
}
//if auth is failed.
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
onAuthError("Authentication failed.")
}
}
)
}
//ダイアログに表示するタイトルやテキストをセットする
LaunchedEffect(Unit) {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Authentication")
.setSubtitle("Please touch your finger")
.setNegativeButtonText("Cancel")
.build()
biometricPrompt.authenticate(promptInfo)
}
}
これに加えて、この関数を呼び出す親のアクティビティの継承元を
ComponentActivity
からFragmentActivity
に変更しておきます。(理由は後述)
class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Favorite_actor_appTheme {
MyApp()
}
}
}
}
解説
作成したBiometricAuthenticationDialog
は二つの引数を持たせます。
-
onAuthSuccess
: 認証が成功した時の動作を定義する関数 -
onAuthError
:認証に失敗した時の動作を定義する関数
onAuthError
には、エラー内容を渡すため、String型の引数を定義しておきます
val context = LocalContext.current
val executor = ContextCompat.getMainExecutor(context)
この後作成する認証時の動作を定義するBIometricPrompt
の引数としてFragmentActivity
とexecutor
が必要なため、あらかじめ定義しておきます。
exexutor
は処理を行うスレッドやタスクを管理するためのもので、今回はメインスレッドで処理を行う必要があるため、上記のように定義しておきます。
val biometricPrompt = remember {
BiometricPrompt(
context as FragmentActivity,
executor,
object : BiometricPrompt.AuthenticationCallback() {
//if auth is successful.
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
onAuthSuccess()
}
//if auth is error.
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
onAuthError(errString.toString())
}
//if auth is failed.
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
onAuthError("Authentication failed.")
}
}
)
}
生体認証の動作の核となるBiometricPrompt
を定義します
BiometricPrompt
メソッドの実装は以下のようになっています
public BiometricPrompt(
@NonNull FragmentActivity activity,
@NonNull Executor executor,
@NonNull AuthenticationCallback callback) {
if (activity == null) {
throw new IllegalArgumentException("FragmentActivity must not be null.");
}
if (executor == null) {
throw new IllegalArgumentException("Executor must not be null.");
}
if (callback == null) {
throw new IllegalArgumentException("AuthenticationCallback must not be null.");
}
final FragmentManager fragmentManager = activity.getSupportFragmentManager();
final BiometricViewModel viewModel =
new ViewModelProvider(activity).get(BiometricViewModel.class);
init(true /* hostedInActivity */, fragmentManager, viewModel, executor, callback);
}
このため、まず第1引数としてFragmentActivity
を渡す必要があります。
すでに先ほど現在のコンテキストを取得しているため、
context as FragmentActivity,
として渡します。
先ほど、呼び出し先の親アクティビティをFragmentActivity
に変更したのはこのためです。
ここで親アクティビティがComponentActivity
を使用していると、実行時にエラーが発生します。
第2引数には先ほど定義したexeutorを渡します。
そして最後に第3引数として、認証時の動作を定義するコールバックを作成します。
object : BiometricPrompt.AuthenticationCallback() {
//if auth is successful.
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
onAuthSuccess()
}
//if auth is error.
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
onAuthError(errString.toString())
}
//if auth is failed.
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
onAuthError("Authentication failed.")
}
}
見ての通り、BiometricPrompt.AuthenticationCallback
には3つのメソッドがあり、
-
onAuthenticationSucceeded
には成功時の動作 -
onAuthenticationError
にはなんらかのエラーが発生した時の動作 -
onAuthenticatoinFailed
には認証失敗時の動作
をそれぞれ定義します。
あとは、認証ダイアログに表示される文字などを定義して、ダイアログを表示させます。
LaunchedEffect(Unit) {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
//ダイアログ上部に表示されるタイトル文字列
.setTitle("Authentication")
//タイトル下に表示される文字列
.setSubtitle("Please touch your finger")
//ダイアログ左下に表示されるネガティブボタンの文字(主にキャンセル用)
.setNegativeButtonText("Cancel")
.build()
//ダイアログを表示
biometricPrompt.authenticate(promptInfo)
}
3.呼び出し
ここまでこれば、作成したBiometricAuthenticationDialog
Composableを呼び出すだけです。
@Composable
fun TopPage(modifier: Modifier = Modifier) {
var showBiometricDialog by remember { mutableStateOf(false) }
var biometricResult by remember { mutableStateOf<String?>(null) }
if (showBiometricDialog) {
BiometricAuthenticationDialog(
onAuthSuccess = { biometricResult = "Success" },
onAuthError = { error ->
biometricResult = error
showBiometricDialog = false
}
)
}
Box(
modifier = modifier.fillMaxSize(),
) {
Image(
painter = painterResource(R.drawable.actor),
contentDescription = "background",
contentScale = ContentScale.Crop
)
Box(
modifier = Modifier
.fillMaxSize()
.background(color = colorResource(R.color.dark_effect))
)
Text(
"",
style = TextStyle(color = colorResource(R.color.white), fontSize = 30.sp),
modifier = Modifier.align(
Alignment.Center
)
)
Button(
onClick = { showBiometricDialog = true }, modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 200.dp)
) {
Text("Authorization and Start")
}
}
}
この例では、ボタンが押されるとshowBiometricDialog
変数がtrue
になり、ダイアログが呼び出されます。
また、認証結果が成功の場合はbiometricResult
にSuccess
が代入され、失敗時は受け取ったエラー文が代入されるようになっています。
実際にはこの後navController
などを使用して画面遷移などを行うことになると思います。
実装は以上です。
まとめ
作成してみた感想としては、意外と簡単でシンプルな印象を持ちました。
インターネット接続なども必要なく、お手軽ながらもしっかりした認証を作成できて自分はとても好印象でした!