mmssms.db
中存储MMS条目的part表。我正在使用游标并想知道适当的URI
。我正在使用"content://mms-sms/conversations"和"Address"(发送至)、"Text"或"Subject"以及图像的"Data"列名。我已查看了
mmssms.db
的架构和Part表的列。关于此事的文档很难找到,所以我会在这里收集所有我发现的信息。如果您时间紧迫或者不喜欢阅读,请跳转至如何获取SMS数据部分。
这是MMS和SMS提供程序的URI......它允许我们同时查询MMS和SMS数据库,并将它们混合在一个单独的线程中(称为对话)。
为什么URI很重要呢?嗯,这是获取MMS和SMS消息的标准方式;例如,当您收到短信并单击通知栏时,它将发送像这样的广播意图:content://mms-sms/conversations/XXX
,其中XXX
是对话的ID。
您唯一需要做的就是查询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);
注意:通常情况下,当您调用query
并希望返回所有列时,可以将null
作为projection
参数传递。但是,在此提供程序中无法这样做,这就是我为什么使用*
的原因。
现在,您可以像往常一样循环遍历Cursor
。以下是您想要使用的更重要的列:
_id
是消息的ID。显而易见吧?其实不是。可以使用此ID使用content://sms
或content://mms
检索详细信息。date
不需要解释。thread_id
是对话的IDbody
此对话中最后一个短信的内容。如果它是MMS,则即使它有文本部分,这也将是null
。注意:如果您查询content://mms-sms/conversations
,它将返回不同会话的列表,其中每个会话的_id
是每个会话中的最后一个短信或MMS。如果您查询content://mms-sms/conversations/xxx
,它将返回ID为xxx
的会话中的每个SMS和/或MMS。
通常,您将希望知道正在处理的消息类型。文档说:
可以在查询的投影中请求虚拟列
MmsSms.TYPE_DISCRIMINATOR_COLUMN
。它的值是“mms”或“sms”,具体取决于表示行的消息是MMS消息还是SMS消息。
我认为它指的是此变量...但是我无法让它起作用。如果您已经成功,请告诉我如何操作或编辑此帖子。
到目前为止,这就是我所做的,它似乎有效,但肯定有更好的方法:
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());
}
如果您拥有短信的ID,则您需要做的是:
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"));
MMS有些不同。它们可以由不同的部分(文本、音频、图像等)构成;因此,这里将介绍如何分别检索每种数据。
假设我们在mmsId
变量中拥有MMS id。我们可以使用content://mms/
提供程序获得关于这个MMS的详细信息:
Uri uri = Uri.parse("content://mms/");
String selection = "_id = " + mmsId;
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
然而,唯一有趣的列是read
,如果消息已被阅读,则为1
。
在这里我们必须使用content://mms/part
... 例如:
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());
}
它可能包含不同部分的文本...但通常只有一个。因此,如果您想要删除循环,它将在大多数情况下运行。这就是getMmsText
方法的样子:
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();
}
与获取文本部分相同...唯一的区别是你需要查找不同的MIME类型:
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());
}
这是 getMmsImage
方法的样子:
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;
}
您需要使用content://mms/xxx/addr
提供程序,其中xxx
是MMS的ID:
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;
}
content://mms-sms/conversations
在一些手机上不能使用(例如我的 Galaxy S6 就不能)。我必须使用 content://mms/
来完成所有操作。 - KVISHMessageFormat.format("content://mms/{0}/addr", id);
只适用于小于1,000的ID。难道不应该是 MessageFormat.format("content://mms/{0,number,#}/addr", id);
吗? - Nickpublic 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;
}
我并不在意是否全是数字,但如果需要的话,我在注释中提供了一种方法来消除除数字以外的所有内容。它很容易被修改为返回所有收件人。
我假设这对他起作用。如果异常发生在第一行,它看起来会给出正确的答案。
type
常量,它们来自于PduHeaders
类:0x97
/ 151是PduHeaders.TO
,0x89
/ 137是PduHeaders.FROM
。其他有效的参考值包括:0x81
/ 129是PduHeaders.BCC
和0x82
/ 130是PduHeaders.CC
。也请参考Telephony.Mms.Addr。 - zelanix我刚刚遇到了这个问题,经过努力,最终解决了,并且我认为本帖可能会从我的经验中受益。
我可以在content://mms-sms/conversations/ (Telephony.Threads.CONTENT_URI)
上查询并像本帖子中所描述的那样有助于获取地址和部分内容,但我发现该URI无法检索只包含MMS消息的主题-例如,具有两个以上对应方的主题。
在浏览AOSP MMS应用程序源代码后,我发现它正在使用变量进行Telephony.Threads.CONTENT_URI
生成其对话列表-它正在添加参数“simple”,值为“true”。当我添加此参数时,我发现提供者将查询完全不同的表,该表确实具有所有SMS和MMS线程。
此表与常规的Telephony.Threads.CONTENT_URI不同(???);这是AOSP应用程序正在使用的投影--
public static final String[] ALL_THREADS_PROJECTION = {
Threads._ID, Threads.DATE, Threads.MESSAGE_COUNT, Threads.RECIPIENT_IDS,
Threads.SNIPPET, Threads.SNIPPET_CHARSET, Threads.READ, Threads.ERROR,
Threads.HAS_ATTACHMENT
};
我不得不进行一些修改才能使它对我起作用。
When I retrieve the cursor.getString(cursor.getColumnIndex("type")) from the mms-sms/conversations content, ("content://mms-sms/conversations/") I test the value of the "type" field for null. If the variable is null - i.e.
String otype = c.getString(c.getColumnIndex("type"));
if(otype != null) {
//this is an sms - handle it...
the message is an SMS, else it is an MMS. For MMS's you have to test for both mime types as follows:-
if (("application/vnd.wap.multipart.related".equalsIgnoreCase(msg_type)
||"application/vnd.wap.multipart.mixed".equalsIgnoreCase(msg_type))
&& !id.equalsIgnoreCase(lastMMSID)) {
//this is a MMS - handle it...
The only way that I could find that works pretty well to differentiate between inbound and outbound MMS's, is to test the null status of the "m_id" field of the mms-sms/conversations content.
String m_id = c.getString(c.getColumnIndex("m_id"));
String mDirection = m_id == null? "OUT": "IN";
关于如何获取地址字段的最后一点建议。由于某种原因,地址内容不喜欢使用{" * "}参数进行查询,但是这个可以起作用:
final String[] projection = new String[] {"address", "contact_id", "charset", "type"};
如果是出站消息,需要查找的“type”将为151。对于入站消息,“type”将为137。一个完全功能的代码应该像这样:
private String getANumber(int id) {
String add = "";
final String[] projection = new String[] {"address","contact_id","charset","type"};
final String selection = "type=137 or type=151"; // PduHeaders
Uri.Builder builder = Uri.parse("content://mms").buildUpon();
builder.appendPath(String.valueOf(id)).appendPath("addr");
Cursor cursor = context.getContentResolver().query(
builder.build(),
projection,
selection,
null, null);
if (cursor.moveToFirst()) {
do {
String add = cursor.getString(cursor.getColumnIndex("address"));
String type: cursor.getString(cursor.getColumnIndex("type"));
} while(cursor.moveToNext());
}
// Outbound messages address type=137 and the value will be 'insert-address-token'
// Outbound messages address type=151 and the value will be the address
// Additional checking can be done here to return the correct address.
return add;
}
对于在这个职位上走在我前面的所有勇士们,我由衷地感谢你们!
这段话是与IT技术无关的表达感激之情的句子。
mmssms.db
数据库是固件的一部分,Android 应用程序无法访问它。content://mms-sms/conversations
内容提供程序不是 SDK 的一部分,Android 应用程序不应访问它。 - CommonsWare