本人一直在手机ODM公司做Android开发,不是纯应用开发,和大多数Android应用开发有点区别。偶尔也会做一些系统应用上的需求开发以及个人兴趣会看看app应用和动手操作。平时的总结都是保存在本地或者使用印象笔记,记录得比较零散,今天分享在博客上。
由于是系统应用,只有在特定开发机型上使用,不好上源码,而且项目有保密要求。所以我主要写开发中的大体思路和一些知识点的总结。这个是去年的一个项目开发.目前也应用到了好多个项目中了.
一、功能需求和目标
仿照小米收藏短信功能,在短信会话界面,对长按某一条短信后,弹出包含收藏/取消收藏的Menu选项,并且该界面显示出是收藏状态/未收藏状态; 然后主界面会有收藏item列; 点击收藏列进入收藏界面,长按短信可以进行复制\取消\转发\保存到SIM卡等操作.
二.解决方法概述:
开发环境: Ubuntu系统。安卓源码android5.1上MTK6735平台及相应的编译环境。手机有root权限,eng或者userdebug版本.
短信message是存储在SQLite数据库mmssms.db中的,通过在数据库对应的表中增加字段favorite(可对照原生Mms的lock字段)来对收藏短信进行标记。界面的显示可以根据当前Mms的会话界面基础上进行修改。
总的来说就三步,修改数据库字段,添加ContentProvider查询和UI界面的修改。从界面到数据库的修改都可以参考lock字段来进行.
涉及知识点:
ContentProvider的相关知识;
SQL语言增删改查的简单了解;
适配器模式.
Android中Handler操作,异步查询数据操作.
三、主要步骤及涉及主要文件:
图1 修改的文件列表
1.数据库字段的增加。
(1)首先要找到短信使用的数据库,使用DDMS工具导出db文件,然后使用sqlite工具查看数据库。
通过DDMS工具查看FileExplorer找到文件/data/data/com.android.providers.telephony/databases/mmssms.db导出到本地电脑上(也可以在终端上使用命令adb pull /data/data/com.android.providers.telephony/databases/mmssms.db ~/),再使用工具SQLiteExpert查看,如下:
图2 未添加favorite字段的sms表
如图左边一列所示这个数据库中有多张表,每张表都有不同作用,如表canonical_addresses是短信会话中所有会话联系人地址的列表,一个会话对应一个_id和address,包含文本短信、彩信、邮件等;cellbroadcast是小区广播信息有关的表;part和pdu是和彩信有关的表;sms是文本信息的表。我们选其中sms表来看,如上图sms表右边的所有字段,可以仿照lock字段进行添加我们需要的favorite字段。
(2)其中我们主要修改sms/pdu/cellbroadcast三张表,在表中增加favorite字段。然后编译TelephonyProvider,push到手机,删除手机中的mmssms.db数据库,重启手机,再重复(1)步骤导出db文件,查看数据库表是否增加了favorite字段来验证修改是否成功。其中favorite默认是0,表示用户没有收藏,1表示用户收藏此信息。
sms表的具体修改:
M:asp/packages/providers/TelephonyProvider/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java——数据库修改文件
MmsSmsDatabaseHelper继承自SQLiteOpenHelper,这个类是专门用来进行数据库管理的,包括create、update等等。
......
private void createSmsTables(SQLiteDatabase db) {
// N.B.: Whenever the columns here are changed, the columns in
// {@ref MmsSmsProvider} must be changed to match.
db.execSQL("CREATE TABLE sms (" +
"_id INTEGER PRIMARY KEY," +
"thread_id INTEGER," +
"address TEXT," +
/// M: Code analyze 016, unknown, new column in sms table.
"m_size INTEGER," +
"person INTEGER," +
"date INTEGER," +
"date_sent INTEGER DEFAULT 0," +
"protocol INTEGER," +
"read INTEGER DEFAULT 0," +
"status INTEGER DEFAULT -1," + // a TP-Status value
......
"locked INTEGER DEFAULT 0," +
//Mms start
"favorite INTEGER DEFAULT 0," +
//Mms end
/// M: Code analyze 015, new feature, support for gemini.
"sim_id INTEGER DEFAULT -1," +
"error_code INTEGER DEFAULT 0," +
......
很明显在这个函数中创建了sms表,另外两个类似这样的修改,就不举例代码了。
此时在重新编译该修改模块(终端命令mmm package/providers/TelephonyProvider/),将out目录下生成的TelephonyProvider.apk push到手机中(终端命令adb push TelephonyProvider.apk system/priv-app/),重启手机之前一定要删除之前DDMS下用FileExplorer看到的mmssms.db数据库文件,不然重启也不生效,因为数据库文件存在了,不会再重新创建,走我们修改后的代码。
重复(1)步骤导出db文件,查看数据库表是否增加了favorite字段来验证修改是否成功。如下图右边显示已经有favorite字段了,表示修改成功。
图3 添加favorite字段后得到的sms数据库表
2.在MmsSmsProvider添加查询URL及方法,供全局查找和修改
M:asp/packages/providers/TelephonyProvider/src/com/android/providers/telephony/MmsSmsProvider.java ——继承ContentProvider的文件
这个文件比较多,只要在其中query时添加一个查询的URL就行了,其中涉及到SQL语句的组织,我自己也了解不深,只是边学边用的。,ContentProvider的知识有了解的,对这个部分就很熟悉流程了。贴一个关键的查询收藏功能的方法:
private Cursor getFavoriteMessage(String[] projection, String selection,
String sortOrder){
String[] mmsProjection = createMmsProjection(projection);
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
mmsQueryBuilder.setTables(MmsProvider.TABLE_PDU);
smsQueryBuilder.setTables(SmsProvider.TABLE_SMS);
String[] smsColumns = handleNullMessageProjection(projection);
String[] mmsColumns = handleNullMessageProjection(mmsProjection);
Set<String> columnsPresentInTable = new HashSet<String>(MMS_COLUMNS);
columnsPresentInTable.add("pdu._id");
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(
MmsSms.TYPE_DISCRIMINATOR_COLUMN, mmsColumns,
columnsPresentInTable, 0, "mms", selection,
null, null);
String smsSubQuery = smsQueryBuilder.buildUnionSubQuery(
MmsSms.TYPE_DISCRIMINATOR_COLUMN, smsColumns,
SMS_COLUMNS, 0, "sms", selection,
null, null);
SQLiteQueryBuilder unionQueryBuilder = new SQLiteQueryBuilder();
String unionQuery = null;
unionQuery = unionQueryBuilder.buildUnionQuery(
new String[] { smsSubQuery, mmsSubQuery}, sortOrder, null);
Cursor cursor = mOpenHelper.getReadableDatabase().rawQuery(unionQuery, EMPTY_STRING_ARRAY);
MmsLog.d(LOG_TAG, "getFavoriteMessage query: " + unionQuery);
MmsLog.d(LOG_TAG, "cursor count: " + cursor.getCount());
return cursor;
}
3.界面修改
由于UI界面代码比较多,更不好贴代码了,主要讲解下思路和关键文件说明
(1)会话界面
M:asp/packages/apps/Mms/src/com/android/mms/ui/MessageItem.java
——这个文件主要是对信息Message抽象出来的一个类,所以我们添加了一个favorite字段,必然要在这个类中增加一个变量来表示短信的favorite是否为1(即该某个短信Message对象是否被用户收藏)。
其中肯定也要对其构造方法,进行初始化该字段的值。
M:asp/packages/apps/Mms/src/com/android/mms/ui/ComposeMessageActivity.java ——会话界面短信列表的修改,长按某条信息之后,有对短信进行收藏/取消收藏的Menu操作.
长按ListView的item弹出Menu这个应该是比较简单的,可以在现有基础上修改.复写onCreateOptionsMenu();onPrepareOptionsMenu();
关键性方法是点击某个menu选项之后的收藏/取消收藏操作,修改数据库.
private void favoriteMessage(final MessageItem msgItem, final boolean favorite){
Uri uri;
if ("sms".equals(msgItem.mType)) {
uri = Sms.CONTENT_URI;
} else {
uri = Mms.CONTENT_URI;
}
final Uri favoriteUri = ContentUris.withAppendedId(uri, msgItem.mMsgId);
final ContentValues values = new ContentValues(1);
values.put("favorite", favorite ? 1 : 0);
Log.d(TAG, "favoriteMessage: favoriteUri = " + favoriteUri +",values = " + values);
new Thread(new Runnable(){
public void run(){
getContentResolver().update(favoriteUri, values, null, null);
}
}, "ComposeMessageActivity.favoriteMessage").start();
}
---M:asp/packages/apps/Mms/src/com/android/mms/ui/MessageListItem.java ---LinearLayout布局文件,控制收藏图标的显示与否.
图4 会话界面添加后显示
图5
图6
(2)收藏界面-略讲
主要仿照ComposeMessageActivity界面,ContextMenu,ListView,分割线等都同上,res的layout文件图片等就不细说了
A:asp/packages/apps/Mms/src/com/android/mms/ui/FavoriteMessageActivity.java
---M:asp/packages/apps/Mms/src/com/android/mms/ui/MessageListAdapter.java ---如果FavoriteMessageActivity复用这个MessageListAdapter,则需要修改以下文件,如果是单独重新写的MessageListAdapter则就不用修改以下这个个文件了.我在现有基础上改的
——短信列表显示的adpter适配,listView与MessageItem的适配器模式。里面有关查询数组列表PROJECTION需要添加favorite字段;
另外在newView复写的时候,收藏界面使用的不同layout文件
if (context instanceof FavoriteMessageActivity){
return mInflater.inflate(R.layout.favorite_message_list_item, parent, false);
}
(3)主界面-略讲
M:asp/packages/apps/Mms/src/com/android/mms/ui/ConversationList.java---主要是在ListView上面增加一个HeadView,代码不想贴了,比较简单的.另外就是要主要每次全局刷新界面的时候,收藏里面的内容都要修改,如果item上面有提示也要跟着刷新,这个可以参考每个会话在ConversationList中如何刷新的.
View headerFavorite = inflater.inflate(R.layout.conversation_list_myfavorite, null);
getListView().addHeaderView(headerFavorite, null, true);
本身由于是系统应用,只有在特定开发机型上使用,代码也太多,不好上源码. 主要记录一些思路和相关知识点总结吧.有时间也谢谢短信通知类消息聚合的开发.