之前一直以为是应用本身在对图标进行修改,看了源码之后发现其实主要的工作并不是应用自己完成的,主要的工作在是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. jQuery 之 $.get、$.post、$.getJSON、$.ajax

    对 JSTL标签 加以巩固,同时在自己的博客中与之分享: http://app.yinxiang.com/shard/s20/sh/76feadb0-957a-40bc-895d-4d28025ce2 ...

  2. 製程能力介紹(SPC introduction) ─ Cpk之製程能力解釋

    Cpk之製程能力解釋 Cpk就是綜合考慮精度與準度的製程能力指標. Cpk=(1-Ck)‧Cp 也就是在考慮Cp的同時,再考慮乘上一個(1-Ck)係數,去彌補Cp之不足,此係數最大時為1,也就是(Ck ...

  3. Microsoft Office 2010 Pro VOL简体中文正式版

    网友们期待的Microsoft Office Professional Plus 2010 VOL简体中文正式版.所谓的“VOL”,即是Volume Licensing for Organizatio ...

  4. C++中输入输出流及文件流操作笔记

    1.流的控制 iomanip          在使用格式化I/O时应包含此头文件.    stdiostream   用于混合使用C和C + +的I/O机制时,例如想将C程序转变为C++程序 2.类 ...

  5. MongoDB存储时间

    之前一篇博客C++的时间中提到了MongoDB保存时间类型数据可以使用timestamp类型. 不过在实际编程过程中,发现保存timestamp容易,读取难.MongoDB C++的这方面的例子还没有 ...

  6. docker 数据映射方案

    docker run -itd -v /data/:/data1 centos bash // -v 用来指定挂载目录, :前面的/data为本地目录,:后面的/data1 为容器里的目录: dock ...

  7. chrome 下的 proxy 插件安装

    Install “Proxy SwitchyOmega” extensions for chrome.

  8. C++四种强制类型转换详解

    什么是类型转换? 类型转换的含义是通过改变一个变量的类型为别的类型从而改变该变量的表示方式.为了类型转换一个简单对象为另一个对象你会使用传统的类型转换操作符. C与C++的类型转换 //C中: //复 ...

  9. 深入理解Linux网络技术内幕——中断与网络驱动程序

    接收到帧时通知驱动程序     在网络环境中.设备(网卡)接收到一个数据帧时,须要通知驱动程序进行处理. 有一下几种通知机制: 轮询:     内核不断检查设备是否有话要说.(比較耗资源,但在一些情况 ...

  10. Date Time Picker控件

    Step1 在界面中添加一个Date Time Picker控件,ID为:IDC_DATETIMEPICKER1 Step2 该控件关联变量 CDateTimeCtrl m_dateCtrl; Ste ...