Android - Nghe tin nhắn SMS đến


155

Tôi đang cố gắng tạo một ứng dụng để theo dõi các tin nhắn SMS đến và khởi chạy một chương trình thông qua SMS đến, nó cũng nên đọc nội dung từ SMS.

Quy trình làm việc:

  • SMS được gửi đến thiết bị Android
  • ứng dụng tự thực thi
  • Đọc thông tin SMS

1
Tôi biết để tạo một ứng dụng để gửi SMS, nhưng ở đây tôi cần tạo một ứng dụng SMS lấy thông tin từ SMS và lưu nó vào Cơ sở dữ liệu SQLite ..... Làm cách nào tôi có thể phát triển Ứng dụng đó
iShader

@iShader Tôi hy vọng bạn đã thành công trong việc tạo ứng dụng, chỉ muốn biết làm thế nào bạn quản lý để đồng bộ hóa các thông điệp b / w thiết bị và máy chủ
John x

Câu trả lời:


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

Lưu ý: Trong tệp kê khai của bạn, hãy thêm BroadcastReceiver-

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

Thêm quyền này:

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

2
Bạn có thể cho tôi biết lý do tại sao bạn sử dụng một máy thu thứ cấp?
WindRider

2
@VineetShukla bạn có thể vui lòng giải thích pdus là gì không ??
TheGradiatedGuy

11
sử dụng Intents.SMS_RECEIVED_ACTION thay vì mã hóa cứng.
Ahmad Kayyali

6
Nhận xét trên không đúng. Bất kỳ ứng dụng nào vẫn có thể SMS_RECEIVEDphát sóng trong 4.4 trở lên và hiện tại, chương trình phát sóng đó không thể bị hủy bỏ, nó chắc chắn hơn so với các phiên bản trước.
Mike M.

3
@RuchirBaronia Tin nhắn đa mục. Một tin nhắn SMS có giới hạn ký tự (nó thay đổi tùy theo bộ ký tự bạn đang sử dụng, nhưng giới hạn phổ biến là 70, 140, 160 ký tự). Nếu một tin nhắn vượt quá giới hạn đó, nó có thể được chia thành nhiều tin nhắn, nhiều phần. Mảng đó là mảng các phần mà bạn cần ghép để có được thông điệp hoàn chỉnh. Người nhận của bạn sẽ chỉ nhận được một tin nhắn hoàn chỉnh tại một thời điểm; nó chỉ có thể ở nhiều phần
Mike M.

65

Lưu ý rằng trên một số thiết bị, mã của bạn sẽ không hoạt động mà không có android: ưu tiên = "1000" trong bộ lọc ý định:

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

Và đây là một số tối ưu hóa:

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

Lưu ý :
Giá trị phải là số nguyên, chẳng hạn như "100". Số cao hơn có mức độ ưu tiên cao hơn. Giá trị mặc định là 0. Giá trị phải lớn hơn -1000 và nhỏ hơn 1000.

Đây là một liên kết.


30
Câu trả lời này có thể thanh lịch hơn, nhưng yêu cầu API 19. Chỉ là một FYI cho người khác.
baekacaek

10
Dựa theo đó , android:prioritykhông thể cao hơn 1000(hoặc ít hơn -1000).
vươn

2
Nó không hoạt động trên Xiaomi Redmi Note 3 Pro với Android 5.1. Mọi người đều cung cấp giải pháp này, nhưng dường như nó không hiệu quả với tôi.
Bài giảng

Đâu là <nhận ... đánh dấu được chèn trong tệp kê khai?
John Ward

3
@Sermilion Bạn phải cho phép thủ công để đọc SMS trong trình quản lý ứng dụng của điện thoại di động.
Sanjay Kushwah

6

@Mike M. và tôi đã tìm thấy một vấn đề với câu trả lời được chấp nhận (xem ý kiến ​​của chúng tôi):

Về cơ bản, không có điểm nào đi qua vòng lặp for nếu chúng ta không nối thông điệp nhiều phần mỗi lần:

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

