Skip to content

Instantly share code, notes, and snippets.

@nostra13
Last active May 6, 2020 13:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nostra13/63aa357d2337905d78b001fc1211ccdc to your computer and use it in GitHub Desktop.
Save nostra13/63aa357d2337905d78b001fc1211ccdc to your computer and use it in GitHub Desktop.
SmsUserConsentManager
implementation "com.google.android.gms:play-services-auth:18.0.0" // For SMS User Consent
implementation "com.google.android.gms:play-services-auth-api-phone:17.4.0" // For SMS User Consent
package com.nostra13.manager
import android.content.*
import com.google.android.gms.auth.api.phone.SmsRetriever.*
import com.google.android.gms.common.api.CommonStatusCodes.SUCCESS
import com.google.android.gms.common.api.CommonStatusCodes.TIMEOUT
import com.google.android.gms.common.api.Status
import com.nostra13.log.L
import com.nostra13.log.nonFatal
import io.reactivex.Observable
interface SmsUserConsentManager {
companion object {
const val VERIFICATION_CODE_LENGTH = 6
}
/** SMS User Consent intents to start */
val consentIntent: Observable<Intent>
/** Returns verification code from incoming SMS */
fun extractVerificationCode(smsIntent: Intent): String?
/** Restarts SMS User Consent flow. Call it if incoming SMS wasn't right. */
fun restart()
class Impl(
private val context: Context
) : SmsUserConsentManager {
private val codePattern = """\d{${VERIFICATION_CODE_LENGTH}}""".toRegex()
private fun startSmsUserConsent() {
getClient(context).startSmsUserConsent(null)
}
override val consentIntent: Observable<Intent> = Observable
.create<Intent> { emitter ->
startSmsUserConsent()
val smsVerificationReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == SMS_RETRIEVED_ACTION) {
val extras = intent.extras!!
val status = extras.getParcelable<Status>(EXTRA_STATUS)!!
val consIntent = extras.getParcelable<Intent>(EXTRA_CONSENT_INTENT)
when (status.statusCode) {
SUCCESS -> consIntent?.let { if (!emitter.isDisposed) emitter.onNext(it) }
TIMEOUT -> startSmsUserConsent()
}
}
}
}
context.registerReceiver(
smsVerificationReceiver,
IntentFilter(SMS_RETRIEVED_ACTION),
SEND_PERMISSION, null
)
emitter.setCancellable { context.unregisterReceiver(smsVerificationReceiver) }
}
.share()
override fun extractVerificationCode(smsIntent: Intent): String? =
smsIntent.getStringExtra(EXTRA_SMS_MESSAGE)?.let { sms ->
codePattern.find(sms)?.value
.apply { if (this == null) L.nonFatal("Can't extract verification code from SMS") }
}
override fun restart() = startSmsUserConsent()
}
}
package io.goeleven.service.manager
import android.content.Intent
import com.google.android.gms.auth.api.phone.SmsRetriever
import com.nostra13.test.BaseSpek
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.spekframework.spek2.style.specification.describe
object SmsUserConsentManagerSpek : BaseSpek({
val env by memoized { Environment() }
describe("SMS parsing") {
context("receive SMS with 5-digit code") {
it("returns null") {
assertThat(env.manager.extractVerificationCode(env.smsIntent("Your code: 12345!"))).isEqualTo(null)
}
}
context("receive SMS with 6-digit code") {
it("returns code from SMS") {
assertThat(env.manager.extractVerificationCode(env.smsIntent("Your code: 123456."))).isEqualTo("123456")
}
}
context("receive SMS with 7-digit code") {
it("returns 6-digit code from SMS") {
assertThat(env.manager.extractVerificationCode(env.smsIntent("Your code: 1234567!!!"))).isEqualTo("123456")
}
}
context("receive SMS without verification code") {
it("returns null") {
assertThat(env.manager.extractVerificationCode(env.smsIntent("Hey! It's me - Mario!"))).isEqualTo(null)
}
}
context("receive empty SMS") {
it("returns null") {
assertThat(env.manager.extractVerificationCode(env.smsIntent(""))).isEqualTo(null)
}
}
}
}) {
class Environment {
val manager = SmsUserConsentManager.Impl(mockk())
fun smsIntent(sms: String) = mockk<Intent> {
every { getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE) } returns sms
}
}
}
private const val REQUEST_CODE_SMS_CONSENT = 13
// smsUserConsentManager.consentIntent.subscribe { showSmsConsent() }
private fun showSmsConsent(intent: Intent) = try {
startActivityForResult(intent, REQUEST_CODE_SMS_CONSENT)
} catch (e: ActivityNotFoundException) {
L.nonFatal(e, "Can't start User SMS Consent intent")
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_SMS_CONSENT && resultCode == RESULT_OK && data != null) {
val verificationCode = smsUserConsentManager.extractVerificationCode(data)
// insert code into text field
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment