android短信模块中,短信彩信的收发是重点,不同于短信,基本所有短信元素全部保存在mmssms.db的sms表中。
注:mmssms.db在/data/data/com.previder.telephony/databases/下
http://blog.csdn.net/t12x3456/article/details/9336869
彩信的存取是pdu表,pdu表里面通过各种外键来将彩信的各元素联系起来,来看一下彩信pdu表中的元素有哪些:
这些数据源代表的意义通过命名可以稍微了解到一些,如sub存储的彩信主题,locked存储的是该信息是否锁定。表格元素的设定各芯片厂商各不相同,对于手机内置应用的定制和功能开发,也会有修改到这些表。这里不是我们的讨论重点。
下面主要将到怎样在代码中通过cursor组合外键来来获取彩信需要显示的部分信息,已彩信在notification显示时获取的方法为例说明。代码中类和常量的定义未加说明,可以从名称理解。
select的定义,即projection:
private static final String[] MMS_STATUS_PROJECTION = new String[] { Mms.THREAD_ID, Mms.DATE, Mms._ID, Mms.SUBJECT, Mms.SUBJECT_CHARSET };
where的定义,即判断条件:
private static final String NEW_INCOMING_MM_CONSTRAINT = "(" + Mms.MESSAGE_BOX + "=" + Mms.MESSAGE_BOX_INBOX + " AND " + Mms.SEEN + "=0" + " AND (" + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_NOTIFICATION_IND + " OR " + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_RETRIEVE_CONF + "))";
获取查询游标:
Cursor cursor = SqliteWrapper.query(context, resolver, Mms.CONTENT_URI, MMS_STATUS_PROJECTION, NEW_INCOMING_MM_CONSTRAINT, null, Mms.DATE + " desc");
表设计里面甚至彩信发送人地址都没有!太过分了,那首先我们获取address和联系人对象:
long msgId = cursor.getLong(COLUMN_MMS_ID);//获取彩信pdu的id Uri msgUri = Mms.CONTENT_URI.buildUpon().appendPath( Long.toString(msgId)).build();//根据msgId查询这条信息Uri String address = AddressUtils.getFrom(context, msgUri);//根据Uri和上下文获取发信人地址 Contact contact = Contact.get(address, false);//根据地址获取联系人对象
(1)分析下AddressUtils.getFrom(context, msgUri);其实也很简单,就是另外一个查询数据库的操作,直接上源码,有心人可以把这个查询和上面查询组合,不过好复杂的样子,我讨厌数据库。
public static String getFrom(Context context, Uri uri) { String msgId = uri.getLastPathSegment(); Uri.Builder builder = Mms.CONTENT_URI.buildUpon(); builder.appendPath(msgId).appendPath("addr"); Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(), builder.build(), new String[] {Addr.ADDRESS, Addr.CHARSET}, Addr.TYPE + "=" + PduHeaders.FROM, null, null); if (cursor != null) { try { if (cursor.moveToFirst()) { String from = cursor.getString(0); if (!TextUtils.isEmpty(from)) { byte[] bytes = PduPersister.getBytes(from); int charset = cursor.getInt(1); return new EncodedStringValue(charset, bytes) .getString(); } else { /// M: @{ Log.d(TAG, "getFrom EncodedStringValue1 is null"); } /// @} } } finally { cursor.close(); } } /// M: @{ Log.d(TAG, "getFrom EncodedStringValue2 is null"); /// @} return context.getString(R.string.hidden_sender_address); }
(2)再分析Contact.get(address, false);直接上源码吧,我也不分析了- - 原因同上!
private Contact get(String number, boolean isMe, boolean canBlock) { if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) { logWithTrace("get(%s, %s, %s)", number, isMe, canBlock); } final Object obj = new Object(); if (TextUtils.isEmpty(number)) { number = ""; // In some places (such as Korea), it's possible to receive // a message without the sender's address. In this case, // all such anonymous messages will get added to the same // thread. } // Always return a Contact object, if if we don't have an actual contact // in the contacts db. Contact contact = internalGet(number, isMe); Runnable r = null; synchronized (contact) { // If there's a query pending and we're willing to block then // wait here until the query completes. /// M: make sure the block can update contact immediately @{ if (canBlock) { contact.mIsStale = true; } /// @} // If we're stale and we haven't already kicked off a query then kick // it off here. if (contact.mIsStale) { contact.mIsStale = false; if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { log("async update for " + contact.toString() + " canBlock: " + canBlock + " isStale: " + contact.mIsStale); } final Contact c = contact; r = new Runnable() { public void run() { updateContact(c); /// M:@{ synchronized (obj) { obj.notifyAll(); } c.mQueryPending = false; /// @} } }; } } // do this outside of the synchronized so we don't hold up any // subsequent calls to "get" on other threads //MmsLog.d(M_TAG, "get(" + number + ", " + isMe + ", " + canBlock + "): mWaitTime = " + mWaitTime); if (r != null) { if (canBlock) { /// M: @{ pushTask(r); synchronized (obj) { try { obj.wait(mWaitTime); } catch (InterruptedException ex) { // do nothing } } if (mWaitTime < mMaxWaitTime) { mWaitTime += 5; } } else { pushTask(r); } } else { if ((mWaitTime -= mMinWaitTime) < mMinWaitTime) { mWaitTime = mMinWaitTime; } } /// @} return contact; }
地址和联系人获取到之后,我们开始获取彩信主题,表内有这个元素,直接获取,可能有些编码要处理,这个就不写了。
然后根据thread_id开始各种查询了!
long threadId = cursor.getLong(COLUMN_THREAD_ID); long timeMillis = cursor.getLong(COLUMN_DATE) * 1000; Bitmap attachedPicture = null; String messageBody = null; int attachmentType = WorkingMessage.TEXT; try { GenericPdu pdu = sPduPersister.load(msgUri);//获取Uri对应pdu类 if (pdu != null && pdu instanceof MultimediaMessagePdu) { SlideshowModel slideshow = SlideshowModel.createFromPduBody(context,((MultimediaMessagePdu)pdu).getBody());//彩信的幻灯片 attachmentType = getAttachmentType(slideshow); SlideModel firstSlide = slideshow.get(0);//获取第一张幻灯片,因为在通知栏只需要显示彩信预览即可 if (firstSlide != null) { if (cursor.getCount() <= 1) { if (firstSlide.hasImage()) { int maxDim = dp2Pixels(MAX_BITMAP_DIMEN_DP); attachedPicture = firstSlide.getImage().getBitmap(maxDim, maxDim);//获取第一张幻灯片的图片 } } if (firstSlide.hasText()) { messageBody = firstSlide.getText().getText(); } } } } catch (final MmsException e) { Log.e(TAG, "MmsException loading uri: " + msgUri, e); }
这样彩信预览相关的元素就全部获取到了。基本都是些查表和对接口的掌握。
上述的主体方法在\packages\apps\Mms\src\com\android\mms\transaction\MessagingNotification.java中有定义。
小伙伴们也可自行前往观看。