diff --git a/app/src/main/java/com/geeksville/mesh/ui/SignalInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/SignalInfo.kt index 162f755c9..4a3d43b2c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SignalInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SignalInfo.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import com.geeksville.mesh.R import com.geeksville.mesh.database.entity.NodeEntity +import com.geeksville.mesh.ui.components.NodeSignalQuality import com.geeksville.mesh.ui.preview.NodeEntityPreviewParameterProvider import com.geeksville.mesh.ui.theme.AppTheme @@ -21,7 +22,7 @@ fun signalInfo( modifier: Modifier = Modifier, node: NodeEntity, isThisNode: Boolean -): Boolean { +) { val text = if (isThisNode) { stringResource(R.string.channel_air_util).format( node.deviceMetrics.channelUtilization, @@ -40,24 +41,22 @@ fun signalInfo( if (node.channel > 0) { add("ch:${node.channel}") } - if (node.hopsAway <= 0) { - if (node.snr < MAX_VALID_SNR && node.rssi < MAX_VALID_RSSI) { - add("RSSI: %d SNR: %.1f".format(node.rssi, node.snr)) - } - } if (node.hopsAway != 0) add(hopsString) }.joinToString(" | ") } - return if (text.isNotEmpty()) { + if (text.isNotEmpty()) { Text( modifier = modifier, text = text, color = MaterialTheme.colors.onSurface, fontSize = MaterialTheme.typography.button.fontSize ) - true - } else { - false + } + /* We only know the Signal Quality from direct nodes aka 0 hop. */ + if (node.hopsAway <= 0) { + if (node.snr < MAX_VALID_SNR && node.rssi < MAX_VALID_RSSI) { + NodeSignalQuality(node.snr, node.rssi) + } } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/LoraSignalIndicator.kt b/app/src/main/java/com/geeksville/mesh/ui/components/LoraSignalIndicator.kt index 6f6f42629..9e0faf679 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/LoraSignalIndicator.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/LoraSignalIndicator.kt @@ -2,7 +2,9 @@ package com.geeksville.mesh.ui.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme @@ -38,45 +40,46 @@ private enum class Quality( GOOD(R.string.good, Icons.Default.SignalCellular4Bar, Color.Green) } +/** + * Displays the `snr` and `rssi` color coded based on the signal quality, along with + * a human readable description and related icon. + */ @Composable -fun Snr(snr: Float) { - val color: Color = if (snr > SNR_GOOD_THRESHOLD) { - Quality.GOOD.color - } else if (snr > SNR_FAIR_THRESHOLD) { - Quality.FAIR.color - } else { - Quality.BAD.color - } - - Text( - text = "%s %.2fdB".format( - stringResource(id = R.string.snr), - snr - ), - color = color, - fontSize = MaterialTheme.typography.button.fontSize +fun NodeSignalQuality(snr: Float, rssi: Int) { + val quality = determineSignalQuality(snr, rssi) + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Snr(snr) + Rssi(rssi) + Text(text = "${stringResource(R.string.signal)} ${stringResource(quality.nameRes)}") + Icon( + imageVector = quality.imageVector, + contentDescription = stringResource(R.string.signal_quality), + tint = quality.color ) -} - -@Composable -fun Rssi(rssi: Int) { - val color: Color = if (rssi > RSSI_GOOD_THRESHOLD) { - Quality.GOOD.color - } else if (rssi > RSSI_FAIR_THRESHOLD) { - Quality.FAIR.color - } else { - Quality.BAD.color } - Text( - text = "%s %ddB".format( - stringResource(id = R.string.rssi), - rssi - ), - color = color, - fontSize = MaterialTheme.typography.button.fontSize - ) } +/** + * Displays the `snr` and `rssi` with color depending on the values respectively. + */ +@Composable +fun SnrAndRssi(snr: Float, rssi: Int) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Snr(snr) + Rssi(rssi) + } +} + +/** + * Displays a human readable description and icon representing the signal quality. + */ @Composable fun LoraSignalIndicator(snr: Float, rssi: Int) { @@ -98,6 +101,45 @@ fun LoraSignalIndicator(snr: Float, rssi: Int) { } } +@Composable +private fun Snr(snr: Float) { + val color: Color = if (snr > SNR_GOOD_THRESHOLD) { + Quality.GOOD.color + } else if (snr > SNR_FAIR_THRESHOLD) { + Quality.FAIR.color + } else { + Quality.BAD.color + } + + Text( + text = "%s %.2fdB".format( + stringResource(id = R.string.snr), + snr + ), + color = color, + fontSize = MaterialTheme.typography.button.fontSize + ) +} + +@Composable +private fun Rssi(rssi: Int) { + val color: Color = if (rssi > RSSI_GOOD_THRESHOLD) { + Quality.GOOD.color + } else if (rssi > RSSI_FAIR_THRESHOLD) { + Quality.FAIR.color + } else { + Quality.BAD.color + } + Text( + text = "%s %ddB".format( + stringResource(id = R.string.rssi), + rssi + ), + color = color, + fontSize = MaterialTheme.typography.button.fontSize + ) +} + private fun determineSignalQuality(snr: Float, rssi: Int): Quality = when { snr > SNR_GOOD_THRESHOLD && rssi > RSSI_GOOD_THRESHOLD -> Quality.GOOD snr > SNR_GOOD_THRESHOLD && rssi > RSSI_FAIR_THRESHOLD -> Quality.FAIR diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/SignalMetrics.kt b/app/src/main/java/com/geeksville/mesh/ui/components/SignalMetrics.kt index 0e2f88408..af8124f2a 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/SignalMetrics.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/SignalMetrics.kt @@ -266,13 +266,7 @@ private fun SignalMetricsCard(meshPacket: MeshPacket) { Spacer(modifier = Modifier.height(8.dp)) /* SNR and RSSI */ - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Snr(meshPacket.rxSnr) - Rssi(meshPacket.rxRssi) - } + SnrAndRssi(meshPacket.rxSnr, meshPacket.rxRssi) } }