P.S. 最近工作需要非要做安卓的小红点,真是伤透了脑筋,做个总结记录下
关于安卓提醒功能及角标的实现,主要有以下需要注意的点:
在了解了上面需要注意的点之后,我们可以开始着手做实现代码。每个厂家的实现文档可以在厂家官网查找也可以百度,这里也只是做了一个最新的总结。
需要以下权限:
首先需要弹出一条notification,在Android API>26时,需要使用NotificationChannel提醒组来实现,这里需要注意下。具体实现如下
public class CommonNotification {
// 消息提示管理器
private static NotificationManager manager;
private static NotificationChannel channel;
private CommonNotification() throws InstantiationException {
throw new InstantiationException("This class is not for instantiation");
}
/**
* 发送提醒
* @param intent 需要传输的数据,使用bundle传输,可在to中使用onGetNewIntent中获取数据
* @param title 提醒标题
* @param text 提醒文本
* @param count 提醒条数,可显示在应用图标右上角的小红点
* @param id 提醒的id,同id将覆盖,可通过过id进行消除相应提醒
* @param start 发送提醒的context
* @param to 点击提醒,需要跳转的context
*/
public static void sendNotification(Intent intent , String title , String text , int count , int id , Context start , Context to){
if (manager == null){
manager = (NotificationManager) start.getSystemService(Context.NOTIFICATION_SERVICE);
}
if (channel == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
//初始化方法中第一个参数为chanel的id,后边需要使用
channel = new NotificationChannel("1",
"Channel1", NotificationManager.IMPORTANCE_HIGH);
channel.enableLights(true); //是否在桌面icon右上角展示小红点
channel.setBypassDnd(true);
channel.enableVibration(true);//震动
channel.setLightColor(Color.RED); //小红点颜色
channel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知
if (manager != null) {
manager.createNotificationChannel(channel);
}
}
//region 设置启动特征,不重新绘制,这里可以防止点击提醒后跳转进来新创建activity
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//endregion
//初始化pendingIntent,需要跳转的activity,id,intent参数等
PendingIntent pendingIntent = PendingIntent.getActivity(start, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//第二个参数即是上边channel的id,即notification所属的channel
builder = new Notification.Builder(to , "1");
}else {
builder = new Notification.Builder(to);
}
builder.setContentTitle(title)
.setContentText(text)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentIntent(pendingIntent)
.setAutoCancel(true)//设置点击自动取消
.setDefaults(Notification.DEFAULT_SOUND)//设置提醒时的生效
.setNumber(8);//设置可折叠提醒数量
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// //锁屏提醒显示内容
// builder.setVisibility(Notification.VISIBILITY_PUBLIC);
// }
Notification notification = builder.build();
//唤醒屏幕
PowerUtil.wakeAndUnlock(to , true);
//更新角标
try {
BadgeUtil.setBadgeCount(start.getApplicationContext() , count , notification);
} catch (Exception e) {
e.printStackTrace();
}
if (manager != null) {
manager.notify(id, notification);
}
}
/**
* 取消所有提醒
*/
public static void cancelAll(){
if (manager != null) {
manager.cancelAll();
}
}
}
依于前边的讨论以及各大官网上的API文档,做工具类如下:
注:当前已测试通过的有Samsung、MIUI系列、华为(含荣耀系列)、OPPO、vivo。欢迎各位评论自己的机型测试结果
public class BadgeUtil {
private BadgeUtil() throws InstantiationException {
throw new InstantiationException("This class is not for instantiation");
}
/**
* 设置Badge 目前支持Launcher
*/
public static void setBadgeCount(Context context, int count , Notification notification) {
if (count <= 0) {
count = 0;
} else {
count = Math.max(0, Math.min(count, 99));
}
String rom = Build.MANUFACTURER.toLowerCase();
if (rom.contains("xiaomi")) { //小米
setBadgeOfMIUI(count , notification);
} else if (rom.contains("sony")) { //索尼
setBadgeOfSony(context, count);
} else if (rom.contains("samsung") || rom.contains("lg")) {
setBadgeOfSumsung(context, count);
} else if (rom.contains("htc")) { //htc
setBadgeOfHTC(context, count);
} else if (rom.contains("nova")) { //nova
setBadgeOfNova(context, count);
}else if (rom.contains("oppo")) { //oppo
setBadgeOfOPPO(context, count);
}else if (rom.contains("lemobile")) {//乐视
Log.e("BadgeUtil", "setBadgeCount: 乐视 暂不支持");
}else if (rom.contains("vivo")) {
setBadgeOfVIVO(context, count);
}else if (rom.contains("huawei") || Build.BRAND.equals("Huawei") ||Build.BRAND.equals("HONOR") || rom.equalsIgnoreCase("huawei")) {//华为
setHuaweiBadge(context, count);
}else if (rom.contains("meizu")) {//魅族
Log.e("BadgeUtil", "setBadgeCount: 魅族 暂不支持");
}else if (rom.contains("jinli")) {//金立
Log.e("BadgeUtil", "setBadgeCount: 金立 暂不支持");
}else if (rom.contains("chuizi")) {//锤子
Log.e("BadgeUtil", "setBadgeCount: 锤子 暂不支持");
}else {
Log.e("BadgeUtil", "setBadgeCount: 未匹配机型:"+rom);
}
}
/**
* 设置MIUI的Badge
*/
private static void setBadgeOfMIUI(int count , Notification notification) {
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();
}
}
/**
* 设置索尼的Badge
* 需添加权限:
*/
private static void setBadgeOfSony(Context context, int count) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
boolean isShow = true;
if (count == 0) {
isShow = false;
}
Intent localIntent = new Intent();
localIntent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", isShow);//是否显示
localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", launcherClassName);//启动页
localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", String.valueOf(count));//数字
localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context.getPackageName());//包名
context.sendBroadcast(localIntent);
}
/**
* 设置三星的Badge\设置LG的Badge
*/
private static void setBadgeOfSumsung(Context context, int count) {
// 获取你当前的应用
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
return;
}
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);
}
/**
* 设置HTC的Badge
*/
private static void setBadgeOfHTC(Context context, int count) {
Intent intentNotification = new Intent("com.htc.launcher.action.SET_NOTIFICATION");
ComponentName localComponentName = new ComponentName(context.getPackageName(), getLauncherClassName(context));
intentNotification.putExtra("com.htc.launcher.extra.COMPONENT", localComponentName.flattenToShortString());
intentNotification.putExtra("com.htc.launcher.extra.COUNT", count);
context.sendBroadcast(intentNotification);
Intent intentShortcut = new Intent("com.htc.launcher.action.UPDATE_SHORTCUT");
intentShortcut.putExtra("packagename", context.getPackageName());
intentShortcut.putExtra("count", count);
context.sendBroadcast(intentShortcut);
}
/**
* 设置Nova的Badge
*/
private static void setBadgeOfNova(Context context, int count) {
ContentValues contentValues = new ContentValues();
contentValues.put("tag", context.getPackageName() + "/" + getLauncherClassName(context));
contentValues.put("count", count);
context.getContentResolver().insert(Uri.parse("content://com.teslacoilsw.notifier/unread_count"),
contentValues);
}
/**
* 设置vivo的Badge :vivoXplay5 vivo x7无效果
*/
private static void setBadgeOfVIVO(Context context,int count){
try {
//老版可用,新版已封闭,需申请
Intent intent = new Intent("launcher.action.CHANGE_APPLICATION_NOTIFICATION_NUM");
intent.putExtra("packageName", context.getPackageName());
String launchClassName = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()).getComponent().getClassName();
intent.putExtra("className", launchClassName); intent.putExtra("notificationNum", count);
context.sendBroadcast(intent);
}catch (Exception e){
e.printStackTrace();
}
}
/**
*设置oppo的Badge :oppo角标提醒目前只针对内部软件还有微信、QQ开放,其他的暂时无法提供
*/
private static void setBadgeOfOPPO(Context context,int count){
try {
if (Constant.version >= Build.VERSION_CODES.LOLLIPOP) {
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);
} else {
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 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);
}
}
Log.e("BadgeUtil", "setBadgeOfOPPO: "+"设置角标");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置华为的Badge :mate8 和华为 p7,honor畅玩系列可以,honor6plus 无效果
*/
private static void setHuaweiBadge(Context context, int count)
{
//权限检查
try {
Bundle bundle = new Bundle();
bundle.putString("package", context.getPackageName());
bundle.putString("class", getLauncherClassName(context));
bundle.putInt("badgenumber", count);
context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, bundle);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setBadgeOfMadMode(Context context, int count, String packageName, String className) {
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", packageName);
intent.putExtra("badge_count_class_name", className);
context.sendBroadcast(intent);
}
/**
* 重置Badge
*/
public static void resetBadgeCount(Context context , Notification notification) {
setBadgeCount(context, 0 , notification);
}
private static String getLauncherClassName(Context context) {
PackageManager packageManager = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setPackage(context.getPackageName());
intent.addCategory(Intent.CATEGORY_LAUNCHER);
ResolveInfo info = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (info == null) {
info = packageManager.resolveActivity(intent, 0);
}
return info.activityInfo.name;
}
}
调用时只需要调用sendNotification方法并传入相关参数即可,如:
Bundle bundle = new Bundle();
bundle.putString("name" , Constant.intentMsg);
intent.putExtras(bundle);
CommonNotification.sendNotification(intent , title, text, count, id ,activity , activity);
其中bundle的参数可以在点击提醒后跳转的activity的onNewIntent方法中获取值。这也就是如何通过notification传递参数的方法了。
这里提供一个demo供大家下载交流: