feat: network module (#1905)

This commit is contained in:
James Rich 2025-05-22 08:30:08 -05:00 committed by GitHub
parent 520d058546
commit 02bb3f02e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
80 changed files with 2165 additions and 15032 deletions

View file

@ -57,11 +57,11 @@ import androidx.compose.material.icons.filled.Share
import androidx.compose.material.icons.filled.SignalCellularAlt
import androidx.compose.material.icons.filled.Speed
import androidx.compose.material.icons.filled.Thermostat
import androidx.compose.material.icons.filled.Verified
import androidx.compose.material.icons.filled.WaterDrop
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Navigation
import androidx.compose.material.icons.outlined.NoCell
import androidx.compose.material.icons.twotone.Verified
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
@ -79,6 +79,7 @@ import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
@ -90,7 +91,11 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
import coil3.request.ErrorResult
import coil3.request.ImageRequest
import coil3.request.SuccessResult
import com.geeksville.mesh.R
import com.geeksville.mesh.android.BuildUtils.debug
import com.geeksville.mesh.model.DeviceHardware
import com.geeksville.mesh.model.MetricsState
import com.geeksville.mesh.model.MetricsViewModel
@ -207,6 +212,33 @@ private fun NodeDetailList(
NodeDetailsContent(node)
}
}
node.metadata?.firmwareVersion?.let { firmwareVersion ->
item {
PreferenceCategory(stringResource(R.string.firmware)) {
val latestStableFirmware = metricsState.latestStableFirmware
val latestAlphaFirmware = metricsState.latestAlphaFirmware
NodeDetailRow(
label = "Installed",
icon = Icons.Default.Memory,
value = firmwareVersion.substringBeforeLast(".")
)
latestStableFirmware?.let { stable ->
NodeDetailRow(
label = "Latest stable",
icon = Icons.Default.Memory,
value = stable.id.substringBeforeLast(".").replace("v", "")
)
}
latestAlphaFirmware?.let { alpha ->
NodeDetailRow(
label = "Latest alpha",
icon = Icons.Default.Memory,
value = alpha.id.substringBeforeLast(".").replace("v", "")
)
}
}
}
}
item {
DeviceActions(
@ -332,25 +364,19 @@ private fun DeviceDetailsContent(
),
contentAlignment = Alignment.Center
) {
DeviceHardwareImage(
deviceHardware = deviceHardware,
modifier = Modifier
.size(100.dp)
)
DeviceHardwareImage(deviceHardware, Modifier.fillMaxSize())
}
NodeDetailRow(
label = stringResource(R.string.hardware),
icon = Icons.Default.Router,
value = hwModelName
)
if (isSupported) {
NodeDetailRow(
label = stringResource(R.string.supported),
icon = Icons.Default.Verified,
value = "",
iconTint = Color.Green
)
}
NodeDetailRow(
label = if (isSupported) stringResource(R.string.supported) else "Supported by Community",
icon = if (isSupported) Icons.TwoTone.Verified else ImageVector.vectorResource(R.drawable.unverified),
value = "",
iconTint = if (isSupported) Color.Green else Color.Red
)
}
@Composable
@ -358,20 +384,37 @@ fun DeviceHardwareImage(
deviceHardware: DeviceHardware,
modifier: Modifier = Modifier,
) {
val hwImg = deviceHardware.images?.lastOrNull()
if (hwImg != null) {
val imageUrl = "file:///android_asset/device_hardware/$hwImg"
AsyncImage(
model = imageUrl,
contentScale = ContentScale.Inside,
contentDescription = deviceHardware.displayName,
placeholder = painterResource(R.drawable.hw_unknown),
error = painterResource(R.drawable.hw_unknown),
fallback = painterResource(R.drawable.hw_unknown),
modifier = modifier
.padding(16.dp)
)
val hwImg = deviceHardware.images?.get(1) ?: deviceHardware.images?.get(0) ?: "unknown.svg"
val imageUrl = "https://flasher.meshtastic.org/img/devices/$hwImg"
val listener = object : ImageRequest.Listener {
override fun onStart(request: ImageRequest) {
super.onStart(request)
debug("Image request started")
}
override fun onError(request: ImageRequest, result: ErrorResult) {
super.onError(request, result)
debug("Image request failed: ${result.throwable.message}")
}
override fun onSuccess(request: ImageRequest, result: SuccessResult) {
super.onSuccess(request, result)
debug("Image request succeeded: ${result.dataSource.name}")
}
}
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.listener(listener)
.data(imageUrl)
.build(),
contentScale = ContentScale.Inside,
contentDescription = deviceHardware.displayName,
placeholder = painterResource(R.drawable.hw_unknown),
error = painterResource(R.drawable.hw_unknown),
fallback = painterResource(R.drawable.hw_unknown),
modifier = modifier
.padding(16.dp)
)
}
@Suppress("LongMethod")
@ -435,13 +478,6 @@ private fun NodeDetailsContent(
value = formatUptime(node.deviceMetrics.uptimeSeconds)
)
}
if (node.metadata != null) {
NodeDetailRow(
label = stringResource(R.string.firmware_version),
icon = Icons.Default.Memory,
value = node.metadata.firmwareVersion.substringBeforeLast(".")
)
}
NodeDetailRow(
label = stringResource(R.string.node_sort_last_heard),
icon = Icons.Default.History,