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?

Guillaume Perrot
  • 4,278
  • 3
  • 27
  • 37
  • 1
    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. – Kevin Coppock Jul 25 '12 at 13:15
  • I explained the reason in the first anwser's comments. – Guillaume Perrot Jul 25 '12 at 20:13
  • 1
    Here is the issue to star/track https://code.google.com/p/android/issues/detail?id=38482 Really need this.... – brandall Sep 03 '13 at 15:55

6 Answers6

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

Sufian
  • 6,405
  • 16
  • 66
  • 120
Blundell
  • 75,855
  • 30
  • 208
  • 233
  • 2
    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. – Guillaume Perrot Jul 25 '12 at 12:45
  • 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. – Blundell Jul 25 '12 at 14:09
  • 2
    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... – Guillaume Perrot Jul 25 '12 at 20:09
  • @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! – Blundell Jul 26 '12 at 07:12
  • 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. – Guillaume Perrot Jul 31 '12 at 12:25
  • 10
    FYI, the question is asked (and answered) at 48:05 in the video (during the Q&A) with one short word...No. http://www.youtube.com/watch?v=Yc8YrVc47TI&feature=player_detailpage#t=2885s – devunwired Nov 15 '12 at 21:12
  • 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. – Stavros_S Jun 13 '16 at 13:45
  • 2
    @Stavros_S updated answer with the full link. [NotificationManagerCompat.areNotificationsEnabled()](https://developer.android.com/reference/android/support/v4/app/NotificationManagerCompat.html#areNotificationsEnabled()). – Sufian Jun 24 '16 at 07:05
  • 18
    @Stavros_S You need to use `NotificationManagerCompat.from(ctx).areNotificationsEnabled()` – AlexAndro Nov 15 '16 at 16:06
  • Does NotificationManagerCompat.areNotificationsEnabled() work for modified Android system such as MIUI or Huawei phone? – neobie Mar 21 '17 at 07:22
  • 1
    Note that on Android 4.3,`NotificationManagerCompat.from(ctx).areNotificationsEnabled(‌​)` always returns `true`, whereas [the reflection approach](https://stackoverflow.com/a/30108004/238753) returned the correct value when I tested it on the official emulator. – Sam May 23 '17 at 00:47
48

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

NotificationManagerCompat.from(context).areNotificationsEnabled()
Wayne
  • 59,728
  • 15
  • 131
  • 126
Prakash
  • 7,794
  • 4
  • 48
  • 44
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;
    }
}
Sufian
  • 6,405
  • 16
  • 66
  • 120
desgraci
  • 1,191
  • 1
  • 11
  • 25
  • 3
    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. – Guillaume Perrot May 07 '15 at 23:42
  • @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. – desgraci Jul 30 '15 at 19:44
  • 1
    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. – Guillaume Perrot Jul 31 '15 at 02:20
  • 1
    @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! – desgraci Jul 31 '15 at 16:27
  • Thanks for the usefull answer – Darko Petkovski Sep 10 '15 at 10:59
  • @desgraci Why can't we call checkOpNoThrow() directly instead of using reflection? – Xi Wei Nov 17 '15 at 22:02
  • @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 it – desgraci Dec 11 '15 at 13:37
  • @desgraci, thank you! That's why it didn't work when I tried to use it directly. – Xi Wei Dec 14 '15 at 17:06
  • 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? – neobie May 17 '16 at 04:15
  • @neobie 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? – desgraci Jun 01 '16 at 23:33
  • Looking at the source, it seems the Google engineers used a very similar reflection approach in the v.24 support library [**NotificationManagerCompat.areNotificationsEnabled()**](https://github.com/android/platform_frameworks_support/blob/3619a79ce14c2d0d1e0c528ce1941c07796ce2fb/v4/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java#L30). 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? – Tony Chan Oct 10 '16 at 21:51
  • 1
    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. – desgraci Oct 18 '16 at 18:04
  • use hardcode "android.app.AppOpsManager" instead of AppOpsManager class. see [Android 4.1 and AppOpsManager](https://github.com/phonegap/phonegap-plugin-push/issues/495). – jiashie Nov 19 '16 at 07:10
  • @TonyChan, it definitely looks 'inspired' to me! They even copied the bug where `.get(Integer.class)` was called instead of `.get(null)`! – Sam May 23 '17 at 00:37
  • @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. – Sam May 23 '17 at 00:39
  • 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. – Sam May 23 '17 at 00:41
  • I used this before but seems not working on Android 8, 9. – Lazy Ninja May 08 '19 at 07:38
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;
    }
}
user3030630
  • 51
  • 1
  • 2
  • @AdamPedley AreNotificationsEnabled() was added in API 24 https://developer.android.com/reference/android/app/NotificationManager.html#areNotificationsEnabled() – Sune Kjærgård Aug 01 '17 at 11:14
  • You are correct, must have misread it previously. I deleted my original comment, so as to not confuse anyone. – Adam Aug 01 '17 at 11:37
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

Sufian
  • 6,405
  • 16
  • 66
  • 120
trante
  • 33,518
  • 47
  • 192
  • 272
  • 1
    This is a very broad and abstract approach. – IgorGanapolsky Mar 19 '15 at 20:14
  • 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. – JacksOnF1re Jun 11 '15 at 15:26
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;
    }

sai Pavan Kumar
  • 1,129
  • 12
  • 20