Android appcompat v7 support library Notification compact view repeating buttons issue - android

This is the method I use to retrieve/create/update app's notification inside a service class, called PlayerService:
import android.support.v7.app.NotificationCompat;
import android.app.Notification;
import android.app.NotificationManager;
// ...
private Notification getCompatNotification(String contentText) {
m_notificationBuilder
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("PlayerService")
.setContentText(contentText)
.setContentIntent(null)
.setWhen(0)
.setShowWhen(false)
.addAction(android.R.drawable.ic_media_previous, "", null)
.addAction((isPlaying() ? android.R.drawable.ic_media_pause : android.R.drawable.ic_media_play), "", null)
.addAction(android.R.drawable.ic_media_next, "", null)
.setStyle(new NotificationCompat.MediaStyle()
.setShowActionsInCompactView(0, 1, 2)
.setShowCancelButton(true)
.setCancelButtonIntent(null))
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setPriority(NotificationCompat.PRIORITY_MAX);
Notification notification = m_notificationBuilder.build();
return notification;
}
Now, when the media player activity is opened but playback not started, notification shows its big view with those 3 action buttons(previous, play/pause, next), but when the playback is started, notification view changes to compact and displays those 3 buttons in first place and then again the first and second. Please see the images.Test device has KitKat 4.4.4.
No Playback
Playback started
To update notification:
private void updateNotification(String contentText){
nm.notify(NOTIFICATION_ID, getCompatNotification(contentText));
}
And in onCreate():
#Override
public void onCreate() {
super.onCreate();
nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
m_notificationBuilder = new NotificationCompat.Builder(this);
}
In onStartCommand():
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(NOTIFICATION_ID, getCompatNotification("Test Content"));
return START_STICKY; // run until explicitly stopped.
}
Can't figure out what is the problem. Any help highly appreciated.

You are using the same NotificationCompat.Builder instance to create your Notifications and whenever you add an Action it is added to the previous List and creates an unwanted behavior into the System Api. You have to call clearActions() first on the Builder before adding any if you want to use the same Builder instance.

hope this could help: add android:stateListAnimator="#null" to your button

Related

Is there any Android notifications setContentTitle and setContextText magic values?

I'm currently working on a library with android support.
I'm being asked to notify user on foreground service start.
The notification must contain "ApplicationName" as title, and "ApplicationName is running" as text. The notification icon has to be the same as the launcher one.
The target API level is 26.
The notification did not work because the previous developper forgot to open the notification chanel. This is now fixed, we have the notification that pops correctly. And the label are matching expectation.
But now i'm questioning why the notification contains the expected values. I could not find any reference in the javadoc.
The following code will display the notification as expectecd the application's name as title and the text "ApplicationName is running" :
#Override
public void onCreate() {
NotificationChannel channel = new NotificationChannel("APPLICATION_CHANNEL", "MyService", NotificationManager.IMPORTANCE_LOW);
channel.setDescription(notificationChannelText);
//block below useful for head up notification
channel.setSound(null, null);
channel.setShowBadge(false);
channel.enableLights(false);
channel.enableVibration(false);
NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
foregroundNotification();
return Service.START_STICKY;
}
/**
* In order to start foreground service we and generate a service running notification.
*/
private void foregroundNotification() {
Context context = getApplicationContext();
Intent notificationIntent = new Intent(context, getClass());
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(context, "APPLICATION_CHANNEL")
.setContentTitle("Title")
.setContentText("Subject")
.setContentIntent(pendingIntent)
.build();
startForeground(42666, notification);
}
Why doesn't it just display a notification with "Title" as the title and "Subject" as content ?
Are there any constants or magic values that we have to know ?
Where can we find any documentation or definition about it ?
Edit 2020/04/01 : Added code representing notification channel creation
I found your problem. This is result of your code:
and after add small icon:
.setSmallIcon(R.mipmap.ic_launcher)
It works fine

NotificationManager Using Clear All Notification [duplicate]

