desktop: notifications support (#2754)
* desktop: notifications support * adapted external lib for interacting with notificatioins * disabled some functions
This commit is contained in:
committed by
GitHub
parent
02d00944ff
commit
2b715a0d8c
@@ -107,39 +107,18 @@ fun processNotificationIntent(intent: Intent?) {
|
||||
val chatId = intent.getStringExtra("chatId")
|
||||
Log.d(TAG, "processNotificationIntent: OpenChatAction $chatId")
|
||||
if (chatId != null) {
|
||||
withBGApi {
|
||||
awaitChatStartedIfNeeded(chatModel)
|
||||
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
|
||||
chatModel.controller.changeActiveUser(userId, null)
|
||||
}
|
||||
val cInfo = chatModel.getChat(chatId)?.chatInfo
|
||||
chatModel.clearOverlays.value = true
|
||||
if (cInfo != null && (cInfo is ChatInfo.Direct || cInfo is ChatInfo.Group)) openChat(cInfo, chatModel)
|
||||
}
|
||||
ntfManager.openChatAction(userId, chatId)
|
||||
}
|
||||
}
|
||||
NtfManager.ShowChatsAction -> {
|
||||
Log.d(TAG, "processNotificationIntent: ShowChatsAction")
|
||||
withBGApi {
|
||||
awaitChatStartedIfNeeded(chatModel)
|
||||
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
|
||||
chatModel.controller.changeActiveUser(userId, null)
|
||||
}
|
||||
chatModel.chatId.value = null
|
||||
chatModel.clearOverlays.value = true
|
||||
}
|
||||
ntfManager.showChatsAction(userId)
|
||||
}
|
||||
NtfManager.AcceptCallAction -> {
|
||||
val chatId = intent.getStringExtra("chatId")
|
||||
if (chatId == null || chatId == "") return
|
||||
Log.d(TAG, "processNotificationIntent: AcceptCallAction $chatId")
|
||||
chatModel.clearOverlays.value = true
|
||||
val invitation = chatModel.callInvitations[chatId]
|
||||
if (invitation == null) {
|
||||
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.call_already_ended))
|
||||
} else {
|
||||
chatModel.callManager.acceptIncomingCall(invitation = invitation)
|
||||
}
|
||||
ntfManager.acceptCallAction(chatId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,19 +180,6 @@ fun processExternalIntent(intent: Intent?) {
|
||||
fun isMediaIntent(intent: Intent): Boolean =
|
||||
intent.type?.startsWith("image/") == true || intent.type?.startsWith("video/") == true
|
||||
|
||||
suspend fun awaitChatStartedIfNeeded(chatModel: ChatModel, timeout: Long = 30_000) {
|
||||
// Still decrypting database
|
||||
if (chatModel.chatRunning.value == null) {
|
||||
val step = 50L
|
||||
for (i in 0..(timeout / step)) {
|
||||
if (chatModel.chatRunning.value == true || chatModel.onboardingStage.value == OnboardingStage.Step1_SimpleXInfo) {
|
||||
break
|
||||
}
|
||||
delay(step)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//fun testJson() {
|
||||
// val str: String = """
|
||||
// """.trimIndent()
|
||||
|
||||
@@ -139,14 +139,11 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
androidAppContext = this
|
||||
APPLICATION_ID = BuildConfig.APPLICATION_ID
|
||||
ntfManager = object : chat.simplex.common.platform.NtfManager() {
|
||||
override fun notifyContactConnected(user: User, contact: Contact) = NtfManager.notifyContactConnected(user, contact)
|
||||
override fun notifyContactRequestReceived(user: User, cInfo: ChatInfo.ContactRequest) = NtfManager.notifyContactRequestReceived(user, cInfo)
|
||||
override fun notifyMessageReceived(user: User, cInfo: ChatInfo, cItem: ChatItem) = NtfManager.notifyMessageReceived(user, cInfo, cItem)
|
||||
override fun notifyCallInvitation(invitation: RcvCallInvitation) = NtfManager.notifyCallInvitation(invitation)
|
||||
override fun hasNotificationsForChat(chatId: String): Boolean = NtfManager.hasNotificationsForChat(chatId)
|
||||
override fun cancelNotificationsForChat(chatId: String) = NtfManager.cancelNotificationsForChat(chatId)
|
||||
override fun displayNotification(user: User, chatId: String, displayName: String, msgText: String, image: String?, actions: List<NotificationAction>) = NtfManager.displayNotification(user, chatId, displayName, msgText, image, actions)
|
||||
override fun createNtfChannelsMaybeShowAlert() = NtfManager.createNtfChannelsMaybeShowAlert()
|
||||
override fun displayNotification(user: User, 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()
|
||||
override fun cancelAllNotifications() = NtfManager.cancelAllNotifications()
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import chat.simplex.app.*
|
||||
import chat.simplex.app.TAG
|
||||
import chat.simplex.app.views.call.IncomingCallActivity
|
||||
import chat.simplex.app.views.call.getKeyguardManager
|
||||
import chat.simplex.common.views.chatlist.acceptContactRequest
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.*
|
||||
@@ -82,31 +81,6 @@ object NtfManager {
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyContactRequestReceived(user: User, cInfo: ChatInfo.ContactRequest) {
|
||||
displayNotification(
|
||||
user = user,
|
||||
chatId = cInfo.id,
|
||||
displayName = cInfo.displayName,
|
||||
msgText = generalGetString(MR.strings.notification_new_contact_request),
|
||||
image = cInfo.image,
|
||||
listOf(NotificationAction.ACCEPT_CONTACT_REQUEST)
|
||||
)
|
||||
}
|
||||
|
||||
fun notifyContactConnected(user: User, contact: Contact) {
|
||||
displayNotification(
|
||||
user = user,
|
||||
chatId = contact.id,
|
||||
displayName = contact.displayName,
|
||||
msgText = generalGetString(MR.strings.notification_contact_connected)
|
||||
)
|
||||
}
|
||||
|
||||
fun notifyMessageReceived(user: User, cInfo: ChatInfo, cItem: ChatItem) {
|
||||
if (!cInfo.ntfsEnabled) return
|
||||
displayNotification(user = user, chatId = cInfo.id, displayName = cInfo.displayName, msgText = hideSecrets(cItem))
|
||||
}
|
||||
|
||||
fun displayNotification(user: User, chatId: String, displayName: String, msgText: String, image: String? = null, actions: List<NotificationAction> = emptyList()) {
|
||||
if (!user.showNotifications) return
|
||||
Log.d(TAG, "notifyMessageReceived $chatId")
|
||||
@@ -243,19 +217,6 @@ object NtfManager {
|
||||
|
||||
fun hasNotificationsForChat(chatId: String): Boolean = manager.activeNotifications.any { it.id == chatId.hashCode() }
|
||||
|
||||
private fun hideSecrets(cItem: ChatItem): String {
|
||||
val md = cItem.formattedText
|
||||
return if (md != null) {
|
||||
var res = ""
|
||||
for (ft in md) {
|
||||
res += if (ft.format is Format.Secret) "..." else ft.text
|
||||
}
|
||||
res
|
||||
} else {
|
||||
cItem.text
|
||||
}
|
||||
}
|
||||
|
||||
private fun chatPendingIntent(intentAction: String, userId: Long?, chatId: String? = null, broadcast: Boolean = false): PendingIntent {
|
||||
Log.d(TAG, "chatPendingIntent for $intentAction")
|
||||
val uniqueInt = (System.currentTimeMillis() and 0xfffffff).toInt()
|
||||
@@ -299,18 +260,7 @@ object NtfManager {
|
||||
val chatId = intent?.getStringExtra(ChatIdKey) ?: return
|
||||
val m = SimplexApp.context.chatModel
|
||||
when (intent.action) {
|
||||
NotificationAction.ACCEPT_CONTACT_REQUEST.name -> {
|
||||
val isCurrentUser = m.currentUser.value?.userId == userId
|
||||
val cInfo: ChatInfo.ContactRequest? = if (isCurrentUser) {
|
||||
(m.getChat(chatId)?.chatInfo as? ChatInfo.ContactRequest) ?: return
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val apiId = chatId.replace("<@", "").toLongOrNull() ?: return
|
||||
acceptContactRequest(apiId, cInfo, isCurrentUser, m)
|
||||
cancelNotificationsForChat(chatId)
|
||||
}
|
||||
|
||||
NotificationAction.ACCEPT_CONTACT_REQUEST.name -> ntfManager.acceptContactRequestAction(userId, chatId)
|
||||
RejectCallAction -> {
|
||||
val invitation = m.callInvitations[chatId]
|
||||
if (invitation != null) {
|
||||
@@ -55,6 +55,7 @@ allprojects {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ kotlin {
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.7.1")
|
||||
implementation("com.github.Dansoftowner:jSystemThemeDetector:3.6")
|
||||
implementation("com.sshtools:two-slices:0.9.0-SNAPSHOT")
|
||||
implementation("org.slf4j:slf4j-simple:2.0.7")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +277,7 @@ actual fun getDrawableFromUri(uri: URI, withAlertOnException: Boolean): Any? {
|
||||
actual suspend fun saveTempImageUncompressed(image: ImageBitmap, asPng: Boolean): File? {
|
||||
return try {
|
||||
val ext = if (asPng) "png" else "jpg"
|
||||
tmpDir.mkdir()
|
||||
return File(tmpDir.absolutePath + File.separator + generateNewFileName("IMG", ext)).apply {
|
||||
outputStream().use { out ->
|
||||
image.asAndroidBitmap().compress(if (asPng) Bitmap.CompressFormat.PNG else Bitmap.CompressFormat.JPEG, 85, out)
|
||||
|
||||
@@ -13,14 +13,14 @@ actual fun SetNotificationsModeAdditions() {
|
||||
val notificationsPermissionState = rememberPermissionState(Manifest.permission.POST_NOTIFICATIONS)
|
||||
LaunchedEffect(notificationsPermissionState.hasPermission) {
|
||||
if (notificationsPermissionState.hasPermission) {
|
||||
ntfManager.createNtfChannelsMaybeShowAlert()
|
||||
ntfManager.androidCreateNtfChannelsMaybeShowAlert()
|
||||
} else {
|
||||
notificationsPermissionState.launchPermissionRequest()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LaunchedEffect(Unit) {
|
||||
ntfManager.createNtfChannelsMaybeShowAlert()
|
||||
ntfManager.androidCreateNtfChannelsMaybeShowAlert()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,12 @@ package chat.simplex.common.platform
|
||||
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.views.call.RcvCallInvitation
|
||||
import chat.simplex.common.views.chatlist.acceptContactRequest
|
||||
import chat.simplex.common.views.chatlist.openChat
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.onboarding.OnboardingStage
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
enum class NotificationAction {
|
||||
ACCEPT_CONTACT_REQUEST
|
||||
@@ -10,14 +16,104 @@ enum class NotificationAction {
|
||||
lateinit var ntfManager: NtfManager
|
||||
|
||||
abstract class NtfManager {
|
||||
abstract fun notifyContactConnected(user: User, contact: Contact)
|
||||
abstract fun notifyContactRequestReceived(user: User, cInfo: ChatInfo.ContactRequest)
|
||||
abstract fun notifyMessageReceived(user: User, cInfo: ChatInfo, cItem: ChatItem)
|
||||
fun notifyContactConnected(user: User, contact: Contact) = displayNotification(
|
||||
user = user,
|
||||
chatId = contact.id,
|
||||
displayName = contact.displayName,
|
||||
msgText = generalGetString(MR.strings.notification_contact_connected)
|
||||
)
|
||||
|
||||
fun notifyContactRequestReceived(user: User, cInfo: ChatInfo.ContactRequest) = displayNotification(
|
||||
user = user,
|
||||
chatId = cInfo.id,
|
||||
displayName = cInfo.displayName,
|
||||
msgText = generalGetString(MR.strings.notification_new_contact_request),
|
||||
image = cInfo.image,
|
||||
listOf(NotificationAction.ACCEPT_CONTACT_REQUEST to { acceptContactRequestAction(user.userId, cInfo.id) })
|
||||
)
|
||||
|
||||
fun notifyMessageReceived(user: User, cInfo: ChatInfo, cItem: ChatItem) {
|
||||
if (!cInfo.ntfsEnabled) return
|
||||
displayNotification(user = user, chatId = cInfo.id, displayName = cInfo.displayName, msgText = hideSecrets(cItem))
|
||||
}
|
||||
|
||||
fun acceptContactRequestAction(userId: Long?, chatId: ChatId) {
|
||||
val isCurrentUser = ChatModel.currentUser.value?.userId == userId
|
||||
val cInfo: ChatInfo.ContactRequest? = if (isCurrentUser) {
|
||||
(ChatModel.getChat(chatId)?.chatInfo as? ChatInfo.ContactRequest) ?: return
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val apiId = chatId.replace("<@", "").toLongOrNull() ?: return
|
||||
acceptContactRequest(apiId, cInfo, isCurrentUser, ChatModel)
|
||||
cancelNotificationsForChat(chatId)
|
||||
}
|
||||
|
||||
fun openChatAction(userId: Long?, chatId: ChatId) {
|
||||
withBGApi {
|
||||
awaitChatStartedIfNeeded(chatModel)
|
||||
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
|
||||
chatModel.controller.changeActiveUser(userId, null)
|
||||
}
|
||||
val cInfo = chatModel.getChat(chatId)?.chatInfo
|
||||
chatModel.clearOverlays.value = true
|
||||
if (cInfo != null && (cInfo is ChatInfo.Direct || cInfo is ChatInfo.Group)) openChat(cInfo, chatModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun showChatsAction(userId: Long?) {
|
||||
withBGApi {
|
||||
awaitChatStartedIfNeeded(chatModel)
|
||||
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
|
||||
chatModel.controller.changeActiveUser(userId, null)
|
||||
}
|
||||
chatModel.chatId.value = null
|
||||
chatModel.clearOverlays.value = true
|
||||
}
|
||||
}
|
||||
|
||||
fun acceptCallAction(chatId: ChatId) {
|
||||
chatModel.clearOverlays.value = true
|
||||
val invitation = chatModel.callInvitations[chatId]
|
||||
if (invitation == null) {
|
||||
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.call_already_ended))
|
||||
} else {
|
||||
chatModel.callManager.acceptIncomingCall(invitation = invitation)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun notifyCallInvitation(invitation: RcvCallInvitation)
|
||||
abstract fun hasNotificationsForChat(chatId: String): Boolean
|
||||
abstract fun cancelNotificationsForChat(chatId: String)
|
||||
abstract fun displayNotification(user: User, chatId: String, displayName: String, msgText: String, image: String? = null, actions: List<NotificationAction> = emptyList())
|
||||
abstract fun createNtfChannelsMaybeShowAlert()
|
||||
abstract fun displayNotification(user: User, chatId: String, displayName: String, msgText: String, image: String? = null, actions: List<Pair<NotificationAction, () -> Unit>> = emptyList())
|
||||
abstract fun cancelCallNotification()
|
||||
abstract fun cancelAllNotifications()
|
||||
// Android only
|
||||
abstract fun androidCreateNtfChannelsMaybeShowAlert()
|
||||
|
||||
private suspend fun awaitChatStartedIfNeeded(chatModel: ChatModel, timeout: Long = 30_000) {
|
||||
// Still decrypting database
|
||||
if (chatModel.chatRunning.value == null) {
|
||||
val step = 50L
|
||||
for (i in 0..(timeout / step)) {
|
||||
if (chatModel.chatRunning.value == true || chatModel.onboardingStage.value == OnboardingStage.Step1_SimpleXInfo) {
|
||||
break
|
||||
}
|
||||
delay(step)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideSecrets(cItem: ChatItem): String {
|
||||
val md = cItem.formattedText
|
||||
return if (md != null) {
|
||||
var res = ""
|
||||
for (ft in md) {
|
||||
res += if (ft.format is Format.Secret) "..." else ft.text
|
||||
}
|
||||
res
|
||||
} else {
|
||||
cItem.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ fun showApp() = application {
|
||||
}
|
||||
}
|
||||
}
|
||||
var windowFocused by remember { mutableStateOf(true) }
|
||||
var windowFocused by remember { simplexWindowState.windowFocused }
|
||||
LaunchedEffect(windowFocused) {
|
||||
val delay = ChatController.appPrefs.laLockDelay.get()
|
||||
if (!windowFocused && ChatModel.performLA.value && delay > 0) {
|
||||
@@ -119,6 +119,7 @@ class SimplexWindowState {
|
||||
val openMultipleDialog = DialogState<List<File>>()
|
||||
val saveDialog = DialogState<File?>()
|
||||
val toasts = mutableStateListOf<Pair<String, Long>>()
|
||||
var windowFocused = mutableStateOf(true)
|
||||
}
|
||||
|
||||
data class DialogParams(
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package chat.simplex.common.model
|
||||
|
||||
import androidx.compose.ui.graphics.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.simplexWindowState
|
||||
import chat.simplex.common.views.call.CallMediaType
|
||||
import chat.simplex.common.views.call.RcvCallInvitation
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import com.sshtools.twoslices.*
|
||||
import java.awt.*
|
||||
import java.awt.TrayIcon.MessageType
|
||||
import java.io.File
|
||||
import javax.imageio.ImageIO
|
||||
|
||||
object NtfManager {
|
||||
private val prevNtfs = arrayListOf<Pair<ChatId, Slice>>()
|
||||
|
||||
fun notifyCallInvitation(invitation: RcvCallInvitation) {
|
||||
if (simplexWindowState.windowFocused.value) return
|
||||
val contactId = invitation.contact.id
|
||||
Log.d(TAG, "notifyCallInvitation $contactId")
|
||||
val image = invitation.contact.image
|
||||
val text = generalGetString(
|
||||
if (invitation.callType.media == CallMediaType.Video) {
|
||||
if (invitation.sharedKey == null) MR.strings.video_call_no_encryption else MR.strings.encrypted_video_call
|
||||
} else {
|
||||
if (invitation.sharedKey == null) MR.strings.audio_call_no_encryption else MR.strings.encrypted_audio_call
|
||||
}
|
||||
)
|
||||
val previewMode = appPreferences.notificationPreviewMode.get()
|
||||
val title = if (previewMode == NotificationPreviewMode.HIDDEN.name)
|
||||
generalGetString(MR.strings.notification_preview_somebody)
|
||||
else
|
||||
invitation.contact.displayName
|
||||
val largeIcon = if (image == null || previewMode == NotificationPreviewMode.HIDDEN.name)
|
||||
MR.images.icon_foreground_common.image.toComposeImageBitmap()
|
||||
else
|
||||
base64ToBitmap(image)
|
||||
|
||||
val actions = listOf(
|
||||
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) {
|
||||
ntfManager.openChatAction(invitation.user.userId, contactId)
|
||||
}
|
||||
}
|
||||
|
||||
fun hasNotificationsForChat(chatId: ChatId) = false//prevNtfs.any { it.first == chatId }
|
||||
|
||||
fun cancelNotificationsForChat(chatId: ChatId) {
|
||||
val ntf = prevNtfs.firstOrNull { it.first == chatId }
|
||||
if (ntf != null) {
|
||||
prevNtfs.remove(ntf)
|
||||
/*try {
|
||||
ntf.second.close()
|
||||
} catch (e: Exception) {
|
||||
// Can be java.lang.UnsupportedOperationException, for example. May do nothing
|
||||
println("Failed to close notification: ${e.stackTraceToString()}")
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelAllNotifications() {
|
||||
// prevNtfs.forEach { try { it.second.close() } catch (e: Exception) { println("Failed to close notification: ${e.stackTraceToString()}") } }
|
||||
prevNtfs.clear()
|
||||
}
|
||||
|
||||
fun displayNotification(user: User, chatId: String, displayName: String, msgText: String, image: String?, actions: List<Pair<NotificationAction, () -> Unit>>) {
|
||||
if (!user.showNotifications) return
|
||||
Log.d(TAG, "notifyMessageReceived $chatId")
|
||||
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
|
||||
val largeIcon = when {
|
||||
actions.isEmpty() -> null
|
||||
image == null || previewMode == NotificationPreviewMode.HIDDEN.name -> MR.images.icon_foreground_common.image.toComposeImageBitmap()
|
||||
else -> base64ToBitmap(image)
|
||||
}
|
||||
|
||||
displayNotificationViaLib(chatId, title, content, prepareIconPath(largeIcon), actions.map { it.first.name to it.second }) {
|
||||
ntfManager.openChatAction(user.userId, chatId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayNotificationViaLib(
|
||||
chatId: String,
|
||||
title: String,
|
||||
text: String,
|
||||
iconPath: String?,
|
||||
actions: List<Pair<String, () -> Unit>>,
|
||||
defaultAction: (() -> Unit)?
|
||||
) {
|
||||
val builder = Toast.builder()
|
||||
.title(title)
|
||||
.content(text)
|
||||
if (iconPath != null) {
|
||||
builder.icon(iconPath)
|
||||
}
|
||||
if (defaultAction != null) {
|
||||
builder.defaultAction(defaultAction)
|
||||
}
|
||||
actions.forEach {
|
||||
builder.action(it.first, it.second)
|
||||
}
|
||||
prevNtfs.add(chatId to builder.toast())
|
||||
}
|
||||
|
||||
private fun prepareIconPath(icon: ImageBitmap?): String? = if (icon != null) {
|
||||
tmpDir.mkdir()
|
||||
val newFile = File(tmpDir.absolutePath + File.separator + generateNewFileName("IMG", "png"))
|
||||
try {
|
||||
ImageIO.write(icon.toAwtImage(), "PNG", newFile.outputStream())
|
||||
newFile.absolutePath
|
||||
} catch (e: Exception) {
|
||||
println("Failed to write an icon to tmpDir: ${e.stackTraceToString()}")
|
||||
null
|
||||
}
|
||||
} else null
|
||||
|
||||
private fun displayNotification(title: String, text: String, icon: ImageBitmap?) = when (desktopPlatform) {
|
||||
DesktopPlatform.LINUX_X86_64, DesktopPlatform.LINUX_AARCH64 -> linuxDisplayNotification(title, text, prepareIconPath(icon))
|
||||
DesktopPlatform.WINDOWS_X86_64 -> windowsDisplayNotification(title, text, icon)
|
||||
DesktopPlatform.MAC_X86_64, DesktopPlatform.MAC_AARCH64 -> macDisplayNotification(title, text, prepareIconPath(icon))
|
||||
}
|
||||
|
||||
private fun linuxDisplayNotification(title: String, text: String, iconPath: String?) {
|
||||
if (iconPath != null) {
|
||||
Runtime.getRuntime().exec(arrayOf("notify-send", "-i", iconPath, title, text))
|
||||
} else {
|
||||
Toast.toast(ToastType.INFO, title, text)
|
||||
Runtime.getRuntime().exec(arrayOf("notify-send", title, text))
|
||||
}
|
||||
}
|
||||
|
||||
private fun windowsDisplayNotification(title: String, text: String, icon: ImageBitmap?) {
|
||||
if (SystemTray.isSupported()) {
|
||||
val tray = SystemTray.getSystemTray()
|
||||
tray.remove(tray.trayIcons.firstOrNull { it.toolTip == "SimpleX" })
|
||||
val trayIcon = TrayIcon(icon?.toAwtImage(), "SimpleX")
|
||||
trayIcon.isImageAutoSize = true
|
||||
tray.add(trayIcon)
|
||||
trayIcon.displayMessage(title, text, MessageType.INFO)
|
||||
} else {
|
||||
Log.e(TAG, "System tray not supported!")
|
||||
}
|
||||
}
|
||||
|
||||
private fun macDisplayNotification(title: String, text: String, iconPath: String?) {
|
||||
Runtime.getRuntime().exec(arrayOf("osascript", "-e", """display notification "${text.replace("\"", "\\\"")}" with title "${title.replace("\"", "\\\"")}""""))
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,14 @@ actual val appPlatform = AppPlatform.DESKTOP
|
||||
val defaultLocale: Locale = Locale.getDefault()
|
||||
|
||||
fun initApp() {
|
||||
ntfManager = object : NtfManager() { // LALAL
|
||||
override fun notifyContactConnected(user: User, contact: Contact) {}
|
||||
override fun notifyContactRequestReceived(user: User, cInfo: ChatInfo.ContactRequest) {}
|
||||
override fun notifyMessageReceived(user: User, cInfo: ChatInfo, cItem: ChatItem) {}
|
||||
override fun notifyCallInvitation(invitation: RcvCallInvitation) {}
|
||||
override fun hasNotificationsForChat(chatId: String): Boolean = false
|
||||
override fun cancelNotificationsForChat(chatId: String) {}
|
||||
override fun displayNotification(user: User, chatId: String, displayName: String, msgText: String, image: String?, actions: List<NotificationAction>) {}
|
||||
override fun createNtfChannelsMaybeShowAlert() {}
|
||||
ntfManager = object : NtfManager() {
|
||||
override fun notifyCallInvitation(invitation: RcvCallInvitation) = 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 displayNotification(user: User, 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() {}
|
||||
override fun cancelAllNotifications() {}
|
||||
override fun cancelAllNotifications() = chat.simplex.common.model.NtfManager.cancelAllNotifications()
|
||||
}
|
||||
applyAppLocale()
|
||||
withBGApi {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import chat.simplex.common.model.NotificationsMode
|
||||
import chat.simplex.common.simplexWindowState
|
||||
|
||||
actual fun allowedToShowNotification(): Boolean = true
|
||||
actual fun allowedToShowNotification(): Boolean = !simplexWindowState.windowFocused.value
|
||||
|
||||
Reference in New Issue
Block a user