Android - Ascolta i messaggi SMS in arrivo


155

Sto cercando di creare un'applicazione per il monitoraggio dei messaggi SMS in arrivo e avviare un programma tramite SMS in arrivo, inoltre dovrebbe leggere il contenuto dell'SMS.

Flusso di lavoro:

  • SMS inviati al dispositivo Android
  • Applicazione auto-eseguibile
  • Leggi le informazioni SMS

1
So di creare un'app per inviare l'SMS, ma qui ho bisogno di creare un'app SMS che ottenga le informazioni dall'SMS e salvalo nel database SQLite ..... Come posso sviluppare tale app
iShader

@iShader spero che tu abbia avuto successo nella creazione dell'app, volevo solo sapere come sei riuscito a sincronizzare i messaggi con il dispositivo e il server
John x

Risposte:


265
public class SmsListener extends BroadcastReceiver{

    private SharedPreferences preferences;

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub

        if(intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){
            Bundle bundle = intent.getExtras();           //---get the SMS message passed in---
            SmsMessage[] msgs = null;
            String msg_from;
            if (bundle != null){
                //---retrieve the SMS message received---
                try{
                    Object[] pdus = (Object[]) bundle.get("pdus");
                    msgs = new SmsMessage[pdus.length];
                    for(int i=0; i<msgs.length; i++){
                        msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
                        msg_from = msgs[i].getOriginatingAddress();
                        String msgBody = msgs[i].getMessageBody();
                    }
                }catch(Exception e){
//                            Log.d("Exception caught",e.getMessage());
                }
            }
        }
    }
}

Nota: nel tuo file manifest aggiungi BroadcastReceiver-

<receiver android:name=".listener.SmsListener">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Aggiungi questa autorizzazione:

<uses-permission android:name="android.permission.RECEIVE_SMS" />

2
Puoi estinguermi perché usi un ricevitore secondario?
WindRider

2
@VineetShukla puoi per favore spiegare cos'è pdus ??
TheGraduateGuy

11
usa Intents.SMS_RECEIVED_ACTION invece di quello hardcoded.
Ahmad Kayyali,

6
Il commento sopra non è corretto. Qualsiasi app può ancora ottenere la SMS_RECEIVEDtrasmissione in 4.4+ e, ora che quella trasmissione non può essere interrotta, è più certo che nelle versioni precedenti.
Mike M.

3
@RuchirBaronia Messaggi multipart. Un singolo messaggio SMS ha un limite di caratteri (varia in base al set di caratteri che stai utilizzando, ma i limiti comuni sono 70, 140, 160 caratteri). Se un messaggio supera tale limite, può essere suddiviso in più messaggi, parti. Tale array è l'array di parti che è necessario concatenare per ottenere il messaggio completo. Il tuo Ricevitore riceverà sempre un solo messaggio completo alla volta; potrebbe essere solo in più parti.
Mike M.

65

Nota che su alcuni dispositivi il tuo codice non funzionerà senza Android: Priorità = "1000" nel filtro intenti:

<receiver android:name=".listener.SmsListener">
    <intent-filter android:priority="1000">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Ed ecco alcune ottimizzazioni:

public class SmsListener extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
            for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
                String messageBody = smsMessage.getMessageBody();
            }
        }
    }
}

Nota :
il valore deve essere un numero intero, ad esempio "100". I numeri più alti hanno una priorità più alta. Il valore predefinito è 0. Il valore deve essere maggiore di -1000 e inferiore a 1000.

Ecco un link.


30
Questa risposta può essere più elegante, ma richiede API 19. Solo una FYI per gli altri.
baekacaek,

10
In base a ciò , android:prioritynon può essere superiore a 1000(o inferiore a -1000).
Cranato il

2
Non funziona su Xiaomi Redmi Note 3 Pro con Android 5.1. Tutti stanno fornendo questa soluzione, ma non sembra funzionare per me.
Sermilion,

Dove è inserito il markup <receiver ... nel file manifest?
John Ward,

3
@Sermilion Devi autorizzare manualmente l'autorizzazione a leggere SMS nell'applicazione manager del cellulare.
Sanjay Kushwah

6

@Mike M. e ho riscontrato un problema con la risposta accettata (vedere i nostri commenti):

Fondamentalmente, non ha senso passare attraverso il ciclo for se non stiamo concatenando il messaggio multipart ogni volta:

for (int i = 0; i < msgs.length; i++) {
    msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
    msg_from = msgs[i].getOriginatingAddress();
    String msgBody = msgs[i].getMessageBody();
}

Si noti che abbiamo appena impostato msgBodyil valore di stringa della rispettiva parte del messaggio, indipendentemente dall'indice in cui ci troviamo, il che rende inutile l'intero punto del ciclo attraverso le diverse parti del messaggio SMS, poiché sarà impostato solo sul ultimo valore dell'indice. Invece dovremmo usare +=, o come Mike ha notato, StringBuilder:

Tutto sommato, ecco come appare il mio codice di ricezione SMS:

if (myBundle != null) {
    Object[] pdus = (Object[]) myBundle.get("pdus"); // pdus is key for SMS in bundle

    //Object [] pdus now contains array of bytes
    messages = new SmsMessage[pdus.length];
    for (int i = 0; i < messages.length; i++) {
         messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); //Returns one message, in array because multipart message due to sms max char
         Message += messages[i].getMessageBody(); // Using +=, because need to add multipart from before also
    }

    contactNumber = messages[0].getOriginatingAddress(); //This could also be inside the loop, but there is no need
}

Basta mettere questa risposta là fuori nel caso qualcun altro abbia la stessa confusione.


4

Questo è quello che ho usato!

public class SMSListener extends BroadcastReceiver {

    // Get the object of SmsManager
    final SmsManager sms = SmsManager.getDefault();
String mobile,body;

    public void onReceive(Context context, Intent intent) {

        // Retrieves a map of extended data from the intent.
        final Bundle bundle = intent.getExtras();

        try {

            if (bundle != null) {

                final Object[] pdusObj = (Object[]) bundle.get("pdus");

                for (int i = 0; i < pdusObj.length; i++) {

                    SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
                    String phoneNumber = currentMessage.getDisplayOriginatingAddress();

                    String senderNum = phoneNumber;
                    String message = currentMessage.getDisplayMessageBody();
                     mobile=senderNum.replaceAll("\\s","");
                     body=message.replaceAll("\\s","+");


                    Log.i("SmsReceiver", "senderNum: "+ senderNum + "; message: " + body);


                    // Show Alert
                    int duration = Toast.LENGTH_LONG;
                    Toast toast = Toast.makeText(context,
                            "senderNum: "+ mobile+ ", message: " + message, duration);
                    toast.show();

                } // end for loop
            } // bundle is null

        } catch (Exception e) {
            Log.e("SmsReceiver", "Exception smsReceiver" +e);

        }
    }
}

2

Nel caso in cui si desideri gestire l'intento sull'attività aperta, è possibile utilizzare PendintIntent (passaggi completi di seguito):

public class SMSReciver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        final Bundle bundle = intent.getExtras();
        try {
            if (bundle != null) {
                final Object[] pdusObj = (Object[]) bundle.get("pdus");
                for (int i = 0; i < pdusObj.length; i++) {
                    SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
                    String phoneNumber = currentMessage.getDisplayOriginatingAddress();
                    String senderNum = phoneNumber;
                    String message = currentMessage.getDisplayMessageBody();
                    try {
                        if (senderNum.contains("MOB_NUMBER")) {
                            Toast.makeText(context,"",Toast.LENGTH_SHORT).show();

                            Intent intentCall = new Intent(context, MainActivity.class);
                            intentCall.putExtra("message", currentMessage.getMessageBody());

                            PendingIntent pendingIntent= PendingIntent.getActivity(context, 0, intentCall, PendingIntent.FLAG_UPDATE_CURRENT);
                            pendingIntent.send();
                        }
                    } catch (Exception e) {
                    }
                }
            }
        } catch (Exception e) {
        }
    }
} 

