fix(neighborinfo): disable neighbor info dialog temporarily (#4345)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich
2026-01-27 21:21:04 -06:00
committed by GitHub
parent 12f7fe30b9
commit 091ca9b400
3 changed files with 101 additions and 101 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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