I want to read MMS data I have seen the part table in the mmssms.db
where the mms entries are stored; I am using a cursor and I want to know the appropriate URI
; I am using "content://mms-sms/conversations" and the Column names of "Address"(Sent to), "Text" or "Subject" and "Data" column name of image.
I have seen the schema of mmssms.db
and Their Column of part Table.
It's kind of difficult to find documentation about this, so I will collect here all information I have found. If you are in a rush or just don't like to read, jump to the How to get data from a SMS section.
This is the URI of the Mms and SMS provider... which allows us to query the MMS and SMS databases at the same time, and mix them in a single thread (which are called conversations).
Why is the URI important? Well, that's the standard way of getting MMS and SMS messages; for instance, when you receive a SMS and click on the notification bar, it will send a broadcast intent like this: content://mms-sms/conversations/XXX
, where XXX
is the id of the conversation.
The only thing you have to do is to query the content://mms-sms/conversations
Uri:
ContentResolver contentResolver = getContentResolver(); final String[] projection = new String[]{"*"}; Uri uri = Uri.parse("content://mms-sms/conversations/"); Cursor query = contentResolver.query(uri, projection, null, null, null);
Note: usually, when you call query
and want to return all columns you can pass null
as the projection
parameter. However, you cannot do that with this provider, so that's why I'm using *
.
Now you can loop through the Cursor
as usual. These are the more important columns you would want to use:
_id
is the ID of the message. Captain obvious to the rescue? Not really. This ID can be used to retrieve detailed information using either content://sms
or content://mms
.date
no explanation needed.thread_id
is the ID of the conversationbody
The content of the last SMS on this conversation. If it's an MMS, even if it has a text part, this will be null
.Note: if you query content://mms-sms/conversations
it will return a list of different conversations whose _id
is the last SMS or MMS in each conversation. If you query content://mms-sms/conversations/xxx
it will return each SMS and/or MMS on the conversation whose ID is xxx
.
Usually, you will want to know which type of message you are handling. Documentation says:
A virtual column,
MmsSms.TYPE_DISCRIMINATOR_COLUMN
, may be requested in the projection for a query. Its value is either "mms" or "sms", depending on whether the message represented by the row is an MMS message or an SMS message, respectively.
I think it's referring to this variable... however I have not been able to make it work. If you have please tell me how or edit this post.
So far this is what I have done and it seems to work but there must be better ways:
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 string = query.getString(query.getColumnIndex("ct_t")); if ("application/vnd.wap.multipart.related".equals(string)) { // it's MMS } else { // it's SMS } } while (query.moveToNext()); }
So you have the ID of the SMS, then the only thing you have to do is:
String selection = "_id = "+id; Uri uri = Uri.parse("content://sms"); Cursor cursor = contentResolver.query(uri, null, selection, null, null); String phone = cursor.getString(cursor.getColumnIndex("address")); int type = cursor.getInt(cursor.getColumnIndex("type"));// 2 = sent, etc. String date = cursor.getString(cursor.getColumnIndex("date")); String body = cursor.getString(cursor.getColumnIndex("body"));
MMSs are a little bit different. They can be built with different parts (text, audio, images, etc.); so here will see how to retrieve each kind of data separately.
So let's guess we have the MMS id in the mmsId
variable. We can get detailed information about this MMS by using the content://mms/
provider:
Uri uri = Uri.parse("content://mms/"); String selection = "_id = " + mmsId; Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
However, the only interesting column is read
which is 1
if the message has already been read.
Here we have to use content://mms/part
... for instance:
String selectionPart = "mid=" + mmsId; Uri uri = Uri.parse("content://mms/part"); Cursor cursor = getContentResolver().query(uri, null, selectionPart, null, null); if (cursor.moveToFirst()) { do { String partId = cursor.getString(cursor.getColumnIndex("_id")); String type = cursor.getString(cursor.getColumnIndex("ct")); if ("text/plain".equals(type)) { String data = cursor.getString(cursor.getColumnIndex("_data")); String body; if (data != null) { // implementation of this method below body = getMmsText(partId); } else { body = cursor.getString(cursor.getColumnIndex("text")); } } } while (cursor.moveToNext()); }
It could contain different parts of text... but usually it'd be only one. So if you want to remove the loop it will work most of the times. This is how the getMmsText
method looks like:
private String getMmsText(String id) { Uri partURI = Uri.parse("content://mms/part/" + id); InputStream is = null; StringBuilder sb = new StringBuilder(); try { is = getContentResolver().openInputStream(partURI); if (is != null) { InputStreamReader isr = new InputStreamReader(is, "UTF-8"); BufferedReader reader = new BufferedReader(isr); String temp = reader.readLine(); while (temp != null) { sb.append(temp); temp = reader.readLine(); } } } catch (IOException e) {} finally { if (is != null) { try { is.close(); } catch (IOException e) {} } } return sb.toString(); }
It's the same than getting the text part... the only difference is that you will be looking for a different mime-type:
String selectionPart = "mid=" + mmsId; Uri uri = Uri.parse("content://mms/part"); Cursor cPart = getContentResolver().query(uri, null, selectionPart, null, null); if (cPart.moveToFirst()) { do { String partId = cPart.getString(cPart.getColumnIndex("_id")); String type = cPart.getString(cPart.getColumnIndex("ct")); if ("image/jpeg".equals(type) || "image/bmp".equals(type) || "image/gif".equals(type) || "image/jpg".equals(type) || "image/png".equals(type)) { Bitmap bitmap = getMmsImage(partId); } } while (cPart.moveToNext()); }
This is how the getMmsImage
method looks like:
private Bitmap getMmsImage(String _id) { Uri partURI = Uri.parse("content://mms/part/" + _id); InputStream is = null; Bitmap bitmap = null; try { is = getContentResolver().openInputStream(partURI); bitmap = BitmapFactory.decodeStream(is); } catch (IOException e) {} finally { if (is != null) { try { is.close(); } catch (IOException e) {} } } return bitmap; }
You will need to use the content://mms/xxx/addr
provider, where xxx
is the id of the MMS:
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; }
The answer by Christian is excellent. However, the method for getting the sender's address did not work for me. The Long.parseLong statement doesn't do anything except possibly throw an exception and new String(...) ?.
On my device the cursor count is 2 or more. The first typically has a "type" of 137 and the others have a "type" of 151. I cannot find where this is documented, but one can deduce 137 is "from" and 151 is "to". Thus, if I run the method as is, I do not get an exception, and it returns the last row, which is a recipient and only one of several in many cases.
Also AFAICT the selection is not necessary as all the rows have the same msg_id. However, it doesn't hurt.
This is what works for me to get the sender's address:
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; // Use the first one found if more than one break; } } while (cursor.moveToNext()); } if (cursor != null) { cursor.close(); } // return address.replaceAll("[^0-9]", ""); return address; }
I didn't care about whether it is all numeric, but I included a way to eliminate everything but numerals as a comment if that is desired. It can easily be modified to return all the recipients, as well.
I assume it worked for him. It looks like it would give the right answer if the exception occurred on the first row.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With