mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-02-05 21:33:01 -05:00
Fix/2100 graph labels (#2182)
Co-authored-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
@@ -50,6 +50,7 @@ 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.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalWindowInfo
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
@@ -58,12 +59,14 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
import com.geeksville.mesh.ui.common.components.BatteryInfo
|
||||
import com.geeksville.mesh.ui.common.components.OptionLabel
|
||||
import com.geeksville.mesh.ui.common.components.SlidingSelector
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.common.theme.Orange
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.MAX_PERCENT_VALUE
|
||||
@@ -71,6 +74,7 @@ import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC
|
||||
import com.geeksville.mesh.util.GraphUtil
|
||||
import com.geeksville.mesh.util.GraphUtil.createPath
|
||||
import com.geeksville.mesh.util.GraphUtil.plotPoint
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
|
||||
private enum class Device(val color: Color) {
|
||||
BATTERY(Color.Green),
|
||||
@@ -139,6 +143,7 @@ private fun DeviceMetricsChart(
|
||||
selectedTime: TimeFrame,
|
||||
promptInfoDialog: () -> Unit
|
||||
) {
|
||||
val graphColor = MaterialTheme.colorScheme.onSurface
|
||||
|
||||
ChartHeader(amount = telemetries.size)
|
||||
if (telemetries.isEmpty()) return
|
||||
@@ -151,21 +156,31 @@ private fun DeviceMetricsChart(
|
||||
}
|
||||
val timeDiff = newest.time - oldest.time
|
||||
|
||||
TimeLabels(
|
||||
oldest = oldest.time,
|
||||
newest = newest.time
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
val graphColor = MaterialTheme.colorScheme.onSurface
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
val screenWidth = LocalWindowInfo.current.containerSize.width
|
||||
val dp by remember(key1 = selectedTime) {
|
||||
mutableStateOf(selectedTime.dp(screenWidth, time = timeDiff.toLong()))
|
||||
}
|
||||
|
||||
// Calculate visible time range based on scroll position and chart width
|
||||
val visibleTimeRange = run {
|
||||
val totalWidthPx = with(LocalDensity.current) { dp.toPx() }
|
||||
val scrollPx = scrollState.value.toFloat()
|
||||
val visibleWidthPx = with(LocalDensity.current) { screenWidth.toDp().toPx() }
|
||||
val leftRatio = (scrollPx / totalWidthPx).coerceIn(0f, 1f)
|
||||
val rightRatio = ((scrollPx + visibleWidthPx) / totalWidthPx).coerceIn(0f, 1f)
|
||||
val visibleOldest = oldest.time + (timeDiff * leftRatio).toInt()
|
||||
val visibleNewest = oldest.time + (timeDiff * rightRatio).toInt()
|
||||
visibleOldest to visibleNewest
|
||||
}
|
||||
|
||||
TimeLabels(
|
||||
oldest = visibleTimeRange.first,
|
||||
newest = visibleTimeRange.second
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Row {
|
||||
Box(
|
||||
contentAlignment = Alignment.TopStart,
|
||||
@@ -265,6 +280,34 @@ private fun DeviceMetricsChart(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
@Suppress("detekt:MagicNumber") // fake data
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun DeviceMetricsChartPreview() {
|
||||
val now = (System.currentTimeMillis() / 1000).toInt()
|
||||
val telemetries = List(20) { i ->
|
||||
Telemetry.newBuilder()
|
||||
.setTime(now - (19 - i) * 60 * 60) // 1-hour intervals, oldest first
|
||||
.setDeviceMetrics(
|
||||
TelemetryProtos.DeviceMetrics.newBuilder()
|
||||
.setBatteryLevel(80 - i)
|
||||
.setVoltage(3.7f - i * 0.02f)
|
||||
.setChannelUtilization(10f + i * 2)
|
||||
.setAirUtilTx(5f + i)
|
||||
.setUptimeSeconds(3600 + i * 300)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
AppTheme {
|
||||
DeviceMetricsChart(
|
||||
modifier = Modifier.height(400.dp),
|
||||
telemetries = telemetries,
|
||||
selectedTime = TimeFrame.TWENTY_FOUR_HOURS,
|
||||
promptInfoDialog = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeviceMetricsCard(telemetry: Telemetry) {
|
||||
val deviceMetrics = telemetry.deviceMetrics
|
||||
@@ -321,3 +364,24 @@ private fun DeviceMetricsCard(telemetry: Telemetry) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("detekt:MagicNumber") // fake data
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun DeviceMetricsCardPreview() {
|
||||
val now = (System.currentTimeMillis() / 1000).toInt()
|
||||
val telemetry = Telemetry.newBuilder()
|
||||
.setTime(now)
|
||||
.setDeviceMetrics(
|
||||
TelemetryProtos.DeviceMetrics.newBuilder()
|
||||
.setBatteryLevel(75)
|
||||
.setVoltage(3.65f)
|
||||
.setChannelUtilization(22.5f)
|
||||
.setAirUtilTx(12.0f)
|
||||
.setUptimeSeconds(7200)
|
||||
)
|
||||
.build()
|
||||
AppTheme {
|
||||
DeviceMetricsCard(telemetry = telemetry)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -165,7 +166,6 @@ fun EnvironmentMetricsScreen(
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO need to take the time to understand this. */
|
||||
@SuppressLint("ConfigurationScreenWidthHeight")
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
@@ -182,9 +182,30 @@ private fun EnvironmentMetricsChart(
|
||||
}
|
||||
|
||||
val (oldest, newest) = graphData.times
|
||||
val timeDiff = newest - oldest
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp
|
||||
val dp by remember(key1 = selectedTime) {
|
||||
mutableStateOf(selectedTime.dp(screenWidth, time = timeDiff.toLong()))
|
||||
}
|
||||
|
||||
// Calculate visible time range based on scroll position and chart width
|
||||
val visibleTimeRange = run {
|
||||
val density = LocalDensity.current
|
||||
val totalWidthPx = with(density) { dp.toPx() }
|
||||
val scrollPx = scrollState.value.toFloat()
|
||||
val visibleWidthPx = with(density) { screenWidth.dp.toPx() }
|
||||
val leftRatio = (scrollPx / totalWidthPx).coerceIn(0f, 1f)
|
||||
val rightRatio = ((scrollPx + visibleWidthPx) / totalWidthPx).coerceIn(0f, 1f)
|
||||
val visibleOldest = oldest + (timeDiff * leftRatio).toInt()
|
||||
val visibleNewest = oldest + (timeDiff * rightRatio).toInt()
|
||||
visibleOldest to visibleNewest
|
||||
}
|
||||
|
||||
TimeLabels(
|
||||
oldest = oldest,
|
||||
newest = newest
|
||||
oldest = visibleTimeRange.first,
|
||||
newest = visibleTimeRange.second
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
@@ -196,12 +217,6 @@ private fun EnvironmentMetricsChart(
|
||||
var min = rightMin
|
||||
var diff = rightMax - rightMin
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp
|
||||
val timeDiff = newest - oldest
|
||||
val dp by remember(key1 = selectedTime) {
|
||||
mutableStateOf(selectedTime.dp(screenWidth, time = timeDiff.toLong()))
|
||||
}
|
||||
val shouldPlot = graphData.shouldPlot
|
||||
|
||||
Row {
|
||||
|
||||
@@ -51,6 +51,7 @@ 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.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalWindowInfo
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
@@ -157,9 +158,28 @@ private fun PowerMetricsChart(
|
||||
}
|
||||
val timeDiff = newest.time - oldest.time
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
val screenWidth = LocalWindowInfo.current.containerSize.width
|
||||
val dp by remember(key1 = selectedTime) {
|
||||
mutableStateOf(selectedTime.dp(screenWidth, time = (newest.time - oldest.time).toLong()))
|
||||
}
|
||||
|
||||
// Calculate visible time range based on scroll position and chart width
|
||||
val visibleTimeRange = run {
|
||||
val density = LocalDensity.current
|
||||
val totalWidthPx = with(density) { dp.toPx() }
|
||||
val scrollPx = scrollState.value.toFloat()
|
||||
val visibleWidthPx = with(density) { screenWidth.toDp().toPx() }
|
||||
val leftRatio = (scrollPx / totalWidthPx).coerceIn(0f, 1f)
|
||||
val rightRatio = ((scrollPx + visibleWidthPx) / totalWidthPx).coerceIn(0f, 1f)
|
||||
val visibleOldest = oldest.time + (timeDiff * leftRatio).toInt()
|
||||
val visibleNewest = oldest.time + (timeDiff * rightRatio).toInt()
|
||||
visibleOldest to visibleNewest
|
||||
}
|
||||
|
||||
TimeLabels(
|
||||
oldest = oldest.time,
|
||||
newest = newest.time
|
||||
oldest = visibleTimeRange.first,
|
||||
newest = visibleTimeRange.second
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
@@ -168,12 +188,6 @@ private fun PowerMetricsChart(
|
||||
val currentDiff = Power.CURRENT.difference()
|
||||
val voltageDiff = Power.VOLTAGE.difference()
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
val screenWidth = LocalWindowInfo.current.containerSize.width
|
||||
val dp by remember(key1 = selectedTime) {
|
||||
mutableStateOf(selectedTime.dp(screenWidth, time = (newest.time - oldest.time).toLong()))
|
||||
}
|
||||
|
||||
Row {
|
||||
YAxisLabels(
|
||||
modifier = modifier.weight(weight = .1f),
|
||||
|
||||
@@ -49,6 +49,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -148,9 +149,29 @@ private fun SignalMetricsChart(
|
||||
}
|
||||
val timeDiff = newest.rxTime - oldest.rxTime
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
val configuration = LocalConfiguration.current
|
||||
val screenWidth = configuration.screenWidthDp
|
||||
val dp by remember(key1 = selectedTime) {
|
||||
mutableStateOf(selectedTime.dp(screenWidth, time = (newest.rxTime - oldest.rxTime).toLong()))
|
||||
}
|
||||
|
||||
// Calculate visible time range based on scroll position and chart width
|
||||
val visibleTimeRange = run {
|
||||
val density = LocalDensity.current
|
||||
val totalWidthPx = with(density) { dp.toPx() }
|
||||
val scrollPx = scrollState.value.toFloat()
|
||||
val visibleWidthPx = with(density) { screenWidth.dp.toPx() }
|
||||
val leftRatio = (scrollPx / totalWidthPx).coerceIn(0f, 1f)
|
||||
val rightRatio = ((scrollPx + visibleWidthPx) / totalWidthPx).coerceIn(0f, 1f)
|
||||
val visibleOldest = oldest.rxTime + (timeDiff * leftRatio).toInt()
|
||||
val visibleNewest = oldest.rxTime + (timeDiff * rightRatio).toInt()
|
||||
visibleOldest to visibleNewest
|
||||
}
|
||||
|
||||
TimeLabels(
|
||||
oldest = oldest.rxTime,
|
||||
newest = newest.rxTime
|
||||
oldest = visibleTimeRange.first,
|
||||
newest = visibleTimeRange.second
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
@@ -159,13 +180,6 @@ private fun SignalMetricsChart(
|
||||
val snrDiff = Metric.SNR.difference()
|
||||
val rssiDiff = Metric.RSSI.difference()
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
val configuration = LocalConfiguration.current
|
||||
val screenWidth = configuration.screenWidthDp
|
||||
val dp by remember(key1 = selectedTime) {
|
||||
mutableStateOf(selectedTime.dp(screenWidth, time = (newest.rxTime - oldest.rxTime).toLong()))
|
||||
}
|
||||
|
||||
Row {
|
||||
YAxisLabels(
|
||||
modifier = modifier.weight(weight = .1f),
|
||||
|
||||
Reference in New Issue
Block a user