kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
Device metric lines and info dialog (#1252)
* Removed constants from CommonCharts only used in specific charts. * Altered CommonCharts.ChartOverlay to take a list of colors for the lines. Adjusted the device metrics line colors for channel utilization. * Started an info dialog in the device metric chart to help users better understand Meshtastic.pull/1255/head
rodzic
f863f00d4a
commit
843e423648
|
@ -27,28 +27,21 @@ import androidx.compose.ui.text.TextStyle
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.LINE_OFF
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.LINE_ON
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.LINE_LIMIT
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TEXT_PAINT_ALPHA
|
||||
import com.geeksville.mesh.ui.theme.Orange
|
||||
import java.text.DateFormat
|
||||
|
||||
|
||||
object CommonCharts {
|
||||
val DEVICE_METRICS_COLORS = listOf(Color.Green, Color.Magenta, Color.Cyan)
|
||||
val ENVIRONMENT_METRICS_COLORS = listOf(Color.Red, Color.Blue)
|
||||
val TIME_FORMAT: DateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
|
||||
const val MAX_PERCENT_VALUE = 100f
|
||||
const val LINE_LIMIT = 4
|
||||
const val TEXT_PAINT_ALPHA = 192
|
||||
const val LINE_ON = 10f
|
||||
const val LINE_OFF = 20f
|
||||
const val LEFT_CHART_SPACING = 8f
|
||||
const val MS_PER_SEC = 1000.0f
|
||||
}
|
||||
|
||||
private const val LINE_LIMIT = 4
|
||||
private const val TEXT_PAINT_ALPHA = 192
|
||||
private const val LINE_ON = 10f
|
||||
private const val LINE_OFF = 20f
|
||||
|
||||
|
||||
@Composable
|
||||
fun ChartHeader(amount: Int) {
|
||||
|
@ -68,11 +61,13 @@ fun ChartHeader(amount: Int) {
|
|||
|
||||
/**
|
||||
* Draws chart lines and labels with respect to the Y-axis range; defined by (`maxValue` - `minValue`).
|
||||
* Assumes `lineColors` is a list of 5 `Color`s with index 0 being the lowest line on the chart.
|
||||
*/
|
||||
@Composable
|
||||
fun ChartOverlay(
|
||||
modifier: Modifier,
|
||||
graphColor: Color,
|
||||
lineColors: List<Color>,
|
||||
minValue: Float,
|
||||
maxValue: Float
|
||||
) {
|
||||
|
@ -89,15 +84,10 @@ fun ChartOverlay(
|
|||
for (i in 0..LINE_LIMIT) {
|
||||
val ratio = (lineY - minValue) / range
|
||||
val y = height - (ratio * height)
|
||||
val color: Color = when (i) {
|
||||
1 -> Color.Red
|
||||
2 -> Orange
|
||||
else -> graphColor
|
||||
}
|
||||
drawLine(
|
||||
start = Offset(0f, y),
|
||||
end = Offset(width, y),
|
||||
color = color,
|
||||
color = lineColors[i],
|
||||
strokeWidth = 1.dp.toPx(),
|
||||
cap = StrokeCap.Round,
|
||||
pathEffect = PathEffect.dashPathEffect(floatArrayOf(LINE_ON, LINE_OFF), 0f)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.geeksville.mesh.ui.components
|
||||
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
@ -15,39 +16,62 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.AlertDialog
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.ui.BatteryInfo
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.DEVICE_METRICS_COLORS
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.LEFT_CHART_SPACING
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.MAX_PERCENT_VALUE
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.theme.Orange
|
||||
|
||||
|
||||
private val DEVICE_METRICS_COLORS = listOf(Color.Green, Color.Magenta, Color.Cyan)
|
||||
private const val MAX_PERCENT_VALUE = 100f
|
||||
|
||||
@Composable
|
||||
fun DeviceMetricsScreen(telemetries: List<Telemetry>) {
|
||||
|
||||
var displayInfoDialog by remember { mutableStateOf(false) }
|
||||
|
||||
Column {
|
||||
|
||||
if (displayInfoDialog)
|
||||
DeviceInfoDialog { displayInfoDialog = false }
|
||||
|
||||
DeviceMetricsChart(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(fraction = 0.33f),
|
||||
telemetries.reversed()
|
||||
telemetries.reversed(),
|
||||
promptInfoDialog = { displayInfoDialog = true }
|
||||
)
|
||||
/* Device Metric Cards */
|
||||
LazyColumn(
|
||||
|
@ -60,7 +84,11 @@ fun DeviceMetricsScreen(telemetries: List<Telemetry>) {
|
|||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun DeviceMetricsChart(modifier: Modifier = Modifier, telemetries: List<Telemetry>) {
|
||||
private fun DeviceMetricsChart(
|
||||
modifier: Modifier = Modifier,
|
||||
telemetries: List<Telemetry>,
|
||||
promptInfoDialog: () -> Unit
|
||||
) {
|
||||
|
||||
ChartHeader(amount = telemetries.size)
|
||||
if (telemetries.isEmpty())
|
||||
|
@ -73,7 +101,18 @@ private fun DeviceMetricsChart(modifier: Modifier = Modifier, telemetries: List<
|
|||
|
||||
Box(contentAlignment = Alignment.TopStart) {
|
||||
|
||||
ChartOverlay(modifier, graphColor, minValue = 0f, maxValue = 100f)
|
||||
/*
|
||||
* The order of the colors are with respect to the ChUtil.
|
||||
* 25 - 49 Orange
|
||||
* 50 - 100 Red
|
||||
*/
|
||||
ChartOverlay(
|
||||
modifier,
|
||||
graphColor,
|
||||
lineColors = listOf(graphColor, Orange, Color.Red, graphColor, graphColor),
|
||||
minValue = 0f,
|
||||
maxValue = 100f
|
||||
)
|
||||
|
||||
/* Plot Battery Line, ChUtil, and AirUtilTx */
|
||||
Canvas(modifier = modifier) {
|
||||
|
@ -142,7 +181,7 @@ private fun DeviceMetricsChart(modifier: Modifier = Modifier, telemetries: List<
|
|||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
DeviceLegend()
|
||||
DeviceLegend(promptInfoDialog)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
@ -205,7 +244,7 @@ private fun DeviceMetricsCard(telemetry: Telemetry) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun DeviceLegend() {
|
||||
private fun DeviceLegend(promptInfoDialog: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
@ -223,6 +262,65 @@ private fun DeviceLegend() {
|
|||
|
||||
LegendLabel(text = stringResource(R.string.air_utilization), color = DEVICE_METRICS_COLORS[2])
|
||||
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
modifier = Modifier.clickable { promptInfoDialog() },
|
||||
contentDescription = stringResource(R.string.info)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeviceInfoDialog(onDismiss: () -> Unit) {
|
||||
AlertDialog(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.info),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.channel_utilization),
|
||||
style = TextStyle(fontWeight = FontWeight.Bold),
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.ch_util_definition),
|
||||
style = TextStyle.Default,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.air_utilization),
|
||||
style = TextStyle(fontWeight = FontWeight.Bold),
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.air_util_definition),
|
||||
style = TextStyle.Default
|
||||
)
|
||||
}
|
||||
},
|
||||
onDismissRequest = onDismiss,
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.okay))
|
||||
}
|
||||
},
|
||||
backgroundColor = MaterialTheme.colors.background
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun DeviceInfoDialogPreview() {
|
||||
DeviceInfoDialog {}
|
||||
}
|
||||
|
|
|
@ -36,12 +36,13 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.ENVIRONMENT_METRICS_COLORS
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.LEFT_CHART_SPACING
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TIME_FORMAT
|
||||
|
||||
|
||||
private val ENVIRONMENT_METRICS_COLORS = listOf(Color.Red, Color.Blue)
|
||||
|
||||
@Composable
|
||||
fun EnvironmentMetricsScreen(telemetries: List<Telemetry>) {
|
||||
Column {
|
||||
|
@ -95,7 +96,13 @@ private fun EnvironmentMetricsChart(modifier: Modifier = Modifier, telemetries:
|
|||
|
||||
Box(contentAlignment = Alignment.TopStart) {
|
||||
|
||||
ChartOverlay(modifier = modifier, graphColor = graphColor, minValue = min, maxValue = max)
|
||||
ChartOverlay(
|
||||
modifier = modifier,
|
||||
graphColor = graphColor,
|
||||
lineColors = List(size = 5) { graphColor },
|
||||
minValue = min,
|
||||
maxValue = max
|
||||
)
|
||||
|
||||
/* Plot Temperature and Relative Humidity */
|
||||
Canvas(modifier = modifier) {
|
||||
|
|
|
@ -227,4 +227,7 @@
|
|||
<string name="humidity">Humidity</string>
|
||||
<string name="logs">Logs</string>
|
||||
<string name="hops_away">Hops Away</string>
|
||||
<string name="info">Information</string>
|
||||
<string name="ch_util_definition">Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise).</string>
|
||||
<string name="air_util_definition">Percent of airtime for transmission used within the last hour.</string>
|
||||
</resources>
|
||||
|
|
Ładowanie…
Reference in New Issue