Automatic SMS Verification With Android's SMS Retrieval API
Tags: android
Published: 2020-12-14 14:07:31

We, as Android developers always strive to make our app's onboarding process of new users as friction-less as possible. One way to achieve such smooth, effortless on-boarding is to implement an Automatic SMS verification feature. 

If you've used [Uber](https://www.uber.com) or [Whatsapp](https://www.whatsapp.com), you would have noticed that their SMS verification is automatic: The user need not manually type in the verification code. This makes their on-boarding feel seamless and kind of effortless on the part of the user. And the best part of this is that they do not request the user's SMS permission.:open_mouth: This is done using [`Google's SMS Retrieval API`](https://developers.google.com/identity/sms-retriever/overview), and in this article, we will see how to achieve just that in our apps as well.



Before we begin, we need to import the following libraries:

1. [`Google Play Services Auth API`](https://developers.google.com/android/guides/setup) - This library contains the `SMS Retrieval API` classes.

2. [`Apache Commons`](https://github.com/apache/commons-lang) - This will be used to parse out the verification code from the SMS message.

3. [`EventBus`](https://github.com/greenrobot/EventBus) -  We will be using a BroadcastReceiver to listen for the retrieved SMS from the `SMS Retrieval API`. EventBus is a library that uses the publisher/subscriber pattern and will be used to simplify the communication between our `BroadcastReceiver` and our `Activity` classes.


Let's add them to our app's `build.gradle` file:

```gradle
    implementation 'com.google.android.gms:play-services-auth:19.0.0'
    implementation 'org.apache.commons:commons-lang3:3.11'
    implementation 'org.greenrobot:eventbus:3.2.0'
```

Alright, we are set.

Our XML layout file is extremely simple. It contains a `TextView` in the middle of the screen. This view will be used to display the verification code we extract using the `SMS Retrieval API`.

*activity_main.xml*

```xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/otp_text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textColor="@android:color/black"
        android:textSize="30sp"
        android:gravity="center"
        android:text="Loading..."
        />

</RelativeLayout>
``` 

What we are going to do next is to get an instance of the `SmsRetrieverClient` object, call its `startSmsRetriever` instance method, and attach onSuccess and onFailure Listeners to the resulting [`Task`](https://developer.android.com/reference/com/google/android/play/core/tasks/Task). I'm going to wrap that portion of code in a method to easily reuse later:

```kotlin
private fun startSmsListener() {
    smsRetrieverClient.startSmsRetriever()
        .addOnSuccessListener {
            Toast.makeText(this@MainActivity, "Starting Sms Retriever", Toast.LENGTH_SHORT).show()
        }.addOnFailureListener { e ->
            e.printStackTrace()
            Toast.makeText(this@MainActivity, e.message, Toast.LENGTH_SHORT).show()
        }
}
```

Then we call that method immediately after `onCreate`:

*MainActivity.kt*

```kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var smsRetrieverClient: SmsRetrieverClient
    private lateinit var otpTextView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        otpTextView = findViewById(R.id.otp_text_view)
        smsRetrieverClient = SmsRetriever.getClient(this)
        startSmsListener()
    }
...

```

At this point, when an SMS message is received on our device, the Play Services library we added earlier will broadcast an `SmsRetriever.SMS_RETRIEVED_ACTION` intent to our app. This intent contains the status of the background processing as well as the text of the SMS message. 
Let's create a BroadcastReceiver class to handle this:

*SmsBroadcastReceiver.kt*

```kotlin
class SmsBroadcastReceiver : BroadcastReceiver() {

    companion object {
        val TAG = SmsBroadcastReceiver::class.java.simpleName
    }
    
    override fun onReceive(context: Context?, intent: Intent) {
        Log.d(TAG, "onReceive Called!")
        if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
            val bundle = intent.extras
            if (bundle != null) {
                val status: Status = bundle[SmsRetriever.EXTRA_STATUS] as Status
                var isTimeout = false
                var smsMessage: String? = null
                when (status.statusCode) {
                    CommonStatusCodes.SUCCESS -> {
                        val message =
                            bundle[SmsRetriever.EXTRA_SMS_MESSAGE] as String
                        Log.d(TAG, message)
                        smsMessage = message
                    }
                    CommonStatusCodes.TIMEOUT -> {
                        Log.d(TAG, "Timeout")
                        isTimeout = true
                    }
                }
                EventBus.getDefault().post(SmsRetrievedEvent(isTimeout, smsMessage))
            }
        }
    }
    
}
```

In the BroadcastReceiver's `onReceive` method, we first check the [`Status`](https://developers.google.com/android/reference/com/google/android/gms/common/api/Status) of the background processing done by the `SMS Retrieval API`. We also create an instance of a class called `SmsRetrievedEvent`. This is our event class that will be published by `EventBus` to our `Subscriber`.The `SmsRetrievedEvent` class is nothing but a simple Kotlin data class. Kindly read more [here](http://greenrobot.org/eventbus) if you are totally new to `EventBus`.

*SmsRetrievedEvent.kt*

```kotlin
data class SmsRetrievedEvent(
    val isTimeout: Boolean,
    val smsMessage: String?
)
```

If the background processing was successful, we set the message property of the `SmsRetrievedEvent` class to the SMS message we've retrieved. Otherwise, we set timeout to true if a timeout occurred. We finally publish the event to any listening subscriber.

**Note:** Timeouts occurs if messages are not handled within 5 minutes after processing by the `SMS Retreival API`.

Let's not forget to register this `BroadcastReceiver` in our `AndroidManifest` file:

*AndroidManifest.xml*

```xml

<application>
        ...
        <receiver
            android:name=".SmsBroadcastReceiver"
            android:exported="true">
            <intent-filter>
                <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
            </intent-filter>
        </receiver>
    </application>

```

Next, we are going to register, unregister as well as implement our subscriber in our `MainActivity` class. This method will be defined with the `@Subscribe` annotation and will be called when an event is published/posted.

**Note:** Always remember to unregister your subscribers to avoid memory leak issues.


*MainActivity.kt*

```kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var smsRetrieverClient: SmsRetrieverClient
    private lateinit var otpTextView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        otpTextView = findViewById(R.id.otp_text_view)
        smsRetrieverClient = SmsRetriever.getClient(this)
        startSmsListener()
    }


    override fun onStart() {
        super.onStart()
        EventBus.getDefault().register(this)
    }

    override fun onStop() {
        EventBus.getDefault().unregister(this)
        super.onStop()
    }

    @Subscribe
    fun onEvent(event: SmsRetrievedEvent) {
    }

    private fun startSmsListener() {
        smsRetrieverClient.startSmsRetriever()
            .addOnSuccessListener {
                Toast.makeText(this@MainActivity, "Starting Sms Retriever", Toast.LENGTH_SHORT).show()
            }.addOnFailureListener { e ->
                e.printStackTrace()
                Toast.makeText(this@MainActivity, e.message, Toast.LENGTH_SHORT).show()
            }
    }
}
```
Finally, we parse out the 4 digit verification code from the message using the `substringAfterLast` method from the `Apache Commons` library, and display it in our `TextView`


```kotlin
...
@Subscribe
    fun onEvent(event: SmsRetrievedEvent) {
        Log.d("SMS EVENT", "RECEIVED SMS EVENT")
        Log.d("SMS EVENT - Timeout=", event.isTimeout.toString())
        Log.d("SMS EVENT - Message=", if(!event.isTimeout) event.smsMessage!! else "Timeout: No Message")

        val otp: String =
            StringUtils.substringAfterLast(event.smsMessage, "is").replace(":", "")
                .trim().substring(0, 4)
        Log.d("EVENT", otp)

        runOnUiThread {
            otpTextView.text = if (!event.isTimeout) otp else "Timeout :("
        }

        startSmsListener()
    }
...
```

One important thing to keep in the back of our minds:
The `SMS Retreival API` requires that SMS messages be in the following format:

```
Your verification code is 9872


FA+9qCX9VSu

```
The SMS message must end with an 11-character hash string that identifies your app(which is `FA+9qCX9VSu` in this case). [Click here to see how to compute your app's hash.](https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string)
 
At this point we can send the SMS message to our device, extract and send the 4 digit code to our server for verification. 

[Click here](https://github.com/KwabenBerko/SmsRetreiver-Sample) to see a sample project on GitHub.

Happy Coding, everyone!