如何在Android中读取彩信数据?

use*_*373 71 android mms

我想读取MMS数据,我已经看到了mmssms.db存储mms条目的部分表; 我正在使用光标,我想知道合适的URI; 我正在使用"content:// mms-sms/conversations"和"地址"(发送到)的列名,"文本"或"主题"和"数据"列图像名称.

我已经看到了mmssms.db部分表的模式和他们的列.

Cri*_*ian 268

很难找到关于此的文档,所以我将在这里收集我找到的所有信息.如果您匆忙或只是不喜欢阅读,请跳转到如何从SMS部分获取数据.

内容://彩信,短信/通话

这是Mms和SMS提供程序的URI ...它允许我们同时查询MMS和SMS数据库,并将它们混合在一个线程中(称为会话).

为什么URI很重要?嗯,这是获取彩信和短信的标准方式; 例如,当您收到短信并单击通知栏时,它将发送如下广播意图:content://mms-sms/conversations/XXX,其中XXX是对话的ID.

获取所有会话的列表

你唯一要做的就是查询content://mms-sms/conversationsUri:

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);
Run Code Online (Sandbox Code Playgroud)

注意:通常,当您调用query并希望返回所有列时,您可以将其null作为projection参数传递.但是,您无法使用此提供程序执行此操作,因此这就是我使用的原因*.

现在你可以Cursor像往常一样循环.这些是您想要使用的更重要的列:

  • _id是邮件的ID.船长明显要救援?并不是的.此ID可用于使用content://sms或中检索详细信息content://mms.
  • date 无需解释.
  • thread_id 是对话的ID
  • body此对话中最后一条短信的内容.如果它是MMS,即使它有文本部分,也是如此null.

注意:如果您查询content://mms-sms/conversations,它将返回不同会话的列表,这些会话_id是每个会话中的最后一个SMS或MMS.如果您查询content://mms-sms/conversations/xxx它将返回ID为的对话的每个SMS和/或MMS xxx.

如何区分短信和彩信

通常,您需要知道要处理的消息类型.文件说:

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());
}
Run Code Online (Sandbox Code Playgroud)

如何从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"));
Run Code Online (Sandbox Code Playgroud)

如何从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);
Run Code Online (Sandbox Code Playgroud)

但是,唯一有趣的列是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());
}
Run Code Online (Sandbox Code Playgroud)

它可能包含文本的不同部分......但通常它只是一个.因此,如果您想要删除循环,它将在大多数情况下工作.这是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();
}
Run Code Online (Sandbox Code Playgroud)

如何从MMS获取图像

它与获取文本部分相同...唯一的区别是您将寻找不同的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());
}
Run Code Online (Sandbox Code Playgroud)

这是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;
}
Run Code Online (Sandbox Code Playgroud)

如何获取发件人地址

您将需要使用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;
}
Run Code Online (Sandbox Code Playgroud)

最后的想法

  • 无法理解为什么谷歌,用这些数千万美元,不支付学生或其他人来记录这个API.您必须检查源代码以了解它是如何工作的,更糟糕​​的是,它们不会公开数据库列中使用的那些常量,因此我们必须手动编写它们.
  • 对于MMS中的其他类型的数据,您可以应用上面学到的相同想法...这只是了解mime类型的问题.

  • 保留内容:// mms-sms/conversation.此URL包含所有线程的列表.但不是单独的消息(短信或短信).所以没有任何意义去了解短信或短信,而不是它们. (2认同)
  • Justin,因为MMS使用SMIL作为幻灯片显示存储在数据库中. (2认同)
  • `content:// mms-sms/conversations`不能在所有手机上运行(我有Galaxy S6但它没有).我不得不使用`content:// mms /`来表示所有这些内容. (2认同)

Ken*_*ans 8

基督徒的答案非常好.但是,获取发件人地址的方法对我不起作用.除了可能抛出异常和新的String(...)之外,Long.parseLong语句不会执行任何操作.

在我的设备上,光标数为2或更多.第一个通常具有137的"类型",而其他的"类型"为151.我无法找到记录的位置,但可以推断137是"来自"而151是"来".因此,如果我按原样运行该方法,我不会得到异常,并返回最后一行,这是一个收件人,在许多情况下只有几个中的一个.

此外,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;
}
Run Code Online (Sandbox Code Playgroud)

我不关心它是否都是数字,但我提供了一种方法来消除除了数字之外的所有内容,如果需要的话.它可以很容易地修改为返回所有收件人.

我认为这对他有用.如果在第一行发生异常,它看起来会给出正确的答案.

  • 为了澄清`type`常量,它们来自[`PduHeaders`](http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.0_r1/com /google/android/mms/pdu/PduHeaders.java)类:`0x97`/151是`PduHeaders.TO`和`0x89`/137是`PduHeaders.FROM`.其他有效的参考值是:`0x81`/129是`PduHeaders.BCC`和`0x82`/130是'PduHeaders.CC`.另请参阅[Telephony.Mms.Addr](https://developer.android.com/reference/android/provider/Telephony.Mms.Addr.html). (4认同)

Jas*_*tor 5

我一直在为此苦苦挣扎;但是,我最终使它起作用,我认为该线程可能会从我的经验中受益。

我可以查询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
};
Run Code Online (Sandbox Code Playgroud)

_ID是线程的ID,因此是Telephony.Sms.CONTENT_URI或Telephony.Mms.CONTENT_URI的ID。

当我发现这个奇怪的细节之后,事情开始变得更好了!但是请注意,“ simple = true”变体中的DATE列不可靠,我不得不改用最新的Sms或Mms消息中的日期。

我可能应该提到的另一件事是,为了获取特定线程的正确消息列表,我必须同时查询Mms和Sms提供程序,然后将结果合并为一个列表,然后按日期对它们进行排序。

我验证了Android 5.x和7.x的行为。

我希望这会有所帮助。