diff --git a/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/IServiceNotification.kt b/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/IServiceNotification.kt index 6ea49891d..654366fa1 100644 --- a/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/IServiceNotification.kt +++ b/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/IServiceNotification.kt @@ -24,7 +24,7 @@ interface IServiceNotification { * Create and return a notification (creating the channel if applicable) that can be used in * {@code Service#startForeground()} */ - fun buildNotification(context: Context): Notification + fun buildNotification(context: Context, pendingShutdownIntent: PendingIntent): Notification /** * Return the notification ID to use diff --git a/src/xrt/ipc/android/build.gradle b/src/xrt/ipc/android/build.gradle index 5ebf0c13e..176150e73 100644 --- a/src/xrt/ipc/android/build.gradle +++ b/src/xrt/ipc/android/build.gradle @@ -21,10 +21,13 @@ android { // Single point of truth for these names. def serviceAction = "org.freedesktop.monado.ipc.CONNECT" + def shutdownAction = "org.freedesktop.monado.ipc.SHUTDOWN" manifestPlaceholders = [ serviceActionName : serviceAction, + shutdownActionName: shutdownAction ] buildConfigField("String", "SERVICE_ACTION", "\"${serviceAction}\"") + buildConfigField("String", "SHUTDOWN_ACTION", "\"${shutdownAction}\"") buildConfigField("Long", "WATCHDOG_TIMEOUT_MILLISECONDS", "1500L") } diff --git a/src/xrt/ipc/android/src/main/AndroidManifest.xml b/src/xrt/ipc/android/src/main/AndroidManifest.xml index 6bb3b14f0..4f8c19ed8 100644 --- a/src/xrt/ipc/android/src/main/AndroidManifest.xml +++ b/src/xrt/ipc/android/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ + diff --git a/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/MonadoService.kt b/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/MonadoService.kt index 2d87bdd22..2bff4a341 100644 --- a/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/MonadoService.kt +++ b/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/MonadoService.kt @@ -8,6 +8,7 @@ */ package org.freedesktop.monado.ipc +import android.app.PendingIntent import android.app.Service import android.content.Intent import android.content.pm.ServiceInfo @@ -48,7 +49,9 @@ class MonadoService : Service(), Watchdog.ShutdownListener { watchdog.startMonitor() // start the service so it could be foregrounded - startService(Intent(this, javaClass)) + val intent = Intent(this, javaClass) + intent.action = BuildConfig.SERVICE_ACTION + startService(intent) } override fun onDestroy() { @@ -62,7 +65,13 @@ class MonadoService : Service(), Watchdog.ShutdownListener { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.d(TAG, "onStartCommand") - handleStart() + // if this isn't a restart + if (intent != null) { + when (intent.action) { + BuildConfig.SERVICE_ACTION -> handleStart() + BuildConfig.SHUTDOWN_ACTION -> handleShutdown() + } + } return START_STICKY } @@ -73,7 +82,14 @@ class MonadoService : Service(), Watchdog.ShutdownListener { } private fun handleStart() { - val notification = serviceNotification.buildNotification(this) + val pendingShutdownIntent = PendingIntent.getForegroundService( + this, + 0, + Intent(BuildConfig.SHUTDOWN_ACTION).setPackage(packageName), + 0 + ) + + val notification = serviceNotification.buildNotification(this, pendingShutdownIntent) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { startForeground( @@ -106,6 +122,10 @@ class MonadoService : Service(), Watchdog.ShutdownListener { override fun onShutdown() { Log.d(TAG, "onShutdown") + handleShutdown() + } + + private fun handleShutdown() { stopForeground(true) stopSelf() } diff --git a/src/xrt/targets/android_common/src/main/java/org/freedesktop/monado/android_common/ServiceNotificationImpl.kt b/src/xrt/targets/android_common/src/main/java/org/freedesktop/monado/android_common/ServiceNotificationImpl.kt index bf2692015..61c224482 100644 --- a/src/xrt/targets/android_common/src/main/java/org/freedesktop/monado/android_common/ServiceNotificationImpl.kt +++ b/src/xrt/targets/android_common/src/main/java/org/freedesktop/monado/android_common/ServiceNotificationImpl.kt @@ -70,9 +70,14 @@ class ServiceNotificationImpl @Inject constructor() : IServiceNotification { * Create and return a notification (creating the channel if applicable) that can be used in * {@code Service#startForeground()} */ - override fun buildNotification(context: Context): Notification { + override fun buildNotification(context: Context, pendingShutdownIntent: PendingIntent): Notification { createChannel(context) + val action = Notification.Action.Builder( + Icon.createWithResource(context, R.drawable.ic_feathericons_x), + context.getString(R.string.notifExitRuntime), + pendingShutdownIntent) + .build() // Make a notification for our foreground service // When selected it will open the "About" activity val builder = makeNotificationBuilder(context) @@ -82,6 +87,7 @@ class ServiceNotificationImpl @Inject constructor() : IServiceNotification { R.string.notif_text, nameAndLogoProvider.getLocalizedRuntimeName())) .setShowWhen(false) + .setActions(action) .setContentIntent(uiProvider.makeAboutActivityPendingIntent()) // Notification icon is optional