This the tutorial helps you to implement auto detect OTP in your Android Application.

Now a days most of the Applications Uses the SMS Based Verification to authenticate their Users By Sending OTP(One-Time-Password), where the service provider sends a message and the application automatically reads it and authenticate the user. This Flow Helps the users to avoid Switching between the applications to enter password Manually.

To auto detect OTP previously most of the applications uses SMS Permissions for reading messages .Now google play restricting application and Removing applications from play store on name of SMS Permissions.

Why Google Play SMS permissions are restricted :

The use of high risk or sensitive permissions, including SMS permission groups, is restricted by Google Play.

If your app does not require SMS permissions, these permissions must be removed from the manifest of your app. Details on alternative implementation compliant with policies are also detailed below.

For applications with previously submitted Declaration Forms, Google Play may, at its discretion, grant extensions for you to comply with this Play policy until March 9, 2019.

You can also request an extension directly through the Play Console for March 9, 2019 by releasing a new version of your APK with a higher version code if you are not planning to use these permissions but still need additional time to comply with your app(s).

You should declare any SMS permissions directly through the Play Console if you believe that your app meets the policy requirements for acceptable use or is eligible for an exception.

Apps that fail to meet policy requirements or submit a Declaration Form may be removed from Google Play.

When should these permissions be accessed?

Only when your app falls within the permitted uses should you access SMS permissions and only to enable the core functionality of your app.

Core features are defined as the app’s main purpose. It is the most prominently documented and promoted feature in the description of the app no other feature is more central to the functionality of the app. If this feature is not provided, the app will be “broken” or unusable (i.e., the app will be deprived of its primary functionality and will not perform as expected by a user).

OTP Verification Alternative for Common Use

With the SMS Retriever API, you can automatically perform SMS-based user verification in your app without requiring the user to type verification codes manually and without requiring any additional permissions for the application.

If your app does not have the SMS Retriever API, users can also enter a verification code manually.

Automatic SMS Verification with the SMS Retriever API

The verification flow looks like this when you implement automatic SMS verification in your app:

Flow Of SMS Retriver
  1. In your application, a user initiates SMS verification. If this information was not required to create the user’s account, your app might prompt the user to provide a phone number by using the Smart Lock for Passwords hint selector.
  2. Your app asks your server to check the telephone number of the user. This request may include the user ID, phone number of the user, or both, depending on what information is available in your user database.
  3. Then calls the SMS Retriever API to start listening to your server for an SMS response.
  4. Your server sends the user a SMS message that includes a one-time code to can be sent back to your server and a hash identifying your application.
  5. Upon receiving the SMS message in the user’s device, Google Play services will use the app hash to determine that the message is intended for your app and make the message text available through the SMS Retriever API to your app.
  6. Your app will parse the one-time message text code and send it back to your server.
  7. Your server receives your app’s one-time code, checks the code, and finally records that your account has been successfully verified by the user.

Message Format

<#>Your AndroidHunt OTP is: 8686. ynfd/rIwy/+
view raw SMSFORMATE.txt hosted with ❤ by GitHub

Here ynfd/rIwy/+ hash code of the application which Play major role in this SMS Dectection.

How To Generate Hash Code

There are two ways to generate hash code

1.Using Command Line With Key store Tool and Command as Shown below

keytool -exportcert -alias YOUR_KEYSTORE_ALIAS -keystore YOUR_KEYSTORE_FILE | xxd -p | tr -d "[:space:]" | echo -n com.example.myapp `cat` | sha256sum | tr -d "[:space:]-" | xxd -r -p | base64 | cut -c1-11
view raw keytool.sh hosted with ❤ by GitHub

2. Programmatically Generating Hash Code at run time.

The Helper Class to Generate Hash Code Is Mention’s in This Tutorials Below AppSignatureHelper.java

Lets Starts Coding From here

Prerequisites

The SMS Retriever API is only available with Play Services version 10.2 and newer on Android devices.

1. Get the phone number of the user

You can get the phone number of the user in any way that suits your app. Often, using the hint picker to prompt the user to select from the phone numbers stored on the device and thus avoid having to type a phone number manually is the best user experience. To use the picker hint:

