Merge 371c123910ef9ca4c3bf16063e83fff839cff3e2 into c127eb83ab94c069c32d37530d2faecd381cd2a8

This commit is contained in:
Taksh Kothari 2026-09-14 09:43:25 +05:30 committed by GitHub
commit f046f3f9f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 108 additions and 6 deletions

View File

@ -852,6 +852,34 @@ class BluetoothMeshService(private val context: Context) : TransportBridgeServic
return reusable
}
/**
* Mesh timeline stays UTF-8 for iOS interop. Channel messages use the shared
* [BitchatMessage] binary envelope so receivers can route by `channel`.
*/
private fun encodePublicOrChannelPayload(
content: String,
mentions: List<String>,
channel: String?
): ByteArray {
if (channel == null) return content.toByteArray(Charsets.UTF_8)
val nickname = try {
com.bitchat.android.services.NicknameProvider.getNickname(context, myPeerID)
} catch (_: Exception) {
myPeerID
}
val encoded = BitchatMessage(
sender = nickname,
content = content,
timestamp = java.util.Date(),
isRelay = false,
senderPeerID = myPeerID,
mentions = mentions.takeIf { it.isNotEmpty() },
channel = channel
).toBinaryPayload()
return encoded ?: content.toByteArray(Charsets.UTF_8)
}
/**
* Send public message
*/
@ -859,13 +887,14 @@ class BluetoothMeshService(private val context: Context) : TransportBridgeServic
if (content.isEmpty()) return
serviceScope.launch {
val payloadBytes = encodePublicOrChannelPayload(content, mentions, channel)
val packet = BitchatPacket(
version = 1u,
type = MessageType.MESSAGE.value,
senderID = hexStringToByteArray(myPeerID),
recipientID = SpecialRecipients.BROADCAST,
timestamp = System.currentTimeMillis().toULong(),
payload = content.toByteArray(Charsets.UTF_8),
payload = payloadBytes,
signature = null,
ttl = MAX_TTL
)

View File

@ -530,16 +530,42 @@ class MeshCore(
}
}
/**
* Mesh timeline stays UTF-8 for iOS interop. Channel messages use the shared
* [BitchatMessage] binary envelope so receivers can route by `channel` instead
* of dumping every #room into the public Mesh feed.
*/
private fun encodePublicOrChannelPayload(
content: String,
mentions: List<String>,
channel: String?
): ByteArray {
if (channel == null) return content.toByteArray(Charsets.UTF_8)
val nickname = hooks.announcementNicknameProvider?.invoke() ?: myPeerID
val encoded = BitchatMessage(
sender = nickname,
content = content,
timestamp = java.util.Date(),
isRelay = false,
senderPeerID = myPeerID,
mentions = mentions.takeIf { it.isNotEmpty() },
channel = channel
).toBinaryPayload()
return encoded ?: content.toByteArray(Charsets.UTF_8)
}
fun sendMessage(content: String, mentions: List<String> = emptyList(), channel: String? = null) {
if (content.isEmpty()) return
scope.launch {
val payloadBytes = encodePublicOrChannelPayload(content, mentions, channel)
val packet = BitchatPacket(
version = 1u,
type = MessageType.MESSAGE.value,
senderID = MeshPacketUtils.hexStringToByteArray(myPeerID),
recipientID = SpecialRecipients.BROADCAST,
timestamp = System.currentTimeMillis().toULong(),
payload = content.toByteArray(Charsets.UTF_8),
payload = payloadBytes,
signature = null,
ttl = maxTtl
)

View File

@ -484,10 +484,20 @@ class MessageHandler(private val myPeerID: String, private val appContext: andro
Log.w(TAG, "FILE_TRANSFER decode failed (broadcast) from ${peerID.take(8)}")
}
// Fallback: plain text
val message = BitchatMessage(
id = PacketIdUtil.computeIdHex(packet).uppercase(),
sender = delegate?.getPeerNickname(peerID) ?: "unknown",
// Prefer the shared binary envelope (channel / mentions). Fall back to
// legacy UTF-8 mesh text for older peers and for iOS public broadcasts.
val packetId = PacketIdUtil.computeIdHex(packet).uppercase()
val peerNickname = delegate?.getPeerNickname(peerID) ?: "unknown"
val message = BitchatMessage.fromBinaryPayload(packet.payload)?.let { decoded ->
decoded.copy(
id = packetId,
sender = decoded.sender.ifBlank { peerNickname },
senderPeerID = peerID,
timestamp = Date(packet.timestamp.toLong())
)
} ?: BitchatMessage(
id = packetId,
sender = peerNickname,
content = String(packet.payload, Charsets.UTF_8),
senderPeerID = peerID,
timestamp = Date(packet.timestamp.toLong())

View File

@ -0,0 +1,37 @@
package com.bitchat.android.model
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import java.util.Date
class ChannelWireFormatTest {
@Test
fun channelFlagSurvivesBinaryRoundTrip() {
val original = BitchatMessage(
sender = "alice",
content = "hello channel",
timestamp = Date(1_700_000_000_000L),
senderPeerID = "aabbccddeeff0011",
channel = "#general"
)
val encoded = original.toBinaryPayload()
assertNotNull(encoded)
// Must not be plain UTF-8 of the content alone — that is the mesh leak.
assertTrue(encoded!!.size > original.content.toByteArray(Charsets.UTF_8).size)
val decoded = BitchatMessage.fromBinaryPayload(encoded)
assertNotNull(decoded)
assertEquals("#general", decoded!!.channel)
assertEquals("hello channel", decoded.content)
assertEquals("alice", decoded.sender)
}
@Test
fun plainUtf8MeshPayloadDoesNotParseAsChannelEnvelope() {
val plain = "just a mesh hello".toByteArray(Charsets.UTF_8)
assertNull(BitchatMessage.fromBinaryPayload(plain))
}
}