一 分析步骤和过程
我们需要实现的是当有一个未接来电和未读短信时,Launcher界面的Dialer和Msm的icon能够显示未读条数,其实和微信的差不多。里面有一个功能就是显示未读新闻的条数
步入正题,首先未接电话和未读短信都会插入都各自的数据库中,未读短信会插入到短信的数据库中,我们只需要监听数据库的变化,如果有未读短信插入数据库我们就更新Launcher界面的Msm的ICON来重新绘制ICON来显示所需要的东西。
1 首先我们监听各自的数据库,我们可以在Launcher的onCreate里做此操作代码如下分析:
private MissedCallContentObserver mMissedCallContentObserver;
private NewMmsContentObserver mNewMmsContentObserver
mMissedCallContentObserver = new MissedCallContentObserver(getApplicationContext(), new Handler());
getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, true, mMissedCallContentObserver);
mNewMmsContentObserver = new NewMmsContentObserver(getApplicationContext(), new Handler());
getContentResolver().registerContentObserver(Uri.parse("content://mms-sms/"), true, mNewMmsContentObserver ; );
我们完成注册后对应的onDestory里就有如下:
getContentResolver().unregisterContentObserver(mMissedCallContentObserver);
getContentResolver().unregisterContentObserver(mNewMmsContentObserver);
这个是监听未接来电数据库的变化具体看MissedCallContentObserver当数据库有数据变化是会执行对应的方法,这个监听者模式我就不详细介绍了,大家都是明白的也经常会用到此功能的具体实现如下:
public class MissedCallContentObserver extends ContentObserver {
public MissedCallContentObserver(Context context, Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
mIconHandler.sendEmptyMessageDelayed(MSG_REFRESH_LAUNCHER, ICON_MSG_DELAYED);
}
}
这个是监听未接来电的方法,我们在看下未读短信的监听
public class NewMmsContentObserver extends ContentObserver{
public NewMmsContentObserver(Context context, Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange){
mIconHandler.sendEmptyMessageDelayed(MSG_REFRESH_LAUNCHER,ICON_MSG_DELAYED);
}
}
}
这两个类就实现了数据库的动态监听,当数据库有数据发生变化时,我们就这两个方法就会回调,我们就可以在onChange方法里做我们需要做的动作,更新ICON,更新为我们需要显示的样子,接下来我们开始分析onChange方法里的动作,我们在这个方法里利用Handler发送了一个message来执行更新Dialer和Msm的CION。
当然我们就要实现自己的Handler了具体实现如下:
private final static int MSG_REFRESH_LAUNCHER = 2000;
private final static int ICON_MSG_DELAYED = 2000;
private Handler mIconHandler = new Handler() {
public void handleMessage(Message msg) {
switch(msg.what) {
case MSG_REFRESH_LAUNCHER:
//mModel.startLoader(true, -1);
mModel.updateApp();
break;
}
};
};
当onChange方法触动后我们的Handler就能接受到消息,来完成更新,看看我们是如何更新的,Launcher里有很多更新界面的方法。我们这个开始用了 //mModel.startLoader(true, -1);这个方法会把launcher里所有的ICON全部更新,感觉这里有点不好,最后想到了一个好的办法,那就是只更新Dialer和Mms了,用的是mModel.updateApp();
这个方法是我们自己实现的看看源码
public void updateApp() {
enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, new String[] { "com.android.dialer", "com.android.mms" }));
}
其实这方法里调用的也是系统的方法,具体怎么实现完成的我们可以看看这个方法,此方法就不多介绍了,我们主要还是研究下这个功能的实现思路和方法。
当我们开始更新Dialer和MmS这两个iCON时,当然我们就要对这两个ICON做一些处理了
我们需要在PagedViewIcon类和BubbleTextView这两个类中对Dialer和Mms的ICON进行过滤来完成显示。
Launcher里面IconCache类是对每一个ICON的图片的绘制,我们就把Dialer和Mms的ICON图片做一些特殊的处理吧
我们先来看IconCache类里面的处理我们先来获取未接来电和未读短信的条数
public int getUnreadSmsCount() {
return findNewMmsCount(mContext) + findNewSmsCount(mContext);
}
这个方法是获取未读短信的总条数,里面为了过滤短信发送时回执报告
private int findNewSmsCount(Context ctx){
Cursor csr = null;
int newSmsCount = 0;
try {
// csr = mContext.getContentResolver().query(Uri.parse("content://sms"), null,"type = 1 and read = 0", null, null);
csr = mContext.getContentResolver().query(Uri.parse("content://sms/inbox"), null, "read = 0", null, null);
newSmsCount = csr.getCount();
} catch (Exception e) {
e.printStackTrace();
} finally {
csr.close();
}
return newSmsCount;
}
private int findNewMmsCount(Context ctx){
Cursor csr = null;
int newMmsCount = 0;
try {
csr = mContext.getContentResolver().query(Uri.parse("content://mms/inbox"), null, "read = 0 and m_type != 134", null, null);
newMmsCount = csr.getCount();
} catch (Exception e) {
e.printStackTrace();
} finally {
csr.close();
}
return newMmsCount;
}
再来看未接来电
public int getUnreceivedCallCount() {
ContentResolver localContentResolver = mContext.getContentResolver();
Uri localUri = CallLog.Calls.CONTENT_URI;
String[] arrayOfString = new String[1];
arrayOfString[0] = "_id";
Cursor localCursor = localContentResolver.query(localUri, arrayOfString, "type=3 and new<>0", null, null);
int j;
if (localCursor == null) {
return -1;
} else {
try {
j = localCursor.getCount();
localCursor.close();
} finally {
localCursor.close();
}
}
return j;
}
这两个方法来完成未接来电和未读短信条数的获取。
我们来看看PagedViewIcon是如何来过滤和更新ICON 的
if (name != null) {
if ("com.android.dialer".equals(name.getPackageName())
&& "com.android.dialer.DialtactsActivity".equals(name.getClassName())) {
int unreceivedCall = iconCache.getUnreceivedCallCount();
mIcon = iconCache.createNotifyImage(unreceivedCall, mIcon, getPaint(), name.getPackageName());
name.getPackageName());
} else if ("com.android.mms".equals(name.getPackageName())
&& "com.android.mms.ui.ModeChooser".equals(name.getClassName())) {
int unreadSmsCount = iconCache.getUnreadSmsCount();
mIcon = iconCache.createNotifyImage(unreadSmsCount, mIcon, getPaint(), name.getPackageName());
}
}
这里我们对这两个应用做了特殊的处理,同理在看下BubbleTextView里面的方法
public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) {
Bitmap b = info.getIcon(iconCache);
final Intent intentnew = info.intent;
final ComponentName name = intentnew.getComponent();
if (name != null) {
if ("com.android.dialer".equals(name.getPackageName())
&& "com.android.dialer.DialtactsActivity".equals(name.getClassName())) {
int unreceivedCall = iconCache.getUnreceivedCallCount();
b = iconCache.createNotifyImage(unreceivedCall, b, getPaint(), name.getPackageName());
} else if ("com.android.mms".equals(name.getPackageName())
&& "com.android.mms.ui.ModeChooser".equals(name.getClassName())) {
int unreadSmsCount = iconCache.getUnreadSmsCount();
b = iconCache.createNotifyImage(unreadSmsCount, b, getPaint(), name.getPackageName());
}
}
这就基本完成了这两个ICON的过滤功能
iconCache.createNotifyImage(unreceivedCall, b, getPaint(), name.getPackageName())这个方法我们就知道是绘制ICON图片的显示我们在IconCache里面研究下看怎么绘制的
public Bitmap createNotifyImage(int number, Bitmap bitmap, Paint paint, String packageName) {
String num = String.valueOf(number);
if (number == 0) {
Bitmap bitmap2 = getAppIcon(packageName);
if(bitmap2 != null) {
return zoomBitmap(bitmap2, bitmap.getWidth(), bitmap.getHeight());
}
}
if(number > 99) {
num = "99+";
} Drawable drawable = mContext.getResources().getDrawable(R.drawable.notification_icon);
Bitmap originalBitmap = ((BitmapDrawable) drawable).getBitmap();
int width = originalBitmap.getWidth();
int height = originalBitmap.getHeight();
int textSize = 22;
int textHeight = 30;
int textStartX = 0;
if (number > 99) {
textStartX = 20;} else if (number > 9){
textStartX = 22;
} else {
textStartX = 24;
}
if (mIconDpi < 320) {
textSize = 15;
textHeight = 22;
if (number > 99) {
textStartX = 12;
} else if (number > 9){
textStartX = 14;
} else {
textStartX = 16;
}
} Matrix matrix = new Matrix();
matrix.preScale(1, -1);
Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);Paint defaultPaint = new Paint();
defaultPaint.setAntiAlias(true);
defaultPaint.setFakeBoldText(true);
defaultPaint.setTextSize(textSize);
defaultPaint.setTextAlign(Align.CENTER);
defaultPaint.setColor(0xffffffff);
canvas.drawBitmap(originalBitmap, 0, 0, null);
canvas.drawText(num, textStartX, textHeight, defaultPaint);
Canvas canvas1 = new Canvas(bitmap);
canvas1.drawBitmap(newBitmap, bitmap.getWidth() - newBitmap.getWidth() , 0, paint);
return bitmap;
}通过这个方法我们就完成了过滤ICON的绘制
基本功能的思路我们已经讲解完成,代码只是一些大概,如需要详细的了解我们还要继续研究Android Launcher的源码分析,
Launcher源码博大精深值得我们好好学习和探究。