diff --git a/app/src/main/java/com/geeksville/mesh/ui/Main.kt b/app/src/main/java/com/geeksville/mesh/ui/Main.kt index 842966f98..f45d749eb 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt @@ -32,10 +32,8 @@ import androidx.compose.animation.scaleOut import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.recalculateWindowInsets import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.width @@ -46,7 +44,6 @@ import androidx.compose.material3.BadgedBox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.PlainTooltip import androidx.compose.material3.Text @@ -133,7 +130,6 @@ import org.meshtastic.core.strings.firmware_old import org.meshtastic.core.strings.firmware_too_old import org.meshtastic.core.strings.map import org.meshtastic.core.strings.must_update -import org.meshtastic.core.strings.neighbor_info import org.meshtastic.core.strings.nodes import org.meshtastic.core.strings.okay import org.meshtastic.core.strings.should_update @@ -295,98 +291,101 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode onDismiss = { tracerouteMapError = null }, ) } - - val neighborInfoResponse by uIViewModel.neighborInfoResponse.observeAsState() - neighborInfoResponse?.let { response -> - SimpleAlertDialog( - title = Res.string.neighbor_info, - text = { - Column(modifier = Modifier.fillMaxWidth()) { - fun tryParseNeighborInfo(input: String): MeshProtos.NeighborInfo? { - // First, try parsing directly from raw bytes of the string - var neighborInfo: MeshProtos.NeighborInfo? = - runCatching { MeshProtos.NeighborInfo.parseFrom(input.toByteArray()) }.getOrNull() - - if (neighborInfo == null) { - // Next, try to decode a hex dump embedded as text (e.g., "AA BB CC ...") - val hexPairs = Regex("""\b[0-9A-Fa-f]{2}\b""").findAll(input).map { it.value }.toList() - @Suppress("detekt:MagicNumber") // byte offsets - if (hexPairs.size >= 4) { - val bytes = hexPairs.map { it.toInt(16).toByte() }.toByteArray() - neighborInfo = runCatching { MeshProtos.NeighborInfo.parseFrom(bytes) }.getOrNull() - } - } - - return neighborInfo - } - - val parsed = tryParseNeighborInfo(response) - if (parsed != null) { - fun fmtNode(nodeNum: Int): String = "!%08x".format(nodeNum) - Text(text = "NeighborInfo:", style = MaterialTheme.typography.bodyMedium) - Text( - text = "node_id: ${fmtNode(parsed.nodeId)}", - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(top = 8.dp), - ) - Text( - text = "last_sent_by_id: ${fmtNode(parsed.lastSentById)}", - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(top = 2.dp), - ) - Text( - text = "node_broadcast_interval_secs: ${parsed.nodeBroadcastIntervalSecs}", - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(top = 2.dp), - ) - if (parsed.neighborsCount > 0) { - Text( - text = "neighbors:", - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(top = 4.dp), - ) - parsed.neighborsList.forEach { n -> - Text( - text = " - node_id: ${fmtNode(n.nodeId)} snr: ${n.snr}", - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(start = 8.dp), - ) - } - } - } else { - val rawBytes = response.toByteArray() - - @Suppress("detekt:MagicNumber") // byte offsets - val isBinary = response.any { it.code < 32 && it != '\n' && it != '\r' && it != '\t' } - if (isBinary) { - val hexString = rawBytes.joinToString(" ") { "%02X".format(it) } - Text( - text = "Binary data (hex view):", - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 4.dp), - ) - Text( - text = hexString, - style = - MaterialTheme.typography.bodyMedium.copy( - fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace, - ), - modifier = Modifier.padding(bottom = 8.dp).fillMaxWidth(), - ) - } else { - Text( - text = response, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 8.dp).fillMaxWidth(), - ) - } - } - } - }, - dismissText = stringResource(Res.string.okay), - onDismiss = { uIViewModel.clearNeighborInfoResponse() }, - ) - } + // FIXME: uncomment and update Capabilities.kt when working better + // + // val neighborInfoResponse by uIViewModel.neighborInfoResponse.observeAsState() + // neighborInfoResponse?.let { response -> + // SimpleAlertDialog( + // title = Res.string.neighbor_info, + // text = { + // Column(modifier = Modifier.fillMaxWidth()) { + // fun tryParseNeighborInfo(input: String): MeshProtos.NeighborInfo? { + // // First, try parsing directly from raw bytes of the string + // var neighborInfo: MeshProtos.NeighborInfo? = + // runCatching { MeshProtos.NeighborInfo.parseFrom(input.toByteArray()) }.getOrNull() + // + // if (neighborInfo == null) { + // // Next, try to decode a hex dump embedded as text (e.g., "AA BB CC ...") + // val hexPairs = Regex("""\b[0-9A-Fa-f]{2}\b""").findAll(input).map { it.value + // }.toList() + // @Suppress("detekt:MagicNumber") // byte offsets + // if (hexPairs.size >= 4) { + // val bytes = hexPairs.map { it.toInt(16).toByte() }.toByteArray() + // neighborInfo = runCatching { MeshProtos.NeighborInfo.parseFrom(bytes) + // }.getOrNull() + // } + // } + // + // return neighborInfo + // } + // + // val parsed = tryParseNeighborInfo(response) + // if (parsed != null) { + // fun fmtNode(nodeNum: Int): String = "!%08x".format(nodeNum) + // Text(text = "NeighborInfo:", style = MaterialTheme.typography.bodyMedium) + // Text( + // text = "node_id: ${fmtNode(parsed.nodeId)}", + // style = MaterialTheme.typography.bodySmall, + // modifier = Modifier.padding(top = 8.dp), + // ) + // Text( + // text = "last_sent_by_id: ${fmtNode(parsed.lastSentById)}", + // style = MaterialTheme.typography.bodySmall, + // modifier = Modifier.padding(top = 2.dp), + // ) + // Text( + // text = "node_broadcast_interval_secs: ${parsed.nodeBroadcastIntervalSecs}", + // style = MaterialTheme.typography.bodySmall, + // modifier = Modifier.padding(top = 2.dp), + // ) + // if (parsed.neighborsCount > 0) { + // Text( + // text = "neighbors:", + // style = MaterialTheme.typography.bodySmall, + // modifier = Modifier.padding(top = 4.dp), + // ) + // parsed.neighborsList.forEach { n -> + // Text( + // text = " - node_id: ${fmtNode(n.nodeId)} snr: ${n.snr}", + // style = MaterialTheme.typography.bodySmall, + // modifier = Modifier.padding(start = 8.dp), + // ) + // } + // } + // } else { + // val rawBytes = response.toByteArray() + // + // @Suppress("detekt:MagicNumber") // byte offsets + // val isBinary = response.any { it.code < 32 && it != '\n' && it != '\r' && it != '\t' } + // if (isBinary) { + // val hexString = rawBytes.joinToString(" ") { "%02X".format(it) } + // Text( + // text = "Binary data (hex view):", + // style = MaterialTheme.typography.bodyMedium, + // modifier = Modifier.padding(bottom = 4.dp), + // ) + // Text( + // text = hexString, + // style = + // MaterialTheme.typography.bodyMedium.copy( + // fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace, + // ), + // modifier = Modifier.padding(bottom = 8.dp).fillMaxWidth(), + // ) + // } else { + // Text( + // text = response, + // style = MaterialTheme.typography.bodyMedium, + // modifier = Modifier.padding(bottom = 8.dp).fillMaxWidth(), + // ) + // } + // } + // } + // }, + // dismissText = stringResource(Res.string.okay), + // onDismiss = { uIViewModel.clearNeighborInfoResponse() }, + // ) + // } val navSuiteType = NavigationSuiteScaffoldDefaults.navigationSuiteType(currentWindowAdaptiveInfo()) val currentDestination = navController.currentBackStackEntryAsState().value?.destination val topLevelDestination = TopLevelDestination.fromNavDestination(currentDestination) diff --git a/core/model/src/main/kotlin/org/meshtastic/core/model/Capabilities.kt b/core/model/src/main/kotlin/org/meshtastic/core/model/Capabilities.kt index eab99644f..cde571951 100644 --- a/core/model/src/main/kotlin/org/meshtastic/core/model/Capabilities.kt +++ b/core/model/src/main/kotlin/org/meshtastic/core/model/Capabilities.kt @@ -36,9 +36,9 @@ data class Capabilities(val firmwareVersion: String?, internal val forceEnableAl val canMuteNode: Boolean get() = isSupported("2.7.18") - /** Ability to request neighbor information from other nodes. Supported since firmware v2.7.15. */ + /** FIXME: Ability to request neighbor information from other nodes. Disabled until working better. */ val canRequestNeighborInfo: Boolean - get() = isSupported("2.7.15") + get() = isSupported("9.9.9") /** Ability to send verified shared contacts. Supported since firmware v2.7.12. */ val canSendVerifiedContacts: Boolean diff --git a/core/model/src/test/kotlin/org/meshtastic/core/model/CapabilitiesTest.kt b/core/model/src/test/kotlin/org/meshtastic/core/model/CapabilitiesTest.kt index 1beba0388..6e77cc6a8 100644 --- a/core/model/src/test/kotlin/org/meshtastic/core/model/CapabilitiesTest.kt +++ b/core/model/src/test/kotlin/org/meshtastic/core/model/CapabilitiesTest.kt @@ -32,11 +32,12 @@ class CapabilitiesTest { assertTrue(caps("2.8.1").canMuteNode) } + // FIXME: needs updating when NeighborInfo is working properly @Test - fun `canRequestNeighborInfo requires v2 7 15`() { + fun `canRequestNeighborInfo disabled`() { assertFalse(caps("2.7.14").canRequestNeighborInfo) - assertTrue(caps("2.7.15").canRequestNeighborInfo) - assertTrue(caps("2.8.0").canRequestNeighborInfo) + assertFalse(caps("2.7.15").canRequestNeighborInfo) + assertFalse(caps("2.8.0").canRequestNeighborInfo) } @Test