From 31eabf07e4d66dfd12d289867dd95620049147b9 Mon Sep 17 00:00:00 2001 From: IanRDavies Date: Tue, 12 Apr 2022 11:55:18 +0100 Subject: [PATCH 1/2] android: notifications improvements (to stable) (#522) * add intent to grouped notifications * clear overlays on open from ntf * cancel notifications alongside unmarked markers * tidy up --- .../java/chat/simplex/app/MainActivity.kt | 5 ++++ .../java/chat/simplex/app/model/ChatModel.kt | 4 +++ .../java/chat/simplex/app/model/NtfManager.kt | 28 ++++++++++++++++++- .../java/chat/simplex/app/model/SimpleXAPI.kt | 6 +++- .../chat/simplex/app/views/chat/ChatView.kt | 1 + .../app/views/chatlist/ChatListView.kt | 6 ++++ .../simplex/app/views/helpers/ModalView.kt | 2 +- 7 files changed, 49 insertions(+), 3 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index b32b2bb276..2435f012bd 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -100,9 +100,14 @@ fun processIntent(intent: Intent?, chatModel: ChatModel) { Log.d(TAG, "processIntent: OpenChatAction $chatId") if (chatId != null) { val cInfo = chatModel.getChat(chatId)?.chatInfo + chatModel.clearOverlays.value = true if (cInfo != null) withApi { openChat(chatModel, cInfo) } } } + NtfManager.ShowChatsAction -> { + Log.d(TAG, "processIntent: ShowChatsAction") + chatModel.clearOverlays.value = true + } "android.intent.action.VIEW" -> { val uri = intent.data if (uri != null) connectIfOpenedViaUri(uri, chatModel) diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt index 3eb4ddedfa..cbf3880903 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt @@ -27,6 +27,10 @@ class ChatModel(val controller: ChatController) { var terminalItems = mutableStateListOf() var userAddress = mutableStateOf(null) var userSMPServers = mutableStateOf<(List)?>(null) + + // set when app opened from external intent + var clearOverlays = mutableStateOf(false) + // set when app is opened via contact or invitation URI var appOpenUrl = mutableStateOf(null) var runServiceInBackground = mutableStateOf(true) diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt b/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt index d95da55ccc..2d83c79d50 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt @@ -14,6 +14,7 @@ class NtfManager(val context: Context) { const val MessageChannel: String = "chat.simplex.app.MESSAGE_NOTIFICATION" const val MessageGroup: String = "chat.simplex.app.MESSAGE_NOTIFICATION" const val OpenChatAction: String = "chat.simplex.app.OPEN_CHAT" + const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS" } private val manager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -28,6 +29,18 @@ class NtfManager(val context: Context) { )) } + fun cancelNotificationsForChat(chatId: String) { + prevNtfTime.remove(chatId) + manager.cancel(chatId.hashCode()) + val msgNtfs = manager.activeNotifications.filter { + ntf -> ntf.notification.channelId == MessageChannel + } + if (msgNtfs.count() == 1) { + // Have a group notification with no children so cancel it + manager.cancel(0) + } + } + fun notifyMessageReceived(cInfo: ChatInfo, cItem: ChatItem) { Log.d(TAG, "notifyMessageReceived ${cInfo.id}") val now = Clock.System.now().toEpochMilliseconds() @@ -53,6 +66,7 @@ class NtfManager(val context: Context) { .setGroup(MessageGroup) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN) .setGroupSummary(true) + .setContentIntent(getSummaryNtfIntent()) .build() with(NotificationManagerCompat.from(context)) { @@ -62,7 +76,7 @@ class NtfManager(val context: Context) { } } - private fun hideSecrets(cItem: ChatItem): String { + private fun hideSecrets(cItem: ChatItem) : String { val md = cItem.formattedText return if (md == null) { cItem.content.text @@ -87,4 +101,16 @@ class NtfManager(val context: Context) { getPendingIntent(uniqueInt, PendingIntent.FLAG_IMMUTABLE) } } + + private fun getSummaryNtfIntent() : PendingIntent{ + Log.d(TAG, "getSummaryNtfIntent") + val uniqueInt = (System.currentTimeMillis() and 0xfffffff).toInt() + val intent = Intent(context, MainActivity::class.java) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP) + .setAction(ShowChatsAction) + return TaskStackBuilder.create(context).run { + addNextIntentWithParentStack(intent) + getPendingIntent(uniqueInt, PendingIntent.FLAG_IMMUTABLE) + } + } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index 53968f7719..1872dbb72f 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -30,7 +30,7 @@ typealias ChatCtrl = Long open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: NtfManager, val appContext: Context) { var chatModel = ChatModel(this) - private val sharedPreferences: SharedPreferences = appContext.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE) + private val sharedPreferences: SharedPreferences = appContext.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE) init { chatModel.runServiceInBackground.value = getRunServiceInBackground() @@ -73,6 +73,10 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt return false } + fun cancelNotificationsForChat(chatId: String) { + ntfManager.cancelNotificationsForChat(chatId) + } + suspend fun sendCmd(cmd: CC): CR { return withContext(Dispatchers.IO) { val c = cmd.cmdString diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt index 02b8ab1da6..ef7aea17d7 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt @@ -52,6 +52,7 @@ fun ChatView(chatModel: ChatModel) { delay(1000L) if (chat.chatItems.count() > 0) { chatModel.markChatItemsRead(chat.chatInfo) + chatModel.controller.cancelNotificationsForChat(chat.id) withApi { chatModel.controller.apiChatRead( chat.chatInfo.chatType, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt index 44017bc904..1f1c6b912e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt @@ -18,6 +18,7 @@ import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.ToolbarDark import chat.simplex.app.ui.theme.ToolbarLight import chat.simplex.app.views.chat.ChatHelpView +import chat.simplex.app.views.newchat.ModalManager import chat.simplex.app.views.newchat.NewChatSheet import chat.simplex.app.views.usersettings.SettingsView import kotlinx.coroutines.CoroutineScope @@ -63,6 +64,11 @@ fun scaffoldController(): ScaffoldController { @Composable fun ChatListView(chatModel: ChatModel) { val scaffoldCtrl = scaffoldController() + if (chatModel.clearOverlays.value) { + scaffoldCtrl.collapse() + ModalManager.shared.closeModal() + chatModel.clearOverlays.value = false + } BottomSheetScaffold( scaffoldState = scaffoldCtrl.state, drawerContent = { SettingsView(chatModel) }, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt index bb10bd639f..298867bc8e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt @@ -43,7 +43,7 @@ class ModalManager { } fun closeModal() { - if (!modalViews.isEmpty()) { + if (modalViews.isNotEmpty()) { modalViews.removeAt(modalViews.count() - 1) } modalCount.value = modalViews.count() From 34c513adeb639bbb8d11fd4b08039dfc03b39bca Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Tue, 12 Apr 2022 12:24:34 +0100 Subject: [PATCH 2/2] core: update simplexmq (fixes SMP END from disconnected clients removing active subscriptions) (#523) --- cabal.project | 2 +- sha256map.nix | 2 +- stack.yaml | 2 +- tests/MobileTests.hs | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cabal.project b/cabal.project index b30676122e..a7af3451fd 100644 --- a/cabal.project +++ b/cabal.project @@ -3,7 +3,7 @@ packages: . source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 3ba1926b1e5ab32451a2239831d614492d40c9be + tag: d38303d5f1d55a90dd95d951ba8d798e17699269 source-repository-package type: git diff --git a/sha256map.nix b/sha256map.nix index 77f2287dd2..63e6c1a0bb 100644 --- a/sha256map.nix +++ b/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."3ba1926b1e5ab32451a2239831d614492d40c9be" = "1rgadib3xjzi81i1xda55gv1mdaq8vvyyh65qg482rnyh6kq9f6g"; + "https://github.com/simplex-chat/simplexmq.git"."d38303d5f1d55a90dd95d951ba8d798e17699269" = "0gx9wlk0v8ml6aid9hzm1nqy357plk8sbnhfcnchna12dgm8v0sd"; "https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp"; "https://github.com/simplex-chat/haskell-terminal.git"."f708b00009b54890172068f168bf98508ffcd495" = "0zmq7lmfsk8m340g47g5963yba7i88n4afa6z93sg9px5jv1mijj"; "https://github.com/zw3rk/android-support.git"."3c3a5ab0b8b137a072c98d3d0937cbdc96918ddb" = "1r6jyxbim3dsvrmakqfyxbd6ms6miaghpbwyl0sr6dzwpgaprz97"; diff --git a/stack.yaml b/stack.yaml index b9c297539d..bd9c1fda85 100644 --- a/stack.yaml +++ b/stack.yaml @@ -49,7 +49,7 @@ extra-deps: # - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 3ba1926b1e5ab32451a2239831d614492d40c9be + commit: d38303d5f1d55a90dd95d951ba8d798e17699269 # - terminal-0.2.0.0@sha256:de6770ecaae3197c66ac1f0db5a80cf5a5b1d3b64a66a05b50f442de5ad39570,2977 - github: simplex-chat/aeson commit: 3eb66f9a68f103b5f1489382aad89f5712a64db7 diff --git a/tests/MobileTests.hs b/tests/MobileTests.hs index ce9add0e8a..0ca78fa798 100644 --- a/tests/MobileTests.hs +++ b/tests/MobileTests.hs @@ -16,28 +16,28 @@ mobileTests = do it "start new chat with existing user" testChatApi noActiveUser :: String -#if defined(darwin_HOST_OS) +#if defined(darwin_HOST_OS) && defined(swiftJSON) noActiveUser = "{\"resp\":{\"chatCmdError\":{\"chatError\":{\"error\":{\"errorType\":{\"noActiveUser\":{}}}}}}}" #else noActiveUser = "{\"resp\":{\"type\":\"chatCmdError\",\"chatError\":{\"type\":\"error\",\"errorType\":{\"type\":\"noActiveUser\"}}}}" #endif activeUserExists :: String -#if defined(darwin_HOST_OS) +#if defined(darwin_HOST_OS) && defined(swiftJSON) activeUserExists = "{\"resp\":{\"chatCmdError\":{\"chatError\":{\"error\":{\"errorType\":{\"activeUserExists\":{}}}}}}}" #else activeUserExists = "{\"resp\":{\"type\":\"chatCmdError\",\"chatError\":{\"type\":\"error\",\"errorType\":{\"type\":\"activeUserExists\"}}}}" #endif activeUser :: String -#if defined(darwin_HOST_OS) +#if defined(darwin_HOST_OS) && defined(swiftJSON) activeUser = "{\"resp\":{\"activeUser\":{\"user\":{\"userId\":1,\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"displayName\":\"alice\",\"fullName\":\"Alice\"},\"activeUser\":true}}}}" #else activeUser = "{\"resp\":{\"type\":\"activeUser\",\"user\":{\"userId\":1,\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"displayName\":\"alice\",\"fullName\":\"Alice\"},\"activeUser\":true}}}" #endif chatStarted :: String -#if defined(darwin_HOST_OS) +#if defined(darwin_HOST_OS) && defined(swiftJSON) chatStarted = "{\"resp\":{\"chatStarted\":{}}}" #else chatStarted = "{\"resp\":{\"type\":\"chatStarted\"}}"