Android 4.1: How to check notifications are disabled for the application?
Asked Answered
C

6

103

Android 4.1 offers the user a check box to disable notifications for a specific application.

However, as a developer we have no way to know whether a call to notify was effective or not.

I really need to check if the notifications are disabled for the current application but I can't find any setting for that in the API.

Is there ever a way to check this setting in the code?

Centrality answered 25/7, 2012 at 11:58 Comment(3)
You really shouldn't concern yourself with it. Just assume your notification was successful. If the user has explicitly disabled your notifications, then he/she probably had good reason to do so, and your application should not care whether the notification was displayed or not.Saddlecloth
I explained the reason in the first anwser's comments.Centrality
Here is the issue to star/track code.google.com/p/android/issues/detail?id=38482 Really need this....Aquino
B
154

You can't 100% can't.

It is asked in this Google I/O 2012 video and the Project lead for the new notifications declares that you can't.


Edit

2016 update: Now you can check it, as said in this Google I/O 2016 video.

Use NotificationManagerCompat.areNotificationsEnabled(), from support library, to check if notifications are blocked on API 19+. The versions below API 19 will return true (notifications are enabled).

enter image description here

Baca answered 25/7, 2012 at 12:25 Comment(11)
There is nothing in the video showing we can't read this setting. Just to be clear: I just want to be able to read the current state of the check box, not altering it. I am afraid you did not understand my question.Centrality
I get the question. You want to know if the user has blocked your notifications from going into the system tray. I was sure they said it in that talk, I've seen all the GoogleIO talks and they definitely say you can't find this out. Also why would you want to, you shouldn't worry about it.Baca
We need this because we show 1 notification at a time (which can be in-app or in the system bar). If a system notification is displayed, we don't show an in app banner and vice versa. If we can't know whether a notification is displayed or not, we can't manage its life cycle anymore. I guess we have to entirely change the way we manage notifications now...Centrality
@GuillaumePerrot but you do know if your activity is in the foreground. Why don't you reverse your logic and if your application is on the screen show it in app otherwise show it in the system bar. You could always give the user a setting/preference that they get in app and the status bar. n.b if they are using your app and want to use your app they won't disable notifications from you!Baca
In fact the system or in-app flag is determined from server side by a developer (and our code is a SDK). What we plan to do is to let multiple system notifications being visible at the same time while keeping the rule to show only 1 in-app notification at a time.Centrality
FYI, the question is asked (and answered) at 48:05 in the video (during the Q&A) with one short word...No. youtube.com/…Aureolin
Based off of the edit here can some one verify that checking if notifications are enabled is possible? I don't see the areNotificationsEnabled() method any where in the docs.Rotberg
@Rotberg updated answer with the full link. NotificationManagerCompat.areNotificationsEnabled().Unadvised
@Rotberg You need to use NotificationManagerCompat.from(ctx).areNotificationsEnabled()Exterminate
Does NotificationManagerCompat.areNotificationsEnabled() work for modified Android system such as MIUI or Huawei phone?Unlace
Note that on Android 4.3,NotificationManagerCompat.from(ctx).areNotificationsEnabled(‌​) always returns true, whereas the reflection approach returned the correct value when I tested it on the official emulator.J
G
48

Answer from @blundell is correct but there is a minor change in newer versions.

NotificationManagerCompat.from(context).areNotificationsEnabled()
Goober answered 2/1, 2017 at 16:58 Comment(0)
K
39

Actually this is pretty easy to do:

/**
 * Created by desgraci on 5/7/15.
*/
public class NotificationsUtils {

    private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
    private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";

    public static boolean isNotificationEnabled(Context context) {

        AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);

        ApplicationInfo appInfo = context.getApplicationInfo();

        String pkg = context.getApplicationContext().getPackageName();

        int uid = appInfo.uid;

        Class appOpsClass = null; /* Context.APP_OPS_MANAGER */

        try {

            appOpsClass = Class.forName(AppOpsManager.class.getName());

            Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);

            Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
            int value = (int)opPostNotificationValue.get(Integer.class);

