如何取消其他应用的持续通知?

时间:2018-07-30 05:24:18

标签: android notifications foreground-service heads-up-notifications

背景

我找到了一个名为NCleaner的应用程序,该应用程序以某种方式隐藏了抬头通知,甚至包括正在进行的通知(由前台服务使用)。

我想知道这种事情是如何工作的。

问题

关于如何控制其他应用程序的通知的信息并不多。仅出于当前应用程序,由于某种原因,我未能取消正在进行的通知,但成功取消了正常通知。

我发现的东西

我发现this sample显示了如何监视通知。在搜索有关NotificationListenerService的文档之后,我还发现了如何取消其他应用程序的特定通知。

但是,它对于正在进行的通知不起作用。

为了测试正常的通知,我只是通过PC给自己发送电子邮件或在PushBullet应用中写了一些东西。

为了测试正在进行的通知,我已经安装并运行了我的业余时间应用程序(here),该应用程序从Android O开始就在应用程序的开头显示了正在进行的通知。实际上,它可以甚至隐藏操作系统的通知,USB连接和调试的通知(只需通过USB将设备连接到PC)...

这是我根据示例制作的用于取消其收到的所有通知的代码:

NotificationListenerExampleService.kt

class NotificationListenerExampleService : NotificationListenerService() {

    override fun onNotificationPosted(sbn: StatusBarNotification) {
        Log.d("AppLog", "sbn:" + sbn.toString())
        cancelNotification(sbn.key)
    }

    override fun onListenerConnected() {
        super.onListenerConnected()
        Log.d("AppLog", "onListenerConnected")
    }

    override fun onNotificationRemoved(sbn: StatusBarNotification) {}
}

清单:

<application
  android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"
  tools:ignore="AllowBackup,GoogleAppIndexingWarning">
  <activity android:name=".MainActivity">
    <intent-filter>
      <action android:name="android.intent.action.MAIN"/>

      <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
  </activity>

  <service
    android:name=".NotificationListenerExampleService" android:label="@string/service_label" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
    <intent-filter>
      <action android:name="android.service.notification.NotificationListenerService"/>
    </intent-filter>
  </service>
</application>

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private val isNotificationServiceEnabled: Boolean
        get() {
            val pkgName = packageName
            val flat = Settings.Secure.getString(contentResolver, "enabled_notification_listeners"")
            if (!TextUtils.isEmpty(flat)) {
                val names = flat.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                for (i in names.indices) {
                    val cn = ComponentName.unflattenFromString(names[i])
                    if (cn != null) {
                        if (TextUtils.equals(pkgName, cn.packageName)) {
                            return true
                        }
                    }
                }
            }
            return false
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (!isNotificationServiceEnabled) {
            startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
        }
    }

}

问题

  1. 为什么我的代码无法删除正在进行的通知?
  2. 我找到的应用程序为何能做到?它是做什么的?
  3. 此API在通知方面提供了多少?看一下文档,似乎可以做很多事情:阅读通知,关闭它们,获取有关它们的各种信息,...但是我找不到其他功能:是否可以修改通知?要更改其优先级?为了使其避免成为平淡无奇的通知,而只是放在通知抽屉中?可以触发通知操作吗?

1 个答案:

答案 0 :(得分:2)

我使用此代码构建通知。它具有标题,文本,图标, contentAction 作为广播消息。我希望这足以回答您的问题)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            val notChannel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT)
            notManager.createNotificationChannel(notChannel)
        }

        val builder = NotificationCompat.Builder(this@MainActivity, CHANNEL_ID)

        val contentIntent = Intent(NOT_ACTION)
        contentIntent.putExtra(EXTRA_NOT_ID, notificationId)

        val contentAction = PendingIntent.getBroadcast(this@MainActivity, NOT_REQ_CODE, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT)

        builder.setContentTitle("Notification $notificationId")
                .setContentText("Awesome text for $notificationId notification")
                .setContentIntent(contentAction)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setAutoCancel(true)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setCategory(NotificationCompat.CATEGORY_MESSAGE)
                .setDefaults(NotificationCompat.DEFAULT_ALL)

        NotificationManagerCompat
                .from(this@MainActivity)
                .notify(notificationId++, builder.build())

1。您可以触发通知操作吗?

是的。。您可以从Notification获取StatusBarNotification,然后获取PendingIntent进行操作并加以使用。
例如,我要触发通知的 contentAction

override fun onNotificationPosted(sbn: StatusBarNotification) {
    super.onNotificationPosted(sbn)

    Log.d(TAG, "Gotcha notification from ${sbn.packageName}")

    if (sbn.packageName == TARGET_PACKAGE) {
        val contentIntent = sbn.notification.contentIntent
        contentIntent.send()
    }
}

此代码将触发发送广播消息。但是它将忽略.setAutoCancel(true)通知,因此您需要像这样手动处理

    if (sbn.packageName == TARGET_PACKAGE) {
        val contentIntent = sbn.notification.contentIntent
        contentIntent.send()
        if (sbn.notification.flags and Notification.FLAG_AUTO_CANCEL != 0) {
            cancelNotification(sbn.key)
        }
    }

2。 NCleaner 如何取消正在进行的通知?

我想到的第一种方法是像这样使用NotificationListenerService.snoozeNotification

if (sbn.packageName == TARGET_PACKAGE) {
        snoozeNotification(sbn.key, Long.MAX_VALUE - System.currentTimeMillis())
}

我有99%的把握确定NCLeaner使用此代码。因为它不支持Pre-O设备的此功能。

注意。您不能只使用Long.MAX_VALUE,通知将被延后几秒钟,然后再次出现。在Pixel 2 8.1上进行了测试。

3。您可以修改现有通知吗?

否,您不能。只有通知所有者应用可以修改它。

P.S。

我还尝试通过隐藏方法和NotificationManagerNotificationListenerService的字段克服反射的限制。所有尝试的结果是:

  

由以下原因引起:java.lang.SecurityException:调用uid 10210给出了uid.10211拥有的包com.ns.notificationsender。

因此,您的应用必须是系统之一,否则root可以帮助成功。但是我没有这样的设备。