【问题标题】:Find and interate all SMS/MMS messages in android在 android 中查找并迭代所有 SMS/MMS 消息
【发布时间】:2016-06-30 07:20:37
【问题描述】:

首先,我发现answer 特别有用。然而,这让我想知道如何找到这样的信息。

我似乎不知道如何迭代收件箱中的所有邮件。我当前的解决方案使用Uri.parse("content://mms-sms/conversations"),我在其中使用“_id”和“ct_t”。但是,尽管有 30 条消息(其中 20 条在保存会话线程中,其他在另外两个会话中分配),但我似乎只在手机中找到了三个对话。这样的声明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;
}

我用下一个函数迭代消息

    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;
    }

【问题讨论】:

    标签: android android-contentprovider android-mms


    【解决方案1】:

    好吧,我的要求似乎不太可能。
    对于那些刚开始从事此类任务的人,建议了解content providers 的一般工作方式。添加到查询中的每个 Uri 值都会返回对特定表的访问权限。

    花一些时间查看可以访问的不同Telephony.Mmssms 表,从我的测试看来,您可以访问的唯一表是使用"content://mms-sms/conversations,因为使用"content://mms-sms" 会导致空游标。

    这就是生活,以这种方式迭代消息并没有真正的意义,因为提取数据的内容和方法根据消息是 SMS 还是 MMS 消息而有很大差异。分别迭代和解析 SMS 和 MMS 消息并将感兴趣的数据存储到同一个类对象类型中以便以后操作他们想要的方式是有意义的。

    对此类主题有用的是Telephony.Sms documentation。这是可以找到列索引字段的descriptions 的地方。您可以找到 Telephony.Mms 以及子表 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 表,从每个 MMS 中提取数据。

    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();
    
        }
    
    }
    

    可以看到,很多重要的彩信数据都在这张表中,比如消息的日期、消息的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;
        }
    

    每个部分作为中间字段,对应于之前找到的消息的 id。我们仅在 MMS 部件库中搜索该 mms id,然后迭代找到的不同部件。 ctcontent_type 如文档中所述,描述了该部分是什么,即文本、图像等。我扫描类型以查看如何处理该部分。如果是纯文本,我将该文本添加到当前消息正文中(显然可以有多个文本部分,但我没有看到它,但我相信它)如果是图像,则将图像加载到位图中。我想位图很容易用 java 发送到我的计算机,但谁知道呢,也许只想将它作为字节数组加载。

    不管怎样,这是从彩信部分获取图像数据的方法。

    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;
    }
    

    您知道,我不完全确定在内容解析器上打开输入流的真正工作原理以及它如何只给我图像而不是像所有其他数据一样,不知道,但它似乎工作。我在寻找解决方案时从一些不同的来源偷了这个。

    彩信地址不像短信那样直接提取,但您可以通过以下方式获取所有地址。我唯一没能做的就是弄清楚发件人是谁。如果有人知道,我会很高兴的。

    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;
    }
    

    这一切都只是为了彩信。好消息是 SMS 要简单得多。

    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;
        }
    
    
    
    }
    

    关于这个解决方案的一些最终的 cmets 和注释。

    person 字段似乎始终为 NULL,稍后我计划实现联系人查找。我也无法确定是谁发送了彩信。

    我对java不是很熟悉,我还在学习它。我很肯定有一个数据容器(ArrayList)(Vector?)可以容纳用户定义的对象。如果可以按对象中的特定字段(日期)排序,则可以迭代该列表并按时间顺序排列所有消息:MMS/SMS 和已发送/已接收。

    【讨论】:

    • 很好的答案@Chemistpp。您知道我们如何获取 android 5.1 以上多卡手机的 SIM 卡详细信息吗?我没有看到任何存储 sim 详细信息的列。
    • @Riz Hah,好吧,我想如果我要成为一个 dbag 并回答我自己的问题,我应该做得很好。我无法接受。太过分了哈哈哈。无论如何,我不知道sim id。有几件事表明从短信中是不可能的。 *.com/questions/34311299/…*.com/questions/14051023/…
    【解决方案2】:

    有没有办法以这种方式迭代整个消息列表,将"content://mms-sms/conversations" 替换为其他内容?

    可以使用content://mms-sms/complete-conversations URL 在单个查询中获取所有彩信和短信。出于某种奇怪的原因,Telephony.MmsSms class 中没有用于此的 Uri 字段,但至少从 Froyo 开始就可以使用它。

    使用这个单一的查询肯定会比单独查询表更有效,并且任何需要完成的排序、分组或过滤肯定会比操作 Java 集合更快地由 SQLite 引擎执行。

    请注意,您必须为此查询使用特定的projection。您不能传递 null* 通配符。此外,建议在您的projection 中包含MmsSms.TYPE_DISCRIMINATOR_COLUMN ("transport_type") - 其值为"mms""sms" - 以便轻松区分消息类型。

    selectionselectionArgsorderBy 参数照常工作,null 可以为其中任何一个或所有参数传递。

    【讨论】:

    • 由于没有记录,并且无法为投影传递 null 或 *,是否有人有可以返回的列列表?
    • @Bink 可以是android.provider.Telephony.Mmsandroid.provider.Telephony.Sms中列字段的任意组合。
    • 感谢迈克的快速回复。 FWIW,我试过这个 URI,它只返回短信,而不是彩信。
    • 您是否尝试仅使用 MMS 列,只是为了测试?您确定您期望的 MMS 消息实际上是 MMS,而不是其他东西,例如 RCS?如果您查询 MMS 提供商会发生什么?即查询Telephony.Mms.CONTENT_URI返回什么?在您在这里发表评论后,我进行了快速测试,以确保我记得这是如何工作的,并且每个测试都按我的预期出现。不过,我要提一下,并非每个设备/制造商都遵守这些提供商的标准。我从这里的用户那里看到了一些非常奇怪的数据库结构和 URI 映射,他们提出了类似的问题。
    • 对不起,迈克——我的评论过激了。彩信就在那里。我按日期排序,看起来,彩信使用秒,而短信使用毫秒。抱歉打扰了。