Files
bitchat-android/app/src/main/java/com/bitchat/android/ui/MeshDelegateHandler.kt
T
2025-07-12 12:53:01 +02:00

162 lines
6.2 KiB
Kotlin

package com.bitchat.android.ui
import androidx.lifecycle.LifecycleCoroutineScope
import com.bitchat.android.mesh.BluetoothMeshDelegate
import com.bitchat.android.model.BitchatMessage
import com.bitchat.android.model.DeliveryAck
import com.bitchat.android.model.DeliveryStatus
import com.bitchat.android.model.ReadReceipt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.util.*
/**
* Handles all BluetoothMeshDelegate callbacks and routes them to appropriate managers
*/
class MeshDelegateHandler(
private val state: ChatState,
private val messageManager: MessageManager,
private val channelManager: ChannelManager,
private val privateChatManager: PrivateChatManager,
private val notificationManager: NotificationManager,
private val coroutineScope: CoroutineScope,
private val onHapticFeedback: () -> Unit,
private val getMyPeerID: () -> String
) : BluetoothMeshDelegate {
override fun didReceiveMessage(message: BitchatMessage) {
coroutineScope.launch {
// FIXED: Deduplicate messages from dual connection paths
val messageKey = messageManager.generateMessageKey(message)
if (messageManager.isMessageProcessed(messageKey)) {
return@launch // Duplicate message, ignore
}
messageManager.markMessageProcessed(messageKey)
// Check if sender is blocked
message.senderPeerID?.let { senderPeerID ->
if (privateChatManager.isPeerBlocked(senderPeerID)) {
return@launch
}
}
// Trigger haptic feedback
onHapticFeedback()
if (message.isPrivate) {
// Private message
privateChatManager.handleIncomingPrivateMessage(message)
// Show notification with enhanced information - now includes senderPeerID
message.senderPeerID?.let { senderPeerID ->
if (state.getSelectedPrivateChatPeerValue() != senderPeerID) {
// Use nickname if available, fall back to sender or senderPeerID
val senderNickname = message.sender.takeIf { it != senderPeerID } ?: senderPeerID
notificationManager.showPrivateMessageNotification(
senderPeerID = senderPeerID,
senderNickname = senderNickname,
messageContent = message.content
)
}
}
} else if (message.channel != null) {
// Channel message
if (state.getJoinedChannelsValue().contains(message.channel)) {
channelManager.addChannelMessage(message.channel, message, message.senderPeerID)
}
} else {
// Public message
messageManager.addMessage(message)
}
// Periodic cleanup
if (messageManager.isMessageProcessed("cleanup_check_${System.currentTimeMillis()/30000}")) {
messageManager.cleanupDeduplicationCaches()
}
}
}
override fun didConnectToPeer(peerID: String) {
coroutineScope.launch {
// FIXED: Deduplicate connection events from dual connection paths
if (messageManager.isDuplicateSystemEvent("connect", peerID)) {
return@launch
}
val systemMessage = BitchatMessage(
sender = "system",
content = "$peerID connected",
timestamp = Date(),
isRelay = false
)
messageManager.addMessage(systemMessage)
}
}
override fun didDisconnectFromPeer(peerID: String) {
coroutineScope.launch {
// FIXED: Deduplicate disconnection events from dual connection paths
if (messageManager.isDuplicateSystemEvent("disconnect", peerID)) {
return@launch
}
val systemMessage = BitchatMessage(
sender = "system",
content = "$peerID disconnected",
timestamp = Date(),
isRelay = false
)
messageManager.addMessage(systemMessage)
}
}
override fun didUpdatePeerList(peers: List<String>) {
coroutineScope.launch {
state.setConnectedPeers(peers)
state.setIsConnected(peers.isNotEmpty())
// Clean up channel members who disconnected
channelManager.cleanupDisconnectedMembers(peers, getMyPeerID())
// Exit private chat if peer disconnected
state.getSelectedPrivateChatPeerValue()?.let { currentPeer ->
if (!peers.contains(currentPeer)) {
privateChatManager.cleanupDisconnectedPeer(currentPeer)
}
}
}
}
override fun didReceiveChannelLeave(channel: String, fromPeer: String) {
coroutineScope.launch {
channelManager.removeChannelMember(channel, fromPeer)
}
}
override fun didReceiveDeliveryAck(ack: DeliveryAck) {
coroutineScope.launch {
messageManager.updateMessageDeliveryStatus(ack.originalMessageID, DeliveryStatus.Delivered(ack.recipientNickname, ack.timestamp))
}
}
override fun didReceiveReadReceipt(receipt: ReadReceipt) {
coroutineScope.launch {
messageManager.updateMessageDeliveryStatus(receipt.originalMessageID, DeliveryStatus.Read(receipt.readerNickname, receipt.timestamp))
}
}
override fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String? {
return channelManager.decryptChannelMessage(encryptedContent, channel)
}
override fun getNickname(): String? = state.getNicknameValue()
override fun isFavorite(peerID: String): Boolean {
return privateChatManager.isFavorite(peerID)
}
override fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) {
privateChatManager.registerPeerPublicKey(peerID, publicKeyData)
}
}