android桌面角标调研和代码实现

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申请接入“桌面图标角标”,但是官方没放开

image-20210601163058274.png

资料: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

你可能感兴趣的:(android桌面角标调研和代码实现)