dynamic direct connection detection

This commit is contained in:
callebtc
2026-01-10 00:41:10 +07:00
parent ea6b212c53
commit c848de9d42
2 changed files with 38 additions and 23 deletions
@@ -111,6 +111,13 @@ class BluetoothMeshService(private val context: Context) {
return signPacketBeforeBroadcast(packet) return signPacketBeforeBroadcast(packet)
} }
} }
// Inject dynamic direct connection check into PeerManager
// Matches iOS logic: checks if we have an active hardware mapping for this peer
peerManager.isPeerDirectlyConnected = { peerID ->
connectionManager.addressPeerMap.containsValue(peerID)
}
Log.d(TAG, "Delegates set up; GossipSyncManager initialized") Log.d(TAG, "Delegates set up; GossipSyncManager initialized")
} }
@@ -478,8 +485,8 @@ class BluetoothMeshService(private val context: Context) {
connectionManager.addressPeerMap[deviceAddress] = pid connectionManager.addressPeerMap[deviceAddress] = pid
Log.d(TAG, "Mapped device $deviceAddress to peer $pid (TTL=${routed.packet.ttl})") Log.d(TAG, "Mapped device $deviceAddress to peer $pid (TTL=${routed.packet.ttl})")
// Mark as directly connected // Mark as directly connected - refresh UI state
try { peerManager.setDirectConnection(pid, true) } catch (_: Exception) { } try { peerManager.refreshPeerList() } catch (_: Exception) { }
// Initial sync for this direct peer // Initial sync for this direct peer
try { gossipSyncManager.scheduleInitialSyncToPeer(pid, 1_000) } catch (_: Exception) { } try { gossipSyncManager.scheduleInitialSyncToPeer(pid, 1_000) } catch (_: Exception) { }
@@ -584,8 +591,8 @@ class BluetoothMeshService(private val context: Context) {
if (peer != null) { if (peer != null) {
val stillMapped = connectionManager.addressPeerMap.values.any { it == peer } val stillMapped = connectionManager.addressPeerMap.values.any { it == peer }
if (!stillMapped) { if (!stillMapped) {
// Peer might still be reachable indirectly; mark as not-direct // Peer might still be reachable indirectly; refresh to update state
try { peerManager.setDirectConnection(peer, false) } catch (_: Exception) { } try { peerManager.refreshPeerList() } catch (_: Exception) { }
} }
// Verbose debug: device disconnected // Verbose debug: device disconnected
try { try {
@@ -86,6 +86,9 @@ class PeerManager {
// Delegate for callbacks // Delegate for callbacks
var delegate: PeerManagerDelegate? = null var delegate: PeerManagerDelegate? = null
// Callback to check if a peer is directly connected (injected by BluetoothMeshService)
var isPeerDirectlyConnected: ((String) -> Boolean)? = null
// Coroutines // Coroutines
private val managerScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val managerScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@@ -145,10 +148,18 @@ class PeerManager {
} }
/** /**
* Get peer info * Get peer info with dynamic direct connection status
*/ */
fun getPeerInfo(peerID: String): PeerInfo? { fun getPeerInfo(peerID: String): PeerInfo? {
return peers[peerID] return peers[peerID]?.let { info ->
// Dynamically check direct connection status from ConnectionManager
val isDirect = isPeerDirectlyConnected?.invoke(peerID) ?: false
if (info.isDirectConnection != isDirect) {
info.copy(isDirectConnection = isDirect)
} else {
info
}
}
} }
/** /**
@@ -159,28 +170,21 @@ class PeerManager {
} }
/** /**
* Get all verified peers * Get all verified peers with dynamic direct connection status
*/ */
fun getVerifiedPeers(): Map<String, PeerInfo> { fun getVerifiedPeers(): Map<String, PeerInfo> {
return peers.filterValues { it.isVerifiedNickname } return peers.filterValues { it.isVerifiedNickname }.mapValues { (_, info) ->
val isDirect = isPeerDirectlyConnected?.invoke(info.id) ?: false
if (info.isDirectConnection != isDirect) info.copy(isDirectConnection = isDirect) else info
}
} }
/** /**
* Set whether a peer is directly connected over Bluetooth. * Force a peer list update notification.
* Triggers a peer list update to refresh UI badges. * Call this when connection state changes to refresh UI badges.
*/ */
fun setDirectConnection(peerID: String, isDirect: Boolean) { fun refreshPeerList() {
peers[peerID]?.let { existing -> notifyPeerListUpdate()
if (existing.isDirectConnection != isDirect) {
peers[peerID] = existing.copy(isDirectConnection = isDirect)
notifyPeerListUpdate()
// NEW: notify UI state (if available via delegate path) about directness change
try {
// Best-effort: delegate path flows up to ChatViewModel via didUpdatePeerList
// No direct reference to UI layer here by design.
} catch (_: Exception) { }
}
}
} }
// MARK: - Legacy Methods (maintained for compatibility) // MARK: - Legacy Methods (maintained for compatibility)
@@ -364,7 +368,11 @@ class PeerManager {
return buildString { return buildString {
appendLine("=== Peer Manager Debug Info ===") appendLine("=== Peer Manager Debug Info ===")
appendLine("Active Peers: ${activeIds.size}") appendLine("Active Peers: ${activeIds.size}")
peers.forEach { (peerID, info) -> peers.forEach { (peerID, storedInfo) ->
// Use dynamic direct status for debug log accuracy
val isDirect = isPeerDirectlyConnected?.invoke(peerID) ?: false
val info = if (storedInfo.isDirectConnection != isDirect) storedInfo.copy(isDirectConnection = isDirect) else storedInfo
val timeSince = (now - info.lastSeen) / 1000 val timeSince = (now - info.lastSeen) / 1000
val rssi = peerRSSI[peerID]?.let { "${it} dBm" } ?: "No RSSI" val rssi = peerRSSI[peerID]?.let { "${it} dBm" } ?: "No RSSI"
val deviceAddress = addressPeerMap?.entries?.find { it.value == peerID }?.key val deviceAddress = addressPeerMap?.entries?.find { it.value == peerID }?.key