之前一直以为是应用本身在对图标进行修改,看了源码之后发现其实主要的工作并不是应用自己完成的,主要的工作在是launcher里面完成的.

关于系统里面类似未读短信的具体处理流程如下,
原理
一个应用要实现这个效果,就要在自己有未读的消息的时候发送一个广播告诉系统我有未处理的事件了(例如:短信,电话和邮件等),同时将相关的信息进行保存,比如应用的名称(这里指的是ComponentName)和未处理事件的数量.系统将提升用户有待处理的事件交给Launcher去处理,launcher会接收到对应的广播,不是随便一个应用都有这个待遇的,launcher接到广播之后会先解析数据,看当前的应用有没有这个待遇也就是是否支持显示这个待处理事件的通知.如果通过launcher就会调用相关方法去重绘应用的icon.
效果图: 实现过程分析

Intent.java(系统增加3个常量)
frameworks/base/core/java/android/content/Intent.java
  1. //状态
  2. public static final String MTK_ACTION_UNREAD_CHANGED = "com.mediatek.action.UNREAD_CHANGED";
  3. //应用名称
  4. public static final String MTK_EXTRA_UNREAD_COMPONENT = "com.mediatek.intent.extra.UNREAD_COMPONENT";
  5. //数量
  6. public static final String MTK_EXTRA_UNREAD_NUMBER = "com.mediatek.intent.extra.UNREAD_NUMBER";
系统里面有这个功能几个应用相关的代码路径
日历 packages/apps/Calendar/src/com/mediatek/calendar/MTKUtils.java
邮件 :packages/apps/Email/src/com/android/email/NotificationController.java
电话 :packages/providers/ContactsProvider/src/com/android/providers/contacts/CallLogProvider.java 
短信 :packages/providers/TelephonyProvider/src/com/android/providers/telephony/MmsSmsProvider.java
 
这里以电话为例:
假设当前有人打电话进来,系统会保存改记录到数据库,根据事件是否已经处理来判断是否发送广播
 
CallLogProvider.java(发送有待处理事件的广播)
packages/providers/ContactsProvider/src/com/android/providers/contacts/CallLogProvider.java
  1. /** M: send new Calls broadcast to luancher to update unread icon @{ */
  2. public static final void notifyNewCallsCount(SQLiteDatabase db, Context context) {
  3. Cursor c = null;
  4. …..
  5. //send count=0 to clear the unread icon
  6. if (newCallsCount >= 0) { //有新的来电数量
  7. Intent newIntent = new Intent(Intent.MTK_ACTION_UNREAD_CHANGED);
  8. newIntent.putExtra(Intent.MTK_EXTRA_UNREAD_NUMBER, newCallsCount);
  9. newIntent.putExtra(Intent.MTK_EXTRA_UNREAD_COMPONENT, new ComponentName(Constants.CONTACTS_PACKAGE,
  10. Constants.CONTACTS_DIALTACTS_ACTIVITY));
  11. context.sendBroadcast(newIntent); //发送对应的广播
  12. android.provider.Settings.System.putInt(context.getContentResolver(), Constants.CONTACTS_UNREAD_KEY, Integer
  13. .valueOf(newCallsCount));
  14. }
  15. }

MTKUnreadLoader.java(Launcher接收到应用发送的广播,进行判断改应用是否可以显示有未处理事件的图标)

