Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cancel an ongoing notification of another app?

Background

I've found an app that somehow hides heads-up notifications, including even ongoing notifications (used by foreground services), called NCleaner .

I was wondering how such a thing works.

The problem

There isn't much information of how to control notifications of other apps. Only of the current app, and for some reason I've failed to cancel an ongoing notification, but I've succeeded canceling a normal one.

What I've found

I've found this sample showing how to monitor notifications. After searching on the docs about NotificationListenerService, I've also found how to cancel specific notifications of other apps.

However, it doesn't work for ongoing notifications.

For testing of normal notifications I just sent myself an email or wrote something on PushBullet app via my PC.

For testing of ongoing notifications, I've installed and ran my spare time app (here), which shows an ongoing notification right on the beginning of the app, starting from Android O. In fact, it can even hide the notifications of the OS, of USB connected and debugging (just connect the device to the PC via USB) ...

Here's the code I've made, based on the sample, to cancel all notifications it gets:

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) {}
}

manifest:

<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"))
        }
    }

}

The questions

  1. How come my code can't remove ongoing notifications?
  2. How come the app I've found can do it? What does it do?
  3. Just how much does this API provide in terms of notifications? Looking at the docs, it seems it can do a lot: read notifications, dismiss them, get all kinds of information about them, ... But I can't find some other features: Is it possible to modify a notification? To change its priority? To make it avoid being a heads-up-notification and just be in the notification drawer? Is it possible to trigger an action of a notification?
like image 673
android developer Avatar asked Jul 30 '18 05:07

android developer


People also ask

How do I get rid of a notification that won't go away?

First, press-and-hold on the persistent notification you want to remove. Another option is to swipe the notification left or right, and then tap on the cogwheel icon shown next to it. Next, tap on the switch next to Permanent to disable it, and then press Save.

How do I dismiss app notifications?

To dismiss a notification, touch it and swipe left or right. Tap the dismiss icon to dismiss all notifications. On newer versions of Android, you can manage some notifications from the lock screen. Double-tap a notification to open the app or swipe left or right to dismiss the notification.

How do I get rid of system notifications on Android?

Step 1: Swipe down from the top to expand the Notification Shade. Step 2: Long-press on the notification and then tap the Info icon. Step 3: On the app management screen, tap the Show Notifications box. Note: All notifications from that app will be turned off.


1 Answers

I used this code for notification building. It has title, text, icon, contentAction as Broadcast Message. It is enough for answering your questions, I hope)

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. Can you trigger an actions of notification?

Yes, you can. You can get Notification from StatusBarNotification, and then get PendingIntents for actions and use them.
For example, I want to trigger a contentAction of notification

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()
    }
}

This code will trigger sending of Broadcast message. BUT it will ignore a .setAutoCancel(true) of notification, so you need to handle it manually like this

    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. How NCleaner cancels ongoing notifications?

The first way which comes on my mind is usage of NotificationListenerService.snoozeNotification like this

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

I'm 99% percent sure, that NCLeaner uses this code. Because it doesn't support this feature for pre-O devices.

Note. You can't just use Long.MAX_VALUE, notification will be snoozed for a couple of seconds, and then appear again. Tested on Pixel 2 8.1.

3. Can you modify an existing notification?

No, you cannot. Only notification owner app can modify it.

P.S.

I also tried to overcome limits with reflection via hidden methods and fields of NotificationManager and NotificationListenerService. The result of all attempts is:

Caused by: java.lang.SecurityException: Calling uid 10210 gave package com.ns.notificationsender which is owned by uid 10211

So you app need to be system one, or maybe root can help to succeed. But I don't have such device.

like image 165
Ufkoku Avatar answered Sep 20 '22 01:09

Ufkoku