Android - Auf eingehende SMS-Nachrichten warten

155

Ich versuche, eine Anwendung zum Überwachen eingehender SMS-Nachrichten zu erstellen und ein Programm über eingehende SMS zu starten. Außerdem sollte es den Inhalt der SMS lesen.

Arbeitsablauf:

  • SMS an Android-Gerät gesendet
  • selbst ausführbare Anwendung
  • Lesen Sie die SMS-Informationen
iShader
quelle
1
Ich weiß, dass ich eine App zum Senden der SMS erstellen muss, aber hier muss ich eine SMS-App erstellen, die die Informationen aus der SMS
abruft
@iShader Ich hoffe, Sie haben die App erfolgreich erstellt. Ich wollte nur wissen, wie Sie es geschafft haben, die Nachrichten zwischen dem Gerät und dem Server zu synchronisieren
John x

Antworten:

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

Hinweis: Fügen Sie in Ihrer Manifestdatei den BroadcastReceiver hinzu.

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

Fügen Sie diese Berechtigung hinzu:

<uses-permission android:name="android.permission.RECEIVE_SMS" />
Vineet Shukla
quelle
2
Können Sie mir erklären, warum Sie einen Sekundärempfänger verwenden?
WindRider
2
@VineetShukla kannst du bitte erklären was pdus ist ??
TheGraduateGuy
11
Verwenden Sie Intents.SMS_RECEIVED_ACTION anstelle der fest codierten.
Ahmad Kayyali
6
Der obige Kommentar ist nicht korrekt. Jede App kann die SMS_RECEIVEDSendung weiterhin in Version 4.4+ erhalten. Da diese Sendung jetzt nicht abgebrochen werden kann, ist sie sicherer als in früheren Versionen.
Mike M.
3
@RuchirBaronia Mehrteilige Nachrichten. Eine einzelne SMS-Nachricht hat eine Zeichenbeschränkung (diese hängt vom verwendeten Zeichensatz ab, die allgemeinen Beschränkungen liegen jedoch bei 70, 140, 160 Zeichen). Wenn eine Nachricht diese Grenze überschreitet, kann sie in mehrere Nachrichten, Teile, aufgeteilt werden. Dieses Array ist das Array von Teilen, die Sie verketten müssen, um die vollständige Nachricht zu erhalten. Ihr Empfänger erhält immer nur eine vollständige Nachricht. Es kann nur aus mehreren Teilen bestehen.
Mike M.
65

Beachten Sie, dass auf einigen Geräten Ihr Code ohne Android nicht funktioniert : priority = "1000" im Intent-Filter:

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

Und hier einige Optimierungen:

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

Hinweis :
Der Wert muss eine Ganzzahl sein, z. B. "100". Höhere Zahlen haben eine höhere Priorität. Der Standardwert ist 0. Der Wert muss größer als -1000 und kleiner als 1000 sein.

Hier ist ein Link.

stefan.nsk
quelle
30
Diese Antwort mag eleganter sein, erfordert jedoch API 19. Nur eine Information für andere.
Baekacaek
10
Nach diesem , android:prioritykann nicht höher als 1000(oder weniger als -1000).
Craned
2
Es funktioniert nicht auf Xiaomi Redmi Note 3 Pro mit Android 5.1. Jeder bietet diese Lösung an, aber sie scheint für mich nicht zu funktionieren.
Sermilion
Wo wird das Markup <Empfänger ... in die Manifestdatei eingefügt?
John Ward
3
@Sermilion Sie müssen die Berechtigung zum Lesen von SMS im Anwendungsmanager des Mobiltelefons manuell zulassen.
Sanjay Kushwah
6

@ Mike M. und ich haben ein Problem mit der akzeptierten Antwort gefunden (siehe unsere Kommentare):

Grundsätzlich macht es keinen Sinn, die for-Schleife zu durchlaufen, wenn wir die mehrteilige Nachricht nicht jedes Mal verketten:

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

