mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-07-08 04:15:37 -04:00
chore: fix build warnings and migrate deprecated APIs (#6130)
This commit is contained in:
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import kotlin.test.Test
|
||||
|
||||
class KoinVerificationTest {
|
||||
|
||||
@OptIn(org.koin.core.annotation.KoinExperimentalAPI::class)
|
||||
@Test
|
||||
fun verifyKoinConfiguration() {
|
||||
AppKoinModule()
|
||||
|
||||
@@ -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<String>) {
|
||||
// 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)) }
|
||||
}
|
||||
|
||||
@@ -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<String>) {
|
||||
subprojectPaths.forEach { path -> dependencies.add("kover", project(path)) }
|
||||
subprojectPaths.forEach { path -> dependencies.add("kover", dependencies.project(path)) }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -28,7 +28,7 @@ kotlin {
|
||||
android { withHostTest {} }
|
||||
|
||||
sourceSets {
|
||||
val jvmTest by getting {
|
||||
getByName("jvmTest") {
|
||||
dependencies {
|
||||
implementation(libs.konsist)
|
||||
implementation(libs.junit)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<MqttClientProxyMessage> = callbackFlow {
|
||||
// Append a per-connection random id. myId identifies the *node* (and is null →
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<CoTMessage> = try {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ---
|
||||
|
||||
@@ -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) }
|
||||
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<ByteArray>()
|
||||
override val meshActivity: Flow<MeshActivity> = MutableSharedFlow<MeshActivity>().asFlow()
|
||||
override val connectionError: Flow<String> = MutableSharedFlow<String>().asFlow()
|
||||
override val meshActivity: Flow<MeshActivity> = MutableSharedFlow<MeshActivity>()
|
||||
override val connectionError: Flow<String> = MutableSharedFlow<String>()
|
||||
|
||||
override fun sendToRadio(bytes: ByteArray) {
|
||||
logWarn("NoopRadioInterfaceService.sendToRadio(${bytes.size} bytes)")
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -57,8 +57,8 @@ kotlin {
|
||||
*
|
||||
* This task runs automatically before resource generation tasks.
|
||||
*/
|
||||
val syncDocsToComposeResources by
|
||||
tasks.registering(Sync::class) {
|
||||
val syncDocsToComposeResources =
|
||||
tasks.register<Sync>("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<Copy>("syncTranslatedDocsToComposeResources") {
|
||||
description = "Syncs Crowdin-translated docs into locale-qualified composeResources"
|
||||
group = "docs"
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<MeshLog>, List<MeshLog>>
|
||||
val (hw, deviceLinks) = args[HARDWARE_INDEX] as Pair<DeviceHardware?, List<DeviceLink>>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val hardwareAndLinks = args[HARDWARE_INDEX] as Pair<DeviceHardware?, List<DeviceLink>>
|
||||
val (hw, deviceLinks) = hardwareAndLinks
|
||||
|
||||
val (trReqs, niReqs) = requests
|
||||
val isLocal = node.num == identity.ourNode?.num
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<ID>ModifierMissing:ExternalNotificationConfigScreen.android.kt:@Suppress("TooGenericExceptionCaught") @Composable actual fun RingtoneTrailingIcon</ID>
|
||||
<ID>ModifierMissing:FilterSettingsScreen.kt:@Composable fun FilterSettingsScreen</ID>
|
||||
<ID>ModifierMissing:LoRaConfigItemList.kt:@Composable fun LoRaConfigScreen</ID>
|
||||
<ID>ModifierMissing:MQTTConfigItemList.kt:@Composable fun MQTTConfigScreen</ID>
|
||||
<ID>ModifierMissing:MQTTConfigItemList.kt:@Suppress("DEPRECATION") @Composable fun MQTTConfigScreen</ID>
|
||||
<ID>ModifierMissing:MapReportingPreference.kt:@Suppress("LongMethod") @Composable fun MapReportingPreference</ID>
|
||||
<ID>ModifierMissing:ModuleConfigurationScreen.kt:@Composable fun ModuleConfigurationScreen</ID>
|
||||
<ID>ModifierMissing:NeighborInfoConfigItemList.kt:@Composable fun NeighborInfoConfigScreen</ID>
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user