How to Implement Seamless OTP on Android
Hello Utopians, in this post I will show you a tutorial on how to implement seamless OTP (One Time Password) on Android.
What Will I Learn?
- Learn how seamless OTP works.
- Learn how to implement a third party library to read OTP.
Requirements
- Android Studio
- Basic knowledge of Java.
Difficulty
- Basic
Overview
Seamless OTP works like this. For example, there is an Application that log in a user with phone number and a password that will be sent to the phone number. The user will input his number and request a one time password. OTP will be sent to the phone number and when the SMS is recieved the application automatically read the sms OTP and fill it directly inside the application without the user doing the input. This improve the user experience, so users don't need to open the Messaging apps and then switch back to another apps to input OTP. The apps implementing this feature will require permission to recieve and read SMS, thus this feature can't be implemented on iOS Device since there is no permission to read SMS in iOS.
Tutorial Contents
1. Add Library Dependency
First we will need to add this dependency in our gradle file implementation 'com.github.stfalcon:smsverifycatcher:0.3.1'
.
This is my entire app build gradle file:
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
defaultConfig {
applicationId "com.steven.example.seamlessotpexample"
minSdkVersion 21
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:27.0.2'
implementation 'com.android.support:cardview-v7:27.0.2'
implementation 'com.github.stfalcon:smsverifycatcher:0.3.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
After adding the dependency sync the gradle file and we will move to the next step.
2. Add Permission
In order to read OTP we need to let the application to recieve and read SMS. So open AndroidManifest.xml
and add the following code.
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
This is my entire Android Manifest code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.steven.example.seamlessotpexample">
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<application
android:allowBackup="true"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3. Modify Layout for our Activity
In my layout, I use CardView
and an EditText
inside the CardView to input the OTP which looks like this:
This is my entire layout activity_main.xml
code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:paddingTop="64dp"
tools:context="com.steven.example.seamlessotpexample.MainActivity">
<android.support.v7.widget.CardView
android:id="@+id/card1"
android:layout_width="40dp"
android:layout_height="54dp"
android:layout_margin="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<android.support.v7.widget.AppCompatEditText
android:id="@+id/digit1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="4dp"
android:background="@android:color/white"
android:gravity="center"
android:inputType="number"
android:maxLength="1"
android:maxLines="1"
tools:text="1" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/card2"
android:layout_width="40dp"
android:layout_height="54dp"
android:layout_margin="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<android.support.v7.widget.AppCompatEditText
android:id="@+id/digit2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="4dp"
android:background="@android:color/white"
android:gravity="center"
android:inputType="number"
android:maxLength="1"
android:maxLines="1"
tools:text="2" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/card3"
android:layout_width="40dp"
android:layout_height="54dp"
android:layout_margin="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<android.support.v7.widget.AppCompatEditText
android:id="@+id/digit3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="4dp"
android:background="@android:color/white"
android:gravity="center"
android:inputType="number"
android:maxLength="1"
android:maxLines="1"
tools:text="3" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/card4"
android:layout_width="40dp"
android:layout_height="54dp"
android:layout_margin="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<android.support.v7.widget.AppCompatEditText
android:id="@+id/digit4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="4dp"
android:background="@android:color/white"
android:gravity="center"
android:inputType="number"
android:maxLength="1"
android:maxLines="1"
tools:text="4" />
</android.support.v7.widget.CardView>
</LinearLayout>
4. Implement The Library
Now, in the MainActivity.java
file we will implement the library to read OTP message.
First add this code globally inside the activity.
private SmsVerifyCatcher mSmsVerifyCatcher;
Then implement SmsVerifyCatcher inside the onCreate method like this:
mSmsVerifyCatcher = new SmsVerifyCatcher(this, message -> {
mDigit1.setText(String.valueOf(message.charAt(0)));
mDigit2.setText(String.valueOf(message.charAt(1)));
mDigit3.setText(String.valueOf(message.charAt(2)));
mDigit4.setText(String.valueOf(message.charAt(3)));
});
You can filter the phone number by the OTP provider by adding this line of code:
mSmsVerifyCatcher.setPhoneNumberFilter(PHONE_NUMBER);
You need to start and stop the SmsVerifyCatcher by overriding the onStart and onStop method by adding this code:
@Override
protected void onStart() {
super.onStart();
mSmsVerifyCatcher.onStart();
}
@Override
protected void onStop() {
super.onStop();
mSmsVerifyCatcher.onStop();
}
You will also need to enable runtime permission for Android 6.0 and above.
This is my entire MainActivity.java
code:
package com.steven.example.seamlessotpexample;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatEditText;
import android.util.Log;
import android.widget.Toast;
import com.stfalcon.smsverifycatcher.SmsVerifyCatcher;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private AppCompatEditText mDigit1, mDigit2, mDigit3, mDigit4;
private SmsVerifyCatcher mSmsVerifyCatcher;
private static final int READ_SMS_PERMISSION = 104;
private static final String PHONE_NUMBER = "123456789";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDigit1 = findViewById(R.id.digit1);
mDigit2 = findViewById(R.id.digit2);
mDigit3 = findViewById(R.id.digit3);
mDigit4 = findViewById(R.id.digit4);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) {
checkPermission();
}
catchSMS();
}
@Override
protected void onStart() {
super.onStart();
mSmsVerifyCatcher.onStart();
}
@Override
protected void onStop() {
super.onStop();
mSmsVerifyCatcher.onStop();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) {
catchSMS();
}
}
private void checkPermission() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_SMS)) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, READ_SMS_PERMISSION);
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, READ_SMS_PERMISSION);
}
}
}
private void catchSMS() {
mSmsVerifyCatcher = new SmsVerifyCatcher(this, message -> {
logMessage(message);
mDigit1.setText(String.valueOf(message.charAt(0)));
mDigit2.setText(String.valueOf(message.charAt(1)));
mDigit3.setText(String.valueOf(message.charAt(2)));
mDigit4.setText(String.valueOf(message.charAt(3)));
});
mSmsVerifyCatcher.setPhoneNumberFilter(PHONE_NUMBER);
}
private void logMessage(String message) {
Log.d(TAG, message);
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
5. Test The Code
For testing, I sent a message from one phone to another phone. To show you that it really works seamlessly, I will show you the test result on a video below.
You can check the library used in this tutorial here: https://github.com/stfalcon-studio/SmsVerifyCatcher
You can download the example code in this tutorial from my Github: https://github.com/steven-tjg/seamless-otp-example
Posted on Utopian.io - Rewarding Open Source Contributors
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
Hey @steven.tjg I am @utopian-io. I have just upvoted you!
Achievements
Suggestions
Get Noticed!
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x