// Construct a request for phone numbers and show the picker
private void requestHint() {
HintRequest hintRequest = new HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build();
PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(
apiClient, hintRequest);
startIntentSenderForResult(intent.getIntentSender(),
RESOLVE_HINT, null, 0, 0, 0);
}
// Obtain the phone number from the result
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESOLVE_HINT) {
if (resultCode == RESULT_OK) {
Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
// credential.getId(); <-- will need to process phone number string
}
}
}
view raw HintPhoneNumber.java hosted with ❤ by GitHub

2. Start the SMS retriever

Receive an instance of the SmsRetrieverClient object, call startSmsRetriever, and attach success and failure listeners to the SMS retrieval task when you are ready to check the phone number of the user:

// Get an instance of SmsRetrieverClient, used to start listening for a matching
// SMS message.
SmsRetrieverClient client = SmsRetriever.getClient(this /* context */);
// Starts SmsRetriever, which waits for ONE matching SMS message until timeout
// (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
// action SmsRetriever#SMS_RETRIEVED_ACTION.
Task<Void> task = client.startSmsRetriever();
// Listen for success/failure of the start Task. If in a background thread, this
// can be made blocking using Tasks.await(task, [timeout]);
task.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
// Successfully started retriever, expect broadcast intent
// ...
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// Failed to start retriever, inspect Exception for more details
// ...
}
});
view raw SmsRetriver.java hosted with ❤ by GitHub

The SMS retrieval task will listen to an SMS message with a unique string identifying your app for up to five minutes.

3. Send the phone number to your server

Using any method (usually with an HTTPS POST request) after you have obtained the phone number of the user and have started listening for SMS messages, send the phone number of the user to your verification to server.

Your server generates a message of verification and sends it to your specified phone number by SMS. See Verifying SMS on the server.

4. Receive verification messages

When a verification message is received on the device of the user, Play services will explicitly transmit a SmsRetriever. SMS RETRIEVED ACTION Intent containing the message text to your application. To receive this verification message, use a BroadcastReceiver.

In the BroadcastReceiver‘s onReceive handler, get the text of the verification message from the Intent’s extras:

/**
* BroadcastReceiver to wait for SMS messages. This can be registered either
* in the AndroidManifest or at runtime. Should filter Intents on
* SmsRetriever.SMS_RETRIEVED_ACTION.
*/
public class MySMSBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
switch(status.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
// Get SMS message contents
String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
// Extract one-time code from the message and complete verification
// by sending the code back to your server.
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
break;
}
}
}
}

Register this BroadcastReceiver with the intent filter com.google.android.gms.auth.api.phone.SMS RETRIEVED (value of constant SmsRetriever. SMS RETRIEVED ACTION) in the AndroidManifest.xml file of your app, as shown in the example below, or use Context.registerReceiver dynamically.

<receiver android:name=".MySMSBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
</intent-filter>
</receiver>
view raw AndroidManifest.xml hosted with ❤ by GitHub

That’s all This Are Basic Things Required For OTP Verification Using SMS Retriever API . You Get OTP and Verify Its As Per Your Requirements.

Implementation Of Sms Retriever Api with Sample App

1.Creating Main Activity For Entering Mobile Number

MainActivity.java