Lưu ý rằng chúng ta chỉ đặt msgBodygiá trị chuỗi của phần tương ứng của tin nhắn cho dù chúng ta đang sử dụng chỉ mục nào, điều này làm cho toàn bộ điểm lặp qua các phần khác nhau của tin nhắn SMS trở nên vô dụng, vì nó sẽ chỉ được đặt thành giá trị chỉ số cuối cùng. Thay vào đó, chúng ta nên sử dụng +=, hoặc như Mike lưu ý , StringBuilder:

Nói chung, đây là mã nhận SMS của tôi trông như thế nào:

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
}

Chỉ cần đưa câu trả lời này ra trong trường hợp bất cứ ai khác có cùng một sự nhầm lẫn.


4

Đây là những gì tôi đã sử dụng!

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

Trong trường hợp bạn muốn xử lý ý định đối với hoạt động đã mở, bạn có thể sử dụng PendintIntent (Hoàn thành các bước bên dưới):

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

rõ ràng:

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

trên NewIntent:

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

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

            }

quyền:

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

Các quản trị viên Google cho Cửa hàng Google Play coi quyền RECEIVE_SMS (trong hướng dẫn bạn đề cập) là nguy hiểm. Do đó, một ứng dụng có quyền sẽ bị từ chối. Sau đó, nhà phát triển phải gửi biểu mẫu cho quản trị viên Google Play để phê duyệt. Các nhà phát triển khác đã đề cập đến quá trình này là khủng khiếp với phản hồi mất nhiều tuần và nhận được sự từ chối hoàn toàn mà không có giải thích hoặc phản hồi chung chung. Bất cứ ý tưởng về làm thế nào để tránh?
AJW

2

Nếu ai đó giới thiệu cách thực hiện tính năng tương tự (đọc OTP bằng SMS đã nhận) trên Xamarin Android như tôi:

  1. Thêm mã này vào tệp AndroidManifest.xml của bạn:

    <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. Sau đó, tạo lớp BroadcastReveiver trong Dự án Android của bạn.

    [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. Đăng ký lớp BroadcastReceiver này trong lớp MainActivity của bạn trên Dự án 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"));
    
            }
    }

Có một lỗi trình biên dịch nói rằng "ERIC.BROADCAST_SMS" chỉ được cấp cho các ứng dụng hệ thống.
committedandroider

2

Cảm ơn @Vineet Shukla (câu trả lời được chấp nhận) và @Ruchir Baronia (tìm thấy vấn đề trong câu trả lời được chấp nhận), bên dưới là Kotlinphiên bản:

Thêm quyền:

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

Đăng ký BroadcastReceiver trong 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>

Thêm triển khai cho 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) {
                }
            }
        }
    }
}

Đôi khi sự kiện cháy hai lần vì vậy tôi thêm mLastTimeReceived = System.currentTimeMillis()


1

thực hiện phát sóng trên 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
    }
}

Lưu ý: Trong tệp kê khai của bạn, hãy thêm BroadcastReceiver-

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

Thêm quyền này:

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

1

Câu trả lời được chấp nhận là chính xác và hoạt động trên các phiên bản Android cũ hơn, nơi HĐH Android yêu cầu quyền khi cài đặt ứng dụng, tuy nhiên trên các phiên bản mới hơn, Android không hoạt động ngay lập tức vì HĐH Android mới hơn yêu cầu quyền trong thời gian chạy khi ứng dụng yêu cầu tính năng đó . Do đó, để nhận SMS trên các phiên bản Android mới hơn bằng kỹ thuật được đề cập trong lập trình câu trả lời được chấp nhận cũng phải triển khai mã sẽ kiểm tra và yêu cầu quyền từ người dùng trong thời gian chạy. Trong trường hợp này, quyền kiểm tra chức năng / mã có thể được triển khai trong onCreate () của hoạt động đầu tiên của ứng dụng. Chỉ cần sao chép và dán theo hai phương thức trong hoạt động đầu tiên của bạn và gọi phương thức checkForSmsReceivePermissions () ở cuối 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");
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.