In our app OneBusAway Android (open-source on Github), we need to be notified when the user dismisses a particular reminder notification, so we don't post another reminder notification for the same event (how long until their bus arrives).
We're doing this by listening for an Intent
in our app, registered as the DeleteIntent
with the Notification
. When the user dismisses the notification (either by swiping it away, or tapping the clear button in the notification window), our app should receive that Intent
.
From testing, it seems that with the current version on Google Play (and the current master branch on Github), the DeleteIntent is never received in our application in the following versions of Android:
However, the exact same code DOES work (i.e., the Intent registered as the DeleteIntent is received by the app) on:
I've looked at the following SO posts that deal with DeleteIntent, and none of the solutions listed work on Android 4.4.3 and 4.4.4:
The current working master branch uses a Service to listen for the Intent. However, based on some of the above posts, I did tweak some of the code to be more in line with working examples that use a BroadcastReceiver to listen for the Intent.
The code using the BroadcastReceiver is in the following Github branch:
https://github.com/CUTR-at-USF/onebusaway-android/tree/issue104-RepeatingReminders
Below are excerpts for what my current version looks like (that still works on Android 4.1.2 and lower, but not 4.4.3 or 4.4.4), along with links to Github source:
Creating the notification
https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/java/com/joulespersecond/seattlebusbot/tripservice/NotifierTask.java#L131
private Notification createNotification(Uri alertUri) {
//Log.d(TAG, "Creating notification for alert: " + alertUri);
Intent deleteIntent = new Intent(mContext, AlarmReceiver.class);
deleteIntent.setAction(TripService.ACTION_CANCEL);
deleteIntent.setData(alertUri);
return new NotificationCompat.Builder(mContext)
.setSmallIcon(R.drawable.ic_stat_notification)
.setDefaults(Notification.DEFAULT_ALL)
.setOnlyAlertOnce(true)
.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build();
}
Title and other dynamic notification info are set a few lines later (and reset later, if the notification remains undismissed):
@SuppressWarnings("deprecation")
private void setLatestInfo(Notification notification,
String stopId,
String routeId,
long timeDiff) {
final String title = mContext.getString(R.string.app_name);
final PendingIntent intent = PendingIntent.getActivity(mContext, 0,
new ArrivalsListActivity.Builder(mContext, stopId).getIntent(),
PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(mContext,
title,
getNotifyText(routeId, timeDiff),
intent);
}
TripService
contains the constants for the action:
public static final String ACTION_CANCEL =
"com.joulespersecond.seattlebusbot.action.CANCEL";
AlarmReceiver
https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/java/com/joulespersecond/seattlebusbot/AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
private static final String TAG = "AlarmReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "In onReceive with intent action " + intent.getAction());
...
}
}
AndroidManifest
https://github.com/CUTR-at-USF/onebusaway-android/blob/issue104-RepeatingReminders/onebusaway-android/src/main/AndroidManifest.xml
<receiver android:name=".AlarmReceiver">
<!-- These action names must match the constants in TripService -->
<intent-filter>
<action android:name="com.joulespersecond.seattlebusbot.action.SCHEDULE" />
<action android:name="com.joulespersecond.seattlebusbot.action.POLL" />
<action android:name="com.joulespersecond.seattlebusbot.action.CANCEL" />
</intent-filter>
</receiver>
With the above, on Android 4.4.3/4.4.4, the AlarmReceiver never sees the Intent when the user dismisses the notification.
I also tried adding a MIME type, as specified in Custom actions using implicit intents between applications, but that didn't work on Android 4.4.3/4.4.4 either:
Intent deleteIntent = new Intent(mContext, AlarmReceiver.class);
deleteIntent.setAction(TripService.ACTION_CANCEL);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
deleteIntent.setDataAndTypeAndNormalize(alertUri, TripService.REMINDER_MIME_TYPE);
} else {
deleteIntent.setDataAndType(alertUri, TripService.REMINDER_MIME_TYPE);
}
return new NotificationCompat.Builder(mContext)
.setSmallIcon(R.drawable.ic_stat_notification)
.setDefaults(Notification.DEFAULT_ALL)
.setOnlyAlertOnce(true)
.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
deleteIntent, 0))
//.setLights(0xFF00FF00, 1000, 1000)
//.setVibrate(VIBRATE_PATTERN)
.build();
REMINDER_MIME_TYPE
is application/vnd.com.joulespersecond.seattlebusbot.reminder
Manifest for using the MIME type:
<receiver android:name=".AlarmReceiver">
<!-- These action names must match the constants in TripService -->
<intent-filter>
<action android:name="com.joulespersecond.seattlebusbot.action.SCHEDULE" />
<action android:name="com.joulespersecond.seattlebusbot.action.POLL" />
<action android:name="com.joulespersecond.seattlebusbot.action.CANCEL" />
<data android:mimeType="application/vnd.com.joulespersecond.seattlebusbot.reminder" />
</intent-filter>
</receiver>
I also tried not using the support library (i.e., using Notification.Builder
instead of NotificationCompat.Builder
), but that didn't change anything either.
Any ideas why this isn't working on Android 4.4.3/4.4.4?
More info is shown in the Github issue for this problem.
EDIT
I've also replicated this issue in a small Github project "DeleteIntentDemo":
https://github.com/barbeau/DeleteIntentDemo
Instructions to reproduce are in the README for this project.
EDIT 2
This appears to be due to a bug in Android in Notification.setLatestEventInfo()
- I've reported it here:
https://code.google.com/p/android/issues/detail?id=73720
Please see @CommonsWare's answer for the workaround.
EDIT 3
My AOSP patch to fix this issue has now been merged so this problem won't appear for legacy apps in future releases of Android: https://code.google.com/p/android/issues/detail?id=73720#c4
However, in the above AOSP thread is it emphasized that one should no longer be using Notification.setLatestEventInfo()
- instead, use Notification.Builder
to create a new Notification.
In your sample project, if you remove the following line, the deleteIntent
works on a Nexus 4 running 4.4.4:
setLatestInfo(getActivity(), notification, routeId);
I suspect that this call is wiping out your deleteIntent
. It may work to re-apply your deleteIntent
to the Notification
as part of your setLatestInfo()
processing.
You must have a different problem because I'm able to receive the deleteintent in several 4.3 and 4.4 emulators.
I wanted to test your "simple" project but it uses Android Studio, so I made my own simpler test.
Steps to reproduce:
-Create an Activity and set the launch mode to singleInstance in the manifest.
-In the handler of a button or menu item, launch a notification:
Intent deleteIntent = new Intent(this, MainActivity.class);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setOnlyAlertOnce(true)
.setContentTitle("Notification delete intent test")
.setContentText("Please dismiss this notification by swipping or deleting it. A Toast will be shown if the deletion intent works.")
.setDeleteIntent(PendingIntent.getActivity(this, 0, deleteIntent, 0))
.setAutoCancel(true)
.build();
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.notify((int)System.currentTimeMillis(), notification);
-Override onNewIntent
to show a toast or log a message when the notification is cancelled:
@Override
public void onNewIntent(Intent intent){
Toast.makeText(this, "Notification deleted!", Toast.LENGTH_LONG).show();
}
To dismiss the notification either swipe or press the clear button. It wont work pressing over it because autocancel is not considered an explicit user action and hence the delete intent wont be delivered.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With