Android 8.0之前原生是不支持應(yīng)用角標(biāo)的徐勃,是各個手機廠商自己在系統(tǒng)中實現(xiàn)的應(yīng)用角標(biāo)禁添,并且部分廠商提供了設(shè)置的方式败匹,所以需要對各個廠商的系統(tǒng)進(jìn)行適配寨昙。
GitHub地址:https://github.com/jimmysuncpt/AppBadge
下面是針對各個手機廠商的系統(tǒng)進(jìn)行適配的原理讥巡。
0 公用方法
在下面的實現(xiàn)代碼中,有很多公用的代碼舔哪,我們提取出來一些公用方法欢顷,代碼如下:
private static String getLauncherClassName(Context context) {
ComponentName launchComponent = getLauncherComponentName(context);
if (launchComponent == null) {
return "";
} else {
return launchComponent.getClassName();
}
}
private static ComponentName getLauncherComponentName(Context context) {
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(context
.getPackageName());
if (launchIntent != null) {
return launchIntent.getComponent();
} else {
return null;
}
}
1 小米(支持)
小米在MIUI6及以上版本中是通過發(fā)送通知來設(shè)置角標(biāo)的。當(dāng)APP向通知欄發(fā)送了一條通知 (通知不帶進(jìn)度條并且用戶可以刪除的)捉蚤,那么桌面APP icon角標(biāo)就會顯示1抬驴。此時app顯示的角標(biāo)數(shù)是和通知欄里app發(fā)送的通知數(shù)對應(yīng)的,即向通知欄發(fā)送了多少通知就會顯示多少角標(biāo)缆巧。
可以通過反射機制來定義每次通知的消息個數(shù)布持,應(yīng)用的角標(biāo)數(shù)為每條通知定義的通知個數(shù)的總和,如下圖所示:
我們發(fā)送了兩條通知盅蝗,一條通知為5個消息鳖链,一條為10個消息姆蘸,應(yīng)用角標(biāo)顯示為15個墩莫。此時,如果用戶點擊或移除掉5條消息的那個通知逞敷,應(yīng)用角標(biāo)會變成10狂秦。
另外,僅在APP在后臺時收到通知會顯示角標(biāo)推捐,而APP在前臺時不會顯示裂问,APP被殺掉后通知及角標(biāo)消失。
實現(xiàn)代碼:
public static boolean setNotificationBadge(int count, Context context) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService
(Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
return false;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 8.0之后添加角標(biāo)需要NotificationChannel
NotificationChannel channel = new NotificationChannel("badge", "badge",
NotificationManager.IMPORTANCE_DEFAULT);
channel.setShowBadge(true);
notificationManager.createNotificationChannel(channel);
}
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(context, "badge")
.setContentTitle("應(yīng)用角標(biāo)")
.setContentText("您有" + count + "條未讀消息")
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap
.ic_launcher))
.setSmallIcon(R.mipmap.ic_launcher_round)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setChannelId("badge")
.setNumber(count)
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL).build();
// 小米
try {
Field field = notification.getClass().getDeclaredField("extraNotification");
Object extraNotification = field.get(notification);
Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int
.class);
method.invoke(extraNotification, count);
} catch (Exception e) {
e.printStackTrace();
}
notificationManager.notify(notificationId++, notification);
return true;
}
參考鏈接:
https://dev.mi.com/docs/appsmarket/technical_docs/badge/
https://dev.mi.com/console/doc/detail?pId=939
2 華為(支持)
可以通過ContentResolver方法直接設(shè)置應(yīng)用角標(biāo)牛柒,且應(yīng)用在前臺和被殺掉后仍可顯示堪簿。
實現(xiàn)代碼:
private static boolean setHuaweiBadge(int count, Context context) {
try {
String launchClassName = getLauncherClassName(context);
if (TextUtils.isEmpty(launchClassName)) {
return false;
}
Bundle bundle = new Bundle();
bundle.putString("package", context.getPackageName());
bundle.putString("class", launchClassName);
bundle.putInt("badgenumber", count);
context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher" +
".settings/badge/"), "change_badge", null, bundle);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
參考鏈接:
https://developer.huawei.com/consumer/cn/devservice/doc/30802
3 OPPO(不支持)
舊款的OPPO手機有兩種方法設(shè)置,沒有測試機測試皮壁,不知道是否可行椭更,實現(xiàn)代碼如下:
private static boolean setOPPOBadge(int count, Context context) {
try {
Bundle extras = new Bundle();
extras.putInt("app_badge_count", count);
context.getContentResolver().call(Uri.parse("content://com.android.badge/badge"),
"setAppBadgeCount", String.valueOf(count), extras);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static boolean setOPPOBadge2(int count, Context context) {
try {
Intent intent = new Intent("com.oppo.unsettledevent");
intent.putExtra("packageName", context.getPackageName());
intent.putExtra("number", count);
intent.putExtra("upgradeNumber", count);
PackageManager packageManager = context.getPackageManager();
List<ResolveInfo> receivers = packageManager.queryBroadcastReceivers(intent, 0);
if (receivers != null && receivers.size() > 0) {
context.sendBroadcast(intent);
} else {
Bundle extras = new Bundle();
extras.putInt("app_badge_count", count);
context.getContentResolver().call(Uri.parse("content://com.android.badge/badge"),
"setAppBadgeCount", null, extras);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
新款的OPPO僅支持內(nèi)置應(yīng)用、微信和QQ顯示角標(biāo)蛾魄,若要使用角標(biāo)功能虑瀑,必須提交申請,審核通過了才能開放滴须,官方給的具體審核標(biāo)準(zhǔn)如下:
申請角標(biāo)接入規(guī)則(應(yīng)用必須適配OPPO手機舌狗,保證角標(biāo)功能測試通過)
a) 系統(tǒng)應(yīng)用;
b) 國內(nèi)外各區(qū)域用戶量排名Top5的三方即時通訊類應(yīng)用扔水,且只允許顯示即時通信消息類通知(如QQ痛侍、微信、facebook魔市、line)恋日;
c) OPPO公司內(nèi)部費商業(yè)化及運營性質(zhì)的辦公類型即時通信應(yīng)用(如Teamtalk)膀篮;
d) 國內(nèi)外郵件類應(yīng)用(各區(qū)域各屬于用戶量第一梯隊的應(yīng)用)。
4 vivo(不支持)
舊款的vivo手機實現(xiàn)代碼:
private static boolean setVivoBadge(int count, Context context) {
try {
String launcherClassName = getLauncherClassName(context);
if (TextUtils.isEmpty(launcherClassName)) {
return false;
}
Intent intent = new Intent("launcher.action.CHANGE_APPLICATION_NOTIFICATION_NUM");
intent.putExtra("packageName", context.getPackageName());
intent.putExtra("className", launcherClassName);
intent.putExtra("notificationNum", count);
context.sendBroadcast(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
新款的手機也不可行了岂膳,如下圖:
5 三星(支持)
可以通過廣播機制直接設(shè)置應(yīng)用角標(biāo)澳窑,且應(yīng)用在前臺和被殺掉后仍可顯示。
實現(xiàn)代碼:
private static boolean setSamsungBadge(int count, Context context) {
try {
String launcherClassName = getLauncherClassName(context);
if (TextUtils.isEmpty(launcherClassName)) {
return false;
}
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", context.getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
6 魅族(不支持)
官方不支持唉铜。
7 360(不支持)
目前可能僅支持系統(tǒng)應(yīng)用芜辕、微信和QQ,不支持支付寶簸喂,未找到設(shè)置角標(biāo)相關(guān)文檔毙死。
8 錘子(不支持)
目前僅支持系統(tǒng)應(yīng)用和微信,甚至不支持QQ喻鳄。
9 努比亞(不支持)
目前僅支持系統(tǒng)應(yīng)用扼倘。
10 金立(不支持)
找不到相關(guān)文檔。
11 樂視(支持除呵,無法自定義數(shù)目)
僅支持通過通知設(shè)置角標(biāo)再菊,且角標(biāo)數(shù)字為收到通知的條數(shù),無法自定義角標(biāo)數(shù)目颜曾。
12 聯(lián)想ZUK(支持)
實現(xiàn)代碼:
private static boolean setZukBadge(int count, Context context) {
try {
Bundle extra = new Bundle();
ArrayList<String> ids = new ArrayList<>();
// 以列表形式傳遞快捷方式id纠拔,可以添加多個快捷方式id
// ids.add("custom_id_1");
// ids.add("custom_id_2");
extra.putStringArrayList("app_shortcut_custom_id", ids);
extra.putInt("app_badge_count", count);
Uri contentUri = Uri.parse("content://com.android.badge/badge");
Bundle bundle = context.getContentResolver().call(contentUri, "setAppBadgeCount", null,
extra);
return bundle != null;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
參考鏈接:
http://developer.zuk.com/detail/12
13 中興(不支持)
目前僅支持系統(tǒng)應(yīng)用和微信,甚至不支持QQ泛豪。
14 HTC(支持)
可通過廣播機制直接設(shè)置角標(biāo)稠诲。
實現(xiàn)代碼:
private static boolean setHTCBadge(int count, Context context) {
try {
ComponentName launcherComponentName = getLauncherComponentName(context);
if (launcherComponentName == null) {
return false;
}
Intent intent1 = new Intent("com.htc.launcher.action.SET_NOTIFICATION");
intent1.putExtra("com.htc.launcher.extra.COMPONENT", launcherComponentName
.flattenToShortString());
intent1.putExtra("com.htc.launcher.extra.COUNT", count);
context.sendBroadcast(intent1);
Intent intent2 = new Intent("com.htc.launcher.action.UPDATE_SHORTCUT");
intent2.putExtra("packagename", launcherComponentName.getPackageName());
intent2.putExtra("count", count);
context.sendBroadcast(intent2);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
15 諾基亞(支持)
可通過通知設(shè)置角標(biāo)數(shù)量,和小米類似诡曙,不同之處是只要通知欄的通知一直存在臀叙,數(shù)字就一直存在,且應(yīng)用在前臺仍可通過通知設(shè)置价卤。
實現(xiàn)代碼:
與小米實現(xiàn)代碼相同劝萤。
16 索尼(支持)
實現(xiàn)代碼:
private static boolean setSonyBadge(int count, Context context) {
String launcherClassName = getLauncherClassName(context);
if (TextUtils.isEmpty(launcherClassName)) {
return false;
}
try {
//官方給出方法
ContentValues contentValues = new ContentValues();
contentValues.put("badge_count", count);
contentValues.put("package_name", context.getPackageName());
contentValues.put("activity_name", launcherClassName);
SonyAsyncQueryHandler asyncQueryHandler = new SonyAsyncQueryHandler(context
.getContentResolver());
asyncQueryHandler.startInsert(0, null, Uri.parse("content://com.sonymobile.home" +
".resourceprovider/badge"), contentValues);
return true;
} catch (Exception e) {
try {
//網(wǎng)上大部分使用方法
Intent intent = new Intent("com.sonyericsson.home.action.UPDATE_BADGE");
intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", count > 0);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME",
launcherClassName);
intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", String
.valueOf(count));
intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context
.getPackageName());
context.sendBroadcast(intent);
return true;
} catch (Exception e1) {
e1.printStackTrace();
return false;
}
}
}
static class SonyAsyncQueryHandler extends AsyncQueryHandler {
SonyAsyncQueryHandler(ContentResolver cr) {
super(cr);
}
}
參考鏈接:
https://github.com/sonyxperiadev/home-badge
17 原生Android(部分支持,無法直接顯示數(shù)目)
Android 8.0及之后的版本Google官方API支持通過發(fā)送系統(tǒng)通知的方式設(shè)置應(yīng)用角標(biāo)荠雕,但是不支持顯示數(shù)量稳其,而是一個小點兒,如下圖所示炸卑。
在發(fā)送通知時可以設(shè)置消息數(shù)量既鞠,部分手機在長按圖標(biāo)時會顯示所有通知設(shè)置的數(shù)量的總和。如上圖所示盖文,該示例中我們發(fā)送了兩條通知嘱蛋,一條設(shè)置消息數(shù)量為88,一條為12,所以總和顯示為100洒敏。
實現(xiàn)代碼:
與小米實現(xiàn)代碼相同龄恋。
參考鏈接:
https://developer.android.com/training/notify-user/badges