package in.androidhunt.otpdemo;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatEditText;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.rilixtech.CountryCodePicker;
import in.androidhunt.otp.AutoDetectOTP;
public class MainActivity extends AppCompatActivity {
AutoDetectOTP autoDetectOTP;
CountryCodePicker countryCodePicker;
AppCompatEditText edtPhoneNumber;
String testMessage="<#>Your AndroidHunt OTP is: 8686. ";
TextView test_message;
Button copyMessage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
countryCodePicker = findViewById(R.id.ccp);
edtPhoneNumber = findViewById(R.id.phone_number_edt);
countryCodePicker.registerPhoneNumberTextView(edtPhoneNumber);
autoDetectOTP= new AutoDetectOTP(this);
test_message=findViewById(R.id.message);
copyMessage=findViewById(R.id.copy_message);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent(MainActivity.this,OtpActivity.class);
intent.putExtra("NO",countryCodePicker.getFullNumberWithPlus());
startActivity(intent);
}
});
//Requesting Phone Numbers Hint Prompt
autoDetectOTP.requestPhoneNoHint();
String message=testMessage+AutoDetectOTP.getHashCode(this);
test_message.setText("Hash Code For This App is "+AutoDetectOTP.getHashCode(this)+"\n"+message);
copyMessage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", testMessage+AutoDetectOTP.getHashCode(MainActivity.this));
if (clipboard == null) return;
clipboard.setPrimaryClip(clip);
Toast.makeText(MainActivity.this,"Message Copied To ClipBoard", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == AutoDetectOTP.RC_HINT) {
if (resultCode == RESULT_OK) {
countryCodePicker.setFullNumber(autoDetectOTP.getPhoneNo(data));
Snackbar.make(findViewById(R.id.root_view), autoDetectOTP.getPhoneNo(data), Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
} else {
}
}
}
}
view raw MainActivity.java hosted with ❤ by GitHub

Creating Xml Design For MainActivity.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root_view"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
<TextView
android:id="@+id/message"
android:textColor="@color/black"
android:layout_margin="20dp"
android:layout_gravity="bottom|center_horizontal"
android:textSize="18dp"
android:paddingBottom="200dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/copy_message"
android:layout_width="wrap_content"
android:layout_gravity="bottom"
android:layout_marginLeft="20dp"
android:layout_marginBottom="20dp"
android:layout_height="wrap_content"
android:background="@drawable/bg_buttons"
android:text=" Copy Test Message " />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@drawable/ic_right" />
</android.support.design.widget.CoordinatorLayout>
view raw activty_main.xml hosted with ❤ by GitHub

content_main.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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".MainActivity"
tools:showIn="@layout/activity_main">
<RelativeLayout
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginLeft="32dp"
android:layout_marginRight="16dp"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Phone number"
android:textSize="18sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<com.rilixtech.CountryCodePicker
android:id="@+id/ccp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:ccp_enableHint="false"
app:ccp_defaultCode="91"/>
<android.support.v7.widget.AppCompatEditText
android:layout_marginLeft="5dp"
android:id="@+id/phone_number_edt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Phone No"
android:inputType="phone"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
view raw content_main.xml hosted with ❤ by GitHub
Final Design For MainActivity

Above Shows Design Of MainActivity

2.Creating OTP Screen:

OtpActivity.java

