refactor: null safety, update date/time libraries, and migrate tests (#4900)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2026-03-23 18:17:50 -05:00 committed by GitHub
parent f826cac6c8
commit 664ebf218e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
163 changed files with 503 additions and 4993 deletions

View file

@ -92,17 +92,13 @@ class CompassViewModel(
updatesJob?.cancel()
updatesJob =
viewModelScope.launch {
combine(headingProvider.headingUpdates(), phoneLocationProvider.locationUpdates()) {
heading,
location,
->
buildState(heading, location)
}
.flowOn(dispatchers.default)
.collect { _uiState.value = it }
updatesJob = viewModelScope.launch {
combine(headingProvider.headingUpdates(), phoneLocationProvider.locationUpdates()) { heading, location ->
buildState(heading, location)
}
.flowOn(dispatchers.default)
.collect { _uiState.value = it }
}
}
fun stop() {

View file

@ -114,8 +114,9 @@ data class EnvironmentMetricsState(val environmentMetrics: List<Telemetry> = emp
}
// Relative Humidity
val humidities =
telemetries.mapNotNull { it.environment_metrics?.relative_humidity?.takeIf { !it.isNaN() && it != 0.0f } }
val humidities = telemetries.mapNotNull {
it.environment_metrics?.relative_humidity?.takeIf { !it.isNaN() && it != 0.0f }
}
if (humidities.isNotEmpty()) {
minValues.add(humidities.minOf { it })
maxValues.add(humidities.maxOf { it })
@ -123,8 +124,9 @@ data class EnvironmentMetricsState(val environmentMetrics: List<Telemetry> = emp
}
// Soil Temperature
val soilTemperatures =
telemetries.mapNotNull { it.environment_metrics?.soil_temperature?.takeIf { !it.isNaN() } }
val soilTemperatures = telemetries.mapNotNull {
it.environment_metrics?.soil_temperature?.takeIf { !it.isNaN() }
}
if (soilTemperatures.isNotEmpty()) {
var minSoilTemperatureValue = soilTemperatures.minOf { it }
var maxSoilTemperatureValue = soilTemperatures.maxOf { it }
@ -138,8 +140,9 @@ data class EnvironmentMetricsState(val environmentMetrics: List<Telemetry> = emp
}
// Soil Moisture
val soilMoistures =
telemetries.mapNotNull { it.environment_metrics?.soil_moisture?.takeIf { it != Int.MIN_VALUE } }
val soilMoistures = telemetries.mapNotNull {
it.environment_metrics?.soil_moisture?.takeIf { it != Int.MIN_VALUE }
}
if (soilMoistures.isNotEmpty()) {
minValues.add(soilMoistures.minOf { it.toFloat() })
maxValues.add(soilMoistures.maxOf { it.toFloat() })

View file

@ -35,7 +35,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import okio.ByteString.Companion.decodeBase64
@ -71,6 +70,7 @@ import org.meshtastic.feature.node.model.MetricsState
import org.meshtastic.feature.node.model.TimeFrame
import org.meshtastic.proto.PortNum
import org.meshtastic.proto.Telemetry
import kotlin.time.Instant
import org.meshtastic.proto.Paxcount as ProtoPaxcount
/**

View file

@ -148,47 +148,43 @@ fun TracerouteLogScreen(
val (text, icon) = route.getTextAndIcon()
var expanded by remember { mutableStateOf(false) }
val tracerouteDetailsAnnotated: AnnotatedString? =
result?.let { res ->
if (route != null && route.route.isNotEmpty() && route.route_back.isNotEmpty()) {
val seconds =
(res.received_date - log.received_date).coerceAtLeast(0).toDouble() / MS_PER_SEC
val annotatedBase =
annotateTraceroute(
res.fromRadio.packet?.getTracerouteResponse(
::getUsername,
headerTowards = stringResource(Res.string.traceroute_route_towards_dest),
headerBack = headerBackStr,
),
statusGreen = statusGreen,
statusYellow = statusYellow,
statusOrange = statusOrange,
)
val durationText =
stringResource(Res.string.traceroute_duration, formatString("%.1f", seconds))
buildAnnotatedString {
append(annotatedBase)
append("\n\n$durationText")
}
} else {
// For cases where there's a result but no full route, display plain text
res.fromRadio.packet
?.getTracerouteResponse(
val tracerouteDetailsAnnotated: AnnotatedString? = result?.let { res ->
if (route != null && route.route.isNotEmpty() && route.route_back.isNotEmpty()) {
val seconds = (res.received_date - log.received_date).coerceAtLeast(0).toDouble() / MS_PER_SEC
val annotatedBase =
annotateTraceroute(
res.fromRadio.packet?.getTracerouteResponse(
::getUsername,
headerTowards = stringResource(Res.string.traceroute_route_towards_dest),
headerBack = headerBackStr,
)
?.let { AnnotatedString(it) }
),
statusGreen = statusGreen,
statusYellow = statusYellow,
statusOrange = statusOrange,
)
val durationText = stringResource(Res.string.traceroute_duration, formatString("%.1f", seconds))
buildAnnotatedString {
append(annotatedBase)
append("\n\n$durationText")
}
} else {
// For cases where there's a result but no full route, display plain text
res.fromRadio.packet
?.getTracerouteResponse(
::getUsername,
headerTowards = stringResource(Res.string.traceroute_route_towards_dest),
headerBack = headerBackStr,
)
?.let { AnnotatedString(it) }
}
val overlay =
route?.let {
TracerouteOverlay(
requestId = log.fromRadio.packet?.id ?: 0,
forwardRoute = it.route,
returnRoute = it.route_back,
)
}
}
val overlay = route?.let {
TracerouteOverlay(
requestId = log.fromRadio.packet?.id ?: 0,
forwardRoute = it.route,
returnRoute = it.route_back,
)
}
Box {
MetricLogItem(