As part of my assignment I want to remove a notification that has been received but not been interacted with after a certain amount of time. This means if the notification is still in the notification tray after this amount of time, the app will delete it automatically.
For foreground notifications this wasn't the issue, as I applied the following code:
void SendNotification(RemoteMessage remotemessage)
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
long[] pattern = { 100, 100, 100, 100 };
var notificationBuilder = new Notification.Builder(this)
.SetVibrate(pattern)
.SetSmallIcon(Resource.Drawable.mhu2)
.SetContentTitle(remotemessage.GetNotification().Title)
.SetContentText(remotemessage.GetNotification().Body)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager)GetSystemService(Context.NotificationService);
int id = 0;
notificationManager.Notify(id, notificationBuilder.Build());
Handler handler = new Handler(Looper.MainLooper);
long delayInMilliseconds = 5000;
handler.PostDelayed(new Runnable(() => notificationManager.Cancel(id)), delayInMilliseconds);
}
When a notification is received, it will automatically be removed after 5 seconds (debugging purposes). However, as we all know, notifications are not handled the same depending on the state of the app.
This code works for foreground apps, but will never be run when the app is in the background or killed. So when the user receives a notification when the app was not opened or in the background, the notification will not be removed.
I've tried to look into this and saw partial solutions by executing code when overriding the OnDestroy/OnStop/OnPause state, but that still won't help to remove the notification when the app was never opened.
Any suggestions are greatly appreciated!
I just want to post a quick update as I've been able to solve this issue.
Android handles notification differently based on what is provided within the notification. I found out that if the notification is only build with datafields (so no notification body), the OnMessageReceived() will always be fired regardless of the state of the app. The timer that I realised will fire on foreground, background and app closed states. The only time this doesn't work is when the app is forced stopped, but in my context this won't cause issues.
Related
about event for click on notification:
I am searching since yesterday about that , what I found/understood: in FirebaseMessagingService will receive notification data , after will fire local notification, so need to add event in that local notification, I tried to add that many times with many ways but nothing worked …
after I tried to deleted notification files (firebase notification files, and local notification files) but still can receive notification. do you know how to know if the user clicked on the notification ?
To receive messages, use a service that extends FirebaseMessagingService. Your service should override the onMessageReceived and onDeletedMessages callbacks. It should handle any message within 20 seconds of receipt (10 seconds on Android Marshmallow). The time window may be shorter depending on OS delays incurred ahead of calling onMessageReceived. After that time, various OS behaviors such as Android O's background execution limits may interfere with your ability to complete your work.
For further info. you can visit the official website:
Link: https://firebase.google.com/docs/cloud-messaging/android/receive
Hope you'll get your answer here.
Step 1:
// Create an Intent for the activity you want to start
Intent intent = new Intent(this, MainActivity.class);
Step 2:
// Create the PendingIntent
PendingIntent pendingIntent = PendingIntent.getActivity(this, Calendar.getInstance().get(Calendar.MILLISECOND), intent, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
Step3:
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
builder.setContentIntent(pendingIntent);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(NOTIFICATION_ID, builder.build());
Whenever a user clicks on notification MainActivity will be opened.
Here is details implementation of Android Notification Sample https://github.com/android/user-interface-samples/tree/master/Notifications
Title is pretty self-explanatory. For those who don't know, there is an annoying warning that Android Oreo devices have, that if an app runs on background, there is a persistent notification that notifies you about the app running in "background".
On our application, we are using a long-living service with a notification, by calling Context.startForegroundService(intent) and this is how we create the notification inside the service:
// Check if the device has Oreo as its lowest OS. Same thing with Build.VERSION_CODES check.
if (Helper.ATLEAST_OREO)
{
// Get notification service.
NotificationManager notifManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Old notification channel handling.
NotificationChannel oldChannel = notifManager.getNotificationChannel("old_channel");
if (oldChannel != null)
notifManager.deleteNotificationChannel("old_channel");
// Get new channel.
NotificationChannel channel = notifManager.getNotificationChannel("new_channel");
if (channel == null)
{
// If null, create it without sound or vibration.
channel = new NotificationChannel("new_channel", "new_name",
NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("some_description");
channel.enableVibration(false);
channel.setSound(null, null);
notifManager.createNotificationChannel(channel);
}
// Create builder.
builder = new NotificationCompat.Builder(this, "new_channel");
// Create a pending intent to redirect to startup activity.
intent = new Intent(this, StartUpActivity.class);
intent.setFlags(
Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction("fromNotification"); // for logging purposes
pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
// Generate notification.
builder.setContentTitle(getString(R.string.app_name)) // required
.setSmallIcon(R.drawable.app_icon_white) // required
.setContentText(getString(R.string.notification_description)) // required
.setOngoing(true)
.setOnlyAlertOnce(true)
.setPriority(NotificationManager.IMPORTANCE_DEFAULT)
.setContentIntent(pendingIntent);
// Start foreground service.
startForeground(NOTIFY_ID, builder.build());
}
Everything is good. The service also have a thread that loops continuously in-between 100 milliseconds interval, while the screen is on. (We are detecting the change with a screen on-off broadcast receiver and start-finish the thread accordingly while the service continues to run.)
However, on Oreo devices user can go to notification settings manually and remove the ongoing notification as it takes space while the service is running (which is all the time in this case). After user removes notifications of the app, when some time passes a notification pops up that says "App is running on the background." with an IGNORE option, that the user will question "what is this app doing?" while it's doing what they asked for.
How can we prevent this from happening? Any help is appreciated, thank you so much.
I've been trying to remove a persistent Notification set by a Service using:
startForeground(1337, notification);
The code I'm using to cancel it:
NotificationManager nManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nManager.cancel(1337); // cancel existing service notification, doesn't take effect
nManager.cancelAll(); //surpluous, but also doesn't take effect
To clarify why I am doing this: the Service starts with a default persistent Notification. When my app runs, it needs to replace this Notification with another. Using notify() on the existing Notification works perfectly, however, I need it to show the ticker text for the new Notification as well. This is why I decided to remove the existing Notification (using the code above), create a new one, and then I call startForeground() again and pass the new Notification to it, so my Service persists.
The problem is that you're issuing the Notification in an indirect way by using startForeground(). You can't just cancel that Notification for the same reason the system insists on you providing a Notification when starting a foreground Service. As long as your foreground Service is running, that Notification will be there.
In most cases, Services really shouldn't be in the foreground. If you can use a normal priority for your Service, then you can start and stop your Notification normally.
If you're actually doing something that truly does require a foreground Service, and if you really want to show the user a ticker text, I believe your only option is to issue another Notification.
You can always remove notification from a foreground service by callng stopForeground(boolean removeNotification). Then a service exits his foregroundState and once again can be killed by the system when the memory is needed.
You could update the notification by passing in an empty Builder.
if(showNotification){
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setVisibility(Notification.VISIBILITY_SECRET)
.setSmallIcon(R.mipmap.ic_spotify_white_24dp)
.setTicker("Playing Now")
.setContentTitle("Spotify")
.setContentText("Preview");
return mBuilder;
}else{
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
return mBuilder;
}
In my app, I place my Service in the foreground to prevent it from being killed by using:
startForeground(NOTIFY_ID, notification);
This also displays the notification to the user (which is great). The problem is that later I need to update the notification. So I use the code:
notification.setLatestEventInfo(getApplicationContext(), someString, someOtherString, contentIntent);
mNotificationManager.notify(NOTIFY_ID, notification);
The question then is: will doing this knock the Service out of it's special foreground status?
In this answer, CommonsWare indicates that this behavior is possible, but he's not sure. So does anyone know the actual answer?
Note: I am aware that a simple way to get out of this question is to repeatedly call startForeground() every time I want to update the notification. I'm looking to know whether this alternative will also work.
To clarify what has been said here:
From what I understand, if you cancel the notification the service
will cease being a foreground service, so keep that in mind; if you
cancel the notification, you'll need to call startForeground() again
to restore the service's foreground status.
This part of the answer suggest it is possible to remove an ongoing Notification set by a Service by using NotificationManager.cancel() on the persistent Notification.
This is not true.
It's impossible to remove a ongoing notification set by startForeground() by using NotificationManager.cancel().
The only way to remove it, is to call stopForeground(true), so the ongoing Notification is removed, which ofcourse also makes the Service stop being in the foreground. So it's actually the other way around; the Service doesn't stop being in the foreground because the Notification is cancelled, the Notification can only be cancelled by stopping the Service being in the foreground.
Naturally one could call startForeground() after this right away, to restore the state with a new Notification. One reason you would want to do this if a ticker text has to be shown again, because it will only run the first time the Notification is displayed.
This behaviour is not documented, and I wasted 4 hours trying to figure out why I couldn't remove the Notification.
More on the issue here: NotificationManager.cancel() doesn't work for me
The RandomMusicPlayer (archived) app at the Android developer site uses NotificationManager to update the notification of a foreground service, so chances are pretty good that it retains the foreground status.
(See setUpAsForeground() and updateNotification() in the MusicService.java class.)
From what I understand, if you cancel the notification the service will cease being a foreground service, so keep that in mind; if you cancel the notification, you'll need to call startForeground() again to restore the service's foreground status.
When you want to update a Notification set by startForeground(), simply build a new notication and then use NotificationManager to notify it.
The key point is to use the same notification id.
Updating the Notification will NOT remove the Service from the foreground status (this can be done only by calling stopForground );
Example:
private static final int notif_id=1;
#Override
public void onCreate (){
this.startForeground();
}
private void startForeground() {
startForeground(notif_id, getMyActivityNotification(""));
}
private Notification getMyActivityNotification(String text){
// The PendingIntent to launch our activity if the user selects
// this notification
CharSequence title = getText(R.string.title_activity);
PendingIntent contentIntent = PendingIntent.getActivity(this,
0, new Intent(this, MyActivity.class), 0);
return new Notification.Builder(this)
.setContentTitle(title)
.setContentText(text)
.setSmallIcon(R.drawable.ic_launcher_b3)
.setContentIntent(contentIntent).getNotification();
}
/**
this is the method that can be called to update the Notification
*/
private void updateNotification() {
String text = "Some text that will update the notification";
Notification notification = getMyActivityNotification(text);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(notif_id, notification);
}
I need to write some application which will do some work in background. This application will be run from autostart and there wont be any start gui. Gui can be call from click on notification which will be showing with autostart. I worried that, when user clear notifications he lost opportunity to call this gui. My question is that is there any way to block clearing my notification by user?
Here's a notification that won't allow the user to clear it.
Notification notification = new NotificationCompat.Builder(this)
.setTicker(r.getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(r.getString(R.string.app_name))
.setAutoCancel(false)
.setOngoing(true)
.build();
The setOngoing(true) call achieves this, and setAutoCancel(false) stops the notification from going away when the user taps the notification.
The notification will be cleared if the application is uninstalled or by calling Cancel or CancelAll: http://developer.android.com/reference/android/app/NotificationManager.html#cancel(int)
You might want to look into making a notification in the "running" section of the notifications. These notifications aren't cleared when the user clears them.
Use the Notification.FLAG_NO_CLEAR AND Notification.FLAG_ONGOING_EVENT. This should give you the effect you want
Though #cja answer might be correct though he is missing some few lines of codes(Notification wont show or wont display on your notification tray).
This is the complete working function :
public void createNotification() {
NotificationCompat.Builder notification = new NotificationCompat.Builder(this);
notification.setTicker( "Ticker Text" );
notification.setSmallIcon(R.drawable.ic_launcher);
notification.setContentTitle( "Content Title" );
notification.setContentText( "Content Text" );
notification.setAutoCancel( false );
notification.setOngoing( true );
notification.setNumber( ++NotificationCount );
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);
notification.setContentIntent(pIntent);
notification.build();
NotificationManager nManger = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nManger.notify(NotificationID, notification.build());
}
NotificationID is int serve as your notification's ID.
you can clear it by using this :
public void clear() {
NotificationManager oldNoti = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
oldNoti.cancel( NotificationID );
}
make sure that notification.setAutoCancel( false ); is set to false so it wont be cleared when clear button is pressed or when swipe gesture is present.
few lines of codes are originally from #cja post.
cheers / happy codings...
Just add these two flags to the notification, FLAG_AUTO_CANCEL prevent notification automatically dismissed when the user touches it and FLAG_ONGOING_EVENT make it an Ongoing Notification.
notification.flags=Notification.FLAG_AUTO_CANCEL|Notification.FLAG_ONGOING_EVENT;
using this alone works perfectly for me Notification.FLAG_NO_CLEAR
along with
notification.SetOngoing(true);
notification.SetAutoCancel(false);
You want to implement a Foreground Service.