Last active
May 19, 2022 06:58
-
-
Save esabook/6ca064acc595656f4435759f4e680114 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2022 esabook. | |
* Author: egit | |
*/ | |
package com.esabook.app.services | |
import android.app.Activity | |
import android.content.BroadcastReceiver | |
import android.content.Context | |
import android.content.Intent | |
import android.content.IntentFilter | |
import androidx.activity.result.ActivityResult | |
import androidx.activity.result.contract.ActivityResultContracts | |
import androidx.appcompat.app.AppCompatActivity | |
import com.google.android.gms.auth.api.phone.SmsRetriever | |
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 | |
import timber.log.Timber | |
import java.util.regex.Matcher | |
import java.util.regex.Pattern | |
/** | |
* register action | |
* call async smsRetriver | |
* parse smsOTP | |
* invoke callback | |
* unregister action | |
*/ | |
class OtpAutoCompleteService( | |
private val activity: AppCompatActivity, | |
private val onOTPResult: (String) -> Unit | |
) { | |
companion object { | |
private val senderPhoneNumber = listOf("egit") //known esabook's numbers | |
private val otpPattern = listOf("(\\d{6})") | |
} | |
private val smsBroadcastReceiver = object : BroadcastReceiver() { | |
override fun onReceive(context: Context?, intent: Intent?) { | |
Timber.d("onReceive otp broadcast") | |
if (SMS_RETRIEVED_ACTION == intent?.action) { | |
val extras = intent.extras | |
val smsRetrieverStatus = extras?.get(EXTRA_STATUS) as Status | |
when (smsRetrieverStatus.statusCode) { | |
CommonStatusCodes.SUCCESS -> { | |
// Get consent intent | |
val consentIntent = extras.getParcelable<Intent>(EXTRA_CONSENT_INTENT) | |
try { | |
Timber.d("Get otp intent from broadcast with startActivityForResult") | |
// Start activity to show consent dialog to user, activity must be started in | |
// 5 minutes, otherwise you'll receive another TIMEOUT intent | |
val activityResultTask = activity.activityResultRegistry.register( | |
"otp", | |
ActivityResultContracts.StartActivityForResult(), | |
this@OtpAutoCompleteService::onOtpActivityResult | |
) | |
activityResultTask.launch(consentIntent) | |
} catch (e: Exception) { | |
Timber.e(e) | |
} | |
} | |
CommonStatusCodes.TIMEOUT -> { | |
Timber.d("Receive otp broadcast [TIMEOUT]") | |
} | |
} | |
} | |
} | |
} | |
fun startListener() { | |
registerReceiver() | |
senderPhoneNumber.forEach { | |
getClient(activity).startSmsUserConsent(it) | |
} | |
} | |
fun unregisterListener() { | |
activity.unregisterReceiver(smsBroadcastReceiver) | |
} | |
private fun registerReceiver() { | |
val intentFilter = IntentFilter(SMS_RETRIEVED_ACTION) | |
activity.registerReceiver( | |
smsBroadcastReceiver, | |
intentFilter, | |
SmsRetriever.SEND_PERMISSION, | |
null | |
) | |
} | |
private fun onOtpActivityResult(activityResult: ActivityResult) { | |
// Obtain the phone number from the result | |
if (activityResult.data != null) { | |
// Get SMS message content | |
val message = activityResult.data?.getStringExtra(EXTRA_SMS_MESSAGE) | |
Timber.d("Parsing otp from message: %s", message) | |
// Extract one-time code from the message and complete verification | |
// `message` contains the entire text of the SMS message, so you will need | |
// to parse the string. | |
if (message != null) { | |
otpPattern.forEach { | |
val otp = parseOneTimeCode(message, it) | |
if (otp.isNotBlank()) { | |
Timber.d("Result otp: %s", otp) | |
onOTPResult.invoke(otp) | |
return@forEach | |
} | |
} | |
} | |
} else { | |
Timber.d("Skip parsing otp") | |
// Consent denied. User can type OTC manually. | |
} | |
} | |
private fun parseOneTimeCode(message: String, regexPattern: String): String { | |
// \d is for a digit | |
// {} is the number of digits here 6. | |
val pattern: Pattern = Pattern.compile(regexPattern) | |
val matcher: Matcher = pattern.matcher(message) | |
var result = "" | |
if (matcher.find()) { | |
result = matcher.group(0) ?: result // 6 digit number | |
} | |
return result | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment