0

I have an Android app developed in Kotlin and it has a button to share a file. If I close the app before this process is completed, the file isn't shared and when I re-open the app, I have to start all the process again: select the file, click the button, and wait. I want it to continue working even if I close the app, so I added this class to my project:

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_MUTABLE
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat

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 {

        //do heavy work on a background thread
        val input = intent?.getStringExtra("inputExtra")
        createNotificationChannel()
        val notificationIntent = Intent(this, MainActivity::class.java)

        val pendingIntent: PendingIntent =
            Intent(this, MainActivity::class.java).let { notificationIntent ->
                PendingIntent.getActivity(this, 0, notificationIntent,
                PendingIntent.FLAG_IMMUTABLE)
        }
        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Foreground Service Kotlin Example")
            .setContentText(input)
            //.setSmallIcon(R.drawable.ic_notification)
            .setContentIntent(pendingIntent)
            .build()

        startForeground(1, notification)
        //stopSelf();
        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)
        }
    }
}

and this code into my MainActivy.kt:

buttonStart.setOnClickListener(View.OnClickListener {
        ForegroundService.startService(requireActivity(), "Foreground Service is running...")
    })

    buttonStop.setOnClickListener(View.OnClickListener {
        ForegroundService.stopService(requireActivity())
    })

When I run the app in an emulated phone on Android Studio (a Pixel 6 Pro API 30), I see there is a notification but when I run it on my phone, a Samsung Galaxy, I don't see any notification.

In both cases, if I press the button "Start Service", the button "Share file" and I close the app, the app doesn't continue working on the background. It resumes when I re-open it. Any idea on what I'm doing wrong?

2 Answers2

0

What you need is something called a Foreground Service, which will keep running even if the app terminates; you need to check this guide to convert your Service into a foreground service.

You need to make a permission in your manifest like that:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

    <application ...>
        ...
    </application>
</manifest>

and then you need to call it like that:

val intent = Intent(...) // Build the intent for the service
applicationContext.startForegroundService(intent)

and if you need to communicate with this service, you need to to create service Connection, check it here.

Hope this help!

Younes Charfaoui
  • 1,059
  • 3
  • 10
  • 20
0

You may also need to make use of another permission called REQUEST_IGNORE_BATTERY_OPTIMIZATIONS which shall produce a prompt like "Always run in Background" to user

Abdullah Sohail
  • 355
  • 4
  • 8
  • thank you! I tried it and it looks like I don't need to include also the FOREGROUND_SERVICE, just adding the REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission in the Manifest makes the app work in the background, without the need to call it in the code. is that right? – Harmony Valley Apr 13 '23 at 07:22
  • You need Foreground service to display notifications, the request_ignore_battery_optimization was to make sure the os doesnt kill the app during idle or doze mode which i suspected was the problem you were facing, so you need it – Abdullah Sohail Apr 13 '23 at 10:21
  • Also make sure you dont violate google policies while using this permission, https://developer.android.com/training/monitoring-device-state/doze-standby#:~:text=Note%3A%20Google%20Play%20policies%20prohibit,list%20by%20calling%20isIgnoringBatteryOptimizations()%20. – Abdullah Sohail Apr 13 '23 at 10:22
  • that link describes policies for messaging apps. my app doesn't need any internet connection, it is a video processor, so I guess the policies apply. – Harmony Valley Apr 14 '23 at 06:45