Android - Dengarkan Pesan SMS yang Masuk

155

Saya mencoba membuat aplikasi untuk memonitor pesan SMS yang masuk, dan meluncurkan program melalui SMS yang masuk, juga harus membaca konten dari SMS.

Alur kerja:

  • SMS dikirim ke perangkat Android
  • Aplikasi yang dapat dieksekusi sendiri
  • Baca informasi SMS
iShader
sumber
1
Saya tahu untuk membuat aplikasi untuk mengirim SMS, tetapi di sini saya perlu membuat aplikasi SMS yang mendapatkan informasi dari SMS dan menyimpannya ke Database SQLite ..... Bagaimana saya bisa mengembangkan aplikasi seperti itu
iShader
@ iShader saya harap Anda berhasil membuat aplikasi, hanya ingin tahu bagaimana Anda bisa menyinkronkan pesan b / w perangkat dan server
John x

Jawaban:

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

Catatan: Dalam file manifes Anda tambahkan BroadcastReceiver-

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

Tambahkan izin ini:

<uses-permission android:name="android.permission.RECEIVE_SMS" />
Vineet Shukla
sumber
2
Bisakah Anda menjelaskan kepada saya mengapa Anda menggunakan penerima sekunder?
WindRider
2
@VineetShukla bisa tolong jelaskan apa itu PDD ??
TheGr GraduateGuy
11
gunakan Intents.SMS_RECEIVED_ACTION alih-alih yang hard-coded.
Ahmad Kayyali
6
Komentar di atas tidak benar. Aplikasi apa pun masih bisa mendapatkan SMS_RECEIVEDsiaran dalam 4.4+, dan, karena siaran itu tidak dapat dibatalkan, itu lebih pasti daripada di versi sebelumnya.
Mike M.
3
@RuchirBaronia Pesan multi bagian. Satu pesan SMS memiliki batas karakter (bervariasi tergantung pada set karakter yang Anda gunakan, tetapi batas umum adalah 70, 140, 160 karakter). Jika suatu pesan melebihi batas itu, pesan itu dapat dibagi menjadi beberapa pesan, bagian-bagian. Array itu adalah susunan bagian yang harus Anda gabungkan untuk mendapatkan pesan lengkap. Penerima Anda hanya akan mendapatkan satu pesan lengkap pada satu waktu; mungkin saja dalam beberapa bagian.
Mike M.
65

Perhatikan bahwa pada beberapa perangkat kode Anda tidak akan berfungsi tanpa android: priority = "1000" di filter maksud:

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

Dan berikut ini beberapa optimasi:

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

Catatan :
Nilai harus bilangan bulat, seperti "100". Angka yang lebih tinggi memiliki prioritas yang lebih tinggi. Nilai default adalah 0. Nilai harus lebih besar dari -1000 dan kurang dari 1000.

Ini tautannya.

stefan.nsk
sumber
30
Jawaban ini mungkin lebih elegan, tetapi membutuhkan API 19. Hanya FYI untuk orang lain.
baekacaek
10
Menurut ini , android:prioritytidak boleh lebih tinggi dari 1000(atau kurang dari -1000).
derek
2
Ini tidak berfungsi pada Xiaomi Redmi Note 3 Pro dengan Android 5.1. Setiap orang menyediakan solusi ini, tetapi sepertinya itu tidak berhasil untuk saya.
Sermilion
Di mana markup <receiver ... dimasukkan dalam file manifes?
John Ward
3
@Sermilion Anda harus secara manual mengizinkan izin untuk membaca SMS di manajer aplikasi ponsel.
Sanjay Kushwah
6

@Mike M. dan saya menemukan masalah dengan jawaban yang diterima (lihat komentar kami):

Pada dasarnya, tidak ada gunanya menelusuri for loop jika kita tidak menyatukan pesan multi bagian setiap kali:

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

Perhatikan bahwa kita hanya mengatur msgBodynilai string dari masing-masing bagian pesan, apa pun indeks kita, yang membuat seluruh titik perulangan melalui bagian-bagian berbeda dari pesan SMS tidak berguna, karena itu hanya akan diatur ke bagian paling nilai indeks terakhir. Sebaliknya, kita harus menggunakan +=, atau sebagai Mike mencatat, StringBuilder:

Secara keseluruhan, inilah yang tampak seperti kode penerima SMS saya:

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
}

Hanya menempatkan jawaban ini di luar sana kalau-kalau ada orang yang memiliki kebingungan yang sama.

Ruchir Baronia
sumber
4

Inilah yang saya gunakan!

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
sumber
2

Jika Anda ingin menangani niat pada aktivitas yang dibuka, Anda dapat menggunakan PendintIntent (Langkah-langkah lengkap di bawah):

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

nyata:

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

            }

izin:

<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
sumber
Administrator Google untuk Google Play Store menganggap izin RECEIVE_SMS (dalam tutorial yang Anda sebutkan) berbahaya. Akibatnya, aplikasi yang berisi izin akan ditolak. Kemudian pengembang harus mengirimkan formulir kepada administrator Google Play untuk persetujuan. Pengembang lain mengatakan prosesnya buruk dengan umpan balik yang memakan waktu berminggu-minggu dan menerima penolakan langsung tanpa penjelasan atau umpan balik umum. Ada ide tentang cara menghindari?
AJW
2

Jika seseorang merujuk cara melakukan fitur yang sama (membaca OTP menggunakan SMS yang diterima) di Xamarin Android seperti saya:

  1. Tambahkan kode ini ke file AndroidManifest.xml Anda:

    <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. Kemudian buat kelas BroadcastReveiver Anda di Proyek Android Anda.

    [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. Daftarkan kelas BroadcastReceiver ini di kelas MainActivity Anda di Proyek Android:

    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
sumber
Mendapat kesalahan kompiler yang mengatakan "android.permission.BROADCAST_SMS" hanya diberikan untuk aplikasi sistem.
berkomitmenandroider
2

Terima kasih kepada @Vineet Shukla (jawaban yang diterima) dan @Ruchir Baronia (menemukan masalah dalam jawaban yang diterima), di bawah ini adalah Kotlinversinya:

Tambahkan izin:

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

Daftarkan BroadcastReceiver di 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>

Tambahkan implementasi untuk 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) {
                }
            }
        }
    }
}

Kadang-kadang peristiwa kebakaran dua kali jadi saya tambahkan mLastTimeReceived = System.currentTimeMillis()

Pembohong
sumber
1

implementasi siaran di 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
    }
}

Catatan: Dalam file manifes Anda tambahkan BroadcastReceiver-

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

Tambahkan izin ini:

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

Jawaban yang diterima adalah benar dan bekerja pada Android versi lama di mana Android OS meminta izin pada pemasangan aplikasi, namun pada versi yang lebih baru Android tidak langsung bekerja karena Android OS yang lebih baru meminta izin selama runtime ketika aplikasi membutuhkan fitur itu . Oleh karena itu untuk menerima SMS pada versi Android yang lebih baru menggunakan teknik yang disebutkan dalam programmer jawaban yang diterima juga harus menerapkan kode yang akan memeriksa dan meminta izin dari pengguna selama runtime. Dalam hal ini izin memeriksa fungsionalitas / kode dapat diimplementasikan di onCreate () dari aktivitas pertama aplikasi. Cukup salin dan tempel berikut dua metode dalam aktivitas pertama Anda dan panggil metode checkForSmsReceivePermissions () di akhir 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");
        }
    }
}
Adnan Ahmed
sumber