I'm an Android app developer, and is developing an instant message app. The app has a notification problem on Android Oreo system, which shows persistent notification "App is running in the background" and cannot be cleared, and it's OK on system before Android Oreo.
Screenshot: The phone shows persistent notification App is running in the background
I find some discussion, such as Nexus Help Forum about this question, but it doesn't work in my phone's settings.
I want to know how to hide this notification programmatically and the app also can receive message instantly because it's an instant message app.
Any help is very appreciated.
The app has a notification problem on Android Oreo system, which shows persistent notification "App is running in the background" and cannot be cleared, and it's OK on system before Android Oreo.
You used startForeground() with a minimum-importance Notification.
I want to know how to hide this notification programmatically
Use startForeground() with a Notification that has higher than minimum importance. Or, do not use startForeground().
I find some install message apps such as WeChat, Facebook doesn't have this problem on Android Oreo
They are not using foreground services, presumably. For example, they might be using Firebase Cloud Messaging (FCM).
Before we talk about how to get rid of it, however, let’s talk about why it’s there in the first place.
Basically, in previous versions of Android, there was no real way of knowing if an app was running in the background doing a bunch of stuff it’s not supposed to be doing. In most scenarios, these misbehaving apps would wreak havoc on the battery by keeping the system awake—these are called “wakelocks.” In laymen’s terms, it was keeping the system from sleeping. That’s bad.
With Oreo, Google is calling out developers that let their apps do this sort of thing with the new notification. Essentially, if an app is running in the background and chewing up battery life, this new notification will tell you.
NOTE: There are a few legitimate scenarios where an app will continuously run in the background, like the VPN service running. Often, however, apps are running in the background unjustifiably.
It’s worth noting, though, removing the notification does not solve the issue. Period. There’s a reason this notification exists, and getting rid of it will do nothing to solve the underlying issue. You’ll either need to change a setting within the app or uninstall it altogether.
As long as you understand that and still want to remove it, let’s do this thing.
Because this is a relatively crucial system setting, there’s no way within Oreo itself to remove it. That makes sense.
But like with most things, the developer community has found a way to remove it, and developer iboalali released an app to do just that. It’s actually just called “Hide ‘running in the background’ Notification,” which is about as straightforward as an app name could ever be. Go ahead and give it an install.
Without root, there is no way to actually prevent Android System from displaying the persistent “app is running in the background” notification in Android 8.0 Oreo. Looking at the source code for the ForegroundServiceController, its implementation, and the ForegroundServiceDialog doesn’t really reveal anything we can take advantage of. Programatically nothing have been found so far.
Here's a Blog post that can help you
First, you must have the NotificationListenerService implementation. Second, in this service (after onListenerConnected callback), check the active ongoing notifications with packageName called 'android'. And check this notification's title is your app name or text value is 'App is running in the background' and snooze it.
public class NLService extends NotificationListenerService {
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {}
#Override
public void onListenerConnected() {
super.onListenerConnected();
checkOngoingNotification();
}
#Override
public void onNotificationPosted(StatusBarNotification sbn){
if(sbn.isOngoing()) {
checkOngoingNotification();
return;
}
}
private void checkOngoingNotification() {
StatusBarNotification[] activeNotifications = getActiveNotifications();
Log.i("NLService", "Active notifications size : " + activeNotifications.length);
for (StatusBarNotification statusBarNotification : activeNotifications) {
Log.i("NLService", "notification package : " + statusBarNotification.getPackageName());
Log.i("NLService", "notification id : " + statusBarNotification.getId());
Log.i("NLService", "notification key : " + statusBarNotification.getKey());
Log.i("NLService", "isOngoing : " + statusBarNotification.isOngoing());
Log.i("NLService", "isClearable : " + statusBarNotification.isClearable());
Log.i("NLService", "groupKey : " + statusBarNotification.getGroupKey());
Notification notification = statusBarNotification.getNotification();
CharSequence title = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
CharSequence text = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
if (title != null && text != null) {
Log.i("NLService", "title : " + title);
Log.i("NLService", "text : " + text);
if (statusBarNotification.getPackageName().equals("android") &&
(title.toString().contains("Your App Name") || text.toString().contains("App is running"))) {
long snoozLong = 60000L * 60L * 24L * 20L;
this.snoozeNotification(statusBarNotification.getKey(), snoozLong);
Log.i("NLService", "Snoozed notification : " + title);
}
}
}
}
It turns out startForeground() with channel's IMPORTANCE_MIN is not only one source of the notification. If you call startForeground() and give it notification without setSmallIcon() result will be the same.
https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ServiceRecord.java#816
Also you could find in logs something like that:
ActivityManager: Attempted to start a foreground service (ComponentInfo{com.example.app/com.example.app.ExampleService}) with a broken notification (no icon: Notification(channel=channel_example pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE))
Related
Context
I am working on an app that uses FCM. The use of this application is to alert a user of an event that is occurring (such as an alarm system). In view of the alarm nature of the notification, it is essential that a sound is played when receiving a notification even if the smartphone is in silent or vibrate mode.
Question
Is there a way to achieve this described behavior for all smartphone modes (silent, vibrate, sound) ?
What I've tried
As I am working with API26> I created a notification channel to have the highest priority which is Max Priority,
I've set the notification channel to bypass Do Not Disturb mode like so:
notificationChannel.SetBypassDnd(true);
Obviously it only affects the Do Not Disturb mode and absolutely not what I want,
In the notification builder, I've set the notification priority to Max and the category to Alarm:
.SetPriority(NotificationCompat.PriorityMax)
.SetCategory(NotificationCompat.CategoryAlarm);
Reading the Android documentation, this feature is also related to Do Not Disturb Mode.
I am actively looking for a solution to this problem, but at this point I'm a bit stuck.
Any suggestions ?
I've read about a full screen intent in the Android documentation but it's not written that a sound will fire if the smartphone is in silent mode.
Maybe there is a way to create a service that rings when the notification arrives? But this service has to be running all the time, which isn't really a good design idea.
If you guys have any idea, any remarks or suggestions, i'd be grateful to read them !
I believe you need to set priority for your notification.
private fun setPriorityForAlarmNotification() {
if (notificationManager.isNotificationPolicyAccessGranted) {
notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val policy = NotificationManager.Policy(PRIORITY_CATEGORY_ALARMS, 0, 0)
notificationManager.notificationPolicy = policy
}
}
}
As I can see you setCategory for your notification builder is NotificationCompat.CategoryAlarm.
However, in order to set this priority, you need this permission on your manifest
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
And request permission if needed
fun requestNotificationPolicyPermission() {
val notificationManager = activity!!.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (!notificationManager.isNotificationPolicyAccessGranted) {
val intent = Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)
startActivityForResult(intent, REQUEST_CODE_NOTIFICATION_POLICY)
}
}
This solution should work absolutely. Hope this can help you :D
Background:
There are 2 type of notifications.... Plain ones (The old traditional type) and the ones since Lolipop that can have "reply" actions.
I made a NotificationListenerService that can dismiss notifications (from any other app, not only mine) using command cancelNotification only if the notification is the old "plain" type.
But if the notification has "reply" actions (android wear pending intents), when I execute the cancelNotification(ID) the notification is removed and "re-posted" reapearing on the notification bar. I can endlessly try to remove the notifications and they always come again.
String removing = "example ID";
#Override
public void onNotificationPosted(StatusBarNotification sbn){
if (sbn.getKey().equals(removing))
{
Log.e("INTENT","DISMISS MESSAGE");
super.cancelNotification(sbn.getKey());
super.cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId());
removing = "";
return;
}
both commands (only one parameter and 3 parameters) behave the same way.
Is there another way to dismiss a message that is specting a "wearableExtender" reply?
All prerequisites are met:
Added -> android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
Also 'Notification Access' is active and checked.
I'm building an application using Ionic Framework that implements a chat function similar to good-old facebook messenger, in that i want to notify users of a chat message, but if they view it elsewhere, i want to remove the notification from their home screen.
I'm using firebase as a back-end for push notifications (though that could be changed i suppose).
I know that you can't expire a remote notification, but i've been told you can expire + remove a local notification, so my question is - can i reliably receive a remote notification, create a local one, and display that, and then in response to a notification with a scope of 'expire' or 'remove', delete a local notification so that my users don't see a duplication of information?
Most plugins tend to detect the status of the app and add a remote notification to the homescreen with the info you've pushed by default, is there a way to avoid this?
Thanks guys.
EDIT:
- Local notifications: http://ionicframework.com/docs/native/local-notifications/
- Firebase cloud messaging: https://github.com/fechanique/cordova-plugin-fcm
As far as I can tell there're no plugins which accomplish all what you need. However..
can i reliably receive a remote notification, create a local one, and display that, and then in response to a notification with a scope of 'expire' or 'remove', delete a local notification so that my users don't see a duplication of information?
Most plugins tend to detect the status of the app and add a remote notification to the homescreen with the info you've pushed by default, is there a way to avoid this?
Yes, by using silent notifications and building the local notification by yourself.
For a project I'm working in, I modified the plugin cordova-plugin-fcm to add support for (local on demand) notifications dismiss/display, send multiple notifications to the cordova app, and some PRs that are not included yet. Also I build the notification by myself, to have full control of what is displayed. You can take a look at the code to get some ideas.
In brief it works like this:
Firstly, I send a "silent" push to the app, which is not displayed by Android:
{
"content_available": true, // IMPORTANT: For Apple -> content-available: 1, for firebase -> content_available: true
"priority": "high",
"to": "/topics/all", // or to a fcm token
"data"{
"title": "My title", // this implies that you display the notification by yourself
"body": "My body", // this implies that you display the notification by yourself
"type": "NEW_USER_MESSAGE", // only relevant to this project
"userId": "1", // only relevant to this project
"timestamp", "150000000"
}
}
Note: If the payload have the "notification": {} item, Android will display it on the system tray (if the app is in background).
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
Secondly, when the push arrives to the app (in onMessageReceived()), I build the local notification, assigning it a TAG and an ID. This is the way you can use to dismiss it later.
For example, you could create a local notification with the TAG "NEW_USER_MESSAGE" and ID 1 (a constant indicating a state of the message, or the user ID for example). Also, Android will replace notifications with the same TAG and ID, so this is another way to automatically replace notifications (for example if you send a generic message, like "New update available").
public static String TYPE_NEW_USER_MESSAGE = "NEW_USER_MESSAGE";
public static String TYPE_USER_LEFT_ROOM = "USER_LEFT_ROOM";
NotificationManager notificationManager =
(NotificationManager) _ctx.getSystemService(Context.NOTIFICATION_SERVICE);
// based in the type of the message you've received, you can stylize the notification
if (type.equals( TYPE_USER_LEFT_ROOM )){
notificationBuilder.setColor(Color.RED);
notificationBuilder.setLights(Color.RED, 1000, 500);
}
else if (type.equals( TYPE_NEW_USER_MESSAGE )){
notificationBuilder.setColor(Color.BLUE);
notificationBuilder.setLights(Color.BLUE, 1000, 1000);
}
Notification n = notificationBuilder.build();
notificationManager.notify(type, userId, n);
One advantage of doing it in this way, is that you have full control of the notification to be displayed, so you can stylize it like you want.
If you want to discard expired messages, you can check out the elapsed time between the sent timestamp and the current timestamp:
java.util.Date now = new java.util.Date();
java.util.Date sent_timestamp = new java.util.Date( Long.valueOf(timestamp.toString()) );
final Long elapsed_time = ((now.getTime() - sent_timestamp.getTime()) / 1000);
Log.d(TAG, "New message. sent " + elapsed_time + "s ago");
Thirdly, when the user clicks on a notification Android will launch your app, and the plugin will send the payload of the push message to the cordova view (onNotificationReceived()).
Once your app is opened and you have received the push message, you can dismiss it adding a new action to the plugin:
onNotificationReceived(data){
if (data.wasTapped === true){
if (data.type === 'NEW_USER_MESSAGE'){
FCMPlugin.dismissNotification(NEW_USER_MESSAGE, 1);
}
}
}
The Android action:
else if (action.equals( ACTION_DISMISS_NOTIFICATION )) {
cordova.getThreadPool().execute(new Runnable() {
public void run() {
try{
Log.d(TAG, "FCMPlugin dismissNotificaton: " + args.getString(0)); //tag
NotificationManager nManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
nManager.cancel(args.getString(0)/*NEW_USER_MESSAGE*/, args.getInt(1) /*1*/);
Log.d(TAG, "FCMPlugin dismissNotificaton() to remove: " + id); //tag
callbackContext.success();
}catch(Exception e){
callbackContext.error(e.getMessage());
}
}
});
https://github.com/TrustedCircles/cordova-plugin-fcm/blob/master/src/android/FCMPlugin.java#L286
And the method exposed to the cordova app:
// dismisses a notification by tag+id
FCMPlugin.prototype.dismissNotification = function( tag, userId, success, error ){
exec(success, error, "FCMPlugin", 'dismissNotification', [tag, userId]);
}
https://github.com/TrustedCircles/cordova-plugin-fcm/blob/master/www/FCMPlugin.js#L65
The only tricky bit with notifications in cordova/ionic is the JS part receiving the notification and triggering the Android code.
I used https://github.com/phonegap/phonegap-plugin-push library and its pretty straight forward.
There is a callback when notifications are received in JS(Cordova/Ionic), use this to render you notifications locally in Android.
P.S: Basel's answer tells you how to clear your notifications, so I decided to leave that bit out.
I'm developing a chat application using Firebase Database in Android.
I've already done the core (chat and user's list activities), but I have yet to do the notification system.
I wanted to implement Google FCM as I am using firebase, but I find an hole in it, as it seems that I can not handle at all notifications delivered to iOs apps that are not in foreground.
As found in the Data notification documentation
On iOS, FCM stores the message and delivers it only when the app is in the foreground and has established a FCM connection. On Android, a client app receives a data message in onMessageReceived() and can handle the key-value pairs accordingly.
And I need to catch the data notification even when the app is in background, I need that specifically because I want to update my badge counter on the app icon to let the user know how many unread messages he has.
I'm now tryng the OneSignal solution can receive notification even when in background, it's free and interfaces with GCM. I'm sad to not stay with Google but if I can't update the badge count using FCM I have to look to an other side.
Any consideration will be appreciated.
Use below code for get badge from server when app is in background or not running because your FirebaseMessagingService is running.
If you have any doubts regarding show badge on app icon than comment it because just yesterday i did show badge on app icon.
public class Custom_FirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "FirebaseMsgService";
//private Activity strPushClickDesti;
String activityName;
#Override
public void zzm(Intent intent) {
Log.i("uniqbadge", "zzm");
Set<String> keys = intent.getExtras().keySet();
for (String key : keys) {
try {
Log.i("uniq", " " + key + " " + intent.getExtras().get(key));
if (key.equals("badge")) {
String cnt = intent.getExtras().get(key).toString();
int badgeCount = Integer.valueOf(cnt);
Log.i("uniq", " badge count " + badgeCount);
ShortcutBadger.applyCount(this, badgeCount);
Log.i("uniq", " " + "end");
}
} catch (Exception e) {
Log.i("uniqbadge", "zzm Custom_FirebaseMessagingService" + e.getMessage());
}
}
super.zzm(intent);
}
There is in fact a feature explored by OneSignal to achieve this behavior. It is the content-available value in the notification payload. The good news is that is native to iOS 7 and above. So if you can customize the payload in FCM, it is possible. Just activate remote-notification in background modes and handle the badge update in application(didReceiveRemoteNotification:fetchCompletionHandler:) and you are good to go.
I have a problem with appcelerator, regarding to android background service.
The service starts, and when I press the home button, the service is still running, and also when I click the back button.
But when I remove my app from the recent apps list(pressing long home button) , the service stops.
This is my code for the call service:
var SECONDS = 6;
// every 10 minutes
var intent = Titanium.Android.createServiceIntent({
url : 'myservice.js'
});
intent.putExtra('interval', SECONDS * 1000);
intent.putExtra('message_to_echo', num);
//in millimeter
var service = Titanium.Android.createService(intent);
service.addEventListener('resume', function(e) {
// num++;
// alert(num);
});
service.start();
And this is the file service code:
var service = Titanium.Android.currentService;
var intent = service.intent;
var message = intent.getStringExtra("message_to_echo");
var intent = Ti.Android.createIntent({
flags : Ti.Android.FLAG_ACTIVITY_CLEAR_TOP | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP,
// Substitute the correct classname for your application
className : 'com.mappa.angeli.MappaActivity',
});
intent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
// Create a PendingIntent to tie together the Activity and Intent
var pending = Titanium.Android.createPendingIntent({
intent: intent,
flags: Titanium.Android.FLAG_UPDATE_CURRENT
});
// Create the notification
var notification = Titanium.Android.createNotification({
// icon is passed as an Android resource ID -- see Ti.App.Android.R.
// icon: Ti.App.Android.R.drawable.my_icon,
contentTitle: 'Something Happened',
contentText : 'Click to return to the application.',
// tickerText: 'Our app made a notification!',
defaults:Titanium.Android.NotificationManager.DEFAULT_SOUND,
contentIntent: pending
});
// Send the notification.
Titanium.Android.NotificationManager.notify(0, notification);
How can I run the service continously, such as whats app or something like that?
Please help me, it's very important.
Thanks in advance!!!
First let me give you a little perspective about your questions so that you'll be able to understand the logic behind.
My Understanding about your Question and Conclusion
What you are asking for is not possible even in the native app, specifically if you are using the background services.
Why it is doing so?
Because the application is no longer in service anymore. The app is
forcefully removed from the background and hence the OS stops all its
services running in the background to clear the Memory.
What most of the default players do to play music is the use the foreground service and show a notification that the app is running and is processing something in the background (in this case playing the songs).
Options that you could look for
I would suggest you to add up 'push notification service' to your app rather than going in for 'background service'. You will get the notification even if your app is not in the background (same as it is in WhatsApp).
You could follow the below link to implement the GCM push services for android:
http://www.tidev.io/2013/12/20/using-gcm-for-android-push-notifications-with-acs/
Hope this helps.
Good Luck, Cheers