160

Since API level 16 (Jelly Bean), there is the possibility to add actions to a notification with

builder.addAction(iconId, title, intent);

But when I add an action to a notification and the action is pressed, the notification is not going to be dismissed. When the notification itself is being clicked, it can be dismissed with

notification.flags = Notification.FLAG_AUTO_CANCEL;

or

builder.setAutoCancel(true);

But obviously, this has nothing to with the actions associated to the notification.

Any hints? Or is this not part of the API yet? I did not find anything.

Sufian
  • 5,997
  • 14
  • 60
  • 111
endowzoner
  • 2,058
  • 3
  • 16
  • 20

9 Answers9

169

When you called notify on the notification manager you gave it an id - that is the unique id you can use to access it later (this is from the notification manager:

notify(int id, Notification notification)

To cancel, you would call:

cancel(int id)

with the same id. So, basically, you need to keep track of the id or possibly put the id into a Bundle you add to the Intent inside the PendingIntent?

Kaediil
  • 5,255
  • 2
  • 19
  • 20
  • 29
    Thanks, that solved my issue. However, I still think it is a little bit over-complicated. Instead of just providing an API to auto-dismiss the notification when an action is pressed, you have to work with intent and notification id passing to achieve the same. – endowzoner Aug 09 '12 at 14:44
  • 2
    If you think this is complicated, don't look into updating a notification (don't lose track of that id), or checking whether it's displayed or not (the api doesn't track it, you have to)... :P – Travis Sep 20 '12 at 14:26
  • How did you do that @FleshWound ? Can you please supply me with your code? thanks! – Daksh Dec 16 '12 at 10:24
  • 2
    @Daksh: Basically, you add the notification tag and id to your intent which gets started when your action is pressed. With that extra information, you can check in the starting activity whether it was started via a notifcation action. – endowzoner Dec 19 '12 at 08:43
  • 5
    Code sample from onCreate(): Bundle extras = 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)) { // activity has been started via notification action, dismiss // notification NotificationManager manager = (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE); manager.cancel(tag, id); } } – endowzoner Dec 19 '12 at 08:44
  • my action is to Call a specific number. thus it opens up the Phone app and dials the number. so then how do i do it? is there a way to set up a listener for the call intent Intent.ACTION_DIAL and then check the data in the intent extras? if i'm on some sort of right path, then i would really appreciate code for that too! – Daksh Dec 19 '12 at 20:23
  • @Daksh I would open up a new question for what you want to do. Tell people what you have tried and add other info like sample code they can correct. – Kaediil Dec 20 '12 at 03:26
  • 1
    This no longer works if setGroup is set to true and there is a group summary. In this case, the cancel somehow has no effect despite using correct id – Kushan Aug 31 '17 at 19:33
  • 2
    In new API you have notify(String tag, int id, Notification notification) and correspondingly cancel(String tag, int id) – Malachiasz Nov 14 '18 at 11:00
66

Found this to be an issue when using Lollipop's Heads Up Display notification. See design guidelines. Here's the complete(ish) code to implement.

Until now, having a 'Dismiss' button was less important, but now it's more in your face.

heads up notification

Building the Notification

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 (attributes required to prevent SystemUI from focusing to a back stack)

<activity
    android:name=".NotificationActivity"
    android:taskAffinity=""
    android:excludeFromRecents="true">
</activity>
Sufian
  • 5,997
  • 14
  • 60
  • 111
aaronvargas
  • 9,289
  • 2
  • 42
  • 45
  • i have multiple notifications from one application and the notifaaction are set to ongoing notification. I want to clear the notification when addaction performs on perticular notification – Prasad May 09 '15 at 03:44
  • 3
    Would it not be more effective to use a BroadcastReceiver here to dismiss the notification instead? Here is a good example that shows the implementation, but it can be even further reduced: http://stackoverflow.com/a/19745745/793150 – alice.harrison May 18 '15 at 16:12
  • 1
    Solutions works, except the extras set are forwarded with the intent. They are not forwarded to onCreate. One way is to use static variables. Does anybody know, why the intent extras are not forwarded? – Baschi Feb 11 '16 at 16:56
  • Why does getDismissIntent only work when placed in the NotificationActivity? The dismiss Intent does not work if the PendingIntent creation code is placed in the notification builder class. I just spent 2 hours on this issue and I cannot figure out why the Pending Intent MUST be created in the Activity. Can anyone explain why this is the case? – Ray Li Apr 21 '17 at 15:31
  • getDismissIntent() is a static "helper" function that builds the correct Intent to use to communicate with the NotificationActivity. As such, these are usually bundled with the Activity. But I don't see why this static function could not be placed in the notification builder class, as long as you are careful to set the NOTIFICATION_ID and context correctly. – Mike Apr 14 '18 at 20:52
18

I found that when you use the action buttons in expanded notifications, you have to write extra code and you are more constrained.

You have to manually cancel your notification when the user clicks an action button. The notification is only cancelled automatically for the default action.

Also if you start a broadcast receiver from the button, the notification drawer doesn't close.

I ended up creating a new NotificationActivity to address these issues. This intermediary activity without any UI cancels the notification and then starts the activity I really wanted to start from the notification.

I've posted sample code in a related post Clicking Android Notification Actions does not close Notification drawer.

Community
  • 1
  • 1
Vicki
  • 429
  • 5
  • 8
  • 2
    Too bad they still haven't baked this into the API... It's pretty hackish to do it like this. But still the only way, especially if you don't have any control over the destination intent, like viewing a URL. – Bogdan Zurac May 09 '15 at 20:02
  • Thanks for the info, you're correct. However I would use an intentservice rather than an intermediary activity – Tim Mar 09 '16 at 07:52
7

You can always cancel() the Notification from whatever is being invoked by the action (e.g., in onCreate() of the activity tied to the PendingIntent you supply to addAction()).

CommonsWare
  • 910,778
  • 176
  • 2,215
  • 2,253
  • 2
    But how do I get access to the notification in the activity which has been called? – endowzoner Aug 09 '12 at 12:51
  • @FleshWound: `cancel()` takes the ID of the `Notification`, which you used when you called `notify()`. You do not need the `Notification` object. – CommonsWare Aug 09 '12 at 15:49
  • @CommonsWare cancel(id) has stopped working if the setGroup is set and there is a Group summary notification. In this case, the cancel does not do anything for some reason. Without the group summary, cancel works fine though – Kushan Aug 31 '17 at 19:36
  • if my pending intent is `ACTION_VIEW` and the type is `image/jpeg` (to share an image with another app) then how is that cancel supposed to be triggered ? IMO Android should auto-cancel, I'm puzzled as to why Android doesn't just take care of it ?! – Someone Somewhere Jul 15 '20 at 18:30
  • @SomeoneSomewhere: "then how is that cancel supposed to be triggered ?" -- it can't. While there is nothing stopping you from pointing to a third-party app in a `Notification`-related `PendingIntent`, that's not really how it was designed to work, and so you will run into issues like this one. "IMO Android should auto-cancel" -- I could see offering a flag for that on the action, but it should not be an all-the-time thing. If it were, skipping a track in a music player notification would close the notification. – CommonsWare Jul 15 '20 at 18:36
  • What I've ended up implementing is: notification Action triggers IntentService, that IntentService cancels the notification and either 1) creates a new intent and triggers the intent chooser or 2) performs a specific action within the app – Someone Somewhere Jul 15 '20 at 21:36
7

In my opinion using a BroadcastReceiver is a cleaner way to cancel a Notification:

In AndroidManifest.xml:

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

In java File:

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
    };
}
Vadim Kotov
  • 7,103
  • 8
  • 44
  • 57
Himanshu Khandelwal
  • 4,251
  • 1
  • 15
  • 7
  • 1
    That's what I do but how do you get the notification ID (in this example, 0) from the onReceive method? It's not in the Intent, since it's not been added to it. I tried adding it as an extra but it seems like the real notification ID is not the one I was adding as an extra in the creating activity... :-/ – Marco Zanetti Apr 20 '16 at 14:50
  • I really like this approach of using Broadcast services instead of Activities, it is much lighter approach imho. – Christophe Moine Jan 24 '17 at 09:13
  • But I had to use in adidition to be able to dismiss any of the notification shown. – Christophe Moine Jan 24 '17 at 09:20
  • 1
    @MarcoZanetti you need to generate a notification id that you pass to the pending intent and also to the notify method when sending the notification. If you do that, then when user clicks the action to cancel it will call the broadcast receiver and then you can get the notification id from the extras. – Ray Hunter Jun 21 '17 at 20:58
  • @ChristopheMoine you can put id via `intent.putExtra()` and get it in `BroadcastReceiver` – Vadim Kotov Jul 15 '19 at 14:15
  • This sucks - it means every action in a notification needs to be captured by my app when the user wants to do something, i.e. [SHARE] – Someone Somewhere Jul 15 '20 at 18:28
6

In new APIs don't forget about TAG:

notify(String tag, int id, Notification notification)

and correspondingly

cancel(String tag, int id) 

instead of:

cancel(int id)

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

Malachiasz
  • 6,621
  • 2
  • 31
  • 46
  • You were right! although `cancel()` function has 2 implementations; one with TAG and one without. But we have to provide a `TAG`. Here is `cancel` function from docs `public void cancel(@Nullable String tag, int id)`. Last checked on Android Q – sud007 Apr 14 '20 at 10:22
5

You will need to run the following code after your intent is fired to remove the notification.

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

NB: notificationId is the same id passed to run your notification

Houssin Boulla
  • 1,764
  • 1
  • 13
  • 16
2

Just put this line :

 builder.setAutoCancel(true);

And the full code is :

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
  • 623
  • 6
  • 7
-5

builder.setAutoCancel(true);

Tested on Android 9 also.

rudakovsky
  • 122
  • 1
  • 5