Implementing the Complete “SMS Verification” Flow Using Consent API in Android

Anton Stulnev
The Startup
Published in
5 min readJan 6, 2021

In one of my recent projects I needed to implement a quite popular UI for entering confirmation codes (which are usually received via SMS messages):

Original design from Zeplin
The original design I got from our UI/UX team

After a quick Googling, it appeared that some good solutions had already been available on GitHub but unfortunately, none of those libraries was quite compatible with my design, and many of them didn’t quite match my overall expectations for the functionality and API. So I decided to go with a custom implementation of all the necessary stuff, luckily the deadline wasn’t strict enough and I had some spare time for investigations and playing with custom Android views :) In addition to the view itself, I was looking to have some kind of automation for seamless retrieval of confirmation codes from incoming SMS without the need to bother with runtime permissions and boilerplate code in Activity/Fragment layer. If you’re interested in what I managed to come up with — welcome!

P.S. For those of you who prefer to read code instead of text or if you’re just looking for a ready-to-use solution, I created a library with a sample project on GitHub.

Let’s start implementing our custom view. Essentially, it’s just a set of properly aligned subviews where each subview is a “cell” containing a single symbol of the confirmation code. So I decided to split the overall view into two logical subparts: 1) individual symbol view and 2) whole view holding symbol subviews.

So here’s a simplified implementation of #1:

Nothing unusual here, just a regular custom view stuff like drawing & measurements. For now, individual symbols are not really customizable (except forwidth and height) but it’s just for simplicity, you can extend the Style container by adding other fields like backgroundColor, textColor and so on. These options can be specified by the main container view and passed down to symbol subviews. Now it’s time to implement the main container view, let’s call it SmsConfirmationView:

The confirmation code which is currently displayed by the view can be controlled (getter + setter) via enteredCode property plus we can assign a onChangeListener to observe view’s state updates. The view itself simply redraws its subviews (all of them are instances of SymbolView) every time the enteredCode or style property change, nothing too tricky.

So far so good but onChangeListener needs to be triggered somehow, this is not going to happen with the current implementation. Now it’s time to integrate the view with Android’s soft keyboard. Another approach would be to implement some custom view/layout with 0–9 digits but the default Android’s keyboard sounds like a preferable approach to me as users are more familiar with it + it’s easier to implement from the technical point of view. Since SmsConfirmationView extends LinearLayout it doesn’t know how to interact with the keyboard, let’s solve this problem by further extending our implementation:

We’ve just let the system know that the view can act as a text editor + added corresponding logic to handle keyboard events. Nice! But what about DataBinding? Personally, I like using it in small & middle-sized projects (btw, I’ve already written a post on how you can use it to drastically simplify working with RecyclerView) so it would be great to support it in our custom view as well. Actually, fields like enteredCode can already be used with no extra effort but we have to write some additional code in order to support two-way binding constructions like this one in your XML layouts:

<com.fraggjkee.smsconfirmationview.SmsConfirmationView                           
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:code="@={viewModel.confirmationCodeViewModel.enteredCode}"
app:listener="@{viewModel.confirmationCodeViewModel}"
/>

The implementation is quite routine so I won’t post it here to don’t overwhelm you with too many code snippets in a single post but you can take a look at it here if you’re interested.

Well, the view is now fully functional, yay! Last but not least thing to cover is saving potential users from manual copy-pasting confirmation codes from SMS into your app. The most logical thing to do would be to register some kind of BroadCastReceiver which will listen for incoming messages and automatically parse the codes. But there is one caveat here — access to SMS messages on the device requires permission which has to be explicitly granted by the user. Many users will prefer to do everything manually instead of granting full access to their messages so we need to find some other solution. Luckily, Google provides an alternative named Consent API. Its integration contains quite a lot of code, let’s just cover key ideas here in a simplified format (complete & detailed documentation can be found here):

  1. Start listening for incoming messages:

SmsRetriever.getClient(context).startSmsUserConsent(null)

2. Register a BroadCastReceiver which will be automatically triggered after step #1:

3. The key part of the previous step is the startActivityForResult. Everything that is left to do is to wait for a corresponding onActivityResult callback in host Activity or Fragment:

As you can see, the API is quite straightforward but unfortunately, it suffers from an old Android’s pain. As you might have already guessed, I’m talking about startActivityForResult: you can’t isolate related logic from your Activity (even if you have direct access to its object) and you have to override onActivityResult in your subclass, i.e. Activity is the only place where “start for result -> get result” flow can be completed. And as I mentioned at the beginning of this post, I don’t want to force users of the SmsConfirmationView to write any configuration/glue code in the screens where this view is used, I’d like the automatic SMS verification process to happen under the hood if possible. Luckily for us, Google is aware of all the problems caused by this quirky API and they’ve finally provided a better, more modern approach: starting from version 1.2.0-alpha02, androidx.activity package includes a replacement for the old flow. The library has already reached “release candidate” status so I think it’s relatively safe to start using it. So the last issue to solve in case of a View is to get a reference to its host — Activity or Fragment. There is a semi-official workaround for the first case:

If you rely on Fragments in your application, you still can use a similar approach of seamless integration, FragmentManager has findFragment(view: View)method which can return host Fragment for a given view. The new API for getting results is also available for fragments (androidx.fragment:fragment-ktx). So we just need to find a parent for our view (either Activity or Fragment) and use it to initiate the new “get result” flow as described in the official docs. Again, if you’re curious, you can find the complete implementation in the library sources.

That’s it, we’re finally done :) I hope some of you have found something interesting & new in this post and maybe you’ll apply some of the described ideas and techniques in your future work if you’ll ever need to deal with SMS verification/confirmation flows. Thanks for reading!

--

--