Tag

autofill otp in android example

Browsing

In this blog, I will show how to auto read OTP in android with the help of SMS User Consent API. In our previous blog, we have learned Automatic SMS Verification Android using SMS Retriever API. But it has some difficulty for example

  • SMS should start with <#> tag,
  • SMS should end with application hash code, etc.

In this article, I’m going to show you the implementation auto-read OTP using SMS User Consent API. It is complements of SMS Retriever API. Using SMS User Consent API we can achieve auto read OTP by allowing an app to prompt the user to grant access to the content of a single SMS message. When the user gives consent, the app can access the entire message to automatically complete SMS verification.

auto read otp android user consent 1
auto read otp android user consent 2
auto read otp android user consent 3

Image source google developers website.

  • Start – The first thing you have to start is listening before sending the message or OTP to the server.
  • Prompt – When the user’s device receives the SMS message containing a one-time code, Google Play services display the contents of the message to the user and asks for consent to make that text available to your app.
  • Read Message – If the user consents, the entire SMS message is made available to your app.
Message Criteria

Google play services imposed the followings SMS criteria

  • It contains a one-time password– The should message contains a 4–10 character alphanumeric string with at least one number.
  • Contacts – The message was sent by a phone number that’s not in the user’s contacts.
  • Timing – The API will look for the One Time Code for a maximum time of 5 minutes.
1. Let’s create a new project in Android Studio

Open to an android studio and create a new project. That project we will implement auto read OTP features. So I’m adding few strings in strings.xml file that we will use this project.

<resources>
  <string name="app_name">SMS verification</string>
  <string name="send_otp">Send OTP</string>
  <string name="generate_otp">Generate OTP</string>
  <string name="received_message">Received Message</string>
  <string name="verify_otp">Verify OTP</string>
</resources>

We should add below dependencies in app-level build.gradle in your project.

    // add these lines in your app build.gradle
    implementation 'com.google.android.gms:play-services-auth:17.0.0'
    implementation 'com.google.android.gms:play-services-auth-api-phone:17.1.0'
3. Listen to the incoming messages

The next step is to listen to incoming messaging. we can start listening for incoming messages using below method. We can add sender phone number, SMS User Consent API will only trigger on messages from this number. I’m adding null here

  private void startSmsUserConsent() {
    SmsRetrieverClient client = SmsRetriever.getClient(this);
    //We can add sender phone number or leave it blank
    // I'm adding null here
    client.startSmsUserConsent(null).addOnSuccessListener(new OnSuccessListener<Void>() {
      @Override
      public void onSuccess(Void aVoid) {
        Toast.makeText(getApplicationContext(), "On Success", Toast.LENGTH_LONG).show();
      }
    }).addOnFailureListener(new OnFailureListener() {
      @Override
      public void onFailure(@NonNull Exception e) {
        Toast.makeText(getApplicationContext(), "On OnFailure", Toast.LENGTH_LONG).show();
      }
    });
  }
4. Create a BroadcastReceiver named is SmsBroadcastReceiver

In SmsBroadcastReceiverListener we will catch SMS and set back using SmsBroadcastReceiverListener callback methods.

package com.smsverification;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;

public class SmsBroadcastReceiver extends BroadcastReceiver{

  SmsBroadcastReceiverListener smsBroadcastReceiverListener;

  @Override
  public void onReceive(Context context, Intent intent) {
    if (intent.getAction() == SmsRetriever.SMS_RETRIEVED_ACTION) {
      Bundle extras = intent.getExtras();
      Status smsRetrieverStatus = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
      switch (smsRetrieverStatus.getStatusCode()) {
        case CommonStatusCodes.SUCCESS:
          Intent messageIntent = extras.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT);
          smsBroadcastReceiverListener.onSuccess(messageIntent);
          break;
        case CommonStatusCodes.TIMEOUT:
          smsBroadcastReceiverListener.onFailure();
          break;
      }
    }
  }

  public interface SmsBroadcastReceiverListener {
    void onSuccess(Intent intent);

    void onFailure();
  }
}

When the broadcast receiver catch any message that contains OTP, Google paly services display is contents( BottomSheet ) for asking permission. If the user allows then full messing is available to your app. So let’s register broadcast receiver.

