mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: Send emoji codepoint in reaction packets (#4123)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
5b1693aa04
commit
c9259c793f
6 changed files with 85 additions and 56 deletions
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.repository.network
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
|
|
@ -32,6 +31,7 @@ import kotlinx.coroutines.flow.callbackFlow
|
|||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
|
@ -89,30 +89,37 @@ private fun NsdManager.discoverServices(
|
|||
@SuppressLint("NewApi")
|
||||
private suspend fun NsdManager.resolveService(serviceInfo: NsdServiceInfo): NsdServiceInfo? =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
val isResumed = AtomicBoolean(false)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
val callback =
|
||||
object : NsdManager.ServiceInfoCallback {
|
||||
override fun onServiceInfoCallbackRegistrationFailed(errorCode: Int) {
|
||||
continuation.resume(null)
|
||||
if (isResumed.compareAndSet(false, true)) {
|
||||
continuation.resume(null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceUpdated(updatedServiceInfo: NsdServiceInfo) {
|
||||
if (updatedServiceInfo.hostAddresses.isNotEmpty()) {
|
||||
continuation.resume(updatedServiceInfo)
|
||||
try {
|
||||
unregisterServiceInfoCallback(this)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Logger.w(e) { "Already unregistered" }
|
||||
if (isResumed.compareAndSet(false, true)) {
|
||||
continuation.resume(updatedServiceInfo)
|
||||
try {
|
||||
unregisterServiceInfoCallback(this)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Logger.w(e) { "Already unregistered" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceLost() {
|
||||
continuation.resume(null)
|
||||
try {
|
||||
unregisterServiceInfoCallback(this)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Logger.w(e) { "Already unregistered" }
|
||||
if (isResumed.compareAndSet(false, true)) {
|
||||
continuation.resume(null)
|
||||
try {
|
||||
unregisterServiceInfoCallback(this)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Logger.w(e) { "Already unregistered" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,11 +139,15 @@ private suspend fun NsdManager.resolveService(serviceInfo: NsdServiceInfo): NsdS
|
|||
val listener =
|
||||
object : NsdManager.ResolveListener {
|
||||
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
|
||||
continuation.resume(null)
|
||||
if (isResumed.compareAndSet(false, true)) {
|
||||
continuation.resume(null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
|
||||
continuation.resume(serviceInfo)
|
||||
if (isResumed.compareAndSet(false, true)) {
|
||||
continuation.resume(serviceInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@Suppress("DEPRECATION")
|
||||
|
|
|
|||
|
|
@ -71,44 +71,10 @@ constructor(
|
|||
ignoreException {
|
||||
val myNodeNum = nodeManager.myNodeNum ?: return@ignoreException
|
||||
when (action) {
|
||||
is ServiceAction.Favorite -> {
|
||||
val node = action.node
|
||||
commandSender.sendAdmin(myNodeNum) {
|
||||
if (node.isFavorite) removeFavoriteNode = node.num else setFavoriteNode = node.num
|
||||
}
|
||||
nodeManager.updateNodeInfo(node.num) { it.isFavorite = !node.isFavorite }
|
||||
}
|
||||
is ServiceAction.Ignore -> {
|
||||
val node = action.node
|
||||
commandSender.sendAdmin(myNodeNum) {
|
||||
if (node.isIgnored) removeIgnoredNode = node.num else setIgnoredNode = node.num
|
||||
}
|
||||
nodeManager.updateNodeInfo(node.num) { it.isIgnored = !node.isIgnored }
|
||||
}
|
||||
is ServiceAction.Reaction -> {
|
||||
val channel = action.contactKey[0].digitToInt()
|
||||
val destId = action.contactKey.substring(1)
|
||||
val dataPacket =
|
||||
org.meshtastic.core.model.DataPacket(
|
||||
to = destId,
|
||||
dataType = Portnums.PortNum.TEXT_MESSAGE_APP_VALUE,
|
||||
bytes = action.emoji.encodeToByteArray(),
|
||||
channel = channel,
|
||||
replyId = action.replyId,
|
||||
wantAck = false,
|
||||
)
|
||||
commandSender.sendData(dataPacket)
|
||||
rememberReaction(action)
|
||||
}
|
||||
is ServiceAction.ImportContact -> {
|
||||
val verifiedContact = action.contact.toBuilder().setManuallyVerified(true).build()
|
||||
commandSender.sendAdmin(myNodeNum) { addContact = verifiedContact }
|
||||
nodeManager.handleReceivedUser(
|
||||
verifiedContact.nodeNum,
|
||||
verifiedContact.user,
|
||||
manuallyVerified = true,
|
||||
)
|
||||
}
|
||||
is ServiceAction.Favorite -> handleFavorite(action, myNodeNum)
|
||||
is ServiceAction.Ignore -> handleIgnore(action, myNodeNum)
|
||||
is ServiceAction.Reaction -> handleReaction(action)
|
||||
is ServiceAction.ImportContact -> handleImportContact(action, myNodeNum)
|
||||
is ServiceAction.SendContact -> {
|
||||
commandSender.sendAdmin(myNodeNum) { addContact = action.contact }
|
||||
}
|
||||
|
|
@ -119,6 +85,45 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleFavorite(action: ServiceAction.Favorite, myNodeNum: Int) {
|
||||
val node = action.node
|
||||
commandSender.sendAdmin(myNodeNum) {
|
||||
if (node.isFavorite) removeFavoriteNode = node.num else setFavoriteNode = node.num
|
||||
}
|
||||
nodeManager.updateNodeInfo(node.num) { it.isFavorite = !node.isFavorite }
|
||||
}
|
||||
|
||||
private fun handleIgnore(action: ServiceAction.Ignore, myNodeNum: Int) {
|
||||
val node = action.node
|
||||
commandSender.sendAdmin(myNodeNum) {
|
||||
if (node.isIgnored) removeIgnoredNode = node.num else setIgnoredNode = node.num
|
||||
}
|
||||
nodeManager.updateNodeInfo(node.num) { it.isIgnored = !node.isIgnored }
|
||||
}
|
||||
|
||||
private fun handleReaction(action: ServiceAction.Reaction) {
|
||||
val channel = action.contactKey[0].digitToInt()
|
||||
val destId = action.contactKey.substring(1)
|
||||
val dataPacket =
|
||||
org.meshtastic.core.model.DataPacket(
|
||||
to = destId,
|
||||
dataType = Portnums.PortNum.TEXT_MESSAGE_APP_VALUE,
|
||||
bytes = action.emoji.encodeToByteArray(),
|
||||
channel = channel,
|
||||
replyId = action.replyId,
|
||||
wantAck = false,
|
||||
emoji = action.emoji.codePointAt(0),
|
||||
)
|
||||
commandSender.sendData(dataPacket)
|
||||
rememberReaction(action)
|
||||
}
|
||||
|
||||
private fun handleImportContact(action: ServiceAction.ImportContact, myNodeNum: Int) {
|
||||
val verifiedContact = action.contact.toBuilder().setManuallyVerified(true).build()
|
||||
commandSender.sendAdmin(myNodeNum) { addContact = verifiedContact }
|
||||
nodeManager.handleReceivedUser(verifiedContact.nodeNum, verifiedContact.user, manuallyVerified = true)
|
||||
}
|
||||
|
||||
private fun rememberReaction(action: ServiceAction.Reaction) {
|
||||
scope.handledLaunch {
|
||||
val reaction =
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ constructor(
|
|||
portnumValue = p.dataType
|
||||
payload = ByteString.copyFrom(p.bytes ?: ByteArray(0))
|
||||
p.replyId?.let { if (it != 0) replyId = it }
|
||||
if (p.emoji != 0) emoji = p.emoji
|
||||
}
|
||||
p.time = System.currentTimeMillis()
|
||||
packetHandler?.sendToRadio(meshPacket)
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class MeshDataMapper @Inject constructor(private val nodeManager: MeshNodeManage
|
|||
replyId = data.replyId,
|
||||
relayNode = packet.relayNode,
|
||||
viaMqtt = packet.viaMqtt,
|
||||
emoji = data.emoji,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue