android 桌面角标能力
小米
测试结果:支持角标能力,最大值为9(官方数据), (实测可以到note4 显示为9999...mix2s 显示为99999....)
条件:需要向状态栏发送一条通知,进入APP则角标消失,抹除通知则角标也消失 ,在端内时不显示
首先打开应用通知设置页面,在”设置-通知管理“里点击应用,查看”显示桌面图标角标“开关是否开启。大部分应用默认是关闭状态。
资料:
https://dev.mi.com/console/doc/detail?pId=939
https://dev.mi.com/console/doc/detail?pId=2321
华为(荣耀)
测试结果:支持角标能力
条件 EMUI4.1 及以上及以上手机 打开通知和角标 选择显示为数字角标
最多显示99 超过99显示为99+
官网资料需要添加 华为权限 (实测不加也能显示) 代码需要try处理 避免无角标能力导致崩溃
资料:
https://developer.huawei.com/consumer/cn/doc/development/Corner-Guides/30802
OPPO
结论:需要上架,并且向oppo申请功能,还有适配才有。
资料:https://open.oppomobile.com/bbs/forum.php?mod=viewthread&tid=1711
VIVO
结论:需要向VIVO申请接入“桌面图标角标”,但是官方没放开
资料:https://dev.vivo.com.cn/documentCenter/doc/459
三星
结论:(网上资料)三星S9+ 以上 需和通知的方式结合才能显示角标,
三星 Galaxy S6 :可直接设置。设置为0 则角标消失
显示数量上限不限 ,暂无其他三星手机进行测试。
魅族
结论:官方不支持
总结
华为 小米 三星 部分原生系统 可以支持。 oppo vivo 魅族 暂时不支持
代码编写
小米角标代码
这里简单备注一下,小米在进入app内的时候,角标消息会清除。所以在处理小米的时候,在应用在后台的时候 应该再次将角标显示出来。
/**
* @param markNumber 显示数量
* 显示小米角标
* 小米官网 角标代码
* https://dev.mi.com/console/doc/detail?pId=2321
* https://dev.mi.com/console/doc/detail?pId=939
* 显示小米角标数量
*/
private void showXiaoMiDeskMark(int markNumber) {
try {
markNumber = detectMarkNumber(markNumber);
//因为小米更新数量需要发送通知 当是0的 时候就没必要进行发送了
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();
//小米角标代码
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");
}
}
华为角标代码
/**
* @param number 显示角标数量 0 则隐藏
* 显示华为角标
*/
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 角标值
* 显示三星角标
*/
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 要显示的角标数量
* @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)
端内使用的是TPNS,也就是原来的信鸽,这部分代码不具备通用性。如果你也使用的是TPNS可以进行参考。
通知的增加和删减
经测试,小米在收到通知的时候,在tpns下角标会自动增加1,通知抹除的时候 会自动扣除1。
华为(荣耀)在通知到来时,角标不会增加,但是通知删除的时候则会减去1.如果 在通知删除的时候 进行角标设置。则可能会发生,删除一个扣2个的可能性。则这里进行一个延迟操作。基本可以解决这个问题。如果有其他好的方法请留言。
代码相关
TPNS里面消息的接收在XGPushBaseReceiver 类里,继承XGPushBaseReceiver,
onNotificationShowedResult --消息到来展现
onNotificationClickedResult --消息划掉或者点击回调
我选择封装一个观察者进行处理
public class DeskNoticeObservable extends Observable {
private NoticeDeskReceiver noticeDeskReceiver;
/**
* 是否注册
*/
private boolean isRegister;
public void init(AppCompatActivity appCompatActivity) {
if (appCompatActivity == null) {
return;
}
//监听下消息
if (noticeDeskReceiver == null) {
noticeDeskReceiver = new NoticeDeskReceiver() {
//tpns消息到来
@Override
public void onNotificationShowedResult(Context context, XGPushShowedResult xgPushShowedResult) {
super.onNotificationShowedResult(context, xgPushShowedResult);
notifyNoticeCount(1);
}
//tpns消息点击和消除
@Override
public void onNotificationClickedResult(Context context, XGPushClickedResult xgPushClickedResult) {
super.onNotificationClickedResult(context, xgPushClickedResult);
notifyNoticeCount(-1);
}
};
}
IntentFilter intentFilter = getIntentFilter();
//添加消息监听
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
* 添加对于消息监听
*/
@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 {
}
通知观察者
/**
* 通知角标 观察者
*/
public class DeskNoticeObservable extends Observable {
private NoticeDeskReceiver noticeDeskReceiver;
/**
* 是否注册
*/
private boolean isRegister;
public void init(AppCompatActivity appCompatActivity) {
if (appCompatActivity == null) {
return;
}
//监听下消息
if (noticeDeskReceiver == null) {
noticeDeskReceiver = new NoticeDeskReceiver() {
//tpns消息到来
@Override
public void onNotificationShowedResult(Context context, XGPushShowedResult xgPushShowedResult) {
super.onNotificationShowedResult(context, xgPushShowedResult);
notifyNoticeCount(1);
}
//tpns消息点击和消除
@Override
public void onNotificationClickedResult(Context context, XGPushClickedResult xgPushClickedResult) {
super.onNotificationClickedResult(context, xgPushClickedResult);
notifyNoticeCount(-1);
}
};
}
IntentFilter intentFilter = getIntentFilter();
//添加消息监听
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
* 添加对于消息监听
*/
@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 DesktopCornerMark {
private static final String TAG = "DesktopCornerMark";
/**
* 限定最大显示99条
*/
private static final int MARK_MAX_COUNT = 99;
/**
* 通知id
*/
private static final int NOTIFY_ID = 999533;
/**
* 小米发通知用的id
*/
private static final String XIAOMI = "xiaomi";
/**
* 小米通知渠道
*/
private static final String XIAOMI_CHANLE_NAME = "xiaomi_chanle";
/**
* 默认启动页面名称
*/
private static final String SPLASH_CLASS_NAME = "com.wobo.live.launch.SplashActivity";
/**
* 是否在前台 默认在前台
*/
private boolean isInFrontDesk = true;
/**
* 默认缓存的角标数量
*/
private int imMarkCount = 0;
/**
* 通知数量
*/
private int noticeMarkCount = 0;
/**
* @param number 显示角标数量 0 则隐藏
* 显示华为角标
*/
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 显示数量
* 显示小米角标
* 小米官网 角标代码
* https://dev.mi.com/console/doc/detail?pId=2321
* https://dev.mi.com/console/doc/detail?pId=939
* 显示小米角标数量
*/
private void showXiaoMiDeskMark(int markNumber) {
try {
markNumber = detectMarkNumber(markNumber);
//因为小米更新数量需要发送通知 当是0的 时候就没必要进行发送了
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();
//小米角标代码
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 是否支持角标能力
* 目前调研只有 华为 小米能够支持 Xiaomi HUAWEI
* 三星只进行了s6测试 需要测试进行跟进
*/
private boolean isSupportDeskMark() {
return isHuaWei() || isXiaoMi() || isSamsung();
}
/**
* @return 获取手机厂商
* Xiaomi HUAWEI samsung
*/
private static String getPhoneMode() {
return android.os.Build.MANUFACTURER;
}
/**
* @return 获取启动页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 要显示的角标数量
* @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) {
//在后台
isInFrontDesk = false;
showDeskMark();
}
@Override
public void onResume(@NonNull LifecycleOwner owner) {
//在前台 小米在前台的时候 会清掉数据 华为做同样处理
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;
//在前台就不更改角标 直到在后台为止
if (isInFrontDesk) {
return;
} else {
showDeskMark();
}
}
/**
* 显示角标数量
*/
private void showDeskMark() {
int markNumber = imMarkCount + noticeMarkCount;
if (markNumber < 0) {
markNumber = 0;
}
int finalMarkNumber = markNumber;
//延迟600毫秒执行
io.reactivex.Observable.timer(600, TimeUnit.MILLISECONDS).safeSubscribe(new V3Observer() {
@Override
public void onSuccess(Long aLong) {
//华为
if (isHuaWei()) {
showHuaWeiDeskMark(finalMarkNumber);
} else if (isXiaoMi()) {
//小米 小米在通知的时候 会自己加1减1 只处理im消息通知的数量
showXiaoMiDeskMark(imMarkCount);
} else if (isSamsung()) {
//三星
showSamsungDeskMark(finalMarkNumber);
}
}
});
}
/**
* @return 获取通知管理者
*/
private NotificationManager getNotificationManager() {
return (NotificationManager) VLApplication.instance().getSystemService
(Context.NOTIFICATION_SERVICE);
}
/**
* @param markCount 角标值
* 显示三星角标
*/
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页面
* 注册通知监听
*/
private void registerNotice(AppCompatActivity appCompatActivity) {
DeskNoticeObservable deskNoticeObservable = new DeskNoticeObservable();
deskNoticeObservable.addObserver((o, arg) -> {
int noticeCountChange = (int) arg;
//强转为int 类型 并更新当前通知的数量
noticeMarkCount = noticeMarkCount + noticeCountChange;
//避免出现负数
if (noticeMarkCount < 0) {
noticeMarkCount = 0;
}
//显示角标
showDeskMark();
});
//初始化
deskNoticeObservable.init(appCompatActivity);
}
}
代码使用 可以参考
https://github.com/nuonuoOkami/DesktopCornerMark