private void registerBroadcastReceiver() {
    smsBroadcastReceiver = new SmsBroadcastReceiver();
    smsBroadcastReceiver.smsBroadcastReceiverListener =
        new SmsBroadcastReceiver.SmsBroadcastReceiverListener() {
          @Override
          public void onSuccess(Intent intent) {
            startActivityForResult(intent, REQ_USER_CONSENT);
          }

          @Override
          public void onFailure() {

          }
        };
    IntentFilter intentFilter = new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION);
    registerReceiver(smsBroadcastReceiver, intentFilter);
  }

  @Override
  protected void onStart() {
    super.onStart();
    registerBroadcastReceiver();
  }

  @Override
  protected void onStop() {
    super.onStop();
    unregisterReceiver(smsBroadcastReceiver);
  }
6. Receive message in onActivityResult()

If the user consents, the entire SMS message is made available to your app. we will catch messaging in

 @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQ_USER_CONSENT) {
      if ((resultCode == RESULT_OK) && (data != null)) {
        //That gives all message to us.
        // We need to get the code from inside with regex
        String message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE);
        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
        textViewMessage.setText(
            String.format("%s - %s", getString(R.string.received_message), message));

        getOtpFromMessage(message);
      }
    }
  }
7. Extract OTP from messaging.

Suppose our messaging is “Your OTP is 123456. Please do not share OTP with others “. Now you have to extract OTP from methods using below method.

  private void getOtpFromMessage(String message) {
    // This will match any 6 digit number in the message
    Pattern pattern = Pattern.compile("(|^)\\d{6}");
    Matcher matcher = pattern.matcher(message);
    if (matcher.find()) {
      otpText.setText(matcher.group(0));
    }
  }
8. I’m modifying main activity layout for demonstration

For creating a fully functional sample app, I’m adding TextView for showing messaging and edit text for showing OTP.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    >

  <Button
      android:id="@+id/button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/verify_otp"
      android:textAllCaps="false"
      android:textSize="18sp"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      />
  <TextView
      android:id="@+id/textViewMessage"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginStart="16dp"
      android:layout_marginTop="24dp"
      android:layout_marginEnd="16dp"
      android:gravity="center"
      android:textSize="16sp"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/button"
      tools:text="@string/received_message"
      />
  <ImageView
      android:id="@+id/imageView"
      android:layout_width="150dp"
      android:layout_height="150dp"
      android:layout_marginTop="32dp"
      app:layout_constraintBottom_toTopOf="@+id/editTextOTP"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:srcCompat="@drawable/ic_otp"
      />
  <EditText
      android:id="@+id/editTextOTP"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginStart="16dp"
      android:layout_marginEnd="16dp"
      android:layout_marginBottom="16dp"
      android:ems="10"
      android:gravity="center"
      android:hint="OTP"
      android:inputType="number"
      app:layout_constraintBottom_toTopOf="@+id/button"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      />

</androidx.constraintlayout.widget.ConstraintLayout>
9. We almost done, put all java code together in activity files.
package com.smsverification;

import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MainActivity extends AppCompatActivity {
  private static final int REQ_USER_CONSENT = 200;
  SmsBroadcastReceiver smsBroadcastReceiver;
  Button verifyOTP;
  TextView textViewMessage;
  EditText otpText;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // find view by ids
    verifyOTP = findViewById(R.id.button);
    textViewMessage = findViewById(R.id.textViewMessage);
    otpText = findViewById(R.id.editTextOTP);
    startSmsUserConsent();
  }

  private void startSmsUserConsent() {
    SmsRetrieverClient client = SmsRetriever.getClient(this);
    //We can add sender phone number or leave it blank
    // I'm adding null here
    client.startSmsUserConsent(null).addOnSuccessListener(new OnSuccessListener<Void>() {
      @Override
      public void onSuccess(Void aVoid) {
        Toast.makeText(getApplicationContext(), "On Success", Toast.LENGTH_LONG).show();
      }
    }).addOnFailureListener(new OnFailureListener() {
      @Override
      public void onFailure(@NonNull Exception e) {
        Toast.makeText(getApplicationContext(), "On OnFailure", Toast.LENGTH_LONG).show();
      }
    });
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQ_USER_CONSENT) {
      if ((resultCode == RESULT_OK) && (data != null)) {
        //That gives all message to us.
        // We need to get the code from inside with regex
        String message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE);
        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
        textViewMessage.setText(
            String.format("%s - %s", getString(R.string.received_message), message));

        getOtpFromMessage(message);
      }
    }
  }

  private void getOtpFromMessage(String message) {
    // This will match any 6 digit number in the message
    Pattern pattern = Pattern.compile("(|^)\\d{6}");
    Matcher matcher = pattern.matcher(message);
    if (matcher.find()) {
      otpText.setText(matcher.group(0));
    }
  }

  private void registerBroadcastReceiver() {
    smsBroadcastReceiver = new SmsBroadcastReceiver();
    smsBroadcastReceiver.smsBroadcastReceiverListener =
        new SmsBroadcastReceiver.SmsBroadcastReceiverListener() {
          @Override
          public void onSuccess(Intent intent) {
            startActivityForResult(intent, REQ_USER_CONSENT);
          }

          @Override
          public void onFailure() {

          }
        };
    IntentFilter intentFilter = new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION);
    registerReceiver(smsBroadcastReceiver, intentFilter);
  }

  @Override
  protected void onStart() {
    super.onStart();
    registerBroadcastReceiver();
  }

  @Override
  protected void onStop() {
    super.onStop();
    unregisterReceiver(smsBroadcastReceiver);
  }
}

