From 923624fbfa88c9ef89aa8da9ca63f6aae2309bc3 Mon Sep 17 00:00:00 2001 From: Mac DeCourcy <49794076+mdecourcy@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:00:36 -0800 Subject: [PATCH] fix: further fixes for fdroid json fallbacks (#3847) --- .../platform/FdroidPlatformAnalytics.kt | 3 ++- .../datasource/DeviceHardwareJsonDataSource.kt | 15 ++++++++++++--- .../datasource/FirmwareReleaseJsonDataSource.kt | 16 ++++++++++++---- .../data/repository/DeviceHardwareRepository.kt | 14 +++++++++++++- 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt b/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt index e44a7fba9..84bd0ff07 100644 --- a/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt +++ b/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt @@ -31,7 +31,8 @@ import javax.inject.Inject class FdroidPlatformAnalytics @Inject constructor() : PlatformAnalytics { init { // For F-Droid builds we don't initialize external analytics services. - // In debug builds we attach a DebugTree for convenient local logging. + // In debug builds we attach a DebugTree for convenient local logging, but + // release builds rely on system logging only. if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) Timber.i("F-Droid platform no-op analytics initialized (DebugTree planted).") diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt index bcbe416f9..e6caa4003 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt +++ b/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/DeviceHardwareJsonDataSource.kt @@ -25,9 +25,18 @@ import org.meshtastic.core.model.NetworkDeviceHardware import javax.inject.Inject class DeviceHardwareJsonDataSource @Inject constructor(private val application: Application) { + + // Use a tolerant JSON parser so that additional fields in the bundled asset + // (e.g., "key") do not break deserialization on older app versions. @OptIn(ExperimentalSerializationApi::class) - fun loadDeviceHardwareFromJsonAsset(): List { - val inputStream = application.assets.open("device_hardware.json") - return Json.decodeFromStream>(inputStream) + private val json = Json { + ignoreUnknownKeys = true + isLenient = true } + + @OptIn(ExperimentalSerializationApi::class) + fun loadDeviceHardwareFromJsonAsset(): List = + application.assets.open("device_hardware.json").use { inputStream -> + json.decodeFromStream>(inputStream) + } } diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt index c9de3fe67..a643f2f2b 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt +++ b/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/FirmwareReleaseJsonDataSource.kt @@ -25,10 +25,18 @@ import org.meshtastic.core.model.NetworkFirmwareReleases import javax.inject.Inject class FirmwareReleaseJsonDataSource @Inject constructor(private val application: Application) { + + // Match the network client behavior: be tolerant of unknown fields so that + // older app versions can read newer snapshots of firmware_releases.json. @OptIn(ExperimentalSerializationApi::class) - fun loadFirmwareReleaseFromJsonAsset(): NetworkFirmwareReleases { - val inputStream = application.assets.open("firmware_releases.json") - val result = inputStream.use { Json.decodeFromStream(inputStream) } - return result + private val json = Json { + ignoreUnknownKeys = true + isLenient = true } + + @OptIn(ExperimentalSerializationApi::class) + fun loadFirmwareReleaseFromJsonAsset(): NetworkFirmwareReleases = + application.assets.open("firmware_releases.json").use { inputStream -> + json.decodeFromStream(inputStream) + } } diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepository.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepository.kt index 1be4bab65..ec113ad86 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepository.kt +++ b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepository.kt @@ -123,7 +123,10 @@ constructor( private suspend fun loadFromBundledJson(hwModel: Int): Result = runCatching { Timber.d("DeviceHardwareRepository: loading device hardware from bundled JSON for hwModel=%d", hwModel) val jsonHardware = jsonDataSource.loadDeviceHardwareFromJsonAsset() - Timber.d("DeviceHardwareRepository: bundled JSON returned %d device hardware entries", jsonHardware.size) + Timber.d( + "DeviceHardwareRepository: bundled JSON returned %d device hardware entries", + jsonHardware.size, + ) localDataSource.insertAllDeviceHardware(jsonHardware) val fromDb = localDataSource.getByHwModel(hwModel)?.asExternalModel() @@ -134,6 +137,15 @@ constructor( ) fromDb } + .also { result -> + result.exceptionOrNull()?.let { e -> + Timber.e( + e, + "DeviceHardwareRepository: failed to load device hardware from bundled JSON for hwModel=%d", + hwModel, + ) + } + } /** Returns true if the cached entity is missing important fields and should be refreshed. */ private fun DeviceHardwareEntity.isIncomplete(): Boolean =