            return ((int)checkOpNoThrowMethod.invoke(mAppOps,value, uid, pkg) == AppOpsManager.MODE_ALLOWED);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return false;
    }
}
Key answered 7/5, 2015 at 17:31 Comment(16)
At the time question was posted, Android 4.1 was current, this is for Android 4.4+ only and seems to use reflection and documentation does not recommend using it for a non system app.Centrality
@GuillaumePerrot actually, you're right about reflection, but again, the documentation and official statements from android say that you cannot do it, you can also stick to that. Sorry about the version issue, I cannot help you with that. If your client/solution requires it, well, then you might want to consider raising a little bit the required version since is the SDK that is limiting you at that point. Let me know if you find an alternative way.Key
Fair enough but you should assume return true if you cannot get the information. At least in my use case it makes more sense. Or have default value as a parameter in the static function to make it more reusable.Centrality
@Rahul Matte, GlobalContext is just an utility class that I use to keep a reference to a Context, you can pass a Context through the method if you are not using/or willing to use that structure. Hope this helps!Key
@Key Why can't we call checkOpNoThrow() directly instead of using reflection?Aloha
@Xi Wei you can actually do it, it is not open on some older phones though, that's why I left the reflection, in case someone needed how to force itKey
@desgraci, thank you! That's why it didn't work when I tried to use it directly.Aloha
I can't make this work: context.getSystemService(Context.APP_OPS_SERVICE); It says: This inspection looks at Android API calls that have been annotated with various support annotations (such as RequiresPermission or UiThread) and flags any calls that are not using the API correctly as specified by the annotations. Any help?Unlace
@Unlace I cannot see your code to check what is happening, seems to be something from outside the snippet, would you mind to open a question so we can look through it?Key
Looking at the source, it seems the Google engineers used a very similar reflection approach in the v.24 support library NotificationManagerCompat.areNotificationsEnabled(). Is this a common way of checking for permissions or do you think they were "inspired' by your solution? Also, how did you know OP_POST_NOTIFICATION was the value that was needed?Exhibitioner
I hope they not :p but coming from google XD, since this uses reflection, the OP_POST_NOTIFICATION was reading through the code on the grep after finding myself struggling with the same issue.Key
use hardcode "android.app.AppOpsManager" instead of AppOpsManager class. see Android 4.1 and AppOpsManager.Harden
@TonyChan, it definitely looks 'inspired' to me! They even copied the bug where .get(Integer.class) was called instead of .get(null)!J
@jiashie, I just spent some time investigating the link you posted, and I can't see how the change they made in their code would make any difference. I tested it out, and it still didn't work in Android < 4.3.J
I've noticed that this is generally considered to only work in Android 4.4 and above, but it actually worked in the Android 4.3 emulator for me.J
I used this before but seems not working on Android 8, 9.Dad
N
5

If you are using Xamarin and you need this answer you can use this code:

//return true if this option is not supported.
public class NotificationsUtils 
{
    private const String CHECK_OP_NO_THROW = "checkOpNoThrow";
    private const String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";

    public static bool IsNotificationEnabled(global::Android.Content.Context context) {

        AppOpsManager mAppOps = (AppOpsManager) context.GetSystemService(global::Android.Content.Context.AppOpsService);

        ApplicationInfo appInfo = context.ApplicationInfo;

        String pkg = context.ApplicationContext.PackageName;

        int uid = appInfo.Uid;

        try {

            var appOpsClass = Java.Lang.Class.ForName("android.app.AppOpsManager");
            var checkOpNoThrowMethod = appOpsClass.GetMethod(CHECK_OP_NO_THROW,Java.Lang.Integer.Type,Java.Lang.Integer.Type,new Java.Lang.String().Class);//need to add String.Type

            var opPostNotificationValue = appOpsClass.GetDeclaredField (OP_POST_NOTIFICATION);
            var value = (int)opPostNotificationValue.GetInt(Java.Lang.Integer.Type);
            var mode = (int)checkOpNoThrowMethod.Invoke(mAppOps,value, uid, pkg);
            return (mode == (int)AppOpsManagerMode.Allowed);

        } catch (Exception) 
        {
            System.Diagnostics.Debug.WriteLine  ("Notification services is off or not supported");
        } 
        return true;
    }
}
Nipper answered 2/2, 2016 at 6:11 Comment(2)
@AdamPedley AreNotificationsEnabled() was added in API 24 developer.android.com/reference/android/app/…Stelmach
You are correct, must have misread it previously. I deleted my original comment, so as to not confuse anyone.Griffin
A
4

It seems like there is no way to query notification state.

I recommend this:

  • Design you application with notifications.
  • Let user to disable notifications from application's settings.
  • Check whether notifications are clicked. If user clicks notification, save this to preferences.
  • In your app, if notification setting is on, and if user is Android 4.1+ (API 16), but if user doesn't click notification for some days / weeks, assume that user disabled notifications.

Not 100% correct. But this gives an opinion.
For example if user doesn't click any app notification for 10-15 days, probably he disabled it

Aura answered 8/5, 2014 at 12:36 Comment(2)
This is a very broad and abstract approach.Reminiscent
This is the best approach! We are doing this in our application and you can precisely say, if the notifications are disabled. PendingIndent for EVERY action and save into preference. Don't forget to reset if the smartphone got restarted.Allege
Q
4

I use this method to check whether the notifications are enabled or not, the above-mentioned methods will work for checking whether notifications enabled or not. But from Android 8 onwards for creating notifications we have to create a channel first, so from Oreo, we have to check for your notification channel enabled or not.

    /**
     * Checking Whether notifications are enabled or not
     * @return true if notifications are enabled otherwise false
     */
    public static final String CHANNEL_ID = "your_channel_id";

    private boolean isNotificationChannelEnabled(){
        if(NotificationManagerCompat.from(this).areNotificationsEnabled()) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                NotificationChannel channel = manager.getNotificationChannel(CHANNEL_ID);
                if (channel == null)
                    return true; //channel is not yet created so return boolean
                // by only checking whether notifications enabled or not
                return channel.getImportance() != NotificationManager.IMPORTANCE_NONE;
            }
            return true;
        }
        return false;
    }

Quadricycle answered 18/4, 2020 at 12:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.