package in.androidhunt.otpdemo;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.support.design.widget.AppBarLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import android.widget.Toast;
import java.util.concurrent.TimeUnit;
import in.aabhasjindal.otptextview.OTPListener;
import in.aabhasjindal.otptextview.OtpTextView;
import in.androidhunt.otp.AutoDetectOTP;
public class OtpActivity extends AppCompatActivity {
private String otpnN0;
TextView timer;
AutoDetectOTP autoDetectOTP;
private OtpTextView otpTextView;
CountDownTimer countDownTimer= new CountDownTimer(180000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
timer.setText(millisecondsToTime(millisUntilFinished));
}
@Override
public void onFinish() {
timer.setText("");
}
};
Handler handler=new Handler();
Runnable runnable=new Runnable() {
@Override
public void run() {
if(otpnN0!=null&&otpnN0.equals("1234")){
otpTextView.showSuccess();
countDownTimer.cancel();
}
else {
otpTextView.showError();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
supportRequestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_otp);
autoDetectOTP=new AutoDetectOTP(this);
Toolbar toolbar = findViewById(R.id.toolbar);
TextView phoneview=findViewById(R.id.phone_);
timer= findViewById(R.id.timer);
setSupportActionBar(toolbar);
getSupportActionBar().setBackgroundDrawable(null);
AppBarLayout app= findViewById(R.id.appbar);
if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.LOLLIPOP) {
app.setOutlineProvider(null);
}
findViewById(R.id.fab_previos).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
String no=getIntent().getStringExtra("NO");
if(no!=null){
phoneview.append(no);
}
otpTextView = findViewById(R.id.otp_view);
otpTextView.requestFocusOTP();
otpTextView.setOtpListener(new OTPListener() {;
@Override
public void onInteractionListener() {
}
@Override
public void onOTPComplete(String otp) {
otpnN0=otp;
Toast.makeText(OtpActivity.this,"The OTP is " + otp, Toast.LENGTH_SHORT).show();
handler.postDelayed(runnable,100);
}
});
countDownTimer.start();
autoDetectOTP.startSmsRetriver(new AutoDetectOTP.SmsCallback() {
@Override
public void connectionfailed() {
Toast.makeText(OtpActivity.this,"Failed", Toast.LENGTH_SHORT).show();
}
@Override
public void connectionSuccess(Void aVoid) {
Toast.makeText(OtpActivity.this,"Success", Toast.LENGTH_SHORT).show();
}
@Override
public void smsCallback(String sms) {
if(sms.contains(":") && sms.contains(".")) {
String otp = sms.substring( sms.indexOf(":")+1 , sms.indexOf(".") ).trim();
otpTextView.setOTP(otp);
Toast.makeText(OtpActivity.this,"The OTP is " + otp, Toast.LENGTH_SHORT).show();
}
}
});
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", AutoDetectOTP.getHashCode(OtpActivity.this));
if (clipboard == null) return;
clipboard.setPrimaryClip(clip);
Toast.makeText(OtpActivity.this,AutoDetectOTP.getHashCode(OtpActivity.this), Toast.LENGTH_SHORT).show();
}
});
}
@Override
protected void onDestroy() {
if (countDownTimer != null) {
countDownTimer.onFinish();
countDownTimer.cancel();
}
super.onDestroy();
}
@Override
protected void onStop() {
super.onStop();
autoDetectOTP.stopSmsReciever();
if (countDownTimer != null) {
countDownTimer.onFinish();
countDownTimer.cancel();
}
}
private String millisecondsToTime(long milliseconds) {
return "Time remaining " + String.format("%d : %d ",
TimeUnit.MILLISECONDS.toMinutes(milliseconds),
TimeUnit.MILLISECONDS.toSeconds(milliseconds) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds)));
}
}
view raw OtpActivity.java hosted with ❤ by GitHub

Creating XML Desgin For OtpActivity

activity_otp.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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=".OtpActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/transparent"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_otp" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@drawable/ic_check" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_previos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@drawable/ic_left" />
</android.support.design.widget.CoordinatorLayout>
view raw activity_otp.xml hosted with ❤ by GitHub

content_otp.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
android:background="@android:color/white"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:text="@string/verification_code"
android:textColor="@color/defaultTextColor"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/phone_"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:lineSpacingExtra="4dp"
android:text="@string/please_type_the_verification_code_sent_to_n_9xxxxxxx19"
android:textAlignment="center"
android:textColor="@color/defaultTextColor"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<in.aabhasjindal.otptextview.OtpTextView
android:id="@+id/otp_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
app:bar_active_color="@color/black"
app:bar_enabled="true"
app:bar_error_color="@color/red"
app:bar_height="1.5dp"
app:bar_inactive_color="@color/grey"
app:bar_margin_bottom="0dp"
app:bar_margin_left="2dp"
app:bar_margin_right="2dp"
app:bar_success_color="@color/green"
app:box_margin="0dp"
app:height="40dp"
app:hide_otp="true"
app:hide_otp_drawable="@drawable/black_circle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:length="4"
app:otp=""
app:otp_text_size="20dp"
app:width="40dp" />
<TextView
android:id="@+id/timer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="72dp"
android:textColor="@color/defaultTextColor"
android:textSize="18sp"
android:fontFamily="sans-serif"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/otp_view" />
</android.support.constraint.ConstraintLayout>
view raw content_otp.xml hosted with ❤ by GitHub
Final Design For OtpActivity

3.Creating The Helper Classes To AutoDetectOTP

AutoDetectOTP.java

