Accessing Android NotificationListenerService Settings
Asked Answered
P

5

16

Android has a new notification listener service as of 4.3: http://developer.android.com/about/versions/jelly-bean.html http://developer.android.com/reference/android/service/notification/NotificationListenerService.html

From the docs:

Notification access is disabled by default — apps can use a new Intent to take the user directly to the Settings to enable the listener service after installation.

I don't see the intent to fire documented anywhere. Perusing the Settings doc doesn't seem helpful: http://developer.android.com/reference/android/provider/Settings.html

Looking at the Settings class directly: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/provider/Settings.java

I see ACTION_NOTIFICATION_LISTENER_SETTINGS defined, but when using Android Studio and pointing at 4.3 ACTION_NOTIFICATION_LISTENER_SETTINGS can't be resolved:

Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);

Trying it more manually doesn't seem to work:

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName("com.android.settings", "android.settings.NOTIFICATION_LISTENER_SETTINGS");

edit: doing it the correct way as CommonsWare pointed out below:

Intent intent=new Intent("android.settings.NOTIFICATION_LISTENER_SETTINGS");

leads to a crash:

(android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.settings.NOTIFICATION_LISTENER_SETTINGS })

Am I missing something? I'm not sure how to send the user to the proper settings screen to enable this service in my app.

Polenta answered 25/7, 2013 at 15:14 Comment(7)
interesting question. Is it possible with the new API to get the notification text and the discription and so on?Sophistic
Yep, once the service is setup you can grab notifications directly (developer.android.com/reference/android/app/Notification.html)Polenta
Okay great! But there are no mathods to get the notification or discription text from a StatusBarNotificationSophistic
developer.android.com/reference/android/service/notification/… has a getNotification() methodPolenta
Yes but from there you have to work with the Parcel and that is not so easy to get all the different text messages from the notificationSophistic
Have you managed to get the notificationlistenerservice class to work in your app?#17912383Lethe
@Sophistic i think you are using sdk < 18.. that's way you can't see StatusBarNotificationTuroff
N
19

Am I missing something?

Well, in your last one, you are conflating an action string with a class name. The "manual" approach would be:

Intent intent=new Intent("android.settings.NOTIFICATION_LISTENER_SETTINGS");

In terms of why Android Studio is not finding Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS, that I can't say.


UPDATE

Based on the discussion in the comments, Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS is not in the Android SDK at present (marked with @hide). Also, the manifest for the Settings app has a slightly different version of the action string:

Intent intent=new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
Nerland answered 25/7, 2013 at 15:19 Comment(8)
Turns out ACTION_NOTIFICATION_LISTENER_SETTINGS has @hide in its documentation, which I'm guessing shouldn't be there - though I thought it would just hide the documentation, not make it impossible to resolve within Android Studio. Unfortunately trying it the way mentioned in your answer ends up with a crash: Caused by: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.settings.NOTIFICATION_LISTENER_SETTINGSPolenta
@powerj1984: "Turns out ACTION_NOTIFICATION_LISTENER_SETTINGS has @hide in its documentation" -- I don't see that in the source you linked to. "though I thought it would just hide the documentation" -- no, @hide removes the item from the SDK entirely. Regarding the ActivityNotFoundException, what are you testing on?Nerland
Line number 667 in the linked source doesn't have @hide? Weird, it does on my end. Testing on a Nexus 4 with 4.3Polenta
@powerj1984: Ah, I see. That @hide is in the comment. Usually, I see it outside of any JavaDoc comments, which is why I glossed over it. The fact that it's not showing up in the JavaDocs, though, means it must still be honored. My apologies for missing it. It's odd that it's not working, though, as I see that action in the manifest for Settings in the source code: github.com/android/platform_packages_apps_settings/blob/…Nerland
No worries at all about missing it - I thought maybe I linked the wrong source initially. I guess I'll check the bug tracker on this. It does seem bizarre that it isn't working using the string directly.Polenta
Oh, oops! I left the 'ACTION_' bit off the beginning of the string. Using that allows me to use the string directly.Polenta
@powerj1984: Now that is interesting. Usually, the string representation of the action does not have ACTION_ in it, which fits what the @hide entry in Settings has. However, the manifest entry has ACTION_ in it, so you need to have that in the action in the Intent as well. It will be interesting to see how they reconcile all of this later on. In the meantime, I will update the answer.Nerland
FYI I took the information gathered here and reported the bug on the issue tracker: code.google.com/p/android/issues/…Polenta
E
8

