Google SMS-Retriever API in Jetpack Compose

Google SMS-Retriever API in Jetpack Compose

Introduction:

The Google SMS-Retrieval API allows Android apps to automatically retrieve SMS messages containing one-time passwords (OTPs) from the user’s device by showing a one-time consent to the user before reading the SMS. This can be used to improve the user experience by eliminating the need for the user to give unnecessary permissions or manually enter the OTP into the app.

In this article, we will show you how to use the Google SMS-Retrieval API with Jetpack Compose to automatically retrieve OTPs from the user’s device and display them in your app.

Dependency:

Add these in your app-level build.gradle:

implementation "com.google.android.gms:play-services-auth:20.5.0"
implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'

Implementation:

  1. All the SMS User Consent API’s startSmsUserConsent() method to start listening for incoming messages. If you know the phone number from which the SMS message will originate, specify it (otherwise, pass null).

To start listening:

SmsRetriever.getClient(context).startSmsUserConsent(senderPhoneNumber /* or null */)

You can write this line before sending the OTP (In my case it was in onSendOtpClick).

2. Create an sms receiver Launcher that we will be using to launch the Intent of sms reading and get the sms back.

val smsReceiverLauncher = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.StartActivityForResult(),
    onResult = { result ->
        if (result.resultCode == Activity.RESULT_OK && result.data != null) {
            // Get SMS message content
            val message = result.data!!.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
            val oneTimeCode = parseOneTimeCode(message)//Create this method
            otp = oneTimeCode//Save this otp to use somewhere else
            onOtpSubmit()//OPTIONAL(auto submit the otp)
        } else {
            Toast.makeText(context, "Otp retrieval failed", Toast.LENGTH_SHORT).show()
        }
    }
)

3. For the next five minutes, when the device receives an SMS message that contains a one-time code, Play services will broadcast to your app an intent to prompt the user for permission to read the message. A message triggers the broadcast only if it meets these criteria:

  • The message contains a 4–10 character alphanumeric string with at least one number.

  • If you specified the sender’s phone number, the message was sent by that number.

Handle these broadcasts with a broadcast receiver that has the SEND_PERMISSION permission and responds to SMS_RETRIEVED_ACTION intents. To create and register the broadcast receiver:

val smsVerificationReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
            val extras = intent.extras
            val smsRetrieverStatus = extras?.get(SmsRetriever.EXTRA_STATUS) as Status

            when (smsRetrieverStatus.statusCode) {
                CommonStatusCodes.SUCCESS -> {
                    // Get consent intent
                    val consentIntent =
                        extras.getParcelable<Intent>(SmsRetriever.EXTRA_CONSENT_INTENT)
                    try {
                        // Start activity to show consent dialog to user, activity must be started in
                        // 5 minutes, otherwise you'll receive another TIMEOUT intent
                        smsReceiverLauncher.launch(consentIntent)
                    } catch (e: ActivityNotFoundException) {
                        // Handle the exception ...
                    }
                }

                CommonStatusCodes.TIMEOUT -> {
                    // Time out occurred, handle the error.
                }
            }
        }
    }
}

4. Now, you have to register this broadcast receiver, I don’t know the best way to do this in compose. But, it was working so I was not struggling for the best way of implementation. Let me know if you know the better way to register THIS broadcast receiver in compose.

You can do it right after the code of first step. i.e.

val onSendOtp = {
    if (!valid) {
        //do something
    } else {
        //...
        val task = SmsRetriever.getClient(context).startSmsUserConsent(null)
        val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
        context.registerReceiver(smsVerificationReceiver, intentFilter)
        //sendVerificationCode()
    }
}

5. Unregister the Broadcast receiver when your task is done. In my case, I did it in onSubmitOTP.

context.unregisterReceiver(myOTPReceiver)

That’s it!!

NOTE: You might face a crash because of a conflict between Firebase Auth Instant Verification and SMS User Consent API. To resolve this, you can set the timeout value as 0L while starting phone verification. Check details here.

Output:

I hope you found this helpful. If yes, then do FOLLOW ‘Sagar Malhotra’ for more Android-related content.

#androidWithSagar #android #androiddevelopment #development #compose #kotlin