Last active
May 6, 2020 13:45
-
-
Save nostra13/63aa357d2337905d78b001fc1211ccdc to your computer and use it in GitHub Desktop.
SmsUserConsentManager
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
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 |
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
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() | |
} | |
} |
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
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 | |
} | |
} | |
} |
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
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