之前一直以为是应用本身在对图标进行修改,看了源码之后发现其实主要的工作并不是应用自己完成的,主要的工作在是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. 51cto那些技术专题们

    Nginx配置与应用详解 UML(Unified Modeling Language,统一建模语言) 架构师的成长历程 python python book ruby html5 不可不知的Linux ...

  2. Oracle EBS-SQL (BOM-1):检查供应类型错误.sql

    --检查供应类型错误 SELECT MSI.SEGMENT1                物料编码, MSI.DESCRIPTION             物料描述, DECODE(MSI.WIP ...

  3. iOS开发之主题皮肤

    iOS开发之主题皮肤 分类: [iOS]  最近在开发一款[公交应用],里面有个模块涉及到主题设置,这篇文章主要谈一下个人的做法. 大概的步骤如下: (1):整个应用依赖于一个主题管理器,主题管理器根 ...

  4. 弹飞DZY(思维,打表,还没过全,先放着)

    弹飞DZYDescription某天,机智的ZZC发明了一种超级弹力装置,为了在他的朋友DZY面前显摆,他邀请DZY一起玩个游戏.游戏一开始,ZZC在地上沿着一条直线摆上n个装置,每个装置设定初始弹力 ...

  5. Android Navigation Drawer(导航抽屉)

    Google I/O 2013 Android 更新了Support库,新版本的Support库中新加入了几个比较重要的功能. 添加 DrawerLayout 控件,支持创建  Navigation ...

  6. CodeForces Round #179 (295A) - Greg and Array 一个线段树做两次用

    线段树的区间更新与区间求和...一颗这样的线段树用两次... 先扫描1~k...用线段树统计出每个操作执行的次数... 那么每个操作就变成了 op. l  , op.r , op.c= times* ...

  7. Jade 报错

    今天写jade的时候遇到一个问题 Invalid indentation,you can use tabs or spaces but not both问题 经过查证原来是 在jade模板中 同时存在 ...

  8. 蓝桥杯试题集【Java】

    一.Fibonacci数列 问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. ...

  9. js获取当前年月日

    function GetDate(){ var now = new Date(); var year = now.getFullYear();        //年var month = now.ge ...

  10. VS2013 快捷键乱掉如何修改回来

    比如 CTRL+E+C =注释 F6=重新生成解决方案 CTRL+D+Q=运行时快速监视 工具-->选项-->环境-->键盘-->应用以下其他键盘映射方案,下拉选择 Visua ...