最新安卓提醒及角标功能实现概总(最后更新:2019-4-4)

最新安卓提醒及角标功能实现概总

一、提要

P.S. 最近工作需要非要做安卓的小红点,真是伤透了脑筋,做个总结记录下

关于安卓提醒功能及角标的实现,主要有以下需要注意的点:

  1. Android系统默认是不支持角标的。于是乎国内各大厂家就各显神通,每家的实现方法不一,为适配增加了难度;
  2. Android API版本在26之后的notification实现方法与之前的实现方法不一,需要注意;
  3. 同一个厂家的不同版本实现方法也出现了不同。(如MIUI系统在近年来的高版本就出现了和之前实现方法的差异)

二、实现

在了解了上面需要注意的点之后,我们可以开始着手做实现代码。每个厂家的实现文档可以在厂家官网查找也可以百度,这里也只是做了一个最新的总结。

1. 权限设置

需要以下权限:

 	<!-- 网络权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!--角标相关-->
    <uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE"/>
    <uses-permission android:name="com.sec.android.provider.badge.permission.READ"/>
    <uses-permission android:name="com.sec.android.provider.badge.permission.WRITE"/>
    <!-- 申请华为角标权限,注:华为角标权限需要Internet权限生效的前提下才会生效  -->
    <uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE"/>
    <uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS" />
    <uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS" />

2. 发送notification

首先需要弹出一条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();
        }
    }
}

3. Badge角标实现

依于前边的讨论以及各大官网上的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
     * 需添加权限:<uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_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<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);
                }
            }
            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;
    }
}

3. 调用

调用时只需要调用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供大家下载交流:

  1. 百度云盘下载 提取码:wr2r
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章