Congrats, Let’s use the SMS content API is your application

Conclusion

In this post, we learned how to auto-read OTP android using SMS content API in our application. So let’s recap the step of implementation, basically, three major steps Start Listening messaging, show prompt and Read messaging.

 I hope it’s helpful for you, Help me by sharing this post with all your friends who learning android app development.

Get Solution Code

Keep Learning 🙂

In this post, I’m going to show you how to implement automatic SMS verification with SMS Retriever API. Using SMS Retriever API you can perform SMS verification in your app automatically, without requiring extra permission.

Table of Contents

  • Automatic SMS Verification Demo App
  • Introduction
  • Why you should use SMS Retriever API
  • Understand the SMS verification process
  • Step of ImplementationAdd gradle dependency in-app level
  • Retrieve user’s content from the Phone
  • Start SMS Retriever
  • Create an SMS Broadcast Receiver
  • Register SMS broadcast receiver in AndroidManifest
  • Initiate the request for OTP
  • Get SMS format & verification code in SMS Broadcast Receiver
  • Test the Demo App
  • Things you must do
  • Technology Used
  • Conclusion 

1. Automatic SMS Verification Demo App

2. Introduction

I shared step by step process to implement automatic SMS verification in your Android App. Before that, Let’s understand the flow of SMS verification process.

OTP sms verification flow overview
Source – Google

The above figure gives you little bit clarity on SMS verification.

3. Why you should use SMS Retriever API

Google change some critical changes in policy. From Jan 19th, 2019 google removed all app from play store with permission CALL_LOG and READ_SMS

4. Understand the SMS verification process

Earlier, when user had to login in android app on Android Platform, They enter mobile number to receive OTP. Then they gives READ_SMS permission to app for reading SMS. Recently Google had made some important change in its policy. Now Android Platform removes this permission due to data security reasons. So now you have to copy code received through SMS. Go back to the app and enter that code manually to log in.  

For overcoming this process, Google introduced SMS Retriever API to automatically fetch a verification code sent via SMS within the app. This way, user was not required to manually enter the code every time. Let’s follow the these given step to implement Automatic SMS Verification in an Android App.

5. Step of Implementation

Now, I will explain you step by step process to implement automatic SMS verification in your Android App

5.1 Add gradle dependency in-app level

Add the below lib in app level build.gradle for integrating SMS Retriever API in your project

dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation 'androidx.appcompat:appcompat:1.0.2'
  implementation 'com.android.support.constraint:constraint-layout:1.1.3'
  // lib for SMS verification (Phone Auth)
  implementation 'com.google.android.gms:play-services-auth:17.0.0'
  implementation 'com.google.android.gms:play-services-auth-api-phone:17.0.0'

  testImplementation 'junit:junit:4.12'
  androidTestImplementation 'com.android.support.test:runner:1.0.2'
  androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
5.2 Retrieve user’s content from the Phone

Obtain the phone number from device through hint picker for do that follow below step

  • Setup Google API Client
