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

Ответ 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.

  1. Запустите 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")
        }
    }
  1. Получать сообщения через 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>
  1. Отправьте одноразовый код из сообщения о подтверждении на ваш сервер

Убедитесь, что ваш формат SMS точно такой, как показано ниже:

<#> Your ExampleApp code is: 123ABC78
fBzOyyp9h6L
  1. Быть не длиннее 140 байт
  2. Начните с префикса <#>
  3. Завершите 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

Ответ 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>