Is it possible to clear a notification programatically?
I tried it with the NotificationManager but its not working.
Is there any other way I can do it?
Use the following code to cancel a Notification:
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFICATION_ID);
In this code there is alway the same id used for notifications. If you have different notifications that need to be canceled you have to save the ids that you used to create the Notification.
From: http://developer.android.com/guide/topics/ui/notifiers/notifications.html
To clear the status bar notification when the user selects it from the Notifications window, add the "FLAG_AUTO_CANCEL" flag to your Notification object. You can also clear it manually with cancel(int), passing it the notification ID, or clear all your Notifications with cancelAll().
But Donal is right, you can only clear notifications that you created.
Since no one has posted a code answer to this:
notification.flags = Notification.FLAG_AUTO_CANCEL;
.. and if you already have flags, you can OR FLAG_AUTO_CANCEL like this:
notification.flags = Notification.FLAG_INSISTENT | Notification.FLAG_AUTO_CANCEL;
Please try methods provided in NotificationManagerCompat.
To remove all notifications,
NotificationManagerCompat.from(context).cancelAll();
To remove a particular notification,
NotificationManagerCompat.from(context).cancel(notificationId);
Starting with API level 18 (Jellybean MR2) you can cancel Notifications other than your own via NotificationListenerService.
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class MyNotificationListenerService extends NotificationListenerService {...}
...
private void clearNotificationExample(StatusBarNotification sbn) {
myNotificationListenerService.cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId());
}
If you are generating Notification from a Service that is started in the foreground using
startForeground(NOTIFICATION_ID, notificationBuilder.build());
Then issuing
notificationManager.cancel(NOTIFICATION_ID);
does not end up canceling the Notification, and the notification still appears in the status bar. In this particular case, you will need to issue
stopForeground( true );
from within the service to put it back into background mode and to simultaneously cancel the notifications. Alternately, you can push it into the background without having it cancel the notification and then cancel the notification.
stopForeground( false );
notificationManager.cancel(NOTIFICATION_ID);
Notification mNotification = new Notification.Builder(this)
.setContentTitle("A message from: " + fromUser)
.setContentText(msg)
.setAutoCancel(true)
.setSmallIcon(R.drawable.app_icon)
.setContentIntent(pIntent)
.build();
.setAutoCancel(true)
when you click on notification, open corresponding activity and remove notification from notification bar
I believe the most RECENT and UPDATED for AndroidX and backward compatibility. The best way of doing (Kotlin and Java) this should be done as:
NotificationManagerCompat.from(context).cancel(NOTIFICATION_ID)
Or to cancel all notifications is:
NotificationManagerCompat.from(context).cancelAll()
Made for AndroidX or Support Libraries.
If you're using NotificationCompat.Builder (a part of android.support.v4) then simply call its object's method setAutoCancel
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setAutoCancel(true);
Some guys were reporting that setAutoCancel() did not work for them, so you may try this way as well
builder.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
Note that the method getNotification() has been deprecated!!!
// Get a notification builder that's compatible with platform versions
// >= 4
NotificationCompat.Builder builder = new NotificationCompat.Builder(
this);
builder.setSound(soundUri);
builder.setAutoCancel(true);
this works if you are using a notification builder...
Actually as answered before starting with API Level 18 you can cancel Notifications posted by other apps differet than your own using NotificationListenerService but that approach will no longer work on Lollipop, here is the way to remove notifications covering also Lillipop API.
if (Build.VERSION.SDK_INT < 21) {
cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId());
}
else {
cancelNotification(sbn.getKey());
}
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager Nmang = (NotificationManager) getApplicationContext()
.getSystemService(ns);
Nmang .cancel(getIntent().getExtras().getInt("notificationID"));
All notifications (even other app notifications) can be removed via listening to 'NotificationListenerService' as mentioned in NotificationListenerService Implementation
In the service you have to call cancelAllNotifications().
The service has to be enabled for your application via:
‘Apps & notifications’ -> ‘Special app access’ -> ‘Notifications access’.
this code worked for me:
public class ExampleReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
int notificationId = 1;
notificationManager.cancel(notificationId);
}
}
A function written in Kotlin:
/**
* Delete the notification
*/
fun delete(context: Context, notificationId: Int) =
with(NotificationManagerCompat.from(context)) {
cancel(notificationId)
}
Or shorter:
fun delete(context: Context, notificationId: Int) = NotificationManagerCompat.from(context).cancel(notificationId)
If you use OneSignal, you must use one of this:
Specific notification:
OneSignal.removeNotification(mutableNotification.androidNotificationId)
All notifications:
OneSignal.clearOneSignalNotifications()
In OneSignal's java doc says:
For removeNotification
Cancels a single OneSignal notification based on its Android notification integer ID. Use
* instead of Android's {#link NotificationManager#cancel(int)}, otherwise the notification will be restored
* when your app is restarted.
For clearOneSignalNotifications
If you just use
* {#link NotificationManager#cancelAll()}, OneSignal notifications will be restored when
* your app is restarted.
To clear notifications on Oreo and greater versions
//Create Notification
Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setAutoCancel(true);
Notification notification = builder.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
createNotificationChannel(builder, notificationManager);
mNotificationManager=notificationManager;
startForeground(1, notification);
//Remove notification
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mNotificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
}