//set google api client for hint request
    mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(this)
        .enableAutoManage(this, this)
        .addApi(Auth.CREDENTIALS_API)
        .build();
  • Get an available number in user phone
 public void getHintPhoneNumber() {
    HintRequest hintRequest =
        new HintRequest.Builder()
            .setPhoneNumberIdentifierSupported(true)
            .build();
    PendingIntent mIntent = Auth.CredentialsApi.getHintPickerIntent(mGoogleApiClient, hintRequest);
    try {
      startIntentSenderForResult(mIntent.getIntentSender(), RESOLVE_HINT, null, 0, 0, 0);
    } catch (IntentSender.SendIntentException e) {
      e.printStackTrace();
    }
  }
  • Get Selected Number in onActivityResult
@Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    //Result if we want hint number
    if (requestCode == RESOLVE_HINT) {
      if (resultCode == Activity.RESULT_OK) {

        Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
        // credential.getId();  < – will need to process phone number string
        inputMobileNumber.setText(credential.getId());
      }
    }
  }
5.3 Start SMS Retriever

Once user submitted the phone, we should initiate SMS retrieval task  

public void startSMSListener() {
    SmsRetrieverClient mClient = SmsRetriever.getClient(this);
    Task<Void> mTask = mClient.startSmsRetriever();
    mTask.addOnSuccessListener(new OnSuccessListener<Void>() {
      @Override public void onSuccess(Void aVoid) {
        layoutInput.setVisibility(View.GONE);
        layoutVerify.setVisibility(View.VISIBLE);
        Toast.makeText(MainActivity.this, "SMS Retriever starts", Toast.LENGTH_LONG).show();
      }
    });
    mTask.addOnFailureListener(new OnFailureListener() {
      @Override public void onFailure(@NonNull Exception e) {
        Toast.makeText(MainActivity.this, "Error", Toast.LENGTH_LONG).show();
      }
    });
  }
5.4 Create an SMS Broadcast Receiver

Let’s create a Broadcast Receiver to receive SMS from SMS retriever API  

package com.wave.smsverification.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
import com.wave.smsverification.interfaces.OtpReceivedInterface;

/**
 * Created on : May 21, 2019
 * Author     : AndroidWave
 */
public class SmsBroadcastReceiver extends BroadcastReceiver {
  private static final String TAG = "SmsBroadcastReceiver";
  OtpReceivedInterface otpReceiveInterface = null;

  public void setOnOtpListeners(OtpReceivedInterface otpReceiveInterface) {
    this.otpReceiveInterface = otpReceiveInterface;
  }

  @Override public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "onReceive: ");
    if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
      Bundle extras = intent.getExtras();
      Status mStatus = (Status) extras.get(SmsRetriever.EXTRA_STATUS);

      switch (mStatus.getStatusCode()) {
        case CommonStatusCodes.SUCCESS:
          // Get SMS message contents'

          String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
          Log.d(TAG, "onReceive: failure "+message);
          if (otpReceiveInterface != null) {
            String otp = message.replace("<#> Your otp code is : ", "");
            otpReceiveInterface.onOtpReceived(otp);
          }
          break;
        case CommonStatusCodes.TIMEOUT:
          // Waiting for SMS timed out (5 minutes)
          Log.d(TAG, "onReceive: failure");
          if (otpReceiveInterface != null) {
            otpReceiveInterface.onOtpTimeout();
          }
          break;
      }
    }
  }
}
Create a listener that send the OTP to activity or fragment
package com.wave.smsverification.interfaces;

/**
 * Created on : May 21, 2019
 * Author     : AndroidWave
 */
public interface OtpReceivedInterface {
  void onOtpReceived(String otp);
  void onOtpTimeout();
}
5.6 Register SMS broadcast receiver in AndroidManifest

Open the Android Manifest and register the receiver with intent filter

<receiver
        android:name=".receiver.SmsBroadcastReceiver"
        android:exported="true">
      <intent-filter>
        <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVE" />
      </intent-filter>
</receiver>
5.7 Open activity_main.xml and paste below 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"
    android:orientation="vertical"
    tools:context=".MainActivity"
    >
  <android.support.constraint.ConstraintLayout
      android:id="@+id/getOTPLayout"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginBottom="24dp"
      android:visibility="visible"
      >


    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/otp"
        />
    <EditText
        android:id="@+id/editTextInputMobile"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:gravity="center"
        android:hint="9166964412"
        android:inputType="phone"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2"
        />
    <Button
        android:id="@+id/buttonGetOTP"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="24dp"
        android:background="@color/colorAccent"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        android:text="Get OTP"
        android:textAllCaps="false"
        android:textColor="#fff"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editTextInputMobile"
        />
    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:text="Enter the mobile number"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        />

  </android.support.constraint.ConstraintLayout>
  <android.support.constraint.ConstraintLayout
      android:id="@+id/verifyOTPLayout"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginBottom="24dp"
      android:visibility="gone"
      >


    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/otp"
        />
    <EditText
        android:id="@+id/editTextOTP"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:gravity="center"
        android:hint=""
        android:inputType="phone"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        />
    <Button
        android:id="@+id/buttonVerify"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="24dp"
        android:background="@color/colorAccent"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        android:text="Verify"
        android:textAllCaps="false"
        android:textColor="#fff"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editTextOTP"
        />
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:text="Enter the OTP received on"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView2"
        />

  </android.support.constraint.ConstraintLayout>
