Android 4.1: Jak sprawdzić, czy powiadomienia są wyłączone dla aplikacji?

98

Android 4.1 oferuje użytkownikowi pole wyboru umożliwiające wyłączenie powiadomień dla określonej aplikacji.

Jednak jako programista nie mamy możliwości sprawdzenia, czy wezwanie do powiadomienia było skuteczne, czy nie.

Naprawdę muszę sprawdzić, czy powiadomienia są wyłączone dla bieżącej aplikacji, ale nie mogę znaleźć żadnego ustawienia w interfejsie API.

Czy istnieje sposób, aby sprawdzić to ustawienie w kodzie?

Guillaume Perrot
źródło
1
Naprawdę nie powinieneś się tym przejmować. Załóżmy, że Twoje powiadomienie się powiodło. Jeśli użytkownik wyraźnie wyłączył powiadomienia, prawdopodobnie miał ku temu dobry powód, a Twoja aplikacja nie powinna dbać o to, czy powiadomienie zostało wyświetlone, czy nie.
Kevin Coppock,
Wyjaśniłem powód w komentarzach pierwszej odpowiedzi.
Guillaume Perrot
1
Oto problem, aby oznaczyć / śledzić code.google.com/p/android/issues/detail?id=38482 Naprawdę tego potrzebuję ...
brandall

Odpowiedzi:

147

Nie możesz w 100% nie.

Zostało to poproszone w tym filmie Google I / O 2012, a kierownik projektu dla nowych powiadomień deklaruje, że nie możesz.


Edytować

Aktualizacja 2016: Teraz możesz to sprawdzić, jak powiedziano w tym filmie Google I / O 2016 .

Skorzystaj NotificationManagerCompat.areNotificationsEnabled()z biblioteki pomocy technicznej, aby sprawdzić, czy powiadomienia są blokowane w API 19+. Wersje poniżej API 19 zwrócą prawdę (powiadomienia są włączone).

wprowadź opis obrazu tutaj

Blundell
źródło
2
W filmie nie ma nic, co pokazuje, że nie możemy odczytać tego ustawienia. Dla jasności: chcę tylko móc odczytać aktualny stan pola wyboru, nie zmieniając go. Obawiam się, że nie zrozumiałeś mojego pytania.
Guillaume Perrot
2
Potrzebujemy tego, ponieważ wyświetlamy 1 powiadomienie naraz (które może być w aplikacji lub na pasku systemowym). Jeśli zostanie wyświetlone powiadomienie systemowe, nie pokazujemy banera w aplikacji i odwrotnie. Jeśli nie wiemy, czy powiadomienie jest wyświetlane, czy nie, nie możemy już zarządzać jego cyklem życia. Myślę, że musimy teraz całkowicie zmienić sposób, w jaki zarządzamy powiadomieniami ...
Guillaume Perrot,
9
Do Twojej wiadomości, pytanie jest zadawane (i udzielane) o godzinie 48:05 na wideo (podczas pytań i odpowiedzi) jednym krótkim słowem ... Nie. youtube.com/…
Devunwired
2
@Stavros_S zaktualizowana odpowiedź z pełnym linkiem. NotificationManagerCompat.areNotificationsEnabled () .
Sufian
17
@Stavros_S Musisz użyćNotificationManagerCompat.from(ctx).areNotificationsEnabled()
AlexAndro
39

Odpowiedź od @blundell jest poprawna, ale w nowszych wersjach jest niewielka zmiana.

NotificationManagerCompat.from(context).areNotificationsEnabled()
Prakash
źródło
38

Właściwie jest to całkiem łatwe:

/**
 * 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
źródło
3
W momencie opublikowania pytania Android 4.1 był aktualny, dotyczy to tylko Androida 4.4+ i wydaje się korzystać z refleksji, a dokumentacja nie zaleca używania go w przypadku aplikacji niesystemowej.
Guillaume Perrot
@GuillaumePerrot właściwie masz rację co do refleksji, ale znowu, dokumentacja i oficjalne oświadczenia Androida mówią, że nie możesz tego zrobić, możesz też się tego trzymać. Przepraszamy za problem z wersją, nie mogę Ci w tym pomóc. Jeśli twój klient / rozwiązanie tego wymaga, cóż, możesz rozważyć nieco podniesienie wymaganej wersji, ponieważ jest to SDK, który ogranicza Cię w tym momencie. Daj mi znać, jeśli znajdziesz alternatywny sposób.
desgraci
1
W porządku, ale powinieneś założyć, że zwrot jest prawdziwy, jeśli nie możesz uzyskać informacji. Przynajmniej w moim przypadku ma to większy sens. Lub mieć wartość domyślną jako parametr w funkcji statycznej, aby była bardziej przydatna do ponownego użycia.
Guillaume Perrot
1
@Rahul Matte, GlobalContext to po prostu klasa narzędziowa, której używam, aby zachować odniesienie do kontekstu, możesz przekazać Context przez metodę, jeśli nie używasz / lub chcesz używać tej struktury. Mam nadzieję że to pomoże!
desgraci
1
Mam nadzieję, że nie: p, ale pochodzą z Google XD, ponieważ używa on odbicia, OP_POST_NOTIFICATION czytał kod na grep po tym, jak zmagałem się z tym samym problemem.
desgraci
5

Jeśli korzystasz z platformy Xamarin i potrzebujesz tej odpowiedzi, możesz użyć tego kodu:

//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
źródło
@AdamPedley AreNotificationsEnabled () został dodany w API 24 developer.android.com/reference/android/app/…
Sune Kjærgård
Masz rację, musiałeś ją wcześniej źle przeczytać. Usunąłem swój oryginalny komentarz, żeby nikogo nie pomylić.
Adam Pedley
5

Wygląda na to, że nie ma możliwości sprawdzenia stanu powiadomienia.

Polecam:

  • Zaprojektuj swoją aplikację z powiadomieniami.
  • Pozwól użytkownikowi wyłączyć powiadomienia z ustawień aplikacji.
  • Sprawdź, czy kliknięto powiadomienia. Jeśli użytkownik kliknie powiadomienie, zapisz to w preferencjach.
  • W Twojej aplikacji, jeśli ustawienie powiadomień jest włączone i jeśli użytkownik ma system Android 4.1+ (API 16), ale jeśli użytkownik nie kliknie powiadomienia przez kilka dni / tygodni, załóżmy, że użytkownik wyłączył powiadomienia.

Nie w 100% poprawne. Ale to daje opinię.
Na przykład, jeśli użytkownik nie kliknie żadnego powiadomienia aplikacji przez 10-15 dni, prawdopodobnie je wyłączył

trante
źródło
1
To bardzo szerokie i abstrakcyjne podejście.
IgorGanapolsky
To najlepsze podejście! Robimy to w naszej aplikacji i możesz dokładnie określić, czy powiadomienia są wyłączone. PendingIndent dla KAŻDEJ akcji i zapisz w preferencjach. Nie zapomnij zresetować, jeśli smartfon został ponownie uruchomiony.
JacksOnF1re
2

Używam tej metody, aby sprawdzić, czy powiadomienia są włączone, czy nie, wyżej wymienione metody będą działać, aby sprawdzić, czy powiadomienia są włączone, czy nie. Ale od Androida 8 do tworzenia powiadomień musimy najpierw utworzyć kanał , więc z Oreo musimy sprawdzić, czy Twój kanał powiadomień jest włączony, czy nie .

    /**
     * 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
źródło