查找并迭代 android 中的所有 SMS/MMS 条消息
Find and interate all SMS/MMS messages in android
首先,我发现这个 answer 特别有用。然而,这让我想知道如何找到这样的信息。
我似乎不知道如何遍历收件箱中的所有邮件。我当前的解决方案使用 Uri.parse("content://mms-sms/conversations")
,其中我使用“_id”和 "ct_t"。然而,尽管有 30 条消息(其中 20 条在保存对话线程中,其他的在另外两个对话中分配),但我似乎只在我的 phone 中找到了这三个对话。这样的声明 content://mms-sms/conversations
是有意义的。但是,其他提供商似乎只处理 SMS 或 MMS。有没有办法以这种方式迭代整个消息列表,我将 "content://mms-sms/conversations"
替换为其他内容?
public boolean refresh() {
final String[] proj = new String[]{"_id","ct_t"};
cursor = cr.query(Uri.parse("content://mms-sms/conversations"),proj,null,null,null);
if(!(cursor.moveToFirst())) {
empty = true;
cursor.close();
return false;
}
return true;
}
我用 next 函数迭代消息
public boolean next() {
if(empty) {
cursor.close();
return false;
}
msgCnt = msgCnt + 1;
Msg msg;
String msgData = cursor.getString(cursor.getColumnIndex("ct_t"));
if("application/cnd.wap.multipart.related".equals(msgData)) {
msg = ParseMMS(cursor.getString(cursor.getColumnIndex("_id")));
} else {
msg = ParseSMS(cursor.getString(cursor.getColumnIndex("_id")));
}
if(!(cursor.moveToNext())) {
empty = true;
cursor.close();
return false;
}
return true;
}
好吧,我问的似乎不太可能。
对于刚开始执行此类任务的用户,建议了解 content providers 的一般工作原理。添加到查询中的每个 Uri 值 returns 访问特定 tables.
花一些时间查看可以访问的不同 Telephony.Mmssms table,从我的测试看来,您唯一可以访问的 table 是使用 "content://mms-sms/conversations
因为使用 "content://mms-sms"
会导致空游标。
这就是生活,以这种方式重复消息并没有真正意义,因为提取数据的内容和方法根据消息是 SMS 还是 MMS 消息而有很大差异。分别迭代和解析 SMS 和 MMS 消息并将有趣的数据存储到相同的 class 对象类型中,以便日后操作他们想要的方式是有意义的。
对此类主题有用的是 Telephony.Sms documentation. Which is where one can find a descriptions of the column index fields. You can find the same information for Telephony.Mms as well as the sub table Telephony.Mms.Part,其中包含指向描述信息的每个基本列的链接。
话虽如此,这里是问题 How can I iterate all the SMS/MMS messages in the phone?
的解决方案,这是对我有用的解决方案。
public class Main extends AppCompatActivity {
//Not shown, Overrides, button to call IterateAll();
//implementations to follow
IterateAll();
public void ScanMMS();
public void ScanSMS();
public void ParseMMS(Msg msg);
public Bitmap getMmsImg(String id);
public String getMmsAddr(String id);
}
IterateAll() 只是调用了两个不同的函数
IterateAll() {
ScanMMS();
ScanSMS();
}
ScanMMS() 将遍历 content://mms
table 从每个彩信中提取数据。
public void ScanMMS() {
System.out.println("==============================ScanMMS()==============================");
//Initialize Box
Uri uri = Uri.parse("content://mms");
String[] proj = {"*"};
ContentResolver cr = getContentResolver();
Cursor c = cr.query(uri, proj, null, null, null);
if(c.moveToFirst()) {
do {
/*String[] col = c.getColumnNames();
String str = "";
for(int i = 0; i < col.length; i++) {
str = str + col[i] + ": " + c.getString(i) + ", ";
}
System.out.println(str);*/
//System.out.println("--------------------MMS------------------");
Msg msg = new Msg(c.getString(c.getColumnIndex("_id")));
msg.setThread(c.getString(c.getColumnIndex("thread_id")));
msg.setDate(c.getString(c.getColumnIndex("date")));
msg.setAddr(getMmsAddr(msg.getID()));
ParseMMS(msg);
//System.out.println(msg);
} while (c.moveToNext());
}
c.close();
}
}
可以看出,很多重要的彩信数据都在这个table中,例如消息的日期、消息ID和线程ID。您需要使用该消息 ID 从 MMS 中提取更多信息。
彩信被分成更小的数据部分。每个部分都包含不同的内容,例如图像或文本部分。你必须像我下面做的那样迭代每个部分。
public void ParseMMS(Msg msg) {
Uri uri = Uri.parse("content://mms/part");
String mmsId = "mid = " + msg.getID();
Cursor c = getContentResolver().query(uri, null, mmsId, null, null);
while(c.moveToNext()) {
/* String[] col = c.getColumnNames();
String str = "";
for(int i = 0; i < col.length; i++) {
str = str + col[i] + ": " + c.getString(i) + ", ";
}
System.out.println(str);*/
String pid = c.getString(c.getColumnIndex("_id"));
String type = c.getString(c.getColumnIndex("ct"));
if ("text/plain".equals(type)) {
msg.setBody(msg.getBody() + c.getString(c.getColumnIndex("text")));
} else if (type.contains("image")) {
msg.setImg(getMmsImg(pid));
}
}
c.close();
return;
}
每部分为mid字段,对应之前找到的消息id。我们仅在 MMS 零件库中搜索该 mms id,然后迭代找到的不同零件。 ct
或 content_type
如文档中所述描述了该部分是什么,即文本、图像等。我扫描类型以查看如何处理该部分。如果它是纯文本,我将该文本添加到当前邮件正文(显然可以有多个文本部分,但我没有看到,但我相信),如果它是图像,则将图像加载到位图中。我想位图可以很容易地用 java 发送到我的电脑,但谁知道呢,也许只想将它作为字节数组加载。
无论如何,这是从 MMS 部分获取图像数据的方法。
public Bitmap getMmsImg(String id) {
Uri uri = Uri.parse("content://mms/part/" + id);
InputStream in = null;
Bitmap bitmap = null;
try {
in = getContentResolver().openInputStream(uri);
bitmap = BitmapFactory.decodeStream(in);
if(in != null)
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
你知道,我不完全确定在内容解析器上打开输入流是如何工作的,以及它如何只给我图像而不是像所有其他数据一样,没有线索,但它似乎工作.我在寻找解决方案时从不同来源偷了这个。
MMS 地址不像 SMS 那样直接提取,但您可以通过以下方式获取所有地址。我唯一没能做的就是弄清楚发件人是谁。如果有人知道我会很高兴。
public String getMmsAddr(String id) {
String sel = new String("msg_id=" + id);
String uriString = MessageFormat.format("content://mms/{0}/addr", id);
Uri uri = Uri.parse(uriString);
Cursor c = getContentResolver().query(uri, null, sel, null, null);
String name = "";
while (c.moveToNext()) {
/* String[] col = c.getColumnNames();
String str = "";
for(int i = 0; i < col.length; i++) {
str = str + col[i] + ": " + c.getString(i) + ", ";
}
System.out.println(str);*/
String t = c.getString(c.getColumnIndex("address"));
if(!(t.contains("insert")))
name = name + t + " ";
}
c.close();
return name;
}
这一切都是为了彩信。好消息是短信要简单得多。
public void ScanSMS() {
System.out.println("==============================ScanSMS()==============================");
//Initialize Box
Uri uri = Uri.parse("content://sms");
String[] proj = {"*"};
ContentResolver cr = getContentResolver();
Cursor c = cr.query(uri,proj,null,null,null);
if(c.moveToFirst()) {
do {
String[] col = c.getColumnNames();
String str = "";
for(int i = 0; i < col.length; i++) {
str = str + col[i] + ": " + c.getString(i) + ", ";
}
//System.out.println(str);
System.out.println("--------------------SMS------------------");
Msg msg = new Msg(c.getString(c.getColumnIndex("_id")));
msg.setDate(c.getString(c.getColumnIndex("date")));
msg.setAddr(c.getString(c.getColumnIndex("Address")));
msg.setBody(c.getString(c.getColumnIndex("body")));
msg.setDirection(c.getString(c.getColumnIndex("type")));
msg.setContact(c.getString(c.getColumnIndex("person")));
System.out.println(msg);
} while (c.moveToNext());
}
c.close();
}
这是我的简单消息结构,因此任何人都可以根据需要快速编译上述代码。
import android.graphics.Bitmap;
/**
* Created by rbenedict on 3/16/2016.
*/
//import java.util.Date;
public class Msg {
private String id;
private String t_id;
private String date;
private String dispDate;
private String addr;
private String contact;
private String direction;
private String body;
private Bitmap img;
private boolean bData;
//Date vdat;
public Msg(String ID) {
id = ID;
body = "";
}
public void setDate(String d) {
date = d;
dispDate = msToDate(date);
}
public void setThread(String d) { t_id = d; }
public void setAddr(String a) {
addr = a;
}
public void setContact(String c) {
if (c==null) {
contact = "Unknown";
} else {
contact = c;
}
}
public void setDirection(String d) {
if ("1".equals(d))
direction = "FROM: ";
else
direction = "TO: ";
}
public void setBody(String b) {
body = b;
}
public void setImg(Bitmap bm) {
img = bm;
if (bm != null)
bData = true;
else
bData = false;
}
public String getDate() {
return date;
}
public String getDispDate() {
return dispDate;
}
public String getThread() { return t_id; }
public String getID() { return id; }
public String getBody() { return body; }
public Bitmap getImg() { return img; }
public boolean hasData() { return bData; }
public String toString() {
String s = id + ". " + dispDate + " - " + direction + " " + contact + " " + addr + ": " + body;
if (bData)
s = s + "\nData: " + img;
return s;
}
public String msToDate(String mss) {
long time = Long.parseLong(mss,10);
long sec = ( time / 1000 ) % 60;
time = time / 60000;
long min = time % 60;
time = time / 60;
long hour = time % 24 - 5;
time = time / 24;
long day = time % 365;
time = time / 365;
long yr = time + 1970;
day = day - ( time / 4 );
long mo = getMonth(day);
day = getDay(day);
mss = String.valueOf(yr) + "/" + String.valueOf(mo) + "/" + String.valueOf(day) + " " + String.valueOf(hour) + ":" + String.valueOf(min) + ":" + String.valueOf(sec);
return mss;
}
public long getMonth(long day) {
long[] calendar = {31,28,31,30,31,30,31,31,30,31,30,31};
for(int i = 0; i < 12; i++) {
if(day < calendar[i]) {
return i + 1;
} else {
day = day - calendar[i];
}
}
return 1;
}
public long getDay(long day) {
long[] calendar = {31,28,31,30,31,30,31,31,30,31,30,31};
for(int i = 0; i < 12; i++) {
if(day < calendar[i]) {
return day;
} else {
day = day - calendar[i];
}
}
return day;
}
}
关于此解决方案的一些最终评论和说明。
person字段好像一直是NULL,以后打算实现联系人查找。我也无法确定是谁发送了彩信。
我对java不是很熟悉,我还在学习中。我肯定有一个数据容器(ArrayList)(Vector?)可以容纳用户定义的对象。如果按对象(日期)中的特定字段排序table,则可以迭代该列表并获得所有消息的时间顺序:MMS/SMS 和sent/received.
Isn't there a way to just iterate the entire list of messages in this fashion where I replace "content://mms-sms/conversations"
with something else?
可以使用 content://mms-sms/complete-conversations
URL 在单个查询中获取所有 MMS 和 SMS 消息。出于某种奇怪的原因,Telephony.MmsSms
class 中没有用于此的 Uri
字段,但它至少从 Froyo 开始可用。
使用这个单一查询肯定比单独查询表更有效,而且任何需要完成的排序、分组或过滤肯定比通过操作 Java 更快地由 SQLite 引擎执行collections.
请注意,您必须为此查询使用特定的 projection
。您不能传递 null
或 *
通配符。此外,建议在 projection
中包含 MmsSms.TYPE_DISCRIMINATOR_COLUMN
("transport_type"
) - 其值为 "mms"
或 "sms"
- 以便于区分消息类型。
selection
、selectionArgs
和 orderBy
参数照常工作,null
可以传递给其中任何一个或所有参数。
首先,我发现这个 answer 特别有用。然而,这让我想知道如何找到这样的信息。
我似乎不知道如何遍历收件箱中的所有邮件。我当前的解决方案使用 Uri.parse("content://mms-sms/conversations")
,其中我使用“_id”和 "ct_t"。然而,尽管有 30 条消息(其中 20 条在保存对话线程中,其他的在另外两个对话中分配),但我似乎只在我的 phone 中找到了这三个对话。这样的声明 content://mms-sms/conversations
是有意义的。但是,其他提供商似乎只处理 SMS 或 MMS。有没有办法以这种方式迭代整个消息列表,我将 "content://mms-sms/conversations"
替换为其他内容?
public boolean refresh() {
final String[] proj = new String[]{"_id","ct_t"};
cursor = cr.query(Uri.parse("content://mms-sms/conversations"),proj,null,null,null);
if(!(cursor.moveToFirst())) {
empty = true;
cursor.close();
return false;
}
return true;
}
我用 next 函数迭代消息
public boolean next() {
if(empty) {
cursor.close();
return false;
}
msgCnt = msgCnt + 1;
Msg msg;
String msgData = cursor.getString(cursor.getColumnIndex("ct_t"));
if("application/cnd.wap.multipart.related".equals(msgData)) {
msg = ParseMMS(cursor.getString(cursor.getColumnIndex("_id")));
} else {
msg = ParseSMS(cursor.getString(cursor.getColumnIndex("_id")));
}
if(!(cursor.moveToNext())) {
empty = true;
cursor.close();
return false;
}
return true;
}
好吧,我问的似乎不太可能。
对于刚开始执行此类任务的用户,建议了解 content providers 的一般工作原理。添加到查询中的每个 Uri 值 returns 访问特定 tables.
花一些时间查看可以访问的不同 Telephony.Mmssms table,从我的测试看来,您唯一可以访问的 table 是使用 "content://mms-sms/conversations
因为使用 "content://mms-sms"
会导致空游标。
这就是生活,以这种方式重复消息并没有真正意义,因为提取数据的内容和方法根据消息是 SMS 还是 MMS 消息而有很大差异。分别迭代和解析 SMS 和 MMS 消息并将有趣的数据存储到相同的 class 对象类型中,以便日后操作他们想要的方式是有意义的。
对此类主题有用的是 Telephony.Sms documentation. Which is where one can find a descriptions of the column index fields. You can find the same information for Telephony.Mms as well as the sub table Telephony.Mms.Part,其中包含指向描述信息的每个基本列的链接。
话虽如此,这里是问题 How can I iterate all the SMS/MMS messages in the phone?
的解决方案,这是对我有用的解决方案。
public class Main extends AppCompatActivity {
//Not shown, Overrides, button to call IterateAll();
//implementations to follow
IterateAll();
public void ScanMMS();
public void ScanSMS();
public void ParseMMS(Msg msg);
public Bitmap getMmsImg(String id);
public String getMmsAddr(String id);
}
IterateAll() 只是调用了两个不同的函数
IterateAll() {
ScanMMS();
ScanSMS();
}
ScanMMS() 将遍历 content://mms
table 从每个彩信中提取数据。
public void ScanMMS() {
System.out.println("==============================ScanMMS()==============================");
//Initialize Box
Uri uri = Uri.parse("content://mms");
String[] proj = {"*"};
ContentResolver cr = getContentResolver();
Cursor c = cr.query(uri, proj, null, null, null);
if(c.moveToFirst()) {
do {
/*String[] col = c.getColumnNames();
String str = "";
for(int i = 0; i < col.length; i++) {
str = str + col[i] + ": " + c.getString(i) + ", ";
}
System.out.println(str);*/
//System.out.println("--------------------MMS------------------");
Msg msg = new Msg(c.getString(c.getColumnIndex("_id")));
msg.setThread(c.getString(c.getColumnIndex("thread_id")));
msg.setDate(c.getString(c.getColumnIndex("date")));
msg.setAddr(getMmsAddr(msg.getID()));
ParseMMS(msg);
//System.out.println(msg);
} while (c.moveToNext());
}
c.close();
}
}
可以看出,很多重要的彩信数据都在这个table中,例如消息的日期、消息ID和线程ID。您需要使用该消息 ID 从 MMS 中提取更多信息。
彩信被分成更小的数据部分。每个部分都包含不同的内容,例如图像或文本部分。你必须像我下面做的那样迭代每个部分。
public void ParseMMS(Msg msg) {
Uri uri = Uri.parse("content://mms/part");
String mmsId = "mid = " + msg.getID();
Cursor c = getContentResolver().query(uri, null, mmsId, null, null);
while(c.moveToNext()) {
/* String[] col = c.getColumnNames();
String str = "";
for(int i = 0; i < col.length; i++) {
str = str + col[i] + ": " + c.getString(i) + ", ";
}
System.out.println(str);*/
String pid = c.getString(c.getColumnIndex("_id"));
String type = c.getString(c.getColumnIndex("ct"));
if ("text/plain".equals(type)) {
msg.setBody(msg.getBody() + c.getString(c.getColumnIndex("text")));
} else if (type.contains("image")) {
msg.setImg(getMmsImg(pid));
}
}
c.close();
return;
}
每部分为mid字段,对应之前找到的消息id。我们仅在 MMS 零件库中搜索该 mms id,然后迭代找到的不同零件。 ct
或 content_type
如文档中所述描述了该部分是什么,即文本、图像等。我扫描类型以查看如何处理该部分。如果它是纯文本,我将该文本添加到当前邮件正文(显然可以有多个文本部分,但我没有看到,但我相信),如果它是图像,则将图像加载到位图中。我想位图可以很容易地用 java 发送到我的电脑,但谁知道呢,也许只想将它作为字节数组加载。
无论如何,这是从 MMS 部分获取图像数据的方法。
public Bitmap getMmsImg(String id) {
Uri uri = Uri.parse("content://mms/part/" + id);
InputStream in = null;
Bitmap bitmap = null;
try {
in = getContentResolver().openInputStream(uri);
bitmap = BitmapFactory.decodeStream(in);
if(in != null)
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
你知道,我不完全确定在内容解析器上打开输入流是如何工作的,以及它如何只给我图像而不是像所有其他数据一样,没有线索,但它似乎工作.我在寻找解决方案时从不同来源偷了这个。
MMS 地址不像 SMS 那样直接提取,但您可以通过以下方式获取所有地址。我唯一没能做的就是弄清楚发件人是谁。如果有人知道我会很高兴。
public String getMmsAddr(String id) {
String sel = new String("msg_id=" + id);
String uriString = MessageFormat.format("content://mms/{0}/addr", id);
Uri uri = Uri.parse(uriString);
Cursor c = getContentResolver().query(uri, null, sel, null, null);
String name = "";
while (c.moveToNext()) {
/* String[] col = c.getColumnNames();
String str = "";
for(int i = 0; i < col.length; i++) {
str = str + col[i] + ": " + c.getString(i) + ", ";
}
System.out.println(str);*/
String t = c.getString(c.getColumnIndex("address"));
if(!(t.contains("insert")))
name = name + t + " ";
}
c.close();
return name;
}
这一切都是为了彩信。好消息是短信要简单得多。
public void ScanSMS() {
System.out.println("==============================ScanSMS()==============================");
//Initialize Box
Uri uri = Uri.parse("content://sms");
String[] proj = {"*"};
ContentResolver cr = getContentResolver();
Cursor c = cr.query(uri,proj,null,null,null);
if(c.moveToFirst()) {
do {
String[] col = c.getColumnNames();
String str = "";
for(int i = 0; i < col.length; i++) {
str = str + col[i] + ": " + c.getString(i) + ", ";
}
//System.out.println(str);
System.out.println("--------------------SMS------------------");
Msg msg = new Msg(c.getString(c.getColumnIndex("_id")));
msg.setDate(c.getString(c.getColumnIndex("date")));
msg.setAddr(c.getString(c.getColumnIndex("Address")));
msg.setBody(c.getString(c.getColumnIndex("body")));
msg.setDirection(c.getString(c.getColumnIndex("type")));
msg.setContact(c.getString(c.getColumnIndex("person")));
System.out.println(msg);
} while (c.moveToNext());
}
c.close();
}
这是我的简单消息结构,因此任何人都可以根据需要快速编译上述代码。
import android.graphics.Bitmap;
/**
* Created by rbenedict on 3/16/2016.
*/
//import java.util.Date;
public class Msg {
private String id;
private String t_id;
private String date;
private String dispDate;
private String addr;
private String contact;
private String direction;
private String body;
private Bitmap img;
private boolean bData;
//Date vdat;
public Msg(String ID) {
id = ID;
body = "";
}
public void setDate(String d) {
date = d;
dispDate = msToDate(date);
}
public void setThread(String d) { t_id = d; }
public void setAddr(String a) {
addr = a;
}
public void setContact(String c) {
if (c==null) {
contact = "Unknown";
} else {
contact = c;
}
}
public void setDirection(String d) {
if ("1".equals(d))
direction = "FROM: ";
else
direction = "TO: ";
}
public void setBody(String b) {
body = b;
}
public void setImg(Bitmap bm) {
img = bm;
if (bm != null)
bData = true;
else
bData = false;
}
public String getDate() {
return date;
}
public String getDispDate() {
return dispDate;
}
public String getThread() { return t_id; }
public String getID() { return id; }
public String getBody() { return body; }
public Bitmap getImg() { return img; }
public boolean hasData() { return bData; }
public String toString() {
String s = id + ". " + dispDate + " - " + direction + " " + contact + " " + addr + ": " + body;
if (bData)
s = s + "\nData: " + img;
return s;
}
public String msToDate(String mss) {
long time = Long.parseLong(mss,10);
long sec = ( time / 1000 ) % 60;
time = time / 60000;
long min = time % 60;
time = time / 60;
long hour = time % 24 - 5;
time = time / 24;
long day = time % 365;
time = time / 365;
long yr = time + 1970;
day = day - ( time / 4 );
long mo = getMonth(day);
day = getDay(day);
mss = String.valueOf(yr) + "/" + String.valueOf(mo) + "/" + String.valueOf(day) + " " + String.valueOf(hour) + ":" + String.valueOf(min) + ":" + String.valueOf(sec);
return mss;
}
public long getMonth(long day) {
long[] calendar = {31,28,31,30,31,30,31,31,30,31,30,31};
for(int i = 0; i < 12; i++) {
if(day < calendar[i]) {
return i + 1;
} else {
day = day - calendar[i];
}
}
return 1;
}
public long getDay(long day) {
long[] calendar = {31,28,31,30,31,30,31,31,30,31,30,31};
for(int i = 0; i < 12; i++) {
if(day < calendar[i]) {
return day;
} else {
day = day - calendar[i];
}
}
return day;
}
}
关于此解决方案的一些最终评论和说明。
person字段好像一直是NULL,以后打算实现联系人查找。我也无法确定是谁发送了彩信。
我对java不是很熟悉,我还在学习中。我肯定有一个数据容器(ArrayList)(Vector?)可以容纳用户定义的对象。如果按对象(日期)中的特定字段排序table,则可以迭代该列表并获得所有消息的时间顺序:MMS/SMS 和sent/received.
Isn't there a way to just iterate the entire list of messages in this fashion where I replace
"content://mms-sms/conversations"
with something else?
可以使用 content://mms-sms/complete-conversations
URL 在单个查询中获取所有 MMS 和 SMS 消息。出于某种奇怪的原因,Telephony.MmsSms
class 中没有用于此的 Uri
字段,但它至少从 Froyo 开始可用。
使用这个单一查询肯定比单独查询表更有效,而且任何需要完成的排序、分组或过滤肯定比通过操作 Java 更快地由 SQLite 引擎执行collections.
请注意,您必须为此查询使用特定的 projection
。您不能传递 null
或 *
通配符。此外,建议在 projection
中包含 MmsSms.TYPE_DISCRIMINATOR_COLUMN
("transport_type"
) - 其值为 "mms"
或 "sms"
- 以便于区分消息类型。
selection
、selectionArgs
和 orderBy
参数照常工作,null
可以传递给其中任何一个或所有参数。