package in.androidhunt.otp;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
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.CredentialPickerConfig;
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.CommonStatusCodes;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
/**
* Created by Pratheep Chowdhary on 02,March,2019
*/
public class AutoDetectOTP {
public static final int RC_HINT = 1000;
private SmsCallback smsCallback;
private GoogleApiClient googleApiClient;
private Context context;
private BroadcastReceiver chargerReceiver;
private AppCompatActivity appCompatActivity;
private IntentFilter intentFilter;
public AutoDetectOTP(Context context) {
this.appCompatActivity = (AppCompatActivity) context;
this.context = appCompatActivity.getApplicationContext();
}
public void requestPhoneNoHint() {
googleApiClient = new GoogleApiClient.Builder(context)
.enableAutoManage(appCompatActivity, new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
}
})
.addApi(Auth.CREDENTIALS_API)
.build();
HintRequest hintRequest = new HintRequest.Builder()
.setHintPickerConfig(new CredentialPickerConfig.Builder()
.setShowCancelButton(true)
.build())
.setPhoneNumberIdentifierSupported(true)
.build();
PendingIntent intent =
Auth.CredentialsApi.getHintPickerIntent(googleApiClient, hintRequest);
try {
appCompatActivity.startIntentSenderForResult(intent.getIntentSender(), RC_HINT, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e("PHONE_HINT", "Could not start hint picker Intent", e);
}
}
public void requestPhoneNoHint(final Callback callback) {
googleApiClient = new GoogleApiClient.Builder(context)
.enableAutoManage(appCompatActivity, new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
}
})
.addApi(Auth.CREDENTIALS_API)
.build();
googleApiClient = new GoogleApiClient.Builder(context)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(@Nullable Bundle bundle) {
callback.connectionSuccess(bundle);
}
@Override
public void onConnectionSuspended(int i) {
callback.connectionSuspend(i);
}
})
.enableAutoManage(appCompatActivity, new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
callback.connectionfailed(connectionResult);
}
})
.addApi(Auth.CREDENTIALS_API)
.build();
HintRequest hintRequest = new HintRequest.Builder()
.setHintPickerConfig(new CredentialPickerConfig.Builder()
.setShowCancelButton(true)
.build())
.setPhoneNumberIdentifierSupported(true)
.build();
PendingIntent intent =
Auth.CredentialsApi.getHintPickerIntent(googleApiClient, hintRequest);
try {
appCompatActivity.startIntentSenderForResult(intent.getIntentSender(), RC_HINT, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e("PHONE_HINT", "Could not start hint picker Intent", e);
}
}
public void startSmsRetriver(final SmsCallback smsCallback) {
registerReceiver();
this.smsCallback = smsCallback;
// Get an instance of SmsRetrieverClient, used to start listening for a matching
// SMS message.
SmsRetrieverClient client = SmsRetriever.getClient(context);
// Starts SmsRetriever, which waits for ONE matching SMS message until timeout
// (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
// action SmsRetriever#SMS_RETRIEVED_ACTION.
Task<Void> task = client.startSmsRetriever();
// Listen for success/failure of the start Task. If in a background thread, this
// can be made blocking using Tasks.await(task, [timeout]);
task.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.e("SMSRE","success");
smsCallback.connectionSuccess(aVoid);
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
smsCallback.connectionfailed();
}
});
}
public String getPhoneNo(Intent data) {
Credential cred = data.getParcelableExtra(Credential.EXTRA_KEY);
return cred.getId();
}
private void registerReceiver() {
// filter to receive SMS
intentFilter = new IntentFilter();
intentFilter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
// receiver to receive and to get otp from SMS
chargerReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
switch (status.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
// Get SMS message contents
String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
// Extract one-time code from the message and complete verification
// by sending the code back to your server for SMS authenticity.
smsCallback.smsCallback(message);
stopSmsReciever();
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
smsCallback.connectionfailed();
break;
}
}
}
};
appCompatActivity.getApplication().registerReceiver(chargerReceiver, intentFilter);
}
public void stopSmsReciever() {
try {
appCompatActivity.getApplicationContext().unregisterReceiver(chargerReceiver);
}
catch (IllegalArgumentException e){
e.printStackTrace();
}
}
;
public interface Callback {
void connectionfailed(ConnectionResult connectionResult);
void connectionSuspend(int i);
void connectionSuccess(Bundle bundle);
}
public interface SmsCallback {
void connectionfailed();
void connectionSuccess(Void aVoid);
void smsCallback(String sms);
}
public static String getHashCode(Context context){
AppSignatureHelper appSignature = new AppSignatureHelper(context);
Log.e(" getAppSignatures ",""+appSignature.getAppSignatures());
return appSignature.getAppSignatures().get(0);
}
}
view raw AutoDetectOTP.java hosted with ❤ by GitHub

