From 1d6a3f44e4d8ef5675326725462af1ef83486f2a Mon Sep 17 00:00:00 2001 From: andrekir Date: Thu, 2 Jan 2025 06:42:10 -0300 Subject: [PATCH] feat: add `NodeDetail` request metadata --- .../geeksville/mesh/model/MetricsViewModel.kt | 12 ++++-- .../java/com/geeksville/mesh/ui/NodeDetail.kt | 39 +++++++++++++++---- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt b/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt index 310df5706..6720d019c 100644 --- a/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt @@ -40,6 +40,7 @@ import com.geeksville.mesh.database.MeshLogRepository import com.geeksville.mesh.database.entity.MeshLog import com.geeksville.mesh.model.map.CustomTileSource import com.geeksville.mesh.repository.datastore.RadioConfigRepository +import com.geeksville.mesh.service.ServiceAction import com.geeksville.mesh.ui.Route import com.geeksville.mesh.ui.map.MAP_STYLE_ID import dagger.hilt.android.lifecycle.HiltViewModel @@ -67,6 +68,7 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject data class MetricsState( + val isLocal: Boolean = false, val isManaged: Boolean = true, val isFahrenheit: Boolean = false, val displayUnits: DisplayUnits = DisplayUnits.METRIC, @@ -208,6 +210,10 @@ class MetricsViewModel @Inject constructor( meshLogRepository.deleteLogs(destNum, PortNum.POSITION_APP_VALUE) } + fun onServiceAction(action: ServiceAction) = viewModelScope.launch { + radioConfigRepository.onServiceAction(action) + } + private val _state = MutableStateFlow(MetricsState.Empty) val state: StateFlow = _state @@ -219,10 +225,10 @@ class MetricsViewModel @Inject constructor( init { @OptIn(ExperimentalCoroutinesApi::class) radioConfigRepository.nodeDBbyNum - .mapLatest { nodes -> nodes[destNum] } + .mapLatest { nodes -> nodes[destNum] to nodes.keys.firstOrNull() } .distinctUntilChanged() - .onEach { node -> - _state.update { state -> state.copy(node = node) } + .onEach { (node, ourNode) -> + _state.update { state -> state.copy(node = node, isLocal = destNum == ourNode) } node?.user?.hwModel?.let { hwModel -> _state.update { state -> state.copy(deviceHardware = getDeviceHardwareFromHardwareModel(hwModel)) diff --git a/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt b/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt index f0c6e783e..3c7b19e8c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt @@ -54,6 +54,7 @@ import androidx.compose.material.icons.filled.ChargingStation import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Height import androidx.compose.material.icons.filled.History +import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.KeyOff import androidx.compose.material.icons.filled.LightMode import androidx.compose.material.icons.filled.LocationOn @@ -95,6 +96,7 @@ import com.geeksville.mesh.R import com.geeksville.mesh.model.MetricsState import com.geeksville.mesh.model.MetricsViewModel import com.geeksville.mesh.model.Node +import com.geeksville.mesh.service.ServiceAction import com.geeksville.mesh.ui.components.PreferenceCategory import com.geeksville.mesh.ui.preview.NodePreviewParameterProvider import com.geeksville.mesh.ui.theme.AppTheme @@ -117,7 +119,12 @@ fun NodeDetailScreen( NodeDetailList( node = node, metricsState = state, - onNavigate = onNavigate, + onAction = { action -> + when (action) { + is Route -> onNavigate(action) + is ServiceAction -> viewModel.onServiceAction(action) + } + }, modifier = modifier, ) } else { @@ -135,7 +142,7 @@ private fun NodeDetailList( modifier: Modifier = Modifier, node: Node, metricsState: MetricsState, - onNavigate: (Any) -> Unit = {}, + onAction: (Any) -> Unit = {}, ) { LazyColumn( modifier = modifier.fillMaxSize(), @@ -172,10 +179,17 @@ private fun NodeDetailList( item { PreferenceCategory(stringResource(id = R.string.logs)) - LogNavigationList(metricsState, onNavigate) + LogNavigationList(metricsState, onAction) + } + + if (!metricsState.isLocal) { + item { + PreferenceCategory("Actions") + NodeActionList(node, onAction) + } } - if (!metricsState.isManaged) { + if (node.metadata != null && !metricsState.isManaged) { item { PreferenceCategory(stringResource(id = R.string.administration)) NavCard( @@ -183,7 +197,7 @@ private fun NodeDetailList( icon = Icons.Default.Settings, enabled = true ) { - onNavigate(Route.RadioConfig(node.num)) + onAction(Route.RadioConfig(node.num)) } } } @@ -320,7 +334,7 @@ private fun NodeDetailsContent( } @Composable -fun LogNavigationList(state: MetricsState, onNavigate: (Any) -> Unit) { +fun LogNavigationList(state: MetricsState, onNavigate: (Route) -> Unit) { NavCard( title = stringResource(R.string.device_metrics_log), icon = Icons.Default.ChargingStation, @@ -370,6 +384,17 @@ fun LogNavigationList(state: MetricsState, onNavigate: (Any) -> Unit) { } } +@Composable +fun NodeActionList(node: Node, onAction: (ServiceAction) -> Unit) { + NavCard( + title = "Request Metadata", + icon = Icons.Default.Info, + enabled = true + ) { + onAction(ServiceAction.GetDeviceMetadata(node.num)) + } +} + @Composable private fun InfoCard( icon: ImageVector, @@ -604,7 +629,7 @@ private fun PowerMetrics(node: Node) = with(node.powerMetrics) { @Preview(showBackground = true) @Composable -private fun NodeDetailsPreview( +private fun NodeDetailPreview( @PreviewParameter(NodePreviewParameterProvider::class) node: Node ) {