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>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值