AppSignatureHelper.java

Which Is Used To Generate Hash Code For Your Application Programmatically

package in.androidhunt.otp;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import android.util.Base64;
import android.util.Log;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
public class AppSignatureHelper extends ContextWrapper {
public static final String TAG = AppSignatureHelper.class.getSimpleName();
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);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
}
else {
messageDigest.update(appInfo.getBytes(Charset.forName("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.e(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
return base64Hash;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "hash:NoSuchAlgorithm", e);
}
return null;
}
}

4.Manifest

AndroidManifest.xml

Note:

We are Not Specifying The Broad Cast receiver in Android Manifest Because We are Creating Broadcast receiver Dynamically .

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="in.androidhunt.otpdemo">
<application
android:allowBackup="true"
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=".OtpActivity"
android:label=""
android:theme="@style/AppTheme.NoActionBar"></activity>
<activity
android:name=".MainActivity"
android:label="Sign Up"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
view raw AndroidManifest.xml hosted with ❤ by GitHub

5.Libraries Required

// used For Auto OTP Detection
implementation 'com.google.android.gms:play-services-auth-api-phone:16.0.0'
//Used For Phone Number Hint Promt
implementation 'com.google.android.gms:play-services-base:16.1.0'
implementation 'com.google.android.gms:play-services-identity:16.0.0'
implementation 'com.google.android.gms:play-services-auth:16.0.1'
// Used For OTP View In OtpActivity
implementation 'com.github.aabhasr1:OtpView:1.0.5'
//Used For Phone No Country Code Picker
implementation 'com.github.joielechong:countrycodepicker:2.2.0'
view raw libraries.java hosted with ❤ by GitHub

build.gradle

Full build .gradle File

apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "in.androidhunt.otpdemo"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
signingConfig signingConfigs.config
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config
}
debug {
signingConfig signingConfigs.config
}
}
productFlavors {
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.github.aabhasr1:OtpView:1.0.5'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:design:28.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'
implementation 'com.github.joielechong:countrycodepicker:2.2.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:16.0.0'
implementation 'com.google.android.gms:play-services-base:16.1.0'
implementation 'com.google.android.gms:play-services-identity:16.0.0'
implementation 'com.google.android.gms:play-services-auth:16.0.1'
}
view raw build.gradle hosted with ❤ by GitHub

So this how implementation Auto OTP Detection with out SMS Permissions.

You Can Find The Full Source Code At https://github.com/pratheepchowdhary/AutoDetectOTPAndroid

For Testing This Auto OTP Detect With Our Application You Need Click On Copy Test Message Button In MainActivity. Sample Message Get Copied to Clipboard with hash code of Our Application Try Send This Message From Other Mobile to Your Mobile Now It Detects OTP Thats All.

If You Wants Implement Auto Detect OTP To your Applications With Single Line . Use Our Library.

dependencies {
implementation 'in.androidhunt.otp:AutoDetectOTPAndroid:1.0.0'
}
view raw AutoDetect.gradle hosted with ❤ by GitHub

You Can Find Doc’s For The Usage Our Library At https://github.com/pratheepchowdhary/AutoDetectOTPAndroid

7 COMMENTS

  1. Simply desire to say your article is as surprising. The clarity in your put up is just cool and that i can suppose you’re an expert on this subject. Fine along with your permission allow me to grab your RSS feed to keep updated with forthcoming post. Thank you one million and please carry on the enjoyable work.|

  2. You’ve made some decent points there. I looked on the internet for more information about the issue and found most individuals will go along with your views on this web site.|

  3. Hi when i executed command i got error
    ” ‘xxd’ is not recognized as an internal or external command,
    operable program or batch file.”

Leave a Reply