From 33fbb9f7fcab4786c6db51e1773fc1d396de0cf3 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:17:42 -0500 Subject: [PATCH] chore: fix build warnings and migrate deprecated APIs (#6130) --- .../kotlin/org/meshtastic/app/map/MapView.kt | 4 ++-- .../kotlin/org/meshtastic/app/map/MapView.kt | 1 + .../meshtastic/app/map/model/CustomTileSource.kt | 5 ++--- .../meshtastic/app/di/KoinVerificationTest.kt | 1 + .../kotlin/org/meshtastic/buildlogic/Dokka.kt | 3 ++- .../kotlin/org/meshtastic/buildlogic/Kover.kt | 3 ++- core/ble/build.gradle.kts | 2 +- .../org/meshtastic/core/ble/KableBleScanner.kt | 4 ++++ core/data/build.gradle.kts | 4 ++-- .../core/data/ai/AiFunctionProviderImpl.kt | 2 +- core/database/build.gradle.kts | 4 ++-- core/konsist/build.gradle.kts | 2 +- core/model/build.gradle.kts | 2 +- core/network/build.gradle.kts | 2 +- .../network/repository/MQTTRepositoryImpl.kt | 3 +++ core/service/build.gradle.kts | 2 +- .../core/service/LockdownPassphraseStoreImpl.kt | 5 ++++- .../core/service/SecurityKeyBackupStoreImpl.kt | 5 ++++- .../core/service/SharedRadioInterfaceService.kt | 2 +- .../meshtastic/core/takserver/CoTXmlParser.kt | 16 +++++++++++----- .../core/takserver/TAKDataPackageGenerator.kt | 14 ++++++++++---- .../core/testing/FakeRadioInterfaceService.kt | 2 +- core/ui/build.gradle.kts | 2 +- .../org/meshtastic/core/ui/util/PlatformUtils.kt | 4 ++++ .../core/ui/emoji/EmojiPickerViewModel.kt | 4 ++-- .../meshtastic/core/ui/util/ProtoExtensions.kt | 2 +- .../org/meshtastic/core/ui/util/NoopStubs.kt | 2 +- .../org/meshtastic/core/ui/util/PlatformUtils.kt | 4 +++- .../org/meshtastic/desktop/stub/NoopStubs.kt | 5 ++--- .../meshtastic/feature/car/screens/HomeScreen.kt | 15 +++++++++------ feature/connections/build.gradle.kts | 2 +- .../feature/discovery/ui/DiscoveryScanScreen.kt | 4 ++-- .../ui/component/MeshBeaconInvitationCard.kt | 2 +- feature/docs/build.gradle.kts | 8 ++++---- .../feature/firmware/FirmwareUpdateScreen.kt | 2 -- .../feature/firmware/FirmwareUpdateViewModel.kt | 2 ++ .../usecase/CommonGetNodeDetailsUseCase.kt | 7 ++++++- .../feature/node/metrics/AirQualityMetrics.kt | 4 ++-- .../feature/node/metrics/BaseMetricChart.kt | 2 +- feature/settings/detekt-baseline.xml | 2 +- .../radio/component/MQTTConfigItemList.kt | 3 +++ 41 files changed, 104 insertions(+), 60 deletions(-) diff --git a/androidApp/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt b/androidApp/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt index ca4984b644..0067032c05 100644 --- a/androidApp/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt +++ b/androidApp/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt @@ -1046,8 +1046,8 @@ private fun CacheInfoDialog(mapView: MapView, onDismiss: () -> Unit) { onDismiss = onDismiss, negativeButton = { TextButton(onClick = { onDismiss() }) { Text(text = stringResource(Res.string.close)) } }, ) { - val capacityMb = (cacheCapacity / (1024 * 1024)).toLong() - val usageMb = (currentCacheUsage / (1024 * 1024)).toLong() + val capacityMb = cacheCapacity / (1024 * 1024) + val usageMb = currentCacheUsage / (1024 * 1024) Text(modifier = Modifier.padding(16.dp), text = stringResource(Res.string.map_cache_info, capacityMb, usageMb)) } } diff --git a/androidApp/src/google/kotlin/org/meshtastic/app/map/MapView.kt b/androidApp/src/google/kotlin/org/meshtastic/app/map/MapView.kt index 61a7c02c72..f3425d7a78 100644 --- a/androidApp/src/google/kotlin/org/meshtastic/app/map/MapView.kt +++ b/androidApp/src/google/kotlin/org/meshtastic/app/map/MapView.kt @@ -1230,6 +1230,7 @@ private fun offsetPolyline( // region --- Map Layers --- +@OptIn(MapsComposeExperimentalApi::class) @Composable private fun MapLayerOverlay(layerItem: MapLayerItem, mapViewModel: MapViewModel) { val context = LocalContext.current diff --git a/androidApp/src/google/kotlin/org/meshtastic/app/map/model/CustomTileSource.kt b/androidApp/src/google/kotlin/org/meshtastic/app/map/model/CustomTileSource.kt index 14db01d4b1..eba9f6e69c 100644 --- a/androidApp/src/google/kotlin/org/meshtastic/app/map/model/CustomTileSource.kt +++ b/androidApp/src/google/kotlin/org/meshtastic/app/map/model/CustomTileSource.kt @@ -19,8 +19,7 @@ package org.meshtastic.app.map.model class CustomTileSource { companion object { - fun getTileSource(index: Int) { - index - } + // No-op stub for the Google flavor (osmdroid tile sources are fdroid-only). + fun getTileSource(index: Int) {} } } diff --git a/androidApp/src/test/kotlin/org/meshtastic/app/di/KoinVerificationTest.kt b/androidApp/src/test/kotlin/org/meshtastic/app/di/KoinVerificationTest.kt index 0aa9d2f414..85396c680c 100644 --- a/androidApp/src/test/kotlin/org/meshtastic/app/di/KoinVerificationTest.kt +++ b/androidApp/src/test/kotlin/org/meshtastic/app/di/KoinVerificationTest.kt @@ -38,6 +38,7 @@ import kotlin.test.Test class KoinVerificationTest { + @OptIn(org.koin.core.annotation.KoinExperimentalAPI::class) @Test fun verifyKoinConfiguration() { AppKoinModule() diff --git a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/Dokka.kt b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/Dokka.kt index 40477764b8..1438c22364 100644 --- a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/Dokka.kt +++ b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/Dokka.kt @@ -18,6 +18,7 @@ package org.meshtastic.buildlogic import org.gradle.api.Project import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.project import org.jetbrains.dokka.gradle.DokkaExtension import java.net.URI @@ -73,5 +74,5 @@ fun Project.configureDokkaAggregation(subprojectPaths: List) { // Add each subproject as a Dokka dependency using declared paths rather than // iterating live subproject objects. This avoids cross-project configuration // access and is compatible with Gradle Isolated Projects. - subprojectPaths.forEach { path -> dependencies.add("dokka", project(path)) } + subprojectPaths.forEach { path -> dependencies.add("dokka", dependencies.project(path)) } } diff --git a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/Kover.kt b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/Kover.kt index c55f6c0fe2..b9ab906c06 100644 --- a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/Kover.kt +++ b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/Kover.kt @@ -19,6 +19,7 @@ package org.meshtastic.buildlogic import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension import org.gradle.api.Project import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.project fun Project.configureKover() { val isCi = providers.gradleProperty("ci").map { it.toBoolean() }.getOrElse(false) @@ -64,5 +65,5 @@ fun Project.configureKover() { * Isolated Projects. The list should match the modules declared in `settings.gradle.kts`. */ fun Project.configureKoverAggregation(subprojectPaths: List) { - subprojectPaths.forEach { path -> dependencies.add("kover", project(path)) } + subprojectPaths.forEach { path -> dependencies.add("kover", dependencies.project(path)) } } diff --git a/core/ble/build.gradle.kts b/core/ble/build.gradle.kts index be45663dd4..fe0525d97f 100644 --- a/core/ble/build.gradle.kts +++ b/core/ble/build.gradle.kts @@ -44,7 +44,7 @@ kotlin { implementation(projects.core.testing) } - val androidHostTest by getting { + getByName("androidHostTest") { dependencies { implementation(projects.core.testing) implementation(libs.kotlinx.coroutines.test) diff --git a/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleScanner.kt b/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleScanner.kt index c079498ded..c4e53bbbf5 100644 --- a/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleScanner.kt +++ b/core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleScanner.kt @@ -46,6 +46,10 @@ internal fun resolveKableScanFilter(serviceUuid: Uuid?, address: String?): Kable else -> KableScanFilter.None } +// Kable's Advertisement.identifier is an expect typealias: String on Android/JVM/JS but Uuid on Apple. +// toString() looks redundant when compiling the Android/JVM view (hence the warning) but is required to +// normalize the Apple Uuid to the String this result carries, so it must stay. +@Suppress("REDUNDANT_CALL_OF_CONVERSION_METHOD") private fun Advertisement.toScanResult(): KableScanResult = KableScanResult(identifier = identifier.toString(), name = name, advertisement = this) diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index a3d273839f..3036e7b31e 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -49,7 +49,7 @@ kotlin { } // Room / SQLite runtime shared between Android and Desktop JVM targets - val jvmAndroidMain by getting { + getByName("jvmAndroidMain") { dependencies { implementation(libs.androidx.room.runtime) implementation(libs.androidx.room.paging) @@ -67,7 +67,7 @@ kotlin { implementation(libs.kotlinx.coroutines.test) } - val androidHostTest by getting { + getByName("androidHostTest") { dependencies { // JVM variant provides the host-platform native for BundledSQLiteDriver (same as core:database) runtimeOnly("androidx.sqlite:sqlite-bundled-jvm:2.7.0") diff --git a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/ai/AiFunctionProviderImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/ai/AiFunctionProviderImpl.kt index f1b5a37fc9..549a858405 100644 --- a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/ai/AiFunctionProviderImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/ai/AiFunctionProviderImpl.kt @@ -270,7 +270,7 @@ class AiFunctionProviderImpl( when { totalCount == 0 -> 0 onlineCount == 0 -> HEALTH_SCORE_DEGRADED - else -> (HEALTH_SCORE_BASE + (HEALTH_SCORE_ONLINE_RATIO * onlineCount) / totalCount).toInt() + else -> HEALTH_SCORE_BASE + (HEALTH_SCORE_ONLINE_RATIO * onlineCount) / totalCount } // Find most recent packet: max lastHeard across all nodes (convert seconds to ms) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 5d779655bf..4dd710a32c 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -50,7 +50,7 @@ kotlin { implementation(libs.androidx.room.testing) } - val androidHostTest by getting { + getByName("androidHostTest") { dependencies { implementation(libs.androidx.sqlite.bundled) // JVM variant provides the host-platform native for BundledSQLiteDriver @@ -60,7 +60,7 @@ kotlin { implementation(libs.junit) } } - val androidDeviceTest by getting { + getByName("androidDeviceTest") { dependencies { implementation(libs.androidx.room.testing) implementation(libs.androidx.test.ext.junit) diff --git a/core/konsist/build.gradle.kts b/core/konsist/build.gradle.kts index 1cbc77d8d7..8739598bcf 100644 --- a/core/konsist/build.gradle.kts +++ b/core/konsist/build.gradle.kts @@ -28,7 +28,7 @@ kotlin { android { withHostTest {} } sourceSets { - val jvmTest by getting { + getByName("jvmTest") { dependencies { implementation(libs.konsist) implementation(libs.junit) diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts index 9ab0d4c961..a29782e567 100644 --- a/core/model/build.gradle.kts +++ b/core/model/build.gradle.kts @@ -60,7 +60,7 @@ kotlin { api(libs.androidx.annotation) api(libs.androidx.core.ktx) } - val androidDeviceTest by getting { + getByName("androidDeviceTest") { dependencies { implementation(libs.androidx.test.ext.junit) implementation(libs.androidx.test.runner) diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index c9baaee850..f8ab2ad6da 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -55,7 +55,7 @@ kotlin { implementation(libs.jetbrains.lifecycle.runtime) } - val jvmMain by getting { + getByName("jvmMain") { dependencies { implementation(libs.ktor.client.java) implementation(libs.jserialcomm) diff --git a/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt index 9f76507817..89e214a505 100644 --- a/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt +++ b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt @@ -114,6 +114,9 @@ class MQTTRepositoryImpl( scope.launch { safeCatching { c?.close() }.onFailure { e -> Logger.w(e) { "MQTT clean disconnect failed" } } } } + // json_enabled is deprecated in the protobuf schema but remains the only way to toggle MQTT JSON + // publish/consume, so we must keep reading it until the firmware/proto provides a replacement. + @Suppress("DEPRECATION") @OptIn(ExperimentalSerializationApi::class) override val proxyMessageFlow: Flow = callbackFlow { // Append a per-connection random id. myId identifies the *node* (and is null → diff --git a/core/service/build.gradle.kts b/core/service/build.gradle.kts index d2fea9dde3..fe74edb4c5 100644 --- a/core/service/build.gradle.kts +++ b/core/service/build.gradle.kts @@ -52,7 +52,7 @@ kotlin { implementation(libs.koin.androidx.workmanager) } - val androidHostTest by getting { + getByName("androidHostTest") { dependencies { implementation(projects.core.testing) implementation(libs.androidx.test.ext.junit) diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt index 0c908ae2cd..7711138542 100644 --- a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt +++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt @@ -35,7 +35,10 @@ import org.meshtastic.core.repository.StoredPassphrase @Single(binds = [LockdownPassphraseStore::class]) class LockdownPassphraseStoreImpl(app: Application) : LockdownPassphraseStore { - @Suppress("TooGenericExceptionCaught") + // androidx.security.crypto (MasterKey / EncryptedSharedPreferences) is deprecated by Google with no + // drop-in AndroidX replacement yet. Migrating encrypted storage is a separate, security-sensitive + // effort; suppress until a stable replacement (e.g. Tink) is adopted. + @Suppress("TooGenericExceptionCaught", "DEPRECATION") private val prefs: SharedPreferences? by lazy { try { val masterKey = MasterKey.Builder(app).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build() diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/SecurityKeyBackupStoreImpl.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/SecurityKeyBackupStoreImpl.kt index 5f94b7b3b3..1f12c25bd1 100644 --- a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/SecurityKeyBackupStoreImpl.kt +++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/SecurityKeyBackupStoreImpl.kt @@ -34,7 +34,10 @@ import org.meshtastic.core.repository.StoredSecurityKeys @Single(binds = [SecurityKeyBackupStore::class]) class SecurityKeyBackupStoreImpl(app: Application) : SecurityKeyBackupStore { - @Suppress("TooGenericExceptionCaught") + // androidx.security.crypto (MasterKey / EncryptedSharedPreferences) is deprecated by Google with no + // drop-in AndroidX replacement yet. Migrating encrypted storage is a separate, security-sensitive + // effort; suppress until a stable replacement (e.g. Tink) is adopted. + @Suppress("TooGenericExceptionCaught", "DEPRECATION") private val prefs: SharedPreferences? by lazy { try { val masterKey = MasterKey.Builder(app).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build() diff --git a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/SharedRadioInterfaceService.kt b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/SharedRadioInterfaceService.kt index 0550bf380d..4087d7ef3d 100644 --- a/core/service/src/commonMain/kotlin/org/meshtastic/core/service/SharedRadioInterfaceService.kt +++ b/core/service/src/commonMain/kotlin/org/meshtastic/core/service/SharedRadioInterfaceService.kt @@ -717,7 +717,7 @@ class SharedRadioInterfaceService( // would replay stale bytes ahead of the next session's firmware handshake, since the channel // outlives the orchestrator's per-start scope. @Suppress("EmptyWhileBlock", "ControlFlowWithEmptyBody") - while (_receivedData.tryReceive().isSuccess) Unit + while (_receivedData.tryReceive().isSuccess) {} } override fun onConnect() { diff --git a/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/CoTXmlParser.kt b/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/CoTXmlParser.kt index 4993bf301e..e98dde4a13 100644 --- a/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/CoTXmlParser.kt +++ b/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/CoTXmlParser.kt @@ -23,11 +23,17 @@ import nl.adaptivity.xmlutil.serialization.XML import kotlin.time.Clock import kotlin.time.Instant -private val xmlParser = XML { - // xmlutil 1.0.0 moved repairNamespaces from the policy builder to the top-level XML config. - repairNamespaces = false - defaultPolicy { ignoreUnknownChildren() } -} +// XML.compat preserves xmlutil's pre-1.0 serialization defaults. The non-deprecated 1.0 builders +// (XML.recommended/XML.V1) intentionally change serialization defaults, which would alter the CoT XML +// wire format we exchange with ATAK/TAK servers. Staying on the compat policy until that migration can +// be validated against real TAK interop; suppress the soft-deprecation on the factory itself. +@Suppress("DEPRECATION") +private val xmlParser = + XML.compat { + // xmlutil 1.0.0 moved repairNamespaces from the policy builder to the top-level XML config. + repairNamespaces = false + defaultPolicy { ignoreUnknownChildren() } + } class CoTXmlParser(private val xml: String) { fun parse(): Result = try { diff --git a/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/TAKDataPackageGenerator.kt b/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/TAKDataPackageGenerator.kt index 1b5a86d390..ad2ab988a8 100644 --- a/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/TAKDataPackageGenerator.kt +++ b/core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/TAKDataPackageGenerator.kt @@ -46,10 +46,16 @@ object TAKDataPackageGenerator { private const val CLIENT_P12_FILE_NAME = "client.p12" private const val PACKAGE_NAME = "Meshtastic_TAK_Server" - private val xmlSerializer = XML { - xmlDeclMode = XmlDeclMode.Charset - indentString = " " - } + // XML.compat preserves xmlutil's pre-1.0 serialization defaults. The non-deprecated 1.0 builders + // (XML.recommended/XML.V1) intentionally change serialization defaults, which would alter the CoT + // XML wire format consumed by ATAK/TAK servers. Staying on the compat policy until that migration + // can be validated against real TAK interop; suppress the soft-deprecation on the factory itself. + @Suppress("DEPRECATION") + private val xmlSerializer = + XML.compat { + xmlDeclMode = XmlDeclMode.Charset + indentString = " " + } /** * Platform-specific hook for reading the bundled TLS certificate bytes. Default implementation lives in diff --git a/core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeRadioInterfaceService.kt b/core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeRadioInterfaceService.kt index 21cf6c63fa..048ec37b00 100644 --- a/core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeRadioInterfaceService.kt +++ b/core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeRadioInterfaceService.kt @@ -109,7 +109,7 @@ class FakeRadioInterfaceService(override val serviceScope: CoroutineScope = Main override fun resetReceivedBuffer() { @Suppress("EmptyWhileBlock", "ControlFlowWithEmptyBody") - while (_receivedData.tryReceive().isSuccess) Unit + while (_receivedData.tryReceive().isSuccess) {} } // --- Helper methods for testing --- diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index c6e248638c..4c55e4f6c8 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -64,7 +64,7 @@ kotlin { implementation(libs.jetbrains.lifecycle.runtime.compose) } - val jvmAndroidMain by getting { dependencies { implementation(libs.compose.multiplatform.ui.tooling) } } + getByName("jvmAndroidMain") { dependencies { implementation(libs.compose.multiplatform.ui.tooling) } } androidMain.dependencies { implementation(libs.androidx.activity.compose) } diff --git a/core/ui/src/androidMain/kotlin/org/meshtastic/core/ui/util/PlatformUtils.kt b/core/ui/src/androidMain/kotlin/org/meshtastic/core/ui/util/PlatformUtils.kt index 1afcf0d6ab..d081cf802a 100644 --- a/core/ui/src/androidMain/kotlin/org/meshtastic/core/ui/util/PlatformUtils.kt +++ b/core/ui/src/androidMain/kotlin/org/meshtastic/core/ui/util/PlatformUtils.kt @@ -332,6 +332,10 @@ actual fun isWifiUnavailable(): Boolean { * the transport reduction can delegate to the platform-agnostic [anyNetworkScanTransportAvailable] helper (which is * unit-tested in `commonTest`). */ +// ConnectivityManager.allNetworks is deprecated (API 31+) in favor of NetworkCallback-based discovery, +// but a synchronous snapshot of active networks is exactly what this transport probe needs. Suppress +// until a callback-based rewrite is warranted. +@Suppress("DEPRECATION") private fun ConnectivityManager.hasLocalNetwork(): Boolean { val transports = allNetworks.mapNotNull { network -> diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/emoji/EmojiPickerViewModel.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/emoji/EmojiPickerViewModel.kt index c3e1e68f7d..a4259f6fb3 100644 --- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/emoji/EmojiPickerViewModel.kt +++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/emoji/EmojiPickerViewModel.kt @@ -52,10 +52,10 @@ internal class EmojiPickerViewModel( emojiRepository.preload() _isLoaded.value = true } catch (e: MissingResourceException) { - Logger.e("EmojiPickerViewModel", e) { "Failed to load emoji data" } + Logger.e(tag = "EmojiPickerViewModel", throwable = e) { "Failed to load emoji data" } _loadError.value = true } catch (e: IllegalStateException) { - Logger.e("EmojiPickerViewModel", e) { "Failed to load emoji data" } + Logger.e(tag = "EmojiPickerViewModel", throwable = e) { "Failed to load emoji data" } _loadError.value = true } } diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/ProtoExtensions.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/ProtoExtensions.kt index 12ea29288c..f4d239dacb 100644 --- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/ProtoExtensions.kt +++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/ProtoExtensions.kt @@ -195,7 +195,7 @@ fun normalizeReplacementSettings( } /** True when a [ChannelSettings] carries no name and no PSK — a placeholder, not an intended channel. */ -private fun ChannelSettings.isPlaceholder(): Boolean = name.isNullOrBlank() && (psk == null || psk.size == 0) +private fun ChannelSettings.isPlaceholder(): Boolean = name.isNullOrBlank() && psk.size == 0 /** * Applies an imported [ChannelSet] as an authoritative replacement to the radio and local cache. diff --git a/core/ui/src/iosMain/kotlin/org/meshtastic/core/ui/util/NoopStubs.kt b/core/ui/src/iosMain/kotlin/org/meshtastic/core/ui/util/NoopStubs.kt index 296852973a..f2203eb3d1 100644 --- a/core/ui/src/iosMain/kotlin/org/meshtastic/core/ui/util/NoopStubs.kt +++ b/core/ui/src/iosMain/kotlin/org/meshtastic/core/ui/util/NoopStubs.kt @@ -46,7 +46,7 @@ actual fun rememberSaveFileLauncher( @Composable actual fun rememberOpenFileLauncher(onUriReceived: (CommonUri?) -> Unit): (mimeType: String) -> Unit = { _ -> } -@Composable actual fun rememberReadTextFromUri(): suspend (CommonUri, Int) -> String? = { _, _ -> null } +@Composable actual fun rememberReadTextFromUri(): suspend (uri: CommonUri, maxChars: Int) -> String? = { _, _ -> null } @Composable actual fun KeepScreenOn(enabled: Boolean) {} diff --git a/core/ui/src/jvmMain/kotlin/org/meshtastic/core/ui/util/PlatformUtils.kt b/core/ui/src/jvmMain/kotlin/org/meshtastic/core/ui/util/PlatformUtils.kt index b3fa848157..846a8e2763 100644 --- a/core/ui/src/jvmMain/kotlin/org/meshtastic/core/ui/util/PlatformUtils.kt +++ b/core/ui/src/jvmMain/kotlin/org/meshtastic/core/ui/util/PlatformUtils.kt @@ -76,7 +76,9 @@ actual fun rememberSaveFileLauncher( /** JVM — Opens a native file dialog to pick a file. */ @Composable actual fun rememberOpenFileLauncher(onUriReceived: (CommonUri?) -> Unit): (mimeType: String) -> Unit = { _ -> - val dialog = FileDialog(null as? Frame, "Open File", FileDialog.LOAD) + // Explicit Frame? local disambiguates the FileDialog(Frame, ...) overload from FileDialog(Dialog, ...) + val parentFrame: Frame? = null + val dialog = FileDialog(parentFrame, "Open File", FileDialog.LOAD) dialog.isVisible = true val file = dialog.file val dir = dialog.directory diff --git a/desktopApp/src/main/kotlin/org/meshtastic/desktop/stub/NoopStubs.kt b/desktopApp/src/main/kotlin/org/meshtastic/desktop/stub/NoopStubs.kt index 898c6e2925..c1490675c6 100644 --- a/desktopApp/src/main/kotlin/org/meshtastic/desktop/stub/NoopStubs.kt +++ b/desktopApp/src/main/kotlin/org/meshtastic/desktop/stub/NoopStubs.kt @@ -25,7 +25,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.emptyFlow import org.meshtastic.core.model.ConnectionState import org.meshtastic.core.model.DeviceType @@ -72,8 +71,8 @@ class NoopRadioInterfaceService : RadioInterfaceService { override fun isMockTransport(): Boolean = false override val receivedData = MutableSharedFlow() - override val meshActivity: Flow = MutableSharedFlow().asFlow() - override val connectionError: Flow = MutableSharedFlow().asFlow() + override val meshActivity: Flow = MutableSharedFlow() + override val connectionError: Flow = MutableSharedFlow() override fun sendToRadio(bytes: ByteArray) { logWarn("NoopRadioInterfaceService.sendToRadio(${bytes.size} bytes)") diff --git a/feature/car/src/main/kotlin/org/meshtastic/feature/car/screens/HomeScreen.kt b/feature/car/src/main/kotlin/org/meshtastic/feature/car/screens/HomeScreen.kt index 650f098c6f..b79780ed9c 100644 --- a/feature/car/src/main/kotlin/org/meshtastic/feature/car/screens/HomeScreen.kt +++ b/feature/car/src/main/kotlin/org/meshtastic/feature/car/screens/HomeScreen.kt @@ -257,12 +257,15 @@ class HomeScreen( } } - return ConversationItem.Builder() - .setId(conversation.contactKey) - .setTitle(CarText.create(conversation.displayName)) - .setMessages(messages) - .setSelf(selfPerson) - .setConversationCallback(callback) + // car-app 1.7 deprecated the no-arg Builder + individual setters in favor of the + // required-args constructor (id, title, self, messages, conversationCallback). + return ConversationItem.Builder( + conversation.contactKey, + CarText.create(conversation.displayName), + selfPerson, + messages, + callback, + ) .setGroupConversation(conversation.contactKey.contains(NodeAddress.ID_BROADCAST)) .build() } diff --git a/feature/connections/build.gradle.kts b/feature/connections/build.gradle.kts index 97fd9f284f..bbab106fbc 100644 --- a/feature/connections/build.gradle.kts +++ b/feature/connections/build.gradle.kts @@ -42,7 +42,7 @@ kotlin { androidMain.dependencies { implementation(libs.usb.serial.android) } - val androidHostTest by getting { + getByName("androidHostTest") { dependencies { implementation(projects.core.testing) implementation(libs.kotlinx.coroutines.test) diff --git a/feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/DiscoveryScanScreen.kt b/feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/DiscoveryScanScreen.kt index b556e6011d..02f3dbffa0 100644 --- a/feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/DiscoveryScanScreen.kt +++ b/feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/DiscoveryScanScreen.kt @@ -34,12 +34,12 @@ import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold @@ -361,7 +361,7 @@ private fun DwellTimePicker( readOnly = true, enabled = enabled, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, - modifier = Modifier.fillMaxWidth().menuAnchor(MenuAnchorType.PrimaryNotEditable), + modifier = Modifier.fillMaxWidth().menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable), ) ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { DWELL_OPTIONS.forEach { minutes -> diff --git a/feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/component/MeshBeaconInvitationCard.kt b/feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/component/MeshBeaconInvitationCard.kt index 23ae7ff3e9..f593e5a8e1 100644 --- a/feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/component/MeshBeaconInvitationCard.kt +++ b/feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/component/MeshBeaconInvitationCard.kt @@ -85,7 +85,7 @@ internal fun MeshBeaconInvitationCard( color = MaterialTheme.colorScheme.onSurfaceVariant, ) } - if (region != null && region != RegionCode.UNSET) { + if (region != RegionCode.UNSET) { Text( text = stringResource(Res.string.mesh_beacon_offer_region, region.name), style = MaterialTheme.typography.labelMedium, diff --git a/feature/docs/build.gradle.kts b/feature/docs/build.gradle.kts index b39cc766ed..948ddef73d 100644 --- a/feature/docs/build.gradle.kts +++ b/feature/docs/build.gradle.kts @@ -57,8 +57,8 @@ kotlin { * * This task runs automatically before resource generation tasks. */ -val syncDocsToComposeResources by - tasks.registering(Sync::class) { +val syncDocsToComposeResources = + tasks.register("syncDocsToComposeResources") { description = "Syncs docs/en/ markdown source into composeResources for in-app bundling" group = "docs" @@ -102,8 +102,8 @@ tasks // clean, or commit deliberately when refreshing the docs/Jekyll image set. Add a CI staleness gate // if drift becomes a problem. -val syncTranslatedDocsToComposeResources by - tasks.registering(Copy::class) { +val syncTranslatedDocsToComposeResources = + tasks.register("syncTranslatedDocsToComposeResources") { description = "Syncs Crowdin-translated docs into locale-qualified composeResources" group = "docs" diff --git a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt index 5b2bdfef8d..4ff36840da 100644 --- a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt +++ b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt @@ -145,7 +145,6 @@ import org.meshtastic.core.ui.icon.Dangerous import org.meshtastic.core.ui.icon.Folder import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.Refresh -import org.meshtastic.core.ui.icon.SystemUpdate import org.meshtastic.core.ui.icon.Usb import org.meshtastic.core.ui.icon.Warning import org.meshtastic.core.ui.icon.Wifi @@ -462,7 +461,6 @@ private fun ReadyState( FirmwareUpdateMethod.Ble -> MeshtasticIcons.Bluetooth FirmwareUpdateMethod.Usb -> MeshtasticIcons.Usb FirmwareUpdateMethod.Wifi -> MeshtasticIcons.Wifi - else -> MeshtasticIcons.SystemUpdate }, contentDescription = null, ) diff --git a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt index 96b7af668f..92dbd776f8 100644 --- a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt +++ b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt @@ -21,6 +21,7 @@ import androidx.lifecycle.viewModelScope import co.touchlab.kermit.Logger import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.delay @@ -134,6 +135,7 @@ class FirmwareUpdateViewModel( } } + @OptIn(DelicateCoroutinesApi::class) override fun onCleared() { super.onCleared() // viewModelScope is already cancelled when onCleared() runs, so launch cleanup on the diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/domain/usecase/CommonGetNodeDetailsUseCase.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/domain/usecase/CommonGetNodeDetailsUseCase.kt index c588259302..f449704353 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/domain/usecase/CommonGetNodeDetailsUseCase.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/domain/usecase/CommonGetNodeDetailsUseCase.kt @@ -176,8 +176,13 @@ constructor( val logs = args[LOGS_INDEX] as LogsGroup val identity = args[IDENTITY_INDEX] as IdentityGroup val metadata = args[METADATA_INDEX] as MetadataGroup + + @Suppress("UNCHECKED_CAST") val requests = args[REQUESTS_INDEX] as Pair, List> - val (hw, deviceLinks) = args[HARDWARE_INDEX] as Pair> + + @Suppress("UNCHECKED_CAST") + val hardwareAndLinks = args[HARDWARE_INDEX] as Pair> + val (hw, deviceLinks) = hardwareAndLinks val (trReqs, niReqs) = requests val isLocal = node.num == identity.ourNode?.num diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/AirQualityMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/AirQualityMetrics.kt index b9ce30fa99..547bd53402 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/AirQualityMetrics.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/AirQualityMetrics.kt @@ -49,7 +49,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.patrykandpatrick.vico.compose.cartesian.VicoScrollState import com.patrykandpatrick.vico.compose.cartesian.axis.Axis -import com.patrykandpatrick.vico.compose.cartesian.data.lineSeries +import com.patrykandpatrick.vico.compose.cartesian.data.lineModel import com.patrykandpatrick.vico.compose.cartesian.layer.LineCartesianLayer import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource @@ -223,7 +223,7 @@ private fun AirQualityChart( activeMetrics.forEachIndexed { index, metric -> val metricData = metricDataSets[index] if (metricData.isNotEmpty()) { - lineSeries { + lineModel { series(x = metricData.map { it.time }, y = metricData.map { metric.getValue(it) ?: 0f }) } } diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt index 99a5e7869d..87df574c2f 100644 --- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt +++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt @@ -133,7 +133,7 @@ fun GenericMetricChart( bottomAxis = bottomAxis, marker = marker, markerVisibilityListener = markerVisibilityListener, - persistentMarkers = { _ -> if (selectedX != null && marker != null) marker at selectedX else null }, + persistentMarkers = { _ -> if (selectedX != null && marker != null) marker at selectedX }, fadingEdges = rememberFadingEdges(), decorations = decorations, // Telemetry timestamps arrive at irregular intervals. Without an explicit diff --git a/feature/settings/detekt-baseline.xml b/feature/settings/detekt-baseline.xml index 7a54174fc0..d1ae234f8e 100644 --- a/feature/settings/detekt-baseline.xml +++ b/feature/settings/detekt-baseline.xml @@ -58,7 +58,7 @@ ModifierMissing:ExternalNotificationConfigScreen.android.kt:@Suppress("TooGenericExceptionCaught") @Composable actual fun RingtoneTrailingIcon ModifierMissing:FilterSettingsScreen.kt:@Composable fun FilterSettingsScreen ModifierMissing:LoRaConfigItemList.kt:@Composable fun LoRaConfigScreen - ModifierMissing:MQTTConfigItemList.kt:@Composable fun MQTTConfigScreen + ModifierMissing:MQTTConfigItemList.kt:@Suppress("DEPRECATION") @Composable fun MQTTConfigScreen ModifierMissing:MapReportingPreference.kt:@Suppress("LongMethod") @Composable fun MapReportingPreference ModifierMissing:ModuleConfigurationScreen.kt:@Composable fun ModuleConfigurationScreen ModifierMissing:NeighborInfoConfigItemList.kt:@Composable fun NeighborInfoConfigScreen diff --git a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/MQTTConfigItemList.kt b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/MQTTConfigItemList.kt index 80eccdef5c..8a9b086a06 100644 --- a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/MQTTConfigItemList.kt +++ b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/MQTTConfigItemList.kt @@ -89,6 +89,9 @@ import org.meshtastic.core.ui.component.TitledCard import org.meshtastic.feature.settings.radio.RadioConfigViewModel import org.meshtastic.proto.ModuleConfig +// json_enabled is deprecated in the protobuf schema but remains the only toggle for MQTT JSON +// publish/consume, so the settings UI must keep exposing it until the proto provides a replacement. +@Suppress("DEPRECATION") @Composable fun MQTTConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) { val state by viewModel.radioConfigState.collectAsStateWithLifecycle()