一款适合安卓开发初学者学习的 短信 APP

Ridder_SMS

A SMS app that can replace the System SMS

一个可以替代系统短信应用的sms app

ic_launcher_round.png

前言

本文的内容主要是解析短信App Ridder_SMS 的制作流程,以及代码的具体实现,若有什么不足之处,还请提出建议,附上这个 APP 的 Github 地址 Ridder_SMS 欢迎大家 :heart: star 和 fork.

本文的主要内容

  • 联系人查询 短信查询 以及 短信收发的效果演示
  • 联系人 查询 短信查询的实现
  • 短信接收的实现
  • 短信聊天界面的实现

1.联系人查询 短信查询 以及 短信收发的效果 :

4.png

2.联系人 查询 短信查询的实现 :

(1).联系人 查询 的实现

   private List<ContactBean> getContentConfig()
    {
        Uri raw_uri=Uri.parse("content://com.android.contacts/raw_contacts");
        Uri data_uri=Uri.parse("content://com.android.contacts/data");
        Cursor raw_cursor=getContext().getContentResolver().query(raw_uri,new String[]{"_id"},null,null,null);
        if(raw_cursor==null)
        {
            return mDataList;
        }
        Log.i(TAG,raw_cursor.getCount()+"");
        while(raw_cursor.moveToNext())
        {
            String id=raw_cursor.getString(0);
            Cursor data_cursor= getContext().getContentResolver().query(data_uri,new String[]{"mimetype","data1"},
                    "raw_contact_id = ?",new String[]{id},null);
            if(data_cursor==null)
            {
                return mDataList;
            }
            Log.i(TAG,"联系人"+id);
            String name =null;
            String number=null;
            while(data_cursor.moveToNext())
            {
                String type=data_cursor.getString(0);
                if(type.equals("vnd.android.cursor.item/name"))
                {
                    Log.i(TAG,"name"+data_cursor.getString(1));
                    name=data_cursor.getString(1);
                }else if(type.equals("vnd.android.cursor.item/phone_v2"))
                {
                    Log.i(TAG,"number"+data_cursor.getString(1));
                    number=data_cursor.getString(1);
                }
            }
            ContactBean contactBean=new ContactBean(name,number);
            mDataList.add(contactBean);
            data_cursor.close();
        }
        raw_cursor.close();
        return  mDataList;
    }
       

1.首先利用contentProvider查询 raw_contacts表,得到id的游标raw_cursor。

2.接着游标raw_cursor一行行下移,得到每一行的id。查询data表中raw_contact_id列等于raw_cursor中id的行,选出其中的mimetype和data1列,得到游标data_cursor。

3.data_cursor一一行行下移,首先查询mimetype的类型,如果是name类型,则将data1列赋值给name变量。如果是phone_v2类型,则将data1列赋值给number变量。

4.利用raw_cursor的每一行中的name变量和number变量 构造 ContactBean类型的List,最终返回List。

public class RecyclerViewCallAdapter extends BaseQuickAdapter<ContactBean,BaseViewHolder>

{
    public RecyclerViewCallAdapter(int layoutResId, @Nullable List<ContactBean> data) {
        super(layoutResId, data);
    }

    @Override
    protected void convert(BaseViewHolder helper, ContactBean item) {
        helper.setText(R.id.tv_name,item.getName())
                .setText(R.id.tv_number,item.getCallNum());
    }
}

5.将List 加载到recycleView上。

