diff --git a/app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt b/app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt index 9b475b60..3af70da3 100644 --- a/app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt +++ b/app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt @@ -144,11 +144,13 @@ class MeshForegroundService : Service() { when (intent?.action) { ACTION_STOP -> { // Stop FGS and mesh cleanly + updateJob?.cancel() + updateJob = null try { com.bitchat.android.services.MessageRouter.tryGetInstance()?.stopOutboxScheduler() } catch (_: Exception) { } try { unifiedMeshService?.stopServices() ?: meshService?.stopServices() } catch (_: Exception) { } try { MeshServiceHolder.clear() } catch (_: Exception) { } try { stopForeground(true) } catch (_: Exception) { } - notificationManager.cancel(NOTIFICATION_ID) + clearMeshNotifications() isInForeground = false stopSelf() return START_NOT_STICKY @@ -158,7 +160,7 @@ class MeshForegroundService : Service() { updateJob?.cancel() updateJob = null try { stopForeground(true) } catch (_: Exception) { } - notificationManager.cancel(NOTIFICATION_ID) + clearMeshNotifications() isInForeground = false // Fully stop all background activity, stop Tor (without changing setting), then kill the app AppShutdownCoordinator.requestFullShutdownAndKill( @@ -224,7 +226,7 @@ class MeshForegroundService : Service() { private fun updateNotification(force: Boolean) { if (isShuttingDown) { - notificationManager.cancel(NOTIFICATION_ID) + clearMeshNotifications() return } val count = getUnifiedActivePeerCount() @@ -236,12 +238,17 @@ class MeshForegroundService : Service() { } else if (force) { // If disabled and forced, make sure to remove any prior foreground state try { stopForeground(false) } catch (_: Exception) { } - notificationManager.cancel(NOTIFICATION_ID) + clearMeshNotifications() isInForeground = false lastNotifiedPeerCount = null } } + private fun clearMeshNotifications() { + notificationManager.cancel(NOTIFICATION_ID) + peerAvailabilityNotifier.clear() + } + private fun hasAllRequiredPermissions(): Boolean { // For starting FGS with connectedDevice|dataSync, we need: // - Foreground service permissions (declared in manifest) diff --git a/app/src/main/java/com/bitchat/android/service/PeerAvailabilityNotifier.kt b/app/src/main/java/com/bitchat/android/service/PeerAvailabilityNotifier.kt index 0a610c46..8639ad22 100644 --- a/app/src/main/java/com/bitchat/android/service/PeerAvailabilityNotifier.kt +++ b/app/src/main/java/com/bitchat/android/service/PeerAvailabilityNotifier.kt @@ -185,11 +185,15 @@ internal class PeerAvailabilityNotifier( fun onPeerCountChanged(peerCount: Int, isAppInBackground: Boolean) { when (tracker.update(peerCount, isAppInBackground)) { PeerAvailabilityAction.NONE -> Unit - PeerAvailabilityAction.CLEAR -> notificationManager.cancel(NOTIFICATION_ID) + PeerAvailabilityAction.CLEAR -> clear() PeerAvailabilityAction.SHOW -> showNotification(peerCount) } } + fun clear() { + notificationManager.cancel(NOTIFICATION_ID) + } + private fun createNotificationChannel() { val channel = NotificationChannel( CHANNEL_ID, diff --git a/app/src/test/kotlin/com/bitchat/android/service/PeerAvailabilityNotifierTest.kt b/app/src/test/kotlin/com/bitchat/android/service/PeerAvailabilityNotifierTest.kt index 8e2a0937..176ffad5 100644 --- a/app/src/test/kotlin/com/bitchat/android/service/PeerAvailabilityNotifierTest.kt +++ b/app/src/test/kotlin/com/bitchat/android/service/PeerAvailabilityNotifierTest.kt @@ -189,6 +189,25 @@ class PeerAvailabilityNotifierTest { ) } + @Test + fun `explicit clear cancels availability notification`() { + val notifier = PeerAvailabilityNotifier( + context = context, + tracker = tracker(), + textProvider = testTextProvider(), + canPostNotifications = { true } + ) + + notifier.onPeerCountChanged(1, isAppInBackground = true) + notifier.clear() + + assertNull( + shadowOf(systemNotificationManager).getNotification( + PeerAvailabilityNotifier.NOTIFICATION_ID + ) + ) + } + @Test fun `disabled notifications do not post`() { val history = InMemoryAlertHistory()