Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Device Hardware json and svg graphics #1449

Merged
merged 10 commits into from
Dec 10, 2024
764 changes: 764 additions & 0 deletions app/src/main/assets/device_hardware.json

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions app/src/main/java/com/geeksville/mesh/model/DeviceHardware.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.geeksville.mesh.model

import android.content.Context
import com.geeksville.mesh.MeshProtos.HardwareModel
import com.geeksville.mesh.R
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

@Serializable
data class DeviceHardware(
val hwModel: Int,
val hwModelSlug: String,
val platformioTarget: String,
val architecture: String,
val activelySupported: Boolean,
val supportLevel: Int? = null,
val displayName: String,
val tags: List<String>? = listOf(),
val images: List<String>? = listOf(),
val requiresDfu: Boolean? = null
) {
companion object {
fun getDeviceHardwareFromHardwareModel(
context: Context,
hwModel: HardwareModel
): DeviceHardware? {
val json =
context.assets.open("device_hardware.json").bufferedReader().use { it.readText() }
val deviceHardware = Json.decodeFromString<List<DeviceHardware>>(json)
return deviceHardware.find { it.hwModel == hwModel.number }
}
}

fun getDeviceVectorImage(context: Context): Int {
val resourceId = context.resources.getIdentifier(
andrekir marked this conversation as resolved.
Show resolved Hide resolved
"hw_${this.images?.first()?.removeSuffix(".svg")}",
"drawable",
context.packageName
)
if (resourceId == 0) {
return R.drawable.hw_unknown
}
return resourceId
}
}
84 changes: 72 additions & 12 deletions app/src/main/java/com/geeksville/mesh/ui/NodeDetail.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

@file:Suppress("TooManyFunctions")
@file:Suppress("TooManyFunctions", "LongMethod")

package com.geeksville.mesh.ui

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -36,6 +38,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.CircularProgressIndicator
Expand Down Expand Up @@ -64,16 +67,19 @@ import androidx.compose.material.icons.filled.Settings
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.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
Expand All @@ -85,6 +91,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig.DisplayUnits
import com.geeksville.mesh.R
import com.geeksville.mesh.database.entity.NodeEntity
import com.geeksville.mesh.model.DeviceHardware
import com.geeksville.mesh.model.MetricsState
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.ui.components.PreferenceCategory
Expand All @@ -98,8 +105,8 @@ import kotlin.math.ln

@Composable
fun NodeDetailScreen(
viewModel: MetricsViewModel = hiltViewModel(),
modifier: Modifier = Modifier,
viewModel: MetricsViewModel = hiltViewModel(),
onNavigate: (Any) -> Unit,
) {
val state by viewModel.state.collectAsStateWithLifecycle()
Expand All @@ -124,15 +131,20 @@ fun NodeDetailScreen(

@Composable
private fun NodeDetailList(
modifier: Modifier = Modifier,
node: NodeEntity,
metricsState: MetricsState,
modifier: Modifier = Modifier,
onNavigate: (Any) -> Unit = {},
) {
LazyColumn(
modifier = modifier.fillMaxSize(),
contentPadding = PaddingValues(horizontal = 16.dp),
) {
item {
PreferenceCategory("Device") {
DeviceDetailsContent(node)
}
}
item {
PreferenceCategory("Details") {
NodeDetailsContent(node)
Expand Down Expand Up @@ -176,7 +188,12 @@ private fun NodeDetailList(
}

@Composable
private fun NodeDetailRow(label: String, icon: ImageVector, value: String) {
private fun NodeDetailRow(
label: String,
icon: ImageVector,
value: String,
iconTint: Color = MaterialTheme.colors.onSurface
) {
Row(
modifier = Modifier
.fillMaxWidth()
Expand All @@ -186,7 +203,8 @@ private fun NodeDetailRow(label: String, icon: ImageVector, value: String) {
Icon(
imageVector = icon,
contentDescription = label,
modifier = Modifier.size(24.dp)
modifier = Modifier.size(24.dp),
tint = iconTint
)
Spacer(modifier = Modifier.width(8.dp))
Text(label)
Expand All @@ -196,7 +214,51 @@ private fun NodeDetailRow(label: String, icon: ImageVector, value: String) {
}

@Composable
private fun NodeDetailsContent(node: NodeEntity) {
private fun DeviceDetailsContent(
node: NodeEntity,
) {
val context = LocalContext.current
val deviceHardware =
DeviceHardware.getDeviceHardwareFromHardwareModel(context, node.user.hwModel)
andrekir marked this conversation as resolved.
Show resolved Hide resolved
val deviceImageRes = deviceHardware?.getDeviceVectorImage(context)
val hwModelName = deviceHardware?.displayName ?: node.user.hwModel.name
val isSupported = deviceHardware?.activelySupported == true
if (deviceImageRes != null) {

Box(
modifier = Modifier
.size(100.dp)
.padding(4.dp)
.clip(CircleShape)
.background(color = Color(node.colors.second).copy(alpha = .5f), shape = CircleShape),
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier.padding(16.dp),
imageVector = ImageVector.vectorResource(deviceImageRes),
contentDescription = hwModelName,
)
}
}
NodeDetailRow(
label = "Hardware",
icon = Icons.Default.Router,
value = hwModelName
)
if (isSupported) {
NodeDetailRow(
label = "Supported",
icon = Icons.Default.Verified,
value = "",
iconTint = Color.Green
)
}
}

@Composable
private fun NodeDetailsContent(
node: NodeEntity,
) {
if (node.mismatchKey) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
Expand Down Expand Up @@ -232,11 +294,6 @@ private fun NodeDetailsContent(node: NodeEntity) {
icon = Icons.Default.Work,
value = node.user.role.name
)
NodeDetailRow(
label = "Hardware",
icon = Icons.Default.Router,
value = node.user.hwModel.name
)
if (node.deviceMetrics.uptimeSeconds > 0) {
NodeDetailRow(
label = "Uptime",
Expand Down Expand Up @@ -541,6 +598,9 @@ private fun NodeDetailsPreview(
node: NodeEntity
) {
AppTheme {
NodeDetailList(node, MetricsState.Empty)
NodeDetailList(
node = node,
metricsState = MetricsState.Empty,
)
}
}
Loading
Loading