From d2e59b2741d62eff637ffb7261d6e3d61191d15d Mon Sep 17 00:00:00 2001 From: M57 Date: Sun, 21 Jun 2026 16:40:30 +0300 Subject: [PATCH 1/7] fix(#214): notification tap now navigates to correct session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: MainActivity had no onNewIntent override. When the app was backgrounded and a notification was tapped, Android brought the existing task to the foreground and delivered the intent via onNewIntent — but since it wasn't overridden, the session ID from the notification was silently dropped and the user saw the last-active session. Fix: 1. Changed notificationSessionId from a local val to a Compose mutableStateOf so it's observable across recompositions. 2. Added onNewIntent override that reads the session ID from the incoming intent and updates the observable state. 3. Added FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP to the notification PendingIntent for reliable task management. --- .../java/com/m57/hermescontrol/MainActivity.kt | 16 +++++++++++++++- .../notification/ChatNotificationService.kt | 8 +++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/m57/hermescontrol/MainActivity.kt b/app/src/main/java/com/m57/hermescontrol/MainActivity.kt index 004014da..f1d717ba 100644 --- a/app/src/main/java/com/m57/hermescontrol/MainActivity.kt +++ b/app/src/main/java/com/m57/hermescontrol/MainActivity.kt @@ -1,5 +1,6 @@ package com.m57.hermescontrol +import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -9,17 +10,21 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier import com.m57.hermescontrol.data.local.AuthManager import com.m57.hermescontrol.notification.NotificationReplyReceiver import com.m57.hermescontrol.theme.HermesControlTheme class MainActivity : ComponentActivity() { + // Observable state so both onCreate and onNewIntent updates flow to Compose + private var notificationSessionId by mutableStateOf(null) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Read notification tap sessionId (if any) from the intent that launched us - val notificationSessionId = intent?.getStringExtra(NotificationReplyReceiver.EXTRA_SESSION_ID) + notificationSessionId = intent?.getStringExtra(NotificationReplyReceiver.EXTRA_SESSION_ID) enableEdgeToEdge() setContent { @@ -40,4 +45,13 @@ class MainActivity : ComponentActivity() { } } } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + // When a notification tap arrives while the app is already running + // (task exists in background), Android delivers the intent here instead + // of onCreate. Update the observable state so Compose recomposes + // and ChatScreen's LaunchedEffect switches to the correct session. + notificationSessionId = intent.getStringExtra(NotificationReplyReceiver.EXTRA_SESSION_ID) + } } diff --git a/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt b/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt index 24fa848f..d0092110 100644 --- a/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt +++ b/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt @@ -153,9 +153,11 @@ class ChatNotificationService : Service() { } private fun buildContentIntent(sessionId: String?): PendingIntent { - val intent = Intent(this, MainActivity::class.java) - if (!sessionId.isNullOrBlank()) { - intent.putExtra(NotificationReplyReceiver.EXTRA_SESSION_ID, sessionId) + val intent = Intent(this, MainActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + if (!sessionId.isNullOrBlank()) { + putExtra(NotificationReplyReceiver.EXTRA_SESSION_ID, sessionId) + } } return PendingIntent.getActivity( this, From 37fc9d9f17aee93917fc6d77f184ba15c95384d7 Mon Sep 17 00:00:00 2001 From: M57 Date: Sun, 21 Jun 2026 17:11:28 +0300 Subject: [PATCH 2/7] fix: clarify toast - gateway restart required for platform toggle changes --- .../java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt index 90f3ba50..762cf733 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt @@ -62,7 +62,7 @@ class ChannelsViewModel : ViewModel() { } when (result) { is NetworkResult.Success -> { - _uiState.update { it.copy(isLoading = false, toastMessage = "$platformId configured successfully") } + _uiState.update { it.copy(isLoading = false, toastMessage = "$platformId configured successfully — restart the gateway for changes to take effect") } loadPlatforms() } From ab92b31d2e63d15d79cc2612f6d4833a37f2a6f9 Mon Sep 17 00:00:00 2001 From: M57 Date: Sun, 21 Jun 2026 17:16:05 +0300 Subject: [PATCH 3/7] fix: ktlint wrapping violations on toast message and intent builder --- .../notification/ChatNotificationService.kt | 11 ++++++----- .../hermescontrol/ui/channels/ChannelsViewModel.kt | 7 ++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt b/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt index d0092110..f76723d4 100644 --- a/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt +++ b/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt @@ -153,11 +153,12 @@ class ChatNotificationService : Service() { } private fun buildContentIntent(sessionId: String?): PendingIntent { - val intent = Intent(this, MainActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP - if (!sessionId.isNullOrBlank()) { - putExtra(NotificationReplyReceiver.EXTRA_SESSION_ID, sessionId) - } + val intent = Intent(this, MainActivity::class.java) + .apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + if (!sessionId.isNullOrBlank()) { + putExtra(NotificationReplyReceiver.EXTRA_SESSION_ID, sessionId) + } } return PendingIntent.getActivity( this, diff --git a/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt index 762cf733..25884f13 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt @@ -62,7 +62,12 @@ class ChannelsViewModel : ViewModel() { } when (result) { is NetworkResult.Success -> { - _uiState.update { it.copy(isLoading = false, toastMessage = "$platformId configured successfully — restart the gateway for changes to take effect") } + _uiState.update { + it.copy( + isLoading = false, + toastMessage = "$platformId configured successfully — restart the gateway for changes to take effect", + ) + } loadPlatforms() } From fea7820740682b078beab4fb4004e6c5647d8360 Mon Sep 17 00:00:00 2001 From: M57 Date: Sun, 21 Jun 2026 17:18:59 +0300 Subject: [PATCH 4/7] fix: extract long toast string to val, fix indentation --- .../m57/hermescontrol/notification/ChatNotificationService.kt | 2 +- .../com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt b/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt index f76723d4..77f1aa7e 100644 --- a/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt +++ b/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt @@ -159,7 +159,7 @@ class ChatNotificationService : Service() { if (!sessionId.isNullOrBlank()) { putExtra(NotificationReplyReceiver.EXTRA_SESSION_ID, sessionId) } - } + } return PendingIntent.getActivity( this, sessionId?.hashCode() ?: 0, diff --git a/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt index 25884f13..020fceae 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/channels/ChannelsViewModel.kt @@ -62,10 +62,11 @@ class ChannelsViewModel : ViewModel() { } when (result) { is NetworkResult.Success -> { + val message = "$platformId configured successfully — restart the gateway for changes to take effect" _uiState.update { it.copy( isLoading = false, - toastMessage = "$platformId configured successfully — restart the gateway for changes to take effect", + toastMessage = message, ) } loadPlatforms() From 21744fd95143f7578963a257f81aa0195b828e04 Mon Sep 17 00:00:00 2001 From: M57 Date: Sun, 21 Jun 2026 17:22:35 +0300 Subject: [PATCH 5/7] fix: replace .apply block with direct property/extra calls for ktlint --- .../notification/ChatNotificationService.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt b/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt index 77f1aa7e..07c578ea 100644 --- a/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt +++ b/app/src/main/java/com/m57/hermescontrol/notification/ChatNotificationService.kt @@ -154,12 +154,10 @@ class ChatNotificationService : Service() { private fun buildContentIntent(sessionId: String?): PendingIntent { val intent = Intent(this, MainActivity::class.java) - .apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP - if (!sessionId.isNullOrBlank()) { - putExtra(NotificationReplyReceiver.EXTRA_SESSION_ID, sessionId) - } - } + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP + if (!sessionId.isNullOrBlank()) { + intent.putExtra(NotificationReplyReceiver.EXTRA_SESSION_ID, sessionId) + } return PendingIntent.getActivity( this, sessionId?.hashCode() ?: 0, From 59f3af0a555d148191643281b6648550edce2f81 Mon Sep 17 00:00:00 2001 From: M57 Date: Sun, 21 Jun 2026 17:25:54 +0300 Subject: [PATCH 6/7] fix: add missing setValue import for mutableStateOf delegate --- app/src/main/java/com/m57/hermescontrol/MainActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/m57/hermescontrol/MainActivity.kt b/app/src/main/java/com/m57/hermescontrol/MainActivity.kt index f1d717ba..98b98466 100644 --- a/app/src/main/java/com/m57/hermescontrol/MainActivity.kt +++ b/app/src/main/java/com/m57/hermescontrol/MainActivity.kt @@ -11,6 +11,7 @@ import androidx.compose.material3.Surface import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import com.m57.hermescontrol.data.local.AuthManager import com.m57.hermescontrol.notification.NotificationReplyReceiver From f68841c2a7d1832136fc4b4d713b6a668f2d80a9 Mon Sep 17 00:00:00 2001 From: M57 Date: Sun, 21 Jun 2026 17:38:02 +0300 Subject: [PATCH 7/7] fix(#214): update test assertion to match full success message --- .../test/java/com/m57/hermescontrol/E2eIntegrationTest.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/test/java/com/m57/hermescontrol/E2eIntegrationTest.kt b/app/src/test/java/com/m57/hermescontrol/E2eIntegrationTest.kt index 5dd78581..01d612fb 100644 --- a/app/src/test/java/com/m57/hermescontrol/E2eIntegrationTest.kt +++ b/app/src/test/java/com/m57/hermescontrol/E2eIntegrationTest.kt @@ -915,7 +915,10 @@ class E2eIntegrationTest { viewModel.configurePlatform("telegram", mapOf("bot_token" to "xyz")) advanceUntilIdle() - assertEquals("telegram configured successfully", viewModel.uiState.value.toastMessage) + assertEquals( + "telegram configured successfully — restart the gateway for changes to take effect", + viewModel.uiState.value.toastMessage, + ) } @Test