Пуши: учитывать mute и имя отправителя из payload

This commit is contained in:
2026-03-29 23:12:29 +05:00
parent ff854e919e
commit ce6bc985be

View File

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