Android 4.1: как проверить, отключены ли уведомления для приложения?

98

Android 4.1 предлагает пользователю флажок для отключения уведомлений для определенного приложения.

Однако, как разработчик, мы не можем узнать, был ли вызов для уведомления эффективным или нет.

Мне действительно нужно проверить, отключены ли уведомления для текущего приложения, но я не могу найти никаких настроек для этого в API.

Есть ли способ проверить эту настройку в коде?

Гийом Перро
источник
1
Тебе действительно не стоит этим заниматься. Просто предположите, что ваше уведомление прошло успешно. Если пользователь явно отключил ваши уведомления, то у него, вероятно, были веские причины для этого, и ваше приложение не должно заботиться о том, отображалось ли уведомление или нет.
Кевин Коппок
Я объяснил причину в первых комментариях ответчика.
Guillaume Perrot
1
Вот проблема с пометкой / отслеживанием code.google.com/p/android/issues/detail?id=38482 Действительно нужно это ....
brandall

Ответы:

147

Вы не можете на 100% не можете.

В этом видео Google I / O 2012 об этом спрашивают, и руководитель проекта по новым уведомлениям заявляет, что вы не можете.


редактировать

Обновление 2016: Теперь вы можете проверить это, как сказано в этом видео Google I / O 2016 .

Используйте NotificationManagerCompat.areNotificationsEnabled()из библиотеки поддержки, чтобы проверить, заблокированы ли уведомления в API 19+. Версии ниже API 19 вернут true (уведомления включены).

введите описание изображения здесь

Blundell
источник
2
В видео нет ничего, что мы не могли бы прочитать эту настройку. Для ясности: я просто хочу иметь возможность читать текущее состояние флажка, не изменяя его. Боюсь, вы не поняли моего вопроса.
Guillaume Perrot
2
Нам это нужно, потому что мы показываем по одному уведомлению за раз (которое может быть в приложении или на системной панели). Если отображается системное уведомление, мы не показываем баннер в приложении, и наоборот. Если мы не можем знать, отображается уведомление или нет, мы больше не можем управлять его жизненным циклом. Думаю, нам нужно полностью изменить способ управления уведомлениями сейчас ...
Гийом Перро
9
К вашему сведению, вопрос задается (и отвечает) в 48:05 на видео (во время вопросов и ответов) одним коротким словом ... Нет. youtube.com/…
Devunwired
2
@Stavros_S обновил ответ с полной ссылкой. NotificationManagerCompat.areNotificationsEnabled () .
Sufian
17
@Stavros_S Вам нужно использоватьNotificationManagerCompat.from(ctx).areNotificationsEnabled()
AlexAndro
39

Ответ от @blundell правильный, но в новых версиях есть незначительные изменения.

NotificationManagerCompat.from(context).areNotificationsEnabled()
Пракаш
источник
38

На самом деле это довольно просто сделать:

/**
 * 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;
    }
}
Desgraci
источник
3
В то время, когда был опубликован вопрос, текущая версия Android 4.1, это только для Android 4.4+ и, похоже, использует отражение, а документация не рекомендует использовать его для несистемных приложений.
Гийом Перро
@GuillaumePerrot на самом деле, вы правы насчет размышлений, но опять же, документация и официальные заявления от Android говорят, что вы не можете этого сделать, вы также можете придерживаться этого. Извините за проблему с версией, я не могу вам с этим помочь. Если ваш клиент / решение требует этого, тогда вы можете подумать о том, чтобы немного поднять требуемую версию, так как SDK ограничивает вас в этот момент. Дайте мне знать, если найдете альтернативный способ.
desgraci
1
Достаточно справедливо, но вы должны предположить, что return true, если вы не можете получить информацию. По крайней мере, в моем случае это имеет больше смысла. Или укажите значение по умолчанию в качестве параметра в статической функции, чтобы сделать ее более пригодной для повторного использования.
Гийом Перро
1
@Rahul Matte, GlobalContext - это просто служебный класс, который я использую для хранения ссылки на контекст, вы можете передать контекст через метод, если вы не используете / или не хотите использовать эту структуру. Надеюсь это поможет!
desgraci
1
Надеюсь, что нет: p, но исходящий из Google XD, поскольку здесь используется отражение, OP_POST_NOTIFICATION читал код в grep после того, как обнаружил, что борюсь с той же проблемой.
desgraci
5

Если вы используете Xamarin и вам нужен этот ответ, вы можете использовать этот код:

//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
источник
@AdamPedley AreNotificationsEnabled () был добавлен в API 24 developer.android.com/reference/android/app/…
Sune Kjærgård 01
Вы правы, должно быть, раньше неправильно это читали. Я удалил свой оригинальный комментарий, чтобы никого не запутать.
Адам Педли
5

Похоже, что нет способа запросить состояние уведомления.

Я рекомендую это:

  • Создайте свое приложение с уведомлениями.
  • Разрешить пользователю отключать уведомления в настройках приложения.
  • Проверьте, не нажимаются ли уведомления. Если пользователь нажимает на уведомление, сохраните его в настройках.
  • В вашем приложении, если настройка уведомлений включена, и если пользователь Android 4.1+ (API 16), но если пользователь не нажимает на уведомление в течение нескольких дней / недель, предположим, что пользователь отключил уведомления.

Не на 100% правильно. Но это дает мнение.
Например, если пользователь не нажимает на уведомление приложения в течение 10-15 дней, возможно, он отключил его.

транте
источник
1
Это очень широкий и абстрактный подход.
Игорь Ганапольский
Это лучший подход! Мы делаем это в нашем приложении, и вы можете точно сказать, отключены ли уведомления. PendingIndent для КАЖДОГО действия и сохраните в предпочтении. Не забудьте выполнить сброс, если смартфон перезапустился.
JacksOnF1re
2

Я использую этот метод, чтобы проверить, включены ли уведомления или нет, вышеупомянутые методы будут работать для проверки, включены ли уведомления или нет. Но начиная с Android 8 для создания уведомлений мы должны сначала создать канал , поэтому в Oreo мы должны проверить, включен ли ваш канал уведомлений или нет .

    /**
     * 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;
    }
Сай Паван Кумар
источник