일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Rxjava Observable
- android retrofit login
- 큐 자바 코드
- rxjava hot observable
- 안드로이드 유닛테스트란
- 안드로이드 라이선스 종류
- 스택 큐 차이
- 안드로이드 레트로핏 사용법
- 안드로이드 라이선스
- 클래스
- jvm이란
- 객체
- 플러터 설치 2022
- rxjava cold observable
- 안드로이드 레트로핏 crud
- 자바 다형성
- ANR이란
- 서비스 쓰레드 차이
- 2022 플러터 안드로이드 스튜디오
- 서비스 vs 쓰레드
- android ar 개발
- rxjava disposable
- 2022 플러터 설치
- 안드로이드 유닛 테스트 예시
- 안드로이드 os 구조
- 스택 자바 코드
- 멤버변수
- jvm 작동 원리
- 안드로이드 유닛 테스트
- ar vr 차이
- Today
- Total
나만을 위한 블로그
[Android] SMS 인증번호 자동입력 구현하는 법 본문
※ 이 글에선 실제 서버를 통하지 않고 에뮬레이터를 써서 인증번호를 전송한다
※ 인증번호 입력 화면 코드는 생략한다
흔히 앱을 사용하다 보면 SMS로 인증번호가 전달되고, 난 그 인증번호를 앱에 입력해야 하는 경우가 있다.
이 때 유저가 SMS를 확인하지 않아도 자동으로 입력되게 하려면 아래와 같이 구현하면 된다. 먼저 앱 gradle에 라이브러리를 하나 추가한다.
implementation "com.google.android.gms:play-services-auth-api-phone:18.0.1"
그리고 BroadcaseReceiver를 상속하는 아래 클래스를 구현한다.
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.google.android.gms.auth.api.phone.SmsRetriever
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status
class AuthOtpReceiver : BroadcastReceiver() {
companion object {
private const val PATTERN = "^<#>.*\\[Sample\\].+\\[(\\d{6})\\].+\$"
}
private var otpReceiver: OtpReceiveListener? = null
override fun onReceive(context: Context, intent: Intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
intent.extras?.let { bundle ->
val status = bundle.get(SmsRetriever.EXTRA_STATUS) as Status
when (status.statusCode) {
CommonStatusCodes.SUCCESS -> {
val otpSms = bundle.getString(SmsRetriever.EXTRA_SMS_MESSAGE, "")
if (otpReceiver != null && otpSms.isNotEmpty()) {
val otp = PATTERN.toRegex().find(otpSms)?.destructured?.component1()
if (!otp.isNullOrEmpty()) {
otpReceiver!!.onOtpReceived(otp)
}
}
}
}
}
}
}
fun setOtpListener(receiver: OtpReceiveListener) {
this.otpReceiver = receiver
}
fun doFilter() = IntentFilter().apply {
addAction(SmsRetriever.SMS_RETRIEVED_ACTION)
}
interface OtpReceiveListener {
fun onOtpReceived(otp: String)
}
}
에물레이터에서 인증번호를 보낼 때 정규식대로 보내면, 상태값에 따라 인증번호(otp)를 처리하는 코드다.
지금은 성공했을 경우의 처리만 존재하며 그 외의 상태인 경우 어떻게 처리할지는 추가 작성이 필요하다.
그리고 아래 클래스를 작성한다. 에뮬레이터에서 인증문자를 보내려면 맨 마지막에 기기의 해시값을 입력해야 하는데, 이 해시값이 없으면 SMS 인증번호를 자동으로 읽어올 수가 없는데, 이 해시값을 생성해서 로그로 출력하는 클래스가 필요하다.
class AppSignatureHelper(context: Context) : ContextWrapper(context) {
val TAG = AppSignatureHelper::class.java.simpleName
private val HASH_TYPE = "SHA-256"
val NUM_HASHED_BYTES = 9
val NUM_BASE64_CHAR = 11
init {
getAppSignatures()
}
/**
* Get all the app signatures for the current package
* @return
*/
fun getAppSignatures(): ArrayList<String>? {
val appCodes = ArrayList<String>()
try {
// Get all package signatures for the current package
val packageName = packageName
val packageManager = packageManager
val signatures: Array<Signature> = packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES
).signatures
// For each signature create a compatible hash
for (signature in signatures) {
val hash = hash(packageName, signature.toCharsString())
if (hash != null) {
appCodes.add(String.format("%s", hash))
}
Log.e(TAG, "Hash $hash")
}
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Unable to find package to obtain hash.", e)
}
return appCodes
}
private fun hash(packageName: String, signature: String): String? {
val appInfo = "$packageName $signature"
try {
val messageDigest = MessageDigest.getInstance(HASH_TYPE)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8))
}
var hashSignature = messageDigest.digest()
// truncated into NUM_HASHED_BYTES
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES)
// encode into Base64
var base64Hash: String =
Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP)
base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR)
Log.e(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash))
return base64Hash
} catch (e: NoSuchAlgorithmException) {
Log.e(TAG, "hash:NoSuchAlgorithm", e)
}
return null
}
}
그리고 앱 어딘가에 이 클래스를 통해 해시값을 확인하는 코드를 작성한다.
val helper = AppSignatureHelper(this)
val hash = helper.getAppSignatures()?.get(0)
이후 앱을 실행하면 "Hash ~~~~" 형태로 "Hash" 뒤에 해시값이 출력된다. 이 해시값을 복사해둔다.
그리고 에뮬레이터로 앱을 실행하면 아래와 비슷한 화면이 표시될 것이다.
옵션 버튼을 누르면 아래 화면이 표시된다.
정규식에 맞게 인증번호를 작성하고 아까 복사했던 해시값도 뒤에 붙여넣은 다음, 하단의 Send Message를 누르면 에뮬레이터로 SMS가 전송된다.
그러면 아래와 같이 자동으로 editText에 인증번호가 입력된다.
추가해야 하는 예외처리가 많지만 이 코드를 바탕으로 인증번호 자동입력 기능을 구현해볼 수 있다.
참고한 사이트)
https://techblog.woowahan.com/2618/
https://stackoverflow.com/a/54052163
'Android' 카테고리의 다른 글
[Android] 앱 링크 적용하는 법 (0) | 2023.12.12 |
---|---|
[Android] 앱 링크(App Link)란? (0) | 2023.12.12 |
[Android] Firebase App Distribution, Github Action 같이 사용하기 (0) | 2023.11.23 |
[Android] shouldOverrideUrlLoading()이란? (0) | 2023.11.21 |
[Android] BottomSheetDialogFragment 스크롤 시 내부 요소가 스크롤되게 하는 법 (0) | 2023.11.16 |