Android 清除通知栏通知

系统分析

Posted by JovenHe on June 21, 2022

Android上有个通知使用权,给相关应用开启后,应用可以收到其他应用发送的通知,也可以移除相关通知

通知服务类

class NotificationListener : NotificationListenerService() {

    companion object {
        val TAG = NotificationListener::class.java.simpleName
    }

    override fun onNotificationRemoved(sbn: StatusBarNotification?) {
        super.onNotificationRemoved(sbn)
        LogUtil.d("NotificationListener-onNotificationRemoved: " + sbn.toString());

    }

    override fun onNotificationPosted(sbn: StatusBarNotification?) {
        super.onNotificationPosted(sbn)
        LogUtil.d("NotificationListener-onNotificationPosted: " + sbn.toString())

    }
}

manifest中声明

        <service
            android:name=".service.NotificationListener"
            android:label="@string/app_name"
            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">

            <intent-filter>
                <action android:name="android.service.notification.NotificationListenerService" />
            </intent-filter>

        </service>

代码判断是否开启通知使用权,并跳转设置页面打开权限

 override fun onResume() {
        super.onResume()
        if (startSettin) {
            checkNotificationPermission()
        }
    }

    private fun checkNotificationPermission() {
        startSettin = false
        if (notificationListenerEnable()) {
            //有通知使用权,跳出逻辑
            startMainActivity()
        } else {
            if (gotoNotificationAccessSetting()) {
                startSettin = true
            }
        }
    }

    private fun gotoNotificationAccessSetting(): Boolean {
        try {
            var intent = Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent)
            return true;
        } catch (e: Exception) {
            try {
                var intent = Intent()
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                val cn = ComponentName(
                    "com.android.settings",
                    "com.android.settings.Settings\$NotificationAccessSettingsActivity"
                );
                intent.setComponent(cn)
                intent.putExtra(":settings:show_fragment", "NotificationAccessSettings");
                startActivity(intent)
                return true;
            } catch (ex: Exception) {
                ex.printStackTrace()
            }
            return false
        }
    }

    private fun notificationListenerEnable(): Boolean {
        var enable = false
        val packageName = AppContext.getPackageName()
        val flat = Settings.Secure.getString(
            AppContext.getContentResolver(),
            "enabled_notification_listeners"
        )
        if (flat != null) {
            enable = flat.contains(packageName)
        }
        return enable
    }

权限开通后就能看到通知添加或移除时的日志

2022-06-21 16:44:12.850 7726-7726/com.****.**** D/MdmOfflineApp: [MDMOffline-->]/MDMOffline-->NotificationListener-onNotificationPosted: StatusBarNotification(pkg=com.hihonor.hieduservice user=UserHandle{0} id=10001 tag=null key=0|com.hihonor.hieduservice|10001|null|10107: Notification(channel=notification_channel_edu_kit pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x62 color=0x00000000 groupKey=ranker_group10001 vis=PRIVATE))
2022-06-21 16:44:28.413 7726-7726/com.****.**** D/MdmOfflineApp: [MDMOffline-->]/MDMOffline-->NotificationListener-onNotificationRemoved: StatusBarNotification(pkg=com.hihonor.hieduservice user=UserHandle{0} id=10001 tag=null key=0|com.hihonor.hieduservice|10001|null|10107: Notification(channel=notification_channel_edu_kit pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x62 color=0x00000000 groupKey=ranker_group10001 vis=PRIVATE))
2022-06-21 16:44:28.647 7726-7726/com.****.**** D/MdmOfflineApp: [MDMOffline-->]/MDMOffline-->NotificationListener-onNotificationPosted: StatusBarNotification(pkg=android user=UserHandle{-1} id=26 tag=null key=-1|android|26|null|1000: Notification(channel=DEVELOPER pri=0 contentView=null vibrate=null sound=null tick defaults=0x0 flags=0x2 color=0xff607d8b groupKey=ranker_group26 vis=PUBLIC))
2022-06-21 16:44:29.611 7726-7726/com.****.**** D/MdmOfflineApp: [MDMOffline-->]/MDMOffline-->NotificationListener-onNotificationPosted: StatusBarNotification(pkg=com.android.settings user=UserHandle{0} id=1000002 tag=null key=0|com.android.settings|1000002|null|1000: Notification(channel=SETTINGS_USB pri=1 contentView=null vibrate=null sound=null tick defaults=0x0 flags=0x2 color=0x00000000 groupKey=ranker_group1000002 vis=PRIVATE))
2022-06-21 17:00:39.247 7726-7726/com.****.**** D/MdmOfflineApp: [MDMOffline-->]/MDMOffline-->NotificationListener-onNotificationPosted: StatusBarNotification(pkg=com.huawei.trustspace user=UserHandle{0} id=2131099660 tag=null key=0|com.huawei.trustspace|2131099660|null|1000: Notification(channel=trustspace_channel_id pri=1 contentView=null vibrate=null sound=default defaults=0x1 flags=0x10 color=0x00000000 actions=2 vis=PRIVATE))
2022-06-21 17:04:09.562 7726-7726/com.****.**** D/MdmOfflineApp: [MDMOffline-->]/MDMOffline-->NotificationListener-onNotificationPosted: StatusBarNotification(pkg=com.huawei.music user=UserHandle{0} id=1 tag=null key=0|com.huawei.music|1|null|10027: Notification(channel=music_notify_channel_id_play pri=0 contentView=com.huawei.music/0x7f0d03fe vibrate=null sound=null defaults=0x0 flags=0x62 color=0x00000000 category=transport groupKey=music_notify_channel_id_play vis=PUBLIC))

