fix: Уведомление приходит, хотя чат открыт. Нет зелёной точки «в сети» в списке чатов. Тап по уведомлению не открывает чат. Реакции на сторис. Пропали галочки в папке «Все».

This commit is contained in:
Jganenokk
2026-08-19 22:07:16 +07:00
parent 3e6254bc5d
commit 3e4edc535c
14 changed files with 461 additions and 360 deletions
@@ -0,0 +1,22 @@
package ru.komet.app
import android.content.Intent
import io.flutter.plugin.common.EventChannel
object ChatNotifications {
const val EXTRA_CHAT = "komet_chat"
@Volatile
var activeChatId: Long = 0L
@Volatile
var sink: EventChannel.EventSink? = null
fun isDisplayed(chatId: Long): Boolean =
AppState.resumed && activeChatId == chatId
fun chatIdFrom(intent: Intent?): Long {
val id = intent?.getLongExtra(EXTRA_CHAT, 0L) ?: 0L
return if (id > 0L) id else 0L
}
}
@@ -74,6 +74,13 @@ class KometNotifier(private val ctx: Context) {
private fun showMessage(data: Map<String, String>) {
val chatId = data["mc"]?.toLongOrNull() ?: return
val notifId = (chatId and 0x7fffffff).toInt()
if (ChatNotifications.isDisplayed(chatId)) {
manager().cancel(notifId)
clearHistory(chatId)
rebuildSummary(notifId)
return
}
val senderId = data["suid"] ?: ""
val senderName = data["userName"] ?: data["title"] ?: "MAX"
val chatTitle = data["title"] ?: senderName
@@ -81,7 +88,6 @@ class KometNotifier(private val ctx: Context) {
val ts = data["ctime"]?.toLongOrNull() ?: data["ttime"]?.toLongOrNull()
?: System.currentTimeMillis()
val isGroup = chatTitle != senderName
val notifId = (chatId and 0x7fffffff).toInt()
ensureChannel()
@@ -135,12 +141,11 @@ class KometNotifier(private val ctx: Context) {
}
manager().notify(notifId, builder.build())
updateSummary(notifId, chatId, senderName, text, ts, active)
updateSummary(notifId, senderName, text, ts, active)
}
private fun updateSummary(
notifId: Int,
chatId: Long,
senderName: String,
text: String,
ts: Long,
@@ -153,27 +158,50 @@ class KometNotifier(private val ctx: Context) {
val k = keys.next()
val id = k.toIntOrNull() ?: continue
if (id == notifId) continue
if (activeBefore.contains(id)) kept.put(k, reg.getJSONObject(k))
val entry = reg.optJSONObject(k) ?: continue
if (activeBefore.contains(id)) kept.put(k, entry)
}
kept.put(
notifId.toString(),
JSONObject().put("n", senderName).put("t", text).put("ts", ts),
)
saveRegistry(kept)
publishSummary(entriesOf(kept))
}
if (kept.length() < 2) {
manager().cancel(SUMMARY_ID)
return
private fun rebuildSummary(dismissedId: Int) {
val active = activeIds()
val reg = loadRegistry()
val kept = JSONObject()
val keys = reg.keys()
while (keys.hasNext()) {
val k = keys.next()
val id = k.toIntOrNull() ?: continue
if (id == dismissedId) continue
val entry = reg.optJSONObject(k) ?: continue
if (active.contains(id)) kept.put(k, entry)
}
saveRegistry(kept)
publishSummary(entriesOf(kept))
}
private fun entriesOf(reg: JSONObject): List<Triple<String, String, Long>> {
val entries = ArrayList<Triple<String, String, Long>>()
val kk = kept.keys()
while (kk.hasNext()) {
val k = kk.next()
val o = kept.getJSONObject(k)
val keys = reg.keys()
while (keys.hasNext()) {
val o = reg.optJSONObject(keys.next()) ?: continue
entries.add(Triple(o.optString("n"), o.optString("t"), o.optLong("ts")))
}
entries.sortByDescending { it.third }
return entries
}
private fun publishSummary(entries: List<Triple<String, String, Long>>) {
if (entries.size < 2) {
manager().cancel(SUMMARY_ID)
return
}
val newest = entries.first()
val inbox = NotificationCompat.InboxStyle()
for (e in entries.take(6)) inbox.addLine(boldLine(e.first, e.second))
@@ -185,12 +213,12 @@ class KometNotifier(private val ctx: Context) {
.setGroup(GROUP_KEY)
.setGroupSummary(true)
.setAutoCancel(true)
.setWhen(ts)
.setWhen(newest.third)
.setShowWhen(true)
.setNumber(entries.size)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
.setContentTitle("Komet")
.setContentText(boldLine(senderName, text))
.setContentText(boldLine(newest.first, newest.second))
.setStyle(inbox)
.build()
manager().notify(SUMMARY_ID, summary)
@@ -240,11 +268,7 @@ class KometNotifier(private val ctx: Context) {
private fun publishShortcut(id: String, chatId: Long, title: String, person: Person) {
try {
val intent = (ctx.packageManager.getLaunchIntentForPackage(ctx.packageName)
?: Intent(Intent.ACTION_VIEW)).apply {
action = Intent.ACTION_VIEW
putExtra("komet_chat", chatId)
}
val intent = chatIntent(chatId).setAction(Intent.ACTION_VIEW)
val shortcut = ShortcutInfoCompat.Builder(ctx, id)
.setShortLabel(title)
.setLongLived(true)
@@ -260,12 +284,26 @@ class KometNotifier(private val ctx: Context) {
}
}
private fun openIntent(notifId: Int, chatId: Long): PendingIntent? {
val launch = ctx.packageManager.getLaunchIntentForPackage(ctx.packageName) ?: return null
launch.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
launch.putExtra("komet_chat", chatId)
private fun chatIntent(chatId: Long): Intent {
val launcher = ctx.packageManager
.getLaunchIntentForPackage(ctx.packageName)?.component
val intent = if (launcher != null) {
Intent().setComponent(launcher)
} else {
Intent(ctx, MainActivity::class.java)
}
intent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP or
Intent.FLAG_ACTIVITY_CLEAR_TOP,
)
intent.putExtra(ChatNotifications.EXTRA_CHAT, chatId)
return intent
}
private fun openIntent(notifId: Int, chatId: Long): PendingIntent {
val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
return PendingIntent.getActivity(ctx, notifId, launch, flags)
return PendingIntent.getActivity(ctx, notifId, chatIntent(chatId), flags)
}
private fun activeIds(): Set<Int> = try {
@@ -76,6 +76,7 @@ class MainActivity : FlutterActivity() {
@Volatile private var exchangingEmitted = false
private var pendingCall: Map<String, Any?>? = null
private var pendingChat: Long = 0L
private companion object {
const val LOG_TAG = "VpnBypass"
@@ -401,12 +402,49 @@ class MainActivity : FlutterActivity() {
CallEvents.sink = null
}
})
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"ru.komet.app/notifications",
).setMethodCallHandler { call, result ->
when (call.method) {
"consumeInitialChat" -> {
stashChatOpen(intent, emit = false)
val chatId = pendingChat
pendingChat = 0L
result.success(if (chatId > 0L) chatId else null)
}
"setActiveChat" -> {
ChatNotifications.activeChatId = longArg(call.argument<Any>("chatId"))
result.success(null)
}
"clearActiveChat" -> {
ChatNotifications.activeChatId = 0L
result.success(null)
}
else -> result.notImplemented()
}
}
EventChannel(
flutterEngine.dartExecutor.binaryMessenger,
"ru.komet.app/notification_events",
).setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
ChatNotifications.sink = events
}
override fun onCancel(arguments: Any?) {
ChatNotifications.sink = null
}
})
}
override fun onCreate(savedInstanceState: Bundle?) {
if (intent?.hasExtra(CallConst.EXTRA_CALL) == true) applyCallWindowFlags()
super.onCreate(savedInstanceState)
intent?.let { if (it.hasExtra(CallConst.EXTRA_CALL)) stashCall(it, emit = false) }
stashChatOpen(intent, emit = false)
}
override fun onNewIntent(intent: Intent) {
@@ -416,6 +454,19 @@ class MainActivity : FlutterActivity() {
applyCallWindowFlags()
stashCall(intent, emit = true)
}
stashChatOpen(intent, emit = true)
}
private fun stashChatOpen(source: Intent?, emit: Boolean) {
val chatId = ChatNotifications.chatIdFrom(source)
if (chatId == 0L) return
source?.removeExtra(ChatNotifications.EXTRA_CHAT)
val sink = ChatNotifications.sink
if (emit && sink != null) {
sink.success(chatId)
} else {
pendingChat = chatId
}
}
private fun stashCall(intent: Intent, emit: Boolean) {
@@ -793,6 +844,7 @@ class MainActivity : FlutterActivity() {
override fun onResume() {
super.onResume()
AppState.resumed = true
stashChatOpen(intent, emit = true)
}
override fun onPause() {