Our app has some services and intent-services that starts running when the user starts the app. These components needs to be started to continue the process running even if the user minimize the app.
On Android Oreo our app started crashing due the missing initialization of these services using startForegroundService(:intent) and startForeground(:id, :notification). We've fixed it attaching these notifications as requerested but we discovered a strange behaviour. These notifications appear even if the app is in foreground, since it doesn't make sense because our services are really short-running almost all the time, but it needs to be guaranteed to run 100% of the time. That's why the process runs on a service and not in the activity's context. I would like to show these notifications only when the user minimize the app and the service is up.
Is there any way to do it?
I can't change this message, very annoying!
Here is some code used to create the channel:
const val SERVICE_CHANNEL_ID = "com.myFunnyApp.notifications"
//.....
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationService = context.getSystemService(Context.NOTIFICATION_SERVICE)
(notificationService as? NotificationManager)?.let { notificationManager ->
if (notificationManager.getNotificationChannel(SERVICE_CHANNEL_ID) == null) {
val channel = NotificationChannel(
SERVICE_CHANNEL_ID,
context.resources.getString(R.string.test),
NotificationManager.IMPORTANCE_MIN)
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
notificationManager.createNotificationChannel(channel)
return
}
}
}
On the service, the code runs:
val notification = Notification.Builder(context, SERVICE_CHANNEL_ID)
.setSubText("subText") //Not working
.setContentText("contentText") //Not working
.setSettingsText("settingsText") //Not working
.setContentTitle("title") //Not working
.build()
startForeground(1, notification)
Currently I am testing on the emulator, I don't have any Android Oreo device.
Thanks!
EDIT 1: The text only changes if you set the small icon.
In Android 8.0(Android Oreo) and onwards, we have to bring the service to the foreground before we trigger a Notification.
After which once you have triggered your Notification - you will have to stop the service running in the Foreground for which execute the following code after -
val notification = Notification.Builder(context, SERVICE_CHANNEL_ID)
.setSubText("subText") //Not working
.setContentText("contentText") //Not working
.setSettingsText("settingsText") //Not working
.setContentTitle("title") //Not working
.build()
startForeground(1, notification)
stopForeground(true);
stopSelf();
Related
I have a foreground service that shows an ongoing notification while running.
Now, it's a streaming app and I want the user to be notified when the stream gets broken (e.g. internet connection dropped). I can't use the main app because the stream can be going while other apps are active. So I need to send a notification to the user from the Foreground Service I use for streaming. The problem is, the notification is not being displayed.
Here's the code I'm currently using:
// registering notification channels
private fun createNotificationChannels() {
val serviceChannel = NotificationChannel(
NOTIFICATION_CHANNEL_ID_SERVICE,
NOTIFICATION_CHANNEL_NAME_SERVICE,
NotificationManager.IMPORTANCE_DEFAULT
)
val appChannel = NotificationChannel(
NOTIFICATION_CHANNEL_ID_APP,
NOTIFICATION_CHANNEL_NAME_APP,
NotificationManager.IMPORTANCE_HIGH
).apply {
enableVibration(true)
}
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannels(mutableListOf(serviceChannel, appChannel))
}
// starting the service with a required notification
startForeground(
nextInt(100000),
NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID_SERVICE)
.setSmallIcon(R.drawable.recording_notification)
.setContentText("Stream is in progress...")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build(),
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
)
// letting the user know that stream crashed
private fun sendDisconnectNotification() {
val intent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val builder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID_APP)
.setSmallIcon(R.drawable.disconnected_notification)
.setContentTitle("The stream stopped unexpectedly!")
.setContentText("Please check your internet connection.")
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.notify(nextInt(100000), builder.build())
}
I know that sendDisconnectNotification() is being called (put logs there) but the notification never appears.
I was changing so many things that it's hard to specify every piece of code that I tried. But some of the important things I tried were changing the priorities for channels/notifications and sending the notifications in the same/different channels. I also uninstall the app and reboot the phone after every change to make sure the notification settings are applied.
Nothing has been working so far and it got me thinking it's impossible to do. I think that the foreground service only allows one notification to be displayed (the main ongoing one).
Can someone confirm this or give some advice on how to make it work?
I can provide some more code samples if needed.
OK, I’m feeling kinda dumb but it turned out “Do Not Disturb” was turned on on the device. That was the reason the notifications were not visible. Writing this as an answer in case someone like me forgets to turn off DND and finds this SO question.
I'm working on an android application where i need to show a time-sensitive notification in a specific situation.
Normaly our app is running in the background and when the phone is in use the heads up notification is displayed correctly and when the user interacts with it, the correct activity is started.
When the phone is not in use (e.g. locked) the fullScreenIntent works and the correct activity is started.
In the case the user is working with our app at the moment the notification appears, i would like to lead directly to the activity i set as the fullScreenIntent. But at the moment also the head up notification is shown and the activity doesn't start automatically. Is there a way i can make my activity to be started when our app is currently being used by the user?
I build my notification like this and it is used for a foreground service.
val pendingCancelIntent = getCancelPendingIntent()
val pendingFinishIntent = getFinishPendingIntent()
val fullScreenPendingIntent = getFullScreenPendingIntent()
val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_icon)
.setContentTitle(getString(R.string.title))
.setContentText(getString(R.string.text))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setFullScreenIntent(fullScreenPendingIntent, true)
notificationBuilder.addAction(
R.drawable.ic_cancel,
getString(R.string.general_cancel),
pendingCancelIntent
)
notificationBuilder.addAction(
R.drawable.ic_finish,
getString(R.string.finish),
pendingFinishIntent
)
notificationBuilder.build()
I have an app that has a background service running that listens for events.
One of the events should unlock the phone and bring the app to the foreground.
What approaches here are possible?
I was thinking, would it be possible to send a local notification that is actually high priority so it opens the app automatically?
Currently I try to open apps activity this way:
private fun getIntent(pin: String): Intent = Intent(context, XActivity::class.java).apply {
putExtra(XActivity.EXTRA_SMTH, x)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
private fun showActivity(x: String) {
val intent = getIntent(x)
context.startActivity(intent)
}
This code piece works alright if the app is in the foreground, but does not if the app is in the background.
Any ideas/solutions are welcomed.
At first, if you listened for ACTION_SCREEN_ON or ACTION_SCREEN_ON, make sure to explicitly set your listeners ref.
Secondly, due to background restriction, you cannot start an Activity from background. You have to start a foreground service which you will start when the receiver receives the event. From that service, you can launch your activity with your desired intent.
Foreground service needs a notification. Inside your service, create a notification with your intent like following and call startForeground() with this notification. Also create and register NotificationChannel before if not already.
val fullScreenIntent = Intent(this, XActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val notificationBuilder =
NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("Launch Activity")
.setContentText("Tap to launch Activity")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_ALARM) // Set your desired category
// Use a full-screen intent only for the highest-priority alerts where you
// have an associated activity that you would like to launch after the user
// interacts with the notification. Also, if your app targets Android 10
// or higher, you need to request the USE_FULL_SCREEN_INTENT permission in
// order for the platform to invoke this notification.
.setFullScreenIntent(fullScreenPendingIntent, true)
val alarmNotification = notificationBuilder.build()
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.
As I correctly recall, with Android Oreo the system got more restrictive in terms of battery consumption, e.g., showing the user which app is currently running in the background draining the battery life.
This also shows in my app (scanning for BLE devices in the background, even if the app is not running in the foreground), stating
AppName is running in the background. Tap for Details on battery and
data usage
My question is, whether and how I would be able to exchange this text with a more user facing notification like
AppName is running in the background for reason XYZ
Even so with a notification with a specific icon, etc.
I already tried this by
Creating a custom notification channel in the application.
Starting the services with startForegroundService on Oreo devices.
From within the onCreate method of the service, I call
startForeground(1, NotificationCompat.Builder(applicationContext, "my_channel_id")
.setSmallIcon(R.drawable.some_icon)
.setContentTitle("Title")
.setContentText("AppName is running in the background for reason XYZ")
.build())
But all what happens is the same system-generated notification saying "AppName is running in the background".
Is it even possible to change this?
Thank you for sour suggestions in advance :)
In you service create the notification in onStartCommand() and set its priority. Don't forget to add android:enabled="true" in the manifest.
Service
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ANDROID_CHANNEL_ID,
ANDROID_CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, ANDROID_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("Test title")
.setContentText("Test content")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
startForeground(1, notification.build())
return Service.START_NOT_STICKY
}
AndroidManifest
<service android:name=".utils.AppService"
android:enabled="true"/>