diff --git a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt index f828af9e..0bd99223 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt @@ -256,6 +256,14 @@ class BluetoothMeshService(private val context: Context) : TransportBridgeServic try { com.bitchat.android.services.AppStateStore.setTransportPeers("BLE", peerIDs) } catch (_: Exception) { } // Then notify UI delegate if attached delegate?.didUpdatePeerList(peerIDs) + + // If UI is detached (background), update notification manager directly + if (delegate == null) { + try { + serviceNotificationManager.setAppBackgroundState(true) + serviceNotificationManager.showActiveUserNotification(peerIDs) + } catch (_: Exception) { } + } } override fun onPeerRemoved(peerID: String) { authenticatedPeerState.clear(peerID) diff --git a/app/src/main/java/com/bitchat/android/ui/NotificationManager.kt b/app/src/main/java/com/bitchat/android/ui/NotificationManager.kt index f15ada44..2f9e518d 100644 --- a/app/src/main/java/com/bitchat/android/ui/NotificationManager.kt +++ b/app/src/main/java/com/bitchat/android/ui/NotificationManager.kt @@ -177,19 +177,51 @@ class NotificationManager( } fun showActiveUserNotification(peers: List) { - val currentTime = System.currentTimeMillis() - val activePeerNotificationIntervalExceeded = - (currentTime - notificationIntervalManager.lastNetworkNotificationTime) > ACTIVE_PEERS_NOTIFICATION_TIME_INTERVAL - val newPeers = peers - notificationIntervalManager.recentlySeenPeers - if (isAppInBackground && activePeerNotificationIntervalExceeded && newPeers.isNotEmpty()) { - Log.d(TAG, "Showing notification for active peers") - showNotificationForActivePeers(peers.size) - notificationIntervalManager.setLastNetworkNotificationTime(currentTime) - notificationIntervalManager.recentlySeenPeers.addAll(newPeers) - } else { - Log.d(TAG, "Skipping notification - app in foreground or it has been less than 5 minutes since last active peer notification") + if (peers.isEmpty()) { + notificationIntervalManager.recentlySeenPeers.clear() + notificationManager.cancel(ACTIVE_PEERS_NOTIFICATION_ID) return } + + val currentTime = System.currentTimeMillis() + val timeSinceLast = currentTime - notificationIntervalManager.lastNetworkNotificationTime + val activePeerNotificationIntervalExceeded = timeSinceLast > ACTIVE_PEERS_NOTIFICATION_TIME_INTERVAL + + val isFreshStart = notificationIntervalManager.recentlySeenPeers.isEmpty() + + // Check if we should alert (Heads-up / Sound) + // Only alert if we are in Background AND (it's a fresh start OR enough time passed) + val shouldAlert = isAppInBackground && (isFreshStart || activePeerNotificationIntervalExceeded) + + // Check if we should update an existing notification (Silent) + // If alerting, we definitely update. + // If not alerting: + // - If Background: Update silently (create or update). + // - If Foreground: Update silently ONLY IF notification is already active (don't create new banner over UI). + var shouldUpdate = shouldAlert || isAppInBackground + + if (!shouldUpdate && !isAppInBackground && Build.VERSION.SDK_INT >= 23) { + // Check if the notification is currently active + shouldUpdate = systemNotificationManager.activeNotifications.any { it.id == ACTIVE_PEERS_NOTIFICATION_ID } + } + + if (shouldUpdate) { + // If we are alerting, use onlyAlertOnce = false (Sound/Vib). + // If we are just updating/silently posting, use onlyAlertOnce = true. + val onlyAlertOnce = !shouldAlert + + if (shouldAlert) { + Log.d(TAG, "Showing NEW notification for active peers (Heads-up)") + notificationIntervalManager.setLastNetworkNotificationTime(currentTime) + } else { + Log.d(TAG, "Updating active peers notification silently (inBackground=$isAppInBackground)") + } + + showNotificationForActivePeers(peers.size, onlyAlertOnce = onlyAlertOnce) + } + + // Always update tracking + notificationIntervalManager.recentlySeenPeers.addAll(peers) } private fun showNotificationForSender(senderPeerID: String) { @@ -239,7 +271,7 @@ class NotificationManager( .setContentText(contentText) .setContentIntent(pendingIntent) .setAutoCancel(true) - .setPriority(NotificationCompat.PRIORITY_HIGH) + .setPriority(NotificationCompat.PRIORITY_MAX) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .addPerson(person) .setShowWhen(true) @@ -314,7 +346,7 @@ class NotificationManager( notificationManager.notify((System.currentTimeMillis() and 0x7FFFFFFF).toInt(), builder.build()) } - private fun showNotificationForActivePeers(peersSize: Int) { + private fun showNotificationForActivePeers(peersSize: Int, onlyAlertOnce: Boolean) { // Create intent to open the app val intent = Intent(context, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP @@ -341,13 +373,14 @@ class NotificationManager( .setContentText(contentText) .setContentIntent(pendingIntent) .setAutoCancel(true) - .setPriority(NotificationCompat.PRIORITY_MIN) + .setPriority(NotificationCompat.PRIORITY_MAX) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setShowWhen(true) + .setOnlyAlertOnce(onlyAlertOnce) .setWhen(System.currentTimeMillis()) notificationManager.notify(ACTIVE_PEERS_NOTIFICATION_ID, builder.build()) - Log.d(TAG, "Displayed notification for $contentTitle with ID $ACTIVE_PEERS_NOTIFICATION_ID") + Log.d(TAG, "Displayed notification for $contentTitle with ID $ACTIVE_PEERS_NOTIFICATION_ID (onlyAlertOnce=$onlyAlertOnce)") } private fun showSummaryNotification() { if (pendingNotifications.isEmpty()) return