截取通知栏信息

    xiaoxiao2021-03-25  151

    有些时候我们需要获取系统通知栏的一些信息,比如截获一些应用信息,在桌面显示角标等。 要获得通知栏信息,需要一个自定义的service,该服务需要继承NotificationListenerService。(注意有些api是4.4或5.0后才提供的) 这个服务是由系统为我们自动绑定的(我们不能手动绑定且自己绑定是无意义的)。一旦获得通知栏授权,就会回调onBind方法完成绑定。 接下来通知栏有消息或移除消息的时候就会回调: onNotificationPosted(StatusBarNotification sbn) onNotificationRemoved(StatusBarNotification sbn) 这两个方法,其中参数,就是通知栏上的具体消息了,我们可以解析这个消息得到一些字段内容,进而执行自己的逻辑。 说到授权,有两种方式: 1、通过显示的进入系统设置的授权界面,该界面会罗列出系统中所有声明了NotificationListenerService的应用。 也就是说我们的应用要在Manifest中声明这个service,并且这个service的permission 和 intent action是特定的如: <service             android:name="xxx.badge.NotificationListener"             android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >             <intent-filter>                 <action android:name="android.service.notification.NotificationListenerService" />             </intent-filter>         </service> 如果不做这个声明,在授权界面是无法看到我们的应用图标的。(在这里我犯了这个错误,没有声明) 打开授权界面的代码是这样的: String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; Intent intent = new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);             context.startActivity(intent); (这里有个小插曲:使用startActivity打开一个界面的时候,如果当前这个context是个application或是service的话,我们需要在intent 中设置flag为new_task,否则打开的界面是出不来的。原因很简单,application和service是无界面的,而activity是需要一个task来管理回退栈的, 所以我们在application或是service中打开activity的话,需要设置flag)

    2、我们的apk有系统签名,这样可以直接操作授权的数据库,此方法也可以触发系统来绑定我们的服务。

    public static void registerNotificationListener(Context context) { Log.i(tag, "----registerNotificationListener------"); String pkgName = context.getPackageName(); final String flat = Settings.Secure.getString(context.getContentResolver(), getNotificationListenerConfig().setting); StringBuilder sb = new StringBuilder(); boolean isEnable = false; if (flat != null && !"".equals(flat)) { final String[] names = flat.split(":"); for (int i = 0; i < names.length; i++) { final ComponentName cn = ComponentName.unflattenFromString(names[i]); if (cn != null && TextUtils.equals(pkgName, cn.getPackageName())) { isEnable = true; break; } } } boolean writeSetting = false; if (isEnable) { sb.append(flat); //writeSetting = mNotifyService != null && !isServiceRun(context, NotificationListener.class.getName()); writeSetting = !isServiceRun(context); Log.i(tag, "mNotifyService: "+ mNotifyService + ", wirteSetting:" + writeSetting); } else { if (mNotifyService == null) { mNotifyService = new ComponentName(context, NotificationListener.class); } sb.append(flat).append(":").append(mNotifyService.flattenToString()); writeSetting = true; Log.i(tag, "writeSetting: " + writeSetting); } if (writeSetting) { Log.i(tag, "-----registerNotificationListener-----writeSetting----true-----"); /* * int index = sb.lastIndexOf(":"); if (index != -1) { sb.deleteCharAt(index); } */ // sb = new StringBuilder("com.qihoo.appstore/com.qihoo.appstore.AppStoreNotificationListenerService"); Log.i(tag, "-----registerNotificationListener-----writeSetting----true-----" + sb.toString()); try { Settings.Secure.putString(context.getContentResolver(), getNotificationListenerConfig().setting, sb != null ? sb.toString() : ""); } catch (Exception e) { e.printStackTrace(); // 发生异常,说明无权限(使用普通方法重新绑定服务) enableNotificationListener(context); } } else { Log.i(tag, "-----registerNotificationListener-----writeSetting----false---isrunning--"); } // Intent service = new Intent(context, NotificationMonitor.class); // context.startService(service); }

    private static Config getNotificationListenerConfig() { final Config c = new Config(); c.setting = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; c.intentAction = NotificationListenerService.SERVICE_INTERFACE; c.permission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; c.noun = "notification listener"; return c; } protected static class Config { String tag; String setting; String intentAction; String permission; String noun; int warningDialogTitle; int warningDialogSummary; int emptyText; }

    检查服务是否授权: public static boolean isNotificationListenerServiceEnabled(Context context) { Set<String> packageNames = NotificationManagerCompat.getEnabledListenerPackages(context); return packageNames.contains(context.getPackageName()); } 判断服务是否启动: public static boolean isServiceRun(Context context) { boolean isRun = false; ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningServiceInfo> serviceList = activityManager.getRunningServices(Integer.MAX_VALUE); if (serviceList != null && serviceList.size() > 0) { for (ActivityManager.RunningServiceInfo info : serviceList) { if (info.service.getClassName().equals(NotificationListener.class.getName())) { isRun = true; break; } } } return isRun; } 另外有些特殊情况,我们的app被系统杀死,或是发生crash后如何使系统再次绑定这个服务呢? 有三种方式可以触发: 1. 重启手机 2. 有卸载更新或覆盖安装 3. Setting数据库变更。 其中我们可以主动去触发方式2,即 主动去触发广播(Service的disable,会有Intent.ACTION_PACKAGE_CHANGED广播) 利用这一特性,把应用的NotificationListenerService实现类disable再enable,即可触发系统onBind操作。 PackageManager pm = context.getPackageManager();             pm.setComponentEnabledSetting(new ComponentName(context, NotificationListener.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);             pm.setComponentEnabledSetting(new ComponentName(context, NotificationListener.class), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); 另外一种更特殊的: 一旦我们在执行service的onCreate或是onBind之前发生了crash,那么必须重启手机才能使用该服务。 因为NotificationManagerService会记录该service, 这是个系统服务类,只有再次重启才能清除这个记录标志。

    转载请注明原文地址: https://ju.6miu.com/read-2181.html

    最新回复(0)