mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-05-12 16:55:02 -04:00
feat(auto): append outgoing reply to MessagingStyle for brief confirmation
Before cancelling a conversation notification in response to an inline reply, post one final update that appends the outgoing text to the MessagingStyle history, attributed to the local user. This gives assistants such as Android Auto a tick to observe the sent message in the notification's message history and surface a 'reply sent' style confirmation before markConversationRead cancels the notification. Extract the 'me' Person construction into buildMePerson() and share it between showGroupSummary and createConversationNotification. The conversation builder now optionally takes an extraOutgoingMessage which is appended to the MessagingStyle (actions and when-timestamp continue to be anchored on the last incoming message). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -76,6 +76,8 @@ class FakeMeshServiceNotifications : MeshServiceNotifications {
|
||||
|
||||
override suspend fun markConversationRead(contactKey: String) {}
|
||||
|
||||
override suspend fun appendOutgoingMessage(contactKey: String, text: String) {}
|
||||
|
||||
override fun cancelLowBatteryNotification(node: Node) {}
|
||||
|
||||
override fun clearClientNotification(notification: ClientNotification) {}
|
||||
|
||||
@@ -74,6 +74,13 @@ interface MeshServiceNotifications {
|
||||
*/
|
||||
suspend fun markConversationRead(contactKey: String)
|
||||
|
||||
/**
|
||||
* Appends an outgoing [text] message attributed to the local user to the currently posted conversation notification
|
||||
* for [contactKey]. Used so that assistants such as Android Auto can briefly observe the reply in the
|
||||
* MessagingStyle history before the notification is cancelled. No-op when there is nothing to update.
|
||||
*/
|
||||
suspend fun appendOutgoingMessage(contactKey: String, text: String)
|
||||
|
||||
fun cancelLowBatteryNotification(node: Node)
|
||||
|
||||
fun clearClientNotification(notification: ClientNotification)
|
||||
|
||||
@@ -440,20 +440,23 @@ class MeshServiceNotificationsImpl(
|
||||
showGroupSummary()
|
||||
}
|
||||
|
||||
private fun buildMePerson(): Person {
|
||||
val ourNode = nodeRepository.value.ourNodeInfo.value
|
||||
val meName = ourNode?.user?.long_name ?: getString(Res.string.you)
|
||||
return Person.Builder()
|
||||
.setName(meName)
|
||||
.setKey(ourNode?.user?.id ?: DataPacket.ID_LOCAL)
|
||||
.apply { ourNode?.let { setIcon(createPersonIcon(meName, it.colors.second, it.colors.first)) } }
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun showGroupSummary() {
|
||||
val activeNotifications =
|
||||
notificationManager.activeNotifications.filter {
|
||||
it.id != SUMMARY_ID && it.notification.group == GROUP_KEY_MESSAGES
|
||||
}
|
||||
|
||||
val ourNode = nodeRepository.value.ourNodeInfo.value
|
||||
val meName = ourNode?.user?.long_name ?: getString(Res.string.you)
|
||||
val me =
|
||||
Person.Builder()
|
||||
.setName(meName)
|
||||
.setKey(ourNode?.user?.id ?: DataPacket.ID_LOCAL)
|
||||
.apply { ourNode?.let { setIcon(createPersonIcon(meName, it.colors.second, it.colors.first)) } }
|
||||
.build()
|
||||
val me = buildMePerson()
|
||||
|
||||
val messagingStyle =
|
||||
NotificationCompat.MessagingStyle(me)
|
||||
@@ -520,6 +523,39 @@ class MeshServiceNotificationsImpl(
|
||||
cancelMessageNotification(contactKey)
|
||||
}
|
||||
|
||||
override suspend fun appendOutgoingMessage(contactKey: String, text: String) {
|
||||
if (text.isEmpty()) return
|
||||
val ourNode = nodeRepository.value.ourNodeInfo.value
|
||||
val history =
|
||||
packetRepository.value
|
||||
.getMessagesFrom(contactKey, includeFiltered = false) { nodeId ->
|
||||
if (nodeId == DataPacket.ID_LOCAL) {
|
||||
ourNode ?: nodeRepository.value.getNode(nodeId)
|
||||
} else {
|
||||
nodeRepository.value.getNode(nodeId ?: "")
|
||||
}
|
||||
}
|
||||
.first()
|
||||
|
||||
val unread = history.filter { !it.read }
|
||||
if (unread.isEmpty()) return
|
||||
val displayHistory = unread.take(MAX_HISTORY_MESSAGES).reversed()
|
||||
|
||||
val dest = if (contactKey.isNotEmpty()) contactKey.substring(1) else contactKey
|
||||
val isBroadcast = dest == DataPacket.ID_BROADCAST
|
||||
|
||||
val notification =
|
||||
createConversationNotification(
|
||||
contactKey = contactKey,
|
||||
isBroadcast = isBroadcast,
|
||||
channelName = null,
|
||||
history = displayHistory,
|
||||
isSilent = true,
|
||||
extraOutgoingMessage = text,
|
||||
)
|
||||
notificationManager.notify(contactKey.hashCode(), notification)
|
||||
}
|
||||
|
||||
override fun cancelLowBatteryNotification(node: Node) = notificationManager.cancel(node.num)
|
||||
|
||||
override fun clearClientNotification(notification: ClientNotification) =
|
||||
@@ -561,6 +597,7 @@ class MeshServiceNotificationsImpl(
|
||||
channelName: String?,
|
||||
history: List<Message>,
|
||||
isSilent: Boolean = false,
|
||||
extraOutgoingMessage: String? = null,
|
||||
): Notification {
|
||||
val type = if (isBroadcast) NotificationType.BroadcastMessage else NotificationType.DirectMessage
|
||||
val builder = commonBuilder(type, createOpenMessageIntent(contactKey))
|
||||
@@ -569,14 +606,7 @@ class MeshServiceNotificationsImpl(
|
||||
builder.setSilent(true)
|
||||
}
|
||||
|
||||
val ourNode = nodeRepository.value.ourNodeInfo.value
|
||||
val meName = ourNode?.user?.long_name ?: getString(Res.string.you)
|
||||
val me =
|
||||
Person.Builder()
|
||||
.setName(meName)
|
||||
.setKey(ourNode?.user?.id ?: DataPacket.ID_LOCAL)
|
||||
.apply { ourNode?.let { setIcon(createPersonIcon(meName, it.colors.second, it.colors.first)) } }
|
||||
.build()
|
||||
val me = buildMePerson()
|
||||
|
||||
val style =
|
||||
NotificationCompat.MessagingStyle(me)
|
||||
@@ -621,6 +651,9 @@ class MeshServiceNotificationsImpl(
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!extraOutgoingMessage.isNullOrEmpty()) {
|
||||
style.addMessage(extraOutgoingMessage, nowMillis, me)
|
||||
}
|
||||
val lastMessage = history.last()
|
||||
|
||||
ensureShortcutForNotification(contactKey, isBroadcast, channelName, lastMessage)
|
||||
|
||||
@@ -65,6 +65,7 @@ class ReplyReceiver :
|
||||
scope.launch {
|
||||
try {
|
||||
sendMessage(message, contactKey)
|
||||
meshServiceNotifications.appendOutgoingMessage(contactKey, message)
|
||||
meshServiceNotifications.markConversationRead(contactKey)
|
||||
} finally {
|
||||
pendingResult.finish()
|
||||
|
||||
@@ -69,6 +69,8 @@ class FakeMeshServiceNotifications : MeshServiceNotifications {
|
||||
|
||||
override suspend fun markConversationRead(contactKey: String) {}
|
||||
|
||||
override suspend fun appendOutgoingMessage(contactKey: String, text: String) {}
|
||||
|
||||
override fun cancelLowBatteryNotification(node: Node) {}
|
||||
|
||||
override fun clearClientNotification(notification: ClientNotification) {}
|
||||
|
||||
@@ -158,6 +158,10 @@ class DesktopMeshServiceNotifications(private val notificationManager: Notificat
|
||||
notificationManager.cancel(contactKey.hashCode())
|
||||
}
|
||||
|
||||
override suspend fun appendOutgoingMessage(contactKey: String, text: String) {
|
||||
// No-op: desktop tray notifications don't carry MessagingStyle history to augment.
|
||||
}
|
||||
|
||||
override fun cancelLowBatteryNotification(node: Node) {
|
||||
notificationManager.cancel(node.num)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user