How to Implement Seamless OTP on Android

in #utopian-io7 years ago (edited)

layout.png

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:
layout.png

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

Sort:  

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

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

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