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:

- 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.
- 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.
- Then calls the SMS Retriever API to start listening to your server for an SMS response.
- 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.
- 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.
- Your app will parse the one-time message text code and send it back to your server.
- 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/+ |
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 |
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 | |
} | |
} | |
} |
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 | |
// ... | |
} | |
}); |
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> |
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 { | |
} | |
} | |
} | |
} |
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> |
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> |

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))); | |
} | |
} |
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> |
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> |

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); | |
} | |
} |
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> |
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' |
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' | |
} |
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' | |
} |
You Can Find Doc’s For The Usage Our Library At https://github.com/pratheepchowdhary/AutoDetectOTPAndroid
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.|
I am in fact pleased to read this blog posts which includes lots of valuable data, thanks for providing these kinds of information.|
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.|
Good article. I certainly appreciate this site. Keep it up!|
I used to be able to find good information from your blog articles.|
Thanks very nice blog!|
Hi when i executed command i got error
” ‘xxd’ is not recognized as an internal or external command,
operable program or batch file.”