mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: Add acknowledgement status and retry for emoji reactions (#4142)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
41c5992158
commit
2526728859
11 changed files with 1257 additions and 83 deletions
|
|
@ -28,6 +28,7 @@ import org.meshtastic.core.data.repository.PacketRepository
|
|||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.database.entity.ReactionEntity
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.MessageStatus
|
||||
import org.meshtastic.core.model.Position
|
||||
import org.meshtastic.core.prefs.mesh.MeshPrefs
|
||||
import org.meshtastic.core.service.MeshServiceNotifications
|
||||
|
|
@ -112,11 +113,11 @@ constructor(
|
|||
bytes = action.emoji.encodeToByteArray(),
|
||||
channel = channel,
|
||||
replyId = action.replyId,
|
||||
wantAck = false,
|
||||
wantAck = true,
|
||||
emoji = action.emoji.codePointAt(0),
|
||||
)
|
||||
commandSender.sendData(dataPacket)
|
||||
rememberReaction(action)
|
||||
rememberReaction(action, dataPacket.id)
|
||||
}
|
||||
|
||||
private fun handleImportContact(action: ServiceAction.ImportContact, myNodeNum: Int) {
|
||||
|
|
@ -125,7 +126,7 @@ constructor(
|
|||
nodeManager.handleReceivedUser(verifiedContact.nodeNum, verifiedContact.user, manuallyVerified = true)
|
||||
}
|
||||
|
||||
private fun rememberReaction(action: ServiceAction.Reaction) {
|
||||
private fun rememberReaction(action: ServiceAction.Reaction, packetId: Int) {
|
||||
scope.handledLaunch {
|
||||
val reaction =
|
||||
ReactionEntity(
|
||||
|
|
@ -136,6 +137,8 @@ constructor(
|
|||
snr = 0f,
|
||||
rssi = 0,
|
||||
hopsAway = 0,
|
||||
packetId = packetId,
|
||||
status = MessageStatus.QUEUED,
|
||||
)
|
||||
packetRepository.get().insertReaction(reaction)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,10 +333,13 @@ constructor(
|
|||
packetHandler.removeResponse(packet.decoded.requestId, complete = true)
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod", "LongMethod")
|
||||
private fun handleAckNak(requestId: Int, fromId: String, routingError: Int, relayNode: Int?) {
|
||||
scope.handledLaunch {
|
||||
val isAck = routingError == MeshProtos.Routing.Error.NONE_VALUE
|
||||
val p = packetRepository.get().getPacketById(requestId)
|
||||
val reaction = packetRepository.get().getReactionByPacketId(requestId)
|
||||
|
||||
val isMaxRetransmit = routingError == MeshProtos.Routing.Error.MAX_RETRANSMIT_VALUE
|
||||
val shouldRetry =
|
||||
isMaxRetransmit &&
|
||||
|
|
@ -345,14 +348,22 @@ constructor(
|
|||
p.data.from == DataPacket.ID_LOCAL &&
|
||||
p.data.retryCount < MAX_RETRY_ATTEMPTS
|
||||
|
||||
val shouldRetryReaction =
|
||||
isMaxRetransmit &&
|
||||
reaction != null &&
|
||||
reaction.userId == DataPacket.ID_LOCAL &&
|
||||
reaction.retryCount < MAX_RETRY_ATTEMPTS &&
|
||||
reaction.to != null
|
||||
@Suppress("MaxLineLength")
|
||||
Logger.d {
|
||||
val retryInfo = "packetId=${p?.packetId} dataId=${p?.data?.id} retry=${p?.data?.retryCount}"
|
||||
val statusInfo = "status=${p?.data?.status}"
|
||||
val retryInfo =
|
||||
"packetId=${p?.packetId ?: reaction?.packetId} dataId=${p?.data?.id} retry=${p?.data?.retryCount ?: reaction?.retryCount}"
|
||||
val statusInfo = "status=${p?.data?.status ?: reaction?.status}"
|
||||
"[ackNak] req=$requestId routeErr=$routingError isAck=$isAck " +
|
||||
"maxRetransmit=$isMaxRetransmit shouldRetry=$shouldRetry $retryInfo $statusInfo"
|
||||
"maxRetransmit=$isMaxRetransmit shouldRetry=$shouldRetry reaction=$shouldRetryReaction $retryInfo $statusInfo"
|
||||
}
|
||||
|
||||
if (shouldRetry && p != null) {
|
||||
if (shouldRetry) {
|
||||
val newRetryCount = p.data.retryCount + 1
|
||||
val newId = commandSender.generatePacketId()
|
||||
val updatedData =
|
||||
|
|
@ -368,21 +379,66 @@ constructor(
|
|||
return@handledLaunch
|
||||
}
|
||||
|
||||
if (shouldRetryReaction && reaction != null) {
|
||||
val newRetryCount = reaction.retryCount + 1
|
||||
val newId = commandSender.generatePacketId()
|
||||
|
||||
val reactionPacket =
|
||||
DataPacket(
|
||||
to = reaction.to,
|
||||
channel = reaction.channel,
|
||||
bytes = reaction.emoji.toByteArray(Charsets.UTF_8),
|
||||
dataType = Portnums.PortNum.TEXT_MESSAGE_APP_VALUE,
|
||||
replyId = reaction.replyId,
|
||||
wantAck = true,
|
||||
emoji = reaction.emoji.codePointAt(0),
|
||||
id = newId,
|
||||
retryCount = newRetryCount,
|
||||
)
|
||||
|
||||
val updatedReaction =
|
||||
reaction.copy(
|
||||
packetId = newId,
|
||||
status = MessageStatus.QUEUED,
|
||||
retryCount = newRetryCount,
|
||||
relayNode = null,
|
||||
routingError = MeshProtos.Routing.Error.NONE_VALUE,
|
||||
)
|
||||
packetRepository.get().updateReaction(updatedReaction)
|
||||
|
||||
Logger.w { "[ackNak] retrying reaction req=$requestId newId=$newId retry=$newRetryCount" }
|
||||
|
||||
delay(RETRY_DELAY_MS)
|
||||
commandSender.sendData(reactionPacket)
|
||||
return@handledLaunch
|
||||
}
|
||||
|
||||
val m =
|
||||
when {
|
||||
isAck && fromId == p?.data?.to -> MessageStatus.RECEIVED
|
||||
isAck && (fromId == p?.data?.to || fromId == reaction?.to) -> MessageStatus.RECEIVED
|
||||
isAck -> MessageStatus.DELIVERED
|
||||
else -> MessageStatus.ERROR
|
||||
}
|
||||
if (p != null && p.data.status != MessageStatus.RECEIVED) {
|
||||
p.data.status = m
|
||||
p.routingError = routingError
|
||||
p.data.relayNode = relayNode
|
||||
if (isAck) {
|
||||
p.data.relays += 1
|
||||
}
|
||||
p.data.relayNode = relayNode
|
||||
packetRepository.get().update(p)
|
||||
}
|
||||
|
||||
reaction?.let { r ->
|
||||
if (r.status != MessageStatus.RECEIVED) {
|
||||
var updated = r.copy(status = m, routingError = routingError, relayNode = relayNode)
|
||||
if (isAck) {
|
||||
updated = updated.copy(relays = updated.relays + 1)
|
||||
}
|
||||
packetRepository.get().updateReaction(updated)
|
||||
}
|
||||
}
|
||||
|
||||
serviceBroadcasts.broadcastMessageStatus(requestId, m)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.data.repository.PacketRepository
|
||||
import org.meshtastic.core.database.entity.ReactionEntity
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.service.MeshServiceNotifications
|
||||
import org.meshtastic.proto.Portnums
|
||||
|
|
@ -35,6 +37,8 @@ class ReactionReceiver : BroadcastReceiver() {
|
|||
|
||||
@Inject lateinit var meshServiceNotifications: MeshServiceNotifications
|
||||
|
||||
@Inject lateinit var packetRepository: PacketRepository
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
companion object {
|
||||
|
|
@ -71,10 +75,24 @@ class ReactionReceiver : BroadcastReceiver() {
|
|||
bytes = emoji.toByteArray(Charsets.UTF_8),
|
||||
dataType = Portnums.PortNum.TEXT_MESSAGE_APP_VALUE,
|
||||
replyId = packetId,
|
||||
wantAck = true,
|
||||
emoji = emoji.codePointAt(0),
|
||||
)
|
||||
commandSender.sendData(reactionPacket)
|
||||
|
||||
val reaction =
|
||||
ReactionEntity(
|
||||
replyId = packetId,
|
||||
userId = DataPacket.ID_LOCAL,
|
||||
emoji = emoji,
|
||||
timestamp = System.currentTimeMillis(),
|
||||
packetId = reactionPacket.id,
|
||||
status = org.meshtastic.core.model.MessageStatus.QUEUED,
|
||||
to = toId,
|
||||
channel = channelIndex,
|
||||
)
|
||||
packetRepository.insertReaction(reaction)
|
||||
|
||||
// Dismiss the notification after reacting
|
||||
meshServiceNotifications.cancelMessageNotification(contactKey)
|
||||
} finally {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue