I've been fighting this for over a week now, so any help would be appreciated.
I have an Activity starting a Service for media playback. Once playback has begun, the Service starts an Ongoing, Non-Cancellable Notification as such:
realIntent = new Intent(this, EpisodeViewer.class);
realIntent.putExtra("show_name", showName);
realIntent.putExtra("episode_name", episodeName);
pendingIntent = PendingIntent.getActivity(this, 0, realIntent, 0);
note =
new Notification(
R.drawable.ic_notification_bcn,
episodeName,
System.currentTimeMillis());
note.flags =
note.flags |
Notification.FLAG_FOREGROUND_SERVICE |
Notification.FLAG_NO_CLEAR |
Notification.FLAG_ONGOING_EVENT;
note.setLatestEventInfo(this, "", episodeName, pendingIntent);
note.contentView =
new RemoteViews(
getApplicationContext().getPackageName(),
R.layout.episode_player_note);
note.contentView.setImageViewResource(
R.id.player_note_icon,
R.drawable.ic_notification_bcn);
note.contentView.setTextViewText(
R.id.player_note_text,
episodeName);
note.contentView.setProgressBar(
R.id.player_note_progress, 100, 0, false);
noteManager.notify(MEDIA_PLAYER_NOTIFY_ID, note);
And this works just fine. When the user switches to play something else (through the Activity's UI) the Service updates the Notification (using the same as above) to change the name and re-set the progress bar. And this works just fine. And as the media progresses, the progress bar in the Notification updates, and this works as well.
But when the media ends or the User wants to stop, the Service tries to cancel the Notification with
noteManager.cancel(MEDIA_PLAYER_NOTIFY_ID);
But this is ignored. There are no errors in the DDMS log, but from my trace I know for sure the cancel is being called. I've tried cancelling the PendingIntent before cancelling the Notification, but this makes no difference. I've also tried replacing the Notification with an 'Empty' one - clearing the progress and the name - and then cancelling. The new 'Cleared' Notification shows, but then still does not cancel.
So what am I missing here? Is there something else that needs to be done to cancel a FOREGROUND or NO_CLEAR or ONGOING Notification that I'm missing? I've tried this with the Emulator under 2.1 and 2.2, and on my hardware running 2.3, all of which exhibit the exact same behaviour.
I noticed the same problem.
If the notification flag has "Notification.FLAG_FOREGROUND_SERVICE" in it, the only way to get rid of it is to call "stopForeground()", but there is a catch. The "stopForeground()" won't remove the notification (with FLAG_FOREGROUND_SERVICE) if "startForeground()" has not been called!
In simple words, if you use FLAG_FOREGROUND_SERVICE, you have to call "startForeground()" and "stopForeground()"!
If "startForeground()" is used, I don't see a reason for putting FLAG_FOREGROUND_SERVICE in the other notifications. But if it is needed, here is a sample (used in public class CLASS extends Service) :
public int onStartCommand(Intent intent, int flags, int startId) {
PendingIntent notifIntent;
Intent notificationIntent = new Intent(); // fill up yourself
notifIntent = PendingIntent.getActivity(); // fill up yourself
Notification note = new Notification(); // fill up yourself
note.setLatestEventInfo(context, getString(R.string.app_name), Message, notifIntent);
startForeground(yourOwnNumber, note); // fill up yourself
displayYourNotification();
// Fill up the rest yourself.
}
public void onDestroy() {
stopForeground(); // fill up yourself
}
public void displayYourNotification() {
Intent notificationIntent = new Intent(); // fill up yourself
notifIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
Notification note = new Notification(); // fill up yourself
note.flags = Notification.FLAG_FOREGROUND_SERVICE;
note.setLatestEventInfo(context, TITLE, Message, notifIntent);
notifManager.notify(ID, note);
}
Related
I have an app where i'm having some issues with notifications not being consistent on different devices. The notification is used by a foreground service
This is what my notification looks like in my pixel3 api29 emulator
Because of the issue i'm troubleshooting, I decided to extract the code and put it in a dummy app that would let me simulate the triggering event more easily. However, after doing that and running on the exact same pixel3 api29 emulator, i realized the notification is not even consistent on the same device. In the dummy app for a dummy foreground service the notification looks like this
I can't find what's driving the different look of the action buttons in the two apps. Even the behaviour is different. In the first version, the floating headsup notification stays there forever until i clear it programatically but in the second dummy app the notification clears itself after 6 seconds. I tried using the same theme in both, added the exact same dependency versions thinking that i was pulling different versions of the notification library but nothing, the code to create the notifications is the same in both:
private Notification createIncomingCallNotification(Intent intent) {
Intent hangupIntent = new Intent(this, getClass());
hangupIntent.setAction("hangup");
PendingIntent hangupPendingIntent = PendingIntent.getService(this, 1244, hangupIntent, PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Action hangupAction = new NotificationCompat.Action.Builder(0, getActionText(R.string.hangup_button_label, R.color.test1), hangupPendingIntent).build();
Intent uiIntent = new Intent(this, VideoCallActivity.class);
uiIntent.putExtras(intent);
uiIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0, uiIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action previewAction = new NotificationCompat.Action.Builder(0, getActionText(R.string.preview, R.color.test2), fullScreenPendingIntent).build();
String caller = "Caller guy";
Log.v(TAG, "Creating incoming call notification from " + caller);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, ChannelIds.INCOMING_CALL_CHANNEL_ID)
.setContentTitle(getString(R.string.incoming_call_notification_title, caller))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setSmallIcon(R.drawable.ic_white_100dp)
.addAction(hangupAction)
.addAction(previewAction)
.setOnlyAlertOnce(true)
.setFullScreenIntent(fullScreenPendingIntent, true);
return notificationBuilder.build();
}
private Spanned getActionText(#StringRes int stringRes, #ColorRes int colourRes) {
Spannable spannable = new SpannableString(getText(stringRes));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
// This will only work for cases where the Notification.Builder has a fullscreen intent set
// Notification.Builder that does not have a full screen intent will take the color of the
// app and the following leads to a no-op.
spannable.setSpan(new ForegroundColorSpan(getColor(colourRes)), 0, spannable.length(), 0);
}
return spannable;
}
Does anyone know what can cause this inconsistency even on the same device?
I finally figured it out. What changes the headsup notification is this permission in the manifest
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
I'm trying to get my Notification to not cancel when the user presses "Clear All" So far I have the intent working properly on everything except this:
Intent intent = new Intent(getBaseContext(), MainActivity.class);
intent.addFlags(Notification.FLAG_ONGOING_EVENT);
intent.addFlags(Notification.FLAG_NO_CLEAR);
PendingIntent contentIntent = PendingIntent.getActivity(
getBaseContext(), 0, intent, 0);
The question I have at this point is: Are my flags correct?
Yes, your flags look pretty much correct, although I don't know if you even need the FLAG_NO_CLEAR. I currently have an app which creates an ongoing (non-cancellable) notification - I only use the FLAG_ONGOING_EVENT and it works fine for me. I pretty much just copied it from a tutorial and then added the ongoing event flag.
Here's some sample code:
String text = "notification";
Notification notification = new Notification(R.drawable.icon, text,
System.currentTimeMillis());
//launch the activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent().setComponent(ComponentName.unflattenFromString("com.example/com.example.MyActivity")), 0);
// set the label and text...
notification.setLatestEventInfo(this, getText(R.string.notification_label),
text, contentIntent);
notification.flags = Notification.FLAG_ONGOING_EVENT;
// Send the notification.
// We use a string id because it is a unique number. We use it later to cancel.
NotificationManager mNM;
mNM.notify(R.string.notification_label, notification);
I'm trying to create a notification when an alarm fires. No problem so far.
A notification is shown. The problem is on my device (2.1 Update 1) it is displayed as an ongoing notification. While on another device (2.2) it is working fine and is actually shown as a one time only notification. Has anyone experienced this behaviour, and more important has anyone been able to fix it. It seems like 2.1 is not respecting the FLAG_ONLY_ALERT_ONCE or any flags for that mather. See my code below. Any help would be appreciated.
public class AlarmHandler extends BroadcastReceiver {
NotificationManager nm;
#Override
public void onReceive(Context context, Intent intent) {
Debug.log("Alarm notification received");
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
CharSequence from = intent.getStringExtra("AlarmType");
CharSequence message = intent.getStringExtra("AlarmType");
Intent newIntent = new Intent(context, Splashscreen.class);
PendingIntent contentIntent = PendingIntent.getActivity(context, intent.getIntExtra("RequestCode", 0),
newIntent, Intent.FLAG_ACTIVITY_NEW_TASK);
Notification notif = new Notification(R.drawable.icon,
null, System.currentTimeMillis());
notif.setLatestEventInfo(context, from, message, contentIntent);
notif.flags |= Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL;
Debug.log("flags: "+notif.flags);
Debug.log("Defaults: "+notif.flags);
nm.notify(intent.getIntExtra("RequestCode", 0), notif);
}
}
Please find below values and definition for each one. Hope you can get idea from this.
FLAG_ONLY_ALERT_ONCE = 8
Definition : It should be set if you want the sound and/or vibration play each time the notification is sent, even if it has not been canceled before that.
FLAG_ONGOING_EVENT = 2
Definition: It should be set if this notification is in reference to something that is ongoing, like a phone call. It should not be set if this notification is in reference to something that happened at a particular point in time, like a missed phone call.
FLAG_AUTO_CANCEL = 16
Definition: The notification should be canceled when it is clicked by the user
Also you if you want to cancel the notification manually then use below code
nm.cancel(id);
I am new to android development and I try to create a background download feature for my app. I followed this http://developer.android.com/guide/topics/ui/notifiers/notifications.html#CustomExpandedView to create my custom notification.
The downloading is performed, I checked the downloaded file in the sdcard. Also,the status bar icon and title are changed properly.
The problem is that the custom layout I provide for the notification does not appear (expand under the bar). Here is the related code parts inside private AsyncTask class:
#Override
protected void onPreExecute() {
// create and configure the notification
notification = new Notification(R.drawable.download, "Downloading map..", System.currentTimeMillis());
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
//create a custom layout for the notification
myContentView = new RemoteViews(appContext.getPackageName(), R.layout.download_progress);
myContentView.setImageViewResource(R.id.status_icon, R.drawable.ic_menu_save);
myContentView.setTextViewText(R.id.status_text, "download in progress");
myContentView.setProgressBar(R.id.status_progress, 100, 0, false);
notification.contentView = myContentView;
notification.contentView.apply(appContext, dl.getListView());
//instantiate the pending intent
Intent myIntent = new Intent(appContext, DownloadList.class);
myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
int requestID = (int) System.currentTimeMillis();
PendingIntent myPendingIntent = PendingIntent.getActivity(appContext, requestID, myIntent, 0);
notification.contentIntent = myPendingIntent;
//add the Notification object to the notification manager
notificationManager = (NotificationManager) appContext.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIF_ID, notification);
}
#Override
protected void onProgressUpdate(Integer... progress) {
//update progress bar
notification.contentView.setProgressBar(R.id.status_progress, 100, progress[0], false);
notificationManager.notify(NOTIF_ID, notification);
}
}
Note that my DownloadList class extends ListActivity.
Do I need to do something more that just "notification.contentView = myContentView;" in order to inflate the layout?
Hmm... Well I compared your code to my code that already works... and I don't see many differences... But, it is possible that one of these minor differences is important.
final Notification notification = new Notification(R.drawable.icon, "Downloading", System.currentTimeMillis());
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
notification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.download_progress);
notification.contentView.setImageViewResource(R.id.status_icon, R.drawable.ic_status);
notification.contentView.setTextViewText(R.id.status_text, "Downloading in progress");
notification.contentView.setProgressBar(R.id.status_progress, 100, progress, false);
Intent notificationIntent = new Intent(MainPage.mainActivity, MainPage.class);
PendingIntent contentIntent = PendingIntent.getActivity(MainPage.mainActivity, 0, notificationIntent, 0);
notification.contentIntent = contentIntent;
//getApplicationContext();
final NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(
Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_MESSAGE, notification);
First, I looked at your old code and noticed that the NOTIF_ID = 1 I'm not so sure that is a good idea because what if someone else has an ID of one. Of course I could be mistaken about that, but I just pounded in a number like 792489743 and I expect no one else would have the same number. Just a precaution I suppose.
Second, I didn't get to see if the resources were correct? What does the stack trace say? I suppose that it would've just quit out on it if there was a problem there though.
Third, I put my in its own task as Service kinda as follows
public class DownloadService extends IntentService {
//initializing code and stuff
private class DownloadTask extends AsyncTask<String, Void, Boolean> {
and I did it in the doInBackground This way if the user kills the app or what not it wouldn't kill the download.
Lastly, I've never used apply I don't personally see how it would hurt, but I haven't seen an example that uses it either.
Hope this helps some!
It was an emulator problem after all.....
It lagged when I "dragged down" the notification! I killed some CPU extensive processes on my PC resulting to a faster emulator.
Lesson learned. Leave the heavy multitasking to pros or to another PC.
I've hit a strange behaviour in my app: when I try to set a service foreground, the handset (HTC G1 + Cyanogen Mod) reboots.
It works when I don't try to foreground this service.
Here is the incriminated code:
Notification notification = new Notification(R.drawable.icon,
getText(R.string.ticker_text), System.currentTimeMillis());
startForeground(SERVICE_NOTIFICATION_ID, notification);
Log.v(TAG, "Control service foregrounded.");
Can you see where is the problem?
If you need more data, the whole project can be found on GitHub: https://github.com/rbochet/Serval-Video-Discovery/tree/network-remote
Thanks.
Look at this Android API Demo here. Notice that rather than calling startForeground(), it calls startForegroundCompat(), a wrapper that handles your request depending on the new/old startForeground API.
void handleCommand(Intent intent) {
if (ACTION_FOREGROUND.equals(intent.getAction())) {
...
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample, text,
System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, Controller.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.local_service_label),
text, contentIntent);
startForegroundCompat(R.string.foreground_service_started, notification);
...
}
Here is startForegroundCompat():
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat(int id, Notification notification) {
// If we have the new startForeground API, then use it.
if (mStartForeground != null) {
mStartForegroundArgs[0] = Integer.valueOf(id);
mStartForegroundArgs[1] = notification;
invokeMethod(mStartForeground, mStartForegroundArgs);
return;
}
// Fall back on the old API.
mSetForegroundArgs[0] = Boolean.TRUE;
invokeMethod(mSetForeground, mSetForegroundArgs);
mNM.notify(id, notification);
}