안드로이드에서 제공하는 생체 인증 방식은 Fingerprint, Biometric 가 있습니다.
Marshmallow(6.0) ~ Oreo(8.0) : Fingerprint
Pie(9.0) 이상 ~ : Biometric
Android API Level 29 부터는 Biometric 사용을 권장합니다.
Biometric 를 사용한 지문인식를 Kotlin 으로 구현한 소스입니다.
작성한 파일 목록 입니다.
1. build.gradle(:app)
2. AndroidManifest.xml
3. BiometricActivity.kt
4. activity_biometric.xml
build.gradle(:app)
dependencies 에 implementation 'androidx.biometric:biometric:1.1.0' 를 추가해줍니다.
android {
...
buildFeatures {
viewBinding = true
}
}
dependencies {
...
//Bio
implementation 'androidx.biometric:biometric:1.1.0'
...
}
AndroidManifest.xml
manifest 에 <uses-permission android:name="android.permission.USE_BIOMETRIC" /> 권한을 추가해줍니다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.eun.mykotlin">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<application
...
</application>
</manifest>
BiometricActivity.kt
btn_bio_set 버튼을 클릭 시 authenticateToEncrypt() 를 호출하여 생체 인증 가능 여부를 확인하고,
확인 후 goAuthenticate() 메소드를 호출하여 biometricPrompt?.authenticate() 를 통해 생체 인식을 실행합니다.
인증 후 setBiometricPrompt() 의 BiometricPrompt.AuthenticationCallback() 에서 결과를 콜백받습니다.
생체 인식 정보가 등록되어 있지 않은 경우, goBiometricSettings() 을 호출하여 지문등록 설정화면으로 이동되고,
등록 후 registerForActivityResult() 내부에서 다시 authenticateToEncrypt() 를 호출하여 상태를 확인합니다.
package com.eun.mykotlin
import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager
import android.os.Build
import android.provider.Settings
import androidx.biometric.BiometricPrompt
import com.eun.mytmapkotlin.databinding.ActivityBiometricBinding
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt.PromptInfo
import androidx.core.content.ContextCompat
import java.util.concurrent.Executor
class BiometricActivity: AppCompatActivity() {
companion object {
const val TAG: String = "BiometricActivity"
}
lateinit var binding: ActivityBiometricBinding
private var executor: Executor? = null
private var biometricPrompt: BiometricPrompt? = null
private var promptInfo: BiometricPrompt.PromptInfo? = null
private val loginLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
Log.d(TAG, "registerForActivityResult - result : $result")
if (result.resultCode == Activity.RESULT_OK) {
Log.d(TAG, "registerForActivityResult - RESULT_OK")
authenticateToEncrypt() //생체 인증 가능 여부확인 다시 호출
} else {
Log.d(TAG, "registerForActivityResult - NOT RESULT_OK")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContentView(R.layout.activity_biometric)
binding = ActivityBiometricBinding.inflate(layoutInflater)
setContentView(binding.root)
biometricPrompt = setBiometricPrompt()
promptInfo = setPromptInfo()
//지문 인증 호출 버튼 클릭 시
binding.btnBioSet.setOnClickListener {
authenticateToEncrypt() //생체 인증 가능 여부확인
}
}
private fun setPromptInfo(): BiometricPrompt.PromptInfo {
val promptBuilder: BiometricPrompt.PromptInfo.Builder = BiometricPrompt.PromptInfo.Builder()
promptBuilder.setTitle("Biometric login for my app")
promptBuilder.setSubtitle("Log in using your biometric credential")
promptBuilder.setNegativeButtonText("Use account password")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // 안면인식 ap사용 android 11부터 지원
promptBuilder.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
}
promptInfo = promptBuilder.build()
return promptInfo as PromptInfo
}
private fun setBiometricPrompt(): BiometricPrompt {
executor = ContextCompat.getMainExecutor(this)
biometricPrompt = BiometricPrompt(this@BiometricActivity, executor!!, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Toast.makeText(this@BiometricActivity, """"지문 인식 ERROR [ errorCode: $errorCode, errString: $errString ]""".trimIndent(), Toast.LENGTH_SHORT).show()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Toast.makeText(this@BiometricActivity, "지문 인식 성공", Toast.LENGTH_SHORT).show()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Toast.makeText(this@BiometricActivity, "지문 인식 실패", Toast.LENGTH_SHORT).show()
}
} )
return biometricPrompt as BiometricPrompt
}
/*
* 생체 인식 인증을 사용할 수 있는지 확인
* */
fun authenticateToEncrypt() = with(binding) {
Log.d(TAG, "authenticateToEncrypt() ")
var textStatus = ""
val biometricManager = BiometricManager.from(this@BiometricActivity)
// when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) {
//생체 인증 가능
BiometricManager.BIOMETRIC_SUCCESS -> textStatus = "App can authenticate using biometrics."
//기기에서 생체 인증을 지원하지 않는 경우
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> textStatus = "No biometric features available on this device."
//현재 생체 인증을 사용할 수 없는 경우
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> textStatus = "Biometric features are currently unavailable."
//생체 인식 정보가 등록되어 있지 않은 경우
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
textStatus = "Prompts the user to create credentials that your app accepts."
val dialogBuilder = AlertDialog.Builder(this@BiometricActivity)
dialogBuilder
.setTitle("나의앱")
.setMessage("지문 등록이 필요합니다. 지문등록 설정화면으로 이동하시겠습니까?")
.setPositiveButton("확인") { dialog, which -> goBiometricSettings() }
.setNegativeButton("취소") {dialog, which -> dialog.cancel() }
dialogBuilder.show()
}
//기타 실패
else -> textStatus = "Fail Biometric facility"
}
binding.tvStatus.text = textStatus
//인증 실행하기
goAuthenticate()
}
/*
* 생체 인식 인증 실행
* */
private fun goAuthenticate() {
Log.d(TAG, "goAuthenticate - promptInfo : $promptInfo")
promptInfo?.let {
biometricPrompt?.authenticate(it); //인증 실행
}
}
/*
* 지문 등록 화면으로 이동
* */
fun goBiometricSettings() {
val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {
putExtra(
Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
}
loginLauncher.launch(enrollIntent)
}
}
activity_biometric.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="Biometric"
android:textStyle="bold"
android:textSize="20sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/tv_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Biometric status ..."
android:textSize="15sp"
android:background="@color/amber_200"
app:layout_constraintTop_toBottomOf="@id/tv_title"
app:layout_constraintBottom_toTopOf="@id/btn_bio_set"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/btn_bio_set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="지문 인증 호출"
app:layout_constraintTop_toBottomOf="@id/tv_status"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
안드로이드 결과화면
https://developer.android.com/training/sign-in/biometric-auth?hl=ko
감사합니당 :)
'📱 안드로이드 Android ~ Kotlin' 카테고리의 다른 글
[Android/kotlin] 구글 Firebase Crashlytics 사용하기 (0) | 2022.06.09 |
---|---|
[Android/Kotiln] Jetpack Navigation 사용하기 (0) | 2022.05.26 |
[Kotlin/코틀린] 5 - 상속과 클래스 종류 (data 클래스, object 클래스, companion 클래스) (1) | 2022.02.04 |
[Kotlin/코틀린] 4 - 널(null) 안전성 (0) | 2022.02.04 |
[Kotlin/코틀린] 3 - 클래스와 생성자 (0) | 2022.02.04 |