fix: fix animation stalls and update dependencies for stability (#4784)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2026-03-13 18:01:17 -05:00 committed by GitHub
parent 90844301e8
commit 427c0f3bbb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 384 additions and 243 deletions

View file

@ -30,10 +30,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.animateFloatingActionButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
@ -42,7 +40,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalContext
@ -68,7 +65,6 @@ import org.meshtastic.feature.node.component.NodeFilterTextField
import org.meshtastic.feature.node.component.NodeItem
import org.meshtastic.proto.SharedContact
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
fun NodeListScreen(
@ -125,21 +121,18 @@ fun NodeListScreen(
floatingActionButton = {
val shareCapable = ourNode?.capabilities?.supportsQrCodeSharing ?: false
val sharedContact: SharedContact? by viewModel.sharedContactRequested.collectAsStateWithLifecycle(null)
MeshtasticImportFAB(
sharedContact = sharedContact,
modifier =
Modifier.animateFloatingActionButton(
visible = !isScrollInProgress && connectionState == ConnectionState.Connected && shareCapable,
alignment = Alignment.BottomEnd,
),
onImport = { uriString ->
viewModel.handleScannedUri(uriString) {
scope.launch { context.showToast(Res.string.channel_invalid) }
}
},
onDismissSharedContact = { viewModel.setSharedContactRequested(null) },
isContactContext = true,
)
if (!isScrollInProgress && connectionState == ConnectionState.Connected && shareCapable) {
MeshtasticImportFAB(
sharedContact = sharedContact,
onImport = { uriString ->
viewModel.handleScannedUri(uriString) {
scope.launch { context.showToast(Res.string.channel_invalid) }
}
},
onDismissSharedContact = { viewModel.setSharedContactRequested(null) },
isContactContext = true,
)
}
},
) { contentPadding ->
Box(modifier = Modifier.fillMaxSize().padding(contentPadding).focusable()) {

View file

@ -70,7 +70,11 @@ import org.meshtastic.core.resources.air_utilization
import org.meshtastic.core.resources.battery
import org.meshtastic.core.resources.ch_util_definition
import org.meshtastic.core.resources.channel_utilization
import org.meshtastic.core.resources.device_metrics_label_value
import org.meshtastic.core.resources.device_metrics_log
import org.meshtastic.core.resources.device_metrics_numeric_value
import org.meshtastic.core.resources.device_metrics_percent_value
import org.meshtastic.core.resources.device_metrics_voltage_value
import org.meshtastic.core.resources.uptime
import org.meshtastic.core.resources.voltage
import org.meshtastic.core.ui.component.MaterialBatteryInfo
@ -240,16 +244,23 @@ private fun DeviceMetricsChart(
val voltageColor = Device.VOLTAGE.color
val chUtilColor = Device.CH_UTIL.color
val airUtilColor = Device.AIR_UTIL.color
val batteryLabel = stringResource(Res.string.battery)
val voltageLabel = stringResource(Res.string.voltage)
val channelUtilizationLabel = stringResource(Res.string.channel_utilization)
val airUtilizationLabel = stringResource(Res.string.air_utilization)
val percentValueTemplate = stringResource(Res.string.device_metrics_percent_value)
val voltageValueTemplate = stringResource(Res.string.device_metrics_voltage_value)
val numericValueTemplate = stringResource(Res.string.device_metrics_numeric_value)
val marker =
ChartStyling.rememberMarker(
valueFormatter =
ChartStyling.createColoredMarkerValueFormatter { value, color ->
when (color.copy(alpha = 1f)) {
batteryColor -> "Battery: %.1f%%".format(value)
voltageColor -> "Voltage: %.1f V".format(value)
chUtilColor -> "ChUtil: %.1f%%".format(value)
airUtilColor -> "AirUtil: %.1f%%".format(value)
else -> "%.1f".format(value)
batteryColor -> percentValueTemplate.format(batteryLabel, value)
voltageColor -> voltageValueTemplate.format(voltageLabel, value)
chUtilColor -> percentValueTemplate.format(channelUtilizationLabel, value)
airUtilColor -> percentValueTemplate.format(airUtilizationLabel, value)
else -> numericValueTemplate.format(value)
}
},
)
@ -422,6 +433,11 @@ private fun DeviceMetricsChartPreview() {
private fun DeviceMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick: () -> Unit) {
val deviceMetrics = telemetry.device_metrics
val time = telemetry.time.toLong() * MS_PER_SEC
val channelUtilizationLabel = stringResource(Res.string.channel_utilization)
val airUtilizationLabel = stringResource(Res.string.air_utilization)
val uptimeLabel = stringResource(Res.string.uptime)
val percentValueTemplate = stringResource(Res.string.device_metrics_percent_value)
val labelValueTemplate = stringResource(Res.string.device_metrics_label_value)
Card(
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 4.dp).clickable { onClick() },
border = if (isSelected) BorderStroke(2.dp, MaterialTheme.colorScheme.primary) else null,
@ -471,7 +487,11 @@ private fun DeviceMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick
MetricIndicator(Device.CH_UTIL.color)
Spacer(Modifier.width(4.dp))
Text(
text = "Ch: %.1f%%".format(deviceMetrics.channel_utilization ?: 0f),
text =
percentValueTemplate.format(
channelUtilizationLabel,
deviceMetrics.channel_utilization ?: 0f,
),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@ -481,7 +501,11 @@ private fun DeviceMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick
MetricIndicator(Device.AIR_UTIL.color)
Spacer(Modifier.width(4.dp))
Text(
text = "Air: %.1f%%".format(deviceMetrics.air_util_tx ?: 0f),
text =
percentValueTemplate.format(
airUtilizationLabel,
deviceMetrics.air_util_tx ?: 0f,
),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@ -489,9 +513,10 @@ private fun DeviceMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick
}
Text(
text =
stringResource(Res.string.uptime) +
": " +
labelValueTemplate.format(
uptimeLabel,
formatUptime(deviceMetrics?.uptime_seconds ?: 0),
),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)