`

Android 类似未读短信图标显示数字效果的分析

阅读更多

 

  之前一直以为是应用本身在对图标进行修改,看了源码之后发现其实主要的工作并不是应用自己完成的,主要的工作在是launcher里面完成的.
关于系统里面类似未读短信的具体处理流程如下,

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

 实现过程分析
Intent.java(系统增加3个常量)
frameworks/base/core/java/android/content/Intent.java
//状态   
public static final String MTK_ACTION_UNREAD_CHANGED = "com.mediatek.action.UNREAD_CHANGED";
//应用名称    
public static final String MTK_EXTRA_UNREAD_COMPONENT = "com.mediatek.intent.extra.UNREAD_COMPONENT";
//数量
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
/** M: send new Calls broadcast to luancher to update unread icon @{ */
    public static final void notifyNewCallsCount(SQLiteDatabase db, Context context) {
        Cursor c = null;
       …..
        //send count=0 to clear the unread icon
        if (newCallsCount >= 0) { //有新的来电数量
            Intent newIntent = new Intent(Intent.MTK_ACTION_UNREAD_CHANGED);
            newIntent.putExtra(Intent.MTK_EXTRA_UNREAD_NUMBER, newCallsCount);
            newIntent.putExtra(Intent.MTK_EXTRA_UNREAD_COMPONENT, new ComponentName(Constants.CONTACTS_PACKAGE,
                    Constants.CONTACTS_DIALTACTS_ACTIVITY));
            context.sendBroadcast(newIntent); //发送对应的广播
            android.provider.Settings.System.putInt(context.getContentResolver(), Constants.CONTACTS_UNREAD_KEY, Integer
                    .valueOf(newCallsCount));
        }
    }
 MTKUnreadLoader.java(Launcher接收到应用发送的广播,进行判断改应用是否可以显示有未处理事件的图标)
packages/apps/Launcher2/src/com/android/launcher2/MTKUnreadLoader.java
public void onReceive(final Context context, final Intent intent) {
        final String action = intent.getAction();
            //过滤广播
        if (Intent.MTK_ACTION_UNREAD_CHANGED.equals(action)) {
            final ComponentName componentName = (ComponentName) intent.getExtra(Intent.MTK_EXTRA_UNREAD_COMPONENT);
            final int unreadNum = intent.getIntExtra(Intent.MTK_EXTRA_UNREAD_NUMBER, -1);

            if (mCallbacks != null && componentName != null && unreadNum != -1) {
                //判断是否支持该功能
                final int index = supportUnreadFeature(componentName);
                if (index >= 0) { //支持
                    boolean ret = setUnreadNumberAt(index, unreadNum);
                    if (ret) {
                        final UnreadCallbacks callbacks = mCallbacks.get();
                        if (callbacks != null) {
                            callbacks.bindComponentUnreadChanged(componentName, unreadNum);
                        }
                    .........
    }
 LauncherApplication.java(Launcher注册对应的广播接收器)
packages/apps/Launcher2/src/com/android/launcher2/LauncherApplication.java
public void onCreate() {
       ........
        /// M: register unread broadcast.
        if (FeatureOption.MTK_LAUNCHER_UNREAD_SUPPORT) {
            mUnreadLoader = new MTKUnreadLoader(getApplicationContext());
            // Register unread change broadcast.
            filter = new IntentFilter();
            filter.addAction(Intent.MTK_ACTION_UNREAD_CHANGED);
            registerReceiver(mUnreadLoader, filter); //注册对应的广播接收器
        }
       ..............
    }
 MTKUnreadLoader.java(处理应用的图标显示未处理事件的数字)
packages/apps/Launcher2/src/com/android/launcher2/MTKUnreadLoader.java
static void drawUnreadEventIfNeed(Canvas canvas, View icon) {
        ItemInfo info = (ItemInfo)icon.getTag();
        if (info != null && info.unreadNum > 0) { //判断未处理事件数量
            Resources res = icon.getContext().getResources();
           ..........
            if (info.unreadNum > Launcher.MAX_UNREAD_COUNT) {
                unreadTextNumber = String.valueOf(Launcher.MAX_UNREAD_COUNT);
                unreadTextPlusPaint.getTextBounds(unreadTextPlus, 0, unreadTextPlus.length(), unreadTextPlusBounds);
            } else {
                unreadTextNumber = String.valueOf(info.unreadNum);
            }
            unreadTextNumberPaint.getTextBounds(unreadTextNumber, 0, unreadTextNumber.length(), unreadTextNumberBounds);
            int textHeight = unreadTextNumberBounds.height();
            int textWidth = unreadTextNumberBounds.width() + unreadTextPlusBounds.width();

            // 数字的背景图
            NinePatchDrawable unreadBgNinePatchDrawable = (NinePatchDrawable)res.getDrawable(R.drawable.ic_newevents_numberindication);
           .........
            Rect unreadBgBounds = new Rect(0, 0, unreadBgWidth, unreadBgHeight);
            unreadBgNinePatchDrawable.setBounds(unreadBgBounds);

            int unreadMarginTop = 0;
            int unreadMarginRight = 0;
            if (info instanceof ShortcutInfo) { //workspace 里面的快捷方式
                if (info.container == (long)LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                    unreadMarginTop = (int)res.getDimension(R.dimen.hotseat_unread_margin_top);
                    unreadMarginRight = (int)res.getDimension(R.dimen.hotseat_unread_margin_right);
                } else if (info.container == (long)LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                    unreadMarginTop = (int)res.getDimension(R.dimen.workspace_unread_margin_top);
                    unreadMarginRight = (int)res.getDimension(R.dimen.workspace_unread_margin_right);
                } else {
                    unreadMarginTop = (int)res.getDimension(R.dimen.folder_unread_margin_top);
                    unreadMarginRight = (int)res.getDimension(R.dimen.folder_unread_margin_right);
                }
            } else if (info instanceof FolderInfo) { //文件夹
                if (info.container == (long)LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                    unreadMarginTop = (int)res.getDimension(R.dimen.hotseat_unread_margin_top);
                    unreadMarginRight = (int)res.getDimension(R.dimen.hotseat_unread_margin_right);
                } else if (info.container == (long)LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                    unreadMarginTop = (int)res.getDimension(R.dimen.workspace_unread_margin_top);
                    unreadMarginRight = (int)res.getDimension(R.dimen.workspace_unread_margin_right);
                }
            }
            else if (info instanceof ApplicationInfo) { //all app 里面的应用icon
                unreadMarginTop = (int)res.getDimension(R.dimen.app_list_unread_margin_top);
                unreadMarginRight = (int)res.getDimension(R.dimen.app_list_unread_margin_right);
            }

            int unreadBgPosX = icon.getScrollX() + icon.getWidth() - unreadBgWidth - unreadMarginRight;
            int unreadBgPosY = icon.getScrollY() + unreadMarginTop;

            canvas.save();
            canvas.translate(unreadBgPosX, unreadBgPosY);

            unreadBgNinePatchDrawable.draw(canvas);

            /// M: Draw unread text.
            Paint.FontMetrics fontMetrics = unreadTextNumberPaint.getFontMetrics();
            if (info.unreadNum > Launcher.MAX_UNREAD_COUNT) {
                canvas.drawText(unreadTextNumber,
                                (unreadBgWidth - unreadTextPlusBounds.width()) / 2,
                                (unreadBgHeight + textHeight) / 2,
                                unreadTextNumberPaint);
                canvas.drawText(unreadTextPlus,
                                (unreadBgWidth + unreadTextNumberBounds.width()) / 2,
                                (unreadBgHeight + textHeight) / 2 + fontMetrics.ascent / 2,
                                unreadTextPlusPaint);
            } else {
               .....
        }
    }
 unread_support_shortcuts.xml(配置哪些应用可以显示待处理的事件)
packages/apps/Launcher2/res/xml/unread_support_shortcuts.xml
<unreadshortcuts xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher">
<!--电话-->
    <shortcut
        launcher:unreadPackageName="com.android.contacts"
        launcher:unreadClassName="com.android.contacts.activities.DialtactsActivity"
        launcher:unreadType="0"
        launcher:unreadKey="com_android_contacts_mtk_unread"
     />
<!--短信-->
     <shortcut
        launcher:unreadPackageName="com.android.mms"
        launcher:unreadClassName="com.android.mms.ui.BootActivity"
        launcher:unreadType="0"
        launcher:unreadKey="com_android_mms_mtk_unread"
     />
<!--邮件-->
     <shortcut
        launcher:unreadPackageName="com.android.email"
        launcher:unreadClassName="com.android.email.activity.Welcome"
        launcher:unreadType="0"
        launcher:unreadKey="com_android_email_mtk_unread"
     />
    ................
</unreadshortcuts>
 

 
  • 大小: 16.8 KB
  • 大小: 4.2 KB
4
7
分享到:
评论

相关推荐

    android实现类似在短信图标右上角显示短信个数的效果

    标题提到的"android实现类似在短信图标右上角显示短信个数的效果"就是一种常见的用户体验设计,它能有效吸引用户的注意力并提示他们有未处理的内容。 要实现这个功能,通常会用到通知栏通知、自定义ViewGroup或者第...

    Android 桌面图标右上角显示未读消息数字

    在Android系统中,桌面图标右上角显示未读消息数字是一项增强用户通知体验的功能,尤其对于消息类应用来说,能够快速提示用户有多少未查看的消息。然而,原生Android系统并不直接支持这一特性,开发者需要依赖第三方...

    android 桌面快捷键未读的消息数目,未接来电,短信提醒那个右上角的数字

    当这个Activity关联的Intent有未处理的通知时,系统会在快捷方式上显示未读计数。 - 创建快捷方式可以通过`ShortcutManager`(Android Nougat及以上版本)或第三方库,如`androidx.core.app.ShortcutManagerCompat`...

    android_BadgeView

    在Android开发中,BadgeView是一种常见且实用的设计,它通常用于在应用图标或者视图上显示一个小红点或数字,以提示用户有未读的消息、更新或其他重要通知。"android_BadgeView"项目就是这样一个实现,它允许开发者...

    仿短信消息动态提醒

    在Android平台上,这通常涉及到监听系统消息广播,当接收到新的短信时,通过自定义View或者使用第三方库(如Android-ActionBadger或ViewBadger)来显示未读消息的计数。这些库能够方便地集成到应用中,避免开发者从...

    APK信息显示Messenge_v2.4.7z

    在Android中,每个APK都必须经过数字签名,以确保其未被篡改并且来自可信的源。签名包含了开发者的信息,验证了应用的完整性和来源。用户可以通过"APK Messenger"查看签名信息,以确认应用是否来自于正规开发者,...

    安卓短信彩信相关相关-自定义CheckBox短信验证码自动填写.rar

    应用通常会发送一条包含随机数字或字母组合的短信到用户手机,用户需要将接收到的验证码输入到应用内以完成验证。Android提供了`SmsManager`类用于发送和接收短信,但需要注意权限问题,如需读取短信,需要申请`READ...

    安卓android课程设计报告 (2).pdf

    通过以上分析,该课程设计报告详尽地描述了一个安卓天气预报应用的开发过程,涵盖了从需求分析到系统实现的各个阶段,为类似应用的开发提供了参考。在实际开发中,开发者需要关注用户界面的易用性,数据获取的准确性...

    盘点Android设备10个不为人熟悉的操作技巧

    1. 快捷输入技巧:在Android设备上进行英文输入时,常常需要切换到数字和符号键盘,这显得有些繁琐。一个简单的方法是长按句号键,会弹出一个小框,用户可以直接从弹出的菜单中滑动选择想要的符号,无需切换键盘。...

    badgeCount:android 徽章计数

    在Android开发中,"badgeCount"通常指的是应用图标右上角的小红点或者数字,用于显示未读消息或更新的数量,这种功能也被称作“徽章计数”。它是一种非常常见的用户界面元素,用于提示用户有未处理的通知或消息,如...

    来电通 安卓官方正式版

    在骚扰拦截的模块图标上,更有小数字标签提示你拦截到多少骚扰的短信和电话哦。 5、趣味搜索,带给你全新的搜索体验 功能优化,体验更佳 1、信息会话界面,字体大小随心换 信息设置界面新增 信息字体大小 设置项,...

    APK信息查看器.rar

    APK是Android应用程序的安装包文件格式,类似于Windows操作系统中的.exe可执行文件。它包含了应用的所有代码、资源、图标、权限声明以及必要的元数据。通过APK,用户可以在Android设备上安装和运行应用程序。 应用...

    HTC电话界面WIN32程序

    此外,它可能还集成了短信的撰写、阅读和管理功能,为用户提供一个类似于真实手机的使用体验。 【标签】"HTC"是指宏达电(HTC Corporation),是一家知名的台湾智能手机制造商,以其Android和Windows Mobile设备而...

    JAVA上百实例源码以及开源项目源代码

    Java绘制图片火焰效果 1个目标文件 摘要:Java源码,图形操作,火焰效果 Java绘制图片火焰效果,源代码相关注释:前景和背景Image对象、Applet和绘制火焰的效果的Image对象、Applet和绘制火焰的效果的Graphics对象、...

    CallFavorites

    在这个数字化时代,通信效率的重要性不言而喻,CallFavorites通过优化这一过程,极大地提升了用户的体验。本文将对CallFavorites进行详细的探讨,揭示其背后的技术原理和实际应用场景。 首先,从项目名称...

Global site tag (gtag.js) - Google Analytics