【问题标题】:How to cancel an ongoing notification of another app?如何取消另一个应用程序的正在进行的通知?
【发布时间】:2019-01-06 07:38:51
【问题描述】:

背景

我发现了一个名为NCleaner 的应用程序,它以某种方式隐藏了提醒通知,甚至包括正在进行的通知(由前台服务使用)。

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

问题

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

我发现了什么

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

但是,它不适用于持续通知。

为了测试正常通知,我只是通过我的电脑给自己发了一封电子邮件或在 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. 我找到的这个app怎么能做到?它有什么作用?
  3. 这个 API 在通知方面提供了多少?查看文档,它似乎可以做很多事情:阅读通知,关闭它们,获取有关它们的各种信息,......但我找不到其他一些功能:是否可以修改通知?改变它的优先级?为了避免成为提醒通知而只是在通知抽屉中?是否可以触发通知的动作?

【问题讨论】:

  • 也许它会暂停一个无法清除或正在进行的通知? developer.android.com/reference/android/service/notification/…
  • 可以触发通知动作吗?可能可以,因为它可以通过StatusBarNotification访问Notification,并且可以从Notification获取PendingIntents
  • @Ufkoku 似乎运作良好。为什么不把它作为答案让我接受呢?另外,你知道这个的替代品吗,因为它需要 Android O ?
  • 另外,你能回答其他问题吗?
  • 我打算明天或周一写一个完整的答案,如果没有人会在我之前完成)

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


【解决方案1】:

我使用此代码构建通知。它具有标题、文本、图标、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。可以触发通知动作吗?

是的,你可以。你可以从StatusBarNotification获取Notification,然后获取PendingIntents进行操作并使用它们。
比如我想触发一个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。您可以修改现有通知吗?

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

附言

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

Caused by: java.lang.SecurityException: Calling uid 10210 给了包 com.ns.notificationsender 由 uid 10211 拥有

所以你的应用程序需要是系统一,或者也许 root 可以帮助成功。但我没有这样的设备。

【讨论】:

  • 1.即使不是您自己的通知,send 也会起作用? 2.由于某种原因,在Android P上使用Long.MAX_VALUE对我有用,但也许这只是一个持续时间,所以设置100年就足够了吗? 3. 你的意思是你收到了来自onNotificationPosted 的通知,然后你暂停了它,然后你使用反射修改它并作为你自己的发送?你试图做什么样的改变?它在哪一步崩溃?可能是在通知的notify 电话上,对吧?
  • @androiddeveloper 1. 是的,我创建了用于分离 android 应用程序。 NotificationSender 和 NotificationListener。监听者可以使用发送者提供的通知的send()。 2. 是的,第二个参数是以毫秒为单位的持续时间。所以100年就足够了。在我的 Pixel 2 8.1 Long.MAX_VALUE 上会导致奇怪的行为。
  • @androiddeveloper 3. 您可以在sbn.notification 中更改您想要的任何字段,但我没有找到更新状态栏中通知的方法。当您致电notify 时,它的行为就像您发送自己的应用通知,而不是更新捕获的通知。当我尝试使用 uid 和包时,就像通知所有者应用程序一样 - SecurityException.
  • 1.好的。 2. 我明白了。 3.哦,你的意思是你没有打盹,你使用相同的通知并尝试修改它。对吗?
  • @androiddeveloper 是的。在我的接收器中,我更改了捕获通知的标题。但是我没有找到一种方法来对状态栏进行更改。可以修改捕获通知,删除原始通知,并将捕获通知发布为您自己的,但在这种情况下会出现问题。最明显的是 ID 冲突、通知通道处理,如果所有者应用程序将尝试更新原始通知,它将抛出新通知,而不是更新,因为您的接收器已将其删除,因此您必须手动处理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-03
相关资源
最近更新 更多