kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
feat: Scrollable x axis (#1445)
* Started horizontal scrolling for DeviceMetrics. Drawing lines based on the TimeFrame and setting the dp. * Wrote YAxisLabels(), it will replace the Y labels portion of the ChartOverlay(). The composable works for either side of the graph. * Wrote HorizontalLinesOverlay(), it will replace the horizontal lines portion of the ChartOverlay(). * Updated the data points to use their actual x values. * Based the width of the scrollable graph on time. * Added a date label to the TimeAxisOverlay.pull/1321/head
rodzic
3c581f81a8
commit
b3f4929cf4
|
@ -21,6 +21,8 @@ import android.app.Application
|
|||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -120,6 +122,48 @@ enum class TimeFrame(
|
|||
} else {
|
||||
System.currentTimeMillis() / 1000 - this.seconds
|
||||
}
|
||||
|
||||
/**
|
||||
* The time interval to draw the vertical lines representing
|
||||
* time on the x-axis.
|
||||
*
|
||||
* @return seconds epoch seconds
|
||||
*/
|
||||
fun lineInterval(): Long {
|
||||
return when (this.ordinal) {
|
||||
TWENTY_FOUR_HOURS.ordinal,
|
||||
FORTY_EIGHT_HOURS.ordinal ->
|
||||
TimeUnit.HOURS.toSeconds(1)
|
||||
ONE_WEEK.ordinal,
|
||||
TWO_WEEKS.ordinal ->
|
||||
TimeUnit.DAYS.toSeconds(1)
|
||||
else ->
|
||||
TimeUnit.DAYS.toSeconds(7)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the needed [Dp] depending on the amount of time being plotted.
|
||||
*
|
||||
* @param time in seconds
|
||||
*/
|
||||
fun dp(screenWidth: Int, time: Long): Dp {
|
||||
|
||||
val timePerScreen = when (this.ordinal) {
|
||||
TWENTY_FOUR_HOURS.ordinal,
|
||||
FORTY_EIGHT_HOURS.ordinal ->
|
||||
TimeUnit.HOURS.toSeconds(1)
|
||||
ONE_WEEK.ordinal,
|
||||
TWO_WEEKS.ordinal ->
|
||||
TimeUnit.DAYS.toSeconds(1)
|
||||
else ->
|
||||
TimeUnit.DAYS.toSeconds(7)
|
||||
}
|
||||
|
||||
val multiplier = time / timePerScreen
|
||||
val dp = (screenWidth * multiplier).toInt().dp
|
||||
return dp.takeIf { it != 0.dp } ?: screenWidth.dp
|
||||
}
|
||||
}
|
||||
|
||||
private fun MeshPacket.hasValidSignal(): Boolean =
|
||||
|
|
|
@ -59,21 +59,25 @@ import androidx.compose.ui.unit.sp
|
|||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.LINE_LIMIT
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TEXT_PAINT_ALPHA
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.DATE_TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.LEFT_LABEL_SPACING
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
|
||||
import java.text.DateFormat
|
||||
|
||||
object CommonCharts {
|
||||
val TIME_FORMAT: DateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
|
||||
val DATE_TIME_FORMAT: DateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
|
||||
const val X_AXIS_SPACING = 8f
|
||||
const val LEFT_LABEL_SPACING = 36
|
||||
const val MS_PER_SEC = 1000.0f
|
||||
const val MS_PER_SEC = 1000L
|
||||
const val LINE_LIMIT = 4
|
||||
const val TEXT_PAINT_ALPHA = 192
|
||||
}
|
||||
|
||||
private val TIME_FORMAT: DateFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM)
|
||||
private val DATE_FORMAT: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT)
|
||||
private const val LINE_ON = 10f
|
||||
private const val LINE_OFF = 20f
|
||||
private const val DATE_Y = 32f
|
||||
|
||||
data class LegendData(val nameRes: Int, val color: Color, val isLine: Boolean = false)
|
||||
|
||||
|
@ -100,6 +104,7 @@ fun ChartHeader(amount: Int) {
|
|||
* @param lineColors A list of 5 `Color`s for the chart lines, 0 being the lowest line on the chart.
|
||||
* @param leaveSpace When true the lines will leave space for Y labels on the left side of the graph.
|
||||
*/
|
||||
@Deprecated("Will soon be replaced with YAxisLabels() and HorizontalLines()", level = DeprecationLevel.WARNING)
|
||||
@Composable
|
||||
fun ChartOverlay(
|
||||
modifier: Modifier,
|
||||
|
@ -160,14 +165,162 @@ fun ChartOverlay(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws chart lines with respect to the Y-axis range; defined by (`maxValue` - `minValue`).
|
||||
*
|
||||
* @param lineColors A list of 5 `Color`s for the chart lines, 0 being the lowest line on the chart.
|
||||
*/
|
||||
@Composable
|
||||
fun HorizontalLinesOverlay(
|
||||
modifier: Modifier,
|
||||
lineColors: List<Color>,
|
||||
minValue: Float,
|
||||
maxValue: Float,
|
||||
) {
|
||||
val range = maxValue - minValue
|
||||
val verticalSpacing = range / LINE_LIMIT
|
||||
Canvas(modifier = modifier) {
|
||||
|
||||
val lineStart = 0f
|
||||
val height = size.height
|
||||
val width = size.width
|
||||
|
||||
/* Horizontal Lines */
|
||||
var lineY = minValue
|
||||
for (i in 0..LINE_LIMIT) {
|
||||
val ratio = (lineY - minValue) / range
|
||||
val y = height - (ratio * height)
|
||||
drawLine(
|
||||
start = Offset(lineStart, y),
|
||||
end = Offset(width, y),
|
||||
color = lineColors[i],
|
||||
strokeWidth = 1.dp.toPx(),
|
||||
cap = StrokeCap.Round,
|
||||
pathEffect = PathEffect.dashPathEffect(floatArrayOf(LINE_ON, LINE_OFF), 0f)
|
||||
)
|
||||
lineY += verticalSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws labels on the Y-axis with respect to the range. Defined by (`maxValue` - `minValue`).
|
||||
*/
|
||||
@Composable
|
||||
fun YAxisLabels(
|
||||
modifier: Modifier,
|
||||
labelColor: Color,
|
||||
minValue: Float,
|
||||
maxValue: Float,
|
||||
) {
|
||||
val range = maxValue - minValue
|
||||
val verticalSpacing = range / LINE_LIMIT
|
||||
val density = LocalDensity.current
|
||||
Canvas(modifier = modifier) {
|
||||
|
||||
val height = size.height
|
||||
|
||||
/* Y Labels */
|
||||
val textPaint = Paint().apply {
|
||||
color = labelColor.toArgb()
|
||||
textAlign = Paint.Align.LEFT
|
||||
textSize = density.run { 12.dp.toPx() }
|
||||
typeface = setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD))
|
||||
alpha = TEXT_PAINT_ALPHA
|
||||
}
|
||||
|
||||
drawContext.canvas.nativeCanvas.apply {
|
||||
var label = minValue
|
||||
for (i in 0..LINE_LIMIT) {
|
||||
val ratio = (label - minValue) / range
|
||||
val y = height - (ratio * height)
|
||||
drawText(
|
||||
"${label.toInt()}",
|
||||
0f,
|
||||
y + 4.dp.toPx(),
|
||||
textPaint
|
||||
)
|
||||
label += verticalSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the vertical lines to help the user relate the plotted data within a time frame.
|
||||
*/
|
||||
@Composable
|
||||
fun TimeAxisOverlay(
|
||||
modifier: Modifier,
|
||||
oldest: Int,
|
||||
newest: Int,
|
||||
timeInterval: Long
|
||||
) {
|
||||
|
||||
val range = newest - oldest
|
||||
val density = LocalDensity.current
|
||||
val lineColor = MaterialTheme.colors.onSurface
|
||||
Canvas(modifier = modifier) {
|
||||
|
||||
val height = size.height
|
||||
val width = size.width - 28.dp.toPx()
|
||||
|
||||
/* Cut out the time remaining in order to place the lines on the dot. */
|
||||
val timeRemaining = oldest % timeInterval
|
||||
var current = oldest.toLong()
|
||||
current -= timeRemaining
|
||||
current += timeInterval
|
||||
|
||||
val textPaint = Paint().apply {
|
||||
color = lineColor.toArgb()
|
||||
textAlign = Paint.Align.LEFT
|
||||
textSize = density.run { 12.dp.toPx() }
|
||||
typeface = setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD))
|
||||
alpha = TEXT_PAINT_ALPHA
|
||||
}
|
||||
|
||||
/* Vertical Lines with labels */
|
||||
drawContext.canvas.nativeCanvas.apply {
|
||||
while (current <= newest) {
|
||||
val ratio = (current - oldest).toFloat() / range
|
||||
val x = (ratio * width)
|
||||
drawLine(
|
||||
start = Offset(x, 0f),
|
||||
end = Offset(x, height),
|
||||
color = lineColor,
|
||||
strokeWidth = 1.dp.toPx(),
|
||||
cap = StrokeCap.Round,
|
||||
pathEffect = PathEffect.dashPathEffect(floatArrayOf(LINE_ON, LINE_OFF), 0f)
|
||||
)
|
||||
|
||||
/* Time */
|
||||
drawText(
|
||||
TIME_FORMAT.format(current * MS_PER_SEC),
|
||||
x,
|
||||
0f,
|
||||
textPaint
|
||||
)
|
||||
/* Date */
|
||||
drawText(
|
||||
DATE_FORMAT.format(current * MS_PER_SEC),
|
||||
x,
|
||||
DATE_Y,
|
||||
textPaint
|
||||
)
|
||||
current += timeInterval
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the `oldest` and `newest` times for the respective telemetry data.
|
||||
* Expects time in milliseconds
|
||||
* Expects time in seconds.
|
||||
*/
|
||||
@Composable
|
||||
fun TimeLabels(
|
||||
oldest: Float,
|
||||
newest: Float
|
||||
oldest: Int,
|
||||
newest: Int
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
@ -175,14 +328,14 @@ fun TimeLabels(
|
|||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = TIME_FORMAT.format(oldest),
|
||||
text = DATE_TIME_FORMAT.format(oldest * MS_PER_SEC),
|
||||
modifier = Modifier.wrapContentWidth(),
|
||||
style = TextStyle(fontWeight = FontWeight.Bold),
|
||||
fontSize = 12.sp,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
text = TIME_FORMAT.format(newest),
|
||||
text = DATE_TIME_FORMAT.format(newest * MS_PER_SEC),
|
||||
modifier = Modifier.wrapContentWidth(),
|
||||
style = TextStyle(fontWeight = FontWeight.Bold),
|
||||
fontSize = 12.sp
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package com.geeksville.mesh.ui.components
|
||||
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
@ -28,8 +29,10 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.MaterialTheme
|
||||
|
@ -48,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.LocalConfiguration
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
@ -57,10 +61,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
import com.geeksville.mesh.ui.BatteryInfo
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.X_AXIS_SPACING
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.DATE_TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.theme.Orange
|
||||
|
||||
private val DEVICE_METRICS_COLORS = listOf(Color.Green, Color.Magenta, Color.Cyan)
|
||||
|
@ -102,6 +106,7 @@ fun DeviceMetricsScreen(
|
|||
.fillMaxWidth()
|
||||
.fillMaxHeight(fraction = 0.33f),
|
||||
data.reversed(),
|
||||
selectedTimeFrame,
|
||||
promptInfoDialog = { displayInfoDialog = true }
|
||||
)
|
||||
|
||||
|
@ -126,95 +131,133 @@ fun DeviceMetricsScreen(
|
|||
private fun DeviceMetricsChart(
|
||||
modifier: Modifier = Modifier,
|
||||
telemetries: List<Telemetry>,
|
||||
selectedTime: TimeFrame,
|
||||
promptInfoDialog: () -> Unit
|
||||
) {
|
||||
|
||||
ChartHeader(amount = telemetries.size)
|
||||
if (telemetries.isEmpty()) return
|
||||
|
||||
val (oldest, newest) = remember(key1 = telemetries) {
|
||||
Pair(
|
||||
telemetries.minBy { it.time },
|
||||
telemetries.maxBy { it.time }
|
||||
)
|
||||
}
|
||||
val timeDiff = newest.time - oldest.time
|
||||
|
||||
TimeLabels(
|
||||
oldest = telemetries.first().time * MS_PER_SEC,
|
||||
newest = telemetries.last().time * MS_PER_SEC
|
||||
oldest = oldest.time,
|
||||
newest = newest.time
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
val graphColor = MaterialTheme.colors.onSurface
|
||||
val spacing = X_AXIS_SPACING
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
Box(contentAlignment = Alignment.TopStart) {
|
||||
val configuration = LocalConfiguration.current
|
||||
val screenWidth = configuration.screenWidthDp
|
||||
val dp by remember(key1 = selectedTime) {
|
||||
mutableStateOf(selectedTime.dp(screenWidth, time = (newest.time - oldest.time).toLong()))
|
||||
}
|
||||
|
||||
/*
|
||||
* The order of the colors are with respect to the ChUtil.
|
||||
* 25 - 49 Orange
|
||||
* 50 - 100 Red
|
||||
*/
|
||||
ChartOverlay(
|
||||
modifier,
|
||||
Row {
|
||||
Box(
|
||||
contentAlignment = Alignment.TopStart,
|
||||
modifier = Modifier
|
||||
.horizontalScroll(scrollState)
|
||||
.weight(1f)
|
||||
) {
|
||||
|
||||
/*
|
||||
* The order of the colors are with respect to the ChUtil.
|
||||
* 25 - 49 Orange
|
||||
* 50 - 100 Red
|
||||
*/
|
||||
HorizontalLinesOverlay(
|
||||
modifier.width(dp),
|
||||
lineColors = listOf(graphColor, Orange, Color.Red, graphColor, graphColor),
|
||||
minValue = 0f,
|
||||
maxValue = 100f
|
||||
)
|
||||
|
||||
TimeAxisOverlay(
|
||||
modifier.width(dp),
|
||||
oldest = oldest.time,
|
||||
newest = newest.time,
|
||||
selectedTime.lineInterval()
|
||||
)
|
||||
|
||||
/* Plot Battery Line, ChUtil, and AirUtilTx */
|
||||
Canvas(modifier = modifier.width(dp)) {
|
||||
|
||||
val height = size.height
|
||||
val width = size.width
|
||||
val dataPointRadius = 2.dp.toPx()
|
||||
val strokePath = Path().apply {
|
||||
for (i in telemetries.indices) {
|
||||
val telemetry = telemetries[i]
|
||||
|
||||
/* x-value for all three */
|
||||
val x1Ratio = (telemetry.time - oldest.time).toFloat() / timeDiff
|
||||
val x1 = x1Ratio * width
|
||||
|
||||
/* Channel Utilization */
|
||||
val chUtilRatio =
|
||||
telemetry.deviceMetrics.channelUtilization / MAX_PERCENT_VALUE
|
||||
val yChUtil = height - (chUtilRatio * height)
|
||||
drawCircle(
|
||||
color = DEVICE_METRICS_COLORS[Device.CH_UTIL.ordinal],
|
||||
radius = dataPointRadius,
|
||||
center = Offset(x1, yChUtil)
|
||||
)
|
||||
|
||||
/* Air Utilization Transmit */
|
||||
val airUtilRatio = telemetry.deviceMetrics.airUtilTx / MAX_PERCENT_VALUE
|
||||
val yAirUtil = height - (airUtilRatio * height)
|
||||
drawCircle(
|
||||
color = DEVICE_METRICS_COLORS[Device.AIR_UTIL.ordinal],
|
||||
radius = dataPointRadius,
|
||||
center = Offset(x1, yAirUtil)
|
||||
)
|
||||
|
||||
/* Battery line */
|
||||
val nextTelemetry = telemetries.getOrNull(i + 1) ?: telemetries.last()
|
||||
val y1Ratio = telemetry.deviceMetrics.batteryLevel / MAX_PERCENT_VALUE
|
||||
val y1 = height - (y1Ratio * height)
|
||||
|
||||
val x2Ratio = (nextTelemetry.time - oldest.time).toFloat() / timeDiff
|
||||
val x2 = x2Ratio * width
|
||||
|
||||
val y2Ratio = nextTelemetry.deviceMetrics.batteryLevel / MAX_PERCENT_VALUE
|
||||
val y2 = height - (y2Ratio * height)
|
||||
|
||||
if (i == 0) {
|
||||
moveTo(x1, y1)
|
||||
}
|
||||
|
||||
quadraticTo(x1, y1, (x1 + x2) / 2f, (y1 + y2) / 2f)
|
||||
}
|
||||
}
|
||||
|
||||
/* Battery Line */
|
||||
drawPath(
|
||||
path = strokePath,
|
||||
color = DEVICE_METRICS_COLORS[Device.BATTERY.ordinal],
|
||||
style = Stroke(
|
||||
width = dataPointRadius,
|
||||
cap = StrokeCap.Round
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
YAxisLabels(
|
||||
modifier = modifier.weight(weight = .1f),
|
||||
graphColor,
|
||||
lineColors = listOf(graphColor, Orange, Color.Red, graphColor, graphColor),
|
||||
minValue = 0f,
|
||||
maxValue = 100f
|
||||
)
|
||||
|
||||
/* Plot Battery Line, ChUtil, and AirUtilTx */
|
||||
Canvas(modifier = modifier) {
|
||||
|
||||
val height = size.height
|
||||
val width = size.width - 28.dp.toPx()
|
||||
val spacePerEntry = (width - spacing) / telemetries.size
|
||||
val dataPointRadius = 2.dp.toPx()
|
||||
var lastX: Float
|
||||
val strokePath = Path().apply {
|
||||
for (i in telemetries.indices) {
|
||||
val telemetry = telemetries[i]
|
||||
val nextTelemetry = telemetries.getOrNull(i + 1) ?: telemetries.last()
|
||||
val leftRatio = telemetry.deviceMetrics.batteryLevel / MAX_PERCENT_VALUE
|
||||
val rightRatio = nextTelemetry.deviceMetrics.batteryLevel / MAX_PERCENT_VALUE
|
||||
|
||||
val x1 = spacing + i * spacePerEntry
|
||||
val y1 = height - (leftRatio * height)
|
||||
|
||||
/* Channel Utilization */
|
||||
val chUtilRatio = telemetry.deviceMetrics.channelUtilization / MAX_PERCENT_VALUE
|
||||
val yChUtil = height - (chUtilRatio * height)
|
||||
drawCircle(
|
||||
color = DEVICE_METRICS_COLORS[Device.CH_UTIL.ordinal],
|
||||
radius = dataPointRadius,
|
||||
center = Offset(x1, yChUtil)
|
||||
)
|
||||
|
||||
/* Air Utilization Transmit */
|
||||
val airUtilRatio = telemetry.deviceMetrics.airUtilTx / MAX_PERCENT_VALUE
|
||||
val yAirUtil = height - (airUtilRatio * height)
|
||||
drawCircle(
|
||||
color = DEVICE_METRICS_COLORS[Device.AIR_UTIL.ordinal],
|
||||
radius = dataPointRadius,
|
||||
center = Offset(x1, yAirUtil)
|
||||
)
|
||||
|
||||
val x2 = spacing + (i + 1) * spacePerEntry
|
||||
val y2 = height - (rightRatio * height)
|
||||
if (i == 0) {
|
||||
moveTo(x1, y1)
|
||||
}
|
||||
|
||||
lastX = (x1 + x2) / 2f
|
||||
|
||||
quadraticTo(x1, y1, lastX, (y1 + y2) / 2f)
|
||||
}
|
||||
}
|
||||
|
||||
/* Battery Line */
|
||||
drawPath(
|
||||
path = strokePath,
|
||||
color = DEVICE_METRICS_COLORS[Device.BATTERY.ordinal],
|
||||
style = Stroke(
|
||||
width = dataPointRadius,
|
||||
cap = StrokeCap.Round
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
|
@ -246,7 +289,7 @@ private fun DeviceMetricsCard(telemetry: Telemetry) {
|
|||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = TIME_FORMAT.format(time),
|
||||
text = DATE_TIME_FORMAT.format(time),
|
||||
style = TextStyle(fontWeight = FontWeight.Bold),
|
||||
fontSize = MaterialTheme.typography.button.fontSize
|
||||
)
|
||||
|
|
|
@ -63,7 +63,7 @@ import com.geeksville.mesh.copy
|
|||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.X_AXIS_SPACING
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.DATE_TIME_FORMAT
|
||||
|
||||
private val ENVIRONMENT_METRICS_COLORS = listOf(Color.Red, Color.Blue, Color.Green)
|
||||
private enum class Environment {
|
||||
|
@ -170,8 +170,8 @@ private fun EnvironmentMetricsChart(
|
|||
return
|
||||
}
|
||||
TimeLabels(
|
||||
oldest = telemetries.first().time * MS_PER_SEC,
|
||||
newest = telemetries.last().time * MS_PER_SEC
|
||||
oldest = telemetries.first().time,
|
||||
newest = telemetries.last().time
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
@ -428,7 +428,7 @@ private fun EnvironmentMetricsCard(telemetry: Telemetry, environmentDisplayFahre
|
|||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = TIME_FORMAT.format(time),
|
||||
text = DATE_TIME_FORMAT.format(time),
|
||||
style = TextStyle(fontWeight = FontWeight.Bold),
|
||||
fontSize = MaterialTheme.typography.button.fontSize
|
||||
)
|
||||
|
|
|
@ -64,7 +64,7 @@ import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
|
|||
import com.geeksville.mesh.ui.components.CommonCharts.LINE_LIMIT
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TEXT_PAINT_ALPHA
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.LEFT_LABEL_SPACING
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.DATE_TIME_FORMAT
|
||||
|
||||
private val METRICS_COLORS = listOf(Color.Green, Color.Blue)
|
||||
|
||||
|
@ -139,8 +139,8 @@ private fun SignalMetricsChart(
|
|||
}
|
||||
|
||||
TimeLabels(
|
||||
oldest = meshPackets.first().rxTime * MS_PER_SEC,
|
||||
newest = meshPackets.last().rxTime * MS_PER_SEC
|
||||
oldest = meshPackets.first().rxTime,
|
||||
newest = meshPackets.last().rxTime
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
@ -274,7 +274,7 @@ private fun SignalMetricsCard(meshPacket: MeshPacket) {
|
|||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = TIME_FORMAT.format(time),
|
||||
text = DATE_TIME_FORMAT.format(time),
|
||||
style = TextStyle(fontWeight = FontWeight.Bold),
|
||||
fontSize = MaterialTheme.typography.button.fontSize
|
||||
)
|
||||
|
|
Ładowanie…
Reference in New Issue