(2)短信 查询 的实现

   public ArrayList<MessageBean> getSmsContent()
    {
        ContentResolver cr= getContext().getContentResolver();
        String[] projection=new String[]{"_id","thread_id","address","person","date","read","status","type","body"};
        Cursor cur = cr.query(SMS, projection, null, null, "date desc");
        if(cur!=null)
        {
            while (cur.moveToNext())
            {
                int id=cur.getInt(cur.getColumnIndex("_id"));
                long threadId=cur.getLong(cur.getColumnIndex("thread_id"));
                String addressNumber=cur.getString(cur.getColumnIndex("address"));
                String person=cur.getString(cur.getColumnIndex("person"));
                long date=cur.getLong(cur.getColumnIndex("date"));
                int read=cur.getInt(cur.getColumnIndex("read"));
                int status=cur.getInt(cur.getColumnIndex("status"));
                int type=cur.getInt(cur.getColumnIndex("type"));
                String body=cur.getString(cur.getColumnIndex("body"));
                if(type== 4)
                {
                    Log.i("tag","id : " + id + ",address: " + addressNumber + " person : " + person + " date : " + date +" type : "
                            + type+ " read : " + read + " threadid: " + threadId+"body"+body);
                }

                MessageBean messageBean=new MessageBean(id,addressNumber,person,date,read,type,threadId,body);
                mDatalist.add(messageBean);
            }
            cur.close();
            return  mDatalist;
        }else
        {
            MessageBean no_message=new MessageBean(1,"","",0,0,0,0,"No Message");
            mDatalist.add(no_message);
            cur.close();
            return mDatalist;
        }
    }

短信的查询则简单的多,只涉及到了一张表(Uri.parse("content://sms/"))

1.首先利用contentProvider查询sms表,选出其中的id,addressNumber,person,date,read,type,threadId,body列,按照时间降序排列,得到游标cur。

2.cur一行行下移,用cur的每一行和 id,addressNumber,person,date,read,type,threadId,body 变量,构造MessageBean类型的List。

    private ArrayList<MessageBean> ContentRemove(ArrayList<MessageBean> smsContent)
    {
        ArrayList<Long> addressList =new ArrayList<>();
        ArrayList<MessageBean> contentRemoveBeanList=new ArrayList<>();
        for(MessageBean messageBean :smsContent)
        {
            long threadId=messageBean.getThreadId();
            boolean contains=addressList.contains(threadId);
            if(!contains)
            {
                addressList.add(threadId);
                contentRemoveBeanList.add(messageBean);
            }
        }
        return  contentRemoveBeanList;
    }

3.根据threadId去重短信。

    protected void convert(BaseViewHolder helper, MessageBean item)
    {
        if(!TextUtils.isEmpty(item.getPerson()))
        {
            helper.setText(R.id.mi_tv_person,item.getPerson());
        }else
        {
            helper.setText(R.id.mi_tv_person,item.getAddress());
        }

        if(item.getDate()==0)
        {
            helper.setText(R.id.tv_date," ");
        }else
        {
            CharSequence format= DateFormat.format("MM-dd hh:mm",item.getDate());
            helper.setText(R.id.tv_date,format.toString());
        }

        if(!TextUtils.isEmpty(item.getMessage()))
        {
            helper.setText(R.id.mi_tv_content,item.getMessage());
        }else
        {
            helper.setText(R.id.mi_tv_content,"++");
        }

        helper.addOnClickListener(R.id.tv_delete)
                .addOnClickListener(R.id.tv_thumb)
                .addOnClickListener(R.id.mi_rl_content);
    }

4.最终返回List,加list装到recycleView中。

3.短信接收的实现:

1.首先注册SmsSReceiver,当接收到短信和短信发送成功时,系统会发出两条广播,分别是SMS_RECEIVED_ACTION 和 SMS_DELIVER_ACTION。

  private Uri storeMessage(Context context,SmsMessage[] msgs,int error)
    {
        SmsMessage sms=msgs[0];
        ContentValues values=extractContentValues(sms);
        values.put(Telephony.Sms.ERROR_CODE, error);
        int pduCount = msgs.length;
        if (pduCount == 1) {
            values.put(Telephony.Sms.Inbox.BODY, replaceFormFeeds(sms.getDisplayMessageBody()));
        } else {
            StringBuilder body = new StringBuilder();
            for (int i = 0; i < pduCount; i++) {
                sms = msgs[i];
                body.append(sms.getDisplayMessageBody());
            }
            values.put(Telephony.Sms.Inbox.BODY, replaceFormFeeds(body.toString()));
        }

        Long threadId = values.getAsLong(Telephony.Sms.THREAD_ID);
        String address = sms.getOriginatingAddress();
        values.put(Telephony.Sms.ADDRESS, address);
        if (((threadId == null) || (threadId == 0)) && (address != null)) {
            threadId = getOrCreateThreadId(context, address);
            values.put(Telephony.Sms.THREAD_ID, threadId);
        }

        ContentResolver resolver = context.getContentResolver();
        Uri insertedUri = resolver.insert(Uri.parse("content://sms"), values);

        return insertedUri;
    }

2.在onReceive方法中,借助ContentResolver和ContentValues将发出与接收到的短信和写入到sms表中。

        intentFilter=new IntentFilter();
        intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
        mfMessageReceiver=new MFMessageReceiver();
        getActivity().registerReceiver(mfMessageReceiver,intentFilter);
            mDatalist=new ArrayList<>();
            ArrayList<MessageBean> smsContent1=getSmsContent();
            ArrayList<MessageBean> messgeBeans1=ContentRemove(smsContent1);
            recyclerViewMessageAdapter=new RecyclerViewMessageAdapter(R.layout.message_item,messgeBeans1);
            recyclerViewMessageAdapter.setOnItemChildClickListener(MessageFragment.this);
            messageRecycler.setAdapter(recyclerViewMessageAdapter);

3.在MessageFragment中,注册广播,当接收到到android.provider.Telephony.SMS_RECEIVED 广播时,刷新recycleView。

4.短信聊天界面的实现:

    public void onBindViewHolder(ViewHolder holder, int position) {
        MessageBean messageBean=messageBeanList.get(position);
        if(messageBean.getType()==1)
        {
            holder.leftLayout.setVisibility(View.VISIBLE);
            holder.rightLayout.setVisibility(View.GONE);
            holder.leftMsg.setText(messageBean.getMessage());
        }else if(messageBean.getType()==2)
        {
            holder.leftLayout.setVisibility(View.GONE);
            holder.rightLayout.setVisibility(View.VISIBLE);
            holder.rightMsg.setText(messageBean.getMessage());
        }
    }      

1.MsgAdapter中有左右两个layout,根据messageBean的type类型,如果是接收,则显示leftLayout,隐藏rightLayout。如果是发出,则隐藏leftLayout,显示rightLayout。

    private static ArrayList<MessageBean> getSmsContent(String threadId)
    {
        messageBeanList.clear();
        ContentResolver cr=mContext.getContentResolver();
        String[] projection=new String[]{"_id","address","person","date","read","status","type","body" };
        Cursor cur=cr.query(SMS_INBOX,projection,"thread_id=?",new String[]{threadId},"date asc");
        if(null!=cur)
        {
            while(cur.moveToNext())
            {
                int id = cur.getInt(cur.getColumnIndex("_id"));
                String addressNumber=cur.getString(cur.getColumnIndex("address"));
                String person=cur.getString(cur.getColumnIndex("person"));
                long date=cur.getLong(cur.getColumnIndex("date"));
                int read=cur.getInt(cur.getColumnIndex("read"));
                int status=cur.getInt(cur.getColumnIndex("status"));
                int type=cur.getInt(cur.getColumnIndex("type"));
                String body=cur.getString(cur.getColumnIndex("body"));

                Log.i("tag","id : " + id + ",address: " + addressNumber + " person : " + person + " date : " + date + " type : "
                        + type + " read : " + read + " threadid: " + 0+"body"+body);
                 MessageBean messageBean=new MessageBean(id,addressNumber,person,date,read,type,0,body);
                messageBeanList.add(messageBean);
            }
            cur.close();
            return messageBeanList;
        }else
        {
            MessageBean no_message=new MessageBean(1,"","",0,0,0,0,"No message");
            messageBeanList.add(no_message);
            cur.close();
            return messageBeanList;
        }

    }

2.借助 ContentResolver 和从MainActivity传递过来的threadId,进行短信查询,构造messageBeanList,最终加载到RecycleView上。不要忘记将RecycleVIew定位到最后一行。

msgRecyclerView.scrollToPosition(messageBeanArrayList1.size()-1);

结语

以上便是我写这个 APP 的具体实现思路,以及踩过的一些坑,记录下来,给大家看看。

最后附上这个 APP 的 Github 地址 Ridder_SMS 欢迎大家 :heart: star 和 fork。

如果有什么想法或者建议,非常欢迎大家来讨论。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容