The question is already old, but since there is still no solution with code, I simply share my code as an example for solving the problem:
You cannot directly stop the service from a Notification. You can
start the service, using an Intent that has an action string or extra
or something that the service sees in onStartCommand() and triggers it
to call stopSelf().
That's the right solution so let's jump in code (this code is all in your ExampleService class):
@RequiresApi(Build.VERSION_CODES.O)
private void startForegroundService() {
// create PendingIntend to open MainActivity (this is when the notification gets clicked) //
Intent tabIntent = new Intent(this, MainActivity.class);
tabIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent tabPendingIntent = PendingIntent.getActivity(this, 0, tabIntent, 0);
// create PendingIntend to open ExampleService (this is when the notification BUTTON gets clicked) //
Intent closeIntent = new Intent(this, ExampleService.class);
closeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
closeIntent.putExtra("destroyCode", 666); // this is the important line //
PendingIntent closePendingIntent = PendingIntent.getService(this, 0, closeIntent, 0);
createNotificationChannel(); // this is only the default code to create notification channel. I just outsourced? it //
Now the Intent has additional data (the "destroy code" -> 666). Notice that we have created 2 pendingIntents: closePendingIntent (stop Service) and tabPendingIntent (start Activity)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// get extras to know if Intent has destroyCode (666)
Bundle extras = intent.getExtras();
if (extras == null) {
// extras is null which means there is no destroyCode (666)
exampleMethod();
} else {
// Intent has destroyCode (666) -> Intent comes from notification -> stop the service and close notification
stopSelf();
}
return START_STICKY;
}
Now we have the code to check if there is a destroyCode or not. The last step is to create a notification with a button:
// set attributes for notification //
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channelID_2");
Notification notification = builder.setOngoing(true)
.setSmallIcon(R.drawable.example)
.setContentTitle(getText(R.string.notificationTitle))
.setContentText(getText(R.string.notificationText))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(tabPendingIntent) //this is when notification is clicked which only opens ExampleActivity
.addAction(R.drawable.example, getString(R.string.notificationButtonText), closePendingIntent) // here is our closePendingIntent with the destroyCode .addAction is "the onClickListener for the notification button"//
.build();
startForeground(2, notification);
In onCreate you start your service
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
startForegroundService();
else
startForeground(1, new Notification());
// Toast Message that service has started
Toast.makeText(this, R.string.serviceStarted, Toast.LENGTH_SHORT).show();
That's it