1. Android 实现应用桌面角标

  Android 8.0之前原生是不支持应用角标的,是各个手机厂商自己在系统中实现的应用角标,并且部分厂商提供了设置的方式,所以需要对各个厂商的系统进行适配。

1.1. 各机型适配

1.1.1. 小米手机

官方文档:文档中心   小米在MIUI6及以上版本中是通过发送通知来设置角标的。当APP向通知栏发送了一条通知 (通知不带进度条并且用户可以删除的),那么桌面APP icon角标就会显示1。此时app显示的角标数是和通知栏里app发送的通知数对应的,即向通知栏发送了多少通知就会显示多少角标。
  可以通过反射机制来定义每次通知的消息个数,应用的角标数为每条通知定义的通知个数的总和。
  我们发送了两条通知,一条通知为5个消息,一条为10个消息,应用角标显示为15个。此时,如果用户点击或移除掉5条消息的那个通知,应用角标会变成10。
  另外,仅在APP在后台时收到通知会显示角标,而APP在前台时不会显示,APP被杀掉后通知及角标消失。

/**
     * 小米手机创建通知信息并创建角标
     *
     * @param context
     * @param num
     */
    public static void setXiaoMiBadgeNum(Context context, int num) {
        Log.e(TAG, "--------setXiaoMiBadgeNum----------");
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        String title = "消息提示";
        String desc = "您有" + num + "条未读消息";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String channelId = "default";
            String channelName = "默认通知";
            NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
            channel.setShowBadge(true);
            notificationManager.createNotificationChannel(channel);
        }
 
        Notification notification = new NotificationCompat.Builder(context, "default")
                .setContentTitle(title)
                .setContentText(desc)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setAutoCancel(true)
                .setChannelId("default")
                .setNumber(num)//桌面角标数量
                .setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
                .build();
        //取消掉上一条通知消息
        notificationManager.cancel(notificationId);
        try {
            Field field = notification.getClass().getDeclaredField("extraNotification");
            Object extraNotification = field.get(notification);
            Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int.class);
            method.invoke(extraNotification, num);
        } catch (Exception e) {
            e.printStackTrace();
        }
        notificationManager.notify(notificationId, notification);
    }

  需要注意以下几点:
(1)发送通知必须当app处于后台才会生成桌面角标,否则只会生成一次。
(2)该方法目前支持到miui12.

1.1.2. 三星手机

  已在三星s7dege+上,Android8.0上通过

public static void setSamSungBadgeNum(Context context,int num){
 
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setWhen(System.currentTimeMillis())
                .setAutoCancel(true);
 
        mBuilder.setContentTitle("test");
        mBuilder.setTicker("test");
        mBuilder.setContentText("test");
 
        //点击set 后,app退到桌面等待3s看效果(有的launcher当app在前台设置未读数量无效)
        final Notification notification = mBuilder.build();
        sendBadgeNotification(notification, notificationId, context, num, num);
    }
    /**
     * 重置、清除Badge未读显示数<br/>
     *
     * @param context
     */
    public static void resetBadgeCount(Context context) {
        sendBadgeNotification(null, 0, context, 0, 0);
    }
 
    public static void sendBadgeNotification(Notification notification, int notifyID, Context context, int thisNotifyCount, int count) {
        if (count <= 0) {
            count = 0;
        } else {
            count = Math.max(0, Math.min(count, 99));
        }
 
        executeBadge(context, notification, notifyID, thisNotifyCount, count);
    }
 
    public static void executeBadge(Context context, Notification notification, int notificationId, int thisNotificationCount, int count) {
        setNotification(notification, notificationId, context);
        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);
    }
 
    protected static void setNotification(Notification notification, int notificationId, Context context) {
        if (notification != null) {
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.notify(notificationId, notification);
        }
    }
 
    protected static String getLauncherClassName(Context context) {
        PackageManager packageManager = context.getPackageManager();
 
        Intent intent = new Intent(Intent.ACTION_MAIN);
        // To limit the components this Intent will resolve to, by setting an
        // explicit package name.
        intent.setPackage(context.getPackageName());
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
 
        // All Application must have 1 Activity at least.
        // Launcher activity must be found!
        ResolveInfo info = packageManager
                .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
 
        // get a ResolveInfo containing ACTION_MAIN, CATEGORY_LAUNCHER
        // if there is no Activity which has filtered by CATEGORY_DEFAULT
        if (info == null) {
            info = packageManager.resolveActivity(intent, 0);
        }
 
        return info.activityInfo.name;
    }

1.1.3. 华为

  可以通过ContentResolver方法直接设置应用角标,且应用在前台和被杀掉后仍可显示。

<!--华为手机更新应用桌面角标需要的权限-->
    <uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE"/>
 private void setHUAWEIIconBadgeNum(Context context,int count)  {
 
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setWhen(System.currentTimeMillis())
                .setAutoCancel(true);
 
        mBuilder.setContentTitle("test");
        mBuilder.setTicker("test");
        mBuilder.setContentText("test");
 
        //点击set 后,app退到桌面等待3s看效果(有的launcher当app在前台设置未读数量无效)
        final Notification notification = mBuilder.build();
        
        
        setNotification(notification, notificationId, context);
        Bundle bunlde = new Bundle();
        bunlde.putString("package", context.getPackageName());
        bunlde.putString("class", getLauncherClassName(context));
        bunlde.putInt("badgenumber", count);
        context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, bunlde);
    }