</LinearLayout>
5.8 Initiate the request for OTP

Call server API for requesting OTP and when you got success start SMS Listener for listing auto read message listener

// listen sms  
 startSMSListener();
5.9 Get SMS format & verification code in SMS Broadcast Receiver

This receiver will receive the OTP and pass to the activity where you can finish authentication.

The full source code of MainActivity.java

package com.wave.smsverification;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.credentials.Credential;
import com.google.android.gms.auth.api.credentials.HintRequest;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.wave.smsverification.interfaces.OtpReceivedInterface;
import com.wave.smsverification.receiver.SmsBroadcastReceiver;

public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
    OtpReceivedInterface, GoogleApiClient.OnConnectionFailedListener {
  GoogleApiClient mGoogleApiClient;
  SmsBroadcastReceiver mSmsBroadcastReceiver;
  private int RESOLVE_HINT = 2;
  EditText inputMobileNumber, inputOtp;
  Button btnGetOtp, btnVerifyOtp;
  ConstraintLayout layoutInput, layoutVerify;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initViews();
    // init broadcast receiver
    mSmsBroadcastReceiver = new SmsBroadcastReceiver();

    //set google api client for hint request
    mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(this)
        .enableAutoManage(this, this)
        .addApi(Auth.CREDENTIALS_API)
        .build();

    mSmsBroadcastReceiver.setOnOtpListeners(this);
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
    getApplicationContext().registerReceiver(mSmsBroadcastReceiver, intentFilter);

    // get mobile number from phone
    getHintPhoneNumber();
    btnGetOtp.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View v) {
        // Call server API for requesting OTP and when you got success start
        // SMS Listener for listing auto read message lsitner
        startSMSListener();
      }
    });
  }

  private void initViews() {
    inputMobileNumber = findViewById(R.id.editTextInputMobile);
    inputOtp = findViewById(R.id.editTextOTP);
    btnGetOtp = findViewById(R.id.buttonGetOTP);
    btnVerifyOtp = findViewById(R.id.buttonVerify);
    layoutInput = findViewById(R.id.getOTPLayout);
    layoutVerify = findViewById(R.id.verifyOTPLayout);
  }

  @Override public void onConnected(@Nullable Bundle bundle) {

  }

  @Override public void onConnectionSuspended(int i) {

  }

  @Override public void onOtpReceived(String otp) {
    Toast.makeText(this, "Otp Received " + otp, Toast.LENGTH_LONG).show();
    inputOtp.setText(otp);
  }

  @Override public void onOtpTimeout() {
    Toast.makeText(this, "Time out, please resend", Toast.LENGTH_LONG).show();
  }

  @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

  }

  public void startSMSListener() {
    SmsRetrieverClient mClient = SmsRetriever.getClient(this);
    Task<Void> mTask = mClient.startSmsRetriever();
    mTask.addOnSuccessListener(new OnSuccessListener<Void>() {
      @Override public void onSuccess(Void aVoid) {
        layoutInput.setVisibility(View.GONE);
        layoutVerify.setVisibility(View.VISIBLE);
        Toast.makeText(MainActivity.this, "SMS Retriever starts", Toast.LENGTH_LONG).show();
      }
    });
    mTask.addOnFailureListener(new OnFailureListener() {
      @Override public void onFailure(@NonNull Exception e) {
        Toast.makeText(MainActivity.this, "Error", Toast.LENGTH_LONG).show();
      }
    });
  }

  public void getHintPhoneNumber() {
    HintRequest hintRequest =
        new HintRequest.Builder()
            .setPhoneNumberIdentifierSupported(true)
            .build();
    PendingIntent mIntent = Auth.CredentialsApi.getHintPickerIntent(mGoogleApiClient, hintRequest);
    try {
      startIntentSenderForResult(mIntent.getIntentSender(), RESOLVE_HINT, null, 0, 0, 0);
    } catch (IntentSender.SendIntentException e) {
      e.printStackTrace();
    }
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    //Result if we want hint number
    if (requestCode == RESOLVE_HINT) {
      if (resultCode == Activity.RESULT_OK) {
        if (data != null) {
          Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
          // credential.getId();  < – will need to process phone number string
          inputMobileNumber.setText(credential.getId());
        }

      }
    }
  }
}

6. Test the Demo App

When sever receive the request to OTP via REST API, Server will send OTP message to device. You have to follow below message format.

Message Format Must Be –

Google introduced a new format for OTP message. Follow below SMS format

  • Prefix: <#>  
    • The message should start with <#>
  • Content: Your OTP is: 156367
  • Postfix: Application key hash from Keystore (Debug or Release) eg. T61bL03HCN8
    • The message should end with hashcode. It received from LOG CAT generated by the AppSignature helper. Based on this system will pass the message to the respective app.   
Let’s check below Example

<#> You OTP is: 156367 T61bL03HCN8

7. How to get APK’s hashcode for SMS construction

Let’s create a class named is AppSignatureHelper and paste the below code. This is the simplest way to get Hashcode. You can generate using CMD as well. Once you got hashcode than that deletes helper class.

package com.wave.smsverification.helper;

import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.util.Base64;
import android.util.Log;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * Created on : May 21, 2019
 * Author     : AndroidWave
 */
public class AppSignatureHelper extends ContextWrapper {

  private static final String TAG = "AppSignatureHelper";
  private static final String HASH_TYPE = "SHA-256";
  public static final int NUM_HASHED_BYTES = 9;
  public static final int NUM_BASE64_CHAR = 11;

  public AppSignatureHelper(Context context) {
    super(context);
  }

  /**
   * Get all the app signatures for the current package
   */
  public ArrayList<String> getAppSignatures() {
    ArrayList<String> appCodes = new ArrayList<>();

    try {
      // Get all package signatures for the current package
      String packageName = getPackageName();
      PackageManager packageManager = getPackageManager();
      Signature[] signatures = packageManager.getPackageInfo(packageName,
          PackageManager.GET_SIGNATURES).signatures;

      // For each signature create a compatible hash
      for (Signature signature : signatures) {
        String hash = hash(packageName, signature.toCharsString());
        if (hash != null) {
          appCodes.add(String.format("%s", hash));
        }
      }
    } catch (PackageManager.NameNotFoundException e) {
      Log.e(TAG, "Unable to find package to obtain hash.", e);
    }
    return appCodes;
  }

  private static String hash(String packageName, String signature) {
    String appInfo = packageName + " " + signature;
    try {
      MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
      messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
      byte[] hashSignature = messageDigest.digest();

      // truncated into NUM_HASHED_BYTES
      hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
      // encode into Base64
      String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
      base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);

      Log.d(TAG, String.format("pkg: %s – hash: %s", packageName, base64Hash));
      return base64Hash;
    } catch (NoSuchAlgorithmException e) {
      Log.e(TAG, "hash:NoSuchAlgorithm", e);
    }
    return null;
  }
}
Call getAppSignatures() methods in application onCreate()
package com.wave.smsverification;

import android.app.Application;
import com.wave.smsverification.helper.AppSignatureHelper;

/**
 * Created on : May 21, 2019
 * Author     : AndroidWave
 */
public class SmsVerificationApp extends Application {
  @Override public void onCreate() {
    super.onCreate();
    AppSignatureHelper appSignatureHelper = new AppSignatureHelper(this);
    appSignatureHelper.getAppSignatures();
  }
}

8. Things you must do

  • Once you completed get the hash code to remove the AppSignatureHelper class from your project before going to live or production.
  • In Android, Debug and Release APK’s have different Hashcode, Kindly make sure you get hash code from release build.  

9. Technology Used

Tool: Android Studio v3.3 with API 28 (Pie 9.0), SDK
Language: Java, XML

Conclusion

With the help of this android app tutorial, We have learned how to implement automatic SMS verification using SMS Retriever API. Later I will upload APK and Source code as well, So you can get source of this demo app.

Get Solution Code

If you have any comments and queries please put your comment below. If you looking to integrate the automatic SMS verification process in your android project, Feel free to content us.