From 141b54ff9ceebf092794eb2ea420205ae1dc3706 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Thu, 26 Mar 2026 11:42:46 -0500 Subject: [PATCH] feat: migrate to Material 3 Expressive APIs (#4934) Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> --- .../core/ui/component/MainAppBar.kt | 28 +++--- .../meshtastic/core/ui/component/MenuFAB.kt | 97 ++++++------------- .../org/meshtastic/core/ui/theme/Theme.kt | 12 ++- .../feature/node/component/NodeItem.kt | 5 +- .../feature/node/list/NodeListScreen.kt | 26 +++-- .../feature/node/metrics/DeviceMetrics.kt | 8 +- .../node/metrics/EnvironmentMetrics.kt | 13 ++- .../feature/node/metrics/HostMetricsLog.kt | 4 +- .../node/metrics/MetricLogComponents.kt | 4 +- .../feature/node/metrics/PaxMetrics.kt | 5 +- .../feature/node/metrics/PowerMetrics.kt | 8 +- .../feature/node/metrics/SignalMetrics.kt | 5 +- 12 files changed, 112 insertions(+), 103 deletions(-) diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MainAppBar.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MainAppBar.kt index 2fde44e00..650c357b5 100644 --- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MainAppBar.kt +++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MainAppBar.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -39,7 +40,7 @@ import org.meshtastic.core.resources.Res import org.meshtastic.core.resources.ic_meshtastic import org.meshtastic.core.resources.navigate_back -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class) @Composable fun MainAppBar( modifier: Modifier = Modifier, @@ -54,26 +55,25 @@ fun MainAppBar( ) { TopAppBar( title = { - androidx.compose.foundation.layout.Column { + Text( + text = title, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleLarge, + ) + }, + subtitle = { + subtitle?.let { Text( - text = title, + text = it, maxLines = 1, overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.titleLarge, + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, ) - subtitle?.let { - Text( - text = it, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } } }, modifier = modifier, - navigationIcon = if (canNavigateUp) { { IconButton(onClick = onNavigateUp) { diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MenuFAB.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MenuFAB.kt index d5417716a..724e7e0dd 100644 --- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MenuFAB.kt +++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MenuFAB.kt @@ -16,34 +16,23 @@ */ package org.meshtastic.core.ui.component -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.slideOutVertically -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.OfflineShare import androidx.compose.material.icons.filled.Close -import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.FloatingActionButtonMenu +import androidx.compose.material3.FloatingActionButtonMenuItem import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SmallFloatingActionButton -import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.ToggleFloatingActionButton +import androidx.compose.material3.ToggleFloatingActionButtonDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag -import androidx.compose.ui.unit.dp +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun MenuFAB( expanded: Boolean, @@ -53,61 +42,31 @@ fun MenuFAB( contentDescription: String? = null, testTag: String? = null, ) { - Column( + FloatingActionButtonMenu( modifier = modifier.then(if (testTag != null) Modifier.testTag(testTag) else Modifier), + expanded = expanded, + button = { + ToggleFloatingActionButton( + checked = expanded, + onCheckedChange = onExpandedChange, + content = { + val imageVector = if (expanded) Icons.Filled.Close else Icons.AutoMirrored.Rounded.OfflineShare + Icon(imageVector = imageVector, contentDescription = contentDescription) + }, + containerColor = ToggleFloatingActionButtonDefaults.containerColor(), + ) + }, horizontalAlignment = Alignment.End, ) { - AnimatedVisibility( - visible = expanded, - enter = fadeIn() + slideInVertically(initialOffsetY = { it / 2 }), - exit = fadeOut() + slideOutVertically(targetOffsetY = { it / 2 }), - ) { - Column( - horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier.padding(bottom = 16.dp), - ) { - items.forEach { item -> - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp), - modifier = if (item.testTag != null) Modifier.testTag(item.testTag) else Modifier, - ) { - Surface( - shape = MaterialTheme.shapes.small, - color = MaterialTheme.colorScheme.surfaceContainerHigh, - modifier = Modifier.padding(end = 8.dp), - ) { - Text( - text = item.label, - style = MaterialTheme.typography.labelLarge, - modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp), - ) - } - SmallFloatingActionButton( - onClick = { - item.onClick() - onExpandedChange(false) - }, - ) { - Icon(item.icon, contentDescription = item.label) - } - } - } - } - } - - val rotation by animateFloatAsState(targetValue = if (expanded) 180f else 0f, label = "fab_rotation") - - FloatingActionButton( - onClick = { onExpandedChange(!expanded) }, - containerColor = MaterialTheme.colorScheme.primaryContainer, - contentColor = MaterialTheme.colorScheme.onPrimaryContainer, - ) { - Icon( - imageVector = if (expanded) Icons.Filled.Close else Icons.AutoMirrored.Rounded.OfflineShare, - contentDescription = contentDescription, - modifier = Modifier.rotate(rotation), + items.forEach { item -> + FloatingActionButtonMenuItem( + modifier = if (item.testTag != null) Modifier.testTag(item.testTag) else Modifier, + onClick = { + item.onClick() + onExpandedChange(false) + }, + icon = { Icon(item.icon, contentDescription = null) }, + text = { Text(item.label) }, ) } } diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/Theme.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/Theme.kt index ad9270a96..eb40222af 100644 --- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/Theme.kt +++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/Theme.kt @@ -19,7 +19,9 @@ package org.meshtastic.core.ui.theme import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.MaterialExpressiveTheme +import androidx.compose.material3.MotionScheme.Companion.expressive import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable @@ -265,6 +267,7 @@ data class ColorFamily(val color: Color, val onColor: Color, val colorContainer: val unspecified_scheme = ColorFamily(Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified) +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun AppTheme( darkTheme: Boolean = isSystemInDarkTheme(), @@ -276,7 +279,12 @@ fun AppTheme( val dynamicScheme = if (dynamicColor) dynamicColorScheme(darkTheme) else null val colorScheme = dynamicScheme ?: if (darkTheme) darkScheme else lightScheme - MaterialTheme(colorScheme = colorScheme, typography = AppTypography, content = content) + MaterialExpressiveTheme( + colorScheme = colorScheme, + typography = AppTypography, + motionScheme = expressive(), + content = content, + ) } const val MODE_DYNAMIC = 6969420 diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeItem.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeItem.kt index 15d317cae..a96501f6d 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeItem.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeItem.kt @@ -33,6 +33,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.Notes import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -95,6 +96,7 @@ private const val ACTIVE_ALPHA = 0.5f private const val INACTIVE_ALPHA = 0.2f private const val GRID_COLUMNS = 3 +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable @Suppress("LongMethod") fun NodeItem( @@ -389,6 +391,7 @@ private fun MetricsGrid(items: List<@Composable () -> Unit>) { } } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable private fun NodeItemHeader( thatNode: Node, @@ -424,7 +427,7 @@ private fun NodeItemHeader( ) { Text( text = longName, - style = MaterialTheme.typography.titleMedium.copy(fontStyle = style), + style = MaterialTheme.typography.titleMediumEmphasized.copy(fontStyle = style), textDecoration = TextDecoration.LineThrough.takeIf { isIgnored }, maxLines = 1, overflow = TextOverflow.Ellipsis, diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt index ec88674b3..9c2c208f4 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt @@ -30,8 +30,10 @@ 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 @@ -62,6 +64,7 @@ import org.meshtastic.feature.node.component.NodeFilterTextField import org.meshtastic.feature.node.component.NodeItem @Suppress("LongMethod", "CyclomaticComplexMethod") +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun NodeListScreen( navigateToNodeDetails: (Int) -> Unit, @@ -114,16 +117,19 @@ fun NodeListScreen( }, floatingActionButton = { val shareCapable = ourNode?.capabilities?.supportsQrCodeSharing ?: false - if (!isScrollInProgress && connectionState == ConnectionState.Connected && shareCapable) { - MeshtasticImportFAB( - onImport = { uriString -> - onHandleDeepLink(org.meshtastic.core.common.util.MeshtasticUri(uriString)) { - scope.launch { showToast(Res.string.channel_invalid) } - } - }, - isContactContext = true, - ) - } + MeshtasticImportFAB( + modifier = + Modifier.animateFloatingActionButton( + visible = !isScrollInProgress && connectionState == ConnectionState.Connected && shareCapable, + alignment = androidx.compose.ui.Alignment.BottomEnd, + ), + onImport = { uriString -> + onHandleDeepLink(org.meshtastic.core.common.util.MeshtasticUri(uriString)) { + scope.launch { showToast(Res.string.channel_invalid) } + } + }, + isContactContext = true, + ) }, ) { contentPadding -> Box(modifier = Modifier.fillMaxSize().padding(contentPadding).focusable()) { diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt index ebd4fcbd1..78f04396f 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt @@ -35,6 +35,7 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -214,6 +215,7 @@ fun DeviceMetricsScreen(viewModel: MetricsViewModel, onNavigateUp: () -> Unit) { @Suppress("LongMethod", "CyclomaticComplexMethod") @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun DeviceMetricsChart( modifier: Modifier = Modifier, telemetries: List, @@ -386,6 +388,7 @@ private fun DeviceMetricsChart( @Suppress("detekt:MagicNumber", "UnusedPrivateMember") // Compose preview with fake data @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun DeviceMetricsChartPreview() { val now = nowSeconds.toInt() val telemetries = @@ -416,6 +419,7 @@ private fun DeviceMetricsChartPreview() { @Composable @Suppress("LongMethod") +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun DeviceMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick: () -> Unit) { val deviceMetrics = telemetry.device_metrics val time = telemetry.time.toLong() * MS_PER_SEC @@ -444,7 +448,7 @@ private fun DeviceMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Text( text = CommonCharts.formatDateTime(time), - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleMediumEmphasized, fontWeight = FontWeight.Bold, ) @@ -518,6 +522,7 @@ private fun DeviceMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick @Suppress("detekt:MagicNumber", "UnusedPrivateMember") // Compose preview with fake data @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun DeviceMetricsCardPreview() { val now = nowSeconds.toInt() val telemetry = @@ -537,6 +542,7 @@ private fun DeviceMetricsCardPreview() { @Suppress("detekt:MagicNumber", "UnusedPrivateMember") // Compose preview with fake data @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun DeviceMetricsScreenPreview() { val now = nowSeconds.toInt() val telemetries = diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt index 400a014ce..863e09eec 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -119,6 +120,7 @@ fun EnvironmentMetricsScreen(viewModel: MetricsViewModel, onNavigateUp: () -> Un } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun TemperatureDisplay( envMetrics: org.meshtastic.proto.EnvironmentMetrics, environmentDisplayFahrenheit: Boolean, @@ -140,6 +142,7 @@ private fun TemperatureDisplay( } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun HumidityAndBarometricPressureDisplay(envMetrics: org.meshtastic.proto.EnvironmentMetrics) { val hasHumidity = envMetrics.relative_humidity?.let { !it.isNaN() } == true val hasPressure = envMetrics.barometric_pressure?.let { !it.isNaN() && it > 0 } == true @@ -180,6 +183,7 @@ private fun HumidityAndBarometricPressureDisplay(envMetrics: org.meshtastic.prot } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun SoilMetricsDisplay( envMetrics: org.meshtastic.proto.EnvironmentMetrics, environmentDisplayFahrenheit: Boolean, @@ -232,6 +236,7 @@ private fun SoilMetricsDisplay( } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun LuxUVLuxDisplay(envMetrics: org.meshtastic.proto.EnvironmentMetrics) { val hasLux = envMetrics.lux != null && !envMetrics.lux!!.isNaN() val hasUvLux = envMetrics.uv_lux != null && !envMetrics.uv_lux!!.isNaN() @@ -267,6 +272,7 @@ private fun LuxUVLuxDisplay(envMetrics: org.meshtastic.proto.EnvironmentMetrics) } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun VoltageCurrentDisplay(envMetrics: org.meshtastic.proto.EnvironmentMetrics) { val hasVoltage = envMetrics.voltage != null && !envMetrics.voltage!!.isNaN() val hasCurrent = envMetrics.current != null && !envMetrics.current!!.isNaN() @@ -294,6 +300,7 @@ private fun VoltageCurrentDisplay(envMetrics: org.meshtastic.proto.EnvironmentMe } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun GasCompositionDisplay(envMetrics: org.meshtastic.proto.EnvironmentMetrics) { val iaqValue = envMetrics.iaq val gasResistance = envMetrics.gas_resistance @@ -329,6 +336,7 @@ private fun GasCompositionDisplay(envMetrics: org.meshtastic.proto.EnvironmentMe } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun RadiationDisplay(envMetrics: org.meshtastic.proto.EnvironmentMetrics) { envMetrics.radiation?.let { radiation -> if (!radiation.isNaN() && radiation > 0f) { @@ -344,6 +352,7 @@ private fun RadiationDisplay(envMetrics: org.meshtastic.proto.EnvironmentMetrics } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun EnvironmentMetricsCard( telemetry: Telemetry, environmentDisplayFahrenheit: Boolean, @@ -370,6 +379,7 @@ private fun EnvironmentMetricsCard( } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun EnvironmentMetricsContent(telemetry: Telemetry, environmentDisplayFahrenheit: Boolean) { val envMetrics = telemetry.environment_metrics ?: org.meshtastic.proto.EnvironmentMetrics() val time = telemetry.time.toLong() * MS_PER_SEC @@ -378,7 +388,7 @@ private fun EnvironmentMetricsContent(telemetry: Telemetry, environmentDisplayFa Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Text( text = CommonCharts.formatDateTime(time), - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleMediumEmphasized, fontWeight = FontWeight.Bold, ) TemperatureDisplay(envMetrics, environmentDisplayFahrenheit) @@ -401,6 +411,7 @@ private fun EnvironmentMetricsContent(telemetry: Telemetry, environmentDisplayFa @Suppress("MagicNumber", "UnusedPrivateMember") // Compose preview with fake data @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun PreviewEnvironmentMetricsContent() { val fakeEnvMetrics = org.meshtastic.proto.EnvironmentMetrics( diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt index ad43346f3..2d0a9584e 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt @@ -32,6 +32,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LinearProgressIndicator @@ -103,6 +104,7 @@ fun HostMetricsLogScreen(metricsViewModel: MetricsViewModel, onNavigateUp: () -> @Suppress("LongMethod", "MagicNumber") @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) fun HostMetricsItem(modifier: Modifier = Modifier, telemetry: Telemetry) { val hostMetrics = telemetry.host_metrics val time = telemetry.time.toLong() * TimeConstants.MS_PER_SEC @@ -119,7 +121,7 @@ fun HostMetricsItem(modifier: Modifier = Modifier, telemetry: Telemetry) { modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.End, text = DateFormatter.formatDateTime(time), - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleMediumEmphasized, fontWeight = FontWeight.Bold, ) hostMetrics?.uptime_seconds?.let { diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/MetricLogComponents.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/MetricLogComponents.kt index a3962689c..4a928b98a 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/MetricLogComponents.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/MetricLogComponents.kt @@ -30,6 +30,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -48,6 +49,7 @@ import org.meshtastic.core.ui.icon.MeshtasticIcons /** Shared metric log/list UI components used by TracerouteLog, NeighborInfoLog, HostMetricsLog, and PositionLog. */ @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) fun MetricLogItem(icon: ImageVector, text: String, contentDescription: String, modifier: Modifier = Modifier) { Card( modifier = modifier.fillMaxWidth().heightIn(min = 64.dp).padding(vertical = 4.dp, horizontal = 8.dp), @@ -72,7 +74,7 @@ fun MetricLogItem(icon: ImageVector, text: String, contentDescription: String, m } Text( text = text, - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleMediumEmphasized, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurfaceVariant, ) diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt index ec810308a..c2dc2058d 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt @@ -30,6 +30,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -86,6 +87,7 @@ private val LEGEND_DATA = @Suppress("LongMethod") @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun PaxMetricsChart( modifier: Modifier = Modifier, totalSeries: List>, @@ -262,6 +264,7 @@ fun PaxcountInfo( } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) fun PaxMetricsItem(log: MeshLog, pax: ProtoPaxcount, isSelected: Boolean, onClick: () -> Unit) { Card( modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp).clickable { onClick() }, @@ -279,7 +282,7 @@ fun PaxMetricsItem(log: MeshLog, pax: ProtoPaxcount, isSelected: Boolean, onClic Column(modifier = Modifier.fillMaxWidth().padding(12.dp)) { Text( text = DateFormatter.formatDateTime(log.received_date), - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleMediumEmphasized, fontWeight = FontWeight.Bold, textAlign = TextAlign.End, modifier = Modifier.fillMaxWidth(), diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt index 8743efeb1..5501554bf 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.FilterChip import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -168,6 +169,7 @@ fun PowerMetricsScreen(viewModel: MetricsViewModel, onNavigateUp: () -> Unit) { @Suppress("LongMethod") @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun PowerMetricsChart( modifier: Modifier = Modifier, telemetries: List, @@ -295,6 +297,7 @@ private fun PowerMetricsChart( @Composable @Suppress("CyclomaticComplexMethod") +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun PowerMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick: () -> Unit) { val time = telemetry.time.toLong() * MS_PER_SEC Card( @@ -318,7 +321,7 @@ private fun PowerMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick: Row { Text( text = CommonCharts.formatDateTime(time), - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleMediumEmphasized, fontWeight = FontWeight.Bold, ) } @@ -347,6 +350,7 @@ private fun PowerMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick: } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun PowerChannelColumn(titleRes: StringResource, voltage: Float, current: Float) { Column { Text( @@ -376,6 +380,7 @@ private fun PowerChannelColumn(titleRes: StringResource, voltage: Float, current } /** Retrieves the appropriate voltage depending on `channelSelected`. */ +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun retrieveVoltage(channelSelected: PowerChannel, telemetry: Telemetry): Float = when (channelSelected) { PowerChannel.ONE -> telemetry.power_metrics?.ch1_voltage ?: Float.NaN PowerChannel.TWO -> telemetry.power_metrics?.ch2_voltage ?: Float.NaN @@ -383,6 +388,7 @@ private fun retrieveVoltage(channelSelected: PowerChannel, telemetry: Telemetry) } /** Retrieves the appropriate current depending on `channelSelected`. */ +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun retrieveCurrent(channelSelected: PowerChannel, telemetry: Telemetry): Float = when (channelSelected) { PowerChannel.ONE -> telemetry.power_metrics?.ch1_current ?: Float.NaN PowerChannel.TWO -> telemetry.power_metrics?.ch2_current ?: Float.NaN diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt index d53776354..f9c3d6955 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -134,6 +135,7 @@ fun SignalMetricsScreen(viewModel: MetricsViewModel, onNavigateUp: () -> Unit) { @Suppress("LongMethod", "CyclomaticComplexMethod") @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun SignalMetricsChart( modifier: Modifier = Modifier, meshPackets: List, @@ -245,6 +247,7 @@ private fun SignalMetricsChart( } @Composable +@OptIn(ExperimentalMaterial3ExpressiveApi::class) private fun SignalMetricsCard(meshPacket: MeshPacket, isSelected: Boolean, onClick: () -> Unit) { val time = meshPacket.rx_time.toLong() * MS_PER_SEC Card( @@ -270,7 +273,7 @@ private fun SignalMetricsCard(meshPacket: MeshPacket, isSelected: Boolean, onCli Row(horizontalArrangement = Arrangement.SpaceBetween) { Text( text = CommonCharts.formatDateTime(time), - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleMediumEmphasized, fontWeight = FontWeight.Bold, ) }