Jak odrzucić powiadomienie po kliknięciu akcji

142

Od poziomu API 16 (Jelly Bean) istnieje możliwość dodawania działań do powiadomienia za pomocą

builder.addAction(iconId, title, intent);

Ale kiedy dodam akcję do powiadomienia i akcja zostanie naciśnięta, powiadomienie nie zostanie odrzucone. Po kliknięciu samego powiadomienia można je odrzucić za pomocą

notification.flags = Notification.FLAG_AUTO_CANCEL;

lub

builder.setAutoCancel(true);

Ale oczywiście nie ma to nic wspólnego z działaniami związanymi z powiadomieniem.

Jakieś wskazówki? A może nie jest to jeszcze część API? Nic nie znalazłem.

endowzoner
źródło

Odpowiedzi:

154

Gdy zadzwoniłeś do powiadomienia w menedżerze powiadomień, nadałeś mu identyfikator - jest to unikalny identyfikator, którego możesz użyć, aby uzyskać do niego dostęp później (jest to od menedżera powiadomień:

notify(int id, Notification notification)

Aby anulować, zadzwoń:

cancel(int id)

z tym samym identyfikatorem. Więc, po prostu, musisz śledzić identyfikator lub ewentualnie umieścić go w paczce, którą dodajesz do intencji w PendingIntent?

Kaediil
źródło
25
Dzięki, to rozwiązało mój problem. Jednak nadal uważam, że jest to trochę zbyt skomplikowane. Zamiast po prostu udostępniać interfejs API do automatycznego odrzucania powiadomienia po naciśnięciu akcji, musisz pracować z przekazaniem intencji i identyfikatora powiadomienia, aby osiągnąć to samo.
endowzoner
2
Jeśli uważasz, że jest to skomplikowane, nie zaglądaj do aktualizacji powiadomienia (nie trać śledzenia tego identyfikatora) ani sprawdzania, czy jest wyświetlane, czy nie (interfejs API go nie śledzi, musisz) ...: P
Travis,
2
@Daksh: Zasadniczo dodajesz tag powiadomienia i identyfikator do swojej intencji, która zaczyna się po naciśnięciu twojej akcji. Dzięki tym dodatkowym informacjom możesz sprawdzić w działaniu początkowym, czy zostało uruchomione poprzez działanie powiadomienia.
endowzoner
5
Przykładowy kod z onCreate (): Dodatki pakietu = getIntent (). GetExtras (); if (extras! = null) {String tag = extras.getString (NotificationReceiver.INTENT_EXTRA_NOTIFICATION_TAG); int id = extras.getInt (NotificationReceiver.INTENT_EXTRA_NOTIFICATION_ID); if (NotificationReceiver.NOTIFICATION_ID == id && NotificationReceiver.NOTIFICATION_TAG.equals (tag)) {// działanie zostało rozpoczęte poprzez akcję powiadamiania, odrzuć // powiadomienie NotificationManager manager = (NotificationManager) getSystemService (Service.NOTIFICATION_SERVICE); manager.cancel (tag, id); }}
endowzoner
1
W nowym API masz notyfikację (String tag, int id, Notification notification) i odpowiednio anulować (String tag, int id)
Malachiasz
64

Okazało się, że jest to problem podczas korzystania z powiadomienia Heads Up Display firmy Lollipop. Zobacz wytyczne projektowe . Oto kompletny kod do zaimplementowania.

Do tej pory posiadanie przycisku „Odrzuć” było mniej ważne, ale teraz jest bardziej widoczne.

powiadomienie Head Up

Tworzenie powiadomienia

int notificationId = new Random().nextInt(); // just use a counter in some util class...
PendingIntent dismissIntent = NotificationActivity.getDismissIntent(notificationId, context);

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(NotificationCompat.PRIORITY_MAX) //HIGH, MAX, FULL_SCREEN and setDefaults(Notification.DEFAULT_ALL) will make it a Heads Up Display Style
        .setDefaults(Notification.DEFAULT_ALL) // also requires VIBRATE permission
        .setSmallIcon(R.drawable.ic_action_refresh) // Required!
        .setContentTitle("Message from test")
        .setContentText("message")
        .setAutoCancel(true)
        .addAction(R.drawable.ic_action_cancel, "Dismiss", dismissIntent)
        .addAction(R.drawable.ic_action_boom, "Action!", someOtherPendingIntent);

// Gets an instance of the NotificationManager service
NotificationManager notifyMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

// Builds the notification and issues it.
notifyMgr.notify(notificationId, builder.build());

NotificationActivity

public class NotificationActivity extends Activity {

    public static final String NOTIFICATION_ID = "NOTIFICATION_ID";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.cancel(getIntent().getIntExtra(NOTIFICATION_ID, -1));
        finish(); // since finish() is called in onCreate(), onDestroy() will be called immediately
    }

    public static PendingIntent getDismissIntent(int notificationId, Context context) {
        Intent intent = new Intent(context, NotificationActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        intent.putExtra(NOTIFICATION_ID, notificationId);
        PendingIntent dismissIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        return dismissIntent;
    }

}

AndroidManifest.xml (atrybuty wymagane, aby uniemożliwić SystemUI skupienie się na stosie wstecznym)

<activity
    android:name=".NotificationActivity"
    android:taskAffinity=""
    android:excludeFromRecents="true">
</activity>
aaronvargas
źródło
Mam wiele powiadomień z jednej aplikacji, a powiadomienia są ustawione na ciągłe powiadomienia. Chcę wyczyścić powiadomienie, gdy dodatek działa na szczególnym powiadomieniu
Prasad
3
Czy nie byłoby bardziej efektywne użycie BroadcastReceiver tutaj do odrzucenia powiadomienia? Oto dobry przykład, który pokazuje implementację, ale można ją jeszcze bardziej skrócić: stackoverflow.com/a/19745745/793150
alice.harrison
1
Rozwiązania działają, z wyjątkiem tego, że zestawy dodatków są przekazywane z zamiarem. Nie są one przekazywane do onCreate. Jednym ze sposobów jest użycie zmiennych statycznych. Czy ktoś wie, dlaczego dodatki intencyjne nie są przekazywane?
Baschi,
Dlaczego getDismissIntent działa tylko po umieszczeniu w NotificationActivity? Odrzucenie zamiaru nie działa, jeśli kod tworzenia PendingIntent zostanie umieszczony w klasie konstruktora powiadomień. Właśnie spędziłem 2 godziny nad tym problemem i nie mogę zrozumieć, dlaczego oczekujący zamiar MUSI zostać utworzony w działaniu. Czy ktoś może wyjaśnić, dlaczego tak jest?
Ray Li
getDismissIntent () to statyczna funkcja „pomocnicza”, która buduje poprawną intencję do użycia w komunikacji z NotificationActivity. W związku z tym są one zwykle dołączane do działania. Ale nie rozumiem, dlaczego tej statycznej funkcji nie można było umieścić w klasie konstruktora powiadomień, o ile uważnie ustawisz NOTIFICATION_ID i kontekst poprawnie.
Mike,
17

Odkryłem, że kiedy używasz przycisków akcji w rozszerzonych powiadomieniach, musisz napisać dodatkowy kod i jesteś bardziej ograniczony.

Musisz ręcznie anulować powiadomienie, gdy użytkownik kliknie przycisk akcji. Powiadomienie jest anulowane automatycznie tylko dla domyślnej akcji.

Również jeśli uruchomisz odbiornik transmisji za pomocą przycisku, szuflada powiadomień nie zamknie się.

Skończyło się na utworzeniu nowego NotificationActivity, aby rozwiązać te problemy. Ta pośrednia czynność bez interfejsu użytkownika anuluje powiadomienie, a następnie rozpoczyna działanie, które naprawdę chciałem rozpocząć od powiadomienia.

Wysłałem przykładowy kod w powiązanym poście. Kliknięcie akcji powiadomień systemu Android nie zamyka szuflady powiadomień .

Vicki
źródło
2
Szkoda, że ​​nadal nie wprowadzili tego do API ... To dość hakerskie robienie tego w ten sposób. Ale wciąż jedyny sposób, zwłaszcza jeśli nie masz żadnej kontroli nad celem docelowym, na przykład przeglądanie adresu URL.
Bogdan Zurac
Dzięki za informację, masz rację. Jednak użyłbym raczej usługi intentservice niż działania pośredniego
Tim
7

Zawsze możesz cancel()pochodzić Notificationz tego, co jest wywoływane przez działanie (np. W onCreate()działaniu powiązanym z PendingIntentdostawą addAction()).

CommonsWare
źródło
2
Ale jak uzyskać dostęp do powiadomienia w wywołanej czynności?
endowzoner
@FleshWound: cancel()pobiera identyfikator Notification, którego użyłeś, gdy dzwoniłeś notify(). Nie potrzebujesz Notificationobiektu.
CommonsWare
@CommonsWare cancel (id) przestał działać, jeśli setGroup jest ustawiony i istnieje powiadomienie zbiorcze grupy. W takim przypadku anulowanie z jakiegoś powodu nic nie da. Jednak bez podsumowania grupy, anuluj działa dobrze
Kushan
jeśli moja oczekująca intencja to, ACTION_VIEWa typ to image/jpeg(udostępnienie obrazu innej aplikacji), to w jaki sposób ma zostać wywołane anulowanie? IMO Android powinien automatycznie anulować, zastanawiam się, dlaczego Android nie tylko się tym zajmuje ?!
Someone Somewhere
@SomeoneSomewhere: "Więc jak to anulowanie powinno zostać uruchomione?" - nie może. Chociaż nic nie powstrzymuje Cię przed wskazaniem aplikacji innej firmy w Notificationpowiązanej z nią aplikacji PendingIntent, tak naprawdę nie została zaprojektowana do działania, więc napotkasz problemy takie jak ten. „IMO Android powinien automatycznie anulować” - widziałem oferowanie flagi oznaczającej to w akcji, ale nie powinno to być ciągłe. Gdyby tak było, pominięcie utworu w powiadomieniu odtwarzacza muzyki spowodowałoby zamknięcie powiadomienia.
CommonsWare
7

Moim zdaniem użycie BroadcastReceiverjest czystszym sposobem na anulowanie powiadomienia:

W AndroidManifest.xml:

<receiver 
    android:name=.NotificationCancelReceiver" >
    <intent-filter android:priority="999" >
         <action android:name="com.example.cancel" />
    </intent-filter>
</receiver>

W pliku java:

Intent cancel = new Intent("com.example.cancel");
PendingIntent cancelP = PendingIntent.getBroadcast(context, 0, cancel, PendingIntent.FLAG_CANCEL_CURRENT);

NotificationCompat.Action actions[] = new NotificationCompat.Action[1];

NotificationCancelReceiver

public class NotificationCancelReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //Cancel your ongoing Notification
    };
}
Himanshu Khandelwal
źródło
1
To właśnie robię, ale skąd masz identyfikator powiadomienia (w tym przykładzie 0) z metody onReceive? Nie ma tego w zamiarze, ponieważ nie został do niego dodany. Próbowałem dodać to jako dodatek, ale wygląda na to, że prawdziwy identyfikator powiadomienia nie jest tym, który dodawałem jako dodatek w działaniu tworzenia ...: - /
Marco Zanetti
Bardzo podoba mi się to podejście polegające na korzystaniu z usług transmisji zamiast działań, jest to znacznie lżejsze podejście imho.
Christophe Moine
Ale musiałem użyć <intent.setAction (Integer.toString (notificationId));> jako uzupełnienia, aby móc odrzucić którekolwiek z wyświetlonych powiadomień.
Christophe Moine
1
@MarcoZanetti musisz wygenerować identyfikator powiadomienia, który przekazujesz do oczekującego zamiaru, a także do metody powiadamiania podczas wysyłania powiadomienia. Jeśli to zrobisz, gdy użytkownik kliknie akcję, aby anulować, wywoła ona odbiornik transmisji, a następnie możesz uzyskać identyfikator powiadomienia z dodatków.
Ray Hunter
@ChristopheMoine, możesz umieścić identyfikator intent.putExtra()i dostać goBroadcastReceiver
Vadim Kotov
5

W nowych API nie zapomnij o TAG:

notify(String tag, int id, Notification notification)

i odpowiednio

cancel(String tag, int id) 

zamiast:

cancel(int id)

https://developer.android.com/reference/android/app/NotificationManager

Malachiasz
źródło
Miałeś rację! chociaż cancel()funkcja ma 2 implementacje; jeden z TAG i jeden bez. Ale musimy zapewnić plik TAG. Oto cancelfunkcja z docs public void cancel(@Nullable String tag, int id). Ostatnio sprawdzane na Androidzie Q
sud007
1

Po prostu umieść tę linię:

 builder.setAutoCancel(true);

A pełny kod to:

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    builder.setSmallIcon(android.R.drawable.ic_dialog_alert);
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.co.in/"));
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
    builder.setContentIntent(pendingIntent);
    builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.misti_ic));
    builder.setContentTitle("Notifications Title");
    builder.setContentText("Your notification content here.");
    builder.setSubText("Tap to view the website.");
    Toast.makeText(getApplicationContext(), "The notification has been created!!", Toast.LENGTH_LONG).show();

    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    builder.setAutoCancel(true);
    // Will display the notification in the notification bar
    notificationManager.notify(1, builder.build());
Hanisha
źródło
Wydaje się, że AutoCancel nie ma żadnego wpływu na system Android 9 (działał dobrze na Android 8.1)
Alix
rozróżnienie tutaj dotyczy akcji
Someone Somewhere
0

Będziesz musiał uruchomić następujący kod po uruchomieniu zamiaru, aby usunąć powiadomienie.

NotificationManagerCompat.from(this).cancel(null, notificationId);

NB: notificationId to ten sam identyfikator przekazany do uruchomienia powiadomienia

Houssin Boulla
źródło
-3

builder.setAutoCancel (true);

Testowany również na Androidzie 9.

Rudakowski
źródło