Additional to CommonsWare answer here is how to check if you have that permission

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {        
    if (!NotificationManagerCompat.getEnabledListenerPackages(this).contains(getPackageName())) {        //ask for permission
       Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
       startActivity(intent);
    }
}
Elgin answered 3/11, 2016 at 8:52 Comment(4)
You don't have to go over each of the items in the set to check if one is equal to the current one. It's much better to just use contains. Also shorter in code.Perzan
This is correct @androiddeveloper , I have changed the example as well.Elgin
You are asking permission, if package is enabled, but instead you need to ask only if it's not enabled!Mormon
@Mormon this is true, changedElgin
P
8

This is how you can use it today, as it will try to go to as few steps for the user as possible, to turn it on:

@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
fun getIntentForNotificationAccess(packageName: String, notificationAccessServiceClass: Class<out NotificationListenerService>): Intent =
        getIntentForNotificationAccess(packageName, notificationAccessServiceClass.name)


@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
fun getIntentForNotificationAccess(packageName: String, notificationAccessServiceClassName: String): Intent {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        return Intent(Settings.ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS)
                .putExtra(Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME, ComponentName(packageName, notificationAccessServiceClassName).flattenToString())
    }
    val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
    val value = "$packageName/$notificationAccessServiceClassName"
    val key = ":settings:fragment_args_key"
    intent.putExtra(key, value)
    intent.putExtra(":settings:show_fragment_args", Bundle().also { it.putString(key, value) })
    return intent
}

Example usage:

startActivityForResult(getIntentForNotificationAccess(packageName, NotiService::class.java), REQUEST_CODE)

or:

startActivityForResult(getIntentForNotificationAccess(packageName, NotiService::class.java.name), REQUEST_CODE)

Note that sadly this isn't possible for various other cases, sadly. Not even highlight&focus. For these cases, I've made these requests:

Perzan answered 16/9, 2020 at 6:39 Comment(5)
These magic keys are from hereMonadelphous
@Monadelphous Thank you. How did you find it? I can't reach this file. Do you know perhaps of a way to use these for system-alert-window permission too, for Android 11 ?Perzan
These args has any effect only for Android 9 (my app will be simply highlighted in list). I have tested with multiple versionsMonadelphous
@Monadelphous Too bad. Do you know which types of special permissions do handle this "magic"?Perzan
on my device s22 ultra android 13 i removed the if{Build.VERSION_CODES.R } block check and it working fine and highlighting my appRebel
I
2

Thanks @android developer for great answer, but I have a little clarification.

You MUST call startActivity inside try/catch because some OS (Honor/Huawei on Android 11) doesn't have "Settings.ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS", so this exception is thrown:

No Activity found to handle Intent { act=android.settings.NOTIFICATION_LISTENER_DETAIL_SETTINGS (has extras) }

Use in this way:

try {
    startActivity(getIntentForNotificationAccess(packageName, NotiService::class.java))
} catch (e: Exception) {
    if (Build.VERSION.SDK_INT >= 22) {
        try {
            startActivity(Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS))
        } catch (e1: Exception) {
            e1.printStackTrace()
        }
    }
}
Imply answered 19/2, 2022 at 10:42 Comment(0)
R
0

i have tested @android developer code and it was not working on my device(Samsung s22 ultra android 13) so i modified his code and now it is opening NotificationListenerService settings and highlighting filtering my app

 @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
fun getIntentForNotificationAccess(context: Context,  notificationAccessServiceClass: Class<out NotificationListenerService>): Intent {

    val intent:Intent
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        intent= Intent(Settings.ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS)
        intent.putExtra(Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME, ComponentName(context.packageName, notificationAccessServiceClass.name).flattenToString())
    }else{
         intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
    }
    val value = "${context.packageName}/${notificationAccessServiceClass.name}"
    val key = ":settings:fragment_args_key"
    intent.putExtra(key, value)
    intent.putExtra(":settings:show_fragment_args", Bundle().also { it.putString(key, value) })
    return intent
}
Rebel answered 14/7, 2023 at 11:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.