之前一直以为是应用本身在对图标进行修改,看了源码之后发现其实主要的工作并不是应用自己完成的,主要的工作在是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. 搞了一个独立博客,请各位光临pingworld.cn

    嘿嘿,每次在一个大网站上建立自己的博客后总是没有动力持续更新下去,回想其原因很大是因为没有一个自己的地盘,懒得维护!还有一个原因就是自己也没有什么干货值得跟大家分享. 随着工作的时日见长,有了各种各样 ...

  2. MapReduce ---- TD-IDF

    1.TF-IDF TF-IDF(term frequency/inverse document frequency) 的概念被公认为信息检索中最重要的发明.描述单个term与特定document的相关 ...

  3. Cocos2d-x 学习之路------(CCCallfunc 系列)

    CCCallFunc,CCCallFuncN,CCCallFuncND,CCCallFuncO类都是调用函数来执行动作,他们的使用只是局限于他们调用的的函数的参数不同而不同 CCCallFunc的回调 ...

  4. Telnet RFC

    http://tools.ietf.org/html/rfc857 http://www.faqs.org/rfcs/rfc854.html 不错: http://blog.csdn.net/chao ...

  5. ICON图标文件解析

    icon是一种图标格式,用于系统图标.软件图标等,这种图标扩展名为*.icon.*.ico.常见的软件或windows桌面上的那些图标一般都是ICON格式的. ICON文件格式比较简单,包含文件头段. ...

  6. shell脚本中常见的一些特殊符号和作用详解

    这篇文章主要介绍了shell脚本中常见的一些特殊符号和它的作用详解,总结的很简洁,容易看懂,需要的朋友可以参考下   在编写Shell脚本时,我们需要会用到各种各样的特殊符号,通过这些特殊符号可以使我 ...

  7. NOI2015 Day2

    NOI2015 Day2 荷马史诗 题目描述:给出\(n\)个数,要求\(n\)个\(k\)进制数来对应这\(k\)个数(允许有前导零),\(n\)个\(k\)进制数互不为前缀,求\(n\)个数乘以对 ...

  8. Effective Objective-C 读书笔记

    一本不错的书,给出了52条建议来优化程序的性能,对初学者有不错的指导作用,但是对高级阶段的程序员可能帮助不是很大.这里贴出部分笔记: 第2条: 使用#improt导入头文件会把头文件的内容全部暴露到目 ...

  9. Alisha’s Party(队列)

    Alisha’s Party Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) ...

  10. libcurl使用示例

    远程下载文件,并将http 头信息存放内存中以及文件大小等相关信息: #include <stdio.h> #include <curl/curl.h> #include &l ...