2

I'm working on a Cordova app that needs to be able to get a list of phone numbers involved in a group text. I'm querying content://mms/[id]/addr for that. I'm testing on a Pixel 2 and for the MMS messages prior to March 10, 2018, this works fine. But for messages on or after that date, it fails (comes back as null). Is there a different address I should be querying? Any other ideas?

Nick
  • 8,049
  • 18
  • 63
  • 107

2 Answers2

3

Using content://mms/ wil give you MMS conversation list and using content://mms-sms/conversations will give you both the first one or 2nd one you can try both if any of them doesn't work

so first you will have to get a list of MMS only using

ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"_id", "ct_t"};
Uri uri = Uri.parse("content://mms-sms/conversations");
Cursor query = contentResolver.query(uri, projection, null, null, null);
if (query.moveToFirst()) {
    do {
        String itemId = query.getString(query.getColumnIndex("_id"));
        int getRowID = Integer.parseInt(itemId);        
        String string = query.getString(query.getColumnIndex("ct_t"));
        if ("application/vnd.wap.multipart.related".equals(string)) {
            // this item is MMS so get the number using function getAddressNumber and log it 
            Log.d("number","address/number:" + getAddressNumber(getRowID));

        } else {
            // item is sms do nothing                 
        }
    } while (query.moveToNext());
}


private String getAddressNumber(int id) {
    String selectionAdd = new String("msg_id=" + id);
    String uriStr = MessageFormat.format("content://mms/{0}/addr", id);
    Uri uriAddress = Uri.parse(uriStr);
    Cursor cAdd = getContentResolver().query(uriAddress, null,
        selectionAdd, null, null);
    String name = null;
    if (cAdd.moveToFirst()) {
        do {
            String number = cAdd.getString(cAdd.getColumnIndex("address"));
            if (number != null) {
                try {
                    Long.parseLong(number.replace("-", ""));
                    name = number;
                } catch (NumberFormatException nfe) {
                    if (name == null) {
                        name = number;
                    }
                }
            }
        } while (cAdd.moveToNext());
    }
    if (cAdd != null) {
        cAdd.close();
    }
    return name;
}

if above function getAddressNumber doesn't work you can try this one as well with a little bit of changes

public static String getMMSAddress(Context context, String id) {
    String addrSelection = "type=137 AND msg_id=" + id;
    String uriStr = MessageFormat.format("content://mms/{0}/addr", id);
    Uri uriAddress = Uri.parse(uriStr);
    String[] columns = { "address" };
    Cursor cursor = context.getContentResolver().query(uriAddress, columns,
            addrSelection, null, null);
    String address = "";
    String val;
    if (cursor.moveToFirst()) {
        do {
            val = cursor.getString(cursor.getColumnIndex("address"));
            if (val != null) {
                address = val;
                break;
            }
        } while (cursor.moveToNext());
    }
    if (cursor != null) {
        cursor.close();
    }
    return address;
}

here is the defination for line

String addrSelection = "type=137 AND msg_id=" + id;

type constant come from the PduHeadersPduHeaders

class: 0x97 / 151 is PduHeaders.TO and 0x89 / 137 is PduHeaders.FROM you can change FROM or TO what you need.

if its still empty try below part and implement this in your code

Uri uriMms = Uri.parse("content://mms/");
final String[] projection = new String[]{"*"};

Cursor cursor = contentResolver.query(uriMms, projection, null, null, null);
String id = cursor.getString(cursor.getColumnIndex("_id"));

String selectionPart = "mid=" + id;
Uri uri = Uri.parse("content://mms/part");
Cursor cursor2 = getContentResolver().query(uri, null, selectionPart, null, null);
Ashfaque Ali Solangi
  • 1,883
  • 3
  • 22
  • 34
  • Will you please be more specific? What, exactly, should I do with `content://mms-sms/conversations/` in order to get a list of phone numbers involved in a group text? I'm new to Android development. – Nick Sep 11 '18 at 13:34
  • Thank you. The problem is your code is still relying on `content://mms/[id]/addr`. As stated in the question, on the Pixel 2 I'm testing with, that address doesn't always work. Sometimes it comes back as null. – Nick Sep 11 '18 at 14:59
  • are you getting any error in logcat? and have you tried this line ? "type=137 AND msg_id=" + id; – Ashfaque Ali Solangi Sep 11 '18 at 15:06
  • try this for both FROM and TO numbers "type=137 or type=151 AND msg_id=" + id; – Ashfaque Ali Solangi Sep 11 '18 at 15:08
  • if still null you can try Uri uri = Uri.parse("content://mms/part"); thing i have added in answer at end. – Ashfaque Ali Solangi Sep 11 '18 at 15:13
  • also make sure you are not using ending / in this line : "content://mms-sms/conversations/" it should be "content://mms-sms/conversations" – Ashfaque Ali Solangi Sep 11 '18 at 15:15
  • The error I get is `java.lang.NullPointerException: Attempt to invoke interface method 'boolean android.database.Cursor.moveToFirst()' on a null object reference`. This is when trying to query for just the message ID. Adding a second condition (saying that the `type` field must be `137` or `151`) wouldn't make a difference if the message isn't being found by its ID. – Nick Sep 11 '18 at 19:03
2

Here's how AOSP's (Android Open Source Project) messaging app does it:

  1. Every message (SMS/ MMS) has a message ID represented as _ID in their respective tables
  2. With this id pull the thread for the respective message
  3. The threads table has a column called recipient_ids, this might be Space separated for group message, something like this:

    123 456 789

Where 123 456 etc are the recipient ids.

  1. Get address for respective recipient ids. Now this is a bit tricky, but AOSP uses the following uri: content://mms-sms/canonical-address

So the final method to get an array of addresses looks something likes this:

private fun getAddressFromRecipientId(spaceSepIds: String, context: Context): Array<String?> {
    val singleCanonicalAddressUri = Uri.parse("content://mms-sms/canonical-address")
    with(spaceSepIds.split(" ")) {
    val addressArray: Array<String?> = arrayOfNulls(this.size)
        this.forEachIndexed { index, address ->
            if (!address.isEmpty()) {
                val longId = address.toLong()
                context.contentResolver.query(ContentUris.withAppendedId(singleCanonicalAddressUri, longId), null, null, null, null).use { cursor ->
                    if (cursor != null && cursor.moveToNext())
                        addressArray[index] = "${cursor.getString(0)} "
                }
            }
        }
        return addressArray
    }
    return arrayOf()
}

Hope this helps. Also the function is in kotlin but it's pretty easy to figure out what's happening there.

Also, you already have ids, so you can just call this method with either the space separated IDs or without them, the function works both ways.

MadScientist
  • 2,134
  • 14
  • 27
  • This tipped me off to what the problem was. I probably should have posted some code because maybe somebody would have caught it that way. Then again, it's code that I found on Stack Overflow (probably [this answer](https://stackoverflow.com/a/6446831/937084)), so maybe nobody would have caught it. I was using `MessageFormat.format("content://mms/{0}/addr", id);` and IDs over 999 were being formatted with commas. To fix this, I changed it to `MessageFormat.format("content://mms/{0,number,#}/addr", id);` Thank you! – Nick Sep 14 '18 at 02:58
  • Wow, that answer is beautiful. Thank you for posting the link. – MadScientist Sep 14 '18 at 05:43