wifi aware working

This commit is contained in:
CC
2026-06-08 23:31:05 +02:00
parent 3ac6976a54
commit 0a8f77961a
21 changed files with 694 additions and 120 deletions
@@ -100,6 +100,7 @@ class MeshDelegateHandler(
override fun didUpdatePeerList(peers: List<String>) {
coroutineScope.launch {
blePeers = peers.toSet()
try { com.bitchat.android.services.AppStateStore.setTransportPeers("BLE", peers) } catch (_: Exception) { }
processPeerUpdate()
}
}
@@ -107,16 +108,15 @@ class MeshDelegateHandler(
fun onWifiPeersUpdated(peers: List<String>) {
coroutineScope.launch {
wifiPeers = peers.toSet()
try { com.bitchat.android.services.AppStateStore.setTransportPeers("WIFI", peers) } catch (_: Exception) { }
processPeerUpdate()
}
}
private suspend fun processPeerUpdate() {
// Merge peers from multiple transports
val mergedPeers = (blePeers + wifiPeers).toList()
// Update process-wide state as source of truth
try { com.bitchat.android.services.AppStateStore.setPeers(mergedPeers) } catch (_: Exception) { }
val mergedPeers = com.bitchat.android.services.AppStateStore.peers.value
.ifEmpty { (blePeers + wifiPeers).toList() }
state.setConnectedPeers(mergedPeers)
state.setIsConnected(mergedPeers.isNotEmpty())
@@ -61,6 +61,8 @@ fun MeshPeerListSheet(
val peerNicknames by viewModel.peerNicknames.collectAsStateWithLifecycle()
val peerRSSI by viewModel.peerRSSI.collectAsStateWithLifecycle()
val selectedLocationChannel by viewModel.selectedLocationChannel.collectAsStateWithLifecycle()
val wifiAwareConnected by com.bitchat.android.wifiaware.WifiAwareController.connectedPeers.collectAsStateWithLifecycle()
val wifiAwarePeerIDs = remember(wifiAwareConnected) { wifiAwareConnected.keys.toSet() }
// Bottom sheet state
val sheetState = rememberModalBottomSheetState(
@@ -163,11 +165,12 @@ fun MeshPeerListSheet(
nickname = nickname,
colorScheme = colorScheme,
selectedPrivatePeer = selectedPrivatePeer,
wifiAwarePeerIDs = wifiAwarePeerIDs,
viewModel = viewModel,
onPrivateChatStart = { peerID ->
viewModel.showPrivateChatSheet(peerID)
onDismiss()
}
onPrivateChatStart = { peerID ->
viewModel.showPrivateChatSheet(peerID)
onDismiss()
}
)
}
}
@@ -279,6 +282,7 @@ fun PeopleSection(
nickname: String,
colorScheme: ColorScheme,
selectedPrivatePeer: String?,
wifiAwarePeerIDs: Set<String> = emptySet(),
viewModel: ChatViewModel,
onPrivateChatStart: (String) -> Unit
) {
@@ -416,6 +420,7 @@ fun PeopleSection(
peerID = peerID,
displayName = displayName,
isDirect = isDirectLive,
isWifiAware = peerID in wifiAwarePeerIDs,
isSelected = peerID == selectedPrivatePeer,
isFavorite = isFavorite,
isVerified = isVerified,
@@ -544,6 +549,7 @@ private fun PeerItem(
peerID: String,
displayName: String,
isDirect: Boolean,
isWifiAware: Boolean = false,
isSelected: Boolean,
isFavorite: Boolean,
isVerified: Boolean,
@@ -619,10 +625,18 @@ private fun PeerItem(
)
} else {
Icon(
imageVector = if (isDirect) Icons.Outlined.Bluetooth else Icons.Filled.Route,
contentDescription = if (isDirect) "Direct Bluetooth" else "Routed",
imageVector = when {
isWifiAware -> Icons.Filled.Wifi
isDirect -> Icons.Outlined.Bluetooth
else -> Icons.Filled.Route
},
contentDescription = when {
isWifiAware -> "Direct Wi-Fi Aware"
isDirect -> "Direct Bluetooth"
else -> "Routed"
},
modifier = Modifier.size(16.dp),
tint = colorScheme.onSurface.copy(alpha = 0.6f)
tint = if (isWifiAware) Color(0xFF9C27B0) else colorScheme.onSurface.copy(alpha = 0.6f)
)
}
@@ -652,6 +666,16 @@ private fun PeerItem(
color = baseColor.copy(alpha = 0.6f)
)
}
if (isWifiAware && hasUnreadDM) {
Spacer(modifier = Modifier.width(4.dp))
Icon(
imageVector = Icons.Filled.Wifi,
contentDescription = "Direct Wi-Fi Aware",
modifier = Modifier.size(13.dp),
tint = Color(0xFF9C27B0).copy(alpha = 0.8f)
)
}
}
}
@@ -285,6 +285,9 @@ class DebugSettingsManager private constructor() {
DebugPreferenceManager.setBleEnabled(enabled)
_bleEnabled.value = enabled
addDebugMessage(DebugMessage.SystemMessage(if (enabled) "🟢 BLE enabled" else "🔴 BLE disabled"))
try {
com.bitchat.android.service.MeshServiceHolder.meshService?.setBleTransportEnabled(enabled)
} catch (_: Exception) { }
}
fun setWifiAwareEnabled(enabled: Boolean) {
@@ -42,10 +42,12 @@ import com.bitchat.android.core.ui.component.sheet.BitchatSheetTopBar
import com.bitchat.android.core.ui.component.sheet.BitchatSheetTitle
@Composable
fun MeshTopologySection() {
fun MeshTopologySection(localPeerID: String? = null) {
val colorScheme = MaterialTheme.colorScheme
val graphService = remember { MeshGraphService.getInstance() }
val snapshot by graphService.graphState.collectAsState()
val wifiAwareConnected by com.bitchat.android.wifiaware.WifiAwareController.connectedPeers.collectAsState()
val wifiAwarePeerIDs = remember(wifiAwareConnected) { wifiAwareConnected.keys.toSet() }
Surface(shape = RoundedCornerShape(12.dp), color = colorScheme.surfaceVariant.copy(alpha = 0.2f)) {
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
@@ -62,6 +64,8 @@ fun MeshTopologySection() {
ForceDirectedMeshGraph(
nodes = nodes,
edges = edges,
wifiAwarePeerIDs = wifiAwarePeerIDs,
localPeerID = localPeerID,
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
@@ -221,7 +225,7 @@ fun DebugSettingsSheet(
// Mesh topology visualization (moved below verbose logging)
item {
MeshTopologySection()
MeshTopologySection(localPeerID = meshService.myPeerID)
}
// GATT controls
@@ -307,15 +311,6 @@ fun DebugSettingsSheet(
Text("BLE", fontFamily = FontFamily.Monospace, modifier = Modifier.weight(1f))
Switch(checked = bleEnabled, onCheckedChange = {
manager.setBleEnabled(it)
scope.launch {
if (it) {
if (gattServerEnabled) meshService.connectionManager.startServer()
if (gattClientEnabled) meshService.connectionManager.startClient()
} else {
meshService.connectionManager.stopServer()
meshService.connectionManager.stopClient()
}
}
})
}
Row(verticalAlignment = Alignment.CenterVertically) {
@@ -604,8 +599,8 @@ fun DebugSettingsSheet(
Text(if (running) "running" else "stopped", fontFamily = FontFamily.Monospace, fontSize = 12.sp, color = colorScheme.onSurface.copy(alpha = 0.7f))
}
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.startIfPossible() }, label = { Text("Start") })
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.stop() }, label = { Text("Stop") })
AssistChip(onClick = { manager.setWifiAwareEnabled(true) }, label = { Text("Start") })
AssistChip(onClick = { manager.setWifiAwareEnabled(false) }, label = { Text("Stop") })
AssistChip(onClick = { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendBroadcastAnnounce() }, label = { Text("Announce") })
}
Text("Discovered: ${wifiAwareDiscovered.size}", fontFamily = FontFamily.Monospace, fontSize = 12.sp)
@@ -4,6 +4,11 @@ import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Wifi
import androidx.compose.material3.Icon
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -14,6 +19,7 @@ import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.bitchat.android.services.meshgraph.MeshGraphService
@@ -212,6 +218,8 @@ private class Simulation {
fun ForceDirectedMeshGraph(
nodes: List<MeshGraphService.GraphNode>,
edges: List<MeshGraphService.GraphEdge>,
wifiAwarePeerIDs: Set<String> = emptySet(),
localPeerID: String? = null,
modifier: Modifier = Modifier
) {
val density = LocalDensity.current
@@ -405,5 +413,46 @@ fun ForceDirectedMeshGraph(
)
}
}
val iconSize = 16.dp
val iconSizePx = with(density) { iconSize.toPx() }
val halfIconSizePx = iconSizePx / 2f
val localID = localPeerID
val shouldMarkWifiEdge: (MeshGraphService.GraphEdge) -> Boolean = { edge ->
if (localID != null) {
(edge.a == localID && edge.b in wifiAwarePeerIDs) ||
(edge.b == localID && edge.a in wifiAwarePeerIDs)
} else {
edge.a in wifiAwarePeerIDs || edge.b in wifiAwarePeerIDs
}
}
val wifiEdgeMidpoints = tick.let {
simulation.edges.mapNotNull { edge ->
val n1 = simulation.nodes[edge.a]
val n2 = simulation.nodes[edge.b]
if (n1 != null && n2 != null && shouldMarkWifiEdge(edge)) {
((n1.x + n2.x) / 2f) to ((n1.y + n2.y) / 2f)
} else {
null
}
}
}
wifiEdgeMidpoints.forEach { (midX, midY) ->
Icon(
imageVector = Icons.Filled.Wifi,
contentDescription = null,
tint = Color(0xFF9C27B0).copy(alpha = 0.82f),
modifier = Modifier
.offset {
IntOffset(
x = (midX - halfIconSizePx).roundToInt(),
y = (midY - halfIconSizePx).roundToInt()
)
}
.size(iconSize)
)
}
}
}