Refactor NodeDetails to Metrics (#1222)

* Synced the string used for ChUtil and AirUtilTX in the NodeInfo and Device Metrics.

* Refactored NodeDetails to Metrics.

* Added string resources for "Hops Away".
This commit is contained in:
Robert-0410 2024-09-08 03:36:44 -07:00 committed by GitHub
parent d788195340
commit 2c426d470c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 51 additions and 50 deletions

View file

@ -16,27 +16,27 @@ import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.enums.EnumEntries
enum class NodeDetailPage(
enum class MetricsPage(
@StringRes val titleResId: Int,
@DrawableRes val drawableResId: Int,
) {
DEVICE(R.string.device_metrics, R.drawable.baseline_charging_station_24),
ENVIRONMENT(R.string.environment_metrics, R.drawable.baseline_thermostat_24),
DEVICE(R.string.device, R.drawable.baseline_charging_station_24),
ENVIRONMENT(R.string.environment, R.drawable.baseline_thermostat_24),
}
data class NodeDetailsState(
val pages: EnumEntries<NodeDetailPage> = NodeDetailPage.entries,
data class MetricsState(
val pages: EnumEntries<MetricsPage> = MetricsPage.entries,
val isLoading: Boolean = false,
val deviceMetrics: List<Telemetry> = emptyList(),
val environmentMetrics: List<Telemetry> = emptyList(),
) {
companion object {
val Empty = NodeDetailsState()
val Empty = MetricsState()
}
}
@HiltViewModel
class NodeDetailsViewModel @Inject constructor(
class MetricsViewModel @Inject constructor(
val nodeDB: NodeDB,
private val meshLogRepository: MeshLogRepository
) : ViewModel() {
@ -50,7 +50,7 @@ class NodeDetailsViewModel @Inject constructor(
_deviceMetrics,
_environmentMetrics,
) { isLoading, device, environment ->
NodeDetailsState(
MetricsState(
isLoading = isLoading,
deviceMetrics = device,
environmentMetrics = environment,
@ -58,7 +58,7 @@ class NodeDetailsViewModel @Inject constructor(
}.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5_000),
initialValue = NodeDetailsState.Empty,
initialValue = MetricsState.Empty,
)
/**

View file

@ -41,29 +41,29 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.R
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.model.NodeDetailPage
import com.geeksville.mesh.model.NodeDetailsState
import com.geeksville.mesh.model.NodeDetailsViewModel
import com.geeksville.mesh.model.MetricsPage
import com.geeksville.mesh.model.MetricsState
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.ui.components.DeviceMetricsScreen
import com.geeksville.mesh.ui.components.EnvironmentMetricsScreen
import com.geeksville.mesh.ui.theme.AppTheme
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
internal fun FragmentManager.navigateToNodeDetails(nodeNum: Int? = null) {
val nodeDetailsFragment = NodeDetailsFragment().apply {
internal fun FragmentManager.navigateToMetrics(nodeNum: Int? = null) {
val metricsFragment = MetricsFragment().apply {
arguments = bundleOf("nodeNum" to nodeNum)
}
beginTransaction()
.replace(R.id.mainActivityLayout, nodeDetailsFragment)
.replace(R.id.mainActivityLayout, metricsFragment)
.addToBackStack(null)
.commit()
}
@AndroidEntryPoint
class NodeDetailsFragment : ScreenFragment("NodeDetails"), Logging {
class MetricsFragment : ScreenFragment("Metrics"), Logging {
private val model: NodeDetailsViewModel by viewModels()
private val model: MetricsViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
@ -80,7 +80,7 @@ class NodeDetailsFragment : ScreenFragment("NodeDetails"), Logging {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
AppTheme {
NodeDetailsScreen(
MetricsScreen(
model = model,
nodeName = nodeName,
navigateBack = {
@ -95,8 +95,8 @@ class NodeDetailsFragment : ScreenFragment("NodeDetails"), Logging {
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun NodeDetailsScreen(
model: NodeDetailsViewModel = hiltViewModel(),
fun MetricsScreen(
model: MetricsViewModel = hiltViewModel(),
nodeName: String?,
navigateBack: () -> Unit,
) {
@ -113,7 +113,7 @@ fun NodeDetailsScreen(
contentColor = colorResource(R.color.toolbarText),
title = {
Text(
text = "${stringResource(R.string.node_details)}: $nodeName",
text = "${stringResource(R.string.metrics)}: $nodeName",
)
},
navigationIcon = {
@ -127,7 +127,7 @@ fun NodeDetailsScreen(
)
},
) { innerPadding ->
NodeDetailsPagerScreen(
MetricsPagerScreen(
state = state,
pagerState = pagerState,
modifier = Modifier.padding(top = innerPadding.calculateTopPadding())
@ -137,8 +137,8 @@ fun NodeDetailsScreen(
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun NodeDetailsPagerScreen(
state: NodeDetailsState,
fun MetricsPagerScreen(
state: MetricsState,
pagerState: PagerState,
modifier: Modifier = Modifier,
) = with(state) {
@ -178,8 +178,8 @@ fun NodeDetailsPagerScreen(
}
} else {
when (pages[index]) {
NodeDetailPage.DEVICE -> DeviceMetricsScreen(deviceMetrics)
NodeDetailPage.ENVIRONMENT -> EnvironmentMetricsScreen(environmentMetrics)
MetricsPage.DEVICE -> DeviceMetricsScreen(deviceMetrics)
MetricsPage.ENVIRONMENT -> EnvironmentMetricsScreen(environmentMetrics)
}
}
}
@ -189,10 +189,10 @@ fun NodeDetailsPagerScreen(
@OptIn(ExperimentalFoundationApi::class)
@PreviewLightDark
@Composable
private fun NodeDetailsPreview() {
private fun MetricsPreview() {
AppTheme {
val state = NodeDetailsState.Empty
NodeDetailsPagerScreen(
val state = MetricsState.Empty
MetricsPagerScreen(
state = state,
pagerState = rememberPagerState(pageCount = { state.pages.size }),
)

View file

@ -4,10 +4,12 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import com.geeksville.mesh.NodeInfo
import com.geeksville.mesh.R
import com.geeksville.mesh.ui.preview.NodeInfoPreviewParameterProvider
import com.geeksville.mesh.ui.theme.AppTheme
@ -18,7 +20,7 @@ fun signalInfo(
isThisNode: Boolean
): Boolean {
val text = if (isThisNode) {
"ChUtil %.1f%% AirUtilTX %.1f%%".format(
stringResource(R.string.channel_air_util).format(
nodeInfo.deviceMetrics?.channelUtilization,
nodeInfo.deviceMetrics?.airUtilTx
)
@ -30,7 +32,7 @@ fun signalInfo(
add("RSSI: %d SNR: %.1f".format(nodeInfo.rssi, nodeInfo.snr))
}
} else {
add("Hops Away: %d".format(nodeInfo.hopsAway))
add("%s: %d".format(stringResource(R.string.hops_away), nodeInfo.hopsAway))
}
}.joinToString(" ")
}

View file

@ -83,8 +83,8 @@ class UsersFragment : ScreenFragment("Users"), Logging {
navigateToRadioConfig(node)
}
R.id.more_details -> {
navigateToMoreDetails(node)
R.id.metrics -> {
navigateToMetrics(node)
}
}
}
@ -101,9 +101,9 @@ class UsersFragment : ScreenFragment("Users"), Logging {
parentFragmentManager.navigateToRadioConfig(node.num)
}
private fun navigateToMoreDetails(node: NodeInfo) {
info("calling MoreDetails --> destNum: ${node.num}")
parentFragmentManager.navigateToNodeDetails(node.num)
private fun navigateToMetrics(node: NodeInfo) {
info("calling Metrics --> destNum: ${node.num}")
parentFragmentManager.navigateToMetrics(node.num)
}

View file

@ -116,7 +116,7 @@ fun EnvironmentMetricsScreen(telemetries: List<Telemetry>) {
@Composable
private fun DeviceMetricsChart(modifier: Modifier = Modifier, telemetries: List<Telemetry>) {
ChartHeader(amount = telemetries.size, title = stringResource(R.string.device_metrics))
ChartHeader(amount = telemetries.size)
if (telemetries.isEmpty())
return
@ -205,7 +205,7 @@ private fun DeviceMetricsChart(modifier: Modifier = Modifier, telemetries: List<
@Composable
private fun EnvironmentMetricsChart(modifier: Modifier = Modifier, telemetries: List<Telemetry>) {
ChartHeader(amount = telemetries.size, title = stringResource(R.string.environment_metrics))
ChartHeader(amount = telemetries.size)
if (telemetries.isEmpty())
return
@ -406,10 +406,8 @@ private fun DeviceMetricsCard(telemetry: Telemetry) {
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
val text = "%s %.2f%% %s %.2f%%".format(
stringResource(R.string.channel_utilization),
val text = stringResource(R.string.channel_air_util).format(
deviceMetrics.channelUtilization,
stringResource(R.string.air_utilization),
deviceMetrics.airUtilTx
)
Text(
@ -493,14 +491,14 @@ private fun EnvironmentMetricsCard(telemetry: Telemetry) {
}
@Composable
private fun ChartHeader(amount: Int, title: String) {
private fun ChartHeader(amount: Int) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "$amount $title",
text = "$amount ${stringResource(R.string.logs)}",
modifier = Modifier.wrapContentWidth(),
style = TextStyle(fontWeight = FontWeight.Bold),
fontSize = MaterialTheme.typography.button.fontSize

View file

@ -32,8 +32,8 @@
</group>
<group android:id="@+id/group_both">
<item
android:id="@+id/more_details"
android:title="@string/more_details"
android:id="@+id/metrics"
android:title="@string/metrics"
app:showAsAction="withText" />
</group>
</menu>

View file

@ -25,9 +25,8 @@
<string name="node_sort_last_heard">Last heard</string>
<string name="node_sort_via_mqtt">via MQTT</string>
<string name="more_details">More Details</string>
<string name="elevation_suffix" translatable="false">MSL</string>
<string name="channel_air_util" translatable="false">ChUtil %.1f%% AirUtilTX %.1f%%</string>
<string name="channel_name">Channel Name</string>
<string name="channel_options">Channel options</string>
@ -221,9 +220,11 @@
<string name="battery">Battery</string>
<string name="channel_utilization">Channel Utilization</string>
<string name="air_utilization">Air Utilization</string>
<string name="device_metrics">Device Metrics</string>
<string name="node_details">Node Details</string>
<string name="environment_metrics">Environment Metrics</string>
<string name="device">Device</string>
<string name="metrics">Metrics</string>
<string name="environment">Environment</string>
<string name="temperature">Temperature</string>
<string name="humidity">Humidity</string>
<string name="logs">Logs</string>
<string name="hops_away">Hops Away</string>
</resources>