Listen outgoing SMS or sent box in Android
Asked Answered
O

4

33

I am developing an application which will store all the incoming and outgoing sms in a text file in SD card.

I am able to listen incoming messages using broadcast receiver. I am finding it very difficult to listen to outgoing SMS.

I know to some extent that a content observer on the sent box or outbox needs to be set, but I don't know how to do it.

How can this be done?

Ouidaouija answered 27/4, 2011 at 18:9 Comment(0)
C
47

Basically, you have to register a content observer... something like this:

ContentResolver contentResolver = context.getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://sms/out"),true, yourObserver);

yourObserver is an object (new YourObserver(new Handler())) that could look like this:

class YourObserver extends ContentObserver {

    public YourObserver(Handler handler) {
        super(handler);
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        // save the message to the SD card here
    }
}

So, how exactly do you get the content of the SMS? You must use a Cursor:

// save the message to the SD card here
Uri uriSMSURI = Uri.parse("content://sms/out");
Cursor cur = this.getContentResolver().query(uriSMSURI, null, null, null, null);
 // this will make it point to the first record, which is the last SMS sent
cur.moveToNext();
String content = cur.getString(cur.getColumnIndex("body"));
// use cur.getColumnNames() to get a list of all available columns...
// each field that compounds a SMS is represented by a column (phone number, status, etc.)
// then just save all data you want to the SDcard :)
Cowes answered 27/4, 2011 at 18:17 Comment(15)
should I use a cursor to get the message body?Ouidaouija
Yes... the cursor in the case is a pointer to each SMS record. Read a little bit about cursors and you will completely understand what's going on in the code above.Cowes
Thanks a lot for helping Cristian. You saved me a lot of time.Ouidaouija
Should I have an activity to achieve this. Because currently my application doesn't have an activity and I want it to be like that. Is there a way to achieve this without registering an activity?Ouidaouija
Yes, you can register your content observer inside a service.Cowes
@Cowes If I have the content observer in an activity, when the application is closed the observer just doesn't work right? If I want to know of every sent message do i have to register the content observer inside a service right?Bohman
@Cowes is this still working? Because I've just tried and it doesn't work for sent sms...Bohman
@Cowes I also tried this and the callback is invoked. I tested it on htc desire hd and it does not work though there seem to be no exceptions and the content observer is added ok - it just doesn't get notified at all.Proton
the string "content://sms/out" didn't work for me. I have used "content://sms/" without the out and checked the "type" column==2 for outgoingAnu
@Cowes can we stop out going sms using ContentObserver?Trevor
I'm not sure, but I think it's not that easy.Cowes
Where do I register the Observer? Id like it to be registered without starting the app or have the app running in foreground...Smashed
why it's called 3 times when I send an sms?Unsaid
Trying your answer on 6.0 but cant done yet any changes in code for marshmallow?Tao
@CrazyMind This code was slightly inaccurate. https://mcmap.net/q/442972/-listen-outgoing-sms-or-sent-box-in-android is verified on 6.0+Herra
S
5

This one is my approach for solving this

  1. Create a service that called from other activity
  2. Create a content observer inside it

     @Override
     public int onStartCommand(Intent intent, int flag, int startId) {
     MyObserver myObserver = new MyObserver(new Handler());
     ContentResolver contentResolver = this.getApplicationContext().getContentResolver();
     contentResolver.registerContentObserver(Uri.parse("content://sms/sent"), true, myObserver);
     return START_STICKY;
     }
    
  3. Create the observer class

    class MyObserver extends ContentObserver {
    
    public MyObserver(Handler handler) {
        super(handler);
    }
    
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        Uri uriSMSURI = Uri.parse("content://sms/sent");
        Cursor cur = getContentResolver().query(uriSMSURI, null, null, null, null);
        cur.moveToNext();
        String content = cur.getString(cur.getColumnIndex("body"));
        String smsNumber = cur.getString(cur.getColumnIndex("address"));
        if (smsNumber == null || smsNumber.length() <= 0) {
            smsNumber = "Unknown";
        }
        cur.close();
    
        if(smsChecker( "OutgoingSMS to " + smsNumber + ": " + content)) {
            //save data into database/sd card here
        }
    }
    }
    
  4. I added a method smsChecker() to check if the new message is just same as the last message

    public boolean smsChecker(String sms) {
    boolean flagSMS = true;
    
    if (sms.equals(lastSMS)) {
        flagSMS = false;
    }
    else {
        lastSMS = sms;
    }
    //if flagSMS = true, those 2 messages are different
    return flagSMS;
    }
    

if i am not mistaken, we use "content://sms/sent" if we ONLY want to check all sent messages, "content://sms/out" if we ONLY want to check all messages inside outbox, and "content://sms" if we want to check ALL messages.

Shop answered 19/3, 2015 at 17:58 Comment(1)
If two messages are legitimately identical, such as saying "yes" more than once, this would ignore the second message. See https://mcmap.net/q/442972/-listen-outgoing-sms-or-sent-box-in-android for a more effective way to verify duplicates.Herra
H
2

This is my version, which has been verified in Android 6.0+

class smsObserver extends ContentObserver {

    private String lastSmsId;

    public smsObserver(Handler handler) {
        super(handler);
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        Uri uriSMSURI = Uri.parse("content://sms/sent");
        Cursor cur = getContentResolver().query(uriSMSURI, null, null, null, null);
        cur.moveToNext();
        String id = cur.getString(cur.getColumnIndex("_id"));
        if (smsChecker(id)) {
            String address = cur.getString(cur.getColumnIndex("address"));
            // Optional: Check for a specific sender
            if (address.equals(phoneNumber)) {
                String message = cur.getString(cur.getColumnIndex("body"));
                // Use message content for desired functionality
            }
        }
    }

    // Prevent duplicate results without overlooking legitimate duplicates
    public boolean smsChecker(String smsId) {
        boolean flagSMS = true;

        if (smsId.equals(lastSmsId)) {
            flagSMS = false;
        }
        else {
            lastSmsId = smsId;
        }

        return flagSMS;
    }
}

Place this code where the observer should be enabled

ContentResolver contentResolver = getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://sms"), true, new smsObserver(new Handler()));

This assumes you are using an activity. Remember that you will need a Context reference to call getContentResolver() from within a service or receiver.

Herra answered 13/4, 2017 at 13:41 Comment(5)
@CrazyMind Updated the answer. Must have been lost while removing "proprietary" code from the example. Hope this is working for you.Herra
When you present code that should work, you might be considering to test it before sharing it. For example mContext is never defined. Do you send it using constructor or what ? Btw. this does not work on my Nexus 5, version 6+Johannesburg
@Johannesburg As stated above, it was shortened from an implementation. You are calling getContentResolver() and it was written assuming you may need a Context reference. You should take some time to understand an example before blindly copying it into your own implementation.Herra
@LoungeKatt tnx for editing it. It will help users understand they need to provide a context from service or an activity. Still not working on Nexus 5, but this may be something related to proper context from a service: ContentResolver contentResolver = getBaseContext().getContentResolver();Johannesburg
@Johannesburg Most users understand that mContext is a context reference. It sounds like you may not be familiar with context. I recommend seeing my answer at https://mcmap.net/q/435280/-how-to-get-the-current-context about how to obtain a context reference.Herra
S
0

I saw what goes wrong. its on the line:

 contentResolver.registerContentObserver(Uri.parse("content://sms/sent"), true, _myObserver);

you must remove '/sent' and just write 'content://sms' its is already specified in the ContentObserver to look into sent sms.

Stiver answered 15/4, 2016 at 7:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.