diff --git a/app/src/main/java/com/rosetta/messenger/push/RosettaFirebaseMessagingService.kt b/app/src/main/java/com/rosetta/messenger/push/RosettaFirebaseMessagingService.kt index 18bd950..f32ad54 100644 --- a/app/src/main/java/com/rosetta/messenger/push/RosettaFirebaseMessagingService.kt +++ b/app/src/main/java/com/rosetta/messenger/push/RosettaFirebaseMessagingService.kt @@ -56,14 +56,43 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { fun cancelNotificationForChat(context: Context, senderPublicKey: String) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val normalizedKey = senderPublicKey.trim() - if (normalizedKey.isNotEmpty()) { - notificationManager.cancel(getNotificationIdForChat(normalizedKey)) + val variants = buildDialogKeyVariants(senderPublicKey) + for (key in variants) { + notificationManager.cancel(getNotificationIdForChat(key)) } // Fallback: некоторые серверные payload могут прийти без sender key. // Для них используется ID от пустой строки — тоже очищаем при входе в диалог. notificationManager.cancel(getNotificationIdForChat("")) } + + private fun buildDialogKeyVariants(rawKey: String): Set { + val trimmed = rawKey.trim() + if (trimmed.isBlank()) return emptySet() + + val variants = linkedSetOf(trimmed) + val lower = trimmed.lowercase(Locale.ROOT) + when { + lower.startsWith("#group:") -> { + val groupId = trimmed.substringAfter(':').trim() + if (groupId.isNotBlank()) { + variants.add("group:$groupId") + variants.add(groupId) + } + } + lower.startsWith("group:") -> { + val groupId = trimmed.substringAfter(':').trim() + if (groupId.isNotBlank()) { + variants.add("#group:$groupId") + variants.add(groupId) + } + } + else -> { + variants.add("#group:$trimmed") + variants.add("group:$trimmed") + } + } + return variants + } } /** Вызывается когда получен новый FCM токен Отправляем его на сервер через протокол */ @@ -90,6 +119,8 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { // Обрабатываем data payload if (remoteMessage.data.isNotEmpty()) { val data = remoteMessage.data + val notificationTitle = remoteMessage.notification?.title?.trim().orEmpty() + val notificationBody = remoteMessage.notification?.body?.trim().orEmpty() val type = firstNonBlank(data, "type", "event", "action") ?.lowercase(Locale.ROOT) @@ -100,15 +131,26 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { "sender_public_key", "from_public_key", "fromPublicKey", + "from", "public_key", "publicKey" ) val senderName = - firstNonBlank(data, "sender_name", "from_title", "sender", "title", "name") + firstNonBlank( + data, + "sender_name", + "sender_title", + "from_title", + "sender", + "title", + "name" + ) + ?: notificationTitle.takeIf { it.isNotBlank() } ?: senderPublicKey?.take(10) ?: "Rosetta" val messagePreview = firstNonBlank(data, "message_preview", "message", "text", "body") + ?: notificationBody.takeIf { it.isNotBlank() } ?: "New message" val isReadEvent = type == "message_read" || type == "read" @@ -152,7 +194,21 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { // с неуправляемым notification id. remoteMessage.notification?.let { if (!handledMessageData) { - showSimpleNotification(it.title ?: "Rosetta", it.body ?: "New message") + val senderPublicKey = + firstNonBlank( + remoteMessage.data, + "sender_public_key", + "from_public_key", + "fromPublicKey", + "from", + "public_key", + "publicKey" + ) + showSimpleNotification( + it.title ?: "Rosetta", + it.body ?: "New message", + senderPublicKey + ) } } } @@ -223,8 +279,12 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { if (isAppInForeground || !areNotificationsEnabled()) { return } + val senderKey = senderPublicKey?.trim().orEmpty() + if (senderKey.isNotEmpty() && isDialogMuted(senderKey)) { + return + } // Dedup: suppress duplicate pushes within DEDUP_WINDOW_MS - val dedupKey = senderPublicKey?.trim()?.ifEmpty { null } ?: "__simple__" + val dedupKey = senderKey.ifEmpty { "__simple__" } val now = System.currentTimeMillis() val lastTs = lastNotifTimestamps[dedupKey] if (lastTs != null && now - lastTs < DEDUP_WINDOW_MS) { @@ -235,8 +295,8 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { createNotificationChannel() // Используем sender-based ID если известен ключ — чтобы cancelNotificationForChat мог убрать уведомление - val notifId = if (!senderPublicKey.isNullOrBlank()) { - getNotificationIdForChat(senderPublicKey.trim()) + val notifId = if (senderKey.isNotEmpty()) { + getNotificationIdForChat(senderKey) } else { (title + body).hashCode() and 0x7FFFFFFF } @@ -318,7 +378,10 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { val accountManager = AccountManager(applicationContext) val currentAccount = accountManager.getLastLoggedPublicKey().orEmpty() runBlocking(Dispatchers.IO) { - PreferencesManager(applicationContext).isChatMuted(currentAccount, senderPublicKey) + val preferences = PreferencesManager(applicationContext) + buildDialogKeyVariants(senderPublicKey).any { key -> + preferences.isChatMuted(currentAccount, key) + } } }.getOrDefault(false) }