I have created a class which extends Service and runs as a foreground service. I would like my service notification to be persistent (i.e. not removed by swiping). However, my notification can be dismissed by swiping.
The Service documentation states: ...A foreground service must provide a notification for the status bar, which is placed under the Ongoing heading. This means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground...
I did put break points to check whether onDestroy() or stopSelf() is hit, but this is not the case. The service is running in foreground mode, but I can dismiss the notification by swiping.
I have found quite a few questions regarding the opposite case, where it was not possible to dismiss the notification after the service was stopped, but did not find any question similar to my problem.
The service is started via an Intent and initialized as follows:
#Override
public void onCreate()
{
super.onCreate();
initialize();
}
private void initialize()
{
Notification n = get_service_notification();
startForeground(10, n);
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter(BroadcastCodes.service_broadcast_intent_name));
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
startServing();
return START_STICKY;
}
where startServing() subscribes to location updates
The notification is built like this
private Notification get_service_notification()
{
Intent intent = new Intent(this, LoginActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
b.setAutoCancel(false)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.app_icon)
.setTicker("some text")
.setContentTitle("some text")
.setContentText("some text")
.setContentIntent(contentIntent)
.setContentInfo("")
.setOngoing(true);
Notification res = b.build();
res.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
return res;
}
I have tried playing around with the flags and setOngoing/setAutoCancel, but the result stayed the same.
SDK versions are configured as minSdkVersion 18, targetSdkVersion 25
I would really appreciate any input regarding this issue - it might be just some stupid mistake, but I already did spend more than a few hours trying to research on my own...unfortunately to no avail.
EDIT 1
I still have not figured out the problem. I decided to try a dirty hack, but still no luck.
I made a PendingIntent and registered it via setDeleteIntent() the idea being to display a new notification once this one was dismissed by swipe. Unfortunately, I was not able to get this to work (the intent never fires when swiping).
I would also like to clarify, that the notification CAN BE swiped away, but IS NOT AFFECTED (i.e. is not deleted) by the clear-all button in the notification pane (the trash icon).
EDIT 2
As mentioned above I was not able to use setDeleteIntent() to recreate my notification when it is dismissed by swipe.
For now I settled with a workaround - my service does some periodical tasks. I am now calling
notificationManager.notify(10, service_notification);
when my task runs so that even if swiped away, my notification will be recreated after a while.
I still have a strong feeling that I just misread the documentation, since there are several notifications on my test device (Honor API23) that can not be swiped away.
EDIT 3
I have tried the same code on a different device (Lenovo API23) and the notification works as expected (can not be dismissed and can not be swiped). My problem seems to be device specific.
I made a few further tests (besides the ones mentioned in updates). I tried running various examples/tutorials from around the internet like this page here
On the Honor I was initially running my tests the notification could always be swiped, on the Lenovo and others the "ongoing" flag works as stated in the documentation, i.e. it can not be dismissed via swipe action.
To sum it up, my problem really seems to be device specific.
Related
I have a simple Flutter app that start a native background service using MethodChannel. This native background service notify my Flutter app using BasicMessageChannel<String> to display text when a particular native information is catched. All of this work perfectly when my app is in foreground. When an other app is in foreground, I can't see the text without having to switch to my app.
I want that my native service can display a particular Flutter screen even if an other application is running in foreground.
It can be perceived as not user friendly, but this is an message of utmost importance.
Any suggestion or solution will be welcome !
Note : Native service is for the moment only available in Java for Android, I'm working on C# for IOS side.
On Android you need to display a high priority notification. This displays the slide-down notification panel which will appear over the lock screen or another app. Since you are using native code already, you can create this notification there, or send a message to the Dart side (as you are doing, using MethodChannel) where it can use the flutter_local_notifications plugin to display it. When the user click the notification, your flutter app is brought to the foreground. In Java you might use code similar to this:
// Create an intent which triggers the fullscreen notification
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.setAction("SELECT_NOTIFICATION");
Class mainActivityClass = getMainActivityClass(context);
intent.setClass(context, mainActivityClass);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Build the notification as an ongoing high priority item to ensures it will show as
// a heads up notification which slides down over top of the current content.
final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
builder.setOngoing(true);
// Set notification content intent to take user to fullscreen UI if user taps on the
// notification body.
builder.setContentIntent(pendingIntent);
// Set full screen intent to trigger display of the fullscreen UI when the notification
// manager deems it appropriate.
builder.setFullScreenIntent(pendingIntent, true);
// Setup notification content.
int resourceId = context.getResources().getIdentifier("app_icon", "drawable", context.getPackageName());
builder.setSmallIcon(resourceId);
builder.setContentTitle("Your notification title");
builder.setContentText("Your notification content.");
MyPlugin.notificationManager().notify(someId, builder.build());
Then, for Android 8.1 or higher, add the following to your MainActivity class, found under the android/app/src/main/java/packageName/ folder
GeneratedPluginRegistrant.registerWith(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true);
setTurnScreenOn(true);
}
(This shows the Flutter app even when the screen is locked.)
Flutter only has one activity, so the above code will bring that Flutter activity to the foreground (note that you don't always see the notification, but sometimes you do - if you set it to autoCancel then touching it clears it). It's up to you to build the correct screen in Flutter, which you can do as you send the notification. Use Navigator.push or equivalent to change the page that Flutter is showing.
build.setOngoing(true);
build.setAutoCancel(false);
notification.flags= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
setOngoing is not working
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
/* Create or update. */
_notificationChannel = new NotificationChannel("TubeMateGo",
"Downlaod File Notification",
NotificationManager.IMPORTANCE_DEFAULT);
mNotifyManager.createNotificationChannel(_notificationChannel);
build = new NotificationCompat.Builder(getBaseContext(),_notificationChannel.getId());
build.setContentTitle("Download")
.setContentText("Download in progress")
.setSmallIcon(R.drawable.ic_launcher_foreground);
build.setOngoing(true);
build.setChannelId(_notificationChannel.getId());
build.setVisibility(Notification.VISIBILITY_PUBLIC);
build.setAutoCancel(false);
build.setContentIntent(_pedingIntent);
Notification notification = build.build();
notification.flags= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
mNotifyManager.notify(id, notification);
}
else {
build = new NotificationCompat.Builder(getBaseContext());
build.setContentTitle("Download")
.setContentText("Download in progress")
.setSmallIcon(R.drawable.ic_launcher_foreground);
build.setOngoing(true);
build.setVisibility(Notification.VISIBILITY_PUBLIC);
Notification notification = build.build();
notification.flags=Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
mNotifyManager.notify(id, notification);
}
When I try to remove notification from notification bar its removed why ?
i dont know where i am going wrong
According to Docs
public Notification.Builder setOngoing (boolean ongoing)
Set whether this is an "ongoing" notification. Ongoing notifications
cannot be dismissed by the user, so your application or service must
take care of canceling them. They are typically used to indicate a
background task that the user is actively engaged with (e.g., playing
music) or is pending in some way and therefore occupying the device
(e.g., a file download, sync operation, active network connection).
Then Why Its Dismissed By User?
EDIT 2
This Happens in Custom OS ? Like Vivo
Test - RedMI note 5 Pro - working fine
Then Why ? Not working with Vivo ?
In a few words, that's due to fragmentation and customization.
Probably that phone has some kind of setting to configure that behavior. As someone said one day: Some phones are just -trash-. Don't waste your time trying to fix an issue for every possible phone.
I've read a bunch of answers pertaining to this question and they all seem to be the same:
"Run your service with START_STICKY"
"Run your service in the foreground"
"Run your service with startService and don't bind it"
I'm doing ALL of these things, and my service STILL closes and restarts every time my activity is closed.
This is NOT an IntentService.
I'm also not calling stopSelf or stopService anywhere except in onClick handlers.
Please scroll down to my update - This behavior has been confirmed to be a bug in the android OS and I have reported it to google. Click here to view the report.
Starting my service from MainActivity:
svcIntent = new Intent(getBaseContext(), MyService.class);
startService(svcIntent);
In my onStartCommand:
// Enter foreground state
String title = "Service has been started...";
String subject = "Service is running...";
String body = "Monitoring your battery usage.";
Notification notification = new Notification(R.drawable.theicon, title,
System.currentTimeMillis());
if (prefs.getBoolean("notificationSounds", true))
notification.defaults |= Notification.DEFAULT_SOUND;
else
notification.sound = null;
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, subject, body, pendIntent);
startForeground(1500, notification);
At the end of my onStartCommand:
...
// Release WakeLock
wl.release();
return START_STICKY;
UPDATE
I FIGURED OUT WHATS CAUSING IT! But I have no idea how to fix it. In my service i also use an AlarmManager inside my service to set up function calls to the service a specified time away.
// Alarm manager setup for MyService
AlarmManager AM = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
svcIntent1 = new Intent(this, AlarmReceiver.class);
prefs.edit().putInt("initialBatt", initialBatt).apply();
svcIntent1.setAction("com.myApp.servicealarm");
pendingIntent = PendingIntent.getBroadcast(this, 93, svcIntent1, PendingIntent.FLAG_UPDATE_CURRENT);
// Set the alarm
AM.set(AlarmManager.RTC_WAKEUP, timeNow + waitTime, pendingIntent);
I noticed that if I DO NOT comment out the AM.set call to set an alarm, EVEN WITH AN EMPTY onReceive, my service is killed when the alarm goes off, after I swipe my app away in recent apps. If I comment out the set alarm call, then the service is never killed and keeps running after I close my app. What the heck?! I need this alarm for the functionality of my algorithms!!
It's very odd. As soon as the alarm goes off, my debug message does not print, and my service restarts. But the second time around, after the service restarts, the debug message does print and the program executes successfully.
I've tried this and it still happens with a normal broadcast receiver as well. I've also stripped my code down to ONLY the set alarm call from my service and the broadcast receiver, and the same thing occurs so it's not my algorithm. Apparantly if you have a foreground service that sets an alarm, when the alarm goes off your service restarts.
CLOSING
This behavior seems to be caused by a bug in the android OS so I do not expect an answer. If you'd like to see this bug for yourself, click here. I've provided a project that you can compile and reproduce the problem with.
Android kills the process when the broadcast Intent is sent (before it is received/processed in your app).
This is a nasty Android bug, as of 4.4 (API 19).
See https://code.google.com/p/android/issues/detail?id=63618&can=1&q=service%20restart%20broadcast&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
especially comments #22 and #23
Unfortunately, almost all "open" issues were marked as "obsolete" recently, with the assumption that they were all fixed in Android 5.0. There's no way for a developer to reopen an "obsolete" issue.
EDIT: Add details about foreground broadcast
Based on the information in the linked issue, it looks like adding Intent.FLAG_RECEIVER_FOREGROUND to your broadcast Intent will ensure that the the process does not get killed on the next receipt of a broadcast Intent.
To do this, add:
svcIntent1.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
to the code where you set your alarm in the AlarmManager.
Please read the comments in the linked issue for more details.
Try to run your service in separate process. Define it in your manifest like this:
<service
android:name=".path.to.service.class"
android:process=":my_service"/>
i want to make a notification like BBM connected notification (for android)...
so when i open my app, the notification will appear, and it cant be canceled...
so i use this code
nm=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification=new Notification(android.R.drawable.stat_notify_more, "this is important", System.currentTimeMillis());
notification.flags = notification.FLAG_NO_CLEAR; //notification never dissapear
Context context=MainActivity.this;
CharSequence title="hello there";
CharSequence detail="this is demo";
Intent intent=new Intent(context,MainActivity.class);
PendingIntent pending=PendingIntent.getActivity(context, 0, intent, 0);
notification.setLatestEventInfo(context, title, detail, pending);
nm.notify(0, notification);
i put that code in public void onCreate(Bundle savedInstanceState)...
so, the notification cant cancel when i click or slide.
and when i close my app, the notification still there.
then i have an idea to use this code :
#Override
protected void onDestroy() {
nm.cancelAll();
}
but, this function never called???
how can i cancel the notification when i close the app?
It might be because onDestroy will not be called immediately when you navigate away with the home button.
Android has callback methods available that work well with notifications etc.
These are called onUserLeaveHint() and onUserInteraction().
The JavaDoc for onUserLeaveHint() states:
Called as part of the activity lifecycle when an activity is about to
go into the background as the result of user choice. For example, when
the user presses the Home key, onUserLeaveHint() will be called, but
when an incoming phone call causes the in-call Activity to be
automatically brought to the foreground, onUserLeaveHint() will not be
called on the activity being interrupted. In cases when it is invoked,
this method is called right before the activity's onPause() callback.
This callback and onUserInteraction() are intended to help activities
manage status bar notifications intelligently; specifically, for
helping activities determine the proper time to cancel a notfication.
I'd suppose you will want to override either one of these; especially onUserLeaveHint() seems a good choice for your purpose.
#Override
protected void onUserLeaveHint()
{
nm.cancelAll();
super.onUserLeaveHint();
}
You can't intercept the event of 'force closing' the application process. Android doesn't support this. When the user does this, the system calls Process.killProcess(int pid) and all resources will be freed. It's just not possible to catch this event and perform tasks before the process is really killed. Maybe a workaround is possible, but it wouldn't be how Android wants it.
There is no guarantee that the method will called. If application will not have enough memory, it can be killed by OS.
There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.
You missed the super.onDestroy
#Override
protected void onDestroy() {
nm.cancelAll();
super.onDestroy();
}
I'm having a bit of a problem, and having looked in many places I can't seem to find an answer.
I have a service, this service checks for notifications on a site and alerts the user of a new notification. When the user clicks this notification, it should bring them to an Activity (I'll call this CommentActivity)
However, if I click on the notification inside of the app, nothing happens. If I click on the notification outside of the app, it opens up the last activity shown before the app was closed.
Here is my code for displaying the notification:
Notice n; //Notification from site
....
Intent nIntent = new Intent(Intent.ACTION_MAIN);
nIntent.setClass(getApplicationContext(), CommentActivity.class);
nIntent.putExtra("activity_id", n.getFeedID());
nIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
PendingIntent pIntent = PendingIntent.getActivity(getApplicationContext(), 0, nIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(FlankNotificationService.this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(n.getTitle())
.setContentText(n.getText())
.setContentInfo(arg0.getNotificationAmount() + " notification" + (arg0.getNotificationAmount() == 1 ? "." : "s."))
.setContentIntent(pIntent);
Notification mm = mBuilder.getNotification();
mm.flags = Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL;
mm.tickerText = n.getText();
....
mNM.notify(1558, mm);
So instead of opening up the CommentActivity, it will either resume the app (if the app is not opened) or do nothing (if the app is opened)
Thank you.
Edit 1:
Added Intent.FLAG_ACTIVITY_REORDER_TO_FRONT flag to Intent. Still does not fix the problem.
Edit 2:
I found the cause of my problem (which was a rather silly cause). When the CommentActivity starts, if it does not get passed any data from either the savedInstance or the Intent.getExtra, then it will just close out. The reason why I did not think of this at first was because it didn't even show the transition. After fixing the Intent.getExtra problem it now works. Thank you for all the answers.
You may want to add the Intent.FLAG_ACTIVITY_REORDER_TO_FRONT flag to your Intent. This will ensure that the CommentActivity is the resumed and active Activity when the intent is handled.