最新安卓提醒及角標功能實現概總(最後更新: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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章