1.1.4. OPPO

  首先,官方客服和我说他们可以支持,但需要申请。
  而抱歉的是截止目前我还没有得到具体的方法,因为我们还在它的申请流程中。。
  具体申请流程请去OPPO开放平台找他们的人工客服获取申请方法。

public static void setBadgeNumber(Context context, int number) {
        try {
            if (number == 0) {
                number = -1;
            }
            Intent intent = new Intent("com.oppo.unsettledevent");
            intent.putExtra("pakeageName", context.getPackageName());
            intent.putExtra("number", number);
            intent.putExtra("upgradeNumber", number);
            if (canResolveBroadcast(context, intent)) {
                context.sendBroadcast(intent);
            } else {
                try {
                    Bundle extras = new Bundle();
                    extras.putInt("app_badge_count", number);
                    context.getContentResolver().call(Uri.parse("content://com.android.badge/badge"), "setAppBadgeCount", null, extras);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    public static boolean canResolveBroadcast(Context context, Intent intent) {
        PackageManager packageManager = context.getPackageManager();
        List<ResolveInfo> receivers = packageManager.queryBroadcastReceivers(intent, 0);
        return receivers != null && receivers.size() > 0;
    }

1.1.5. VIVO

官方文档:vivo开放平台

public static void setBadgeNumber(Context context, int number) {
        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", number);
            context.sendBroadcast(intent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

1.2. 完整代码

public class MobileConfigUtil {
    /**
     * @return 手机类型 华为、小米、vivo、oppo
     * @desc 获取手机类型
     */
    public static String getMobileType() {
        return Build.MANUFACTURER;
    }


    /**
     * @desc 根据手机机型进入app自启动页面
     */
    public static void jumpStartInterface(Context context) {
        Intent intent = new Intent();
        try {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            ComponentName componentName = null;
            if (getMobileType().equals("Xiaomi")) { // 小米、红米
//                componentName = new ComponentName(
//                        "com.miui.securitycenter",
//                        "com.miui.permcenter.autostart.AutoStartManagementActivity");
                // 在此根据用户手机当前版本跳转系统设置界面
                intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
                intent.setData(Uri.fromParts("package",
                        context.getPackageName(), null));
            } else if (getMobileType().equals("Letv")) { // 乐视
                intent.setAction("com.letv.android.permissionautoboot");
            } else if (getMobileType().equals("samsung")) { // 三星
                componentName = ComponentName.unflattenFromString(
                        "com.samsung.android.sm_cn/com.samsung.android.sm.ui.ram.AutoRunActivity");
            } else if (getMobileType().equals("HONOR") // 荣耀
                    || getMobileType().equals("HUAWEI") // 华为
                    || getMobileType().equals("NOVA"))  // nova
            {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    componentName = new ComponentName(
                            "com.huawei.systemmanager",
                            "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
                } else {
                    componentName = new ComponentName(
                            "com.huawei.systemmanager",
                            "com.huawei.systemmanager.com.huawei.permissionmanager.ui.MainActivity");
                }
            } else if (getMobileType().equals("vivo")) { // vivo
//                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//                    componentName = new ComponentName(
//                            "com.vivo.permissionmanager",
//                            "com.vivo.permissionmanager.activity.PurviewTabActivity");
//                } else {
//                    componentName = ComponentName
//                            .unflattenFromString("com.iqoo.secure/.MainActivity");//i管家
//                }
//                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                // 在此根据用户手机当前版本跳转系统设置界面
                intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
                intent.setData(Uri.fromParts("package",
                        context.getPackageName(), null));
            } else if (getMobileType().equals("Meizu")) {//魅族
                //魅族内置手机管家界面
                componentName = ComponentName.unflattenFromString("com.meizu.safe" +
                        "/.permission.PermissionMainActivity");
            } else if (getMobileType().equals("OPPO")) { // OPPO
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    componentName = new ComponentName(
                            "com.coloros.safecenter",
                            "com.coloros.safecenter.startupapp.StartupAppListActivity");
                } else {
                    componentName = new ComponentName(
                            "com.color.safecenter",
                            "com.color.safecenter.permission.startup.StartupAppListActivity");
                }
            } else if (getMobileType().equals("ulong")) { // 360手机
                componentName = new ComponentName(
                        "com.yulong.android.coolsafe",
                        ".ui.activity.autorun.AutoRunListActivity");
            } else {
                // 在此根据用户手机当前版本跳转系统设置界面
                intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
                intent.setData(Uri.fromParts("package",
                        context.getPackageName(), null));
            }
            intent.setComponent(componentName);
            context.startActivity(intent);
        } catch (Exception e) {//抛出异常就直接打开设置页面
            intent = new Intent(Settings.ACTION_SETTINGS);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
            Log.e("HLQ_Struggle", "抛出异常就直接打开设置页面");
        }
    }
    /**
     * @desc 去配置界面
     */

    public static void goConfigPage(Context context) {
        Intent intent = new Intent(Settings.ACTION_SETTINGS);
        intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
        intent.setData(Uri.fromParts("package",
                context.getPackageName(), null));
        context.startActivity(intent);
    }

}