android短信模块中,短信彩信的收发是重点,不同于短信,基本所有短信元素全部保存在mmssms.db的sms表中。
注:mmssms.db在/data/data/com.previder.telephony/databases/下
关于mmssms.db的表的具体定义可以看这个博客哦~ 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中有定义。
小伙伴们也可自行前往观看。