OTP (токен) должен автоматически считываться из сообщения
Я работаю над Android-приложением, в котором сервер отправляет OTP, и пользователю необходимо ввести этот OTP в приложении, в SignUp для моего приложения. Я хочу, чтобы мое приложение должно было автоматически читать OTP, отправленный сервером. Как я могу это достичь? Любая помощь или руководство в этом отношении будут высоко оценены.
Ответы
Ответ 1
Я рекомендую вам не использовать сторонние библиотеки для автоматической загрузки OTP из SMS Inbox. Это можно легко сделать, если у вас есть базовые знания о Broadcast Receiver и о том, как он работает. Просто попробуйте следующий подход:
Шаг 1) Создайте единый интерфейс, т.е. SmsListner
package com.wnrcorp.reba;
public interface SmsListener{
public void messageReceived(String messageText);}
Шаг 2) Создайте один приемник широковещания, т.е. SmsReceiver
package com.wnrcorp.reba;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
public class SmsReceiver extends BroadcastReceiver {
private static SmsListener mListener;
Boolean b;
String abcd,xyz;
@Override
public void onReceive(Context context, Intent intent) {
Bundle data = intent.getExtras();
Object[] pdus = (Object[]) data.get("pdus");
for(int i=0;i<pdus.length;i++){
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
String sender = smsMessage.getDisplayOriginatingAddress();
// b=sender.endsWith("WNRCRP"); //Just to fetch otp sent from WNRCRP
String messageBody = smsMessage.getMessageBody();
abcd=messageBody.replaceAll("[^0-9]",""); // here abcd contains otp
which is in number format
//Pass on the text to our listener.
if(b==true) {
mListener.messageReceived(abcd); // attach value to interface
object
}
else
{
}
}
}
public static void bindListener(SmsListener listener) {
mListener = listener;
}
}
Шаг 3) Добавьте Слушатель, т.е. широковещательный приемник в файле манифеста Android
<receiver android:name=".SmsReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
и добавить разрешение
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
Заключительный шаг 4) Активность, при которой вы собираетесь автоматически получать otp, когда он получен в папке "Входящие". В моем случае я выбираю otp и устанавливаю поле edittext.
public class OtpVerificationActivity extends AppCompatActivity {
EditText ed;
TextView tv;
String otp_generated,contactNo,id1;
GlobalData gd = new GlobalData();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_otp_verification);
ed=(EditText)findViewById(R.id.otp);
tv=(TextView) findViewById(R.id.verify_otp);
/*This is important because this will be called every time you receive
any sms */
SmsReceiver.bindListener(new SmsListener() {
@Override
public void messageReceived(String messageText) {
ed.setText(messageText);
}
});
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try
{
InputMethodManager imm=
(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
}
catch(Exception e)
{}
if (ed.getText().toString().equals(otp_generated))
{
Toast.makeText(OtpVerificationActivity.this, "OTP Verified
Successfully !", Toast.LENGTH_SHORT).show();
}
});
}
}
Файл макета для OtpVerificationActivity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_otp_verification"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.wnrcorp.reba.OtpVerificationActivity">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/firstcard"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
card_view:cardCornerRadius="10dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@android:color/white">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OTP Confirmation"
android:textSize="18sp"
android:textStyle="bold"
android:id="@+id/dialogTitle"
android:layout_margin="5dp"
android:layout_gravity="center"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/otp"
android:layout_margin="5dp"
android:hint="OTP Here"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Verify"
android:textSize="18sp"
android:id="@+id/verify_otp"
android:gravity="center"
android:padding="10dp"
android:layout_gravity="center"
android:visibility="visible"
android:layout_margin="5dp"
android:background="@color/colorPrimary"
android:textColor="#ffffff"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
Снимки экрана: действие по проверке OTP, при котором вы получаете OTP в виде сообщений, полученных сразу же ![enter image description here]()
Ответ 2
Вы можете попробовать использовать простую библиотеку , например,
После установки через gradle и добавления разрешений запустите SmsVerifyCatcher в методе, подобном действию onCreate:
smsVerifyCatcher = new SmsVerifyCatcher(this, new OnSmsCatchListener<String>() {
@Override
public void onSmsCatch(String message) {
String code = parseCode(message);//Parse verification code
etCode.setText(code);//set code in edit text
//then you can send verification code to server
}
});
Также переопределите методы жизненного цикла активности:
@Override
protected void onStart() {
super.onStart();
smsVerifyCatcher.onStart();
}
@Override
protected void onStop() {
super.onStop();
smsVerifyCatcher.onStop();
}
/**
* need for Android 6 real time permissions
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
smsVerifyCatcher.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
public String parseCode(String message) {
Pattern p = Pattern.compile("\\b\\d{4}\\b");
Matcher m = p.matcher(message);
String code = "";
while (m.find()) {
code = m.group(0);
}
return code;
}
Ответ 3
Это помогло мне, а также сработало для меня:
http://androiddhina.blogspot.in/2015/06/reading-incoming-message-automatically-to-verify-OTP.html
Также, пожалуйста , не забудьте сделать static
EditText
вашего EditText
из вашей Activity/Fragment
Ответ 4
Поскольку Google ограничил использование разрешения READ_SMS, здесь есть решение без разрешения READ_SMS.
API SMS Retriever
Основная функция состоит в том, чтобы избежать использования критического разрешения Android READ_SMS и выполнить задачу, используя этот метод. Удар - это шаги, которые вам нужны.
Отправлять OTP на номер пользователя, проверить, может ли API ретранслятора SMS получать сообщение или нет
SmsRetrieverClient client = SmsRetriever.getClient(SignupSetResetPasswordActivity.this);
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
// Android will provide message once receive. Start your broadcast receiver.
IntentFilter filter = new IntentFilter();
filter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
registerReceiver(new SmsReceiver(), filter);
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// Failed to start retriever, inspect Exception for more details
}
});
Код приемника вещания
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
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 SmsReceiver 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 otp;
String msgs = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
// Extract one-time code from the message and complete verification
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
break;
}
}
}
}
Последний шаг. Зарегистрируйте этот приемник в своем манифесте
<receiver android:name=".service.SmsReceiver" android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
</intent-filter>
</receiver>
Ваше SMS-сообщение должно соответствовать приведенному ниже.
<#> Your OTP code is: 6789
QWsa8754qw2
Здесь QWsa8754qw2 - это хэш-код вашего приложения, состоящий из 11 символов. Перейдите по этой ссылке
- Быть не длиннее 140 байт
- Начните с префикса & lt; #>
- Заканчивается 11-символьной хеш-строкой, идентифицирующей ваше приложение
Чтобы импортировать com.google.android.gms.auth.api.phone.SmsRetriever
, не забудьте добавить эту строку в ваше приложение build.gradle:
implementation "com.google.android.gms:play-services-auth-api-phone:16.0.0"
Ответ 5
Я реализовал что-то подобное. Но вот то, что я сделал, когда приходит сообщение, я извлекаю только шестизначный код, собираю его в намерении и отправляю его на активность или фрагмент, нуждающийся в этом, и проверяет код. В этом примере показан способ получения смс. Посмотрите на приведенный ниже код для иллюстрации того, как отправить с помощью LocalBrodcastManager, и если ваше сообщение содержит больше текстов. Например, приветствуйте, стандартизируйте его, чтобы помочь вам лучше. Например, "ваш код проверки: 84HG73" вы можете создать шаблон регулярного выражения, как этот ([0-9]){2}([A-Z]){2}([0-9]){2}
, что означает два ints, две [capital] буквы и два ints. Удачи!
После отбрасывания всей ненужной информации из сообщения
Intent intent = new Intent("AddedItem");
intent.putExtra("items", code);
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
И Fragment/Activity получая его
@Override
public void onResume() {
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(receiver, new IntentFilter("AddedItem"));
super.onResume();
}
@Override
public void onPause() {
super.onDestroy();
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(receiver);
}
И код, предназначенный для обработки собранной вами полезной нагрузки
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction()) {
final String message = intent.getStringExtra("message");
//Do whatever you want with the code here
}
}
};
Помогает ли это немного. Я сделал это лучше, используя Обратные вызовы
Ответ 6
Извините за поздний ответ, но по-прежнему чувствую, что отправляю свой ответ, если он помогает. Он работает для 6-ти цифр OTP.
@Override
public void onOTPReceived(String messageBody)
{
Pattern pattern = Pattern.compile(SMSReceiver.OTP_REGEX);
Matcher matcher = pattern.matcher(messageBody);
String otp = HkpConstants.EMPTY;
while (matcher.find())
{
otp = matcher.group();
}
checkAndSetOTP(otp);
}
Adding constants here
public static final String OTP_REGEX = "[0-9]{1,6}";
Для прослушивателя SMS можно выполнить следующий класс
public class SMSReceiver extends BroadcastReceiver
{
public static final String SMS_BUNDLE = "pdus";
public static final String OTP_REGEX = "[0-9]{1,6}";
private static final String FORMAT = "format";
private OnOTPSMSReceivedListener otpSMSListener;
public SMSReceiver(OnOTPSMSReceivedListener listener)
{
otpSMSListener = listener;
}
@Override
public void onReceive(Context context, Intent intent)
{
Bundle intentExtras = intent.getExtras();
if (intentExtras != null)
{
Object[] sms_bundle = (Object[]) intentExtras.get(SMS_BUNDLE);
String format = intent.getStringExtra(FORMAT);
if (sms_bundle != null)
{
otpSMSListener.onOTPSMSReceived(format, sms_bundle);
}
else {
// do nothing
}
}
}
@FunctionalInterface
public interface OnOTPSMSReceivedListener
{
void onOTPSMSReceived(@Nullable String format, Object... smsBundle);
}
}
@Override
public void onOTPSMSReceived(@Nullable String format, Object... smsBundle)
{
for (Object aSmsBundle : smsBundle)
{
SmsMessage smsMessage = getIncomingMessage(format, aSmsBundle);
String sender = smsMessage.getDisplayOriginatingAddress();
if (sender.toLowerCase().contains(ONEMG))
{
getIncomingMessage(smsMessage.getMessageBody());
} else
{
// do nothing
}
}
}
private SmsMessage getIncomingMessage(@Nullable String format, Object aObject)
{
SmsMessage currentSMS;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && format != null)
{
currentSMS = SmsMessage.createFromPdu((byte[]) aObject, format);
} else
{
currentSMS = SmsMessage.createFromPdu((byte[]) aObject);
}
return currentSMS;
}
Ответ 7
С помощью API Retriever SMS можно читать OTP без объявления android.permission.READ_SMS
.
- Запустите SMS-ретривер
private fun startSMSRetriever() {
// Get an instance of SmsRetrieverClient, used to start listening for a matching SMS message.
val 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.
val task: Task<Void> = 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 {
Log.d("SmsRetriever", "SmsRetriever Start Success")
}
task.addOnFailureListener {
Log.d("SmsRetriever", "SmsRetriever Start Failed")
}
}
- Получать сообщения через Broadcast
public class MySMSBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action && intent.extras!=null) {
val extras = intent.extras
val status = extras.get(SmsRetriever.EXTRA_STATUS) as Status
when (status.statusCode) {
CommonStatusCodes.SUCCESS -> {
// Get SMS message contents
val message = extras.get(SmsRetriever.EXTRA_SMS_MESSAGE) as String
Log.e("Message", message);
// Extract one-time code from the message and complete verification
// by sending the code back to your server.
}
CommonStatusCodes.TIMEOUT -> {
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
}
}
}
}
}
/**Don't forgot to define BroadcastReceiver in AndroidManifest.xml.*/
<receiver android:name=".MySMSBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
</intent-filter>
</receiver>
- Отправьте одноразовый код из сообщения о подтверждении на ваш сервер
Убедитесь, что ваш формат SMS точно такой, как показано ниже:
<#> Your ExampleApp code is: 123ABC78
fBzOyyp9h6L
- Быть не длиннее 140 байт
- Начните с префикса <#>
-
Завершите 11-символьную хеш-строку, которая идентифицирует ваше приложение
Вы можете вычислить хеш приложения с помощью следующего кода:
import android.content.Context
import android.content.ContextWrapper
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Log
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
/**
* This is a helper class to generate your message hash to be included in your SMS message.
*
* Without the correct hash, your app won't recieve the message callback. This only needs to be
* generated once per app and stored. Then you can remove this helper class from your code.
*
* For More Detail: https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string
*
*/
public class AppSignatureHelper(private val context: Context) : ContextWrapper(context) {
companion object {
val TAG = AppSignatureHelper::class.java.simpleName;
private const val HASH_TYPE = "SHA-256";
const val NUM_HASHED_BYTES = 9;
const val NUM_BASE64_CHAR = 11;
}
/**
* Get all the app signatures for the current package
* @return
*/
public fun getAppSignatures(): ArrayList<String> {
val appCodes = ArrayList<String>();
try {
// Get all package signatures for the current package
val signatures = packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES
).signatures;
// For each signature create a compatible hash
for (signature in signatures) {
val hash = hash(packageName, signature.toCharsString());
if (hash != null) {
appCodes.add(String.format("%s", hash));
}
}
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Unable to find package to obtain hash.", e);
}
return appCodes;
}
private fun hash(packageName: String, signature: String): String? {
val appInfo = "$packageName $signature";
try {
val messageDigest = MessageDigest.getInstance(HASH_TYPE);
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8));
var hashSignature = messageDigest.digest();
// truncated into NUM_HASHED_BYTES
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
// encode into Base64
var base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP);
base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);
Log.e(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
return base64Hash;
} catch (e: NoSuchAlgorithmException) {
Log.e(TAG, "hash:NoSuchAlgorithm", e);
}
return null;
}
}
Требуется Gradle:
implementation "com.google.android.gms:play-services-auth-api-phone:16.0.0"
Рекомендации:
https://developers.google.com/identity/sms-retriever/overview
https://developers.google.com/identity/sms-retriever/request
https://developers.google.com/identity/sms-retriever/verify
Ответ 8
Google предоставляет новый API для чтения SMS и вызова на номер без разрешения пользователя.
Ссылка: Автоматическая проверка SMS с помощью API SMS Retriever
Ответ 9
**activity_main.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"
tools:context="com.example.mukundwn.broadcastreceiver.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
**MainActivity.java**
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private BroadcastReceiver broadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
broadcastReceiver =new MyBroadcastReceiver();
}
@Override
protected void onStart()
{
super.onStart();
IntentFilter intentFilter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(broadcastReceiver,intentFilter);
}
@Override
protected void onStop()
{
super.onStop();
unregisterReceiver(broadcastReceiver);
}
}
**MyBroadcastReceiver.java**
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/**
* Created by mukundwn on 12/02/18.
*/
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"hello received an sms",Toast.LENGTH_SHORT).show();
}
}
**Manifest.xml**
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mukundwn.broadcastreceiver">
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"></uses-permission>
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<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=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVE"></action>
</intent-filter>
</receiver>
</application>
</manifest>