mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-27 02:01:35 -04:00
feat: migrate to Material 3 Expressive APIs (#4934)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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<Telemetry>,
|
||||
@@ -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 =
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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<Pair<Int, Int>>,
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<Telemetry>,
|
||||
@@ -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
|
||||
|
||||
@@ -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<MeshPacket>,
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user