I am writing an Android Wear app that uses a foreground service.
I have verified that the foreground service wokrs, and the code inside the service executes as needed (SensorEventListener) and I even use intents to display information in the UI.
However, the notification for the service is not posting in the list of notifications. I use a Galaxy Watch4 for testing.
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
broadcaster = LocalBroadcastManager.getInstance(this)
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, FLAG_IMMUTABLE)
val notificationChannel = NotificationChannel(foregroundChannelId, "Foreground Service", NotificationManager.IMPORTANCE_HIGH)
notificationChannel.lightColor = Color.BLUE
notificationChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(notificationChannel)
val notification = NotificationCompat.Builder(this, foregroundChannelId).setOngoing(true)
.setContentTitle("Monitoring sensors").setContentText("app_name is actively monitoring your biosensors.")
.setContentIntent(pendingIntent).setCategory(Notification.CATEGORY_SERVICE).setSmallIcon(R.mipmap.ic_launcher).build()
startForeground(1, notification)
prepareSensors()
return START_NOT_STICKY
}
There is no sticky notification in the notification panel.
I've tried removing the .setOngoing.
Maybe it was delayed in showing, as the operation was too quick ?
Try adding this:
builder.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
Related
Need to silence a notification that is shown by a foreground service. To silence the notification I have used many suggested solution from SO. But nothing seems to silence the annoying notification sound. It is silenced on SDK25. But on SDK 29 emulator it is not silenced. What can be the solution?
Code:
//service
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
createNotificationChannel()
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0, notificationIntent, 0
)
val notification: Notification =
NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("app_name")
.setContentText("Updating database.")
.setSmallIcon(R.drawable.logo)
.setContentIntent(pendingIntent)
.build()
startForeground(1, notification)
thread() {
Thread.sleep(3000)
stopSelf()
}
return START_NOT_STICKY
}
override fun onDestroy() {
super.onDestroy()
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = SettingsHelper.KEY_NOTIFICATION_channel_name_service
val description = SettingsHelper.KEY_NOTIFICATION_CH_ID_SERVICE
val channel = NotificationChannel(
SettingsHelper.KEY_NOTIFICATION_CH_ID,
name,
NotificationManager.IMPORTANCE_NONE
)
channel.description = description
channel.setSound(null, null)
val manager = getSystemService(
NotificationManager::class.java
)
manager.createNotificationChannel(channel)
}
}
Could you use setSilent() from NotificationCompat.Builder
If true, silences this instance of the notification, regardless of the sounds or vibrations set on the notification or notification channel. If false, then the normal sound and vibration logic applies. Defaults to false.
Note, that if you are changing notification settings, I recommend you to uninstall and re-install the app everytime since it might not be updated the settings you set in the new run.
I start a foreground service that downloads a file when users click on a button. What happens now is when I click again on the button, the thread inside service starts second time and I see in a notification that progress is updated for both threads (jumps presentae between threads).
How can I prevent starting the second thread in case the first one is running, how can I prevent startService() or onStartCommand() is being called if the service is still running?
class ForegroundService : Service() {
private val CHANNEL_ID = "ForegroundService Kotlin"
companion object {
fun startService(context: Context, message: String) {
val startIntent = Intent(context, ForegroundService::class.java)
startIntent.putExtra("inputExtra", message)
ContextCompat.startForegroundService(context, startIntent)
}
fun stopService(context: Context) {
val stopIntent = Intent(context, ForegroundService::class.java)
context.stopService(stopIntent)
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val input = intent?.getStringExtra("inputExtra")
createNotificationChannel()
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0, notificationIntent, 0
)
//start doing some work and update the notification
//when done calling stopSelf();
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service Kotlin Example")
.setContentText(input)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build()
startForeground(1, notification)
return START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel(
CHANNEL_ID, "Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(NotificationManager::class.java)
manager!!.createNotificationChannel(serviceChannel)
}
}
}
Calling
buttonGoNext.setOnClickListener {
val intent = Intent(this, DownloadActivity::class.java)
startActivity(intent)
}
In your startService function you want to check if this service is already running. If the service is already running, you could just early return. There is multiple SO threads already regarding the problem of how to find out if a service is already running.
To have a little more control over your background work, I would suggest you familiarize yourself with Android's WorkManager. You could create a unique OneTimeWorkRequest with a KEEP policy, that would solve the problem as well.
Also, you should seperate your responsibilities in your code a little better. Your ForegroundService class currently takes care of too many things at once. You create notification channels, show notifications and probably also want to take care of the behaviour you described. It is generally good practice to seperate those concerns and move them to different classes. This also makes it more testable.
I am starting a background service which receives data in the background, so for this, I have used android foreground service, the service works perfectly in some mobiles (MI A2 Stock Android), but in some mobiles when I remove the application from background tray the service gets destroyed.
class MyService : Service() {
private val CHANNEL_ID = "ForegroundService"
companion object {
fun stopService(context: Context) {
val stopIntent = Intent(context, MyService::class.java)
context.stopService(stopIntent)
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// I get some data from intent
// My code which runs in the background
createNotificationChannel()
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0, notificationIntent, 0
)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("App is syncing")
.setContentText("")
.setPriority(2)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setContentIntent(pendingIntent)
.build()
startForeground(190, notification)
return START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel(
CHANNEL_ID, "Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(NotificationManager::class.java)
manager!!.createNotificationChannel(serviceChannel)
}
}
}
This how I start the service
val serviceIntent = Intent(this, MyService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
So my question is how can I make my service running even when the APP is removed from the background tray.
Do these things
1) Restart the service when app is closed by overriding this method in
your service class , copy and paste this
override fun onTaskRemoved(rootIntent: Intent?) {
val restartServiceIntent = Intent(applicationContext, this.javaClass)
restartServiceIntent.setPackage(packageName)
val restartServicePendingIntent = PendingIntent.getService(
applicationContext,
1,
restartServiceIntent,
PendingIntent.FLAG_ONE_SHOT
)
val alarmService =
applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmService[AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000] =
restartServicePendingIntent
super.onTaskRemoved(rootIntent)
}
2) Change this START_NOT_STICKY to START_STICKY
3) Ask user to enable autorun permission from settings , this feature
is provided in custom os like mini devices, vivo,huawei and oppo etc.
4) and you forgot to restart the service on device boot up like you
need to use a broadcast receiver to restart service when the device
restarts
I'm trying to create a foreground service that check devices location every minute and I've managed to make it work. But I have some problems regarding the obligatory notification about the service being active.
I can't get the notification badge to stop showing and I can't make the notification minimized programatically.
Here are the relevant parts:
class LocationPollingService : Service() {
companion object {
const val NOTIF_ID = 2
const val NOTIF_CHANNEL_ID = "background_service_notification"
const val NOTIF_CHANNEL_NAME = "Background service"
}
#Nullable
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
createBackgroundNotifChannel()
startForeground()
requestLocation()
return super.onStartCommand(intent, flags, startId)
}
private fun startForeground() {
val notificationIntent = Intent(this, SplashActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this, 0,
notificationIntent, 0
)
val notification = NotificationCompat.Builder(
this,
NOTIF_CHANNEL_ID
)
.setOngoing(true)
.setSmallIcon(R.drawable.ic_app_icon)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.im_watching_you))
.setContentIntent(pendingIntent)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(NOTIF_ID, notification.build())
} else {
notification.priority = NotificationCompat.PRIORITY_MIN
startForeground(NOTIF_ID, notification.build())
}
}
private fun createBackgroundNotifChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelBackground = NotificationChannel(
NOTIF_CHANNEL_ID,
NOTIF_CHANNEL_NAME,
NotificationManager.IMPORTANCE_LOW
).apply {
description = getString(
R.string.notification_channel_background_description
)
enableLights(false)
enableVibration(false)
setShowBadge(false)
}
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channelBackground)
}
}
}
If I understand correctly, IMPORTANCE_LOW is supposed to be used for this kind of notification and it is supposed to make it minimized by default. The notification is never minimized nor can I minimize it by clicking. The only way I can make it minimized is by manually changing notification channel settings in device settings (setting Notification style to Silent and minimized, instead of just silent, which is the default). I tried setting the importance to IMPORTANCE_MIN and IMPORTANCE_NONE, but that didn't change anything.
As mentioned, the second problem is that the badge is shown (the notification counter displayed on app icon), despite of setShowBadge(false).
What am I missing?
You need to notify notification after build notification like below :
notificationManager.notify(NOTIF_ID , notification.build());
for hiding notification badge from statusbar
you should change notification channel priority:
val channelBackground = NotificationChannel(
NOTIF_CHANNEL_ID,
NOTIF_CHANNEL_NAME,
NotificationManager.IMPORTANCE_MIN
I have been trying to get the Service to run in the background for that I have implemented the foreground approach still its killing the app.
I need this service to return me the location of the user even if the application is killed.
MainActivity.kt
private fun kickStartService(){
var intent = Intent(this , LocationService::class.java)
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra("extra" , "extra")
ContextCompat.startForegroundService( this , intent)
// startService( intent)
}
LocationService.kt
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
myNotification(intent)
mGoogleApiClient = GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build()
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
if (!mGoogleApiClient!!.isConnected) {
mGoogleApiClient!!.connect()
}
return START_NOT_STICKY
}
fun myNotification( intent : Intent?){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var extra = intent?.getStringExtra("extra")
var pendingIntent: PendingIntent =
Intent(this, MainActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(this, 0, notificationIntent, 0)
}
var notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Notification Title")
.setContentText(extra)
.setSmallIcon(R.drawable.notification_icon_background)
.setContentIntent(pendingIntent)
.setTicker("Ticker Text")
.build()
startForeground(1, notification)
}
}
Well, My bad, I was using Xiaomi Mi Mix 2 and so it happens that Mi does not let you start a foreground service if you have auto start off from the app settings in app manager. Once I turned it on, my foreground service is working like a charms.