Running a foreground service in Android without a notification icon [duplicate]

I want to create a service and make it run in the foreground.
Most example codes have notifications on it. But I don't want to show any notification. Is that possible?
Can you give me some examples? Are there any alternatives?
My app service is doing mediaplayer. How to make system not kill my service except the app kill it itself (like pausing or stopping the music by button).
As a security feature of the Android platform, you cannot, under any circumstance, have a foregrounded service without also having a notification. This is because a foregrounded service consumes a heavier amount of resources and is subject to different scheduling constraints (i.e., it doesn't get killed as quickly) than background services, and the user needs to know what's possibly eating their battery. So, don't do this.
However, it is possible to have a "fake" notification, i.e., you can make a transparent notification icon (iirc). This is extremely disingenuous to your users, and you have no reason to do it, other than killing their battery and thus creating malware.
Update: This was "fixed" on Android 7.1.
https://code.google.com/p/android/issues/detail?id=213309
Since the 4.3 update, it's basically impossible to start a service with startForeground() without showing a notification.
You can, however, hide the icon using official APIs... no need for a transparent icon:
(Use NotificationCompat to support older versions)
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MIN);
I've made peace with the fact the notification itself still needs to be there but for who ever who still wants to hide it, I may have found a workaround for that as well:
Start a fake service with startForeground() with the notification and everything.
Start the real service you want to run, also with startForeground() (same notification ID)
Stop the first (fake) service (you can call stopSelf() and in onDestroy call stopForeground(true)).
Voilà! No notification at all and your second service keeps running.
This no longer works as of Android 7.1 and it may violate Google Play's developer policies.
Instead, have the user block the service notification.
Here's my implementation of the technique in the answer by Lior Iluz.
Code
ForegroundService.java
public class ForegroundService extends Service {
static ForegroundService instance;
#Override
public void onCreate() {
super.onCreate();
instance = this;
if (startService(new Intent(this, ForegroundEnablingService.class)) == null)
throw new RuntimeException("Couldn't find " + ForegroundEnablingService.class.getSimpleName());
}
#Override
public void onDestroy() {
super.onDestroy();
instance = null;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
ForegroundEnablingService.java
public class ForegroundEnablingService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ForegroundService.instance == null)
throw new RuntimeException(ForegroundService.class.getSimpleName() + " not running");
//Set both services to foreground using the same notification id, resulting in just one notification
startForeground(ForegroundService.instance);
startForeground(this);
//Cancel this service's notification, resulting in zero notifications
stopForeground(true);
//Stop this service so we don't waste RAM.
//Must only be called *after* doing the work or the notification won't be hidden.
stopSelf();
return START_NOT_STICKY;
}
private static final int NOTIFICATION_ID = 10;
private static void startForeground(Service service) {
Notification notification = new Notification.Builder(service).getNotification();
service.startForeground(NOTIFICATION_ID, notification);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
AndroidManifest.xml
<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />
Compatibility
Tested and working on:
Official Emulator
4.0.2
4.1.2
4.2.2
4.3.1
4.4.2
5.0.2
5.1.1
6.0
7.0
Sony Xperia M
4.1.2
4.3
Samsung Galaxy ?
4.4.2
5.X
Genymotion
5.0
6.0
CyanogenMod
5.1.1
No longer working as of Android 7.1.
Warning: although this answer appears to work, it in fact silently prevents your service from becoming a foreground service.
Original answer:
Just set your notification's ID to zero:
// field for notification ID
private static final int NOTIF_ID = 0;
...
startForeground(NOTIF_ID, mBuilder.build());
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.cancel(NOTIF_ID);
...
A benefit you can get is, a Service will be able to runs on high priority without destroyed by Android system, unless on high memory pressure.
To make it work with Pre-Honeycomb and Android 4.4 and higher, make sure that you use NotificationCompat.Builder which provided by Support Library v7, instead of Notification.Builder.
EDIT
This code will not work anymore due to security reasons in newer api level
NotificationId cannot be set to "0" (which will cause the app to crash)
startForeground(1, notification)
This is the perfect way to show notification (recommended method)
But if you need it reagrdless of the recommended method then try removing the "notificationManager.createNotificationChannel("channel_id")" from your code.
OR
USE notificationManager.removeNotificationChannel(channel)
You can use this (as suggested by #Kristopher Micinski):
Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 42, note );
UPDATE:
Please note that this is not allowed anymore with Android KitKat+ releases. And keep in mind that this is more or less violating the design principle in Android that makes background operations visible to users as mentioned by #Kristopher Micinski
You can hide notification on Android 9+ by using custom layout with layout_height = "0dp"
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.custom_notif);
builder.setContent(remoteViews);
builder.setPriority(NotificationCompat.PRIORITY_LOW);
builder.setVisibility(Notification.VISIBILITY_SECRET);
custom_notif.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp">
</LinearLayout>
Tested on Pixel 1, android 9.
This solution doesn't work on Android 8 or less
Update: this no longer works in Android 4.3 and above
There is one workaround.
Try creating notification without setting icon, and the notification would not show. Don't know how it works, but it does :)
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Title")
.setTicker("Title")
.setContentText("App running")
//.setSmallIcon(R.drawable.picture)
.build();
startForeground(101, notification);
Block the foreground service notification
Most answers here either don't work, break the foreground service, or violate Google Play policies.
The only way to reliably and safely hide the notification is to have the user block it.
Android 4.1 - 7.1
The only way is to block all notifications from your app:
Send user to app's details screen:
Uri uri = Uri.fromParts("package", getPackageName(), null);
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
startActivity(intent);
Have user block app's notifications
Note this also blocks your app's toasts.
Android 8.0 - 8.1
It's not worth blocking the notification on Android O because the OS will just replace it with a "running in the background" or "using battery" notification.
Android 9+
Use a Notification Channel to block the service notification without affecting your other notifications.
Assign service notification to notification channel
Send user to notification channel's settings
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
.putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId());
startActivity(intent);
Have user block channel's notifications
Update: this no longer works in Android 4.3 and above
I set the icon parameter to the constructor for Notification to zero, and then passed the resulting notification to startForeground(). No errors in the log and no notification shows up. I don't know, though, whether the service was successfully foregrounded--is there any way to check?
Edited: Checked with dumpsys, and indeed the service is foregrounded on my 2.3 system. Haven't checked with other OS versions yet.
version 4.3(18) and above hiding service notification is not possible , but you could disable the icon , version 4.3(18) and below is possible to hide the notification
Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);
I've found on Android 8.0 it's still possible by not using a notification channel.
public class BootCompletedIntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent notificationIntent = new Intent(context, BluetoothService.class);
context.startForegroundService(notificationIntent);
} else {
//...
}
}
}
}
And in BluetoothService.class:
#Override
public void onCreate(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent notificationIntent = new Intent(this, BluetoothService.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(this)
.setContentTitle("Title")
.setContentText("App is running")
.setSmallIcon(R.drawable.notif)
.setContentIntent(pendingIntent)
.setTicker("Title")
.setPriority(Notification.PRIORITY_DEFAULT)
.build();
startForeground(15, notification);
}
}
A persistent notification is not shown, however you will see the Android 'x apps are running in the background' notification.
Its a quite troublesome for developer's sometime client did not want permanent notification for foreground service. I have created a Fake notification to start the service after that I cancel that by notificationManager.cancel(1);
final String NOTIFICATION_CHANNEL_ID = "com.exmaple.project";
final String channelName = "Notification";
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public void onCreate() {
super.onCreate();
stopForeground(true);
Intent stopSelf = new Intent(this, Notification_Service.class);
stopSelf.setAction("ACTION_STOP_SERVICE");
PendingIntent pStopSelf = PendingIntent
.getService(this, 0, stopSelf
, PendingIntent.FLAG_CANCEL_CURRENT);
Notification notification;
NotificationCompat.Action action =
new NotificationCompat.Action.Builder(
0, "Close", pStopSelf
).build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Notification One", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(serviceChannel);
notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentText("Welcome to App.")
.setPriority(Notification.PRIORITY_MIN)
.addAction(action)
.build();
} else {
notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("App")
.setContentText("Welcome to App.")
.setPriority(Notification.PRIORITY_MIN)
.addAction(action)
.build();
}
NotificationManager notificationManager =
(NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
notificationManager.notify(1, notification);
startForeground(1, notification);
notificationManager.cancel(1);
}
Sometime the permanent notification does not remove by notificationManager.cancel(1); for that I have add fake close action button.
Action button result:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if ("ACTION_STOP_SERVICE".equals(intent.getAction())) {
stopSelf();
}
return START_STICKY;
}
Start Service:
if (!isMyServiceRunning()) {
Intent serviceIntent = new Intent(this, Notification_Service.class);
ContextCompat.startForegroundService(this, serviceIntent);
}
Check if the service is already running.
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (Notification_Service.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Even though it's not the direct question, many of you searching for this can easily solve your challenges of creating a persistent, long running task by using a WorkManager
As of 2022 it's Google's recommended API for running background tasks of all types (One-time, Periodic, Expedited, Foreground, etc).
The most suitable solution is to work with Notification Channel.
All you need to do is removing notificationManager.createNotificationChannel(channel) from your class.
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channel = NotificationChannel(
notificationChannelId,
"Endless Service notifications channel",
NotificationManager.IMPORTANCE_HIGH
).let {
it.description = "Endless Service channel"
it.enableLights(true)
it.lightColor = Color.RED
it.enableVibration(true)
it.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
it
}
notificationManager.createNotificationChannel(channel)
OR
by simply using notificationManager.deleteNotificationChannel("channel_id")
Although, removing a notification that used by a Foreground service is not recommended.
Update: this no longer works in Android 7.1 and above
Here is a way to make your app 's oom_adj to 1 (Tested in ANDROID 6.0 SDK emulator). Add a temporary service, In your main service call startForgroundService(NOTIFICATION_ID, notificion). And then start the temporary service call startForgroundService(NOTIFICATION_ID, notificion) with same notification id again, after a while in the temporary service call stopForgroundService(true) to dismiss the onging ontification.
You can also declare your application as persistent.
<application
android:icon="#drawable/icon"
android:label="#string/app_name"
android:theme="#style/Theme"
*android:persistent="true"* >
</application>
This essentially sets your app at a higher memory priority, decreasing the probability of it being killed.

While adding startForeground() to a service two notifications are created instead of one

So I've created a service and it works great. All it does is just count. I have a custom class that handles the hassle of using notifications. This class has a getNotification which, obviously, returns the notification it uses. Everything worked great but I've got to make my service to run on foreground (it syncs some important data to the app which must not be interrupted until it finishes) Right now when I'm adding startForeground I add my notification and an id which leaves it like this.
startForeground(1337, notification);
Problem I have is that the first notify() I do is, for some reason, independent from the others notifications. So when I make this run it creates two notifications. The first one is stuck on the first update which has a title called "Zilean" and it's content says "Counting". The other one updates perfectly. I've notice that if startForeground is ran with an id of 0 (startForeground(0,notification)) then this problem gets fixed, but if I kill the activity the notification dies. Doesn't happen when the id <> 0.
I had this problem for too long so I'm using a dummy service which only counts. I want to know that if the activity dies due to the user or just because android decided to delete it's memories, then the service will keep going.
// onStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// NOTIFICATION SECTION
NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
this);
notificationManagerClass = new SBNNotification(mNotifyManager,
mBuilder, true, 1, false, "Zilean",
"Initiating Counter", false);
notificationManagerClass.notificate();
final Notification notification = notificationManagerClass.getNotification();
notification.flags = flags;
notification.defaults = Notification.DEFAULT_LIGHTS;
startForeground(1337, notification);
new Timer().scheduleAtFixedRate(new TimerTask () {
int i=0;
#Override
public void run() {
notificationManagerClass.setContentTitle("Contando");
notificationManagerClass.setContentText(String.valueOf(i));
notificationManagerClass.notificate();
i++;
Log.e("HOLAAA", String.valueOf(i));
}}, 0, 1000);
return START_REDELIVER_INTENT;
}
So.. what I'm missing?
Solved, my error was that the notification id wasnt the same as the startForeground id I was passing (1337)
Edit: It's important to note that the notification id and the service id must not be 0, else it'll blend with the main activity thread