manifesto:

<activity android:name=".MainActivity"
            android:launchMode="singleTask"/>
<receiver android:name=".SMSReciver">
            <intent-filter android:priority="1000">
                <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
            </intent-filter>
        </receiver>

onNewIntent:

 @Override
         protected void onNewIntent(Intent intent) {
                super.onNewIntent(intent);
                Toast.makeText(this, "onNewIntent", Toast.LENGTH_SHORT).show();

                onSMSReceived(intent.getStringExtra("message"));

            }

permesso:

<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />

Gli amministratori di Google per il Google Play Store considerano pericolosa l'autorizzazione RECEIVE_SMS (nel tutorial che menzioni). Di conseguenza, un'app che contiene l'autorizzazione verrà rifiutata. Quindi lo sviluppatore deve inviare un modulo agli amministratori di Google Play per l'approvazione. Altri sviluppatori hanno menzionato il fatto che il processo è terribile con feedback che richiedono settimane e che ricevono rigetti senza spiegazioni o feedback generici. Qualche idea su come evitare?
AJW

2

Se qualcuno che si riferisce come fare la stessa funzione (leggere OTP utilizzando SMS ricevuti) su Xamarin Android come me:

  1. Aggiungi questo codice al tuo file AndroidManifest.xml:

    <receiver android:name=".listener.BroadcastReveiverOTP">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
    </receiver>
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.BROADCAST_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
  2. Quindi crea la tua classe BroadcastReveiver nel tuo progetto Android.

    [BroadcastReceiver(Enabled = true)] [IntentFilter(new[] { "android.provider.Telephony.SMS_RECEIVED" }, Priority = (int)IntentFilterPriority.HighPriority)] 
    public class BroadcastReveiverOTP : BroadcastReceiver {
            public static readonly string INTENT_ACTION = "android.provider.Telephony.SMS_RECEIVED";
    
            protected string message, address = string.Empty;
    
            public override void OnReceive(Context context, Intent intent)
            {
                if (intent.HasExtra("pdus"))
                {
                    var smsArray = (Java.Lang.Object[])intent.Extras.Get("pdus");
                    foreach (var item in smsArray)
                    {
                        var sms = SmsMessage.CreateFromPdu((byte[])item);
                        address = sms.OriginatingAddress;
                        if (address.Equals("NotifyDEMO"))
                        {
                            message = sms.MessageBody;
                            string[] pin = message.Split(' ');
                            if (!string.IsNullOrWhiteSpace(pin[0]))
                            { 
                                    // NOTE : Here I'm passing received OTP to Portable Project using MessagingCenter. So I can display the OTP in the relevant entry field.
                                    MessagingCenter.Send<object, string>(this,MessengerKeys.OnBroadcastReceived, pin[0]);
                            }
                            }
                    }
                }
            }
    }
  3. Registra questa classe BroadcastReceiver nella tua classe MainActivity su Android Project:

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity {
    
            // Initialize your class
            private BroadcastReveiverOTP _receiver = new BroadcastReveiverOTP ();
    
            protected override void OnCreate(Bundle bundle) { 
                    base.OnCreate(bundle);
    
                    global::Xamarin.Forms.Forms.Init(this, bundle);
                    LoadApplication(new App());
    
                    // Register your receiver :  RegisterReceiver(_receiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
    
            }
    }

Viene visualizzato un errore del compilatore che dice "android.permission.BROADCAST_SMS" è concesso solo alle app di sistema.
committedandroider

2

Grazie a @Vineet Shukla (la risposta accettata) e @Ruchir Baronia (trovato il problema nella risposta accettata), di seguito è la Kotlinversione:

Aggiungi autorizzazione:

<uses-permission android:name="android.permission.RECEIVE_SMS" />

Registrati BroadcastReceiver in AndroidManifest:

<receiver
    android:name=".receiver.SmsReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter android:priority="2332412">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Aggiungi implementazione per BroadcastReceiver:

class SmsReceiver : BroadcastReceiver() {
    private var mLastTimeReceived = System.currentTimeMillis()

