Search code examples
androidkotlinandroid-notifications

How to invoke a method on a foreground service by clicking on a notification action?


I have a foreground service. It does some async work in the background and periodically issues a notification asking the user if the work should be stopped.

The notification has a button "Yes, please" and when clicked it must invoke stopAction method.

The code below is where I'm stuck. I'm maybe way off and this can't be done. Any advice?

MainService.kt

...

override fun onCreate() {
  subscribeToStopActionRequest()
}

private fun subscribeToStopActionRequest () {
    var eventReceiverHelper = EventReceiverHelper { stopAction() }
    val filter = IntentFilter().apply {
        addAction("${packageName}.stop_action_request")
    }
    registerReceiver(eventReceiverHelper, filter)
}

private fun stopAction () {
  ...
}

private fun showNotification () {
    val intent = Intent(this, EventService::class.java)
    val pendingIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)

    var notification = NotificationCompat.Builder(this, state.notificationChannelId)
        .setContentTitle("Want to stop?")
        .addAction(R.drawable.stop_icon, "Yes, please", pendingIntent)
        .build()

    with(NotificationManagerCompat.from(this)) {
        notify(1, notification)
    }
}

Event receiver helper

class EventReceiverHelper(val cb: () -> Unit): BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        cb()
    }
}

Solution

  • Define a constant:

    private const val EXTRA_STOP = "stop";
    

    Then create an intent for your service and put an extra flag:

    val intent = Intent(context, YourService::class.java);
    intent.putExtra(EXTRA_STOP, true);
    

    Now you can create a pending intent as your handler:

    val pendingIntent: PendingIntent = PendingIntent.getService(context, YOUR_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE)
    

    This pending intent will trigger the onStartCommand method on your service, where you can check whether the stop flag was set.

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent != null && intent.getBooleanExtra(EXTRA_STOP, false)) {
            stopAction()
        }
        ...
    }