客户需求
最近在做一个项目,android 4.4系统,客户要求在锁屏界面有未读短信未接来电的提醒功能。而平台没有此功能,要自己实现。并且时间非常紧,………。(其实软件工程师基本上都是这样,坑,坑,坑)
效果图
读取数据
(1)读取未接来电的数量
主是是判断db数据库中的type和new二个字段是否同时是Calls.MISSED_TYPE 和1(type == Calls.MISSED_TYPE,new == 1).
通话记录的db数据库在:
/data/data/com.android.providers.contacts/databases/contacts2.db
calls表中
public int getUnreadCallNum(){
Log.d(TAG, "TinnoUnreadCallContentObserver---getUnreadCallNum" );
Cursor cursor = context.getContentResolver().query(
CallLog.Calls.CONTENT_URI,
new String[] {Calls.TYPE},
" type=? and new=?",
new String[] {Calls.MISSED_TYPE + "", "1"},
"date desc");
Log.d(TAG, "TinnoUnreadCallContentObserver---getUnreadCallNum--cursor:"+cursor);
int count = 0;
if (cursor != null) {
try {
count = cursor.getCount();
Log.d(TAG, "TinnoUnreadCallContentObserver---getUnreadCallNum--count:"+count);
} finally {
cursor.close();
}
}
Log.d(TAG, "getUnreadCallNum count=" + count);
return count;
}
(2)读取未读短信数量:
这主要是未读短信和未读彩信的和:
public int getNewSmsCount() {
int result = 0;
Cursor csr = context.getContentResolver().query(
Uri.parse("content://sms"),
null,
"type = 1 and read = 0",
null,
null);
if (csr != null) {
result = csr.getCount();
csr.close();
}
return result;
}
public int getNewMmsCount() {
int result = 0;
Cursor csr = context.getContentResolver().query(
Uri.parse("content://mms/inbox"),
null,
"read = 0",
null,
null);
if (csr != null) {
result = csr.getCount();
csr.close();
}
return result;
}
读取上面的数据,是要在AndroidManifest.xml文件中定义一下权限:
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS"/>
界面显示
好了,要显示的数据得来了,下面就把这些数据在锁屏界面来显示吧。
是不是觉得下面非常简单了,事实上,界面显示才是这个功能的真正的难点所在,我花费了大量的时间在这个界面显示上。那么,坑人的地方,是在那里呢?
因为我本身有MTK的实现了此能的代码,我一开始想直接移植此功能,非常可惜,由于平台差异太大,导致我移植后,一开机systemui就直接报错,根本就没有办法调试和分析,而导致systemui报错的地方,就是界面的布局文件是显示。
最好,我还是老老实实的一步一步的添加控件,一步一步的调试,才把这个界面显示添加上去。有时候,最笨的方法才是最可靠的方法。
我们先在keyguard_selector_view.xml文件中,添加显示未读短信和未接来电和图标和数量:
<com.android.keyguard.KeyguardSelectorView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/res/com.android.keyguard"
android:id="@+id/keyguard_selector_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
androidprv:layout_maxWidth="420dp"
androidprv:layout_maxHeight="@dimen/keyguard_security_height"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:contentDescription="@string/keyguard_accessibility_slide_unlock">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center">
<include layout="@layout/keyguard_message_area"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:id="@+id/keyguard_selector_view_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:background="@drawable/kg_bouncer_bg_white"/>
<!-- add hexiaoming for lockscreen unread event start-->
<FrameLayout
android:id="@+id/flUnreadIncall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:layout_marginLeft="40dp">
<ImageView
android:id="@+id/unreadIncall"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:background="@drawable/mtk_ic_newevent_phone"
android:visibility="invisible"/>
<TextView
android:id="@+id/unreadIncallNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:gravity="center"
android:background="@drawable/mtk_ic_newevents_numberindication"
android:visibility="invisible"/>
</FrameLayout>
<FrameLayout
android:id="@+id/flUnreadIncall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:layout_marginLeft="220dp">
<ImageView
android:id="@+id/unreadMms"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:background="@drawable/mtk_ic_newevent_smsmms"
android:visibility="invisible"/>
<TextView
android:id="@+id/unreadMmsNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:gravity="center"
android:background="@drawable/mtk_ic_newevents_numberindication"
android:visibility="invisible"/>
</FrameLayout>
<!-- add hexiaoming for lockscreen unread event end-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="20dp"
android:id="@+id/keyguard_unlock_panel">
<include layout="@layout/keyguard_glow_pad_container" />
</FrameLayout>
<include layout="@layout/keyguard_eca"
android:id="@+id/keyguard_selector_fade_container"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom|center_horizontal" />
</FrameLayout>
</com.android.keyguard.KeyguardSelectorView>
然后在KeyguardSelectorView.java文件中,添加对显示ui的实现逻辑,而这主要是依靠对call log和短信,彩信三个db数据库的监听来实现的。
先定义:
private SecurityMessageDisplay mSecurityMessageDisplay;
private Drawable mBouncerFrame;
//add hexiaoming for lockscreen unread event start
private boolean isSupportUnreadEventLockscreen = true;
private TextView unreadIncallNum = null;
private ImageView unreadIncall = null;
private Handler handler;
private TextView unreadMmsNum = null;
private ImageView unreadMms = null;
private TinnoUnreadCallContentObserver tinnoUnreadCallContentObserver = null;
private TinnoUnreadMmsContentObserver tinnoUnreadMmsContentObserver = null;
//add hexiaoming for lockscreen unread event end
再初始化和注册监听:
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
mGlowPadView.setOnTriggerListener(mOnTriggerListener);
if (SystemProperties.getBoolean("ro.board.has.3in1speaker", false)) {
mGlowPadView.setVibrateEnabled(false); // Do not vibrate when unlocking phone.
}
updateTargets();
mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
View bouncerFrameView = findViewById(R.id.keyguard_selector_view_frame);
mBouncerFrame = bouncerFrameView.getBackground();
//add hexiaoming for lockscreen unread event start
if(isSupportUnreadEventLockscreen){
unreadIncallNum = (TextView)findViewById(R.id.unreadIncallNum);
unreadIncall = (ImageView)findViewById(R.id.unreadIncall);
handler = new Handler();
tinnoUnreadCallContentObserver = new TinnoUnreadCallContentObserver(
handler,
getContext(),
unreadIncallNum,
unreadIncall);
getContext().getContentResolver().registerContentObserver(
TinnoUnreadCallContentObserver.MISS_CALL_URI,
true,
tinnoUnreadCallContentObserver);
unreadMmsNum = (TextView)findViewById(R.id.unreadMmsNum);
unreadMms = (ImageView)findViewById(R.id.unreadMms);
tinnoUnreadMmsContentObserver = new TinnoUnreadMmsContentObserver(
handler,
getContext(),
unreadMmsNum,
unreadMms);
getContext().getContentResolver().registerContentObserver(
Uri.parse("content://sms"),
true,
tinnoUnreadMmsContentObserver);
getContext().getContentResolver().registerContentObserver(
TinnoUnreadMmsContentObserver.UNREAD_MMSSMS_URI,
true,
tinnoUnreadMmsContentObserver);
}
//add hexiaoming for lockscreen unread event end
}
至此,逻辑部分已经实现,您还需要在Android.mk文件中添加下面的定义,才可以编译:
#add hexiaoming for lockscreen unread event start
LOCAL_JAVA_LIBRARIES += telephony-common mms-common
#add hexiaoming for lockscreen unread event end
bug的解决
你以为就ok了,当然,如果从我们开发者来说,是的,功能是可以了。但是,对于使用者来说,不是这样的。
测试提出来,说有一个bug:当我们有一个未接来电时,我们点击拨号应用,按道理来说,这个未接来电是要不再显示的,但是我们还是会显示未接来电。
我看了一下操作,思考了一下,其实原理非常简单,因为在点击拨号进入应用时,我们没有把未接来电改为已读来电,当然是还显示未接来电了。
那修改就简单了,我直接在拨号应用的主界面,当进来时,直接把未接来电改为已读来电就可以了。
实现逻辑:
Dialer\src\com\android\dialer\DialtactsActivity.java
@Override
protected void onResume() {
super.onResume();
............
............
//add hexiaoming for lockscreen new event start
removeMissedCall();
//add hexiaoming for lockscreen new event end
}
//add hexiaoming for lockscreen new event start
private void removeMissedCall() {
// TODO Auto-generated method stub
new AsyncTask<Void, Void, Void>() {
@Override
public Void doInBackground(Void... params) {
// Cursor cursor = context.getContentResolver().query(
// CallLog.Calls.CONTENT_URI,
// new String[] {Calls.TYPE},
// " type=? and new=?",
// new String[] {Calls.MISSED_TYPE + "", "1"},
// "date desc");
ContentValues values = new ContentValues();
values.put(Calls.TYPE, Calls.MISSED_TYPE);
values.put("new", 0);
String where = "type=? and new=?";
String[] selectValue = {Calls.MISSED_TYPE + "", "1"};
Log.d("debug_hxm","removeMissedCall--update");
getContentResolver().update(
CallLog.Calls.CONTENT_URI,
values,
where,
selectValue);
return null;
}
}.execute(null, null, null);
}
//add hexiaoming for lockscreen new event end