android, desktop: notifications improvement (missed call, hidden user) (#4389)

* android, desktop: notifications improvement (missed call, hidden user)

* change

* change
This commit is contained in:
Stanislav Dmitrenko
2024-07-03 00:19:43 +07:00
committed by GitHub
parent 85af368371
commit ecff3c6ee5
9 changed files with 52 additions and 10 deletions
@@ -179,6 +179,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
override fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean = NtfManager.notifyCallInvitation(invitation)
override fun hasNotificationsForChat(chatId: String): Boolean = NtfManager.hasNotificationsForChat(chatId)
override fun cancelNotificationsForChat(chatId: String) = NtfManager.cancelNotificationsForChat(chatId)
override fun cancelNotificationsForUser(userId: Long) = NtfManager.cancelNotificationsForUser(userId)
override fun displayNotification(user: UserLike, chatId: String, displayName: String, msgText: String, image: String?, actions: List<Pair<NotificationAction, () -> Unit>>) = NtfManager.displayNotification(user, chatId, displayName, msgText, image, actions.map { it.first })
override fun androidCreateNtfChannelsMaybeShowAlert() = NtfManager.createNtfChannelsMaybeShowAlert()
override fun cancelCallNotification() = NtfManager.cancelCallNotification()
@@ -48,7 +48,8 @@ object NtfManager {
}
private val manager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private var prevNtfTime = mutableMapOf<String, Long>()
// (UserId, ChatId) -> Time
private var prevNtfTime = mutableMapOf<Pair<Long, ChatId>, Long>()
private val msgNtfTimeoutMs = 30000L
init {
@@ -72,7 +73,8 @@ object NtfManager {
}
fun cancelNotificationsForChat(chatId: String) {
prevNtfTime.remove(chatId)
val key = prevNtfTime.keys.firstOrNull { it.second == chatId }
prevNtfTime.remove(key)
manager.cancel(chatId.hashCode())
val msgNtfs = manager.activeNotifications.filter { ntf ->
ntf.notification.channelId == MessageChannel
@@ -83,12 +85,26 @@ object NtfManager {
}
}
fun cancelNotificationsForUser(userId: Long) {
prevNtfTime.keys.filter { it.first == userId }.forEach {
prevNtfTime.remove(it)
manager.cancel(it.second.hashCode())
}
val msgNtfs = manager.activeNotifications.filter { ntf ->
ntf.notification.channelId == MessageChannel
}
if (msgNtfs.size <= 1) {
// Have a group notification with no children so cancel it
manager.cancel(0)
}
}
fun displayNotification(user: UserLike, chatId: String, displayName: String, msgText: String, image: String? = null, actions: List<NotificationAction> = emptyList()) {
if (!user.showNotifications) return
Log.d(TAG, "notifyMessageReceived $chatId")
val now = Clock.System.now().toEpochMilliseconds()
val recentNotification = (now - prevNtfTime.getOrDefault(chatId, 0) < msgNtfTimeoutMs)
prevNtfTime[chatId] = now
val recentNotification = (now - prevNtfTime.getOrDefault(user.userId to chatId, 0) < msgNtfTimeoutMs)
prevNtfTime[user.userId to chatId] = now
val previewMode = appPreferences.notificationPreviewMode.get()
val title = if (previewMode == NotificationPreviewMode.HIDDEN.name) generalGetString(MR.strings.notification_preview_somebody) else displayName
val content = if (previewMode != NotificationPreviewMode.MESSAGE.name) generalGetString(MR.strings.notification_preview_new_message) else msgText
@@ -498,11 +498,15 @@ object ChatController {
}
suspend fun changeActiveUser_(rhId: Long?, toUserId: Long?, viewPwd: String?) {
val prevActiveUser = chatModel.currentUser.value
val currentUser = changingActiveUserMutex.withLock {
(if (toUserId != null) apiSetActiveUser(rhId, toUserId, viewPwd) else apiGetActiveUser(rhId)).also {
chatModel.currentUser.value = it
}
}
if (prevActiveUser?.hidden == true) {
ntfManager.cancelNotificationsForUser(prevActiveUser.userId)
}
val users = listUsers(rhId)
chatModel.users.clear()
chatModel.users.addAll(users)
@@ -2354,6 +2358,8 @@ object ChatController {
notify()
} else if (chatModel.upsertChatItem(rh, cInfo, cItem)) {
notify()
} else if (cItem.content is CIContent.RcvCall && cItem.content.status == CICallStatus.Missed) {
notify()
}
}
@@ -96,6 +96,7 @@ abstract class NtfManager {
abstract fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean
abstract fun hasNotificationsForChat(chatId: String): Boolean
abstract fun cancelNotificationsForChat(chatId: String)
abstract fun cancelNotificationsForUser(userId: Long)
abstract fun displayNotification(user: UserLike, chatId: String, displayName: String, msgText: String, image: String? = null, actions: List<Pair<NotificationAction, () -> Unit>> = emptyList())
abstract fun cancelCallNotification()
abstract fun cancelAllNotifications()
@@ -502,6 +502,7 @@ fun deleteChatDatabaseFilesAndState() {
chatModel.chatItems.clear()
chatModel.chats.clear()
chatModel.users.clear()
ntfManager.cancelAllNotifications()
}
private fun exportArchive(
@@ -18,6 +18,7 @@ import androidx.compose.ui.unit.dp
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.User
import chat.simplex.common.platform.ColumnWithScrollBar
import chat.simplex.common.platform.ntfManager
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chatlist.UserProfileRow
import chat.simplex.common.views.database.PassphraseField
@@ -36,6 +37,9 @@ fun HiddenProfileView(
withBGApi {
try {
val u = m.controller.apiHideUser(user, hidePassword)
if (!u.activeUser) {
ntfManager.cancelNotificationsForUser(u.userId)
}
m.updateUser(u)
close()
} catch (e: Exception) {
@@ -367,6 +367,7 @@ private suspend fun doRemoveUser(m: ChatModel, user: User, users: List<User>, de
}
}
m.removeUser(user)
ntfManager.cancelNotificationsForUser(user.userId)
} catch (e: Exception) {
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_deleting_user), e.stackTraceToString())
}
@@ -16,7 +16,7 @@ import java.io.File
import javax.imageio.ImageIO
object NtfManager {
private val prevNtfs = arrayListOf<Pair<ChatId, Slice>>()
private val prevNtfs = arrayListOf<Pair<Pair<Long, ChatId>, Slice>>()
private val prevNtfsMutex: Mutex = Mutex()
fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean {
@@ -45,14 +45,14 @@ object NtfManager {
generalGetString(MR.strings.accept) to { ntfManager.acceptCallAction(invitation.contact.id) },
generalGetString(MR.strings.reject) to { ChatModel.callManager.endCall(invitation = invitation) }
)
displayNotificationViaLib(contactId, title, text, prepareIconPath(largeIcon), actions) {
displayNotificationViaLib(invitation.user.userId, contactId, title, text, prepareIconPath(largeIcon), actions) {
ntfManager.openChatAction(invitation.user.userId, contactId)
}
return true
}
fun showMessage(title: String, text: String) {
displayNotificationViaLib("MESSAGE", title, text, null, emptyList()) {}
displayNotificationViaLib(-1, "MESSAGE", title, text, null, emptyList()) {}
}
fun hasNotificationsForChat(chatId: ChatId) = false//prevNtfs.any { it.first == chatId }
@@ -60,7 +60,7 @@ object NtfManager {
fun cancelNotificationsForChat(chatId: ChatId) {
withBGApi {
prevNtfsMutex.withLock {
val ntf = prevNtfs.firstOrNull { it.first == chatId }
val ntf = prevNtfs.firstOrNull { (userChat) -> userChat.second == chatId }
if (ntf != null) {
prevNtfs.remove(ntf)
/*try {
@@ -74,6 +74,16 @@ object NtfManager {
}
}
fun cancelNotificationsForUser(userId: Long) {
withBGApi {
prevNtfsMutex.withLock {
prevNtfs.filter { (userChat) -> userChat.first == userId }.forEach {
prevNtfs.remove(it)
}
}
}
}
fun cancelAllNotifications() {
// prevNtfs.forEach { try { it.second.close() } catch (e: Exception) { println("Failed to close notification: ${e.stackTraceToString()}") } }
withBGApi {
@@ -95,12 +105,13 @@ object NtfManager {
else -> base64ToBitmap(image)
}
displayNotificationViaLib(chatId, title, content, prepareIconPath(largeIcon), actions.map { it.first.name to it.second }) {
displayNotificationViaLib(user.userId, chatId, title, content, prepareIconPath(largeIcon), actions.map { it.first.name to it.second }) {
ntfManager.openChatAction(user.userId, chatId)
}
}
private fun displayNotificationViaLib(
userId: Long,
chatId: String,
title: String,
text: String,
@@ -123,7 +134,7 @@ object NtfManager {
try {
withBGApi {
prevNtfsMutex.withLock {
prevNtfs.add(chatId to builder.toast())
prevNtfs.add(Pair(userId, chatId) to builder.toast())
}
}
} catch (e: Throwable) {
@@ -18,6 +18,7 @@ fun initApp() {
override fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean = chat.simplex.common.model.NtfManager.notifyCallInvitation(invitation)
override fun hasNotificationsForChat(chatId: String): Boolean = chat.simplex.common.model.NtfManager.hasNotificationsForChat(chatId)
override fun cancelNotificationsForChat(chatId: String) = chat.simplex.common.model.NtfManager.cancelNotificationsForChat(chatId)
override fun cancelNotificationsForUser(userId: Long) = chat.simplex.common.model.NtfManager.cancelNotificationsForUser(userId)
override fun displayNotification(user: UserLike, chatId: String, displayName: String, msgText: String, image: String?, actions: List<Pair<NotificationAction, () -> Unit>>) = chat.simplex.common.model.NtfManager.displayNotification(user, chatId, displayName, msgText, image, actions)
override fun androidCreateNtfChannelsMaybeShowAlert() {}
override fun cancelCallNotification() {}