    override fun onReceive(p0: Context?, intent: Intent?) {
        val currentTimeMillis = System.currentTimeMillis()
        if (currentTimeMillis - mLastTimeReceived > 200) {
            mLastTimeReceived = currentTimeMillis

            val pdus: Array<*>
            val msgs: Array<SmsMessage?>
            var msgFrom: String?
            var msgText: String?
            val strBuilder = StringBuilder()
            intent?.extras?.let {
                try {
                    pdus = it.get("pdus") as Array<*>
                    msgs = arrayOfNulls(pdus.size)
                    for (i in msgs.indices) {
                        msgs[i] = SmsMessage.createFromPdu(pdus[i] as ByteArray)
                        strBuilder.append(msgs[i]?.messageBody)
                    }

                    msgText = strBuilder.toString()
                    msgFrom = msgs[0]?.originatingAddress

                    if (!msgFrom.isNullOrBlank() && !msgText.isNullOrBlank()) {
                        //
                        // Do some thing here
                        //
                    }
                } catch (e: Exception) {
                }
            }
        }
    }
}

A volte l'evento viene generato due volte, quindi aggiungo mLastTimeReceived = System.currentTimeMillis()


1

implementazione della trasmissione su Kotlin:

 private class SmsListener : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d(TAG, "SMS Received!")

        val txt = getTextFromSms(intent?.extras)
        Log.d(TAG, "message=" + txt)
    }

    private fun getTextFromSms(extras: Bundle?): String {
        val pdus = extras?.get("pdus") as Array<*>
        val format = extras.getString("format")
        var txt = ""
        for (pdu in pdus) {
            val smsmsg = getSmsMsg(pdu as ByteArray?, format)
            val submsg = smsmsg?.displayMessageBody
            submsg?.let { txt = "$txt$it" }
        }
        return txt
    }

    private fun getSmsMsg(pdu: ByteArray?, format: String?): SmsMessage? {
        return when {
            SDK_INT >= Build.VERSION_CODES.M -> SmsMessage.createFromPdu(pdu, format)
            else -> SmsMessage.createFromPdu(pdu)
        }
    }

    companion object {
        private val TAG = SmsListener::class.java.simpleName
    }
}

Nota: nel tuo file manifest aggiungi BroadcastReceiver-

<receiver android:name=".listener.SmsListener">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Aggiungi questa autorizzazione:

<uses-permission android:name="android.permission.RECEIVE_SMS" />

1

La risposta accettata è corretta e funziona su versioni precedenti di Android in cui il sistema operativo Android richiede autorizzazioni durante l'installazione dell'app, tuttavia nelle versioni più recenti Android non funziona immediatamente poiché il sistema operativo Android più recente richiede autorizzazioni durante l'esecuzione quando l'app richiede tale funzionalità . Pertanto, al fine di ricevere SMS su versioni più recenti di Android utilizzando la tecnica menzionata nel programmatore di risposte accettate, deve anche implementare il codice che controllerà e chiederà le autorizzazioni all'utente durante il runtime. In questo caso, le autorizzazioni per il controllo della funzionalità / del codice possono essere implementate in onCreate () della prima attività dell'app. Basta copiare e incollare seguendo due metodi nella prima attività e chiamare il metodo checkForSmsReceivePermissions () alla fine di onCreate ().

    void checkForSmsReceivePermissions(){
    // Check if App already has permissions for receiving SMS
    if(ContextCompat.checkSelfPermission(getBaseContext(), "android.permission.RECEIVE_SMS") == PackageManager.PERMISSION_GRANTED) {
        // App has permissions to listen incoming SMS messages
        Log.d("adnan", "checkForSmsReceivePermissions: Allowed");
    } else {
        // App don't have permissions to listen incoming SMS messages
        Log.d("adnan", "checkForSmsReceivePermissions: Denied");

        // Request permissions from user 
        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.RECEIVE_SMS}, 43391);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode == 43391){
        if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            Log.d("adnan", "Sms Receive Permissions granted");
        } else {
            Log.d("adnan", "Sms Receive Permissions denied");
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.