能看到通知是属于哪一个应用发出的,这样我们就可以在onNotificationPosted中接收我们自己的通知然后使用cancelAllNotifications进行删除所有通知

发送通知

private fun sendNotification(){
        val pi = PendingIntent.getActivity(context, 0, Intent(), 0)
        val channel: String
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) channel = createChannel() else {
            channel = ""
        }
        val notification =
            context?.let {
                NotificationCompat.Builder(it, channel)
                    .setContentTitle(context!!.getString(R.string.app_name))
                    .setWhen(System.currentTimeMillis())
                    .setSmallIcon(R.mipmap.icon_logo)
                    .setContentIntent(pi)
                    .build()
            }
        val mNotificationManager =
            context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        mNotificationManager.notify(TAG,ID,notification)
    }
    @TargetApi(26)
    @Synchronized
    private fun createChannel(): String {
        val mNotificationManager =
            context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val importance = NotificationManager.IMPORTANCE_LOW
        val mChannel =
            NotificationChannel(TAG, TAG, importance)
        mChannel.enableLights(false)
        mChannel.enableVibration(false)
        mChannel.setSound(null, null)
        if (mNotificationManager != null) {
            mNotificationManager.createNotificationChannel(mChannel)
        }
        return TAG
    }

接收到通知时,删除所有通知

    override fun onNotificationPosted(sbn: StatusBarNotification?) {
        super.onNotificationPosted(sbn)
        LogUtil.d("NotificationListener-onNotificationPosted: " + sbn.toString())
        if (sbn != null && sbn.tag == TAG) {
            cancelAllNotifications()
        }

    }

测试日志

2022-06-21 17:12:53.928 22742-22742/com.****.**** D/MdmOfflineApp: [MDMOffline-->]/MDMOffline-->NotificationListener-onNotificationPosted: StatusBarNotification(pkg=com.****.**** user=UserHandle{0} id=272 tag=com.****.****.provider.DeviceControlProvider key=0|com.****.****|272|com.****.****.provider.DeviceControlProvider|10132: Notification(channel=com.****.****.provider.DeviceControlProvider pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0x00000000 vis=PRIVATE))
2022-06-21 17:12:53.944 22742-22742/com.****.**** D/MdmOfflineApp: [MDMOffline-->]/MDMOffline-->NotificationListener-onNotificationRemoved: StatusBarNotification(pkg=com.****.**** user=UserHandle{0} id=272 tag=com.****.****.provider.DeviceControlProvider key=0|com.****.****|272|com.****.****.provider.DeviceControlProvider|10132: Notification(channel=com.****.****.provider.DeviceControlProvider pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0x00000000 vis=PRIVATE))
2022-06-21 17:12:53.948 22742-22742/com.****.**** D/MdmOfflineApp: [MDMOffline-->]/MDMOffline-->NotificationListener-onNotificationRemoved: StatusBarNotification(pkg=com.huawei.trustspace user=UserHandle{0} id=2131099660 tag=null key=0|com.huawei.trustspace|2131099660|null|1000: Notification(channel=trustspace_channel_id pri=1 contentView=null vibrate=null sound=default defaults=0x1 flags=0x10 color=0x00000000 actions=2 vis=PRIVATE))

可以看到收到我们发的通知后,移除了我们的通知和com.huawei.trustspace的通知

| 通知1.jpg | 通知2.jpg | | ———————————————————— | ———————————————————— |

可以看到只少了一条通知,因为系统和设置的通知因为usb的原因不能被移除,而音乐的通知因为多媒体的原因需要手动交互才能被移除

因为MDM的原因,可以通过kill掉进程来删除通知

    override fun onNotificationRemoved(sbn: StatusBarNotification?) {
        super.onNotificationRemoved(sbn)
        LogUtil.d("NotificationListener-onNotificationRemoved: " + sbn.toString());
        if (sbn != null && sbn.tag == TAG) {
            val activeNotifications = activeNotifications
            if (activeNotifications.isNotEmpty()) {
                for (activeNotification in activeNotifications) {
                    if (activeNotification.packageName != "com.android.settings" &&
                        activeNotification.packageName != "android"
                    ) {
                        if (activeNotification.notification.contentView != null) {
                            LogUtil.d("NotificationListener-onNotificationPosted: activeNotification ${activeNotification.toString()}")
                            CtrlProxy.get().killApplicationProcess(activeNotification.packageName)
                        }
                    }
                }
            }
        }
    }

可以看到实在我们的通知被移除的时候获取一下还存在的通知,然后过滤掉系统和设置的,再获取需要交互才能关闭的通知,通过killApplicationProcess杀掉进程来删除通知

这样就只保留了系统相关不能被移除的通知了。