How to startForeground() without showing notification?

I want to create a service and make it run in the foreground.
Most example codes have notifications on it. But I don't want to show any notification. Is that possible?
Can you give me some examples? Are there any alternatives?
My app service is doing mediaplayer. How to make system not kill my service except the app kill it itself (like pausing or stopping the music by button).
As a security feature of the Android platform, you cannot, under any circumstance, have a foregrounded service without also having a notification. This is because a foregrounded service consumes a heavier amount of resources and is subject to different scheduling constraints (i.e., it doesn't get killed as quickly) than background services, and the user needs to know what's possibly eating their battery. So, don't do this.
However, it is possible to have a "fake" notification, i.e., you can make a transparent notification icon (iirc). This is extremely disingenuous to your users, and you have no reason to do it, other than killing their battery and thus creating malware.
Update: This was "fixed" on Android 7.1.
https://code.google.com/p/android/issues/detail?id=213309
Since the 4.3 update, it's basically impossible to start a service with startForeground() without showing a notification.
You can, however, hide the icon using official APIs... no need for a transparent icon:
(Use NotificationCompat to support older versions)
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MIN);
I've made peace with the fact the notification itself still needs to be there but for who ever who still wants to hide it, I may have found a workaround for that as well:
Start a fake service with startForeground() with the notification and everything.
Start the real service you want to run, also with startForeground() (same notification ID)
Stop the first (fake) service (you can call stopSelf() and in onDestroy call stopForeground(true)).
Voilà! No notification at all and your second service keeps running.
This no longer works as of Android 7.1 and it may violate Google Play's developer policies.
Instead, have the user block the service notification.
Here's my implementation of the technique in the answer by Lior Iluz.
Code
ForegroundService.java
public class ForegroundService extends Service {
static ForegroundService instance;
#Override
public void onCreate() {
super.onCreate();
instance = this;
if (startService(new Intent(this, ForegroundEnablingService.class)) == null)
throw new RuntimeException("Couldn't find " + ForegroundEnablingService.class.getSimpleName());
}
#Override
public void onDestroy() {
super.onDestroy();
instance = null;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
ForegroundEnablingService.java
public class ForegroundEnablingService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ForegroundService.instance == null)
throw new RuntimeException(ForegroundService.class.getSimpleName() + " not running");
//Set both services to foreground using the same notification id, resulting in just one notification
startForeground(ForegroundService.instance);
startForeground(this);
//Cancel this service's notification, resulting in zero notifications
stopForeground(true);
//Stop this service so we don't waste RAM.
//Must only be called *after* doing the work or the notification won't be hidden.
stopSelf();
return START_NOT_STICKY;
}
private static final int NOTIFICATION_ID = 10;
private static void startForeground(Service service) {
Notification notification = new Notification.Builder(service).getNotification();
service.startForeground(NOTIFICATION_ID, notification);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
AndroidManifest.xml
<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />
Compatibility
Tested and working on:
Official Emulator
4.0.2
4.1.2
4.2.2
4.3.1
4.4.2
5.0.2
5.1.1
6.0
7.0
Sony Xperia M
4.1.2
4.3
Samsung Galaxy ?
4.4.2
5.X
Genymotion
5.0
6.0
CyanogenMod
5.1.1
No longer working as of Android 7.1.
Warning: although this answer appears to work, it in fact silently prevents your service from becoming a foreground service.
Original answer:
Just set your notification's ID to zero:
// field for notification ID
private static final int NOTIF_ID = 0;
...
startForeground(NOTIF_ID, mBuilder.build());
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.cancel(NOTIF_ID);
...
A benefit you can get is, a Service will be able to runs on high priority without destroyed by Android system, unless on high memory pressure.
To make it work with Pre-Honeycomb and Android 4.4 and higher, make sure that you use NotificationCompat.Builder which provided by Support Library v7, instead of Notification.Builder.
EDIT
This code will not work anymore due to security reasons in newer api level
NotificationId cannot be set to "0" (which will cause the app to crash)
startForeground(1, notification)
This is the perfect way to show notification (recommended method)
But if you need it reagrdless of the recommended method then try removing the "notificationManager.createNotificationChannel("channel_id")" from your code.
OR
USE notificationManager.removeNotificationChannel(channel)
You can use this (as suggested by #Kristopher Micinski):
Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 42, note );
UPDATE:
Please note that this is not allowed anymore with Android KitKat+ releases. And keep in mind that this is more or less violating the design principle in Android that makes background operations visible to users as mentioned by #Kristopher Micinski
You can hide notification on Android 9+ by using custom layout with layout_height = "0dp"
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.custom_notif);
builder.setContent(remoteViews);
builder.setPriority(NotificationCompat.PRIORITY_LOW);
builder.setVisibility(Notification.VISIBILITY_SECRET);
custom_notif.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp">
</LinearLayout>
Tested on Pixel 1, android 9.
This solution doesn't work on Android 8 or less
Update: this no longer works in Android 4.3 and above
There is one workaround.
Try creating notification without setting icon, and the notification would not show. Don't know how it works, but it does :)
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Title")
.setTicker("Title")
.setContentText("App running")
//.setSmallIcon(R.drawable.picture)
.build();
startForeground(101, notification);
Block the foreground service notification
Most answers here either don't work, break the foreground service, or violate Google Play policies.
The only way to reliably and safely hide the notification is to have the user block it.
Android 4.1 - 7.1
The only way is to block all notifications from your app:
Send user to app's details screen:
Uri uri = Uri.fromParts("package", getPackageName(), null);
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
startActivity(intent);
Have user block app's notifications
Note this also blocks your app's toasts.
Android 8.0 - 8.1
It's not worth blocking the notification on Android O because the OS will just replace it with a "running in the background" or "using battery" notification.
Android 9+
Use a Notification Channel to block the service notification without affecting your other notifications.
Assign service notification to notification channel
Send user to notification channel's settings
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
.putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId());
startActivity(intent);
Have user block channel's notifications
Update: this no longer works in Android 4.3 and above
I set the icon parameter to the constructor for Notification to zero, and then passed the resulting notification to startForeground(). No errors in the log and no notification shows up. I don't know, though, whether the service was successfully foregrounded--is there any way to check?
Edited: Checked with dumpsys, and indeed the service is foregrounded on my 2.3 system. Haven't checked with other OS versions yet.
version 4.3(18) and above hiding service notification is not possible , but you could disable the icon , version 4.3(18) and below is possible to hide the notification
Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);
I've found on Android 8.0 it's still possible by not using a notification channel.
public class BootCompletedIntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent notificationIntent = new Intent(context, BluetoothService.class);
context.startForegroundService(notificationIntent);
} else {
//...
}
}
}
}
And in BluetoothService.class:
#Override
public void onCreate(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent notificationIntent = new Intent(this, BluetoothService.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(this)
.setContentTitle("Title")
.setContentText("App is running")
.setSmallIcon(R.drawable.notif)
.setContentIntent(pendingIntent)
.setTicker("Title")
.setPriority(Notification.PRIORITY_DEFAULT)
.build();
startForeground(15, notification);
}
}
A persistent notification is not shown, however you will see the Android 'x apps are running in the background' notification.
Its a quite troublesome for developer's sometime client did not want permanent notification for foreground service. I have created a Fake notification to start the service after that I cancel that by notificationManager.cancel(1);
final String NOTIFICATION_CHANNEL_ID = "com.exmaple.project";
final String channelName = "Notification";
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public void onCreate() {
super.onCreate();
stopForeground(true);
Intent stopSelf = new Intent(this, Notification_Service.class);
stopSelf.setAction("ACTION_STOP_SERVICE");
PendingIntent pStopSelf = PendingIntent
.getService(this, 0, stopSelf
, PendingIntent.FLAG_CANCEL_CURRENT);
Notification notification;
NotificationCompat.Action action =
new NotificationCompat.Action.Builder(
0, "Close", pStopSelf
).build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Notification One", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(serviceChannel);
notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentText("Welcome to App.")
.setPriority(Notification.PRIORITY_MIN)
.addAction(action)
.build();
} else {
notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("App")
.setContentText("Welcome to App.")
.setPriority(Notification.PRIORITY_MIN)
.addAction(action)
.build();
}
NotificationManager notificationManager =
(NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
notificationManager.notify(1, notification);
startForeground(1, notification);
notificationManager.cancel(1);
}
Sometime the permanent notification does not remove by notificationManager.cancel(1); for that I have add fake close action button.
Action button result:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if ("ACTION_STOP_SERVICE".equals(intent.getAction())) {
stopSelf();
}
return START_STICKY;
}
Start Service:
if (!isMyServiceRunning()) {
Intent serviceIntent = new Intent(this, Notification_Service.class);
ContextCompat.startForegroundService(this, serviceIntent);
}
Check if the service is already running.
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (Notification_Service.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Even though it's not the direct question, many of you searching for this can easily solve your challenges of creating a persistent, long running task by using a WorkManager
As of 2022 it's Google's recommended API for running background tasks of all types (One-time, Periodic, Expedited, Foreground, etc).
The most suitable solution is to work with Notification Channel.
All you need to do is removing notificationManager.createNotificationChannel(channel) from your class.
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channel = NotificationChannel(
notificationChannelId,
"Endless Service notifications channel",
NotificationManager.IMPORTANCE_HIGH
).let {
it.description = "Endless Service channel"
it.enableLights(true)
it.lightColor = Color.RED
it.enableVibration(true)
it.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
it
}
notificationManager.createNotificationChannel(channel)
OR
by simply using notificationManager.deleteNotificationChannel("channel_id")
Although, removing a notification that used by a Foreground service is not recommended.
Update: this no longer works in Android 7.1 and above
Here is a way to make your app 's oom_adj to 1 (Tested in ANDROID 6.0 SDK emulator). Add a temporary service, In your main service call startForgroundService(NOTIFICATION_ID, notificion). And then start the temporary service call startForgroundService(NOTIFICATION_ID, notificion) with same notification id again, after a while in the temporary service call stopForgroundService(true) to dismiss the onging ontification.
You can also declare your application as persistent.
<application
android:icon="#drawable/icon"
android:label="#string/app_name"
android:theme="#style/Theme"
*android:persistent="true"* >
</application>
This essentially sets your app at a higher memory priority, decreasing the probability of it being killed.

Categories

Resources