packages/apps/Launcher2/src/com/android/launcher2/MTKUnreadLoader.java
  1. public void onReceive(final Context context, final Intent intent) {
  2. final String action = intent.getAction();
  3. //过滤广播
  4. if (Intent.MTK_ACTION_UNREAD_CHANGED.equals(action)) {
  5. final ComponentName componentName = (ComponentName) intent.getExtra(Intent.MTK_EXTRA_UNREAD_COMPONENT);
  6. final int unreadNum = intent.getIntExtra(Intent.MTK_EXTRA_UNREAD_NUMBER, -1);
  7. if (mCallbacks != null && componentName != null && unreadNum != -1) {
  8. //判断是否支持该功能
  9. final int index = supportUnreadFeature(componentName);
  10. if (index >= 0) { //支持
  11. boolean ret = setUnreadNumberAt(index, unreadNum);
  12. if (ret) {
  13. final UnreadCallbacks callbacks = mCallbacks.get();
  14. if (callbacks != null) {
  15. callbacks.bindComponentUnreadChanged(componentName, unreadNum);
  16. }
  17. .........
  18. }

LauncherApplication.java(Launcher注册对应的广播接收器)

packages/apps/Launcher2/src/com/android/launcher2/LauncherApplication.java
  1. public void onCreate() {
  2. ........
  3. /// M: register unread broadcast.
  4. if (FeatureOption.MTK_LAUNCHER_UNREAD_SUPPORT) {
  5. mUnreadLoader = new MTKUnreadLoader(getApplicationContext());
  6. // Register unread change broadcast.
  7. filter = new IntentFilter();
  8. filter.addAction(Intent.MTK_ACTION_UNREAD_CHANGED);
  9. registerReceiver(mUnreadLoader, filter); //注册对应的广播接收器
  10. }
  11. ..............
  12. }

MTKUnreadLoader.java(处理应用的图标显示未处理事件的数字)

packages/apps/Launcher2/src/com/android/launcher2/MTKUnreadLoader.java
  1. static void drawUnreadEventIfNeed(Canvas canvas, View icon) {
  2. ItemInfo info = (ItemInfo)icon.getTag();
  3. if (info != null && info.unreadNum > 0) { //判断未处理事件数量
  4. Resources res = icon.getContext().getResources();
  5. ..........
  6. if (info.unreadNum > Launcher.MAX_UNREAD_COUNT) {
  7. unreadTextNumber = String.valueOf(Launcher.MAX_UNREAD_COUNT);
  8. unreadTextPlusPaint.getTextBounds(unreadTextPlus, 0, unreadTextPlus.length(), unreadTextPlusBounds);
  9. } else {
  10. unreadTextNumber = String.valueOf(info.unreadNum);
  11. }
  12. unreadTextNumberPaint.getTextBounds(unreadTextNumber, 0, unreadTextNumber.length(), unreadTextNumberBounds);
  13. int textHeight = unreadTextNumberBounds.height();
  14. int textWidth = unreadTextNumberBounds.width() + unreadTextPlusBounds.width();
  15. // 数字的背景图
  16. NinePatchDrawable unreadBgNinePatchDrawable = (NinePatchDrawable)res.getDrawable(R.drawable.ic_newevents_numberindication);
  17. .........
  18. Rect unreadBgBounds = new Rect(0, 0, unreadBgWidth, unreadBgHeight);
  19. unreadBgNinePatchDrawable.setBounds(unreadBgBounds);
  20. int unreadMarginTop = 0;
  21. int unreadMarginRight = 0;
  22. if (info instanceof ShortcutInfo) { //workspace 里面的快捷方式
  23. if (info.container == (long)LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
  24. unreadMarginTop = (int)res.getDimension(R.dimen.hotseat_unread_margin_top);
  25. unreadMarginRight = (int)res.getDimension(R.dimen.hotseat_unread_margin_right);
  26. } else if (info.container == (long)LauncherSettings.Favorites.CONTAINER_DESKTOP) {
  27. unreadMarginTop = (int)res.getDimension(R.dimen.workspace_unread_margin_top);
  28. unreadMarginRight = (int)res.getDimension(R.dimen.workspace_unread_margin_right);
  29. } else {
  30. unreadMarginTop = (int)res.getDimension(R.dimen.folder_unread_margin_top);
  31. unreadMarginRight = (int)res.getDimension(R.dimen.folder_unread_margin_right);
  32. }
  33. } else if (info instanceof FolderInfo) { //文件夹
  34. if (info.container == (long)LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
  35. unreadMarginTop = (int)res.getDimension(R.dimen.hotseat_unread_margin_top);
  36. unreadMarginRight = (int)res.getDimension(R.dimen.hotseat_unread_margin_right);
  37. } else if (info.container == (long)LauncherSettings.Favorites.CONTAINER_DESKTOP) {
  38. unreadMarginTop = (int)res.getDimension(R.dimen.workspace_unread_margin_top);
  39. unreadMarginRight = (int)res.getDimension(R.dimen.workspace_unread_margin_right);
  40. }
  41. }
  42. else if (info instanceof ApplicationInfo) { //all app 里面的应用icon
  43. unreadMarginTop = (int)res.getDimension(R.dimen.app_list_unread_margin_top);
  44. unreadMarginRight = (int)res.getDimension(R.dimen.app_list_unread_margin_right);
  45. }
  46. int unreadBgPosX = icon.getScrollX() + icon.getWidth() - unreadBgWidth - unreadMarginRight;
  47. int unreadBgPosY = icon.getScrollY() + unreadMarginTop;
  48. canvas.save();
  49. canvas.translate(unreadBgPosX, unreadBgPosY);
  50. unreadBgNinePatchDrawable.draw(canvas);
  51. /// M: Draw unread text.
  52. Paint.FontMetrics fontMetrics = unreadTextNumberPaint.getFontMetrics();
  53. if (info.unreadNum > Launcher.MAX_UNREAD_COUNT) {
  54. canvas.drawText(unreadTextNumber,
  55. (unreadBgWidth - unreadTextPlusBounds.width()) / 2,
  56. (unreadBgHeight + textHeight) / 2,
  57. unreadTextNumberPaint);
  58. canvas.drawText(unreadTextPlus,
  59. (unreadBgWidth + unreadTextNumberBounds.width()) / 2,
  60. (unreadBgHeight + textHeight) / 2 + fontMetrics.ascent / 2,
  61. unreadTextPlusPaint);
  62. } else {
  63. .....
  64. }
  65. }

unread_support_shortcuts.xml(配置哪些应用可以显示待处理的事件)

packages/apps/Launcher2/res/xml/unread_support_shortcuts.xml
Xml代码 
  1. <unreadshortcuts xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher">
  2. <!--电话-->
  3. <shortcut
  4. launcher:unreadPackageName="com.android.contacts"
  5. launcher:unreadClassName="com.android.contacts.activities.DialtactsActivity"
  6. launcher:unreadType="0"
  7. launcher:unreadKey="com_android_contacts_mtk_unread"
  8. />
  9. <!--短信-->
  10. <shortcut
  11. launcher:unreadPackageName="com.android.mms"
  12. launcher:unreadClassName="com.android.mms.ui.BootActivity"
  13. launcher:unreadType="0"
  14. launcher:unreadKey="com_android_mms_mtk_unread"
  15. />
  16. <!--邮件-->
  17. <shortcut
  18. launcher:unreadPackageName="com.android.email"
  19. launcher:unreadClassName="com.android.email.activity.Welcome"
  20. launcher:unreadType="0"
  21. launcher:unreadKey="com_android_email_mtk_unread"
  22. />
  23. ................
  24. </unreadshortcuts>

Android 类似未读短信图标显示数字效果的分析的更多相关文章

  1. Android中为图标加上数字--用于未读短信数提醒,待更新应用数提醒等

    本文属于原创,转载请著名出处:http://flysnow.iteye.com/blog/906770 写道 在我们开发一些如短消息.应用商店等应用时,会考虑在短消息的图标上加上未读短信的数量,在应用 ...

  2. android: 接收和发送短信

    8.2    接收和发送短信 收发短信应该是每个手机最基本的功能之一了,即使是许多年前的老手机也都会具备这 项功能,而 Android 作为出色的智能手机操作系统,自然也少不了在这方面的支持.每个 A ...

  3. 解决:HotSeat短信图标提醒有误

    [操作步骤]正常收发短信.彩信. [测试结果]所有短信均已阅读,但在HOME界面的短信图标仍提示有一条短信未读.重启后仍存在. 经过分析,导致该情况的主要原因为当彩信已读的时候,launcher中进行 ...

  4. 向android模拟器打电话发短信的简单方法

    在开发android应用程序时,有时候需要测试一下向android手机拨打电话发送短信时该应用程序的反应.譬如编写一个广播接收器,来提示用户有短信收到或者处理短信,就需要向该手机发送短信来进行测试.这 ...

  5. Android监听系统短信数据库变化-提取短信内容

    由于监听系统短信广播受到权限的限制,所以很多手机可能使用这种方式没法监听广播,从而没办法获取到系统短信,所以又重新开辟一条路. Android监听系统短信数据库内容变化使用场景: 1.监听短信数据库的 ...

  6. Android学习笔记之短信验证码的获取和读取

    PS:最近很多事情都拖拖拉拉的..都什么办事效率啊!!! 还得吐槽一下移动运营商,验证码超过五次的时候,直接把我的手机号封闭.真是受够了. 学习笔记: 1.Android之如何获取短信验证码. 2.如 ...

  7. android打电话、发短信实现

    打电话: Intent intent = newIntent(Intent.ACTION_CALL,Uri.parse("tel:"+"156666666666" ...

  8. Android开发之发送短信

    本实例通过SmsManager的sendTextMessage方法实现发送短信关于SmsManager的具体解释大家能够參照:Android开发之SmsManager具体解释 实例执行效果图: 程序代 ...

  9. Android下调用收发短信邮件等

    Android下调用收发短信邮件等 1,调web浏览器Uri myBlogUri = Uri.parse("http://xxxxx.com");returnIt = new In ...

随机推荐

  1. Scala主构造器、私有构造器、构造器重载

    Scala中的主构造器跟在定义类的时候声明类名之后 如下: class scala(arg : String) { // } private[this] 修饰该字段只能被当前所对应的对象所访问,其他对 ...

  2. J2SE知识点摘记(二十三)

    我们简单介绍一下这个接口: 1.4.3        Comparable 接口 在 java.lang 包中,Comparable 接口适用于一个类有自然顺序的时候.假定对象集合是同一类型,该接口允 ...

  3. J2SE知识点摘记(七)

    1.        枚举的用法 enum 枚举名{枚举值表标};例子:"enum weekday{sun,mon,tue,wed,fri,sat}a,b,c;" For循环语句中使 ...

  4. 【Windows 8 Store App】学习一:获取设备信息

    原文http://www.cnblogs.com/java-koma/archive/2013/05/22/3093306.html 通常情况下我们需要知道用户设备的一些信息:deviceId, os ...

  5. C,C++经典(程序、错误程序)

    1,程序 未执行完错误的return 0

  6. javascript数组排序-----1

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. python-操作hive

    python访问hive2 HiveServer2为客户端在远程执行hive查询提供了接口,通过Thrift RPC来实现,还提供了多用户并发和认证功能.目前使用python的用户可以通过pyhs2这 ...

  8. ReactNative遇到的一些坑

    1. 如果通过一个url进行下载上传操作,这个url中包含有中文的话,一定要记得将这个url转为URLEncode编码. 如: encodeURI('http://test/有中文.doc'); 2. ...

  9. JSP、JSTL、EL

    Jsp Jsp内置对象: Response.Request.Page.pageContext.session.out.application.exception.config Out->jspW ...

  10. Dynamics CRM 2013 初体验(1):系统的安装

    最近收到Microsoft的Dynamics CRM 2013 beta测试邀请,终于让我掀开了它神秘的面纱.自从去年的Dynamics CRM 2012 December补丁包发布后,系统就添加了很 ...