如何在Android中读取彩信数据?
我想读取MMS数据,我已经看到mmssms.db
中存储mms条目的部分表; 我正在使用游标,我想知道适当的URI
; 我正在使用“content:// mms-sms /对话”和图像的“地址”(发送到),“文本”或“主题”和“数据”列名称的列名称。
我已经看到了mmssms.db
的模式和他们的部分表的列。
这是很难找到有关这方面的文件,所以我会在这里收集我找到的所有信息。 如果您正忙着或只是不喜欢阅读,请跳到如何从SMS部分获取数据 。
内容://彩信,短信/通话
这是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
是会话的ID -
body
此对话上最后一条短信的内容。 如果它是一个彩信,即使它有一个文本部分,这将是null
。
注意:如果您查询content://mms-sms/conversations
,它将返回不同会话的列表,其中_id
是每个会话中的最后一个SMS或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()); }
如何从短信中获取数据
所以你有SMS的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的详细信息content://mms/
provider:
Uri uri = Uri.parse("content://mms/"); String selection = "_id = " + mmsId; Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
但是,如果消息已被读取,唯一感兴趣的列被read
为1
。
如何从MMS获取文本内容
这里我们必须使用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
provider,其中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; }
最后的想法
- 无法理解为什么有了这些数千万美元的Google,不会付钱给学生或其他人来记录这个API。 你必须检查源代码,知道它是如何工作的,更糟糕的是,他们不公开那些在数据库的列中使用的常量,所以我们必须手动编写它们。
- 对于MMS内的其他类型的数据,您可以应用上面学到的相同的想法…这只是一个了解MIME类型的问题。
基督徒的回答非常好。 但是,获取发件人地址的方法并不适用于我。 Long.parseLong语句除了可能抛出一个异常和新的String(…)之外什么也不做。
在我的设备上,游标数是2或更多。 第一个典型的“类型”是137,其他的是“类型”是151.我无法找到这个文件的位置,但是可以推断出137是“from”,151是“to”。 因此,如果按照原样运行该方法,则不会收到异常,并且会返回最后一行,这是一个收件人,在许多情况下只是其中的一个。
此外AFAICT的选择是不必要的,因为所有的行都有相同的msg_id。 但是,它并没有受到伤害。
这是我得到发件人地址的作品:
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; }
我并不在乎它是否全是数字,但是如果需要的话,我还包括一个方法来消除除了数字之外的所有数字。 它可以很容易地修改,以返回所有的收件人。
我认为它为他工作。 看起来如果在第一行发生异常,它会给出正确的答案。
我不得不做一些修改,以便为我工作。
-
当我从mms-sms / conversations内容(“content:// mms-sms / conversations /”)中检索到cursor.getString(cursor.getColumnIndex(“type”))时 ,我测试“type”字段的值为null。 如果变量为空 – 即
String otype = c.getString(c.getColumnIndex("type")); if(otype != null) { //this is an sms - handle it...
短信是短信,否则是彩信。 对于MMS,你必须按如下方式测试两种MIME类型:
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...
- 当您使用ContentObserver来监视消息内容的更改时,它会针对同一消息触发多个通知。 我使用一个静态变量(在我的例子中是lastMMSID)来跟踪消息。
- 此代码很好地检索入站和出站邮件的内容。 为了获得MMS的内容(文本和/或附件),遍历“content:// mms / part /”uri返回的所有记录是非常重要的。
-
我能找到的方法很好地区分入站和出站MMS,就是测试mms-sms / conversations内容的“m_id”字段的空状态。
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"};
如果是出站消息,则查找的“类型”将是151.对于入站消息,“类型”将是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; }
对于在这个岗位上走在我前面的所有勇敢的战士 – 我从心底感谢你!
上面给出的获取getMMSAddress()的答案不应该包含循环while(cursor.moveToNext());. 它应该只提取光标中第一个元素的地址。 出于某种原因,我不知道这个游标有多个记录。 第一个包含发件人的地址。 超出第一个光标的其他元素包含接收者的地址。 因此代码是返回接收者地址而不是发送者地址。
这对于破解MMS的内容非常有帮助。
我刚才一直在努力 然而,我终于得到了它的工作,我认为这个线程可能会受益于我的经验。
我可以查询content://mms-sms/conversations/ (Telephony.Threads.CONTENT_URI)
并获得地址和部分在线程中的帮助描述,但是我发现这个URI不会检索只有 MMS消息的线程 – 例如,有两个以上记者的线程。
在AOSP MMS应用程序源代码中进行了一些挖掘之后,我发现它正在使用Telephony.Threads.CONTENT_URI
上的一个变体来生成它的对话列表 – 它正在添加值为“true”的参数“simple”。 当我添加这个参数的时候,我发现提供者会查询一个完全不同的表,它确实拥有所有的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 };
这里的_ID是线程的ID – 因此是Telephony.Sms.CONTENT_URI或Telephony.Mms.CONTENT_URI的ID。
当我发现这个奇怪的细节之后,事情开始变得更好了! 但请注意,“simple = true”变体中的DATE列不可靠,我不得不使用最近的Sms或Mms消息中的日期。
另一件我应该提到的是,为了得到一个特定的线程的消息正确的列表,我不得不查询Mms和Sms提供商,然后将结果合并成一个列表,然后按日期排序。
我在Android 5.x和7.x上验证了行为。
我希望这有助于更多。
- android.support.v4.widget.CircleImageView不起作用
- 可以使用ObjectAnimator进行animation的Android属性
- 如何添加-Xlint:取消选中我的基于Android Gradle的项目?
- Dagger和ButterKnife Android之间的区别
- Android:为了使用filter创build一个StateListDrawable,克隆一个drawable
- 如何在Android中启用/禁用日志级别?
- ADT 22.3(android 4.4) – 获取错误“场景创build后,#init()必须被称为”
- 如何将String 转换为ArrayList <String>
- 尝试从MediaStore读取时出现Android KitKat securityException