android 桌面角標(biāo)能力
小米
測(cè)試結(jié)果:支持角標(biāo)能力捆憎,最大值為9(官方數(shù)據(jù))侮邀, (實(shí)測(cè)可以到note4 顯示為9999...mix2s 顯示為99999....)
條件:需要向狀態(tài)欄發(fā)送一條通知,進(jìn)入APP則角標(biāo)消失撩满,抹除通知?jiǎng)t角標(biāo)也消失 习蓬,在端內(nèi)時(shí)不顯示
首先打開應(yīng)用通知設(shè)置頁(yè)面攒霹,在”設(shè)置-通知管理“里點(diǎn)擊應(yīng)用忧陪,查看”顯示桌面圖標(biāo)角標(biāo)“開關(guān)是否開啟扣泊。大部分應(yīng)用默認(rèn)是關(guān)閉狀態(tài)。
資料:
https://dev.mi.com/console/doc/detail?pId=939
https://dev.mi.com/console/doc/detail?pId=2321
華為(榮耀)
測(cè)試結(jié)果:支持角標(biāo)能力
條件 EMUI4.1 及以上及以上手機(jī) 打開通知和角標(biāo) 選擇顯示為數(shù)字角標(biāo)
最多顯示99 超過99顯示為99+
官網(wǎng)資料需要添加 華為權(quán)限 (實(shí)測(cè)不加也能顯示) 代碼需要try處理 避免無角標(biāo)能力導(dǎo)致崩潰
<uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE"/>
資料:
https://developer.huawei.com/consumer/cn/doc/development/Corner-Guides/30802
OPPO
結(jié)論:需要上架嘶摊,并且向oppo申請(qǐng)功能,還有適配才有评矩。
資料:https://open.oppomobile.com/bbs/forum.php?mod=viewthread&tid=1711
VIVO
結(jié)論:需要向VIVO申請(qǐng)接入“桌面圖標(biāo)角標(biāo)”叶堆,但是官方?jīng)]放開
資料:https://dev.vivo.com.cn/documentCenter/doc/459
三星
結(jié)論:(網(wǎng)上資料)三星S9+ 以上 需和通知的方式結(jié)合才能顯示角標(biāo),
三星 Galaxy S6 :可直接設(shè)置斥杜。設(shè)置為0 則角標(biāo)消失
顯示數(shù)量上限不限 虱颗,暫無其他三星手機(jī)進(jìn)行測(cè)試。
魅族
結(jié)論:官方不支持
總結(jié)
華為 小米 三星 部分原生系統(tǒng) 可以支持蔗喂。 oppo vivo 魅族 暫時(shí)不支持
代碼編寫
小米角標(biāo)代碼
這里簡(jiǎn)單備注一下忘渔,小米在進(jìn)入app內(nèi)的時(shí)候,角標(biāo)消息會(huì)清除缰儿。所以在處理小米的時(shí)候畦粮,在應(yīng)用在后臺(tái)的時(shí)候 應(yīng)該再次將角標(biāo)顯示出來。
/**
* @param markNumber 顯示數(shù)量
* 顯示小米角標(biāo)
* 小米官網(wǎng) 角標(biāo)代碼
* https://dev.mi.com/console/doc/detail?pId=2321
* https://dev.mi.com/console/doc/detail?pId=939
* 顯示小米角標(biāo)數(shù)量
*/
private void showXiaoMiDeskMark(int markNumber) {
try {
markNumber = detectMarkNumber(markNumber);
//因?yàn)樾∶赘聰?shù)量需要發(fā)送通知 當(dāng)是0的 時(shí)候就沒必要進(jìn)行發(fā)送了
if (markNumber == 0) {
return;
}
NotificationManager notificationManager = getNotificationManager();
if (notificationManager == null) {
return;
}
// 通知8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(XIAOMI, XIAOMI_CHANLE_NAME, NotificationManager.IMPORTANCE_DEFAULT);
channel.setShowBadge(true);
notificationManager.createNotificationChannel(channel);
}
Intent intent = new Intent(getContext(), HomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, 0);
Notification notification = new NotificationCompat.Builder(getContext(), XIAOMI)
.setContentTitle("棉花糖消息")
.setContentText("您有" + markNumber + "條IM未讀消息")
.setLargeIcon(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.icon))
.setSmallIcon(R.drawable.icon)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setChannelId(XIAOMI)
.setNumber(markNumber)
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL).build();
//小米角標(biāo)代碼
Field field = notification.getClass().getDeclaredField("extraNotification");
Object extraNotification = field.get(notification);
Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int.class);
method.invoke(extraNotification, markNumber);
notificationManager.notify(NOTIFY_ID, notification);
} catch (Exception e) {
e.printStackTrace();
XLog.e("showXiaoMiDeskMark is Error");
}
}
華為角標(biāo)代碼
/**
* @param number 顯示角標(biāo)數(shù)量 0 則隱藏
* 顯示華為角標(biāo)
*/
private void showHuaWeiDeskMark(int number) {
try {
number = detectMarkNumber(number);
Bundle extra = new Bundle();
extra.putString("package", getContext().getPackageName());
extra.putString("class", getLauncherClassName());
extra.putInt("badgenumber", number);
getContext().getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, extra);
} catch (Exception ignored) {
ignored.printStackTrace();
XLog.e("showHuaWeiDeskMark is Error");
}
}
三星代碼
/**
* @param markCount 角標(biāo)值
* 顯示三星角標(biāo)
*/
private void showSamsungDeskMark(int markCount) {
try {
String launcherClassName = getLauncherClassName();
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", markCount);
intent.putExtra("badge_count_package_name", getContext().getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
getContext().sendBroadcast(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
限定最大值99
/**
* @param markNumber 要顯示的角標(biāo)數(shù)量
* @return 最大99 最小0
*/
private int detectMarkNumber(int markNumber) {
if (markNumber < 0) {
markNumber = 0;
} else if (markNumber > MARK_MAX_COUNT) {
markNumber = MARK_MAX_COUNT;
}
return markNumber;
}
通知的加入(TPNS)
端內(nèi)使用的是TPNS,也就是原來的信鴿,這部分代碼不具備通用性宣赔。如果你也使用的是TPNS可以進(jìn)行參考预麸。
通知的增加和刪減
經(jīng)測(cè)試,小米在收到通知的時(shí)候儒将,在tpns下角標(biāo)會(huì)自動(dòng)增加1吏祸,通知抹除的時(shí)候 會(huì)自動(dòng)扣除1。
華為(榮耀)在通知到來時(shí)钩蚊,角標(biāo)不會(huì)增加贡翘,但是通知?jiǎng)h除的時(shí)候則會(huì)減去1.如果 在通知?jiǎng)h除的時(shí)候 進(jìn)行角標(biāo)設(shè)置。則可能會(huì)發(fā)生砰逻,刪除一個(gè)扣2個(gè)的可能性床估。則這里進(jìn)行一個(gè)延遲操作∮詹常基本可以解決這個(gè)問題丐巫。如果有其他好的方法請(qǐng)留言。
代碼相關(guān)
TPNS里面消息的接收在XGPushBaseReceiver 類里勺美,繼承XGPushBaseReceiver递胧,
onNotificationShowedResult --消息到來展現(xiàn)
onNotificationClickedResult --消息劃掉或者點(diǎn)擊回調(diào)
我選擇封裝一個(gè)觀察者進(jìn)行處理
public class DeskNoticeObservable extends Observable {
private NoticeDeskReceiver noticeDeskReceiver;
/**
* 是否注冊(cè)
*/
private boolean isRegister;
public void init(AppCompatActivity appCompatActivity) {
if (appCompatActivity == null) {
return;
}
//監(jiān)聽下消息
if (noticeDeskReceiver == null) {
noticeDeskReceiver = new NoticeDeskReceiver() {
//tpns消息到來
@Override
public void onNotificationShowedResult(Context context, XGPushShowedResult xgPushShowedResult) {
super.onNotificationShowedResult(context, xgPushShowedResult);
notifyNoticeCount(1);
}
//tpns消息點(diǎn)擊和消除
@Override
public void onNotificationClickedResult(Context context, XGPushClickedResult xgPushClickedResult) {
super.onNotificationClickedResult(context, xgPushClickedResult);
notifyNoticeCount(-1);
}
};
}
IntentFilter intentFilter = getIntentFilter();
//添加消息監(jiān)聽
appCompatActivity.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> {
if (event == Lifecycle.Event.ON_CREATE) {
try {
appCompatActivity.registerReceiver(noticeDeskReceiver, intentFilter);
isRegister = true;
} catch (Exception e) {
XLog.e("registerReceiver: isError");
}
} else if (event == Lifecycle.Event.ON_DESTROY) {
if (noticeDeskReceiver != null) {
if (!isRegister) {
return;
}
try {
//反注銷
appCompatActivity.unregisterReceiver(noticeDeskReceiver);
isRegister = false;
//剔除掉所有觀察者
DeskNoticeObservable.this.deleteObservers();
} catch (Exception exception) {
XLog.e("unregisterReceiver: isError");
}
}
}
});
}
/**
* @param number 通知消息變更
*/
private void notifyNoticeCount(int number) {
setChanged();
notifyObservers(number);
}
/**
* @return 返回Filter
* 添加對(duì)于消息監(jiān)聽
*/
@NotNull
private IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
//添加過濾的Action值;
intentFilter.addAction("com.tencent.android.xg.vip.action.PUSH_MESSAGE");
intentFilter.addAction("com.tencent.android.xg.vip.action.FEEDBACK");
return intentFilter;
}
}
public class NoticeDeskReceiver extends XGPushBaseReceiver {
//需要覆蓋很多方法 這里就不寫出來了
}
全部代碼
通知接收器
public class NoticeDeskReceiver extends XGPushBaseReceiver {
}
通知觀察者
/**
* 通知角標(biāo) 觀察者
*/
public class DeskNoticeObservable extends Observable {
private NoticeDeskReceiver noticeDeskReceiver;
/**
* 是否注冊(cè)
*/
private boolean isRegister;
public void init(AppCompatActivity appCompatActivity) {
if (appCompatActivity == null) {
return;
}
//監(jiān)聽下消息
if (noticeDeskReceiver == null) {
noticeDeskReceiver = new NoticeDeskReceiver() {
//tpns消息到來
@Override
public void onNotificationShowedResult(Context context, XGPushShowedResult xgPushShowedResult) {
super.onNotificationShowedResult(context, xgPushShowedResult);
notifyNoticeCount(1);
}
//tpns消息點(diǎn)擊和消除
@Override
public void onNotificationClickedResult(Context context, XGPushClickedResult xgPushClickedResult) {
super.onNotificationClickedResult(context, xgPushClickedResult);
notifyNoticeCount(-1);
}
};
}
IntentFilter intentFilter = getIntentFilter();
//添加消息監(jiān)聽
appCompatActivity.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> {
if (event == Lifecycle.Event.ON_CREATE) {
try {
appCompatActivity.registerReceiver(noticeDeskReceiver, intentFilter);
isRegister = true;
} catch (Exception e) {
XLog.e("registerReceiver: isError");
}
} else if (event == Lifecycle.Event.ON_DESTROY) {
if (noticeDeskReceiver != null) {
if (!isRegister) {
return;
}
try {
//反注銷
appCompatActivity.unregisterReceiver(noticeDeskReceiver);
isRegister = false;
//剔除掉所有觀察者
DeskNoticeObservable.this.deleteObservers();
} catch (Exception exception) {
XLog.e("unregisterReceiver: isError");
}
}
}
});
}
/**
* @param number 通知消息變更
*/
private void notifyNoticeCount(int number) {
setChanged();
notifyObservers(number);
}
/**
* @return 返回Filter
* 添加對(duì)于消息監(jiān)聽
*/
@NotNull
private IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
//添加過濾的Action值赡茸;
intentFilter.addAction("com.tencent.android.xg.vip.action.PUSH_MESSAGE");
intentFilter.addAction("com.tencent.android.xg.vip.action.FEEDBACK");
return intentFilter;
}
角標(biāo)管理者
/**
* 桌面角標(biāo)
*/
public class DesktopCornerMark {
private static final String TAG = "DesktopCornerMark";
/**
* 限定最大顯示99條
*/
private static final int MARK_MAX_COUNT = 99;
/**
* 通知id
*/
private static final int NOTIFY_ID = 999533;
/**
* 小米發(fā)通知用的id
*/
private static final String XIAOMI = "xiaomi";
/**
* 小米通知渠道
*/
private static final String XIAOMI_CHANLE_NAME = "xiaomi_chanle";
/**
* 默認(rèn)啟動(dòng)頁(yè)面名稱
*/
private static final String SPLASH_CLASS_NAME = "com.wobo.live.launch.SplashActivity";
/**
* 是否在前臺(tái) 默認(rèn)在前臺(tái)
*/
private boolean isInFrontDesk = true;
/**
* 默認(rèn)緩存的角標(biāo)數(shù)量
*/
private int imMarkCount = 0;
/**
* 通知數(shù)量
*/
private int noticeMarkCount = 0;
/**
* @param number 顯示角標(biāo)數(shù)量 0 則隱藏
* 顯示華為角標(biāo)
*/
private void showHuaWeiDeskMark(int number) {
try {
number = detectMarkNumber(number);
Bundle extra = new Bundle();
extra.putString("package", getContext().getPackageName());
extra.putString("class", getLauncherClassName());
extra.putInt("badgenumber", number);
getContext().getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, extra);
} catch (Exception ignored) {
ignored.printStackTrace();
XLog.e("showHuaWeiDeskMark is Error");
}
}
/**
* @param markNumber 顯示數(shù)量
* 顯示小米角標(biāo)
* 小米官網(wǎng) 角標(biāo)代碼
* https://dev.mi.com/console/doc/detail?pId=2321
* https://dev.mi.com/console/doc/detail?pId=939
* 顯示小米角標(biāo)數(shù)量
*/
private void showXiaoMiDeskMark(int markNumber) {
try {
markNumber = detectMarkNumber(markNumber);
//因?yàn)樾∶赘聰?shù)量需要發(fā)送通知 當(dāng)是0的 時(shí)候就沒必要進(jìn)行發(fā)送了
if (markNumber == 0) {
return;
}
NotificationManager notificationManager = getNotificationManager();
if (notificationManager == null) {
return;
}
// 通知8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(XIAOMI, XIAOMI_CHANLE_NAME, NotificationManager.IMPORTANCE_DEFAULT);
channel.setShowBadge(true);
notificationManager.createNotificationChannel(channel);
}
Intent intent = new Intent(getContext(), HomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, 0);
Notification notification = new NotificationCompat.Builder(getContext(), XIAOMI)
.setContentTitle("消息提醒")
.setContentText("您有" + markNumber + "條IM未讀消息")
.setLargeIcon(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.icon))
.setSmallIcon(R.drawable.icon)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setChannelId(XIAOMI)
.setNumber(markNumber)
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL).build();
//小米角標(biāo)代碼
Field field = notification.getClass().getDeclaredField("extraNotification");
Object extraNotification = field.get(notification);
Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int.class);
method.invoke(extraNotification, markNumber);
notificationManager.notify(NOTIFY_ID, notification);
} catch (Exception e) {
e.printStackTrace();
XLog.e("showXiaoMiDeskMark is Error");
}
}
/**
* @return 通知是否打開
*/
private boolean notificationIsOpen() {
return NotificationManagerCompat.from(VLApplication.instance()).areNotificationsEnabled();
}
/**
* @return 是否支持角標(biāo)能力
* 目前調(diào)研只有 華為 小米能夠支持 Xiaomi HUAWEI
* 三星只進(jìn)行了s6測(cè)試 需要測(cè)試進(jìn)行跟進(jìn)
*/
private boolean isSupportDeskMark() {
return isHuaWei() || isXiaoMi() || isSamsung();
}
/**
* @return 獲取手機(jī)廠商
* Xiaomi HUAWEI samsung
*/
private static String getPhoneMode() {
return android.os.Build.MANUFACTURER;
}
/**
* @return 獲取啟動(dòng)頁(yè)name
*/
private String getLauncherClassName() {
ComponentName launchComponent = getLauncherComponentName();
if (launchComponent == null) {
return SPLASH_CLASS_NAME;
} else {
return launchComponent.getClassName();
}
}
private ComponentName getLauncherComponentName() {
Intent launchIntent = getContext().getPackageManager().getLaunchIntentForPackage(getContext()
.getPackageName());
if (launchIntent != null) {
return launchIntent.getComponent();
} else {
return null;
}
}
/**
* @param markNumber 要顯示的角標(biāo)數(shù)量
* @return 最大99 最小0
*/
private int detectMarkNumber(int markNumber) {
if (markNumber < 0) {
markNumber = 0;
} else if (markNumber > MARK_MAX_COUNT) {
markNumber = MARK_MAX_COUNT;
}
return markNumber;
}
/**
* 初始化
*/
public void init(AppCompatActivity appCompatActivity) {
ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() {
@Override
public void onPause(@NonNull LifecycleOwner owner) {
//在后臺(tái)
isInFrontDesk = false;
showDeskMark();
}
@Override
public void onResume(@NonNull LifecycleOwner owner) {
//在前臺(tái) 小米在前臺(tái)的時(shí)候 會(huì)清掉數(shù)據(jù) 華為做同樣處理
isInFrontDesk = true;
if (isXiaoMi()) {
cancelNotice();
}
}
});
//添加通知 如果不需要屏蔽掉就行了
registerNotice(appCompatActivity);
}
/**
* @return 是否是小米
*/
private boolean isXiaoMi() {
return "xiaomi".equals(getPhoneMode().toLowerCase());
}
/**
* @return 是否是華為
*/
private boolean isHuaWei() {
return "huawei".equals(getPhoneMode().toLowerCase());
}
/**
* @return 是否是三星
*/
private boolean isSamsung() {
return "samsung".equals(getPhoneMode().toLowerCase());
}
/**
* @return 上下文
*/
private Context getContext() {
return VLApplication.instance();
}
public void showDeskMark(int markNumber) {
//不是華為 不是小米 直接返回
if (!isSupportDeskMark() || !notificationIsOpen()) {
return;
}
imMarkCount = markNumber;
//在前臺(tái)就不更改角標(biāo) 直到在后臺(tái)為止
if (isInFrontDesk) {
return;
} else {
showDeskMark();
}
}
/**
* 顯示角標(biāo)數(shù)量
*/
private void showDeskMark() {
int markNumber = imMarkCount + noticeMarkCount;
if (markNumber < 0) {
markNumber = 0;
}
int finalMarkNumber = markNumber;
//延遲600毫秒執(zhí)行
io.reactivex.Observable.timer(600, TimeUnit.MILLISECONDS).safeSubscribe(new V3Observer<Long>() {
@Override
public void onSuccess(Long aLong) {
//華為
if (isHuaWei()) {
showHuaWeiDeskMark(finalMarkNumber);
} else if (isXiaoMi()) {
//小米 小米在通知的時(shí)候 會(huì)自己加1減1 只處理im消息通知的數(shù)量
showXiaoMiDeskMark(imMarkCount);
} else if (isSamsung()) {
//三星
showSamsungDeskMark(finalMarkNumber);
}
}
});
}
/**
* @return 獲取通知管理者
*/
private NotificationManager getNotificationManager() {
return (NotificationManager) VLApplication.instance().getSystemService
(Context.NOTIFICATION_SERVICE);
}
/**
* @param markCount 角標(biāo)值
* 顯示三星角標(biāo)
*/
private void showSamsungDeskMark(int markCount) {
try {
String launcherClassName = getLauncherClassName();
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", markCount);
intent.putExtra("badge_count_package_name", getContext().getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
getContext().sendBroadcast(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 取消通知
*/
private void cancelNotice() {
NotificationManager notificationManager = getNotificationManager();
if (notificationManager != null) {
notificationManager.cancel(NOTIFY_ID);
}
}
/**
* @param appCompatActivity activity頁(yè)面
* 注冊(cè)通知監(jiān)聽
*/
private void registerNotice(AppCompatActivity appCompatActivity) {
DeskNoticeObservable deskNoticeObservable = new DeskNoticeObservable();
deskNoticeObservable.addObserver((o, arg) -> {
int noticeCountChange = (int) arg;
//強(qiáng)轉(zhuǎn)為int 類型 并更新當(dāng)前通知的數(shù)量
noticeMarkCount = noticeMarkCount + noticeCountChange;
//避免出現(xiàn)負(fù)數(shù)
if (noticeMarkCount < 0) {
noticeMarkCount = 0;
}
//顯示角標(biāo)
showDeskMark();
});
//初始化
deskNoticeObservable.init(appCompatActivity);
}
}