Beachten Sie, dass wir nur msgBodyden Zeichenfolgenwert des jeweiligen Teils der Nachricht festlegen , unabhängig davon, auf welchem ​​Index wir uns befinden. Dies macht den gesamten Punkt des Durchlaufens der verschiedenen Teile der SMS-Nachricht unbrauchbar, da er nur auf den Wert gesetzt wird letzter Indexwert. Stattdessen sollten wir verwenden +=, oder wie Mike bemerkte StringBuilder:

Alles in allem sieht mein SMS-Empfangscode folgendermaßen aus:

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
}

Stellen Sie diese Antwort einfach da raus, falls jemand anderes die gleiche Verwirrung hat.

Ruchir Baronia
quelle
4

Das habe ich benutzt!

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

        }
    }
}
Debasish Ghosh
quelle
2

Wenn Sie die Absicht einer geöffneten Aktivität behandeln möchten, können Sie PendintIntent verwenden (Schritte unten ausführen):

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

Manifest:

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

            }

Berechtigungen:

<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
AskQ
quelle
Die Google-Administratoren für den Google Play Store halten die Berechtigung RECEIVE_SMS (in dem von Ihnen erwähnten Lernprogramm) für gefährlich. Infolgedessen wird eine App, die die Berechtigung enthält, abgelehnt. Anschließend muss der Entwickler ein Formular an die Google Play-Administratoren zur Genehmigung senden. Andere Entwickler haben erwähnt, dass der Prozess schrecklich ist, da das Feedback Wochen dauert und direkte Ablehnungen ohne Erklärungen oder allgemeines Feedback erhalten. Irgendwelche Ideen, wie man das vermeidet?
AJW
2

Wenn jemand wie ich auf Xamarin Android verweist, wie dieselbe Funktion (Lesen von OTP mithilfe empfangener SMS) ausgeführt wird:

  1. Fügen Sie diesen Code Ihrer AndroidManifest.xml-Datei hinzu:

    <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. Erstellen Sie dann Ihre BroadcastReveiver-Klasse in Ihrem Android-Projekt.

    [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. Registrieren Sie diese BroadcastReceiver-Klasse in Ihrer MainActivity-Klasse in 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"));
    
            }
    }
Pabodha Wimalasuriya
quelle
Es wurde ein Compilerfehler angezeigt, der besagt, dass "android.permission.BROADCAST_SMS" nur System-Apps gewährt wird.
committedandroider
2

Vielen Dank an @Vineet Shukla (die akzeptierte Antwort) und @Ruchir Baronia (das Problem in der akzeptierten Antwort gefunden). Unten ist die KotlinVersion:

Berechtigung hinzufügen:

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

Registrieren Sie 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>

Implementierung für BroadcastReceiver hinzufügen:

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

Manchmal wird das Ereignis zweimal ausgelöst, also füge ich hinzu mLastTimeReceived = System.currentTimeMillis()

Lügner
quelle
1

Broadcast-Implementierung auf 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
    }
}

Hinweis: Fügen Sie in Ihrer Manifestdatei den BroadcastReceiver hinzu.

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

Fügen Sie diese Berechtigung hinzu:

<uses-permission android:name="android.permission.RECEIVE_SMS" />
Serg Burlaka
quelle
1

Die akzeptierte Antwort ist korrekt und funktioniert auf älteren Android-Versionen, bei denen Android OS bei der App-Installation nach Berechtigungen fragt. Bei neueren Android-Versionen funktioniert dies jedoch nicht sofort, da neuere Android-Betriebssysteme zur Laufzeit nach Berechtigungen fragen, wenn die App diese Funktion benötigt . Um SMS auf neueren Android-Versionen mit der im anerkannten Antwortprogrammierer genannten Technik zu empfangen, muss daher auch Code implementiert werden, der die Berechtigungen des Benutzers zur Laufzeit überprüft und anfordert. In diesem Fall können Berechtigungsprüfungsfunktionen / -code in onCreate () der ersten Aktivität der App implementiert werden. Kopieren Sie in Ihrer ersten Aktivität einfach die folgenden zwei Methoden und fügen Sie sie ein. Rufen Sie am Ende von onCreate () die Methode checkForSmsReceivePermissions () auf.

    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");
        }
    }
}
Adnan Ahmed
quelle