From ba92fbd73745df8518a38eb132df3c197aa0a7e1 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Sat, 13 Jun 2026 08:38:47 +0700 Subject: [PATCH 1/2] ensure start foreground service --- .../su/reya/coop/NostrForegroundService.kt | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt index 0efbd5b..add9fb2 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt @@ -35,20 +35,17 @@ class NostrForegroundService : Service() { override fun onCreate() { super.onCreate() - createNotificationChannel() - val notification = createNotification() - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) - } else { - startForeground(1, notification) - } + startAsForeground() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + startAsForeground() + + // Check if the service is already running if (notificationJob?.isActive == true) return START_STICKY + // Start the Nostr client notificationJob = serviceScope.launch { try { Log.d("Coop", "Starting Nostr in background") @@ -93,6 +90,16 @@ class NostrForegroundService : Service() { return START_STICKY } + private fun startAsForeground() { + val notification = createNotification() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) + } else { + startForeground(1, notification) + } + } + + private fun createNotificationChannel() { val manager = getSystemService(NotificationManager::class.java) -- 2.49.1 From 1cd4eb330b513fce3ecd037c8043fb87ffbbc540 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Sat, 13 Jun 2026 09:04:58 +0700 Subject: [PATCH 2/2] allow user stop service --- .../src/androidMain/AndroidManifest.xml | 4 +-- .../su/reya/coop/NostrForegroundService.kt | 27 +++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index b396cd3..58fb629 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -8,7 +8,7 @@ - + @@ -59,7 +59,7 @@ android:name=".NostrForegroundService" android:enabled="true" android:exported="false" - android:foregroundServiceType="dataSync" /> + android:foregroundServiceType="remoteMessaging" /> \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt index add9fb2..abb13ad 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt @@ -22,6 +22,8 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import java.io.File +private const val GROUP_KEY_MESSAGES = "su.reya.coop.MESSAGES" + class NostrForegroundService : Service() { private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val nostr by lazy { NostrManager.instance } @@ -29,10 +31,6 @@ class NostrForegroundService : Service() { override fun onBind(intent: Intent?): IBinder? = null - private fun isUserInApp(): Boolean { - return ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) - } - override fun onCreate() { super.onCreate() createNotificationChannel() @@ -40,6 +38,13 @@ class NostrForegroundService : Service() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (intent?.action == "STOP_SERVICE") { + stopForeground(STOP_FOREGROUND_REMOVE) + stopSelf() + return START_NOT_STICKY + } + + // Start the nostr service in the foreground startAsForeground() // Check if the service is already running @@ -90,15 +95,25 @@ class NostrForegroundService : Service() { return START_STICKY } + private fun isUserInApp(): Boolean { + return ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) + } + private fun startAsForeground() { val notification = createNotification() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) + startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING) } else { startForeground(1, notification) } } + private fun getStopServicePendingIntent(): PendingIntent { + val intent = Intent(this, NostrForegroundService::class.java).apply { + action = "STOP_SERVICE" + } + return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) + } private fun createNotificationChannel() { val manager = getSystemService(NotificationManager::class.java) @@ -122,10 +137,12 @@ class NostrForegroundService : Service() { private fun createNotification(content: String? = null): Notification { val builder = NotificationCompat.Builder(this, "nostr_service") + .setGroup(GROUP_KEY_MESSAGES) .setSmallIcon(R.drawable.ic_notification) .setOngoing(true) .setPriority(NotificationCompat.PRIORITY_MIN) .setCategory(Notification.CATEGORY_SERVICE) + .addAction(R.drawable.ic_notification, "Stop", getStopServicePendingIntent()) if (content != null) { builder.setContentTitle("Coop") -- 2.49.1