From 45ce1d6e3a28fd12a6e38e64f9ae33df6bf644b3 Mon Sep 17 00:00:00 2001 From: Dane Evans Date: Sat, 30 Aug 2025 11:55:06 +1000 Subject: [PATCH] fix crash by adding a safe call for hardwareModel --- .../geeksville/mesh/model/MetricsViewModel.kt | 6 ++- .../java/com/geeksville/mesh/model/UIState.kt | 5 +- .../mesh/util/HardwareModelExtensions.kt | 54 +++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/util/HardwareModelExtensions.kt 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 560691acc..b5d117788 100644 --- a/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt @@ -47,6 +47,7 @@ import com.geeksville.mesh.repository.api.DeviceHardwareRepository import com.geeksville.mesh.repository.api.FirmwareReleaseRepository import com.geeksville.mesh.repository.datastore.RadioConfigRepository import com.geeksville.mesh.service.ServiceAction +import com.geeksville.mesh.util.safeNumber import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -260,8 +261,9 @@ constructor( .onEach { (node, ourNode) -> // Create a fallback node if not found in database (for hidden clients, etc.) val actualNode = node ?: createFallbackNode(destNum) - val deviceHardware = - actualNode.user.hwModel.number.let { deviceHardwareRepository.getDeviceHardwareByModel(it) } + val deviceHardware = actualNode.user.hwModel.safeNumber().let { + deviceHardwareRepository.getDeviceHardwareByModel(it) + } _state.update { state -> state.copy( node = actualNode, diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index 488be641b..ea1d2e328 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -67,6 +67,7 @@ import com.geeksville.mesh.ui.common.components.MainMenuAction import com.geeksville.mesh.ui.node.components.NodeMenuAction import com.geeksville.mesh.util.getShortDate import com.geeksville.mesh.util.positionToMeter +import com.geeksville.mesh.util.safeNumber import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -228,8 +229,8 @@ constructor( val deviceHardware: StateFlow = ourNodeInfo .mapNotNull { nodeInfo -> - nodeInfo?.user?.hwModel?.let { - deviceHardwareRepository.getDeviceHardwareByModel(it.number).getOrNull() + nodeInfo?.user?.hwModel?.let { hwModel -> + deviceHardwareRepository.getDeviceHardwareByModel(hwModel.safeNumber()).getOrNull() } } .stateIn(scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = null) diff --git a/app/src/main/java/com/geeksville/mesh/util/HardwareModelExtensions.kt b/app/src/main/java/com/geeksville/mesh/util/HardwareModelExtensions.kt new file mode 100644 index 000000000..b36ee7353 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/util/HardwareModelExtensions.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 Meshtastic LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.geeksville.mesh.util + +import com.geeksville.mesh.MeshProtos +import com.geeksville.mesh.android.BuildUtils.warn + +/** + * Safely extracts the hardware model number from a HardwareModel enum. + * + * This function handles unknown enum values gracefully by catching IllegalArgumentException + * and returning a fallback value. This prevents crashes when the app receives data + * from devices with hardware models not yet defined in the current protobuf version. + * + * @param fallbackValue The value to return if the enum is unknown (defaults to 0 for UNSET) + * @return The hardware model number, or the fallback value if the enum is unknown + */ +fun MeshProtos.HardwareModel.safeNumber(fallbackValue: Int = 0): Int { + return try { + this.number + } catch (e: IllegalArgumentException) { + warn("Unknown hardware model enum value: $this, using fallback value: $fallbackValue") + fallbackValue + } +} + +/** + * Checks if the hardware model is a known/supported value. + * + * @return true if the hardware model is known and supported, false otherwise + */ +fun MeshProtos.HardwareModel.isKnown(): Boolean { + return try { + this.number + true + } catch (e: IllegalArgumentException) { + false + } +}