diff --git a/AGENTS.md b/AGENTS.md index ae128ca37f..69027f403e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,13 +32,13 @@ This file serves as a comprehensive guide for AI agents and developers working o - **Material 3:** The app uses Material 3. Look for ways to use **Material 3 Expressive** components where appropriate. - **Strings:** - Do **not** use `app/src/main/res/values/strings.xml` for UI strings. - - Use the **Compose Multiplatform Resource** library in `core/strings`. - - **Definition:** Add strings to `core/strings/src/commonMain/composeResources/values/strings.xml`. + - Use the **Compose Multiplatform Resource** library in `core:resources`. + - **Definition:** Add strings to `core/resources/src/commonMain/composeResources/values/strings.xml`. - **Usage:** ```kotlin import org.jetbrains.compose.resources.stringResource - import org.meshtastic.core.strings.Res - import org.meshtastic.core.strings.your_string_key + import org.meshtastic.core.resources.Res + import org.meshtastic.core.resources.your_string_key Text(text = stringResource(Res.string.your_string_key)) ``` @@ -102,7 +102,7 @@ This file serves as a comprehensive guide for AI agents and developers working o 1. **Explore First:** Before making changes, read `gradle/libs.versions.toml` and the relevant `build.gradle.kts` to understand the environment. 2. **Plan:** Identify which modules (`core` or `feature`) need modification. 3. **Implement:** - - If adding a string, modify `core/strings`. + - If adding a string, modify `core:resources`. - If adding a dependency, modify `libs.versions.toml` first. 4. **Verify:** - Run `./gradlew spotlessApply` (Essential!). @@ -118,8 +118,14 @@ This file serves as a comprehensive guide for AI agents and developers working o ## 7. Troubleshooting -- **Missing Strings:** If `Res.string.xyz` is unresolved, ensure you have imported `org.meshtastic.core.strings.Res` and the specific string property, and that you have run a build to generate the resources. +- **Missing Strings:** If `Res.string.xyz` is unresolved, ensure you have imported `org.meshtastic.core.resources.Res` and the specific string property, and that you have run a build to generate the resources. - **Build Errors:** Check `gradle/libs.versions.toml` for version conflicts. Use `build-logic` conventions to ensure plugins are applied correctly. --- *Refer to `CONTRIBUTING.md` for human-centric processes like Code of Conduct and Pull Request etiquette.* + +### E. Resources and Assets +- **Centralization:** All global app resources (Strings, Drawables, Fonts, raw files) should be placed in `:core:resources`. +- **Module Path:** `core/resources/src/commonMain/composeResources/` +- **Decentralization:** Feature-specific strings and assets can (and should) be housed in their respective feature module's `composeResources` directory to maintain modular boundaries and clean architectural dependency graphs. Crowdin localization handles globbing `/**/composeResources/values/strings.xml` perfectly. +- **Drawables:** Use `painterResource(Res.drawable.your_icon)` to access cross-platform drawables. Name them consistently (`ic_` for icons, `img_` for artwork). Avoid putting standard Drawables or Vectors in legacy Android `res/drawable` folders unless strictly required by a legacy library (like `OsmDroid` map markers) or the OS layer (like `app_icon.xml`). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b47041cebb..d64fe9976d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,14 +19,14 @@ Thank you for your interest in contributing to Meshtastic-Android! We welcome co - Write clear, descriptive variable and function names. - Add comments where necessary, especially for complex logic. - Keep methods and classes focused and concise. -- **Strings:** Use localised strings via the **Compose Multiplatform Resource** library in `:core:strings`. +- **Strings:** Use localised strings via the **Compose Multiplatform Resource** library in `:core:resources`. - Do **not** use the legacy `app/src/main/res/values/strings.xml`. - - **Definition:** Add strings to `core/strings/src/commonMain/composeResources/values/strings.xml`. + - **Definition:** Add strings to `core/resources/src/commonMain/composeResources/values/strings.xml`. - **Usage:** ```kotlin import org.jetbrains.compose.resources.stringResource - import org.meshtastic.core.strings.Res - import org.meshtastic.core.strings.your_string_key + import org.meshtastic.core.resources.Res + import org.meshtastic.core.resources.your_string_key Text(text = stringResource(Res.string.your_string_key)) ``` diff --git a/app/README.md b/app/README.md index 6dd8c1ca7b..d61f3a4183 100644 --- a/app/README.md +++ b/app/README.md @@ -39,7 +39,7 @@ graph TB :app -.-> :core:prefs :app -.-> :core:proto :app -.-> :core:service - :app -.-> :core:strings + :app -.-> :core:resources :app -.-> :core:ui :app -.-> :core:barcode :app -.-> :feature:intro diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7503e1f7b0..1743e37bce 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -188,11 +188,9 @@ secrets { androidComponents { onVariants(selector().withBuildType("debug")) { variant -> - variant.flavorName?.let { flavor -> - variant.applicationId = "com.geeksville.mesh.$flavor.debug" - } + variant.flavorName?.let { flavor -> variant.applicationId = "com.geeksville.mesh.$flavor.debug" } } - + onVariants(selector().withBuildType("release")) { variant -> if (variant.flavorName == "google") { val variantNameCapped = variant.name.replaceFirstChar { it.uppercase() } @@ -226,7 +224,7 @@ dependencies { implementation(projects.core.prefs) implementation(projects.core.proto) implementation(projects.core.service) - implementation(projects.core.strings) + implementation(projects.core.resources) implementation(projects.core.ui) implementation(projects.core.barcode) implementation(projects.feature.intro) diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index ef82258384..3b5dffc1e3 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -50,8 +50,8 @@ import no.nordicsemi.kotlin.ble.core.android.AndroidEnvironment import no.nordicsemi.kotlin.ble.environment.android.compose.LocalEnvironmentOwner import org.meshtastic.core.model.util.dispatchMeshtasticUri import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.channel_invalid +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.channel_invalid import org.meshtastic.core.ui.theme.AppTheme import org.meshtastic.core.ui.theme.MODE_DYNAMIC import org.meshtastic.core.ui.util.showToast diff --git a/app/src/main/java/com/geeksville/mesh/domain/usecase/GetDiscoveredDevicesUseCase.kt b/app/src/main/java/com/geeksville/mesh/domain/usecase/GetDiscoveredDevicesUseCase.kt new file mode 100644 index 0000000000..a6759dae6c --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/domain/usecase/GetDiscoveredDevicesUseCase.kt @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2025-2026 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 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.domain.usecase + +import android.hardware.usb.UsbManager +import android.net.nsd.NsdServiceInfo +import com.geeksville.mesh.model.DeviceListEntry +import com.geeksville.mesh.model.getMeshtasticShortName +import com.geeksville.mesh.repository.network.NetworkRepository +import com.geeksville.mesh.repository.network.NetworkRepository.Companion.toAddressString +import com.geeksville.mesh.repository.radio.RadioInterfaceService +import com.geeksville.mesh.repository.usb.UsbRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import org.jetbrains.compose.resources.getString +import org.meshtastic.core.ble.BluetoothRepository +import org.meshtastic.core.data.repository.NodeRepository +import org.meshtastic.core.database.DatabaseManager +import org.meshtastic.core.database.model.Node +import org.meshtastic.core.datastore.RecentAddressesDataSource +import org.meshtastic.core.datastore.model.RecentAddress +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.meshtastic +import java.util.Locale +import javax.inject.Inject + +data class DiscoveredDevices( + val bleDevices: List, + val usbDevices: List, + val discoveredTcpDevices: List, + val recentTcpDevices: List, +) + +@Suppress("LongParameterList") +class GetDiscoveredDevicesUseCase +@Inject +constructor( + private val bluetoothRepository: BluetoothRepository, + private val networkRepository: NetworkRepository, + private val recentAddressesDataSource: RecentAddressesDataSource, + private val nodeRepository: NodeRepository, + private val databaseManager: DatabaseManager, + private val usbRepository: UsbRepository, + private val radioInterfaceService: RadioInterfaceService, + private val usbManagerLazy: dagger.Lazy, +) { + private val suffixLength = 4 + + @Suppress("LongMethod", "CyclomaticComplexMethod") + fun invoke(showMock: Boolean): Flow { + val nodeDb = nodeRepository.nodeDBbyNum + + val bondedBleFlow = bluetoothRepository.state.map { ble -> ble.bondedDevices.map { DeviceListEntry.Ble(it) } } + + val processedTcpFlow = + combine(networkRepository.resolvedList, recentAddressesDataSource.recentAddresses) { + tcpServices, + recentList, + -> + val recentMap = recentList.associateBy({ it.address }) { it.name } + tcpServices + .map { service -> + val address = "t${service.toAddressString()}" + val txtRecords = service.attributes + val shortNameBytes = txtRecords["shortname"] + val idBytes = txtRecords["id"] + + val shortName = + shortNameBytes?.let { String(it, Charsets.UTF_8) } ?: getString(Res.string.meshtastic) + val deviceId = idBytes?.let { String(it, Charsets.UTF_8) }?.replace("!", "") + var displayName = recentMap[address] ?: shortName + if (deviceId != null && (displayName.split("_").none { it == deviceId })) { + displayName += "_$deviceId" + } + DeviceListEntry.Tcp(displayName, address) + } + .sortedBy { it.name } + } + + val usbDevicesFlow = + usbRepository.serialDevices.map { usb -> + usb.map { (_, d) -> DeviceListEntry.Usb(radioInterfaceService, usbManagerLazy.get(), d) } + } + + return combine( + nodeDb, + bondedBleFlow, + processedTcpFlow, + usbDevicesFlow, + networkRepository.resolvedList, + recentAddressesDataSource.recentAddresses, + ) { args: Array -> + @Suppress("UNCHECKED_CAST", "MagicNumber") + val db = args[0] as Map + + @Suppress("UNCHECKED_CAST", "MagicNumber") + val bondedBle = args[1] as List + + @Suppress("UNCHECKED_CAST", "MagicNumber") + val processedTcp = args[2] as List + + @Suppress("UNCHECKED_CAST", "MagicNumber") + val usbDevices = args[3] as List + + @Suppress("UNCHECKED_CAST", "MagicNumber") + val resolved = args[4] as List + + @Suppress("UNCHECKED_CAST", "MagicNumber") + val recentList = args[5] as List + + val bleForUi = + bondedBle + .map { entry -> + val matchingNode = + if (databaseManager.hasDatabaseFor(entry.fullAddress)) { + db.values.find { node -> + val suffix = entry.peripheral.getMeshtasticShortName()?.lowercase(Locale.ROOT) + suffix != null && node.user.id.lowercase(Locale.ROOT).endsWith(suffix) + } + } else { + null + } + entry.copy(node = matchingNode) + } + .sortedBy { it.name } + + val usbForUi = + (usbDevices + if (showMock) listOf(DeviceListEntry.Mock("Demo Mode")) else emptyList()).map { entry -> + val matchingNode = + if (databaseManager.hasDatabaseFor(entry.fullAddress)) { + db.values.find { node -> + val suffix = entry.name.split("_").lastOrNull()?.lowercase(Locale.ROOT) + suffix != null && + suffix.length >= suffixLength && + node.user.id.lowercase(Locale.ROOT).endsWith(suffix) + } + } else { + null + } + entry.copy(node = matchingNode) + } + + val discoveredTcpForUi = + processedTcp.map { entry -> + val matchingNode = + if (databaseManager.hasDatabaseFor(entry.fullAddress)) { + val resolvedService = resolved.find { "t${it.toAddressString()}" == entry.fullAddress } + val deviceId = resolvedService?.attributes?.get("id")?.let { String(it, Charsets.UTF_8) } + db.values.find { node -> + node.user.id == deviceId || (deviceId != null && node.user.id == "!$deviceId") + } + } else { + null + } + entry.copy(node = matchingNode) + } + + val discoveredTcpAddresses = processedTcp.map { it.fullAddress }.toSet() + val recentTcpForUi = + recentList + .filterNot { discoveredTcpAddresses.contains(it.address) } + .map { DeviceListEntry.Tcp(it.name, it.address) } + .map { entry -> + val matchingNode = + if (databaseManager.hasDatabaseFor(entry.fullAddress)) { + val suffix = entry.name.split("_").lastOrNull()?.lowercase(Locale.ROOT) + db.values.find { node -> + suffix != null && + suffix.length >= suffixLength && + node.user.id.lowercase(Locale.ROOT).endsWith(suffix) + } + } else { + null + } + entry.copy(node = matchingNode) + } + .sortedBy { it.name } + + DiscoveredDevices( + bleDevices = bleForUi, + usbDevices = usbForUi, + discoveredTcpDevices = discoveredTcpForUi, + recentTcpDevices = recentTcpForUi, + ) + } + } +} diff --git a/app/src/main/java/com/geeksville/mesh/model/UIViewModel.kt b/app/src/main/java/com/geeksville/mesh/model/UIViewModel.kt index 87e0519325..52ef78ce5e 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIViewModel.kt @@ -53,13 +53,13 @@ import org.meshtastic.core.datastore.UiPreferencesDataSource import org.meshtastic.core.model.TracerouteMapAvailability import org.meshtastic.core.model.evaluateTracerouteMapAvailability import org.meshtastic.core.model.util.dispatchMeshtasticUri +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.client_notification +import org.meshtastic.core.resources.compromised_keys import org.meshtastic.core.service.IMeshService import org.meshtastic.core.service.MeshServiceNotifications import org.meshtastic.core.service.ServiceRepository import org.meshtastic.core.service.TracerouteResponse -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.client_notification -import org.meshtastic.core.strings.compromised_keys import org.meshtastic.core.ui.component.ScrollToTopEvent import org.meshtastic.core.ui.util.AlertManager import org.meshtastic.core.ui.util.ComposableContent diff --git a/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt b/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt index 0a8e50f344..d9fded5b43 100644 --- a/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt +++ b/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt @@ -46,16 +46,16 @@ import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI import org.meshtastic.core.navigation.NodeDetailRoutes import org.meshtastic.core.navigation.NodesRoutes import org.meshtastic.core.navigation.Route -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.device -import org.meshtastic.core.strings.environment -import org.meshtastic.core.strings.host -import org.meshtastic.core.strings.neighbor_info -import org.meshtastic.core.strings.pax -import org.meshtastic.core.strings.position_log -import org.meshtastic.core.strings.power -import org.meshtastic.core.strings.signal -import org.meshtastic.core.strings.traceroute +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.device +import org.meshtastic.core.resources.environment +import org.meshtastic.core.resources.host +import org.meshtastic.core.resources.neighbor_info +import org.meshtastic.core.resources.pax +import org.meshtastic.core.resources.position_log +import org.meshtastic.core.resources.power +import org.meshtastic.core.resources.signal +import org.meshtastic.core.resources.traceroute import org.meshtastic.core.ui.component.ScrollToTopEvent import org.meshtastic.feature.map.node.NodeMapScreen import org.meshtastic.feature.map.node.NodeMapViewModel diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshConnectionManager.kt b/app/src/main/java/com/geeksville/mesh/service/MeshConnectionManager.kt index 4512b7a7d2..f746684259 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshConnectionManager.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshConnectionManager.kt @@ -36,14 +36,14 @@ import org.meshtastic.core.common.util.nowSeconds import org.meshtastic.core.data.repository.NodeRepository import org.meshtastic.core.data.repository.RadioConfigRepository import org.meshtastic.core.prefs.ui.UiPrefs +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.connected_count +import org.meshtastic.core.resources.connecting +import org.meshtastic.core.resources.device_sleeping +import org.meshtastic.core.resources.disconnected +import org.meshtastic.core.resources.getString import org.meshtastic.core.service.ConnectionState import org.meshtastic.core.service.MeshServiceNotifications -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.connected_count -import org.meshtastic.core.strings.connecting -import org.meshtastic.core.strings.device_sleeping -import org.meshtastic.core.strings.disconnected -import org.meshtastic.core.strings.getString import org.meshtastic.proto.AdminMessage import org.meshtastic.proto.Config import org.meshtastic.proto.Telemetry diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt b/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt index bc880d4111..36338d4934 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshDataHandler.kt @@ -43,15 +43,15 @@ import org.meshtastic.core.model.util.SfppHasher import org.meshtastic.core.model.util.decodeOrNull import org.meshtastic.core.model.util.toOneLiner import org.meshtastic.core.prefs.mesh.MeshPrefs +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.critical_alert +import org.meshtastic.core.resources.error_duty_cycle +import org.meshtastic.core.resources.getString +import org.meshtastic.core.resources.unknown_username +import org.meshtastic.core.resources.waypoint_received import org.meshtastic.core.service.MeshServiceNotifications import org.meshtastic.core.service.ServiceRepository import org.meshtastic.core.service.filter.MessageFilterService -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.critical_alert -import org.meshtastic.core.strings.error_duty_cycle -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.unknown_username -import org.meshtastic.core.strings.waypoint_received import org.meshtastic.proto.AdminMessage import org.meshtastic.proto.MeshPacket import org.meshtastic.proto.Paxcount diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshNeighborInfoHandler.kt b/app/src/main/java/com/geeksville/mesh/service/MeshNeighborInfoHandler.kt index 37694ada0e..3574bf6e1c 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshNeighborInfoHandler.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshNeighborInfoHandler.kt @@ -21,10 +21,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import org.meshtastic.core.common.util.nowMillis +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.getString +import org.meshtastic.core.resources.unknown_username import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.unknown_username import org.meshtastic.proto.MeshPacket import org.meshtastic.proto.NeighborInfo import java.util.Locale diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 34e1adf4da..db1a6066ff 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -107,7 +107,19 @@ class MeshService : Service() { } override fun onCreate() { - super.onCreate() + try { + super.onCreate() + } catch (e: IllegalStateException) { + // Hilt can throw IllegalStateException in tests if the component is not created. + // This can happen if the service is started by the system (e.g. after a crash or on boot) + // before the test rule has a chance to create the component. + if (e.message?.contains("HiltAndroidRule") == true) { + Logger.w(e) { "MeshService created before Hilt component was ready in test. Stopping service." } + stopSelf() + return + } + throw e + } Logger.i { "Creating mesh service" } serviceNotifications.initChannels() diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt b/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt index 62fe766e11..0a37174ee6 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshServiceNotificationsImpl.kt @@ -53,27 +53,27 @@ import org.meshtastic.core.database.model.Message import org.meshtastic.core.model.DataPacket import org.meshtastic.core.model.util.formatUptime import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.client_notification +import org.meshtastic.core.resources.getString +import org.meshtastic.core.resources.low_battery_message +import org.meshtastic.core.resources.low_battery_title +import org.meshtastic.core.resources.mark_as_read +import org.meshtastic.core.resources.meshtastic_alerts_notifications +import org.meshtastic.core.resources.meshtastic_app_name +import org.meshtastic.core.resources.meshtastic_broadcast_notifications +import org.meshtastic.core.resources.meshtastic_low_battery_notifications +import org.meshtastic.core.resources.meshtastic_low_battery_temporary_remote_notifications +import org.meshtastic.core.resources.meshtastic_messages_notifications +import org.meshtastic.core.resources.meshtastic_new_nodes_notifications +import org.meshtastic.core.resources.meshtastic_service_notifications +import org.meshtastic.core.resources.meshtastic_waypoints_notifications +import org.meshtastic.core.resources.new_node_seen +import org.meshtastic.core.resources.no_local_stats +import org.meshtastic.core.resources.reply +import org.meshtastic.core.resources.you import org.meshtastic.core.service.MeshServiceNotifications import org.meshtastic.core.service.SERVICE_NOTIFY_ID -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.client_notification -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.low_battery_message -import org.meshtastic.core.strings.low_battery_title -import org.meshtastic.core.strings.mark_as_read -import org.meshtastic.core.strings.meshtastic_alerts_notifications -import org.meshtastic.core.strings.meshtastic_app_name -import org.meshtastic.core.strings.meshtastic_broadcast_notifications -import org.meshtastic.core.strings.meshtastic_low_battery_notifications -import org.meshtastic.core.strings.meshtastic_low_battery_temporary_remote_notifications -import org.meshtastic.core.strings.meshtastic_messages_notifications -import org.meshtastic.core.strings.meshtastic_new_nodes_notifications -import org.meshtastic.core.strings.meshtastic_service_notifications -import org.meshtastic.core.strings.meshtastic_waypoints_notifications -import org.meshtastic.core.strings.new_node_seen -import org.meshtastic.core.strings.no_local_stats -import org.meshtastic.core.strings.reply -import org.meshtastic.core.strings.you import org.meshtastic.proto.ClientNotification import org.meshtastic.proto.DeviceMetrics import org.meshtastic.proto.LocalStats diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshTracerouteHandler.kt b/app/src/main/java/com/geeksville/mesh/service/MeshTracerouteHandler.kt index d03c3042a0..0ca3e3947c 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshTracerouteHandler.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshTracerouteHandler.kt @@ -26,14 +26,14 @@ import org.meshtastic.core.data.repository.NodeRepository import org.meshtastic.core.data.repository.TracerouteSnapshotRepository import org.meshtastic.core.model.fullRouteDiscovery import org.meshtastic.core.model.getFullTracerouteResponse +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.getString +import org.meshtastic.core.resources.traceroute_duration +import org.meshtastic.core.resources.traceroute_route_back_to_us +import org.meshtastic.core.resources.traceroute_route_towards_dest +import org.meshtastic.core.resources.unknown_username import org.meshtastic.core.service.ServiceRepository import org.meshtastic.core.service.TracerouteResponse -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.traceroute_duration -import org.meshtastic.core.strings.traceroute_route_back_to_us -import org.meshtastic.core.strings.traceroute_route_towards_dest -import org.meshtastic.core.strings.unknown_username import org.meshtastic.proto.MeshPacket import java.util.Locale import javax.inject.Inject diff --git a/app/src/main/java/com/geeksville/mesh/ui/Main.kt b/app/src/main/java/com/geeksville/mesh/ui/Main.kt index 170926b7cc..bc3b82a6d4 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt @@ -106,26 +106,26 @@ import org.meshtastic.core.navigation.NodeDetailRoutes import org.meshtastic.core.navigation.NodesRoutes import org.meshtastic.core.navigation.Route import org.meshtastic.core.navigation.SettingsRoutes +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.app_too_old +import org.meshtastic.core.resources.bottom_nav_settings +import org.meshtastic.core.resources.connected +import org.meshtastic.core.resources.connecting +import org.meshtastic.core.resources.connections +import org.meshtastic.core.resources.conversations +import org.meshtastic.core.resources.device_sleeping +import org.meshtastic.core.resources.disconnected +import org.meshtastic.core.resources.firmware_old +import org.meshtastic.core.resources.firmware_too_old +import org.meshtastic.core.resources.map +import org.meshtastic.core.resources.must_update +import org.meshtastic.core.resources.nodes +import org.meshtastic.core.resources.okay +import org.meshtastic.core.resources.should_update +import org.meshtastic.core.resources.should_update_firmware +import org.meshtastic.core.resources.traceroute +import org.meshtastic.core.resources.view_on_map import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.app_too_old -import org.meshtastic.core.strings.bottom_nav_settings -import org.meshtastic.core.strings.connected -import org.meshtastic.core.strings.connecting -import org.meshtastic.core.strings.connections -import org.meshtastic.core.strings.conversations -import org.meshtastic.core.strings.device_sleeping -import org.meshtastic.core.strings.disconnected -import org.meshtastic.core.strings.firmware_old -import org.meshtastic.core.strings.firmware_too_old -import org.meshtastic.core.strings.map -import org.meshtastic.core.strings.must_update -import org.meshtastic.core.strings.nodes -import org.meshtastic.core.strings.okay -import org.meshtastic.core.strings.should_update -import org.meshtastic.core.strings.should_update_firmware -import org.meshtastic.core.strings.traceroute -import org.meshtastic.core.strings.view_on_map import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.core.ui.component.ScrollToTopEvent import org.meshtastic.core.ui.icon.Conversations diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt index 6b2873d5b4..7f9c74d594 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt @@ -64,17 +64,17 @@ import org.jetbrains.compose.resources.getString import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.navigation.Route import org.meshtastic.core.navigation.SettingsRoutes +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.connected +import org.meshtastic.core.resources.connected_device +import org.meshtastic.core.resources.connected_sleeping +import org.meshtastic.core.resources.connecting +import org.meshtastic.core.resources.connections +import org.meshtastic.core.resources.must_set_region +import org.meshtastic.core.resources.no_device_selected +import org.meshtastic.core.resources.not_connected +import org.meshtastic.core.resources.set_your_region import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.connected -import org.meshtastic.core.strings.connected_device -import org.meshtastic.core.strings.connected_sleeping -import org.meshtastic.core.strings.connecting -import org.meshtastic.core.strings.connections -import org.meshtastic.core.strings.must_set_region -import org.meshtastic.core.strings.no_device_selected -import org.meshtastic.core.strings.not_connected -import org.meshtastic.core.strings.set_your_region import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.component.TitledCard diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/ScannerViewModel.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/ScannerViewModel.kt index f694a3bf88..131eb33e8f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/ScannerViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/connections/ScannerViewModel.kt @@ -18,16 +18,13 @@ package com.geeksville.mesh.ui.connections import android.app.Application import android.content.Context -import android.hardware.usb.UsbManager import android.os.RemoteException import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import co.touchlab.kermit.Logger import co.touchlab.kermit.Severity +import com.geeksville.mesh.domain.usecase.GetDiscoveredDevicesUseCase import com.geeksville.mesh.model.DeviceListEntry -import com.geeksville.mesh.model.getMeshtasticShortName -import com.geeksville.mesh.repository.network.NetworkRepository -import com.geeksville.mesh.repository.network.NetworkRepository.Companion.toAddressString import com.geeksville.mesh.repository.radio.RadioInterfaceService import com.geeksville.mesh.repository.usb.UsbRepository import com.geeksville.mesh.service.MeshService @@ -36,25 +33,18 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import org.jetbrains.compose.resources.getString import org.meshtastic.core.ble.BluetoothRepository -import org.meshtastic.core.data.repository.NodeRepository -import org.meshtastic.core.database.DatabaseManager -import org.meshtastic.core.database.model.Node import org.meshtastic.core.datastore.RecentAddressesDataSource import org.meshtastic.core.datastore.model.RecentAddress import org.meshtastic.core.model.util.anonymize import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.meshtastic import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed -import java.util.Locale import javax.inject.Inject @HiltViewModel @@ -66,12 +56,9 @@ constructor( private val serviceRepository: ServiceRepository, private val bluetoothRepository: BluetoothRepository, private val usbRepository: UsbRepository, - private val usbManagerLazy: dagger.Lazy, - private val networkRepository: NetworkRepository, private val radioInterfaceService: RadioInterfaceService, private val recentAddressesDataSource: RecentAddressesDataSource, - private val nodeRepository: NodeRepository, - private val databaseManager: DatabaseManager, + private val getDiscoveredDevicesUseCase: GetDiscoveredDevicesUseCase, ) : ViewModel() { private val context: Context get() = application.applicationContext @@ -81,142 +68,32 @@ constructor( private val _errorText = MutableStateFlow(null) val errorText: StateFlow = _errorText.asStateFlow() - private val nodeDb: StateFlow> = nodeRepository.nodeDBbyNum - - private val bondedBleDevicesFlow: StateFlow> = - bluetoothRepository.state - .map { ble -> ble.bondedDevices.map { DeviceListEntry.Ble(it) } } - .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) - - // Flow for discovered TCP devices, using recent addresses for potential name enrichment - private val processedDiscoveredTcpDevicesFlow: StateFlow> = - combine(networkRepository.resolvedList, recentAddressesDataSource.recentAddresses) { tcpServices, recentList -> - val recentMap = recentList.associateBy({ it.address }) { it.name } - tcpServices - .map { service -> - val address = "t${service.toAddressString()}" - val txtRecords = service.attributes // Map - val shortNameBytes = txtRecords["shortname"] - val idBytes = txtRecords["id"] - - val shortName = - shortNameBytes?.let { String(it, Charsets.UTF_8) } ?: getString(Res.string.meshtastic) - val deviceId = idBytes?.let { String(it, Charsets.UTF_8) }?.replace("!", "") - var displayName = recentMap[address] ?: shortName - if (deviceId != null && (displayName.split("_").none { it == deviceId })) { - displayName += "_$deviceId" - } - DeviceListEntry.Tcp(displayName, address) - } - .sortedBy { it.name } - } - .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) + private val discoveredDevicesFlow = + showMockInterface + .flatMapLatest { showMock -> getDiscoveredDevicesUseCase.invoke(showMock) } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null) /** A combined list of bonded BLE devices for the UI. */ val bleDevicesForUi: StateFlow> = - combine(bondedBleDevicesFlow, nodeDb) { bonded, db -> - bonded - .map { entry: DeviceListEntry.Ble -> - val matchingNode = - if (databaseManager.hasDatabaseFor(entry.fullAddress)) { - db.values.find { node -> - val suffix = entry.peripheral.getMeshtasticShortName()?.lowercase(Locale.ROOT) - suffix != null && node.user.id.lowercase(Locale.ROOT).endsWith(suffix) - } - } else { - null - } - entry.copy(node = matchingNode) - } - .sortedBy { it.name } - } - .stateInWhileSubscribed(initialValue = emptyList()) - - private val usbDevicesFlow: StateFlow> = - usbRepository.serialDevices - .map { usb -> usb.map { (_, d) -> DeviceListEntry.Usb(radioInterfaceService, usbManagerLazy.get(), d) } } - .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) - - val mockDevice = DeviceListEntry.Mock("Demo Mode") + discoveredDevicesFlow + .map { it?.bleDevices ?: emptyList() } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList()) /** UI StateFlow for USB devices. */ val usbDevicesForUi: StateFlow> = - combine(usbDevicesFlow, showMockInterface) { usb, showMock -> - val all: List = usb + if (showMock) listOf(mockDevice) else emptyList() - all.map { entry -> - val matchingNode = - if (databaseManager.hasDatabaseFor(entry.fullAddress)) { - nodeDb.value.values.find { node -> - // Hard to match USB to node without connection, but we can try matching by name if it - // follows Meshtastic pattern - val suffix = entry.name.split("_").lastOrNull()?.lowercase(Locale.ROOT) - suffix != null && - suffix.length >= suffixLength && - node.user.id.lowercase(Locale.ROOT).endsWith(suffix) - } - } else { - null - } - entry.copy(node = matchingNode) - } - } - .stateInWhileSubscribed(initialValue = if (showMockInterface.value) listOf(mockDevice) else emptyList()) - - // Flow for recent TCP devices, filtered to exclude any currently discovered devices - private val filteredRecentTcpDevicesFlow: StateFlow> = - combine(recentAddressesDataSource.recentAddresses, processedDiscoveredTcpDevicesFlow) { - recentList, - discoveredDevices, - -> - val discoveredDeviceAddresses = discoveredDevices.map { it.fullAddress }.toSet() - recentList - .filterNot { recentAddress -> discoveredDeviceAddresses.contains(recentAddress.address) } - .map { recentAddress -> DeviceListEntry.Tcp(recentAddress.name, recentAddress.address) } - .sortedBy { it.name } - } - .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) - - private val suffixLength = 4 + discoveredDevicesFlow.map { it?.usbDevices ?: emptyList() }.stateInWhileSubscribed(initialValue = emptyList()) /** UI StateFlow for discovered TCP devices. */ val discoveredTcpDevicesForUi: StateFlow> = - combine(processedDiscoveredTcpDevicesFlow, networkRepository.resolvedList, nodeDb) { devices, resolved, db -> - devices.map { entry -> - val matchingNode = - if (databaseManager.hasDatabaseFor(entry.fullAddress)) { - val resolvedService = resolved.find { "t${it.toAddressString()}" == entry.fullAddress } - val deviceId = resolvedService?.attributes?.get("id")?.let { String(it, Charsets.UTF_8) } - db.values.find { node -> - node.user.id == deviceId || (deviceId != null && node.user.id == "!$deviceId") - } - } else { - null - } - entry.copy(node = matchingNode) - } - } - .stateInWhileSubscribed(initialValue = listOf()) + discoveredDevicesFlow + .map { it?.discoveredTcpDevices ?: emptyList() } + .stateInWhileSubscribed(initialValue = emptyList()) /** UI StateFlow for recently connected TCP devices that are not currently discovered. */ val recentTcpDevicesForUi: StateFlow> = - combine(filteredRecentTcpDevicesFlow, nodeDb) { devices, db -> - devices.map { entry -> - val matchingNode = - if (databaseManager.hasDatabaseFor(entry.fullAddress)) { - // For recent TCP, we don't have the TXT records, but maybe the name contains the ID - val suffix = entry.name.split("_").lastOrNull()?.lowercase(Locale.ROOT) - db.values.find { node -> - suffix != null && - suffix.length >= suffixLength && - node.user.id.lowercase(Locale.ROOT).endsWith(suffix) - } - } else { - null - } - entry.copy(node = matchingNode) - } - } - .stateInWhileSubscribed(initialValue = listOf()) + discoveredDevicesFlow + .map { it?.recentTcpDevices ?: emptyList() } + .stateInWhileSubscribed(initialValue = emptyList()) val selectedAddressFlow: StateFlow = radioInterfaceService.currentDeviceAddressFlow diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/BLEDevices.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/BLEDevices.kt index 8747eca701..ed0a540bb0 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/BLEDevices.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/BLEDevices.kt @@ -37,9 +37,9 @@ import no.nordicsemi.android.common.scanner.view.ScannerView import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.ble.MeshtasticBleConstants.BLE_NAME_PATTERN import org.meshtastic.core.ble.MeshtasticBleConstants.SERVICE_UUID +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.bluetooth_available_devices import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.bluetooth_available_devices /** * Composable that displays a list of Bluetooth Low Energy (BLE) devices and allows scanning. It handles Bluetooth diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectingDeviceInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectingDeviceInfo.kt index f26aa2e05f..86341f2ce3 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectingDeviceInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectingDeviceInfo.kt @@ -35,9 +35,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.connecting -import org.meshtastic.core.strings.disconnect +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.connecting +import org.meshtastic.core.resources.disconnect import org.meshtastic.core.ui.theme.StatusColors.StatusRed @OptIn(ExperimentalMaterial3ExpressiveApi::class) diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsSegmentedBar.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsSegmentedBar.kt index 1b5ed2de5c..705918d9ac 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsSegmentedBar.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/ConnectionsSegmentedBar.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package com.geeksville.mesh.ui.connections.components import androidx.compose.material.icons.Icons @@ -34,10 +33,10 @@ import androidx.compose.ui.tooling.preview.Preview import com.geeksville.mesh.ui.connections.DeviceType import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.bluetooth -import org.meshtastic.core.strings.network -import org.meshtastic.core.strings.serial +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.bluetooth +import org.meshtastic.core.resources.network +import org.meshtastic.core.resources.serial import org.meshtastic.core.ui.theme.AppTheme @Suppress("LambdaParameterEventTrailing") diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt index f95fe05ba4..e4b7115808 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt @@ -48,9 +48,9 @@ import no.nordicsemi.kotlin.ble.client.exception.OperationFailedException import no.nordicsemi.kotlin.ble.client.exception.PeripheralNotConnectedException import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.disconnect -import org.meshtastic.core.strings.firmware_version +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.disconnect +import org.meshtastic.core.resources.firmware_version import org.meshtastic.core.ui.component.MaterialBatteryInfo import org.meshtastic.core.ui.component.NodeChip import org.meshtastic.core.ui.theme.AppTheme diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListItem.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListItem.kt index 92b0e762d7..78e64088cc 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListItem.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/DeviceListItem.kt @@ -56,12 +56,12 @@ import com.geeksville.mesh.model.DeviceListEntry import kotlinx.coroutines.delay import no.nordicsemi.android.common.ui.view.RssiIcon import org.jetbrains.compose.resources.stringResource +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add +import org.meshtastic.core.resources.bluetooth +import org.meshtastic.core.resources.network +import org.meshtastic.core.resources.serial import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add -import org.meshtastic.core.strings.bluetooth -import org.meshtastic.core.strings.network -import org.meshtastic.core.strings.serial import org.meshtastic.core.ui.component.NodeChip private const val RSSI_UPDATE_RATE_MS = 2000L diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/NetworkDevices.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/NetworkDevices.kt index 499c1cd952..ea7edb5ff3 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/NetworkDevices.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/NetworkDevices.kt @@ -53,17 +53,17 @@ import com.geeksville.mesh.ui.connections.ScannerViewModel import com.geeksville.mesh.ui.connections.isValidAddress import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add_network_device +import org.meshtastic.core.resources.address +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.confirm_forget_connection +import org.meshtastic.core.resources.discovered_network_devices +import org.meshtastic.core.resources.forget_connection +import org.meshtastic.core.resources.ip_port +import org.meshtastic.core.resources.no_network_devices +import org.meshtastic.core.resources.recent_network_devices import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add_network_device -import org.meshtastic.core.strings.address -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.confirm_forget_connection -import org.meshtastic.core.strings.discovered_network_devices -import org.meshtastic.core.strings.forget_connection -import org.meshtastic.core.strings.ip_port -import org.meshtastic.core.strings.no_network_devices -import org.meshtastic.core.strings.recent_network_devices import org.meshtastic.core.ui.component.MeshtasticResourceDialog import org.meshtastic.core.ui.theme.AppTheme diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/UsbDevices.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/UsbDevices.kt index abcea444e5..fe7aa4d708 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/UsbDevices.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/UsbDevices.kt @@ -27,9 +27,9 @@ import androidx.compose.ui.unit.dp import com.geeksville.mesh.model.DeviceListEntry import com.geeksville.mesh.ui.connections.ScannerViewModel import org.jetbrains.compose.resources.stringResource +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.no_usb_devices import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.no_usb_devices @Composable fun UsbDevices( diff --git a/app/src/main/java/com/geeksville/mesh/ui/contact/AdaptiveContactsScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/contact/AdaptiveContactsScreen.kt index 281cb9cedc..ee1649613b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/contact/AdaptiveContactsScreen.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/contact/AdaptiveContactsScreen.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package com.geeksville.mesh.ui.contact import androidx.activity.compose.BackHandler @@ -49,8 +48,8 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.navigation.ChannelsRoutes import org.meshtastic.core.navigation.ContactsRoutes import org.meshtastic.core.navigation.NodesRoutes -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.conversations +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.conversations import org.meshtastic.core.ui.component.ScrollToTopEvent import org.meshtastic.core.ui.icon.Conversations import org.meshtastic.core.ui.icon.MeshtasticIcons diff --git a/app/src/main/java/com/geeksville/mesh/ui/contact/ContactItem.kt b/app/src/main/java/com/geeksville/mesh/ui/contact/ContactItem.kt index 7f3735817e..fdb970abe8 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/contact/ContactItem.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/contact/ContactItem.kt @@ -53,10 +53,10 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import com.geeksville.mesh.model.Contact import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.sample_message -import org.meshtastic.core.strings.some_username -import org.meshtastic.core.strings.unknown_username +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.sample_message +import org.meshtastic.core.resources.some_username +import org.meshtastic.core.resources.unknown_username import org.meshtastic.core.ui.component.SecurityIcon import org.meshtastic.core.ui.theme.AppTheme import org.meshtastic.proto.ChannelSet diff --git a/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt b/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt index cdd1703594..6346bc8cef 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt @@ -70,27 +70,27 @@ import org.meshtastic.core.database.entity.ContactSettings import org.meshtastic.core.model.util.TimeConstants import org.meshtastic.core.model.util.formatMuteRemainingTime import org.meshtastic.core.model.util.getChannel -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.are_you_sure -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.channel_invalid -import org.meshtastic.core.strings.close_selection -import org.meshtastic.core.strings.conversations -import org.meshtastic.core.strings.currently -import org.meshtastic.core.strings.delete -import org.meshtastic.core.strings.delete_messages -import org.meshtastic.core.strings.delete_selection -import org.meshtastic.core.strings.mute_1_week -import org.meshtastic.core.strings.mute_8_hours -import org.meshtastic.core.strings.mute_always -import org.meshtastic.core.strings.mute_notifications -import org.meshtastic.core.strings.mute_status_always -import org.meshtastic.core.strings.mute_status_muted_for_days -import org.meshtastic.core.strings.mute_status_muted_for_hours -import org.meshtastic.core.strings.mute_status_unmuted -import org.meshtastic.core.strings.okay -import org.meshtastic.core.strings.select_all -import org.meshtastic.core.strings.unmute +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.are_you_sure +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.channel_invalid +import org.meshtastic.core.resources.close_selection +import org.meshtastic.core.resources.conversations +import org.meshtastic.core.resources.currently +import org.meshtastic.core.resources.delete +import org.meshtastic.core.resources.delete_messages +import org.meshtastic.core.resources.delete_selection +import org.meshtastic.core.resources.mute_1_week +import org.meshtastic.core.resources.mute_8_hours +import org.meshtastic.core.resources.mute_always +import org.meshtastic.core.resources.mute_notifications +import org.meshtastic.core.resources.mute_status_always +import org.meshtastic.core.resources.mute_status_muted_for_days +import org.meshtastic.core.resources.mute_status_muted_for_hours +import org.meshtastic.core.resources.mute_status_unmuted +import org.meshtastic.core.resources.okay +import org.meshtastic.core.resources.select_all +import org.meshtastic.core.resources.unmute import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.core.ui.component.MeshtasticImportFAB diff --git a/app/src/main/java/com/geeksville/mesh/ui/contact/ContactsViewModel.kt b/app/src/main/java/com/geeksville/mesh/ui/contact/ContactsViewModel.kt index 6b219e8956..0ee5ed8d8f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/contact/ContactsViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/contact/ContactsViewModel.kt @@ -39,9 +39,9 @@ import org.meshtastic.core.database.entity.Packet import org.meshtastic.core.model.DataPacket import org.meshtastic.core.model.util.getChannel import org.meshtastic.core.model.util.getShortDate +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.channel_name import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.channel_name import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed import org.meshtastic.proto.ChannelSet import javax.inject.Inject diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt index d9ef36b87c..fdef5b4bdd 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/node/AdaptiveNodeListScreen.kt @@ -48,8 +48,8 @@ import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.navigation.ChannelsRoutes import org.meshtastic.core.navigation.NodesRoutes -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.nodes +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.nodes import org.meshtastic.core.ui.component.ScrollToTopEvent import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.Nodes diff --git a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt b/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt index 454e839283..b3960ecd7c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt @@ -72,21 +72,21 @@ import org.meshtastic.core.model.Channel import org.meshtastic.core.model.util.getChannelUrl import org.meshtastic.core.model.util.qrCode import org.meshtastic.core.navigation.Route +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add +import org.meshtastic.core.resources.apply +import org.meshtastic.core.resources.are_you_sure_change_default +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.cant_change_no_radio +import org.meshtastic.core.resources.edit +import org.meshtastic.core.resources.generate_qr_code +import org.meshtastic.core.resources.modem_preset +import org.meshtastic.core.resources.navigate_into_label +import org.meshtastic.core.resources.replace +import org.meshtastic.core.resources.reset +import org.meshtastic.core.resources.reset_to_defaults +import org.meshtastic.core.resources.share_channels_qr import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add -import org.meshtastic.core.strings.apply -import org.meshtastic.core.strings.are_you_sure_change_default -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.cant_change_no_radio -import org.meshtastic.core.strings.edit -import org.meshtastic.core.strings.generate_qr_code -import org.meshtastic.core.strings.modem_preset -import org.meshtastic.core.strings.navigate_into_label -import org.meshtastic.core.strings.replace -import org.meshtastic.core.strings.reset -import org.meshtastic.core.strings.reset_to_defaults -import org.meshtastic.core.strings.share_channels_qr import org.meshtastic.core.ui.component.AdaptiveTwoPane import org.meshtastic.core.ui.component.ChannelSelection import org.meshtastic.core.ui.component.MainAppBar diff --git a/app/src/main/java/com/geeksville/mesh/ui/sharing/Share.kt b/app/src/main/java/com/geeksville/mesh/ui/sharing/Share.kt index 688b19c35b..0694ae7c26 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/sharing/Share.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/sharing/Share.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package com.geeksville.mesh.ui.sharing import androidx.compose.foundation.layout.Column @@ -43,12 +42,12 @@ import com.geeksville.mesh.model.Contact import com.geeksville.mesh.ui.contact.ContactItem import com.geeksville.mesh.ui.contact.ContactsViewModel import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.sample_message -import org.meshtastic.core.strings.share -import org.meshtastic.core.strings.share_to -import org.meshtastic.core.strings.some_username -import org.meshtastic.core.strings.unknown_username +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.sample_message +import org.meshtastic.core.resources.share +import org.meshtastic.core.resources.share_to +import org.meshtastic.core.resources.some_username +import org.meshtastic.core.resources.unknown_username import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.theme.AppTheme diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml deleted file mode 100644 index a7f9760aee..0000000000 --- a/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - Meshtastic - diff --git a/app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceDrainTest.kt b/app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceDrainTest.kt deleted file mode 100644 index b914cb7f7f..0000000000 --- a/app/src/test/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceDrainTest.kt +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2026 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 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.repository.radio - -import io.mockk.clearMocks -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.runTest -import no.nordicsemi.kotlin.ble.client.android.CentralManager -import no.nordicsemi.kotlin.ble.client.android.mock.mock -import no.nordicsemi.kotlin.ble.client.mock.ConnectionResult -import no.nordicsemi.kotlin.ble.client.mock.PeripheralSpec -import no.nordicsemi.kotlin.ble.client.mock.PeripheralSpecEventHandler -import no.nordicsemi.kotlin.ble.client.mock.Proximity -import no.nordicsemi.kotlin.ble.client.mock.ReadResponse -import no.nordicsemi.kotlin.ble.client.mock.WriteResponse -import no.nordicsemi.kotlin.ble.client.mock.internal.MockRemoteCharacteristic -import no.nordicsemi.kotlin.ble.core.CharacteristicProperty -import no.nordicsemi.kotlin.ble.core.LegacyAdvertisingSetParameters -import no.nordicsemi.kotlin.ble.core.Permission -import no.nordicsemi.kotlin.ble.environment.android.mock.MockAndroidEnvironment -import org.junit.Test -import org.meshtastic.core.ble.MeshtasticBleConstants.FROMNUM_CHARACTERISTIC -import org.meshtastic.core.ble.MeshtasticBleConstants.FROMRADIO_CHARACTERISTIC -import org.meshtastic.core.ble.MeshtasticBleConstants.LOGRADIO_CHARACTERISTIC -import org.meshtastic.core.ble.MeshtasticBleConstants.SERVICE_UUID -import org.meshtastic.core.ble.MeshtasticBleConstants.TORADIO_CHARACTERISTIC -import kotlin.time.Duration.Companion.milliseconds - -@OptIn(ExperimentalCoroutinesApi::class) -class NordicBleInterfaceDrainTest { - - private val testDispatcher = StandardTestDispatcher() - private val address = "00:11:22:33:44:55" - - @Test - fun `drainPacketQueueAndDispatch reads multiple packets until empty`() = runTest(testDispatcher) { - val mockEnvironment = MockAndroidEnvironment.Api31(isBluetoothEnabled = true) - val centralManager = CentralManager.mock(mockEnvironment, scope = backgroundScope) - val service = mockk(relaxed = true) - - var fromRadioHandle: Int = -1 - var fromNumHandle: Int = -1 - val packetsToRead = mutableListOf(byteArrayOf(0x01), byteArrayOf(0x02), byteArrayOf(0x03)) - - val eventHandler = - object : PeripheralSpecEventHandler { - override fun onConnectionRequest(preferredPhy: List) = - ConnectionResult.Accept - - override fun onReadRequest(characteristic: MockRemoteCharacteristic): ReadResponse { - if (characteristic.instanceId == fromRadioHandle) { - return if (packetsToRead.isNotEmpty()) { - val p = packetsToRead.removeAt(0) - println("Mock: Returning packet ${p.contentToString()}") - ReadResponse.Success(p) - } else { - println("Mock: Queue empty, returning empty") - ReadResponse.Success(byteArrayOf()) - } - } - return ReadResponse.Success(byteArrayOf()) - } - - override fun onWriteRequest(characteristic: MockRemoteCharacteristic, value: ByteArray) = - WriteResponse.Success - } - - val peripheralSpec = - PeripheralSpec.simulatePeripheral(identifier = address, proximity = Proximity.IMMEDIATE) { - advertising( - parameters = LegacyAdvertisingSetParameters(connectable = true, interval = 100.milliseconds), - ) { - CompleteLocalName("Meshtastic_Drain") - } - connectable( - name = "Meshtastic_Drain", - isBonded = true, - eventHandler = eventHandler, - cachedServices = { - Service(uuid = SERVICE_UUID) { - Characteristic( - uuid = TORADIO_CHARACTERISTIC, - properties = - setOf( - CharacteristicProperty.WRITE, - CharacteristicProperty.WRITE_WITHOUT_RESPONSE, - ), - permission = Permission.WRITE, - ) - fromNumHandle = - Characteristic( - uuid = FROMNUM_CHARACTERISTIC, - properties = setOf(CharacteristicProperty.NOTIFY), - permission = Permission.READ, - ) - fromRadioHandle = - Characteristic( - uuid = FROMRADIO_CHARACTERISTIC, - properties = setOf(CharacteristicProperty.READ), - permission = Permission.READ, - ) - Characteristic( - uuid = LOGRADIO_CHARACTERISTIC, - properties = setOf(CharacteristicProperty.NOTIFY), - permission = Permission.READ, - ) - } - }, - ) - } - - centralManager.simulatePeripherals(listOf(peripheralSpec)) - - val nordicInterface = - NordicBleInterface( - serviceScope = this, - centralManager = centralManager, - service = service, - address = address, - ) - - // Wait for connection - delay(2000.milliseconds) - verify(timeout = 5000) { service.onConnect() } - clearMocks(service, answers = false, recordedCalls = true) - - // Trigger drain - println("Simulating FromNum notification...") - peripheralSpec.simulateValueUpdate(fromNumHandle, byteArrayOf(0x01)) - - // Wait for all packets to be processed - delay(2000.milliseconds) - - // Verify handleFromRadio was called 3 times - verify(timeout = 2000) { service.handleFromRadio(p = byteArrayOf(0x01)) } - verify(timeout = 2000) { service.handleFromRadio(p = byteArrayOf(0x02)) } - verify(timeout = 2000) { service.handleFromRadio(p = byteArrayOf(0x03)) } - - assert(packetsToRead.isEmpty()) { "All packets should have been read" } - - nordicInterface.close() - } -} 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 be41ad5d48..e3bb46435d 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 @@ -33,7 +33,7 @@ fun Project.configureDokka() { suppress.set(true) } perPackageOption { - matchingRegex.set("org.meshtastic.core.strings.*") + matchingRegex.set("org.meshtastic.core.resources.*") suppress.set(true) } 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 23a0e95429..b4c4deedde 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 @@ -57,7 +57,7 @@ fun Project.configureKover() { // Suppress generated code packages("hilt_aggregated_deps") - packages("org.meshtastic.core.strings") + packages("org.meshtastic.core.resources") } } } diff --git a/core/barcode/README.md b/core/barcode/README.md index 4bb25c4c19..053e5655e4 100644 --- a/core/barcode/README.md +++ b/core/barcode/README.md @@ -34,7 +34,7 @@ BarcodeScanner( ```mermaid graph TB :core:barcode[barcode]:::android-library - :core:barcode -.-> :core:strings + :core:barcode -.-> :core:resources classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; diff --git a/core/barcode/build.gradle.kts b/core/barcode/build.gradle.kts index 46ece16e7a..f5978105ca 100644 --- a/core/barcode/build.gradle.kts +++ b/core/barcode/build.gradle.kts @@ -29,7 +29,7 @@ configure { } dependencies { - implementation(project(":core:strings")) + implementation(project(":core:resources")) implementation(libs.androidx.activity.compose) implementation(libs.androidx.compose.material3) diff --git a/core/barcode/src/fdroid/kotlin/org/meshtastic/core/barcode/BarcodeScannerProvider.kt b/core/barcode/src/fdroid/kotlin/org/meshtastic/core/barcode/BarcodeScannerProvider.kt index 6761e997a1..5d12b6f13d 100644 --- a/core/barcode/src/fdroid/kotlin/org/meshtastic/core/barcode/BarcodeScannerProvider.kt +++ b/core/barcode/src/fdroid/kotlin/org/meshtastic/core/barcode/BarcodeScannerProvider.kt @@ -64,8 +64,8 @@ import com.google.zxing.MultiFormatReader import com.google.zxing.PlanarYUVLuminanceSource import com.google.zxing.common.HybridBinarizer import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.close +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.close import java.nio.ByteBuffer import java.util.concurrent.Executors diff --git a/core/barcode/src/google/kotlin/org/meshtastic/core/barcode/BarcodeScannerProvider.kt b/core/barcode/src/google/kotlin/org/meshtastic/core/barcode/BarcodeScannerProvider.kt index beb0484bbd..c9ff070bd1 100644 --- a/core/barcode/src/google/kotlin/org/meshtastic/core/barcode/BarcodeScannerProvider.kt +++ b/core/barcode/src/google/kotlin/org/meshtastic/core/barcode/BarcodeScannerProvider.kt @@ -64,8 +64,8 @@ import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.close +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.close import java.util.concurrent.Executors @Composable diff --git a/core/ble/src/main/kotlin/org/meshtastic/core/ble/BluetoothRepository.kt b/core/ble/src/main/kotlin/org/meshtastic/core/ble/BluetoothRepository.kt index a22e0729ef..e58e804b68 100644 --- a/core/ble/src/main/kotlin/org/meshtastic/core/ble/BluetoothRepository.kt +++ b/core/ble/src/main/kotlin/org/meshtastic/core/ble/BluetoothRepository.kt @@ -53,7 +53,8 @@ constructor( hasPermissions = true, ), ) - val state: StateFlow = _state.asStateFlow() + val state: StateFlow + get() = _state.asStateFlow() init { processLifecycle.coroutineScope.launch(dispatchers.default) { diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt index b7c1016ead..67ccdc091c 100644 --- a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt +++ b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt @@ -67,9 +67,11 @@ constructor( // 1. Emit cached data first, regardless of staleness. // This gives the UI something to show immediately. val cachedRelease = localDataSource.getLatestRelease(releaseType) - cachedRelease?.let { - Logger.d { "Emitting cached firmware for $releaseType (isStale=${it.isStale()})" } - emit(it.asExternalModel()) + if (cachedRelease != null) { + Logger.d { "Emitting cached firmware for $releaseType (isStale=${cachedRelease.isStale()})" } + emit(cachedRelease.asExternalModel()) + } else { + emit(null) } // 2. If the cache was fresh and we are not forcing a refresh, we're done. diff --git a/core/database/README.md b/core/database/README.md index 3447954ec7..dd56166f1f 100644 --- a/core/database/README.md +++ b/core/database/README.md @@ -30,7 +30,7 @@ graph TB :core:database -.-> :core:di :core:database -.-> :core:model :core:database -.-> :core:proto - :core:database -.-> :core:strings + :core:database -.-> :core:resources classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index e1f9521b51..5c5ed5dcb7 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { implementation(projects.core.di) implementation(projects.core.model) implementation(projects.core.proto) - implementation(projects.core.strings) + implementation(projects.core.resources) implementation(libs.androidx.room.paging) implementation(libs.kotlinx.serialization.json) diff --git a/core/database/src/main/kotlin/org/meshtastic/core/database/model/Message.kt b/core/database/src/main/kotlin/org/meshtastic/core/database/model/Message.kt index 459e0f815f..3205c05298 100644 --- a/core/database/src/main/kotlin/org/meshtastic/core/database/model/Message.kt +++ b/core/database/src/main/kotlin/org/meshtastic/core/database/model/Message.kt @@ -19,33 +19,33 @@ package org.meshtastic.core.database.model import org.jetbrains.compose.resources.StringResource import org.meshtastic.core.database.entity.Reaction import org.meshtastic.core.model.MessageStatus -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.delivery_confirmed -import org.meshtastic.core.strings.error -import org.meshtastic.core.strings.message_delivery_status -import org.meshtastic.core.strings.message_status_enroute -import org.meshtastic.core.strings.message_status_queued -import org.meshtastic.core.strings.message_status_sfpp_confirmed -import org.meshtastic.core.strings.message_status_sfpp_routing -import org.meshtastic.core.strings.routing_error_admin_bad_session_key -import org.meshtastic.core.strings.routing_error_admin_public_key_unauthorized -import org.meshtastic.core.strings.routing_error_bad_request -import org.meshtastic.core.strings.routing_error_duty_cycle_limit -import org.meshtastic.core.strings.routing_error_got_nak -import org.meshtastic.core.strings.routing_error_max_retransmit -import org.meshtastic.core.strings.routing_error_no_channel -import org.meshtastic.core.strings.routing_error_no_interface -import org.meshtastic.core.strings.routing_error_no_response -import org.meshtastic.core.strings.routing_error_no_route -import org.meshtastic.core.strings.routing_error_none -import org.meshtastic.core.strings.routing_error_not_authorized -import org.meshtastic.core.strings.routing_error_pki_failed -import org.meshtastic.core.strings.routing_error_pki_send_fail_public_key -import org.meshtastic.core.strings.routing_error_pki_unknown_pubkey -import org.meshtastic.core.strings.routing_error_rate_limit_exceeded -import org.meshtastic.core.strings.routing_error_timeout -import org.meshtastic.core.strings.routing_error_too_large -import org.meshtastic.core.strings.unrecognized +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.delivery_confirmed +import org.meshtastic.core.resources.error +import org.meshtastic.core.resources.message_delivery_status +import org.meshtastic.core.resources.message_status_enroute +import org.meshtastic.core.resources.message_status_queued +import org.meshtastic.core.resources.message_status_sfpp_confirmed +import org.meshtastic.core.resources.message_status_sfpp_routing +import org.meshtastic.core.resources.routing_error_admin_bad_session_key +import org.meshtastic.core.resources.routing_error_admin_public_key_unauthorized +import org.meshtastic.core.resources.routing_error_bad_request +import org.meshtastic.core.resources.routing_error_duty_cycle_limit +import org.meshtastic.core.resources.routing_error_got_nak +import org.meshtastic.core.resources.routing_error_max_retransmit +import org.meshtastic.core.resources.routing_error_no_channel +import org.meshtastic.core.resources.routing_error_no_interface +import org.meshtastic.core.resources.routing_error_no_response +import org.meshtastic.core.resources.routing_error_no_route +import org.meshtastic.core.resources.routing_error_none +import org.meshtastic.core.resources.routing_error_not_authorized +import org.meshtastic.core.resources.routing_error_pki_failed +import org.meshtastic.core.resources.routing_error_pki_send_fail_public_key +import org.meshtastic.core.resources.routing_error_pki_unknown_pubkey +import org.meshtastic.core.resources.routing_error_rate_limit_exceeded +import org.meshtastic.core.resources.routing_error_timeout +import org.meshtastic.core.resources.routing_error_too_large +import org.meshtastic.core.resources.unrecognized import org.meshtastic.proto.MeshPacket import org.meshtastic.proto.Routing diff --git a/core/database/src/main/kotlin/org/meshtastic/core/database/model/NodeSortOption.kt b/core/database/src/main/kotlin/org/meshtastic/core/database/model/NodeSortOption.kt index d186341efa..c54a66b637 100644 --- a/core/database/src/main/kotlin/org/meshtastic/core/database/model/NodeSortOption.kt +++ b/core/database/src/main/kotlin/org/meshtastic/core/database/model/NodeSortOption.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,18 +14,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.database.model import org.jetbrains.compose.resources.StringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.node_sort_alpha -import org.meshtastic.core.strings.node_sort_channel -import org.meshtastic.core.strings.node_sort_distance -import org.meshtastic.core.strings.node_sort_hops_away -import org.meshtastic.core.strings.node_sort_last_heard -import org.meshtastic.core.strings.node_sort_via_favorite -import org.meshtastic.core.strings.node_sort_via_mqtt +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.node_sort_alpha +import org.meshtastic.core.resources.node_sort_channel +import org.meshtastic.core.resources.node_sort_distance +import org.meshtastic.core.resources.node_sort_hops_away +import org.meshtastic.core.resources.node_sort_last_heard +import org.meshtastic.core.resources.node_sort_via_favorite +import org.meshtastic.core.resources.node_sort_via_mqtt enum class NodeSortOption(val sqlValue: String, val stringRes: StringResource) { LAST_HEARD("last_heard", Res.string.node_sort_last_heard), diff --git a/core/strings/README.md b/core/resources/README.md similarity index 78% rename from core/strings/README.md rename to core/resources/README.md index a0a0ac015c..8c24dda541 100644 --- a/core/strings/README.md +++ b/core/resources/README.md @@ -1,7 +1,7 @@ -# `:core:strings` +# `:core:resources` ## Overview -The `:core:strings` module is the centralized source for all UI strings and localizable resources. It uses the **Compose Multiplatform Resource** library to provide a type-safe way to access strings. +The `:core:resources` module is the centralized source for all UI strings and localizable resources. It uses the **Compose Multiplatform Resource** library to provide a type-safe way to access strings. ## Key Features @@ -13,8 +13,8 @@ The library provides a standard way to access strings in Jetpack Compose. ```kotlin import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.your_string_key +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.your_string_key Text(text = stringResource(Res.string.your_string_key)) ``` @@ -24,7 +24,7 @@ Text(text = stringResource(Res.string.your_string_key)) ```mermaid graph TB - :core:strings[strings]:::kmp-library + :core:resources[strings]:::kmp-library classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; diff --git a/core/strings/build.gradle.kts b/core/resources/build.gradle.kts similarity index 94% rename from core/strings/build.gradle.kts rename to core/resources/build.gradle.kts index 4f7c8998a6..347c9d69a0 100644 --- a/core/strings/build.gradle.kts +++ b/core/resources/build.gradle.kts @@ -29,5 +29,5 @@ kotlin { compose.resources { publicResClass = true - packageOfResClass = "org.meshtastic.core.strings" + packageOfResClass = "org.meshtastic.core.resources" } diff --git a/core/strings/src/androidMain/kotlin/org/meshtastic/core/strings/ContextExt.kt b/core/resources/src/androidMain/kotlin/org/meshtastic/core/resources/ContextExt.kt similarity index 97% rename from core/strings/src/androidMain/kotlin/org/meshtastic/core/strings/ContextExt.kt rename to core/resources/src/androidMain/kotlin/org/meshtastic/core/resources/ContextExt.kt index 42e68fd896..5ebc912504 100644 --- a/core/strings/src/androidMain/kotlin/org/meshtastic/core/strings/ContextExt.kt +++ b/core/resources/src/androidMain/kotlin/org/meshtastic/core/resources/ContextExt.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.core.strings +package org.meshtastic.core.resources import kotlinx.coroutines.runBlocking import org.jetbrains.compose.resources.StringResource diff --git a/core/ui/src/main/res/drawable/ic_antenna_24.xml b/core/resources/src/commonMain/composeResources/drawable/ic_antenna.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_antenna_24.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_antenna.xml diff --git a/core/ui/src/main/res/drawable/ic_battery_alert.xml b/core/resources/src/commonMain/composeResources/drawable/ic_battery_alert.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_battery_alert.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_battery_alert.xml diff --git a/core/ui/src/main/res/drawable/ic_battery_high.xml b/core/resources/src/commonMain/composeResources/drawable/ic_battery_high.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_battery_high.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_battery_high.xml diff --git a/core/ui/src/main/res/drawable/ic_battery_low.xml b/core/resources/src/commonMain/composeResources/drawable/ic_battery_low.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_battery_low.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_battery_low.xml diff --git a/core/ui/src/main/res/drawable/ic_battery_medium.xml b/core/resources/src/commonMain/composeResources/drawable/ic_battery_medium.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_battery_medium.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_battery_medium.xml diff --git a/core/ui/src/main/res/drawable/ic_battery_outline.xml b/core/resources/src/commonMain/composeResources/drawable/ic_battery_outline.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_battery_outline.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_battery_outline.xml diff --git a/core/ui/src/main/res/drawable/ic_battery_unknown.xml b/core/resources/src/commonMain/composeResources/drawable/ic_battery_unknown.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_battery_unknown.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_battery_unknown.xml diff --git a/core/ui/src/main/res/drawable/counter_0_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_counter_0.xml similarity index 100% rename from core/ui/src/main/res/drawable/counter_0_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_counter_0.xml diff --git a/core/ui/src/main/res/drawable/counter_1_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_counter_1.xml similarity index 100% rename from core/ui/src/main/res/drawable/counter_1_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_counter_1.xml diff --git a/core/ui/src/main/res/drawable/counter_2_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_counter_2.xml similarity index 100% rename from core/ui/src/main/res/drawable/counter_2_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_counter_2.xml diff --git a/core/ui/src/main/res/drawable/counter_3_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_counter_3.xml similarity index 100% rename from core/ui/src/main/res/drawable/counter_3_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_counter_3.xml diff --git a/core/ui/src/main/res/drawable/counter_4_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_counter_4.xml similarity index 100% rename from core/ui/src/main/res/drawable/counter_4_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_counter_4.xml diff --git a/core/ui/src/main/res/drawable/counter_5_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_counter_5.xml similarity index 100% rename from core/ui/src/main/res/drawable/counter_5_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_counter_5.xml diff --git a/core/ui/src/main/res/drawable/counter_6_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_counter_6.xml similarity index 100% rename from core/ui/src/main/res/drawable/counter_6_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_counter_6.xml diff --git a/core/ui/src/main/res/drawable/counter_7_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_counter_7.xml similarity index 100% rename from core/ui/src/main/res/drawable/counter_7_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_counter_7.xml diff --git a/core/ui/src/main/res/drawable/counter_8_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_counter_8.xml similarity index 100% rename from core/ui/src/main/res/drawable/counter_8_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_counter_8.xml diff --git a/feature/node/src/main/res/drawable/ic_outlined_dew_point_24.xml b/core/resources/src/commonMain/composeResources/drawable/ic_dew_point.xml similarity index 100% rename from feature/node/src/main/res/drawable/ic_outlined_dew_point_24.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_dew_point.xml diff --git a/core/ui/src/main/res/drawable/ic_baseline_location_on_24.xml b/core/resources/src/commonMain/composeResources/drawable/ic_location_on.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_baseline_location_on_24.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_location_on.xml diff --git a/core/ui/src/main/res/drawable/ic_lock_open_right_24.xml b/core/resources/src/commonMain/composeResources/drawable/ic_lock_open_right.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_lock_open_right_24.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_lock_open_right.xml diff --git a/core/ui/src/main/res/drawable/ic_map_location_dot_24.xml b/core/resources/src/commonMain/composeResources/drawable/ic_map_location_dot.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_map_location_dot_24.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_map_location_dot.xml diff --git a/core/ui/src/main/res/drawable/ic_map_navigation_24.xml b/core/resources/src/commonMain/composeResources/drawable/ic_map_navigation.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_map_navigation_24.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_map_navigation.xml diff --git a/core/ui/src/main/res/drawable/ic_meshtastic.xml b/core/resources/src/commonMain/composeResources/drawable/ic_meshtastic.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_meshtastic.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_meshtastic.xml diff --git a/core/ui/src/main/res/drawable/mountain_flag_24px.xml b/core/resources/src/commonMain/composeResources/drawable/ic_mountain_flag.xml similarity index 100% rename from core/ui/src/main/res/drawable/mountain_flag_24px.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_mountain_flag.xml diff --git a/core/ui/src/main/res/drawable/ic_power_plug_24.xml b/core/resources/src/commonMain/composeResources/drawable/ic_power_plug.xml similarity index 100% rename from core/ui/src/main/res/drawable/ic_power_plug_24.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_power_plug.xml diff --git a/feature/node/src/main/res/drawable/ic_filled_radioactive_24.xml b/core/resources/src/commonMain/composeResources/drawable/ic_radioactive.xml similarity index 100% rename from feature/node/src/main/res/drawable/ic_filled_radioactive_24.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_radioactive.xml diff --git a/feature/node/src/main/res/drawable/soil_moisture.xml b/core/resources/src/commonMain/composeResources/drawable/ic_soil_moisture.xml similarity index 100% rename from feature/node/src/main/res/drawable/soil_moisture.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_soil_moisture.xml diff --git a/feature/node/src/main/res/drawable/soil_temperature.xml b/core/resources/src/commonMain/composeResources/drawable/ic_soil_temperature.xml similarity index 100% rename from feature/node/src/main/res/drawable/soil_temperature.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_soil_temperature.xml diff --git a/feature/node/src/main/res/drawable/unverified.xml b/core/resources/src/commonMain/composeResources/drawable/ic_unverified.xml similarity index 100% rename from feature/node/src/main/res/drawable/unverified.xml rename to core/resources/src/commonMain/composeResources/drawable/ic_unverified.xml diff --git a/core/ui/src/main/res/drawable/chirpy.xml b/core/resources/src/commonMain/composeResources/drawable/img_chirpy.xml similarity index 100% rename from core/ui/src/main/res/drawable/chirpy.xml rename to core/resources/src/commonMain/composeResources/drawable/img_chirpy.xml diff --git a/feature/node/src/main/res/drawable/hw_unknown.xml b/core/resources/src/commonMain/composeResources/drawable/img_hw_unknown.xml similarity index 100% rename from feature/node/src/main/res/drawable/hw_unknown.xml rename to core/resources/src/commonMain/composeResources/drawable/img_hw_unknown.xml diff --git a/core/ui/src/main/res/drawable-nodpi/qrcode.png b/core/resources/src/commonMain/composeResources/drawable/img_qrcode.png similarity index 100% rename from core/ui/src/main/res/drawable-nodpi/qrcode.png rename to core/resources/src/commonMain/composeResources/drawable/img_qrcode.png diff --git a/core/strings/src/commonMain/composeResources/values-ar/strings.xml b/core/resources/src/commonMain/composeResources/values-ar/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-ar/strings.xml rename to core/resources/src/commonMain/composeResources/values-ar/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-be/strings.xml b/core/resources/src/commonMain/composeResources/values-be/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-be/strings.xml rename to core/resources/src/commonMain/composeResources/values-be/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-bg/strings.xml b/core/resources/src/commonMain/composeResources/values-bg/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-bg/strings.xml rename to core/resources/src/commonMain/composeResources/values-bg/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-ca/strings.xml b/core/resources/src/commonMain/composeResources/values-ca/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-ca/strings.xml rename to core/resources/src/commonMain/composeResources/values-ca/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-cs/strings.xml b/core/resources/src/commonMain/composeResources/values-cs/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-cs/strings.xml rename to core/resources/src/commonMain/composeResources/values-cs/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-de/strings.xml b/core/resources/src/commonMain/composeResources/values-de/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-de/strings.xml rename to core/resources/src/commonMain/composeResources/values-de/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-el/strings.xml b/core/resources/src/commonMain/composeResources/values-el/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-el/strings.xml rename to core/resources/src/commonMain/composeResources/values-el/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-es/strings.xml b/core/resources/src/commonMain/composeResources/values-es/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-es/strings.xml rename to core/resources/src/commonMain/composeResources/values-es/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-et/strings.xml b/core/resources/src/commonMain/composeResources/values-et/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-et/strings.xml rename to core/resources/src/commonMain/composeResources/values-et/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-fi/strings.xml b/core/resources/src/commonMain/composeResources/values-fi/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-fi/strings.xml rename to core/resources/src/commonMain/composeResources/values-fi/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-fr/strings.xml b/core/resources/src/commonMain/composeResources/values-fr/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-fr/strings.xml rename to core/resources/src/commonMain/composeResources/values-fr/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-ga/strings.xml b/core/resources/src/commonMain/composeResources/values-ga/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-ga/strings.xml rename to core/resources/src/commonMain/composeResources/values-ga/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-gl/strings.xml b/core/resources/src/commonMain/composeResources/values-gl/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-gl/strings.xml rename to core/resources/src/commonMain/composeResources/values-gl/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-he/strings.xml b/core/resources/src/commonMain/composeResources/values-he/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-he/strings.xml rename to core/resources/src/commonMain/composeResources/values-he/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-hr/strings.xml b/core/resources/src/commonMain/composeResources/values-hr/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-hr/strings.xml rename to core/resources/src/commonMain/composeResources/values-hr/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-ht/strings.xml b/core/resources/src/commonMain/composeResources/values-ht/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-ht/strings.xml rename to core/resources/src/commonMain/composeResources/values-ht/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-hu/strings.xml b/core/resources/src/commonMain/composeResources/values-hu/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-hu/strings.xml rename to core/resources/src/commonMain/composeResources/values-hu/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-is/strings.xml b/core/resources/src/commonMain/composeResources/values-is/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-is/strings.xml rename to core/resources/src/commonMain/composeResources/values-is/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-it/strings.xml b/core/resources/src/commonMain/composeResources/values-it/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-it/strings.xml rename to core/resources/src/commonMain/composeResources/values-it/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-ja/strings.xml b/core/resources/src/commonMain/composeResources/values-ja/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-ja/strings.xml rename to core/resources/src/commonMain/composeResources/values-ja/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-ko/strings.xml b/core/resources/src/commonMain/composeResources/values-ko/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-ko/strings.xml rename to core/resources/src/commonMain/composeResources/values-ko/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-lt/strings.xml b/core/resources/src/commonMain/composeResources/values-lt/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-lt/strings.xml rename to core/resources/src/commonMain/composeResources/values-lt/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-nl/strings.xml b/core/resources/src/commonMain/composeResources/values-nl/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-nl/strings.xml rename to core/resources/src/commonMain/composeResources/values-nl/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-no/strings.xml b/core/resources/src/commonMain/composeResources/values-no/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-no/strings.xml rename to core/resources/src/commonMain/composeResources/values-no/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-pl/strings.xml b/core/resources/src/commonMain/composeResources/values-pl/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-pl/strings.xml rename to core/resources/src/commonMain/composeResources/values-pl/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-pt-rBR/strings.xml b/core/resources/src/commonMain/composeResources/values-pt-rBR/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-pt-rBR/strings.xml rename to core/resources/src/commonMain/composeResources/values-pt-rBR/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-pt/strings.xml b/core/resources/src/commonMain/composeResources/values-pt/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-pt/strings.xml rename to core/resources/src/commonMain/composeResources/values-pt/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-ro/strings.xml b/core/resources/src/commonMain/composeResources/values-ro/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-ro/strings.xml rename to core/resources/src/commonMain/composeResources/values-ro/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-ru/strings.xml b/core/resources/src/commonMain/composeResources/values-ru/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-ru/strings.xml rename to core/resources/src/commonMain/composeResources/values-ru/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-sk/strings.xml b/core/resources/src/commonMain/composeResources/values-sk/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-sk/strings.xml rename to core/resources/src/commonMain/composeResources/values-sk/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-sl/strings.xml b/core/resources/src/commonMain/composeResources/values-sl/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-sl/strings.xml rename to core/resources/src/commonMain/composeResources/values-sl/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-sq/strings.xml b/core/resources/src/commonMain/composeResources/values-sq/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-sq/strings.xml rename to core/resources/src/commonMain/composeResources/values-sq/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-sr/strings.xml b/core/resources/src/commonMain/composeResources/values-sr/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-sr/strings.xml rename to core/resources/src/commonMain/composeResources/values-sr/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-srp/strings.xml b/core/resources/src/commonMain/composeResources/values-srp/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-srp/strings.xml rename to core/resources/src/commonMain/composeResources/values-srp/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-sv/strings.xml b/core/resources/src/commonMain/composeResources/values-sv/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-sv/strings.xml rename to core/resources/src/commonMain/composeResources/values-sv/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-tr/strings.xml b/core/resources/src/commonMain/composeResources/values-tr/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-tr/strings.xml rename to core/resources/src/commonMain/composeResources/values-tr/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-uk/strings.xml b/core/resources/src/commonMain/composeResources/values-uk/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-uk/strings.xml rename to core/resources/src/commonMain/composeResources/values-uk/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-zh-rCN/strings.xml b/core/resources/src/commonMain/composeResources/values-zh-rCN/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-zh-rCN/strings.xml rename to core/resources/src/commonMain/composeResources/values-zh-rCN/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values-zh-rTW/strings.xml b/core/resources/src/commonMain/composeResources/values-zh-rTW/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values-zh-rTW/strings.xml rename to core/resources/src/commonMain/composeResources/values-zh-rTW/strings.xml diff --git a/core/strings/src/commonMain/composeResources/values/strings.xml b/core/resources/src/commonMain/composeResources/values/strings.xml similarity index 100% rename from core/strings/src/commonMain/composeResources/values/strings.xml rename to core/resources/src/commonMain/composeResources/values/strings.xml diff --git a/core/resources/src/commonMain/kotlin/org/meshtastic/core/resources/UiText.kt b/core/resources/src/commonMain/kotlin/org/meshtastic/core/resources/UiText.kt new file mode 100644 index 0000000000..843ab7883c --- /dev/null +++ b/core/resources/src/commonMain/kotlin/org/meshtastic/core/resources/UiText.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025-2026 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 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 org.meshtastic.core.resources + +import androidx.compose.runtime.Composable +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.getString +import org.jetbrains.compose.resources.stringResource + +/** + * A wrapper class for UI text that can be either a dynamic string or a localized string resource. This allows passing + * text from domain/data layers to the UI without resolving strings early. + */ +sealed class UiText { + data class DynamicString(val value: String) : UiText() + + class Resource(val res: StringResource, vararg val args: Any) : UiText() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Resource + + if (res != other.res) return false + if (!args.contentEquals(other.args)) return false + + return true + } + + override fun hashCode(): Int { + var result = res.hashCode() + result = 31 * result + args.contentHashCode() + return result + } + } + + @Composable + fun asString(): String = when (this) { + is DynamicString -> value + is Resource -> { + val resolvedArgs = + args.map { arg -> + when (arg) { + is StringResource -> stringResource(arg) + is UiText -> arg.asString() + else -> arg + } + } + @Suppress("SpreadOperator") + stringResource(res, *resolvedArgs.toTypedArray()) + } + } + + /** Resolves the string in a suspend context. Useful for non-composable code like snackbars. */ + suspend fun resolve(): String = when (this) { + is DynamicString -> value + is Resource -> { + val resolvedArgs = + args.map { arg -> + when (arg) { + is StringResource -> getString(arg) + is UiText -> arg.resolve() + else -> arg + } + } + @Suppress("SpreadOperator") + getString(res, *resolvedArgs.toTypedArray()) + } + } +} diff --git a/core/ui/README.md b/core/ui/README.md index 3be5c4e192..61bad4bda3 100644 --- a/core/ui/README.md +++ b/core/ui/README.md @@ -33,8 +33,8 @@ Most components are designed to be used with the **Compose Multiplatform Resourc ```kotlin import org.meshtastic.core.ui.component.MeshtasticResourceDialog -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.ok +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.ok MeshtasticResourceDialog( title = Res.string.your_title, @@ -59,7 +59,7 @@ graph TB :core:ui -.-> :core:prefs :core:ui -.-> :core:proto :core:ui -.-> :core:service - :core:ui -.-> :core:strings + :core:ui -.-> :core:resources classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 9441d291ab..1dfdc27b8f 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { implementation(projects.core.prefs) implementation(projects.core.proto) implementation(projects.core.service) - implementation(projects.core.strings) + implementation(projects.core.resources) implementation(libs.accompanist.permissions) implementation(libs.androidx.activity.compose) diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/AlertDialogs.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/AlertDialogs.kt index 7e983126cb..a06a9e6079 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/AlertDialogs.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/AlertDialogs.kt @@ -42,9 +42,9 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.okay +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.okay /** * A comprehensive and flexible dialog component for the Meshtastic application. diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/BitwisePreference.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/BitwisePreference.kt index 547340a6ea..558af087bc 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/BitwisePreference.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/BitwisePreference.kt @@ -37,9 +37,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.clear -import org.meshtastic.core.strings.close +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.clear +import org.meshtastic.core.resources.close @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ChannelInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ChannelInfo.kt index 3e01ec5580..150c88d51c 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ChannelInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ChannelInfo.kt @@ -22,8 +22,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewLightDark import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.channel +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.channel import org.meshtastic.core.ui.icon.Channel import org.meshtastic.core.ui.icon.Counter0 import org.meshtastic.core.ui.icon.Counter1 diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ContactSharing.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ContactSharing.kt index d4ca9f16d6..98036bab96 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ContactSharing.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ContactSharing.kt @@ -30,8 +30,8 @@ import com.google.zxing.common.BitMatrix import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.util.getSharedContactUrl -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.share_contact +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.share_contact import org.meshtastic.proto.SharedContact /** diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/CopyIconButton.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/CopyIconButton.kt index b8168904d7..c6af5cd73a 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/CopyIconButton.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/CopyIconButton.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.ui.component import android.content.ClipData @@ -29,8 +28,8 @@ import androidx.compose.ui.platform.ClipEntry import androidx.compose.ui.platform.LocalClipboard import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.copy +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.copy @Composable fun CopyIconButton( diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/DistanceInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/DistanceInfo.kt index 3ced8c7cc5..992f98c2c3 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/DistanceInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/DistanceInfo.kt @@ -22,8 +22,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewLightDark import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.distance +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.distance import org.meshtastic.core.ui.icon.Distance import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.theme.AppTheme diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditBase64Preference.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditBase64Preference.kt index f5e6391e0c..26d2277a6f 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditBase64Preference.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditBase64Preference.kt @@ -47,9 +47,9 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.Channel import org.meshtastic.core.model.util.base64ToByteString import org.meshtastic.core.model.util.encodeToString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.error -import org.meshtastic.core.strings.reset +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.error +import org.meshtastic.core.resources.reset @Suppress("LongMethod", "CyclomaticComplexMethod", "MagicNumber") @Composable diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditListPreference.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditListPreference.kt index feda280178..29f6baca06 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditListPreference.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditListPreference.kt @@ -39,13 +39,13 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add -import org.meshtastic.core.strings.delete -import org.meshtastic.core.strings.gpio_pin -import org.meshtastic.core.strings.ignore_incoming -import org.meshtastic.core.strings.name -import org.meshtastic.core.strings.type +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add +import org.meshtastic.core.resources.delete +import org.meshtastic.core.resources.gpio_pin +import org.meshtastic.core.resources.ignore_incoming +import org.meshtastic.core.resources.name +import org.meshtastic.core.resources.type import org.meshtastic.proto.RemoteHardwarePin import org.meshtastic.proto.RemoteHardwarePinType diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditPasswordPreference.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditPasswordPreference.kt index da78d7f516..e8b71ee018 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditPasswordPreference.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditPasswordPreference.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.ui.component import androidx.compose.foundation.text.KeyboardActions @@ -35,9 +34,9 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.hide_password -import org.meshtastic.core.strings.show_password +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.hide_password +import org.meshtastic.core.resources.show_password @Composable fun EditPasswordPreference( diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditTextPreference.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditTextPreference.kt index 3e366d062d..5752287aec 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditTextPreference.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/EditTextPreference.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.ui.component import androidx.compose.foundation.layout.Box @@ -44,8 +43,8 @@ import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.error +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.error @Composable fun SignedIntegerEditTextPreference( diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ElevationInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ElevationInfo.kt index 176cd6c7dd..9bf3e702cc 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ElevationInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ElevationInfo.kt @@ -24,9 +24,9 @@ import androidx.compose.ui.tooling.preview.Preview import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.util.metersIn import org.meshtastic.core.model.util.toString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.altitude -import org.meshtastic.core.strings.elevation_suffix +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.altitude +import org.meshtastic.core.resources.elevation_suffix import org.meshtastic.core.ui.icon.Elevation import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.proto.Config.DisplayConfig.DisplayUnits diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/HopsInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/HopsInfo.kt index 270ef1255e..42b569094f 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/HopsInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/HopsInfo.kt @@ -22,8 +22,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewLightDark import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.hops_away +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.hops_away import org.meshtastic.core.ui.icon.Hops import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.theme.AppTheme diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ImportFab.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ImportFab.kt index 609e649caf..e4852111c7 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ImportFab.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ImportFab.kt @@ -41,22 +41,22 @@ import androidx.core.net.toUri import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.barcode.rememberBarcodeScanner import org.meshtastic.core.nfc.NfcScannerEffect -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.import_label -import org.meshtastic.core.strings.input_channel_url -import org.meshtastic.core.strings.input_shared_contact_url -import org.meshtastic.core.strings.nfc_disabled -import org.meshtastic.core.strings.okay -import org.meshtastic.core.strings.open_settings -import org.meshtastic.core.strings.scan_channels_nfc -import org.meshtastic.core.strings.scan_channels_qr -import org.meshtastic.core.strings.scan_nfc -import org.meshtastic.core.strings.scan_nfc_text -import org.meshtastic.core.strings.scan_shared_contact_nfc -import org.meshtastic.core.strings.scan_shared_contact_qr -import org.meshtastic.core.strings.share_channels_qr -import org.meshtastic.core.strings.url +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.import_label +import org.meshtastic.core.resources.input_channel_url +import org.meshtastic.core.resources.input_shared_contact_url +import org.meshtastic.core.resources.nfc_disabled +import org.meshtastic.core.resources.okay +import org.meshtastic.core.resources.open_settings +import org.meshtastic.core.resources.scan_channels_nfc +import org.meshtastic.core.resources.scan_channels_qr +import org.meshtastic.core.resources.scan_nfc +import org.meshtastic.core.resources.scan_nfc_text +import org.meshtastic.core.resources.scan_shared_contact_nfc +import org.meshtastic.core.resources.scan_shared_contact_qr +import org.meshtastic.core.resources.share_channels_qr +import org.meshtastic.core.resources.url import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.QrCode2 import org.meshtastic.core.ui.theme.AppTheme diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/IndoorAirQuality.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/IndoorAirQuality.kt index 630f348a00..deb6cd03ea 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/IndoorAirQuality.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/IndoorAirQuality.kt @@ -50,10 +50,10 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.air_quality_icon -import org.meshtastic.core.strings.close -import org.meshtastic.core.strings.indoor_air_quality_iaq +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.air_quality_icon +import org.meshtastic.core.resources.close +import org.meshtastic.core.resources.indoor_air_quality_iaq import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.ThumbUp import org.meshtastic.core.ui.icon.Warning diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/LastHeardInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/LastHeardInfo.kt index ada97a8ad7..34921cb09d 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/LastHeardInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/LastHeardInfo.kt @@ -20,14 +20,13 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewLightDark import org.jetbrains.compose.resources.stringResource +import org.jetbrains.compose.resources.vectorResource import org.meshtastic.core.common.util.nowSeconds -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.node_sort_last_heard -import org.meshtastic.core.ui.R +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.ic_antenna +import org.meshtastic.core.resources.node_sort_last_heard import org.meshtastic.core.ui.theme.AppTheme import org.meshtastic.core.ui.util.formatAgo @@ -40,7 +39,7 @@ fun LastHeardInfo( ) { IconInfo( modifier = modifier, - icon = ImageVector.vectorResource(id = R.drawable.ic_antenna_24), + icon = vectorResource(Res.drawable.ic_antenna), contentDescription = stringResource(Res.string.node_sort_last_heard), label = if (showLabel) stringResource(Res.string.node_sort_last_heard) else null, text = formatAgo(lastHeard), diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/LoraSignalIndicator.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/LoraSignalIndicator.kt index b96eb81de8..a818208a70 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/LoraSignalIndicator.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/LoraSignalIndicator.kt @@ -45,15 +45,15 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.bad -import org.meshtastic.core.strings.fair -import org.meshtastic.core.strings.good -import org.meshtastic.core.strings.none_quality -import org.meshtastic.core.strings.rssi -import org.meshtastic.core.strings.signal -import org.meshtastic.core.strings.signal_quality -import org.meshtastic.core.strings.snr +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.bad +import org.meshtastic.core.resources.fair +import org.meshtastic.core.resources.good +import org.meshtastic.core.resources.none_quality +import org.meshtastic.core.resources.rssi +import org.meshtastic.core.resources.signal +import org.meshtastic.core.resources.signal_quality +import org.meshtastic.core.resources.snr import org.meshtastic.core.ui.theme.StatusColors.StatusGreen import org.meshtastic.core.ui.theme.StatusColors.StatusOrange import org.meshtastic.core.ui.theme.StatusColors.StatusRed diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MainAppBar.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MainAppBar.kt index 741ad0fa30..afb0539af3 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MainAppBar.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MainAppBar.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.ui.component import androidx.compose.animation.AnimatedVisibility @@ -32,17 +31,16 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource +import org.jetbrains.compose.resources.vectorResource import org.meshtastic.core.database.model.Node -import org.meshtastic.core.strings.R -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.navigate_back +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.ic_meshtastic +import org.meshtastic.core.resources.navigate_back import org.meshtastic.core.ui.component.preview.BooleanProvider import org.meshtastic.core.ui.component.preview.previewNode import org.meshtastic.core.ui.theme.AppTheme @@ -82,12 +80,7 @@ fun MainAppBar( } } } else { - { - Icon( - imageVector = ImageVector.vectorResource(id = org.meshtastic.core.ui.R.drawable.ic_meshtastic), - contentDescription = null, - ) - } + { Icon(imageVector = vectorResource(Res.drawable.ic_meshtastic), contentDescription = null) } }, actions = { TopBarActions(ourNode = ourNode, showNodeChip = showNodeChip, actions = actions, onClickChip = onClickChip) diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MaterialBatteryInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MaterialBatteryInfo.kt index df8cc989fa..4a4cc5ee82 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MaterialBatteryInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MaterialBatteryInfo.kt @@ -39,8 +39,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.unknown +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.unknown import org.meshtastic.core.ui.icon.BatteryEmpty import org.meshtastic.core.ui.icon.BatteryUnknown import org.meshtastic.core.ui.icon.MeshtasticIcons diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MaterialBluetoothSignalInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MaterialBluetoothSignalInfo.kt index c0dbe7c097..cfc368275d 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MaterialBluetoothSignalInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/MaterialBluetoothSignalInfo.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.ui.component import androidx.compose.foundation.layout.Arrangement @@ -40,8 +39,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.dbm_value +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.dbm_value import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.SignalCellular0Bar import org.meshtastic.core.ui.icon.SignalCellular1Bar diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeKeyStatusIcon.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeKeyStatusIcon.kt index 07d2566490..bd7fc8e0df 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeKeyStatusIcon.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeKeyStatusIcon.kt @@ -55,19 +55,19 @@ import okio.ByteString import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.Channel -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.config_security_public_key -import org.meshtastic.core.strings.encryption_error -import org.meshtastic.core.strings.encryption_error_text -import org.meshtastic.core.strings.encryption_pkc -import org.meshtastic.core.strings.encryption_pkc_text -import org.meshtastic.core.strings.encryption_psk -import org.meshtastic.core.strings.encryption_psk_text -import org.meshtastic.core.strings.error -import org.meshtastic.core.strings.security_icon_help_dismiss -import org.meshtastic.core.strings.security_icon_help_show_all -import org.meshtastic.core.strings.security_icon_help_show_less -import org.meshtastic.core.strings.show_all_key_title +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.config_security_public_key +import org.meshtastic.core.resources.encryption_error +import org.meshtastic.core.resources.encryption_error_text +import org.meshtastic.core.resources.encryption_pkc +import org.meshtastic.core.resources.encryption_pkc_text +import org.meshtastic.core.resources.encryption_psk +import org.meshtastic.core.resources.encryption_psk_text +import org.meshtastic.core.resources.error +import org.meshtastic.core.resources.security_icon_help_dismiss +import org.meshtastic.core.resources.security_icon_help_show_all +import org.meshtastic.core.resources.security_icon_help_show_less +import org.meshtastic.core.resources.show_all_key_title import org.meshtastic.core.ui.icon.KeyOff import org.meshtastic.core.ui.icon.Lock import org.meshtastic.core.ui.icon.LockOpen diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/PositionPrecisionPreference.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/PositionPrecisionPreference.kt index 1b4c0a6963..e3fc2a914e 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/PositionPrecisionPreference.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/PositionPrecisionPreference.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.core.ui.component import androidx.compose.foundation.layout.Column @@ -33,9 +32,9 @@ import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.util.DistanceUnit import org.meshtastic.core.model.util.toDistanceString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.position_enabled -import org.meshtastic.core.strings.precise_location +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.position_enabled +import org.meshtastic.core.resources.precise_location import kotlin.math.pow import kotlin.math.roundToInt diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/QrDialog.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/QrDialog.kt index 3c97b9092b..dc41418198 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/QrDialog.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/QrDialog.kt @@ -48,11 +48,11 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.copy -import org.meshtastic.core.strings.okay -import org.meshtastic.core.strings.qr_code -import org.meshtastic.core.strings.url +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.copy +import org.meshtastic.core.resources.okay +import org.meshtastic.core.resources.qr_code +import org.meshtastic.core.resources.url import org.meshtastic.core.ui.util.findActivity private const val QR_IMAGE_SIZE = 320 diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SatelliteCountInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SatelliteCountInfo.kt index 2474c500f2..782b61fa3d 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SatelliteCountInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SatelliteCountInfo.kt @@ -22,8 +22,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewLightDark import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.sats +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.sats import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.Satellites import org.meshtastic.core.ui.theme.AppTheme diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SecurityIcon.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SecurityIcon.kt index e5f16a870f..b54ffa6ce3 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SecurityIcon.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SecurityIcon.kt @@ -57,22 +57,22 @@ import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.Channel import org.meshtastic.core.model.util.getChannel -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.security_icon_badge_warning_description -import org.meshtastic.core.strings.security_icon_description -import org.meshtastic.core.strings.security_icon_help_dismiss -import org.meshtastic.core.strings.security_icon_help_green_lock -import org.meshtastic.core.strings.security_icon_help_red_open_lock -import org.meshtastic.core.strings.security_icon_help_show_all -import org.meshtastic.core.strings.security_icon_help_show_less -import org.meshtastic.core.strings.security_icon_help_title -import org.meshtastic.core.strings.security_icon_help_title_all -import org.meshtastic.core.strings.security_icon_help_warning_precise_mqtt -import org.meshtastic.core.strings.security_icon_help_yellow_open_lock -import org.meshtastic.core.strings.security_icon_insecure_no_precise -import org.meshtastic.core.strings.security_icon_insecure_precise_only -import org.meshtastic.core.strings.security_icon_secure -import org.meshtastic.core.strings.security_icon_warning_precise_mqtt +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.security_icon_badge_warning_description +import org.meshtastic.core.resources.security_icon_description +import org.meshtastic.core.resources.security_icon_help_dismiss +import org.meshtastic.core.resources.security_icon_help_green_lock +import org.meshtastic.core.resources.security_icon_help_red_open_lock +import org.meshtastic.core.resources.security_icon_help_show_all +import org.meshtastic.core.resources.security_icon_help_show_less +import org.meshtastic.core.resources.security_icon_help_title +import org.meshtastic.core.resources.security_icon_help_title_all +import org.meshtastic.core.resources.security_icon_help_warning_precise_mqtt +import org.meshtastic.core.resources.security_icon_help_yellow_open_lock +import org.meshtastic.core.resources.security_icon_insecure_no_precise +import org.meshtastic.core.resources.security_icon_insecure_precise_only +import org.meshtastic.core.resources.security_icon_secure +import org.meshtastic.core.resources.security_icon_warning_precise_mqtt import org.meshtastic.core.ui.icon.Lock import org.meshtastic.core.ui.icon.LockOpen import org.meshtastic.core.ui.icon.MeshtasticIcons diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SignalInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SignalInfo.kt index b698f5b10d..50878e6f8d 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SignalInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/SignalInfo.kt @@ -34,8 +34,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.signal_quality +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.signal_quality import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider import org.meshtastic.core.ui.theme.AppTheme diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TelemetryInfo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TelemetryInfo.kt index 051689af6b..84cb45a690 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TelemetryInfo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TelemetryInfo.kt @@ -39,19 +39,19 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.baro_pressure -import org.meshtastic.core.strings.env_metrics_log -import org.meshtastic.core.strings.humidity -import org.meshtastic.core.strings.iaq -import org.meshtastic.core.strings.node_id -import org.meshtastic.core.strings.pax -import org.meshtastic.core.strings.pax_metrics_log -import org.meshtastic.core.strings.role -import org.meshtastic.core.strings.soil_moisture -import org.meshtastic.core.strings.soil_temperature -import org.meshtastic.core.strings.temperature -import org.meshtastic.core.strings.uptime +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.baro_pressure +import org.meshtastic.core.resources.env_metrics_log +import org.meshtastic.core.resources.humidity +import org.meshtastic.core.resources.iaq +import org.meshtastic.core.resources.node_id +import org.meshtastic.core.resources.pax +import org.meshtastic.core.resources.pax_metrics_log +import org.meshtastic.core.resources.role +import org.meshtastic.core.resources.soil_moisture +import org.meshtastic.core.resources.soil_temperature +import org.meshtastic.core.resources.temperature +import org.meshtastic.core.resources.uptime import org.meshtastic.core.ui.icon.AirQuality import org.meshtastic.core.ui.icon.ArrowCircleUp import org.meshtastic.core.ui.icon.HardwareModel diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TransportIcon.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TransportIcon.kt index 387b44213a..538eaf9965 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TransportIcon.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/TransportIcon.kt @@ -20,11 +20,11 @@ import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.internal -import org.meshtastic.core.strings.via_api -import org.meshtastic.core.strings.via_mqtt -import org.meshtastic.core.strings.via_udp +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.internal +import org.meshtastic.core.resources.via_api +import org.meshtastic.core.resources.via_mqtt +import org.meshtastic.core.resources.via_udp import org.meshtastic.core.ui.icon.Api import org.meshtastic.core.ui.icon.Cloud import org.meshtastic.core.ui.icon.Device diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/icon/Counter.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/icon/Counter.kt index 688a80d79f..4bf0b6a976 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/icon/Counter.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/icon/Counter.kt @@ -18,33 +18,49 @@ package org.meshtastic.core.ui.icon import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource -import org.meshtastic.core.ui.R +import org.meshtastic.core.resources.ic_counter_0 +import org.meshtastic.core.resources.ic_counter_1 +import org.meshtastic.core.resources.ic_counter_2 +import org.meshtastic.core.resources.ic_counter_3 +import org.meshtastic.core.resources.ic_counter_4 +import org.meshtastic.core.resources.ic_counter_5 +import org.meshtastic.core.resources.ic_counter_6 +import org.meshtastic.core.resources.ic_counter_7 +import org.meshtastic.core.resources.ic_counter_8 /** These are from Material Symbols drawables. */ val MeshtasticIcons.Counter0: ImageVector - @Composable get() = ImageVector.vectorResource(R.drawable.counter_0_24px) + @Composable + get() = org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_counter_0) val MeshtasticIcons.Counter1: ImageVector - @Composable get() = ImageVector.vectorResource(R.drawable.counter_1_24px) + @Composable + get() = org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_counter_1) val MeshtasticIcons.Counter2: ImageVector - @Composable get() = ImageVector.vectorResource(R.drawable.counter_2_24px) + @Composable + get() = org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_counter_2) val MeshtasticIcons.Counter3: ImageVector - @Composable get() = ImageVector.vectorResource(R.drawable.counter_3_24px) + @Composable + get() = org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_counter_3) val MeshtasticIcons.Counter4: ImageVector - @Composable get() = ImageVector.vectorResource(R.drawable.counter_4_24px) + @Composable + get() = org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_counter_4) val MeshtasticIcons.Counter5: ImageVector - @Composable get() = ImageVector.vectorResource(R.drawable.counter_5_24px) + @Composable + get() = org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_counter_5) val MeshtasticIcons.Counter6: ImageVector - @Composable get() = ImageVector.vectorResource(R.drawable.counter_6_24px) + @Composable + get() = org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_counter_6) val MeshtasticIcons.Counter7: ImageVector - @Composable get() = ImageVector.vectorResource(R.drawable.counter_7_24px) + @Composable + get() = org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_counter_7) val MeshtasticIcons.Counter8: ImageVector - @Composable get() = ImageVector.vectorResource(R.drawable.counter_8_24px) + @Composable + get() = org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_counter_8) diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/icon/Device.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/icon/Device.kt index 5c368b8fef..1c44b9a13d 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/icon/Device.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/icon/Device.kt @@ -33,9 +33,10 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.path -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp -import org.meshtastic.core.ui.R +import org.jetbrains.compose.resources.vectorResource +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.ic_mountain_flag import org.meshtastic.proto.Config val MeshtasticIcons.HardwareModel: ImageVector @@ -50,7 +51,7 @@ val MeshtasticIcons.NodeId: ImageVector fun MeshtasticIcons.role(role: Config.DeviceConfig.Role?): ImageVector = when (role) { Config.DeviceConfig.Role.CLIENT -> Icons.Rounded.Person Config.DeviceConfig.Role.CLIENT_MUTE -> Icons.Rounded.PersonOff - Config.DeviceConfig.Role.ROUTER -> ImageVector.vectorResource(R.drawable.mountain_flag_24px) + Config.DeviceConfig.Role.ROUTER -> vectorResource(Res.drawable.ic_mountain_flag) Config.DeviceConfig.Role.TRACKER -> Icons.Rounded.MyLocation Config.DeviceConfig.Role.SENSOR -> Icons.Rounded.Sensors Config.DeviceConfig.Role.TAK -> Icons.Rounded.MilitaryTech diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt index 819b26acb9..33e721a3ef 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt @@ -51,14 +51,14 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.Channel -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.accept -import org.meshtastic.core.strings.add -import org.meshtastic.core.strings.add_channels_description -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.new_channel_rcvd -import org.meshtastic.core.strings.replace -import org.meshtastic.core.strings.replace_channels_and_settings_description +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.accept +import org.meshtastic.core.resources.add +import org.meshtastic.core.resources.add_channels_description +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.new_channel_rcvd +import org.meshtastic.core.resources.replace +import org.meshtastic.core.resources.replace_channels_and_settings_description import org.meshtastic.core.ui.component.ChannelSelection import org.meshtastic.proto.ChannelSet diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/share/SharedContactDialog.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/share/SharedContactDialog.kt index b017a88d94..50588f547d 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/share/SharedContactDialog.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/share/SharedContactDialog.kt @@ -27,12 +27,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.util.compareUsers import org.meshtastic.core.model.util.userFieldsToString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.import_known_shared_contact_text -import org.meshtastic.core.strings.import_label -import org.meshtastic.core.strings.import_shared_contact -import org.meshtastic.core.strings.public_key_changed +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.import_known_shared_contact_text +import org.meshtastic.core.resources.import_label +import org.meshtastic.core.resources.import_shared_contact +import org.meshtastic.core.resources.public_key_changed import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.proto.SharedContact import org.meshtastic.proto.User diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/FormatAgo.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/FormatAgo.kt index 6626a8b163..068f60ae46 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/FormatAgo.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/FormatAgo.kt @@ -18,10 +18,10 @@ package org.meshtastic.core.ui.util import android.text.format.DateUtils import org.meshtastic.core.common.util.nowMillis -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.now -import org.meshtastic.core.strings.unknown +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.getString +import org.meshtastic.core.resources.now +import org.meshtastic.core.resources.unknown import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ModelExtensions.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ModelExtensions.kt index e521088836..767f0cbdf9 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ModelExtensions.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ModelExtensions.kt @@ -19,19 +19,19 @@ package org.meshtastic.core.ui.util import org.jetbrains.compose.resources.StringResource import org.meshtastic.core.model.ChannelOption import org.meshtastic.core.model.TracerouteMapAvailability -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.label_long_fast -import org.meshtastic.core.strings.label_long_moderate -import org.meshtastic.core.strings.label_long_slow -import org.meshtastic.core.strings.label_long_turbo -import org.meshtastic.core.strings.label_medium_fast -import org.meshtastic.core.strings.label_medium_slow -import org.meshtastic.core.strings.label_short_fast -import org.meshtastic.core.strings.label_short_slow -import org.meshtastic.core.strings.label_short_turbo -import org.meshtastic.core.strings.label_very_long_slow -import org.meshtastic.core.strings.traceroute_endpoint_missing -import org.meshtastic.core.strings.traceroute_map_no_data +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.label_long_fast +import org.meshtastic.core.resources.label_long_moderate +import org.meshtastic.core.resources.label_long_slow +import org.meshtastic.core.resources.label_long_turbo +import org.meshtastic.core.resources.label_medium_fast +import org.meshtastic.core.resources.label_medium_slow +import org.meshtastic.core.resources.label_short_fast +import org.meshtastic.core.resources.label_short_slow +import org.meshtastic.core.resources.label_short_turbo +import org.meshtastic.core.resources.label_very_long_slow +import org.meshtastic.core.resources.traceroute_endpoint_missing +import org.meshtastic.core.resources.traceroute_map_no_data val ChannelOption.labelRes: StringResource get() = diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ProtoExtensions.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ProtoExtensions.kt index d2df8ad830..9eca1ba874 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ProtoExtensions.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/util/ProtoExtensions.kt @@ -21,8 +21,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.nowMillis -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.unknown_age +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.unknown_age import org.meshtastic.proto.Channel import org.meshtastic.proto.ChannelSettings import org.meshtastic.proto.MeshPacket diff --git a/crowdin.yml b/crowdin.yml index ff4020a884..f37d09a27c 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,5 +1,5 @@ files: - - source: /**/src/commonMain/composeResources/values/strings.xml + - source: /**/composeResources/values/strings.xml translation: /%original_path%-%two_letters_code%/strings.xml translate_attributes: 0 content_segmentation: 0 diff --git a/feature/firmware/README.md b/feature/firmware/README.md index 88c943a309..19fcfa41ad 100644 --- a/feature/firmware/README.md +++ b/feature/firmware/README.md @@ -16,7 +16,7 @@ graph TB :feature:firmware -.-> :core:prefs :feature:firmware -.-> :core:proto :feature:firmware -.-> :core:service - :feature:firmware -.-> :core:strings + :feature:firmware -.-> :core:resources :feature:firmware -.-> :core:ui classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; diff --git a/feature/firmware/build.gradle.kts b/feature/firmware/build.gradle.kts index b4c682d76f..db489e53c5 100644 --- a/feature/firmware/build.gradle.kts +++ b/feature/firmware/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { implementation(projects.core.prefs) implementation(projects.core.proto) implementation(projects.core.service) - implementation(projects.core.strings) + implementation(projects.core.resources) implementation(projects.core.ui) implementation(libs.accompanist.permissions) diff --git a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareDfuService.kt b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareDfuService.kt index c376fa64a0..da1dc18fe6 100644 --- a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareDfuService.kt +++ b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareDfuService.kt @@ -24,9 +24,9 @@ import kotlinx.coroutines.runBlocking import no.nordicsemi.android.dfu.DfuBaseService import org.jetbrains.compose.resources.getString import org.meshtastic.core.model.BuildConfig -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.firmware_update_channel_description -import org.meshtastic.core.strings.firmware_update_channel_name +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.firmware_update_channel_description +import org.meshtastic.core.resources.firmware_update_channel_name class FirmwareDfuService : DfuBaseService() { override fun onCreate() { diff --git a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt index 579eda1ccc..1a52c79d26 100644 --- a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt +++ b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt @@ -91,50 +91,51 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.entity.FirmwareRelease import org.meshtastic.core.database.entity.FirmwareReleaseType import org.meshtastic.core.model.DeviceHardware -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.back -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.chirpy -import org.meshtastic.core.strings.dont_show_again_for_device -import org.meshtastic.core.strings.firmware_update_almost_there -import org.meshtastic.core.strings.firmware_update_alpha -import org.meshtastic.core.strings.firmware_update_checking -import org.meshtastic.core.strings.firmware_update_currently_installed -import org.meshtastic.core.strings.firmware_update_device -import org.meshtastic.core.strings.firmware_update_disclaimer_chirpy_says -import org.meshtastic.core.strings.firmware_update_disclaimer_text -import org.meshtastic.core.strings.firmware_update_disclaimer_title -import org.meshtastic.core.strings.firmware_update_disconnect_warning -import org.meshtastic.core.strings.firmware_update_do_not_close -import org.meshtastic.core.strings.firmware_update_done -import org.meshtastic.core.strings.firmware_update_error -import org.meshtastic.core.strings.firmware_update_hang_tight -import org.meshtastic.core.strings.firmware_update_keep_device_close -import org.meshtastic.core.strings.firmware_update_latest -import org.meshtastic.core.strings.firmware_update_local_file -import org.meshtastic.core.strings.firmware_update_method_detail -import org.meshtastic.core.strings.firmware_update_rak4631_bootloader_hint -import org.meshtastic.core.strings.firmware_update_release_notes -import org.meshtastic.core.strings.firmware_update_retry -import org.meshtastic.core.strings.firmware_update_save_dfu_file -import org.meshtastic.core.strings.firmware_update_select_file -import org.meshtastic.core.strings.firmware_update_source_local -import org.meshtastic.core.strings.firmware_update_stable -import org.meshtastic.core.strings.firmware_update_success -import org.meshtastic.core.strings.firmware_update_taking_a_while -import org.meshtastic.core.strings.firmware_update_target -import org.meshtastic.core.strings.firmware_update_title -import org.meshtastic.core.strings.firmware_update_unknown_release -import org.meshtastic.core.strings.firmware_update_usb_bootloader_warning -import org.meshtastic.core.strings.firmware_update_usb_instruction_text -import org.meshtastic.core.strings.firmware_update_usb_instruction_title -import org.meshtastic.core.strings.firmware_update_verification_failed -import org.meshtastic.core.strings.firmware_update_verifying -import org.meshtastic.core.strings.firmware_update_waiting_reconnect -import org.meshtastic.core.strings.i_know_what_i_m_doing -import org.meshtastic.core.strings.learn_more -import org.meshtastic.core.strings.okay -import org.meshtastic.core.strings.save +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.back +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.chirpy +import org.meshtastic.core.resources.dont_show_again_for_device +import org.meshtastic.core.resources.firmware_update_almost_there +import org.meshtastic.core.resources.firmware_update_alpha +import org.meshtastic.core.resources.firmware_update_checking +import org.meshtastic.core.resources.firmware_update_currently_installed +import org.meshtastic.core.resources.firmware_update_device +import org.meshtastic.core.resources.firmware_update_disclaimer_chirpy_says +import org.meshtastic.core.resources.firmware_update_disclaimer_text +import org.meshtastic.core.resources.firmware_update_disclaimer_title +import org.meshtastic.core.resources.firmware_update_disconnect_warning +import org.meshtastic.core.resources.firmware_update_do_not_close +import org.meshtastic.core.resources.firmware_update_done +import org.meshtastic.core.resources.firmware_update_error +import org.meshtastic.core.resources.firmware_update_hang_tight +import org.meshtastic.core.resources.firmware_update_keep_device_close +import org.meshtastic.core.resources.firmware_update_latest +import org.meshtastic.core.resources.firmware_update_local_file +import org.meshtastic.core.resources.firmware_update_method_detail +import org.meshtastic.core.resources.firmware_update_rak4631_bootloader_hint +import org.meshtastic.core.resources.firmware_update_release_notes +import org.meshtastic.core.resources.firmware_update_retry +import org.meshtastic.core.resources.firmware_update_save_dfu_file +import org.meshtastic.core.resources.firmware_update_select_file +import org.meshtastic.core.resources.firmware_update_source_local +import org.meshtastic.core.resources.firmware_update_stable +import org.meshtastic.core.resources.firmware_update_success +import org.meshtastic.core.resources.firmware_update_taking_a_while +import org.meshtastic.core.resources.firmware_update_target +import org.meshtastic.core.resources.firmware_update_title +import org.meshtastic.core.resources.firmware_update_unknown_release +import org.meshtastic.core.resources.firmware_update_usb_bootloader_warning +import org.meshtastic.core.resources.firmware_update_usb_instruction_text +import org.meshtastic.core.resources.firmware_update_usb_instruction_title +import org.meshtastic.core.resources.firmware_update_verification_failed +import org.meshtastic.core.resources.firmware_update_verifying +import org.meshtastic.core.resources.firmware_update_waiting_reconnect +import org.meshtastic.core.resources.i_know_what_i_m_doing +import org.meshtastic.core.resources.img_chirpy +import org.meshtastic.core.resources.learn_more +import org.meshtastic.core.resources.okay +import org.meshtastic.core.resources.save import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.core.ui.icon.Bluetooth import org.meshtastic.core.ui.icon.CheckCircle @@ -336,7 +337,9 @@ private fun FirmwareUpdateContent( is FirmwareUpdateState.Verifying -> VerifyingState() is FirmwareUpdateState.VerificationFailed -> VerificationFailedState(onRetry = actions.onStartUpdate, onIgnore = actions.onDone) + is FirmwareUpdateState.Error -> ErrorState(error = state.error, onRetry = actions.onRetry) + is FirmwareUpdateState.Success -> SuccessState(onDone = actions.onDone) is FirmwareUpdateState.AwaitingFileSave -> AwaitingFileSaveState(state, actions.onSaveFile) } @@ -493,7 +496,7 @@ private fun ChirpyCard() { AsyncImage( model = ImageRequest.Builder(LocalContext.current) - .data(org.meshtastic.core.ui.R.drawable.chirpy) + .data(Res.drawable.img_chirpy) .crossfade(true) .build(), contentScale = ContentScale.Fit, diff --git a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt index fc25cd754e..81d60db694 100644 --- a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt +++ b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt @@ -49,30 +49,30 @@ import org.meshtastic.core.prefs.radio.RadioPrefs import org.meshtastic.core.prefs.radio.isBle import org.meshtastic.core.prefs.radio.isSerial import org.meshtastic.core.prefs.radio.isTcp +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.firmware_update_battery_low +import org.meshtastic.core.resources.firmware_update_copying +import org.meshtastic.core.resources.firmware_update_dfu_aborted +import org.meshtastic.core.resources.firmware_update_dfu_error +import org.meshtastic.core.resources.firmware_update_disconnecting +import org.meshtastic.core.resources.firmware_update_enabling_dfu +import org.meshtastic.core.resources.firmware_update_extracting +import org.meshtastic.core.resources.firmware_update_failed +import org.meshtastic.core.resources.firmware_update_flashing +import org.meshtastic.core.resources.firmware_update_local_failed +import org.meshtastic.core.resources.firmware_update_method_ble +import org.meshtastic.core.resources.firmware_update_method_usb +import org.meshtastic.core.resources.firmware_update_method_wifi +import org.meshtastic.core.resources.firmware_update_no_device +import org.meshtastic.core.resources.firmware_update_node_info_missing +import org.meshtastic.core.resources.firmware_update_starting_dfu +import org.meshtastic.core.resources.firmware_update_unknown_error +import org.meshtastic.core.resources.firmware_update_unknown_hardware +import org.meshtastic.core.resources.firmware_update_updating +import org.meshtastic.core.resources.firmware_update_validating +import org.meshtastic.core.resources.unknown import org.meshtastic.core.service.ConnectionState import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.firmware_update_battery_low -import org.meshtastic.core.strings.firmware_update_copying -import org.meshtastic.core.strings.firmware_update_dfu_aborted -import org.meshtastic.core.strings.firmware_update_dfu_error -import org.meshtastic.core.strings.firmware_update_disconnecting -import org.meshtastic.core.strings.firmware_update_enabling_dfu -import org.meshtastic.core.strings.firmware_update_extracting -import org.meshtastic.core.strings.firmware_update_failed -import org.meshtastic.core.strings.firmware_update_flashing -import org.meshtastic.core.strings.firmware_update_local_failed -import org.meshtastic.core.strings.firmware_update_method_ble -import org.meshtastic.core.strings.firmware_update_method_usb -import org.meshtastic.core.strings.firmware_update_method_wifi -import org.meshtastic.core.strings.firmware_update_no_device -import org.meshtastic.core.strings.firmware_update_node_info_missing -import org.meshtastic.core.strings.firmware_update_starting_dfu -import org.meshtastic.core.strings.firmware_update_unknown_error -import org.meshtastic.core.strings.firmware_update_unknown_hardware -import org.meshtastic.core.strings.firmware_update_updating -import org.meshtastic.core.strings.firmware_update_validating -import org.meshtastic.core.strings.unknown import java.io.File import javax.inject.Inject diff --git a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/NordicDfuHandler.kt b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/NordicDfuHandler.kt index 0d753ddad7..d104d18d4d 100644 --- a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/NordicDfuHandler.kt +++ b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/NordicDfuHandler.kt @@ -33,12 +33,12 @@ import no.nordicsemi.android.dfu.DfuServiceListenerHelper import org.jetbrains.compose.resources.getString import org.meshtastic.core.database.entity.FirmwareRelease import org.meshtastic.core.model.DeviceHardware +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.firmware_update_downloading_percent +import org.meshtastic.core.resources.firmware_update_nordic_failed +import org.meshtastic.core.resources.firmware_update_not_found_in_release +import org.meshtastic.core.resources.firmware_update_starting_service import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.firmware_update_downloading_percent -import org.meshtastic.core.strings.firmware_update_nordic_failed -import org.meshtastic.core.strings.firmware_update_not_found_in_release -import org.meshtastic.core.strings.firmware_update_starting_service import java.io.File import javax.inject.Inject diff --git a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/UsbUpdateHandler.kt b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/UsbUpdateHandler.kt index 3fdd2245f4..4e7075c217 100644 --- a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/UsbUpdateHandler.kt +++ b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/UsbUpdateHandler.kt @@ -23,12 +23,12 @@ import kotlinx.coroutines.delay import org.jetbrains.compose.resources.getString import org.meshtastic.core.database.entity.FirmwareRelease import org.meshtastic.core.model.DeviceHardware +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.firmware_update_downloading_percent +import org.meshtastic.core.resources.firmware_update_rebooting +import org.meshtastic.core.resources.firmware_update_retrieval_failed +import org.meshtastic.core.resources.firmware_update_usb_failed import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.firmware_update_downloading_percent -import org.meshtastic.core.strings.firmware_update_rebooting -import org.meshtastic.core.strings.firmware_update_retrieval_failed -import org.meshtastic.core.strings.firmware_update_usb_failed import java.io.File import javax.inject.Inject diff --git a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandler.kt b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandler.kt index 25bb0818d1..06bffbb496 100644 --- a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandler.kt +++ b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandler.kt @@ -29,18 +29,18 @@ import org.jetbrains.compose.resources.getString import org.meshtastic.core.common.util.nowMillis import org.meshtastic.core.database.entity.FirmwareRelease import org.meshtastic.core.model.DeviceHardware +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.firmware_update_connecting_attempt +import org.meshtastic.core.resources.firmware_update_downloading_percent +import org.meshtastic.core.resources.firmware_update_erasing +import org.meshtastic.core.resources.firmware_update_hash_rejected +import org.meshtastic.core.resources.firmware_update_loading +import org.meshtastic.core.resources.firmware_update_ota_failed +import org.meshtastic.core.resources.firmware_update_retrieval_failed +import org.meshtastic.core.resources.firmware_update_starting_ota +import org.meshtastic.core.resources.firmware_update_uploading +import org.meshtastic.core.resources.firmware_update_waiting_reboot import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.firmware_update_connecting_attempt -import org.meshtastic.core.strings.firmware_update_downloading_percent -import org.meshtastic.core.strings.firmware_update_erasing -import org.meshtastic.core.strings.firmware_update_hash_rejected -import org.meshtastic.core.strings.firmware_update_loading -import org.meshtastic.core.strings.firmware_update_ota_failed -import org.meshtastic.core.strings.firmware_update_retrieval_failed -import org.meshtastic.core.strings.firmware_update_starting_ota -import org.meshtastic.core.strings.firmware_update_uploading -import org.meshtastic.core.strings.firmware_update_waiting_reboot import org.meshtastic.feature.firmware.FirmwareRetriever import org.meshtastic.feature.firmware.FirmwareUpdateHandler import org.meshtastic.feature.firmware.FirmwareUpdateState diff --git a/feature/intro/README.md b/feature/intro/README.md index 53993b747d..d399c878ba 100644 --- a/feature/intro/README.md +++ b/feature/intro/README.md @@ -20,7 +20,7 @@ Dedicated screens for explaining and requesting specific permissions: ```mermaid graph TB :feature:intro[intro]:::android-feature - :feature:intro -.-> :core:strings + :feature:intro -.-> :core:resources :feature:intro -.-> :core:ui classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; diff --git a/feature/intro/build.gradle.kts b/feature/intro/build.gradle.kts index 6d058a2cca..21aa1b2dbb 100644 --- a/feature/intro/build.gradle.kts +++ b/feature/intro/build.gradle.kts @@ -27,7 +27,7 @@ plugins { configure { namespace = "org.meshtastic.feature.intro" } dependencies { - implementation(projects.core.strings) + implementation(projects.core.resources) implementation(projects.core.ui) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/feature/intro/src/google/kotlin/org/meshtastic/feature/intro/AnalyticsIntro.kt b/feature/intro/src/google/kotlin/org/meshtastic/feature/intro/AnalyticsIntro.kt index 97072122d0..459ca9d826 100644 --- a/feature/intro/src/google/kotlin/org/meshtastic/feature/intro/AnalyticsIntro.kt +++ b/feature/intro/src/google/kotlin/org/meshtastic/feature/intro/AnalyticsIntro.kt @@ -29,13 +29,13 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.analytics_notice -import org.meshtastic.core.strings.analytics_platforms -import org.meshtastic.core.strings.datadog_link -import org.meshtastic.core.strings.firebase_link -import org.meshtastic.core.strings.for_more_information_see_our_privacy_policy -import org.meshtastic.core.strings.privacy_url +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.analytics_notice +import org.meshtastic.core.resources.analytics_platforms +import org.meshtastic.core.resources.datadog_link +import org.meshtastic.core.resources.firebase_link +import org.meshtastic.core.resources.for_more_information_see_our_privacy_policy +import org.meshtastic.core.resources.privacy_url import org.meshtastic.core.ui.component.AutoLinkText @Composable diff --git a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/AppIntroductionScreen.kt b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/AppIntroductionScreen.kt index 7f2270bebb..275dd84b42 100644 --- a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/AppIntroductionScreen.kt +++ b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/AppIntroductionScreen.kt @@ -33,9 +33,9 @@ import kotlinx.serialization.Serializable import no.nordicsemi.android.common.permissions.ble.RequireBluetooth import no.nordicsemi.android.common.permissions.ble.RequireLocation import no.nordicsemi.android.common.permissions.notification.RequestNotificationPermission -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.permission_denied -import org.meshtastic.core.strings.permission_granted +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.permission_denied +import org.meshtastic.core.resources.permission_granted import org.meshtastic.core.ui.util.showToast /** diff --git a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/BluetoothScreen.kt b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/BluetoothScreen.kt index f6e1c60ac8..849c8ce119 100644 --- a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/BluetoothScreen.kt +++ b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/BluetoothScreen.kt @@ -25,16 +25,16 @@ import androidx.compose.material.icons.outlined.SettingsInputAntenna import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.bluetooth_feature_config -import org.meshtastic.core.strings.bluetooth_feature_config_description -import org.meshtastic.core.strings.bluetooth_feature_discovery -import org.meshtastic.core.strings.bluetooth_feature_discovery_description -import org.meshtastic.core.strings.bluetooth_permission -import org.meshtastic.core.strings.configure_bluetooth_permissions -import org.meshtastic.core.strings.next -import org.meshtastic.core.strings.permission_missing_31 -import org.meshtastic.core.strings.settings +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.bluetooth_feature_config +import org.meshtastic.core.resources.bluetooth_feature_config_description +import org.meshtastic.core.resources.bluetooth_feature_discovery +import org.meshtastic.core.resources.bluetooth_feature_discovery_description +import org.meshtastic.core.resources.bluetooth_permission +import org.meshtastic.core.resources.configure_bluetooth_permissions +import org.meshtastic.core.resources.next +import org.meshtastic.core.resources.permission_missing_31 +import org.meshtastic.core.resources.settings /** * Screen for configuring Bluetooth permissions during the app introduction. It explains why Bluetooth permissions are diff --git a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/CriticalAlertsScreen.kt b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/CriticalAlertsScreen.kt index 79b2a3ce8c..eb0f0a1906 100644 --- a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/CriticalAlertsScreen.kt +++ b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/CriticalAlertsScreen.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.intro import androidx.compose.foundation.layout.Column @@ -34,11 +33,11 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.configure_critical_alerts -import org.meshtastic.core.strings.critical_alerts -import org.meshtastic.core.strings.critical_alerts_dnd_request_text -import org.meshtastic.core.strings.skip +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.configure_critical_alerts +import org.meshtastic.core.resources.critical_alerts +import org.meshtastic.core.resources.critical_alerts_dnd_request_text +import org.meshtastic.core.resources.skip /** * Screen for explaining and guiding the user to configure critical alert settings. This screen is part of the app diff --git a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/LocationScreen.kt b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/LocationScreen.kt index f7185d3e29..3d34178d47 100644 --- a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/LocationScreen.kt +++ b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/LocationScreen.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.intro import android.content.Intent @@ -26,20 +25,20 @@ import androidx.compose.material.icons.outlined.Router import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.configure_location_permissions -import org.meshtastic.core.strings.distance_filters -import org.meshtastic.core.strings.distance_filters_description -import org.meshtastic.core.strings.distance_measurements -import org.meshtastic.core.strings.distance_measurements_description -import org.meshtastic.core.strings.mesh_map_location -import org.meshtastic.core.strings.mesh_map_location_description -import org.meshtastic.core.strings.next -import org.meshtastic.core.strings.phone_location -import org.meshtastic.core.strings.phone_location_description -import org.meshtastic.core.strings.settings -import org.meshtastic.core.strings.share_location -import org.meshtastic.core.strings.share_location_description +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.configure_location_permissions +import org.meshtastic.core.resources.distance_filters +import org.meshtastic.core.resources.distance_filters_description +import org.meshtastic.core.resources.distance_measurements +import org.meshtastic.core.resources.distance_measurements_description +import org.meshtastic.core.resources.mesh_map_location +import org.meshtastic.core.resources.mesh_map_location_description +import org.meshtastic.core.resources.next +import org.meshtastic.core.resources.phone_location +import org.meshtastic.core.resources.phone_location_description +import org.meshtastic.core.resources.settings +import org.meshtastic.core.resources.share_location +import org.meshtastic.core.resources.share_location_description /** * Screen for configuring location permissions during the app introduction. It explains why location permissions are diff --git a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/NotificationsScreen.kt b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/NotificationsScreen.kt index 2c8ae60fd1..41a45f4e1a 100644 --- a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/NotificationsScreen.kt +++ b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/NotificationsScreen.kt @@ -26,18 +26,18 @@ import androidx.compose.material.icons.outlined.SpeakerPhone import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.app_notifications -import org.meshtastic.core.strings.configure_notification_permissions -import org.meshtastic.core.strings.incoming_messages -import org.meshtastic.core.strings.low_battery -import org.meshtastic.core.strings.new_nodes -import org.meshtastic.core.strings.next -import org.meshtastic.core.strings.notification_permissions_description -import org.meshtastic.core.strings.notifications_for_channel_and_direct_messages -import org.meshtastic.core.strings.notifications_for_low_battery_alerts -import org.meshtastic.core.strings.notifications_for_newly_discovered_nodes -import org.meshtastic.core.strings.settings +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.app_notifications +import org.meshtastic.core.resources.configure_notification_permissions +import org.meshtastic.core.resources.incoming_messages +import org.meshtastic.core.resources.low_battery +import org.meshtastic.core.resources.new_nodes +import org.meshtastic.core.resources.next +import org.meshtastic.core.resources.notification_permissions_description +import org.meshtastic.core.resources.notifications_for_channel_and_direct_messages +import org.meshtastic.core.resources.notifications_for_low_battery_alerts +import org.meshtastic.core.resources.notifications_for_newly_discovered_nodes +import org.meshtastic.core.resources.settings /** * Screen for configuring notification permissions during the app introduction. It explains why notification permissions diff --git a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/PermissionScreenLayout.kt b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/PermissionScreenLayout.kt index f80456c035..7ecc1d3439 100644 --- a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/PermissionScreenLayout.kt +++ b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/PermissionScreenLayout.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.intro import androidx.compose.foundation.gestures.detectTapGestures @@ -43,8 +42,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.skip +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.skip /** * A generic layout for screens within the app introduction flow. It typically presents a headline, a descriptive text diff --git a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/WelcomeScreen.kt b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/WelcomeScreen.kt index fac0417fb8..8a4843c877 100644 --- a/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/WelcomeScreen.kt +++ b/feature/intro/src/main/kotlin/org/meshtastic/feature/intro/WelcomeScreen.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.intro import androidx.compose.foundation.layout.Column @@ -40,16 +39,16 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.communicate_off_the_grid -import org.meshtastic.core.strings.create_your_own_networks -import org.meshtastic.core.strings.easily_set_up_private_mesh_networks -import org.meshtastic.core.strings.get_started -import org.meshtastic.core.strings.intro_welcome -import org.meshtastic.core.strings.meshtastic -import org.meshtastic.core.strings.share_your_location_in_real_time -import org.meshtastic.core.strings.stay_connected_anywhere -import org.meshtastic.core.strings.track_and_share_locations +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.communicate_off_the_grid +import org.meshtastic.core.resources.create_your_own_networks +import org.meshtastic.core.resources.easily_set_up_private_mesh_networks +import org.meshtastic.core.resources.get_started +import org.meshtastic.core.resources.intro_welcome +import org.meshtastic.core.resources.meshtastic +import org.meshtastic.core.resources.share_your_location_in_real_time +import org.meshtastic.core.resources.stay_connected_anywhere +import org.meshtastic.core.resources.track_and_share_locations /** * The initial welcome screen for the app introduction flow. It displays a brief overview of the app's key features. diff --git a/feature/map/README.md b/feature/map/README.md index e43177b23e..bb70f56c25 100644 --- a/feature/map/README.md +++ b/feature/map/README.md @@ -36,7 +36,7 @@ graph TB :feature:map -.-> :core:prefs :feature:map -.-> :core:proto :feature:map -.-> :core:service - :feature:map -.-> :core:strings + :feature:map -.-> :core:resources :feature:map -.-> :core:ui classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; diff --git a/feature/map/build.gradle.kts b/feature/map/build.gradle.kts index 49d40cc51a..5395120776 100644 --- a/feature/map/build.gradle.kts +++ b/feature/map/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { implementation(projects.core.prefs) implementation(projects.core.proto) implementation(projects.core.service) - implementation(projects.core.strings) + implementation(projects.core.resources) implementation(projects.core.ui) implementation(libs.accompanist.permissions) diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt index f72b132bae..7f63d2b637 100644 --- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt +++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt @@ -89,38 +89,38 @@ import org.meshtastic.core.common.util.nowMillis import org.meshtastic.core.database.entity.Packet import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.DataPacket -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.calculating -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.clear -import org.meshtastic.core.strings.close -import org.meshtastic.core.strings.delete_for_everyone -import org.meshtastic.core.strings.delete_for_me -import org.meshtastic.core.strings.expires -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.location_disabled -import org.meshtastic.core.strings.map_cache_info -import org.meshtastic.core.strings.map_cache_manager -import org.meshtastic.core.strings.map_cache_size -import org.meshtastic.core.strings.map_cache_tiles -import org.meshtastic.core.strings.map_clear_tiles -import org.meshtastic.core.strings.map_download_complete -import org.meshtastic.core.strings.map_download_errors -import org.meshtastic.core.strings.map_download_region -import org.meshtastic.core.strings.map_filter -import org.meshtastic.core.strings.map_node_popup_details -import org.meshtastic.core.strings.map_offline_manager -import org.meshtastic.core.strings.map_purge_fail -import org.meshtastic.core.strings.map_purge_success -import org.meshtastic.core.strings.map_style_selection -import org.meshtastic.core.strings.map_subDescription -import org.meshtastic.core.strings.map_tile_source -import org.meshtastic.core.strings.only_favorites -import org.meshtastic.core.strings.show_precision_circle -import org.meshtastic.core.strings.show_waypoints -import org.meshtastic.core.strings.toggle_my_position -import org.meshtastic.core.strings.waypoint_delete -import org.meshtastic.core.strings.you +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.calculating +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.clear +import org.meshtastic.core.resources.close +import org.meshtastic.core.resources.delete_for_everyone +import org.meshtastic.core.resources.delete_for_me +import org.meshtastic.core.resources.expires +import org.meshtastic.core.resources.getString +import org.meshtastic.core.resources.location_disabled +import org.meshtastic.core.resources.map_cache_info +import org.meshtastic.core.resources.map_cache_manager +import org.meshtastic.core.resources.map_cache_size +import org.meshtastic.core.resources.map_cache_tiles +import org.meshtastic.core.resources.map_clear_tiles +import org.meshtastic.core.resources.map_download_complete +import org.meshtastic.core.resources.map_download_errors +import org.meshtastic.core.resources.map_download_region +import org.meshtastic.core.resources.map_filter +import org.meshtastic.core.resources.map_node_popup_details +import org.meshtastic.core.resources.map_offline_manager +import org.meshtastic.core.resources.map_purge_fail +import org.meshtastic.core.resources.map_purge_success +import org.meshtastic.core.resources.map_style_selection +import org.meshtastic.core.resources.map_subDescription +import org.meshtastic.core.resources.map_tile_source +import org.meshtastic.core.resources.only_favorites +import org.meshtastic.core.resources.show_precision_circle +import org.meshtastic.core.resources.show_waypoints +import org.meshtastic.core.resources.toggle_my_position +import org.meshtastic.core.resources.waypoint_delete +import org.meshtastic.core.resources.you import org.meshtastic.core.ui.component.BasicListItem import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.theme.TracerouteColors @@ -281,18 +281,18 @@ fun MapView( scope.launch { context.showToast(Res.string.location_disabled) } return } + Logger.d { "user clicked MyLocationNewOverlay ${myLocationOverlay == null}" } if (myLocationOverlay == null) { myLocationOverlay = MyLocationNewOverlay(this).apply { enableMyLocation() enableFollowLocation() - getBitmapFromVectorDrawable(context, org.meshtastic.core.ui.R.drawable.ic_map_location_dot_24) - ?.let { - setPersonIcon(it) - setPersonAnchor(0.5f, 0.5f) - } - getBitmapFromVectorDrawable(context, org.meshtastic.core.ui.R.drawable.ic_map_navigation_24)?.let { + getBitmapFromVectorDrawable(context, R.drawable.ic_map_location_dot)?.let { + setPersonIcon(it) + setPersonAnchor(0.5f, 0.5f) + } + getBitmapFromVectorDrawable(context, R.drawable.ic_map_navigation)?.let { setDirectionIcon(it) setDirectionAnchor(0.5f, 0.5f) } @@ -388,9 +388,7 @@ fun MapView( val traceroutePolylines = remember { mutableStateListOf() } var hasCenteredTraceroute by remember(tracerouteOverlay) { mutableStateOf(false) } - val markerIcon = remember { - AppCompatResources.getDrawable(context, org.meshtastic.core.ui.R.drawable.ic_baseline_location_on_24) - } + val markerIcon = remember { AppCompatResources.getDrawable(context, R.drawable.ic_location_on) } fun MapView.onNodesChanged(nodes: Collection): List { val nodesWithPosition = nodes.filter { it.validPosition != null } diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewExtensions.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewExtensions.kt index b82c322725..52ae76c25c 100644 --- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewExtensions.kt +++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewExtensions.kt @@ -24,7 +24,6 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.ContextCompat -import org.meshtastic.core.ui.R import org.meshtastic.proto.Position import org.osmdroid.util.GeoPoint import org.osmdroid.views.MapView @@ -125,7 +124,7 @@ fun MapView.addPolyline(density: Density, geoPoints: List, onClick: () } fun MapView.addPositionMarkers(positions: List, onClick: () -> Unit): List { - val navIcon = ContextCompat.getDrawable(context, R.drawable.ic_map_navigation_24) + val navIcon = ContextCompat.getDrawable(context, R.drawable.ic_map_navigation) val markers = positions.map { Marker(this).apply { diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/CacheLayout.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/CacheLayout.kt index 0e2a7a5394..ac8219d812 100644 --- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/CacheLayout.kt +++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/CacheLayout.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.map.component import androidx.compose.foundation.background @@ -36,11 +35,11 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.map_select_download_region -import org.meshtastic.core.strings.map_start_download -import org.meshtastic.core.strings.map_tile_download_estimate +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.map_select_download_region +import org.meshtastic.core.resources.map_start_download +import org.meshtastic.core.resources.map_tile_download_estimate @OptIn(ExperimentalLayoutApi::class) @Composable diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/DownloadButton.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/DownloadButton.kt index ea7ae21026..6716262411 100644 --- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/DownloadButton.kt +++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/DownloadButton.kt @@ -30,8 +30,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.map_download_region +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.map_download_region @Composable fun DownloadButton(enabled: Boolean, onClick: () -> Unit) { diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/EditWaypointDialog.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/EditWaypointDialog.kt index c2572f1155..0dc57bd4cc 100644 --- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/EditWaypointDialog.kt +++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/EditWaypointDialog.kt @@ -70,18 +70,18 @@ import org.meshtastic.core.common.util.nowInstant import org.meshtastic.core.common.util.nowSeconds import org.meshtastic.core.common.util.systemTimeZone import org.meshtastic.core.common.util.toDate -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.date -import org.meshtastic.core.strings.delete -import org.meshtastic.core.strings.description -import org.meshtastic.core.strings.expires -import org.meshtastic.core.strings.locked -import org.meshtastic.core.strings.name -import org.meshtastic.core.strings.send -import org.meshtastic.core.strings.time -import org.meshtastic.core.strings.waypoint_edit -import org.meshtastic.core.strings.waypoint_new +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.date +import org.meshtastic.core.resources.delete +import org.meshtastic.core.resources.description +import org.meshtastic.core.resources.expires +import org.meshtastic.core.resources.locked +import org.meshtastic.core.resources.name +import org.meshtastic.core.resources.send +import org.meshtastic.core.resources.time +import org.meshtastic.core.resources.waypoint_edit +import org.meshtastic.core.resources.waypoint_new import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.emoji.EmojiPickerDialog import org.meshtastic.core.ui.theme.AppTheme diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/MapButton.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/MapButton.kt index 5ced0960df..b6a368b415 100644 --- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/MapButton.kt +++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/component/MapButton.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.map.component import androidx.compose.foundation.layout.size @@ -29,8 +28,8 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.map_style_selection +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.map_style_selection import org.meshtastic.core.ui.theme.AppTheme @Composable diff --git a/feature/map/src/fdroid/res/drawable/ic_location_on.xml b/feature/map/src/fdroid/res/drawable/ic_location_on.xml new file mode 100644 index 0000000000..3bf5b7133f --- /dev/null +++ b/feature/map/src/fdroid/res/drawable/ic_location_on.xml @@ -0,0 +1,14 @@ + + + + diff --git a/feature/map/src/fdroid/res/drawable/ic_map_location_dot.xml b/feature/map/src/fdroid/res/drawable/ic_map_location_dot.xml new file mode 100644 index 0000000000..2fb6587cc7 --- /dev/null +++ b/feature/map/src/fdroid/res/drawable/ic_map_location_dot.xml @@ -0,0 +1,11 @@ + + + diff --git a/feature/map/src/fdroid/res/drawable/ic_map_navigation.xml b/feature/map/src/fdroid/res/drawable/ic_map_navigation.xml new file mode 100644 index 0000000000..6557bb984d --- /dev/null +++ b/feature/map/src/fdroid/res/drawable/ic_map_navigation.xml @@ -0,0 +1,11 @@ + + + diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt index c15ec03d2d..03081c3abc 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt @@ -97,16 +97,16 @@ import org.meshtastic.core.model.util.metersIn import org.meshtastic.core.model.util.mpsToKmph import org.meshtastic.core.model.util.mpsToMph import org.meshtastic.core.model.util.toString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.alt -import org.meshtastic.core.strings.heading -import org.meshtastic.core.strings.latitude -import org.meshtastic.core.strings.longitude -import org.meshtastic.core.strings.position -import org.meshtastic.core.strings.sats -import org.meshtastic.core.strings.speed -import org.meshtastic.core.strings.timestamp -import org.meshtastic.core.strings.track_point +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.alt +import org.meshtastic.core.resources.heading +import org.meshtastic.core.resources.latitude +import org.meshtastic.core.resources.longitude +import org.meshtastic.core.resources.position +import org.meshtastic.core.resources.sats +import org.meshtastic.core.resources.speed +import org.meshtastic.core.resources.timestamp +import org.meshtastic.core.resources.track_point import org.meshtastic.core.ui.component.NodeChip import org.meshtastic.core.ui.theme.TracerouteColors import org.meshtastic.core.ui.util.formatAgo diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/ClusterItemsListDialog.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/ClusterItemsListDialog.kt index aea1f1114d..6a03e663d1 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/ClusterItemsListDialog.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/ClusterItemsListDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.map.component import androidx.compose.foundation.clickable @@ -31,9 +30,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.nodes_at_this_location -import org.meshtastic.core.strings.okay +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.nodes_at_this_location +import org.meshtastic.core.resources.okay import org.meshtastic.core.ui.component.NodeChip import org.meshtastic.feature.map.model.NodeClusterItem diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomMapLayersSheet.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomMapLayersSheet.kt index 637a1e6536..3a9875ac08 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomMapLayersSheet.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomMapLayersSheet.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.map.component import androidx.compose.foundation.layout.PaddingValues @@ -39,14 +38,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add_layer -import org.meshtastic.core.strings.hide_layer -import org.meshtastic.core.strings.manage_map_layers -import org.meshtastic.core.strings.map_layer_formats -import org.meshtastic.core.strings.no_map_layers_loaded -import org.meshtastic.core.strings.remove_layer -import org.meshtastic.core.strings.show_layer +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add_layer +import org.meshtastic.core.resources.hide_layer +import org.meshtastic.core.resources.manage_map_layers +import org.meshtastic.core.resources.map_layer_formats +import org.meshtastic.core.resources.no_map_layers_loaded +import org.meshtastic.core.resources.remove_layer +import org.meshtastic.core.resources.show_layer import org.meshtastic.feature.map.MapLayerItem @Suppress("LongMethod") diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomTileProviderManagerSheet.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomTileProviderManagerSheet.kt index 15172cd143..c34eb86c52 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomTileProviderManagerSheet.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/CustomTileProviderManagerSheet.kt @@ -49,21 +49,21 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.flow.collectLatest import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.data.model.CustomTileProviderConfig -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add_custom_tile_source -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.delete_custom_tile_source -import org.meshtastic.core.strings.edit_custom_tile_source -import org.meshtastic.core.strings.manage_custom_tile_sources -import org.meshtastic.core.strings.name -import org.meshtastic.core.strings.name_cannot_be_empty -import org.meshtastic.core.strings.no_custom_tile_sources_found -import org.meshtastic.core.strings.provider_name_exists -import org.meshtastic.core.strings.save -import org.meshtastic.core.strings.url_cannot_be_empty -import org.meshtastic.core.strings.url_must_contain_placeholders -import org.meshtastic.core.strings.url_template -import org.meshtastic.core.strings.url_template_hint +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add_custom_tile_source +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.delete_custom_tile_source +import org.meshtastic.core.resources.edit_custom_tile_source +import org.meshtastic.core.resources.manage_custom_tile_sources +import org.meshtastic.core.resources.name +import org.meshtastic.core.resources.name_cannot_be_empty +import org.meshtastic.core.resources.no_custom_tile_sources_found +import org.meshtastic.core.resources.provider_name_exists +import org.meshtastic.core.resources.save +import org.meshtastic.core.resources.url_cannot_be_empty +import org.meshtastic.core.resources.url_must_contain_placeholders +import org.meshtastic.core.resources.url_template +import org.meshtastic.core.resources.url_template_hint import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.core.ui.util.showToast import org.meshtastic.feature.map.MapViewModel diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/EditWaypointDialog.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/EditWaypointDialog.kt index c6b03ed097..8e423dea66 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/EditWaypointDialog.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/EditWaypointDialog.kt @@ -71,18 +71,18 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.nowInstant import org.meshtastic.core.common.util.systemTimeZone import org.meshtastic.core.common.util.toDate -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.date -import org.meshtastic.core.strings.delete -import org.meshtastic.core.strings.description -import org.meshtastic.core.strings.expires -import org.meshtastic.core.strings.locked -import org.meshtastic.core.strings.name -import org.meshtastic.core.strings.send -import org.meshtastic.core.strings.time -import org.meshtastic.core.strings.waypoint_edit -import org.meshtastic.core.strings.waypoint_new +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.date +import org.meshtastic.core.resources.delete +import org.meshtastic.core.resources.description +import org.meshtastic.core.resources.expires +import org.meshtastic.core.resources.locked +import org.meshtastic.core.resources.name +import org.meshtastic.core.resources.send +import org.meshtastic.core.resources.time +import org.meshtastic.core.resources.waypoint_edit +import org.meshtastic.core.resources.waypoint_new import org.meshtastic.core.ui.emoji.EmojiPickerDialog import org.meshtastic.proto.Waypoint import kotlin.time.Duration.Companion.hours diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapControlsOverlay.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapControlsOverlay.kt index cd6ed7ca8c..c2bccf97d4 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapControlsOverlay.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapControlsOverlay.kt @@ -32,12 +32,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.manage_map_layers -import org.meshtastic.core.strings.map_filter -import org.meshtastic.core.strings.map_tile_source -import org.meshtastic.core.strings.orient_north -import org.meshtastic.core.strings.toggle_my_position +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.manage_map_layers +import org.meshtastic.core.resources.map_filter +import org.meshtastic.core.resources.map_tile_source +import org.meshtastic.core.resources.orient_north +import org.meshtastic.core.resources.toggle_my_position import org.meshtastic.core.ui.theme.StatusColors.StatusRed import org.meshtastic.feature.map.MapViewModel diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapFilterDropdown.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapFilterDropdown.kt index c9bcbbca45..6314823bdf 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapFilterDropdown.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapFilterDropdown.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.map.component import androidx.compose.foundation.layout.Column @@ -40,11 +39,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.last_heard_filter_label -import org.meshtastic.core.strings.only_favorites -import org.meshtastic.core.strings.show_precision_circle -import org.meshtastic.core.strings.show_waypoints +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.last_heard_filter_label +import org.meshtastic.core.resources.only_favorites +import org.meshtastic.core.resources.show_precision_circle +import org.meshtastic.core.resources.show_waypoints import org.meshtastic.feature.map.LastHeardFilter import org.meshtastic.feature.map.MapViewModel import kotlin.math.roundToInt diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapTypeDropdown.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapTypeDropdown.kt index 6a6fc74804..e3722ac29d 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapTypeDropdown.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/MapTypeDropdown.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.map.component import androidx.compose.material.icons.Icons @@ -29,13 +28,13 @@ import androidx.compose.runtime.getValue import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.maps.android.compose.MapType import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.manage_custom_tile_sources -import org.meshtastic.core.strings.map_type_hybrid -import org.meshtastic.core.strings.map_type_normal -import org.meshtastic.core.strings.map_type_satellite -import org.meshtastic.core.strings.map_type_terrain -import org.meshtastic.core.strings.selected_map_type +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.manage_custom_tile_sources +import org.meshtastic.core.resources.map_type_hybrid +import org.meshtastic.core.resources.map_type_normal +import org.meshtastic.core.resources.map_type_satellite +import org.meshtastic.core.resources.map_type_terrain +import org.meshtastic.core.resources.selected_map_type import org.meshtastic.feature.map.MapViewModel @Suppress("LongMethod") diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/WaypointMarkers.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/WaypointMarkers.kt index 1776a40394..7072a6ae22 100644 --- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/WaypointMarkers.kt +++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/component/WaypointMarkers.kt @@ -25,8 +25,8 @@ import com.google.android.gms.maps.model.LatLng import com.google.maps.android.compose.Marker import com.google.maps.android.compose.rememberUpdatedMarkerState import kotlinx.coroutines.launch -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.locked +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.locked import org.meshtastic.core.ui.util.showToast import org.meshtastic.feature.map.BaseMapViewModel import org.meshtastic.proto.Waypoint diff --git a/feature/map/src/main/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt b/feature/map/src/main/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt index 70af2c7790..82edfb9bb3 100644 --- a/feature/map/src/main/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt +++ b/feature/map/src/main/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt @@ -36,13 +36,13 @@ import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.DataPacket import org.meshtastic.core.model.util.TimeConstants import org.meshtastic.core.prefs.map.MapPrefs +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.any +import org.meshtastic.core.resources.eight_hours +import org.meshtastic.core.resources.one_day +import org.meshtastic.core.resources.one_hour +import org.meshtastic.core.resources.two_days import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.any -import org.meshtastic.core.strings.eight_hours -import org.meshtastic.core.strings.one_day -import org.meshtastic.core.strings.one_hour -import org.meshtastic.core.strings.two_days import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed import org.meshtastic.feature.map.model.TracerouteOverlay import org.meshtastic.proto.Position diff --git a/feature/map/src/main/kotlin/org/meshtastic/feature/map/MapScreen.kt b/feature/map/src/main/kotlin/org/meshtastic/feature/map/MapScreen.kt index 7a31fbea26..be5d93dcd4 100644 --- a/feature/map/src/main/kotlin/org/meshtastic/feature/map/MapScreen.kt +++ b/feature/map/src/main/kotlin/org/meshtastic/feature/map/MapScreen.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.map import androidx.compose.foundation.layout.Box @@ -26,8 +25,8 @@ import androidx.compose.ui.Modifier import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.map +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.map import org.meshtastic.core.ui.component.MainAppBar @Composable diff --git a/feature/messaging/README.md b/feature/messaging/README.md index 33582b1768..2fc1649144 100644 --- a/feature/messaging/README.md +++ b/feature/messaging/README.md @@ -32,7 +32,7 @@ graph TB :feature:messaging -.-> :core:prefs :feature:messaging -.-> :core:proto :feature:messaging -.-> :core:service - :feature:messaging -.-> :core:strings + :feature:messaging -.-> :core:resources :feature:messaging -.-> :core:ui classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000; diff --git a/feature/messaging/build.gradle.kts b/feature/messaging/build.gradle.kts index 21a483faa0..5f787f3b4d 100644 --- a/feature/messaging/build.gradle.kts +++ b/feature/messaging/build.gradle.kts @@ -48,7 +48,7 @@ dependencies { implementation(projects.core.prefs) implementation(projects.core.proto) implementation(projects.core.service) - implementation(projects.core.strings) + implementation(projects.core.resources) implementation(projects.core.ui) implementation(libs.androidx.compose.material.iconsExtended) @@ -64,4 +64,6 @@ dependencies { androidTestImplementation(libs.androidx.test.ext.junit) testImplementation(libs.junit) + testImplementation(libs.mockk) + testImplementation(libs.kotlinx.coroutines.test) } diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/DeliveryInfoDialog.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/DeliveryInfoDialog.kt index 65d73f6c72..f02ac9218c 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/DeliveryInfoDialog.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/DeliveryInfoDialog.kt @@ -29,10 +29,10 @@ import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.pluralStringResource import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.close -import org.meshtastic.core.strings.relays -import org.meshtastic.core.strings.resend +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.close +import org.meshtastic.core.resources.relays +import org.meshtastic.core.resources.resend import org.meshtastic.core.ui.component.MeshtasticDialog @Suppress("UnusedParameter") diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/Message.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/Message.kt index 6d984e14f3..2c2835c753 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/Message.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/Message.kt @@ -105,32 +105,32 @@ import org.meshtastic.core.database.model.Message import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.DataPacket import org.meshtastic.core.model.util.getChannel -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.alert_bell_text -import org.meshtastic.core.strings.cancel_reply -import org.meshtastic.core.strings.clear_selection -import org.meshtastic.core.strings.copy -import org.meshtastic.core.strings.delete -import org.meshtastic.core.strings.delete_messages -import org.meshtastic.core.strings.delete_messages_title -import org.meshtastic.core.strings.filter_disable_for_contact -import org.meshtastic.core.strings.filter_enable_for_contact -import org.meshtastic.core.strings.filter_hide_count -import org.meshtastic.core.strings.filter_show_count -import org.meshtastic.core.strings.message_input_label -import org.meshtastic.core.strings.navigate_back -import org.meshtastic.core.strings.overflow_menu -import org.meshtastic.core.strings.quick_chat -import org.meshtastic.core.strings.quick_chat_hide -import org.meshtastic.core.strings.quick_chat_show -import org.meshtastic.core.strings.reply -import org.meshtastic.core.strings.replying_to -import org.meshtastic.core.strings.scroll_to_bottom -import org.meshtastic.core.strings.select_all -import org.meshtastic.core.strings.send -import org.meshtastic.core.strings.type_a_message -import org.meshtastic.core.strings.unknown -import org.meshtastic.core.strings.unknown_channel +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.alert_bell_text +import org.meshtastic.core.resources.cancel_reply +import org.meshtastic.core.resources.clear_selection +import org.meshtastic.core.resources.copy +import org.meshtastic.core.resources.delete +import org.meshtastic.core.resources.delete_messages +import org.meshtastic.core.resources.delete_messages_title +import org.meshtastic.core.resources.filter_disable_for_contact +import org.meshtastic.core.resources.filter_enable_for_contact +import org.meshtastic.core.resources.filter_hide_count +import org.meshtastic.core.resources.filter_show_count +import org.meshtastic.core.resources.message_input_label +import org.meshtastic.core.resources.navigate_back +import org.meshtastic.core.resources.overflow_menu +import org.meshtastic.core.resources.quick_chat +import org.meshtastic.core.resources.quick_chat_hide +import org.meshtastic.core.resources.quick_chat_show +import org.meshtastic.core.resources.reply +import org.meshtastic.core.resources.replying_to +import org.meshtastic.core.resources.scroll_to_bottom +import org.meshtastic.core.resources.select_all +import org.meshtastic.core.resources.send +import org.meshtastic.core.resources.type_a_message +import org.meshtastic.core.resources.unknown +import org.meshtastic.core.resources.unknown_channel import org.meshtastic.core.ui.component.MeshtasticTextDialog import org.meshtastic.core.ui.component.NodeKeyStatusIcon import org.meshtastic.core.ui.component.SecurityIcon @@ -305,11 +305,7 @@ fun MessageScreen( val originalMessage by remember(replyingToPacketId, pagedMessages.itemCount) { derivedStateOf { - replyingToPacketId?.let { id -> - (0 until pagedMessages.itemCount).firstNotNullOfOrNull { index -> - pagedMessages[index]?.takeIf { it.packetId == id } - } - } + replyingToPacketId?.let { id -> pagedMessages.itemSnapshotList.firstOrNull { it?.packetId == id } } } } diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageListPaged.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageListPaged.kt index c5c50c82de..25be104303 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageListPaged.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageListPaged.kt @@ -18,13 +18,13 @@ package org.meshtastic.feature.messaging import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.CircularProgressIndicator @@ -53,6 +53,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems +import androidx.paging.compose.itemContentType import androidx.paging.compose.itemKey import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview @@ -65,8 +66,8 @@ import org.meshtastic.core.database.entity.Reaction import org.meshtastic.core.database.model.Message import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.MessageStatus -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.new_messages_below +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.new_messages_below import org.meshtastic.feature.messaging.component.MessageItem import org.meshtastic.feature.messaging.component.ReactionDialog @@ -192,13 +193,13 @@ private fun MessageListPagedContent( modifier: Modifier = Modifier, quickEmojis: List, ) { - // Calculate unread divider position + // Calculate unread divider position using snapshot to avoid side-effects and improve performance + // Optimized: Use full snapshot index to correctly match LazyColumn index range val unreadDividerIndex by remember(state.messages.itemCount, state.firstUnreadMessageUuid) { derivedStateOf { - state.firstUnreadMessageUuid?.let { uuid -> - (0 until state.messages.itemCount).firstOrNull { index -> state.messages[index]?.uuid == uuid } - } + val uuid = state.firstUnreadMessageUuid ?: return@derivedStateOf null + state.messages.itemSnapshotList.indexOfFirst { it?.uuid == uuid }.takeIf { it != -1 } } } @@ -212,7 +213,11 @@ private fun MessageListPagedContent( reverseLayout = true, contentPadding = PaddingValues(bottom = 24.dp), ) { - items(count = state.messages.itemCount, key = state.messages.itemKey { it.uuid }) { index -> + items( + count = state.messages.itemCount, + key = state.messages.itemKey { it.uuid }, + contentType = state.messages.itemContentType { "message" }, + ) { index -> val message = state.messages[index] val visuallyPrevMessage = if (index < state.messages.itemCount - 1) state.messages[index + 1] else null val visuallyNextMessage = if (index > 0) state.messages[index - 1] else null @@ -234,27 +239,49 @@ private fun MessageListPagedContent( } if (message != null) { - renderPagedChatMessageRow( - message = message, - state = state, - nodeMap = nodeMap, - handlers = handlers, - inSelectionMode = inSelectionMode, - coroutineScope = coroutineScope, - haptics = haptics, - listState = listState, - onShowStatusDialog = onShowStatusDialog, - onShowReactions = onShowReactions, - enableAnimations = enableAnimations, - showUserName = !hasSamePrev, - hasSamePrev = hasSamePrev, - hasSameNext = hasSameNext, - quickEmojis = quickEmojis, - ) - - // Show unread divider after the first unread message - if (state.hasUnreadMessages && unreadDividerIndex == index) { - UnreadMessagesDivider(modifier = if (enableAnimations) Modifier.animateItem() else Modifier) + val isFirstUnread = state.hasUnreadMessages && unreadDividerIndex == index + val itemModifier = if (enableAnimations) Modifier.animateItem() else Modifier + + if (isFirstUnread) { + // Wrap in Column to prevent overlapping of divider and message item + // Apply animation to the container Column once + Column(modifier = itemModifier) { + UnreadMessagesDivider() + RenderPagedChatMessageRow( + message = message, + state = state, + nodeMap = nodeMap, + handlers = handlers, + inSelectionMode = inSelectionMode, + coroutineScope = coroutineScope, + haptics = haptics, + listState = listState, + onShowStatusDialog = onShowStatusDialog, + onShowReactions = onShowReactions, + showUserName = !hasSamePrev, + hasSamePrev = hasSamePrev, + hasSameNext = hasSameNext, + quickEmojis = quickEmojis, + ) + } + } else { + RenderPagedChatMessageRow( + message = message, + state = state, + nodeMap = nodeMap, + handlers = handlers, + inSelectionMode = inSelectionMode, + coroutineScope = coroutineScope, + haptics = haptics, + listState = listState, + onShowStatusDialog = onShowStatusDialog, + onShowReactions = onShowReactions, + modifier = itemModifier, + showUserName = !hasSamePrev, + hasSamePrev = hasSamePrev, + hasSameNext = hasSameNext, + quickEmojis = quickEmojis, + ) } } } @@ -263,7 +290,7 @@ private fun MessageListPagedContent( state.messages.apply { when { loadState.append is LoadState.Loading -> { - item(key = "append_loading") { + item(key = "append_loading", contentType = "loading") { Box( modifier = Modifier.fillMaxWidth().padding(16.dp), contentAlignment = Alignment.Center, @@ -280,7 +307,7 @@ private fun MessageListPagedContent( @Suppress("LongParameterList") @Composable -private fun LazyItemScope.renderPagedChatMessageRow( +private fun RenderPagedChatMessageRow( message: Message, state: MessageListPagedState, nodeMap: Map, @@ -291,7 +318,7 @@ private fun LazyItemScope.renderPagedChatMessageRow( listState: LazyListState, onShowStatusDialog: (Message) -> Unit, onShowReactions: (List) -> Unit, - enableAnimations: Boolean, + modifier: Modifier = Modifier, showUserName: Boolean, hasSamePrev: Boolean, hasSameNext: Boolean, @@ -305,7 +332,7 @@ private fun LazyItemScope.renderPagedChatMessageRow( val node = nodeMap[message.node.num] ?: message.node MessageItem( - modifier = if (enableAnimations) Modifier.animateItem() else Modifier, + modifier = modifier, node = node, ourNode = ourNode, message = message, @@ -341,12 +368,10 @@ private fun LazyItemScope.renderPagedChatMessageRow( onNavigateToOriginalMessage = { coroutineScope.launch { // Note: With pagination, we can't guarantee the original message is loaded - // This is a limitation of pagination - we would need to implement - // a search/jump feature to load and scroll to specific messages + // Optimized: Use snapshot to find index to avoid side-effects during search val targetIndex = - (0 until state.messages.itemCount).firstOrNull { index -> - state.messages[index]?.packetId == message.replyId - } + state.messages.itemSnapshotList.indexOfFirst { it?.packetId == message.replyId }.takeIf { it != -1 } + if (targetIndex != null) { listState.animateScrollToItem(index = targetIndex) } @@ -408,19 +433,23 @@ private fun AutoScrollToBottomPaged( } private fun findFirstVisibleUnreadMessage(messages: LazyPagingItems, visibleIndex: Int): Message? { + val snapshot = messages.itemSnapshotList + if (visibleIndex >= snapshot.size) return null val firstVisibleUnreadIndex = - (visibleIndex until messages.itemCount).firstOrNull { i -> - val msg = messages[i] + (visibleIndex until snapshot.size).firstOrNull { i -> + val msg = snapshot[i] msg != null && !msg.read && !msg.fromLocal } - return firstVisibleUnreadIndex?.let { messages[it] } + return firstVisibleUnreadIndex?.let { snapshot[it] } } -private fun findLastUnreadMessageIndex(messages: LazyPagingItems): Int? = - (0 until messages.itemCount).lastOrNull { i -> - val msg = messages[i] +private fun findLastUnreadMessageIndex(messages: LazyPagingItems): Int? { + val snapshot = messages.itemSnapshotList + return (0 until snapshot.size).lastOrNull { i -> + val msg = snapshot[i] msg != null && !msg.read && !msg.fromLocal } +} @OptIn(FlowPreview::class) @Composable @@ -503,7 +532,7 @@ internal fun UnreadMessagesDivider(modifier: Modifier = Modifier) { } @Composable -internal fun MessageStatusDialog( +private fun MessageStatusDialog( message: Message, nodes: List, ourNode: Node?, diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt index 9083dbb579..774faac4ab 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt @@ -16,13 +16,11 @@ */ package org.meshtastic.feature.messaging -import android.os.RemoteException import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn -import co.touchlab.kermit.Logger import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -41,7 +39,6 @@ import org.meshtastic.core.data.repository.RadioConfigRepository import org.meshtastic.core.database.entity.ContactSettings import org.meshtastic.core.database.model.Message import org.meshtastic.core.database.model.Node -import org.meshtastic.core.model.Capabilities import org.meshtastic.core.model.DataPacket import org.meshtastic.core.prefs.emoji.CustomEmojiPrefs import org.meshtastic.core.prefs.homoglyph.HomoglyphPrefs @@ -50,9 +47,8 @@ import org.meshtastic.core.service.MeshServiceNotifications import org.meshtastic.core.service.ServiceAction import org.meshtastic.core.service.ServiceRepository import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed +import org.meshtastic.feature.messaging.domain.usecase.SendMessageUseCase import org.meshtastic.proto.ChannelSet -import org.meshtastic.proto.Config.DeviceConfig.Role -import org.meshtastic.proto.SharedContact import javax.inject.Inject @Suppress("LongParameterList", "TooManyFunctions") @@ -70,6 +66,7 @@ constructor( private val customEmojiPrefs: CustomEmojiPrefs, private val homoglyphEncodingPrefs: HomoglyphPrefs, private val meshServiceNotifications: MeshServiceNotifications, + private val sendMessageUseCase: SendMessageUseCase, ) : ViewModel() { private val _title = MutableStateFlow("") val title: StateFlow = _title.asStateFlow() @@ -194,46 +191,8 @@ constructor( * broadcasting on channel 0. * @param replyId The ID of the message this is a reply to, if any. */ - @Suppress("NestedBlockDepth") fun sendMessage(str: String, contactKey: String = "0${DataPacket.ID_BROADCAST}", replyId: Int? = null) { - // contactKey: unique contact key filter (channel)+(nodeId) - val channel = contactKey[0].digitToIntOrNull() - val dest = if (channel != null) contactKey.substring(1) else contactKey - - // if the destination is a node, we need to ensure it's a - // favorite so it does not get removed from the on-device node database. - if (channel == null) { // no channel specified, so we assume it's a direct message - val fwVersion = ourNodeInfo.value?.metadata?.firmware_version - val destNode = nodeRepository.getNode(dest) - val isClientBase = ourNodeInfo.value?.user?.role == Role.CLIENT_BASE - - val capabilities = Capabilities(fwVersion) - - if (capabilities.canSendVerifiedContacts) { - sendSharedContact(destNode) - } else { - if (!destNode.isFavorite && !isClientBase) { - favoriteNode(destNode) - } - } - } - - // Applying homoglyph encoding to the transmitted string if user has activated the feature - // In most cases the value in "str" parameter will already contain the correct - // transformed string from the text input. This call here added to make sure that - // the feature is effective across all possible message paths (quick-chat, reply, etc.) - val dataPacketText: String = - if (homoglyphEncodingPrefs.homoglyphEncodingEnabled) { - HomoglyphCharacterStringTransformer.optimizeUtf8StringWithHomoglyphs(str) - } else { - str - } - - val p = - DataPacket(dest, channel ?: 0, dataPacketText, replyId).apply { - from = ourNodeInfo.value?.user?.id ?: DataPacket.ID_LOCAL - } - sendDataPacket(p) + viewModelScope.launch { sendMessageUseCase.invoke(str, contactKey, replyId) } } fun sendReaction(emoji: String, replyId: Int, contactKey: String) = @@ -253,30 +212,4 @@ constructor( val unreadCount = packetRepository.getUnreadCount(contact) if (unreadCount == 0) meshServiceNotifications.cancelMessageNotification(contact) } - - private fun favoriteNode(node: Node) = viewModelScope.launch { - try { - serviceRepository.onServiceAction(ServiceAction.Favorite(node)) - } catch (ex: RemoteException) { - Logger.e(ex) { "Favorite node error" } - } - } - - private fun sendSharedContact(node: Node) = viewModelScope.launch { - try { - val contact = - SharedContact(node_num = node.num, user = node.user, manually_verified = node.manuallyVerified) - serviceRepository.onServiceAction(ServiceAction.SendContact(contact = contact)) - } catch (ex: RemoteException) { - Logger.e(ex) { "Send shared contact error" } - } - } - - private fun sendDataPacket(p: DataPacket) { - try { - serviceRepository.meshService?.send(p) - } catch (ex: RemoteException) { - Logger.e { "Send DataPacket error: ${ex.message}" } - } - } } diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/QuickChat.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/QuickChat.kt index 260066e433..f63a8a1013 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/QuickChat.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/QuickChat.kt @@ -65,18 +65,18 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.entity.QuickChatAction -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.delete -import org.meshtastic.core.strings.message -import org.meshtastic.core.strings.name -import org.meshtastic.core.strings.quick_chat -import org.meshtastic.core.strings.quick_chat_append -import org.meshtastic.core.strings.quick_chat_edit -import org.meshtastic.core.strings.quick_chat_instant -import org.meshtastic.core.strings.quick_chat_new -import org.meshtastic.core.strings.save +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.delete +import org.meshtastic.core.resources.message +import org.meshtastic.core.resources.name +import org.meshtastic.core.resources.quick_chat +import org.meshtastic.core.resources.quick_chat_append +import org.meshtastic.core.resources.quick_chat_edit +import org.meshtastic.core.resources.quick_chat_instant +import org.meshtastic.core.resources.quick_chat_new +import org.meshtastic.core.resources.save import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.core.ui.component.dragContainer diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActions.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActions.kt index 1d68d69f31..3ef1e3ccb0 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActions.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActions.kt @@ -42,10 +42,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.MessageStatus -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.message_delivery_status -import org.meshtastic.core.strings.react -import org.meshtastic.core.strings.reply +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.message_delivery_status +import org.meshtastic.core.resources.react +import org.meshtastic.core.resources.reply import org.meshtastic.core.ui.emoji.EmojiPickerDialog @Composable diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActionsBottomSheet.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActionsBottomSheet.kt index 0762fea99e..f95c64b45f 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActionsBottomSheet.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageActionsBottomSheet.kt @@ -47,12 +47,12 @@ import androidx.compose.ui.unit.sp import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.MessageStatus -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.copy -import org.meshtastic.core.strings.delete -import org.meshtastic.core.strings.message_delivery_status -import org.meshtastic.core.strings.reply -import org.meshtastic.core.strings.select +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.copy +import org.meshtastic.core.resources.delete +import org.meshtastic.core.resources.message_delivery_status +import org.meshtastic.core.resources.reply +import org.meshtastic.core.resources.select @Composable fun MessageActionsContent( diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageItem.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageItem.kt index cf3d150f5f..79f7c8ba68 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageItem.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/MessageItem.kt @@ -66,11 +66,11 @@ import org.meshtastic.core.database.entity.Reaction import org.meshtastic.core.database.model.Message import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.MessageStatus -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.filter_message_label -import org.meshtastic.core.strings.message_delivery_status -import org.meshtastic.core.strings.reply -import org.meshtastic.core.strings.sample_message +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.filter_message_label +import org.meshtastic.core.resources.message_delivery_status +import org.meshtastic.core.resources.reply +import org.meshtastic.core.resources.sample_message import org.meshtastic.core.ui.component.AutoLinkText import org.meshtastic.core.ui.component.NodeChip import org.meshtastic.core.ui.component.Rssi diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/Reaction.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/Reaction.kt index 06b80bed8d..0011e1e5c1 100644 --- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/Reaction.kt +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/component/Reaction.kt @@ -64,14 +64,14 @@ import org.meshtastic.core.database.model.getStringResFrom import org.meshtastic.core.model.DataPacket import org.meshtastic.core.model.MessageStatus import org.meshtastic.core.model.util.getShortDateTime -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.delivery_confirmed -import org.meshtastic.core.strings.error -import org.meshtastic.core.strings.message_delivery_status -import org.meshtastic.core.strings.message_status_enroute -import org.meshtastic.core.strings.message_status_queued -import org.meshtastic.core.strings.react -import org.meshtastic.core.strings.you +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.delivery_confirmed +import org.meshtastic.core.resources.error +import org.meshtastic.core.resources.message_delivery_status +import org.meshtastic.core.resources.message_status_enroute +import org.meshtastic.core.resources.message_status_queued +import org.meshtastic.core.resources.react +import org.meshtastic.core.resources.you import org.meshtastic.core.ui.component.BottomSheetDialog import org.meshtastic.core.ui.component.Rssi import org.meshtastic.core.ui.component.Snr diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/domain/usecase/SendMessageUseCase.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/domain/usecase/SendMessageUseCase.kt new file mode 100644 index 0000000000..1c9863015d --- /dev/null +++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/domain/usecase/SendMessageUseCase.kt @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025-2026 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 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 org.meshtastic.feature.messaging.domain.usecase + +import co.touchlab.kermit.Logger +import org.meshtastic.core.data.repository.NodeRepository +import org.meshtastic.core.database.model.Node +import org.meshtastic.core.model.Capabilities +import org.meshtastic.core.model.DataPacket +import org.meshtastic.core.prefs.homoglyph.HomoglyphPrefs +import org.meshtastic.core.service.ServiceAction +import org.meshtastic.core.service.ServiceRepository +import org.meshtastic.feature.messaging.HomoglyphCharacterStringTransformer +import org.meshtastic.proto.Config +import org.meshtastic.proto.SharedContact +import javax.inject.Inject + +@Suppress("TooGenericExceptionCaught") +class SendMessageUseCase +@Inject +constructor( + private val nodeRepository: NodeRepository, + private val serviceRepository: ServiceRepository, + private val homoglyphEncodingPrefs: HomoglyphPrefs, +) { + + @Suppress("NestedBlockDepth", "LongMethod", "CyclomaticComplexMethod") + suspend operator fun invoke( + text: String, + contactKey: String = "0${DataPacket.ID_BROADCAST}", + replyId: Int? = null, + ) { + val channel = contactKey[0].digitToIntOrNull() + val dest = if (channel != null) contactKey.substring(1) else contactKey + + val ourNode = nodeRepository.ourNodeInfo.value + val fromId = ourNode?.user?.id ?: DataPacket.ID_LOCAL + + // logic for direct messages + if (channel == null) { + val destNode = nodeRepository.getNode(dest) + val fwVersion = ourNode?.metadata?.firmware_version + val isClientBase = ourNode?.user?.role == Config.DeviceConfig.Role.CLIENT_BASE + val capabilities = Capabilities(fwVersion) + + if (capabilities.canSendVerifiedContacts) { + sendSharedContact(destNode) + } else { + if (!destNode.isFavorite && !isClientBase) { + favoriteNode(destNode) + } + } + } + + // Apply homoglyph encoding + val finalMessageText = + if (homoglyphEncodingPrefs.homoglyphEncodingEnabled) { + HomoglyphCharacterStringTransformer.optimizeUtf8StringWithHomoglyphs(text) + } else { + text + } + + val packet = DataPacket(dest, channel ?: 0, finalMessageText, replyId).apply { from = fromId } + + try { + serviceRepository.meshService?.send(packet) + } catch (ex: Exception) { + Logger.e(ex) { "Failed to send data packet" } + } + } + + private suspend fun favoriteNode(node: Node) { + try { + serviceRepository.onServiceAction(ServiceAction.Favorite(node)) + } catch (ex: Exception) { + Logger.e(ex) { "Favorite node error" } + } + } + + private suspend fun sendSharedContact(node: Node) { + try { + val contact = + SharedContact(node_num = node.num, user = node.user, manually_verified = node.manuallyVerified) + serviceRepository.onServiceAction(ServiceAction.SendContact(contact = contact)) + } catch (ex: Exception) { + Logger.e(ex) { "Send shared contact error" } + } + } +} diff --git a/feature/messaging/src/test/kotlin/org/meshtastic/feature/messaging/domain/usecase/SendMessageUseCaseTest.kt b/feature/messaging/src/test/kotlin/org/meshtastic/feature/messaging/domain/usecase/SendMessageUseCaseTest.kt new file mode 100644 index 0000000000..42adf05a85 --- /dev/null +++ b/feature/messaging/src/test/kotlin/org/meshtastic/feature/messaging/domain/usecase/SendMessageUseCaseTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2025-2026 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 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 org.meshtastic.feature.messaging.domain.usecase + +import io.mockk.coVerify +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkConstructor +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.meshtastic.core.data.repository.NodeRepository +import org.meshtastic.core.database.model.Node +import org.meshtastic.core.model.Capabilities +import org.meshtastic.core.model.DataPacket +import org.meshtastic.core.prefs.homoglyph.HomoglyphPrefs +import org.meshtastic.core.service.ServiceAction +import org.meshtastic.core.service.ServiceRepository +import org.meshtastic.proto.Config +import org.meshtastic.proto.DeviceMetadata + +class SendMessageUseCaseTest { + + private lateinit var nodeRepository: NodeRepository + private lateinit var serviceRepository: ServiceRepository + private lateinit var homoglyphEncodingPrefs: HomoglyphPrefs + private lateinit var useCase: SendMessageUseCase + + @Before + fun setUp() { + nodeRepository = mockk(relaxed = true) + serviceRepository = mockk(relaxed = true) + homoglyphEncodingPrefs = mockk(relaxed = true) + + useCase = + SendMessageUseCase( + nodeRepository = nodeRepository, + serviceRepository = serviceRepository, + homoglyphEncodingPrefs = homoglyphEncodingPrefs, + ) + + mockkConstructor(Capabilities::class) + } + + @Test + fun `invoke with broadcast message simply sends data packet`() = runTest { + // Arrange + val ourNode = mockk(relaxed = true) + every { ourNode.user.id } returns "!1234" + every { nodeRepository.ourNodeInfo } returns MutableStateFlow(ourNode) + every { homoglyphEncodingPrefs.homoglyphEncodingEnabled } returns false + + // Act + useCase("Hello broadcast", "0${DataPacket.ID_BROADCAST}", null) + + // Assert + coVerify(exactly = 0) { serviceRepository.onServiceAction(any()) } + coVerify(exactly = 1) { serviceRepository.meshService?.send(any()) } + } + + @Test + fun `invoke with direct message to older firmware triggers favoriteNode`() = runTest { + // Arrange + val ourNode = mockk(relaxed = true) + val metadata = mockk(relaxed = true) + every { ourNode.user.id } returns "!local" + every { ourNode.user.role } returns Config.DeviceConfig.Role.CLIENT + every { ourNode.metadata } returns metadata + every { metadata.firmware_version } returns "2.0.0" // Older firmware + every { nodeRepository.ourNodeInfo } returns MutableStateFlow(ourNode) + + val destNode = mockk(relaxed = true) + every { destNode.isFavorite } returns false + every { nodeRepository.getNode("!dest") } returns destNode + + every { homoglyphEncodingPrefs.homoglyphEncodingEnabled } returns false + + every { anyConstructed().canSendVerifiedContacts } returns false + + // Act + useCase("Direct message", "!dest", null) + + // Assert + coVerify(exactly = 1) { serviceRepository.onServiceAction(match { it is ServiceAction.Favorite }) } + coVerify(exactly = 1) { serviceRepository.meshService?.send(any()) } + } + + @Test + fun `invoke with direct message to new firmware triggers sendSharedContact`() = runTest { + // Arrange + val ourNode = mockk(relaxed = true) + val metadata = mockk(relaxed = true) + every { ourNode.user.id } returns "!local" + every { ourNode.user.role } returns Config.DeviceConfig.Role.CLIENT + every { ourNode.metadata } returns metadata + every { metadata.firmware_version } returns "2.7.12" // Newer firmware + every { nodeRepository.ourNodeInfo } returns MutableStateFlow(ourNode) + + val destNode = mockk(relaxed = true) + every { nodeRepository.getNode("!dest") } returns destNode + + every { homoglyphEncodingPrefs.homoglyphEncodingEnabled } returns false + + every { anyConstructed().canSendVerifiedContacts } returns true + + // Act + useCase("Direct message", "!dest", null) + + // Assert + coVerify(exactly = 1) { serviceRepository.onServiceAction(match { it is ServiceAction.SendContact }) } + coVerify(exactly = 1) { serviceRepository.meshService?.send(any()) } + } + + @Test + fun `invoke with homoglyph enabled transforms text`() = runTest { + // Arrange + val ourNode = mockk(relaxed = true) + every { nodeRepository.ourNodeInfo } returns MutableStateFlow(ourNode) + every { homoglyphEncodingPrefs.homoglyphEncodingEnabled } returns true + + // Let's use a cyrillic character 'A' (U+0410) that will be mapped to Latin 'A' + val originalText = "\u0410pple" + + // Act + useCase(originalText, "0${DataPacket.ID_BROADCAST}", null) + + // Assert + // We verify that send was called with the transformed text (Latin 'A'pple) + coVerify(exactly = 1) { serviceRepository.meshService?.send(match { it.text?.contains("Apple") == true }) } + } +} diff --git a/feature/node/README.md b/feature/node/README.md index 787d59de9e..1d2ba4c2fd 100644 --- a/feature/node/README.md +++ b/feature/node/README.md @@ -31,7 +31,7 @@ graph TB :feature:node -.-> :core:model :feature:node -.-> :core:proto :feature:node -.-> :core:service - :feature:node -.-> :core:strings + :feature:node -.-> :core:resources :feature:node -.-> :core:ui :feature:node -.-> :core:navigation :feature:node -.-> :feature:map diff --git a/feature/node/build.gradle.kts b/feature/node/build.gradle.kts index 01d92ddd37..de857e9d97 100644 --- a/feature/node/build.gradle.kts +++ b/feature/node/build.gradle.kts @@ -40,7 +40,7 @@ dependencies { implementation(projects.core.model) implementation(projects.core.proto) implementation(projects.core.service) - implementation(projects.core.strings) + implementation(projects.core.resources) implementation(projects.core.ui) implementation(projects.core.navigation) implementation(projects.feature.map) diff --git a/feature/node/component/DeviceActions.kt b/feature/node/component/DeviceActions.kt index 4607cd4aa9..39bb324c89 100644 --- a/feature/node/component/DeviceActions.kt +++ b/feature/node/component/DeviceActions.kt @@ -56,15 +56,15 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.actions -import org.meshtastic.core.strings.direct_message -import org.meshtastic.core.strings.favorite -import org.meshtastic.core.strings.ignore -import org.meshtastic.core.strings.mute_always -import org.meshtastic.core.strings.remove -import org.meshtastic.core.strings.share_contact -import org.meshtastic.core.strings.unmute +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.actions +import org.meshtastic.core.resources.direct_message +import org.meshtastic.core.resources.favorite +import org.meshtastic.core.resources.ignore +import org.meshtastic.core.resources.mute_always +import org.meshtastic.core.resources.remove +import org.meshtastic.core.resources.share_contact +import org.meshtastic.core.resources.unmute import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.component.SwitchListItem import org.meshtastic.feature.node.model.NodeDetailAction diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/AdministrationSection.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/AdministrationSection.kt index 21ca38a602..0fb96c836b 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/AdministrationSection.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/AdministrationSection.kt @@ -32,16 +32,16 @@ import org.meshtastic.core.database.entity.asDeviceVersion import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.DeviceVersion import org.meshtastic.core.navigation.SettingsRoutes +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.administration +import org.meshtastic.core.resources.firmware +import org.meshtastic.core.resources.firmware_edition +import org.meshtastic.core.resources.installed_firmware_version +import org.meshtastic.core.resources.latest_alpha_firmware +import org.meshtastic.core.resources.latest_stable_firmware +import org.meshtastic.core.resources.remote_admin +import org.meshtastic.core.resources.request_metadata import org.meshtastic.core.service.ServiceAction -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.administration -import org.meshtastic.core.strings.firmware -import org.meshtastic.core.strings.firmware_edition -import org.meshtastic.core.strings.installed_firmware_version -import org.meshtastic.core.strings.latest_alpha_firmware -import org.meshtastic.core.strings.latest_stable_firmware -import org.meshtastic.core.strings.remote_admin -import org.meshtastic.core.strings.request_metadata import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.theme.StatusColors.StatusGreen import org.meshtastic.core.ui.theme.StatusColors.StatusOrange diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/CompassBottomSheet.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/CompassBottomSheet.kt index 407e02830b..7f8b995736 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/CompassBottomSheet.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/CompassBottomSheet.kt @@ -56,20 +56,20 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.compass_bearing -import org.meshtastic.core.strings.compass_bearing_na -import org.meshtastic.core.strings.compass_distance -import org.meshtastic.core.strings.compass_location_disabled -import org.meshtastic.core.strings.compass_no_location_fix -import org.meshtastic.core.strings.compass_no_location_permission -import org.meshtastic.core.strings.compass_no_magnetometer -import org.meshtastic.core.strings.compass_title -import org.meshtastic.core.strings.compass_uncertainty -import org.meshtastic.core.strings.compass_uncertainty_unknown -import org.meshtastic.core.strings.elevation_suffix -import org.meshtastic.core.strings.exchange_position -import org.meshtastic.core.strings.last_position_update +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.compass_bearing +import org.meshtastic.core.resources.compass_bearing_na +import org.meshtastic.core.resources.compass_distance +import org.meshtastic.core.resources.compass_location_disabled +import org.meshtastic.core.resources.compass_no_location_fix +import org.meshtastic.core.resources.compass_no_location_permission +import org.meshtastic.core.resources.compass_no_magnetometer +import org.meshtastic.core.resources.compass_title +import org.meshtastic.core.resources.compass_uncertainty +import org.meshtastic.core.resources.compass_uncertainty_unknown +import org.meshtastic.core.resources.elevation_suffix +import org.meshtastic.core.resources.exchange_position +import org.meshtastic.core.resources.last_position_update import org.meshtastic.core.ui.theme.AppTheme import org.meshtastic.feature.node.compass.CompassUiState import org.meshtastic.feature.node.compass.CompassWarning diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DeviceActions.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DeviceActions.kt index 504d8a6b3e..b96ad7927d 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DeviceActions.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DeviceActions.kt @@ -47,14 +47,14 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.actions -import org.meshtastic.core.strings.direct_message -import org.meshtastic.core.strings.favorite -import org.meshtastic.core.strings.ignore -import org.meshtastic.core.strings.mute_notifications -import org.meshtastic.core.strings.remove -import org.meshtastic.core.strings.share_contact +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.actions +import org.meshtastic.core.resources.direct_message +import org.meshtastic.core.resources.favorite +import org.meshtastic.core.resources.ignore +import org.meshtastic.core.resources.mute_notifications +import org.meshtastic.core.resources.remove +import org.meshtastic.core.resources.share_contact import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.component.SwitchListItem import org.meshtastic.feature.node.model.LogsType diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DeviceDetailsSection.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DeviceDetailsSection.kt index c60023f058..198fdd3d35 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DeviceDetailsSection.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DeviceDetailsSection.kt @@ -36,21 +36,20 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import coil3.request.ImageRequest import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.DeviceHardware -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.device -import org.meshtastic.core.strings.hardware -import org.meshtastic.core.strings.supported -import org.meshtastic.core.strings.supported_by_community +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.device +import org.meshtastic.core.resources.hardware +import org.meshtastic.core.resources.ic_unverified +import org.meshtastic.core.resources.img_hw_unknown +import org.meshtastic.core.resources.supported +import org.meshtastic.core.resources.supported_by_community import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.theme.StatusColors.StatusGreen import org.meshtastic.core.ui.theme.StatusColors.StatusRed @@ -119,7 +118,7 @@ private fun SupportStatusItem(isSupported: Boolean) { if (isSupported) { Icons.TwoTone.Verified } else { - ImageVector.vectorResource(org.meshtastic.feature.node.R.drawable.unverified) + org.jetbrains.compose.resources.vectorResource(org.meshtastic.core.resources.Res.drawable.ic_unverified) }, leadingIconTint = if (isSupported) colorScheme.StatusGreen else colorScheme.StatusRed, trailingIcon = null, @@ -134,9 +133,12 @@ private fun DeviceHardwareImage(deviceHardware: DeviceHardware, modifier: Modifi model = ImageRequest.Builder(LocalContext.current).data(imageUrl).build(), contentScale = ContentScale.Inside, contentDescription = deviceHardware.displayName, - placeholder = painterResource(org.meshtastic.feature.node.R.drawable.hw_unknown), - error = painterResource(org.meshtastic.feature.node.R.drawable.hw_unknown), - fallback = painterResource(org.meshtastic.feature.node.R.drawable.hw_unknown), + placeholder = + org.jetbrains.compose.resources.painterResource(org.meshtastic.core.resources.Res.drawable.img_hw_unknown), + error = + org.jetbrains.compose.resources.painterResource(org.meshtastic.core.resources.Res.drawable.img_hw_unknown), + fallback = + org.jetbrains.compose.resources.painterResource(org.meshtastic.core.resources.Res.drawable.img_hw_unknown), modifier = modifier.padding(16.dp), ) } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DistanceInfo.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DistanceInfo.kt index 4d13c59bf6..c65b9a4902 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DistanceInfo.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/DistanceInfo.kt @@ -24,8 +24,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewLightDark import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.distance +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.distance import org.meshtastic.core.ui.theme.AppTheme @Composable diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/ElevationInfo.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/ElevationInfo.kt index 6a81858ef9..dca3b143f9 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/ElevationInfo.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/ElevationInfo.kt @@ -24,9 +24,9 @@ import androidx.compose.ui.tooling.preview.Preview import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.util.metersIn import org.meshtastic.core.model.util.toString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.altitude -import org.meshtastic.core.strings.elevation_suffix +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.altitude +import org.meshtastic.core.resources.elevation_suffix import org.meshtastic.core.ui.icon.Elevation import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.proto.Config diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/EnvironmentMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/EnvironmentMetrics.kt index 84978557db..05cfd5fc53 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/EnvironmentMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/EnvironmentMetrics.kt @@ -40,23 +40,27 @@ import org.meshtastic.core.model.util.UnitConversions import org.meshtastic.core.model.util.UnitConversions.toTempString import org.meshtastic.core.model.util.toSmallDistanceString import org.meshtastic.core.model.util.toSpeedString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.current -import org.meshtastic.core.strings.dew_point -import org.meshtastic.core.strings.distance -import org.meshtastic.core.strings.gas_resistance -import org.meshtastic.core.strings.humidity -import org.meshtastic.core.strings.iaq -import org.meshtastic.core.strings.lux -import org.meshtastic.core.strings.pressure -import org.meshtastic.core.strings.radiation -import org.meshtastic.core.strings.soil_moisture -import org.meshtastic.core.strings.soil_temperature -import org.meshtastic.core.strings.temperature -import org.meshtastic.core.strings.uv_lux -import org.meshtastic.core.strings.voltage -import org.meshtastic.core.strings.weight -import org.meshtastic.core.strings.wind +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.current +import org.meshtastic.core.resources.dew_point +import org.meshtastic.core.resources.distance +import org.meshtastic.core.resources.gas_resistance +import org.meshtastic.core.resources.humidity +import org.meshtastic.core.resources.iaq +import org.meshtastic.core.resources.ic_dew_point +import org.meshtastic.core.resources.ic_radioactive +import org.meshtastic.core.resources.ic_soil_moisture +import org.meshtastic.core.resources.ic_soil_temperature +import org.meshtastic.core.resources.lux +import org.meshtastic.core.resources.pressure +import org.meshtastic.core.resources.radiation +import org.meshtastic.core.resources.soil_moisture +import org.meshtastic.core.resources.soil_temperature +import org.meshtastic.core.resources.temperature +import org.meshtastic.core.resources.uv_lux +import org.meshtastic.core.resources.voltage +import org.meshtastic.core.resources.weight +import org.meshtastic.core.resources.wind import org.meshtastic.feature.node.model.DrawableMetricInfo import org.meshtastic.feature.node.model.VectorMetricInfo import org.meshtastic.proto.Config @@ -136,7 +140,7 @@ internal fun EnvironmentMetrics( DrawableMetricInfo( Res.string.dew_point, dewPoint.toTempString(isFahrenheit), - org.meshtastic.feature.node.R.drawable.ic_outlined_dew_point_24, + Res.drawable.ic_dew_point, ), ) } @@ -147,7 +151,7 @@ internal fun EnvironmentMetrics( DrawableMetricInfo( Res.string.soil_temperature, st.toTempString(isFahrenheit), - org.meshtastic.feature.node.R.drawable.soil_temperature, + Res.drawable.ic_soil_temperature, ), ) } @@ -157,16 +161,16 @@ internal fun EnvironmentMetrics( DrawableMetricInfo( Res.string.soil_moisture, "%d%%".format(sm), - org.meshtastic.feature.node.R.drawable.soil_moisture, + Res.drawable.ic_soil_moisture, ), ) } radiation?.let { r -> add( DrawableMetricInfo( - Res.string.radiation, - "%.1f µR/h".format(r), - org.meshtastic.feature.node.R.drawable.ic_filled_radioactive_24, + label = Res.string.radiation, + value = "%.1f µR/h".format(r), + icon = Res.drawable.ic_radioactive, ), ) } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt index d5d55abf33..7a6e0e6a08 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt @@ -45,10 +45,10 @@ import com.mikepenz.markdown.m3.Markdown import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.entity.FirmwareRelease -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.download -import org.meshtastic.core.strings.error_no_app_to_handle_link -import org.meshtastic.core.strings.view_release +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.download +import org.meshtastic.core.resources.error_no_app_to_handle_link +import org.meshtastic.core.resources.view_release import org.meshtastic.core.ui.util.showToast @Composable diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/HopsInfo.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/HopsInfo.kt index 2226621ef6..c888bbca1b 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/HopsInfo.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/HopsInfo.kt @@ -24,8 +24,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewLightDark import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.hops_away +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.hops_away import org.meshtastic.core.ui.theme.AppTheme @Composable diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCard.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCard.kt index 864f210bc8..927f375927 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCard.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/InfoCard.kt @@ -17,7 +17,6 @@ package org.meshtastic.feature.node.component import android.content.ClipData -import androidx.annotation.DrawableRes import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement @@ -42,15 +41,16 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.ClipEntry import androidx.compose.ui.platform.Clipboard import androidx.compose.ui.platform.LocalClipboard -import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.copy +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.copy import org.meshtastic.core.ui.util.thenIf @OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalFoundationApi::class) @@ -58,9 +58,9 @@ import org.meshtastic.core.ui.util.thenIf fun InfoCard( text: String, value: String, - icon: ImageVector? = null, - @DrawableRes iconRes: Int? = null, modifier: Modifier = Modifier, + icon: ImageVector? = null, + iconRes: DrawableResource? = null, rotateIcon: Float = 0f, ) { val clipboard: Clipboard = LocalClipboard.current @@ -120,6 +120,6 @@ fun InfoCard( } @Composable -internal fun DrawableInfoCard(@DrawableRes iconRes: Int, text: String, value: String, rotateIcon: Float = 0f) { +internal fun DrawableInfoCard(iconRes: DrawableResource, text: String, value: String, rotateIcon: Float = 0f) { InfoCard(iconRes = iconRes, text = text, value = value, rotateIcon = rotateIcon) } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LastHeardInfo.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LastHeardInfo.kt index 9015284fbe..8821065a04 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LastHeardInfo.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LastHeardInfo.kt @@ -20,14 +20,14 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewLightDark import org.jetbrains.compose.resources.stringResource +import org.jetbrains.compose.resources.vectorResource import org.meshtastic.core.common.util.nowSeconds -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.node_sort_last_heard -import org.meshtastic.core.ui.R +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.ic_antenna +import org.meshtastic.core.resources.node_sort_last_heard import org.meshtastic.core.ui.theme.AppTheme import org.meshtastic.core.ui.util.formatAgo @@ -40,7 +40,7 @@ fun LastHeardInfo( ) { IconInfo( modifier = modifier, - icon = ImageVector.vectorResource(id = R.drawable.ic_antenna_24), + icon = vectorResource(Res.drawable.ic_antenna), contentDescription = stringResource(Res.string.node_sort_last_heard), label = if (showLabel) stringResource(Res.string.node_sort_last_heard) else null, text = formatAgo(lastHeard), diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LinkedCoordinatesItem.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LinkedCoordinatesItem.kt index b42643955c..1098f03bbb 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LinkedCoordinatesItem.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LinkedCoordinatesItem.kt @@ -44,10 +44,10 @@ import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.util.GPSFormat import org.meshtastic.core.model.util.metersIn import org.meshtastic.core.model.util.toString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.copy -import org.meshtastic.core.strings.elevation_suffix -import org.meshtastic.core.strings.last_position_update +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.copy +import org.meshtastic.core.resources.elevation_suffix +import org.meshtastic.core.resources.last_position_update import org.meshtastic.core.ui.component.BasicListItem import org.meshtastic.core.ui.component.icon import org.meshtastic.core.ui.theme.AppTheme diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeDetailComponents.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeDetailComponents.kt index 7d3c931660..bc5b660524 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeDetailComponents.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeDetailComponents.kt @@ -53,8 +53,8 @@ import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.copy +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.copy @Composable internal fun SectionCard( diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeDetailsSection.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeDetailsSection.kt index e84892134c..1ccd6c2780 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeDetailsSection.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeDetailsSection.kt @@ -58,26 +58,26 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.DataPacket import org.meshtastic.core.model.util.formatUptime -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.copy -import org.meshtastic.core.strings.details -import org.meshtastic.core.strings.encryption_error -import org.meshtastic.core.strings.encryption_error_text -import org.meshtastic.core.strings.error -import org.meshtastic.core.strings.hops_away -import org.meshtastic.core.strings.node_id -import org.meshtastic.core.strings.node_number -import org.meshtastic.core.strings.node_sort_last_heard -import org.meshtastic.core.strings.public_key -import org.meshtastic.core.strings.role -import org.meshtastic.core.strings.rssi -import org.meshtastic.core.strings.short_name -import org.meshtastic.core.strings.snr -import org.meshtastic.core.strings.status_message -import org.meshtastic.core.strings.supported -import org.meshtastic.core.strings.uptime -import org.meshtastic.core.strings.user_id -import org.meshtastic.core.strings.via_mqtt +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.copy +import org.meshtastic.core.resources.details +import org.meshtastic.core.resources.encryption_error +import org.meshtastic.core.resources.encryption_error_text +import org.meshtastic.core.resources.error +import org.meshtastic.core.resources.hops_away +import org.meshtastic.core.resources.node_id +import org.meshtastic.core.resources.node_number +import org.meshtastic.core.resources.node_sort_last_heard +import org.meshtastic.core.resources.public_key +import org.meshtastic.core.resources.role +import org.meshtastic.core.resources.rssi +import org.meshtastic.core.resources.short_name +import org.meshtastic.core.resources.snr +import org.meshtastic.core.resources.status_message +import org.meshtastic.core.resources.supported +import org.meshtastic.core.resources.uptime +import org.meshtastic.core.resources.user_id +import org.meshtastic.core.resources.via_mqtt import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider import org.meshtastic.core.ui.icon.ArrowCircleUp import org.meshtastic.core.ui.icon.ChannelUtilization diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeFilterTextField.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeFilterTextField.kt index 1aa8a1bbb3..fa431c8988 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeFilterTextField.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeFilterTextField.kt @@ -61,18 +61,18 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.NodeSortOption -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.desc_node_filter_clear -import org.meshtastic.core.strings.node_filter_exclude_infrastructure -import org.meshtastic.core.strings.node_filter_ignored -import org.meshtastic.core.strings.node_filter_include_unknown -import org.meshtastic.core.strings.node_filter_only_direct -import org.meshtastic.core.strings.node_filter_only_online -import org.meshtastic.core.strings.node_filter_placeholder -import org.meshtastic.core.strings.node_filter_show_ignored -import org.meshtastic.core.strings.node_filter_title -import org.meshtastic.core.strings.node_sort_button -import org.meshtastic.core.strings.node_sort_title +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.desc_node_filter_clear +import org.meshtastic.core.resources.node_filter_exclude_infrastructure +import org.meshtastic.core.resources.node_filter_ignored +import org.meshtastic.core.resources.node_filter_include_unknown +import org.meshtastic.core.resources.node_filter_only_direct +import org.meshtastic.core.resources.node_filter_only_online +import org.meshtastic.core.resources.node_filter_placeholder +import org.meshtastic.core.resources.node_filter_show_ignored +import org.meshtastic.core.resources.node_filter_title +import org.meshtastic.core.resources.node_sort_button +import org.meshtastic.core.resources.node_sort_title import org.meshtastic.core.ui.theme.AppTheme @Suppress("LongParameterList") diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeItem.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeItem.kt index 31f5bddc1f..755c681754 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeItem.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeItem.kt @@ -55,15 +55,15 @@ import org.meshtastic.core.database.model.Node import org.meshtastic.core.database.model.isUnmessageableRole import org.meshtastic.core.model.util.UnitConversions.celsiusToFahrenheit import org.meshtastic.core.model.util.toDistanceString +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.air_utilization +import org.meshtastic.core.resources.channel_utilization +import org.meshtastic.core.resources.current +import org.meshtastic.core.resources.elevation_suffix +import org.meshtastic.core.resources.signal_quality +import org.meshtastic.core.resources.unknown_username +import org.meshtastic.core.resources.voltage import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.air_utilization -import org.meshtastic.core.strings.channel_utilization -import org.meshtastic.core.strings.current -import org.meshtastic.core.strings.elevation_suffix -import org.meshtastic.core.strings.signal_quality -import org.meshtastic.core.strings.unknown_username -import org.meshtastic.core.strings.voltage import org.meshtastic.core.ui.component.AirQualityInfo import org.meshtastic.core.ui.component.ChannelInfo import org.meshtastic.core.ui.component.DistanceInfo diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeStatusIcons.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeStatusIcons.kt index cd55596560..919515426f 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeStatusIcons.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeStatusIcons.kt @@ -37,16 +37,16 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.connected +import org.meshtastic.core.resources.connecting +import org.meshtastic.core.resources.device_sleeping +import org.meshtastic.core.resources.disconnected +import org.meshtastic.core.resources.favorite +import org.meshtastic.core.resources.mute_always +import org.meshtastic.core.resources.unmessageable +import org.meshtastic.core.resources.unmonitored_or_infrastructure import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.connected -import org.meshtastic.core.strings.connecting -import org.meshtastic.core.strings.device_sleeping -import org.meshtastic.core.strings.disconnected -import org.meshtastic.core.strings.favorite -import org.meshtastic.core.strings.mute_always -import org.meshtastic.core.strings.unmessageable -import org.meshtastic.core.strings.unmonitored_or_infrastructure import org.meshtastic.core.ui.icon.CloudDone import org.meshtastic.core.ui.icon.CloudOffTwoTone import org.meshtastic.core.ui.icon.CloudSync diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NotesSection.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NotesSection.kt index 5c28218b1e..257ed0566b 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NotesSection.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NotesSection.kt @@ -44,10 +44,10 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add_a_note -import org.meshtastic.core.strings.notes -import org.meshtastic.core.strings.save +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add_a_note +import org.meshtastic.core.resources.notes +import org.meshtastic.core.resources.save @Composable fun NotesSection(node: Node, onSaveNotes: (Int, String) -> Unit, modifier: Modifier = Modifier) { diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PositionSection.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PositionSection.kt index 0a09214ee2..f4fe60fcc8 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PositionSection.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PositionSection.kt @@ -48,10 +48,10 @@ import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.util.toDistanceString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.exchange_position -import org.meshtastic.core.strings.open_compass -import org.meshtastic.core.strings.position +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.exchange_position +import org.meshtastic.core.resources.open_compass +import org.meshtastic.core.resources.position import org.meshtastic.feature.node.model.LogsType import org.meshtastic.feature.node.model.MetricsState import org.meshtastic.feature.node.model.NodeDetailAction diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt index 2f7b9553fa..6927a7861c 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt @@ -27,10 +27,10 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.channel_1 -import org.meshtastic.core.strings.channel_2 -import org.meshtastic.core.strings.channel_3 +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.channel_1 +import org.meshtastic.core.resources.channel_2 +import org.meshtastic.core.resources.channel_3 import org.meshtastic.feature.node.model.VectorMetricInfo /** diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/SatelliteCountInfo.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/SatelliteCountInfo.kt index da1be0f44d..f11749d983 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/SatelliteCountInfo.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/SatelliteCountInfo.kt @@ -24,8 +24,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewLightDark import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.sats +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.sats import org.meshtastic.core.ui.theme.AppTheme @Composable diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetricActionsSection.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetricActionsSection.kt index d67141e6e2..0cee70ea87 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetricActionsSection.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetricActionsSection.kt @@ -49,12 +49,12 @@ import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.TelemetryType -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.logs -import org.meshtastic.core.strings.request_air_quality_metrics -import org.meshtastic.core.strings.request_telemetry -import org.meshtastic.core.strings.telemetry -import org.meshtastic.core.strings.userinfo +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.logs +import org.meshtastic.core.resources.request_air_quality_metrics +import org.meshtastic.core.resources.request_telemetry +import org.meshtastic.core.resources.telemetry +import org.meshtastic.core.resources.userinfo import org.meshtastic.core.ui.icon.AirQuality import org.meshtastic.core.ui.icon.LineAxis import org.meshtastic.core.ui.icon.MeshtasticIcons diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetryInfo.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetryInfo.kt index 6fa47e7e1c..12acecf9d6 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetryInfo.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/TelemetryInfo.kt @@ -33,17 +33,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.env_metrics_log -import org.meshtastic.core.strings.humidity -import org.meshtastic.core.strings.iaq -import org.meshtastic.core.strings.node_id -import org.meshtastic.core.strings.pax -import org.meshtastic.core.strings.pax_metrics_log -import org.meshtastic.core.strings.role -import org.meshtastic.core.strings.soil_moisture -import org.meshtastic.core.strings.soil_temperature -import org.meshtastic.core.strings.temperature +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.env_metrics_log +import org.meshtastic.core.resources.humidity +import org.meshtastic.core.resources.iaq +import org.meshtastic.core.resources.node_id +import org.meshtastic.core.resources.pax +import org.meshtastic.core.resources.pax_metrics_log +import org.meshtastic.core.resources.role +import org.meshtastic.core.resources.soil_moisture +import org.meshtastic.core.resources.soil_temperature +import org.meshtastic.core.resources.temperature @Composable fun TemperatureInfo( diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreen.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreen.kt index c0a380342e..24c00ff34f 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreen.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailScreen.kt @@ -61,10 +61,9 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.entity.FirmwareRelease import org.meshtastic.core.database.model.Node import org.meshtastic.core.navigation.Route -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.details -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.loading +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.details +import org.meshtastic.core.resources.loading import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.component.SharedContactDialog import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider @@ -100,20 +99,19 @@ fun NodeDetailScreen( onNavigate: (Route) -> Unit = {}, onNavigateUp: () -> Unit = {}, ) { - viewModel.start(nodeId) + LaunchedEffect(nodeId) { viewModel.start(nodeId) } val snackbarHostState = remember { SnackbarHostState() } + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + LaunchedEffect(Unit) { viewModel.effects.collect { effect -> if (effect is NodeRequestEffect.ShowFeedback) { - @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } - val uiState by viewModel.uiState.collectAsStateWithLifecycle() - NodeDetailScaffold( modifier = modifier, uiState = uiState, @@ -149,8 +147,8 @@ private fun NodeDetailScaffold( modifier = modifier, topBar = { MainAppBar( - title = getString(Res.string.details), - subtitle = node?.user?.long_name ?: "", + title = stringResource(Res.string.details), + subtitle = uiState.nodeName.asString(), ourNode = uiState.ourNode, showNodeChip = false, canNavigateUp = true, diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt index f903d8c513..2790cd327b 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt @@ -22,7 +22,6 @@ import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted @@ -31,44 +30,26 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import org.meshtastic.core.data.repository.DeviceHardwareRepository -import org.meshtastic.core.data.repository.FirmwareReleaseRepository -import org.meshtastic.core.data.repository.MeshLogRepository -import org.meshtastic.core.data.repository.NodeRepository -import org.meshtastic.core.data.repository.RadioConfigRepository -import org.meshtastic.core.database.entity.FirmwareRelease -import org.meshtastic.core.database.entity.MeshLog import org.meshtastic.core.database.model.Node import org.meshtastic.core.model.DataPacket -import org.meshtastic.core.model.MyNodeInfo -import org.meshtastic.core.model.util.hasValidEnvironmentMetrics -import org.meshtastic.core.model.util.isDirectSignal import org.meshtastic.core.navigation.NodesRoutes +import org.meshtastic.core.resources.UiText import org.meshtastic.core.service.ServiceAction import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.fallback_node_name -import org.meshtastic.core.strings.getString -import org.meshtastic.core.ui.util.toPosition import org.meshtastic.feature.node.component.NodeMenuAction +import org.meshtastic.feature.node.domain.usecase.GetNodeDetailsUseCase import org.meshtastic.feature.node.metrics.EnvironmentMetricsState import org.meshtastic.feature.node.model.LogsType import org.meshtastic.feature.node.model.MetricsState -import org.meshtastic.proto.Config -import org.meshtastic.proto.DeviceProfile -import org.meshtastic.proto.FirmwareEdition -import org.meshtastic.proto.MeshPacket -import org.meshtastic.proto.PortNum -import org.meshtastic.proto.Telemetry import javax.inject.Inject /** * UI state for the Node Details screen. * * @property node The node being viewed, or null if loading. + * @property nodeName The display name for the node, resolved in the UI. * @property ourNode Information about the locally connected node. * @property metricsState Aggregated sensor and signal metrics. * @property environmentState Standardized environmental sensor data. @@ -76,8 +57,10 @@ import javax.inject.Inject * @property lastTracerouteTime Timestamp of the last successful traceroute request. * @property lastRequestNeighborsTime Timestamp of the last successful neighbor info request. */ +@androidx.compose.runtime.Stable data class NodeDetailUiState( val node: Node? = null, + val nodeName: UiText = UiText.DynamicString(""), val ourNode: Node? = null, val metricsState: MetricsState = MetricsState.Empty, val environmentState: EnvironmentMetricsState = EnvironmentMetricsState(), @@ -93,17 +76,12 @@ data class NodeDetailUiState( @HiltViewModel class NodeDetailViewModel @Inject -@Suppress("LongParameterList") constructor( savedStateHandle: SavedStateHandle, - private val nodeRepository: NodeRepository, private val nodeManagementActions: NodeManagementActions, private val nodeRequestActions: NodeRequestActions, - private val meshLogRepository: MeshLogRepository, - private val radioConfigRepository: RadioConfigRepository, - private val deviceHardwareRepository: DeviceHardwareRepository, - private val firmwareReleaseRepository: FirmwareReleaseRepository, private val serviceRepository: ServiceRepository, + private val getNodeDetailsUseCase: GetNodeDetailsUseCase, ) : ViewModel() { private val nodeIdFromRoute: Int? = @@ -120,166 +98,10 @@ constructor( activeNodeId .flatMapLatest { nodeId -> if (nodeId == null) return@flatMapLatest flowOf(NodeDetailUiState()) - buildUiStateFlow(nodeId) + getNodeDetailsUseCase(nodeId) } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), NodeDetailUiState()) - @Suppress("LongMethod", "CyclomaticComplexMethod") - private fun buildUiStateFlow(nodeId: Int): Flow { - val nodeFlow = - nodeRepository.nodeDBbyNum - .map { it[nodeId] ?: Node.createFallback(nodeId, getString(Res.string.fallback_node_name)) } - .distinctUntilChanged() - - // 1. Logs & Metrics Data (fetches telemetry, packets, paxcount, and response history) - val metricsLogsFlow = - combine( - meshLogRepository.getTelemetryFrom(nodeId), - meshLogRepository.getMeshPacketsFrom(nodeId), - meshLogRepository.getMeshPacketsFrom(nodeId, PortNum.POSITION_APP.value), - meshLogRepository.getLogsFrom(nodeId, PortNum.PAXCOUNTER_APP.value), - meshLogRepository.getLogsFrom(nodeId, PortNum.TRACEROUTE_APP.value), - meshLogRepository.getLogsFrom(nodeId, PortNum.NEIGHBORINFO_APP.value), - ) { args: Array> -> - @Suppress("UNCHECKED_CAST") - LogsGroup( - telemetry = args[0] as List, - packets = args[1] as List, - posPackets = args[2] as List, - pax = args[3] as List, - trRes = args[4] as List, - niRes = args[5] as List, - ) - } - - // 2. Identity & Config (local device info and radio profile) - val identityFlow = - combine(nodeRepository.ourNodeInfo, nodeRepository.myNodeInfo, radioConfigRepository.deviceProfileFlow) { - ourNode, - myInfo, - profile, - -> - IdentityGroup(ourNode, myInfo?.toMyNodeInfo(), profile) - } - - // 3. Metadata & Request Timestamps (firmware versions and last request times) - val metadataFlow = - combine( - meshLogRepository.getMyNodeInfo().map { it?.firmware_edition }.distinctUntilChanged(), - firmwareReleaseRepository.stableRelease, - firmwareReleaseRepository.alphaRelease, - nodeRequestActions.lastTracerouteTimes.map { it[nodeId] }, - nodeRequestActions.lastRequestNeighborTimes.map { it[nodeId] }, - ) { args: Array -> - MetadataGroup( - edition = args[0] as? FirmwareEdition, - stable = args[1] as? FirmwareRelease, - alpha = args[2] as? FirmwareRelease, - trTime = args[3] as? Long, - niTime = args[4] as? Long, - ) - } - - // 4. Requests History (tracking traceroute and neighbor info requests sent from this device) - val requestsFlow = - combine( - meshLogRepository.getRequestLogs(nodeId, PortNum.TRACEROUTE_APP), - meshLogRepository.getRequestLogs(nodeId, PortNum.NEIGHBORINFO_APP), - ) { trReqs, niReqs -> - trReqs to niReqs - } - - // Assemble final UI state - return combine(nodeFlow, metricsLogsFlow, identityFlow, metadataFlow, requestsFlow) { - node, - logs, - identity, - metadata, - requests, - -> - val (trReqs, niReqs) = requests - val isLocal = node.num == identity.ourNode?.num - val pioEnv = if (isLocal) identity.myInfo?.pioEnv else null - val hw = deviceHardwareRepository.getDeviceHardwareByModel(node.user.hw_model.value, pioEnv).getOrNull() - - val moduleConfig = identity.profile.module_config - val displayUnits = identity.profile.config?.display?.units ?: Config.DisplayConfig.DisplayUnits.METRIC - - val metricsState = - MetricsState( - node = node, - isLocal = isLocal, - deviceHardware = hw, - reportedTarget = pioEnv, - isManaged = identity.profile.config?.security?.is_managed ?: false, - isFahrenheit = - moduleConfig?.telemetry?.environment_display_fahrenheit == true || - (displayUnits == Config.DisplayConfig.DisplayUnits.IMPERIAL), - displayUnits = displayUnits, - deviceMetrics = logs.telemetry.filter { it.device_metrics != null }, - powerMetrics = logs.telemetry.filter { it.power_metrics != null }, - hostMetrics = logs.telemetry.filter { it.host_metrics != null }, - signalMetrics = logs.packets.filter { it.isDirectSignal() }, - positionLogs = logs.posPackets.mapNotNull { it.toPosition() }, - paxMetrics = logs.pax, - tracerouteRequests = trReqs, - tracerouteResults = logs.trRes, - neighborInfoRequests = niReqs, - neighborInfoResults = logs.niRes, - firmwareEdition = metadata.edition, - latestStableFirmware = metadata.stable ?: FirmwareRelease(), - latestAlphaFirmware = metadata.alpha ?: FirmwareRelease(), - ) - - val environmentState = - EnvironmentMetricsState(environmentMetrics = logs.telemetry.filter { it.hasValidEnvironmentMetrics() }) - - val availableLogs = buildSet { - if (metricsState.hasDeviceMetrics()) add(LogsType.DEVICE) - if (metricsState.hasPositionLogs()) { - add(LogsType.NODE_MAP) - add(LogsType.POSITIONS) - } - if (environmentState.hasEnvironmentMetrics()) add(LogsType.ENVIRONMENT) - if (metricsState.hasSignalMetrics()) add(LogsType.SIGNAL) - if (metricsState.hasPowerMetrics()) add(LogsType.POWER) - if (metricsState.hasTracerouteLogs()) add(LogsType.TRACEROUTE) - if (metricsState.hasNeighborInfoLogs()) add(LogsType.NEIGHBOR_INFO) - if (metricsState.hasHostMetrics()) add(LogsType.HOST) - if (metricsState.hasPaxMetrics()) add(LogsType.PAX) - } - - NodeDetailUiState( - node = node, - ourNode = identity.ourNode, - metricsState = metricsState, - environmentState = environmentState, - availableLogs = availableLogs, - lastTracerouteTime = metadata.trTime, - lastRequestNeighborsTime = metadata.niTime, - ) - } - } - - private data class LogsGroup( - val telemetry: List, - val packets: List, - val posPackets: List, - val pax: List, - val trRes: List, - val niRes: List, - ) - - private data class IdentityGroup(val ourNode: Node?, val myInfo: MyNodeInfo?, val profile: DeviceProfile) - - private data class MetadataGroup( - val edition: FirmwareEdition?, - val stable: FirmwareRelease?, - val alpha: FirmwareRelease?, - val trTime: Long?, - val niTime: Long?, - ) - val effects: SharedFlow = nodeRequestActions.effects fun start(nodeId: Int) { diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeManagementActions.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeManagementActions.kt index 854fedf41e..a84617dae8 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeManagementActions.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeManagementActions.kt @@ -24,21 +24,21 @@ import kotlinx.coroutines.launch import org.jetbrains.compose.resources.getString import org.meshtastic.core.data.repository.NodeRepository import org.meshtastic.core.database.model.Node +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.favorite +import org.meshtastic.core.resources.favorite_add +import org.meshtastic.core.resources.favorite_remove +import org.meshtastic.core.resources.ignore +import org.meshtastic.core.resources.ignore_add +import org.meshtastic.core.resources.ignore_remove +import org.meshtastic.core.resources.mute_add +import org.meshtastic.core.resources.mute_notifications +import org.meshtastic.core.resources.mute_remove +import org.meshtastic.core.resources.remove +import org.meshtastic.core.resources.remove_node_text +import org.meshtastic.core.resources.unmute import org.meshtastic.core.service.ServiceAction import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.favorite -import org.meshtastic.core.strings.favorite_add -import org.meshtastic.core.strings.favorite_remove -import org.meshtastic.core.strings.ignore -import org.meshtastic.core.strings.ignore_add -import org.meshtastic.core.strings.ignore_remove -import org.meshtastic.core.strings.mute_add -import org.meshtastic.core.strings.mute_notifications -import org.meshtastic.core.strings.mute_remove -import org.meshtastic.core.strings.remove -import org.meshtastic.core.strings.remove_node_text -import org.meshtastic.core.strings.unmute import org.meshtastic.core.ui.util.AlertManager import javax.inject.Inject import javax.inject.Singleton diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeRequestActions.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeRequestActions.kt index eeefac9fe6..2bad12fb92 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeRequestActions.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeRequestActions.kt @@ -27,29 +27,29 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import org.jetbrains.compose.resources.StringResource import org.meshtastic.core.common.util.nowMillis import org.meshtastic.core.model.Position import org.meshtastic.core.model.TelemetryType +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.UiText +import org.meshtastic.core.resources.neighbor_info +import org.meshtastic.core.resources.position +import org.meshtastic.core.resources.request_air_quality_metrics +import org.meshtastic.core.resources.request_device_metrics +import org.meshtastic.core.resources.request_environment_metrics +import org.meshtastic.core.resources.request_host_metrics +import org.meshtastic.core.resources.request_pax_metrics +import org.meshtastic.core.resources.request_power_metrics +import org.meshtastic.core.resources.requesting_from +import org.meshtastic.core.resources.signal_quality +import org.meshtastic.core.resources.traceroute +import org.meshtastic.core.resources.user_info import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.neighbor_info -import org.meshtastic.core.strings.position -import org.meshtastic.core.strings.request_air_quality_metrics -import org.meshtastic.core.strings.request_device_metrics -import org.meshtastic.core.strings.request_environment_metrics -import org.meshtastic.core.strings.request_host_metrics -import org.meshtastic.core.strings.request_pax_metrics -import org.meshtastic.core.strings.request_power_metrics -import org.meshtastic.core.strings.requesting_from -import org.meshtastic.core.strings.signal_quality -import org.meshtastic.core.strings.traceroute -import org.meshtastic.core.strings.user_info import javax.inject.Inject import javax.inject.Singleton sealed class NodeRequestEffect { - data class ShowFeedback(val resource: StringResource, val args: List = emptyList()) : NodeRequestEffect() + data class ShowFeedback(val text: UiText) : NodeRequestEffect() } @Singleton @@ -70,7 +70,9 @@ class NodeRequestActions @Inject constructor(private val serviceRepository: Serv try { serviceRepository.meshService?.requestUserInfo(destNum) _effects.emit( - NodeRequestEffect.ShowFeedback(Res.string.requesting_from, listOf(Res.string.user_info, longName)), + NodeRequestEffect.ShowFeedback( + UiText.Resource(Res.string.requesting_from, Res.string.user_info, longName), + ), ) } catch (ex: android.os.RemoteException) { Logger.e { "Request NodeInfo error: ${ex.message}" } @@ -87,8 +89,7 @@ class NodeRequestActions @Inject constructor(private val serviceRepository: Serv _lastRequestNeighborTimes.update { it + (destNum to nowMillis) } _effects.emit( NodeRequestEffect.ShowFeedback( - Res.string.requesting_from, - listOf(Res.string.neighbor_info, longName), + UiText.Resource(Res.string.requesting_from, Res.string.neighbor_info, longName), ), ) } catch (ex: android.os.RemoteException) { @@ -108,7 +109,9 @@ class NodeRequestActions @Inject constructor(private val serviceRepository: Serv try { serviceRepository.meshService?.requestPosition(destNum, position) _effects.emit( - NodeRequestEffect.ShowFeedback(Res.string.requesting_from, listOf(Res.string.position, longName)), + NodeRequestEffect.ShowFeedback( + UiText.Resource(Res.string.requesting_from, Res.string.position, longName), + ), ) } catch (ex: android.os.RemoteException) { Logger.e { "Request position error: ${ex.message}" } @@ -134,7 +137,9 @@ class NodeRequestActions @Inject constructor(private val serviceRepository: Serv TelemetryType.PAX -> Res.string.request_pax_metrics } - _effects.emit(NodeRequestEffect.ShowFeedback(Res.string.requesting_from, listOf(typeRes, longName))) + _effects.emit( + NodeRequestEffect.ShowFeedback(UiText.Resource(Res.string.requesting_from, typeRes, longName)), + ) } catch (ex: android.os.RemoteException) { Logger.e { "Request telemetry error: ${ex.message}" } } @@ -149,7 +154,9 @@ class NodeRequestActions @Inject constructor(private val serviceRepository: Serv serviceRepository.meshService?.requestTraceroute(packetId, destNum) _lastTracerouteTimes.update { it + (destNum to nowMillis) } _effects.emit( - NodeRequestEffect.ShowFeedback(Res.string.requesting_from, listOf(Res.string.traceroute, longName)), + NodeRequestEffect.ShowFeedback( + UiText.Resource(Res.string.requesting_from, Res.string.traceroute, longName), + ), ) } catch (ex: android.os.RemoteException) { Logger.e { "Request traceroute error: ${ex.message}" } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/domain/usecase/GetFilteredNodesUseCase.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/domain/usecase/GetFilteredNodesUseCase.kt new file mode 100644 index 0000000000..1d11bad9b3 --- /dev/null +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/domain/usecase/GetFilteredNodesUseCase.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025-2026 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 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 org.meshtastic.feature.node.domain.usecase + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import org.meshtastic.core.data.repository.NodeRepository +import org.meshtastic.core.database.model.Node +import org.meshtastic.core.database.model.NodeSortOption +import org.meshtastic.feature.node.list.NodeFilterState +import org.meshtastic.feature.node.model.isEffectivelyUnmessageable +import org.meshtastic.proto.Config +import javax.inject.Inject + +class GetFilteredNodesUseCase @Inject constructor(private val nodeRepository: NodeRepository) { + @Suppress("CyclomaticComplexMethod", "LongMethod") + operator fun invoke(filter: NodeFilterState, sort: NodeSortOption): Flow> = nodeRepository + .getNodes( + sort = sort, + filter = filter.filterText, + includeUnknown = filter.includeUnknown, + onlyOnline = filter.onlyOnline, + onlyDirect = filter.onlyDirect, + ) + .map { list -> + list + .filter { node -> node.isIgnored == filter.showIgnored } + .filter { node -> + if (filter.excludeInfrastructure) { + val role = node.user.role + + @Suppress("DEPRECATION") + val infrastructureRoles = + listOf( + Config.DeviceConfig.Role.ROUTER, + Config.DeviceConfig.Role.REPEATER, + Config.DeviceConfig.Role.ROUTER_LATE, + Config.DeviceConfig.Role.CLIENT_BASE, + ) + role !in infrastructureRoles && !node.isEffectivelyUnmessageable + } else { + true + } + } + } +} diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/domain/usecase/GetNodeDetailsUseCase.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/domain/usecase/GetNodeDetailsUseCase.kt new file mode 100644 index 0000000000..665dd1af6c --- /dev/null +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/domain/usecase/GetNodeDetailsUseCase.kt @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2025-2026 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 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 org.meshtastic.feature.node.domain.usecase + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import org.meshtastic.core.data.repository.DeviceHardwareRepository +import org.meshtastic.core.data.repository.FirmwareReleaseRepository +import org.meshtastic.core.data.repository.MeshLogRepository +import org.meshtastic.core.data.repository.NodeRepository +import org.meshtastic.core.data.repository.RadioConfigRepository +import org.meshtastic.core.database.entity.FirmwareRelease +import org.meshtastic.core.database.entity.MeshLog +import org.meshtastic.core.database.model.Node +import org.meshtastic.core.model.MyNodeInfo +import org.meshtastic.core.model.util.hasValidEnvironmentMetrics +import org.meshtastic.core.model.util.isDirectSignal +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.UiText +import org.meshtastic.core.resources.fallback_node_name +import org.meshtastic.core.ui.util.toPosition +import org.meshtastic.feature.node.detail.NodeDetailUiState +import org.meshtastic.feature.node.detail.NodeRequestActions +import org.meshtastic.feature.node.metrics.EnvironmentMetricsState +import org.meshtastic.feature.node.model.LogsType +import org.meshtastic.feature.node.model.MetricsState +import org.meshtastic.proto.Config +import org.meshtastic.proto.DeviceProfile +import org.meshtastic.proto.FirmwareEdition +import org.meshtastic.proto.MeshPacket +import org.meshtastic.proto.PortNum +import org.meshtastic.proto.Telemetry +import javax.inject.Inject + +class GetNodeDetailsUseCase +@Inject +constructor( + private val nodeRepository: NodeRepository, + private val meshLogRepository: MeshLogRepository, + private val radioConfigRepository: RadioConfigRepository, + private val deviceHardwareRepository: DeviceHardwareRepository, + private val firmwareReleaseRepository: FirmwareReleaseRepository, + private val nodeRequestActions: NodeRequestActions, +) { + + @OptIn(ExperimentalCoroutinesApi::class) + @Suppress("LongMethod", "CyclomaticComplexMethod") + operator fun invoke(nodeId: Int): Flow = + nodeRepository.effectiveLogNodeId(nodeId).flatMapLatest { effectiveNodeId -> + buildFlow(nodeId, effectiveNodeId) + } + + @Suppress("LongMethod", "CyclomaticComplexMethod") + private fun buildFlow(nodeId: Int, effectiveNodeId: Int): Flow { + val nodeFlow = + nodeRepository.nodeDBbyNum.map { it[nodeId] ?: Node.createFallback(nodeId, "") }.distinctUntilChanged() + + // 1. Logs & Metrics Data + val metricsLogsFlow = + combine( + meshLogRepository.getTelemetryFrom(effectiveNodeId).onStart { emit(emptyList()) }, + meshLogRepository.getMeshPacketsFrom(effectiveNodeId).onStart { emit(emptyList()) }, + meshLogRepository.getMeshPacketsFrom(effectiveNodeId, PortNum.POSITION_APP.value).onStart { + emit(emptyList()) + }, + meshLogRepository.getLogsFrom(effectiveNodeId, PortNum.PAXCOUNTER_APP.value).onStart { + emit(emptyList()) + }, + meshLogRepository.getLogsFrom(effectiveNodeId, PortNum.TRACEROUTE_APP.value).onStart { + emit(emptyList()) + }, + meshLogRepository.getLogsFrom(effectiveNodeId, PortNum.NEIGHBORINFO_APP.value).onStart { + emit(emptyList()) + }, + ) { args: Array> -> + @Suppress("UNCHECKED_CAST") + LogsGroup( + telemetry = args[0] as List, + packets = args[1] as List, + posPackets = args[2] as List, + pax = args[3] as List, + trRes = args[4] as List, + niRes = args[5] as List, + ) + } + + // 2. Identity & Config + val identityFlow = + combine( + nodeRepository.ourNodeInfo, + nodeRepository.myNodeInfo, + radioConfigRepository.deviceProfileFlow.onStart { emit(DeviceProfile()) }, + ) { ourNode, myInfo, profile -> + IdentityGroup(ourNode, myInfo?.toMyNodeInfo(), profile) + } + + // 3. Metadata & Request Timestamps + val metadataFlow = + combine( + meshLogRepository + .getMyNodeInfo() + .map { it?.firmware_edition } + .distinctUntilChanged() + .onStart { emit(null) }, + firmwareReleaseRepository.stableRelease, + firmwareReleaseRepository.alphaRelease, + nodeRequestActions.lastTracerouteTimes.map { it[nodeId] }, + nodeRequestActions.lastRequestNeighborTimes.map { it[nodeId] }, + ) { edition, stable, alpha, trTime, niTime -> + MetadataGroup(edition = edition, stable = stable, alpha = alpha, trTime = trTime, niTime = niTime) + } + + // 4. Requests History (we still query request logs by the target nodeId) + val requestsFlow = + combine( + meshLogRepository.getRequestLogs(nodeId, PortNum.TRACEROUTE_APP).onStart { emit(emptyList()) }, + meshLogRepository.getRequestLogs(nodeId, PortNum.NEIGHBORINFO_APP).onStart { emit(emptyList()) }, + ) { trReqs, niReqs -> + trReqs to niReqs + } + + // Assemble final UI state + return combine(nodeFlow, metricsLogsFlow, identityFlow, metadataFlow, requestsFlow) { + node, + logs, + identity, + metadata, + requests, + -> + val (trReqs, niReqs) = requests + val isLocal = node.num == identity.ourNode?.num + val pioEnv = if (isLocal) identity.myInfo?.pioEnv else null + val hw = deviceHardwareRepository.getDeviceHardwareByModel(node.user.hw_model.value, pioEnv).getOrNull() + + val moduleConfig = identity.profile.module_config + val displayUnits = identity.profile.config?.display?.units ?: Config.DisplayConfig.DisplayUnits.METRIC + + val metricsState = + MetricsState( + node = node, + isLocal = isLocal, + deviceHardware = hw, + reportedTarget = pioEnv, + isManaged = identity.profile.config?.security?.is_managed ?: false, + isFahrenheit = + moduleConfig?.telemetry?.environment_display_fahrenheit == true || + (displayUnits == Config.DisplayConfig.DisplayUnits.IMPERIAL), + displayUnits = displayUnits, + deviceMetrics = logs.telemetry.filter { it.device_metrics != null }, + powerMetrics = logs.telemetry.filter { it.power_metrics != null }, + hostMetrics = logs.telemetry.filter { it.host_metrics != null }, + signalMetrics = logs.packets.filter { it.isDirectSignal() }, + positionLogs = logs.posPackets.mapNotNull { it.toPosition() }, + paxMetrics = logs.pax, + tracerouteRequests = trReqs, + tracerouteResults = logs.trRes, + neighborInfoRequests = niReqs, + neighborInfoResults = logs.niRes, + firmwareEdition = metadata.edition, + latestStableFirmware = metadata.stable ?: FirmwareRelease(), + latestAlphaFirmware = metadata.alpha ?: FirmwareRelease(), + ) + + val environmentState = + EnvironmentMetricsState(environmentMetrics = logs.telemetry.filter { it.hasValidEnvironmentMetrics() }) + + val availableLogs = buildSet { + if (metricsState.hasDeviceMetrics()) add(LogsType.DEVICE) + if (metricsState.hasPositionLogs()) { + add(LogsType.NODE_MAP) + add(LogsType.POSITIONS) + } + if (environmentState.hasEnvironmentMetrics()) add(LogsType.ENVIRONMENT) + if (metricsState.hasSignalMetrics()) add(LogsType.SIGNAL) + if (metricsState.hasPowerMetrics()) add(LogsType.POWER) + if (metricsState.hasTracerouteLogs()) add(LogsType.TRACEROUTE) + if (metricsState.hasNeighborInfoLogs()) add(LogsType.NEIGHBOR_INFO) + if (metricsState.hasHostMetrics()) add(LogsType.HOST) + if (metricsState.hasPaxMetrics()) add(LogsType.PAX) + } + + @Suppress("MagicNumber") + val nodeName = + node.user.long_name?.takeIf { it.isNotBlank() }?.let { UiText.DynamicString(it) } + ?: UiText.Resource(Res.string.fallback_node_name, node.user.id.takeLast(4)) + + NodeDetailUiState( + node = node, + nodeName = nodeName, + ourNode = identity.ourNode, + metricsState = metricsState, + environmentState = environmentState, + availableLogs = availableLogs, + lastTracerouteTime = metadata.trTime, + lastRequestNeighborsTime = metadata.niTime, + ) + } + } + + private data class LogsGroup( + val telemetry: List, + val packets: List, + val posPackets: List, + val pax: List, + val trRes: List, + val niRes: List, + ) + + private data class IdentityGroup(val ourNode: Node?, val myInfo: MyNodeInfo?, val profile: DeviceProfile) + + private data class MetadataGroup( + val edition: FirmwareEdition?, + val stable: FirmwareRelease?, + val alpha: FirmwareRelease?, + val trTime: Long?, + val niTime: Long?, + ) +} diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt index 156394d757..6b95f55fab 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt @@ -68,18 +68,18 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add_favorite +import org.meshtastic.core.resources.channel_invalid +import org.meshtastic.core.resources.ignore +import org.meshtastic.core.resources.mute_always +import org.meshtastic.core.resources.node_count_template +import org.meshtastic.core.resources.nodes +import org.meshtastic.core.resources.remove +import org.meshtastic.core.resources.remove_favorite +import org.meshtastic.core.resources.remove_ignored +import org.meshtastic.core.resources.unmute import org.meshtastic.core.service.ConnectionState -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add_favorite -import org.meshtastic.core.strings.channel_invalid -import org.meshtastic.core.strings.ignore -import org.meshtastic.core.strings.mute_always -import org.meshtastic.core.strings.node_count_template -import org.meshtastic.core.strings.nodes -import org.meshtastic.core.strings.remove -import org.meshtastic.core.strings.remove_favorite -import org.meshtastic.core.strings.remove_ignored -import org.meshtastic.core.strings.unmute import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.component.MeshtasticImportFAB import org.meshtastic.core.ui.component.ScrollToTopEvent diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListViewModel.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListViewModel.kt index 2b94935451..c90313ae7a 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListViewModel.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeListViewModel.kt @@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.meshtastic.core.data.repository.NodeRepository import org.meshtastic.core.data.repository.RadioConfigRepository @@ -39,12 +38,13 @@ import org.meshtastic.core.model.util.dispatchMeshtasticUri import org.meshtastic.core.service.ServiceRepository import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed import org.meshtastic.feature.node.detail.NodeManagementActions -import org.meshtastic.feature.node.model.isEffectivelyUnmessageable +import org.meshtastic.feature.node.domain.usecase.GetFilteredNodesUseCase import org.meshtastic.proto.ChannelSet import org.meshtastic.proto.Config import org.meshtastic.proto.SharedContact import javax.inject.Inject +@Suppress("LongParameterList") @HiltViewModel class NodeListViewModel @Inject @@ -54,6 +54,7 @@ constructor( private val radioConfigRepository: RadioConfigRepository, private val serviceRepository: ServiceRepository, val nodeManagementActions: NodeManagementActions, + private val getFilteredNodesUseCase: GetFilteredNodesUseCase, val nodeFilterPreferences: NodeFilterPreferences, ) : ViewModel() { @@ -116,35 +117,7 @@ constructor( val nodeList: StateFlow> = combine(nodeFilter, nodeSortOption, ::Pair) - .flatMapLatest { (filter, sort) -> - nodeRepository - .getNodes( - sort = sort, - filter = filter.filterText, - includeUnknown = filter.includeUnknown, - onlyOnline = filter.onlyOnline, - onlyDirect = filter.onlyDirect, - ) - .map { list -> - list - .filter { node -> node.isIgnored == filter.showIgnored } - .filter { node -> - if (filter.excludeInfrastructure) { - val role = node.user.role - val infrastructureRoles = - listOf( - Config.DeviceConfig.Role.ROUTER, - Config.DeviceConfig.Role.REPEATER, - Config.DeviceConfig.Role.ROUTER_LATE, - Config.DeviceConfig.Role.CLIENT_BASE, - ) - role !in infrastructureRoles && !node.isEffectivelyUnmessageable - } else { - true - } - } - } - } + .flatMapLatest { (filter, sort) -> getFilteredNodesUseCase.invoke(filter, sort) } .stateInWhileSubscribed(initialValue = emptyList()) val unfilteredNodeList: StateFlow> = diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt index 275a0a1eb6..ffc8b698ac 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt @@ -59,9 +59,9 @@ import kotlinx.coroutines.launch import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.TelemetryType -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.info -import org.meshtastic.core.strings.logs +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.info +import org.meshtastic.core.resources.logs import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.Refresh diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/CommonCharts.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/CommonCharts.kt index de263751d8..ee1419b029 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/CommonCharts.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/CommonCharts.kt @@ -63,12 +63,12 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.toDate import org.meshtastic.core.common.util.toInstant import org.meshtastic.core.model.util.TimeConstants -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.close -import org.meshtastic.core.strings.delete -import org.meshtastic.core.strings.info -import org.meshtastic.core.strings.rssi -import org.meshtastic.core.strings.snr +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.close +import org.meshtastic.core.resources.delete +import org.meshtastic.core.resources.info +import org.meshtastic.core.resources.rssi +import org.meshtastic.core.resources.snr import org.meshtastic.core.ui.icon.Delete import org.meshtastic.core.ui.icon.MeshtasticIcons import java.text.DateFormat diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt index 87a6c7ba8d..d7ee8782e9 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt @@ -66,16 +66,15 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.nowSeconds import org.meshtastic.core.model.TelemetryType import org.meshtastic.core.model.util.formatUptime -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.air_util_definition -import org.meshtastic.core.strings.air_utilization -import org.meshtastic.core.strings.battery -import org.meshtastic.core.strings.ch_util_definition -import org.meshtastic.core.strings.channel_utilization -import org.meshtastic.core.strings.device_metrics_log -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.uptime -import org.meshtastic.core.strings.voltage +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.air_util_definition +import org.meshtastic.core.resources.air_utilization +import org.meshtastic.core.resources.battery +import org.meshtastic.core.resources.ch_util_definition +import org.meshtastic.core.resources.channel_utilization +import org.meshtastic.core.resources.device_metrics_log +import org.meshtastic.core.resources.uptime +import org.meshtastic.core.resources.voltage import org.meshtastic.core.ui.component.MaterialBatteryInfo import org.meshtastic.core.ui.theme.AppTheme import org.meshtastic.core.ui.theme.GraphColors.Cyan @@ -141,7 +140,7 @@ fun DeviceMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigat when (effect) { is NodeRequestEffect.ShowFeedback -> { @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentCharts.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentCharts.kt index 60a034bfe6..d7ccf869c1 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentCharts.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentCharts.kt @@ -33,15 +33,15 @@ import com.patrykandpatrick.vico.compose.cartesian.data.lineSeries import com.patrykandpatrick.vico.compose.cartesian.layer.LineCartesianLayer import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLineCartesianLayer import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.baro_pressure -import org.meshtastic.core.strings.humidity -import org.meshtastic.core.strings.iaq -import org.meshtastic.core.strings.lux -import org.meshtastic.core.strings.soil_moisture -import org.meshtastic.core.strings.soil_temperature -import org.meshtastic.core.strings.temperature -import org.meshtastic.core.strings.uv_lux +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.baro_pressure +import org.meshtastic.core.resources.humidity +import org.meshtastic.core.resources.iaq +import org.meshtastic.core.resources.lux +import org.meshtastic.core.resources.soil_moisture +import org.meshtastic.core.resources.soil_temperature +import org.meshtastic.core.resources.temperature +import org.meshtastic.core.resources.uv_lux import org.meshtastic.proto.Telemetry @Suppress("MagicNumber") diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt index daa2e30abd..cffc3d383c 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt @@ -51,21 +51,20 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.nowSeconds import org.meshtastic.core.model.TelemetryType -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.current -import org.meshtastic.core.strings.env_metrics_log -import org.meshtastic.core.strings.gas_resistance -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.humidity -import org.meshtastic.core.strings.iaq -import org.meshtastic.core.strings.iaq_definition -import org.meshtastic.core.strings.lux -import org.meshtastic.core.strings.radiation -import org.meshtastic.core.strings.soil_moisture -import org.meshtastic.core.strings.soil_temperature -import org.meshtastic.core.strings.temperature -import org.meshtastic.core.strings.uv_lux -import org.meshtastic.core.strings.voltage +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.current +import org.meshtastic.core.resources.env_metrics_log +import org.meshtastic.core.resources.gas_resistance +import org.meshtastic.core.resources.humidity +import org.meshtastic.core.resources.iaq +import org.meshtastic.core.resources.iaq_definition +import org.meshtastic.core.resources.lux +import org.meshtastic.core.resources.radiation +import org.meshtastic.core.resources.soil_moisture +import org.meshtastic.core.resources.soil_temperature +import org.meshtastic.core.resources.temperature +import org.meshtastic.core.resources.uv_lux +import org.meshtastic.core.resources.voltage import org.meshtastic.core.ui.component.IaqDisplayMode import org.meshtastic.core.ui.component.IndoorAirQuality import org.meshtastic.feature.node.detail.NodeRequestEffect @@ -87,7 +86,7 @@ fun EnvironmentMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNa when (effect) { is NodeRequestEffect.ShowFeedback -> { @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt index 6bd7df2e61..c870b5e2ce 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt @@ -59,13 +59,12 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.nowSeconds import org.meshtastic.core.model.TelemetryType import org.meshtastic.core.model.util.formatUptime -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.disk_free_indexed -import org.meshtastic.core.strings.free_memory -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.load_indexed -import org.meshtastic.core.strings.uptime -import org.meshtastic.core.strings.user_string +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.disk_free_indexed +import org.meshtastic.core.resources.free_memory +import org.meshtastic.core.resources.load_indexed +import org.meshtastic.core.resources.uptime +import org.meshtastic.core.resources.user_string import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.icon.DataArray import org.meshtastic.core.ui.icon.MeshtasticIcons @@ -88,7 +87,7 @@ fun HostMetricsLogScreen(metricsViewModel: MetricsViewModel = hiltViewModel(), o when (effect) { is NodeRequestEffect.ShowFeedback -> { @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt index ffd5576940..0f3e2820b5 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt @@ -27,18 +27,17 @@ import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute import co.touchlab.kermit.Logger import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -46,11 +45,8 @@ import org.jetbrains.compose.resources.StringResource import org.meshtastic.core.common.util.nowSeconds import org.meshtastic.core.common.util.toDate import org.meshtastic.core.common.util.toInstant -import org.meshtastic.core.data.repository.DeviceHardwareRepository -import org.meshtastic.core.data.repository.FirmwareReleaseRepository import org.meshtastic.core.data.repository.MeshLogRepository import org.meshtastic.core.data.repository.NodeRepository -import org.meshtastic.core.data.repository.RadioConfigRepository import org.meshtastic.core.data.repository.TracerouteSnapshotRepository import org.meshtastic.core.database.entity.MeshLog import org.meshtastic.core.database.model.Node @@ -58,26 +54,21 @@ import org.meshtastic.core.di.CoroutineDispatchers import org.meshtastic.core.model.TelemetryType import org.meshtastic.core.model.evaluateTracerouteMapAvailability import org.meshtastic.core.model.util.UnitConversions -import org.meshtastic.core.model.util.hasValidEnvironmentMetrics import org.meshtastic.core.navigation.NodesRoutes +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.okay +import org.meshtastic.core.resources.traceroute +import org.meshtastic.core.resources.view_on_map import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.fallback_node_name -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.okay -import org.meshtastic.core.strings.traceroute -import org.meshtastic.core.strings.view_on_map import org.meshtastic.core.ui.util.AlertManager import org.meshtastic.core.ui.util.toMessageRes -import org.meshtastic.core.ui.util.toPosition import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed import org.meshtastic.feature.map.model.TracerouteOverlay import org.meshtastic.feature.node.detail.NodeRequestActions import org.meshtastic.feature.node.detail.NodeRequestEffect +import org.meshtastic.feature.node.domain.usecase.GetNodeDetailsUseCase import org.meshtastic.feature.node.model.MetricsState import org.meshtastic.feature.node.model.TimeFrame -import org.meshtastic.proto.Config -import org.meshtastic.proto.MeshPacket import org.meshtastic.proto.PortNum import org.meshtastic.proto.Telemetry import java.io.BufferedWriter @@ -89,8 +80,6 @@ import java.util.Locale import javax.inject.Inject import org.meshtastic.proto.Paxcount as ProtoPaxcount -private fun MeshPacket.hasValidSignal(): Boolean = rx_time > 0 && (rx_snr != 0f || rx_rssi != 0) - /** * ViewModel responsible for managing and graphing metrics (telemetry, signal strength, paxcount) for a specific node. */ @@ -103,85 +92,38 @@ constructor( private val app: Application, private val dispatchers: CoroutineDispatchers, private val meshLogRepository: MeshLogRepository, - private val radioConfigRepository: RadioConfigRepository, private val serviceRepository: ServiceRepository, private val nodeRepository: NodeRepository, private val tracerouteSnapshotRepository: TracerouteSnapshotRepository, - private val deviceHardwareRepository: DeviceHardwareRepository, - private val firmwareReleaseRepository: FirmwareReleaseRepository, private val nodeRequestActions: NodeRequestActions, private val alertManager: AlertManager, + private val getNodeDetailsUseCase: GetNodeDetailsUseCase, ) : ViewModel() { - private var destNum: Int? = + + private val nodeIdFromRoute: Int? = runCatching { savedStateHandle.toRoute().destNum }.getOrNull() - private var jobs: Job? = null + private val manualNodeId = MutableStateFlow(null) + private val activeNodeId = + combine(MutableStateFlow(nodeIdFromRoute), manualNodeId) { fromRoute, manual -> manual ?: fromRoute } private val tracerouteOverlayCache = MutableStateFlow>(emptyMap()) - fun getUser(nodeNum: Int) = nodeRepository.getUser(nodeNum) - - fun deleteLog(uuid: String) = viewModelScope.launch(dispatchers.io) { meshLogRepository.deleteLog(uuid) } - - /** Returns the map overlay for a specific traceroute request ID. */ - fun getTracerouteOverlay(requestId: Int): TracerouteOverlay? { - val cached = tracerouteOverlayCache.value[requestId] - if (cached != null) return cached - - val overlay = - serviceRepository.tracerouteResponse.value - ?.takeIf { it.requestId == requestId } - ?.let { response -> - TracerouteOverlay( - requestId = response.requestId, - forwardRoute = response.forwardRoute, - returnRoute = response.returnRoute, - ) - } - ?.takeIf { it.hasRoutes } - - if (overlay != null) { - tracerouteOverlayCache.update { it + (requestId to overlay) } - } - - return overlay - } - - fun tracerouteSnapshotPositions(logUuid: String) = tracerouteSnapshotRepository.getSnapshotPositions(logUuid) - - fun clearTracerouteResponse() = serviceRepository.clearTracerouteResponse() - - fun positionedNodeNums(): Set = - nodeRepository.nodeDBbyNum.value.values.filter { it.validPosition != null }.numSet() - - private fun List.numSet(): Set = map { it.num }.toSet() - - init { - viewModelScope.launch { - serviceRepository.tracerouteResponse.filterNotNull().collect { response -> - val overlay = - TracerouteOverlay( - requestId = response.requestId, - forwardRoute = response.forwardRoute, - returnRoute = response.returnRoute, - ) - if (overlay.hasRoutes) { - tracerouteOverlayCache.update { it + (response.requestId to overlay) } - } + val state: StateFlow = + activeNodeId + .flatMapLatest { nodeId -> + if (nodeId == null) return@flatMapLatest flowOf(MetricsState.Empty) + getNodeDetailsUseCase(nodeId).map { it.metricsState } } - } - } - - fun clearPosition() = viewModelScope.launch(dispatchers.io) { - destNum?.let { meshLogRepository.deleteLogs(it, PortNum.POSITION_APP.value) } - } - - private val _state = MutableStateFlow(MetricsState.Empty) - - /** Current aggregated metrics state, including signal history and sensor logs. */ - val state: StateFlow = _state + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), MetricsState.Empty) - private val environmentState = MutableStateFlow(EnvironmentMetricsState()) + private val environmentState: StateFlow = + activeNodeId + .flatMapLatest { nodeId -> + if (nodeId == null) return@flatMapLatest flowOf(EnvironmentMetricsState()) + getNodeDetailsUseCase(nodeId).map { it.environmentState } + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), EnvironmentMetricsState()) private val _timeFrame = MutableStateFlow(TimeFrame.TWENTY_FOUR_HOURS) @@ -190,8 +132,8 @@ constructor( /** Returns the list of time frames that are actually available based on the oldest data point. */ val availableTimeFrames: StateFlow> = - combine(_state, environmentState) { state, envState -> - val stateOldest = state.oldestTimestampSeconds() + combine(state, environmentState) { currentState, envState -> + val stateOldest = currentState.oldestTimestampSeconds() val envOldest = envState.environmentMetrics.minOfOrNull { (it.time ?: 0).toLong() }?.takeIf { it > 0 } val oldest = listOfNotNull(stateOldest, envOldest).minOrNull() ?: nowSeconds TimeFrame.entries.filter { it.isAvailable(oldest) } @@ -204,10 +146,10 @@ constructor( /** Exposes filtered and unit-converted environment metrics for the UI. */ val filteredEnvironmentMetrics: StateFlow> = - combine(environmentState, _timeFrame, _state) { envState, timeFrame, state -> + combine(environmentState, _timeFrame, state) { envState, timeFrame, currentState -> val threshold = timeFrame.timeThreshold() val data = envState.environmentMetrics.filter { (it.time ?: 0).toLong() >= threshold } - if (state.isFahrenheit) { + if (currentState.isFahrenheit) { data.map { telemetry -> val em = telemetry.environment_metrics ?: return@map telemetry telemetry.copy( @@ -233,9 +175,9 @@ constructor( /** Exposes filtered and decoded pax metrics for the UI. */ val filteredPaxMetrics: StateFlow>> = - combine(_state, _timeFrame) { state, timeFrame -> + combine(state, _timeFrame) { currentState, timeFrame -> val threshold = timeFrame.timeThreshold() - state.paxMetrics + currentState.paxMetrics .filter { (it.received_date / 1000) >= threshold } .mapNotNull { log -> decodePaxFromLog(log)?.let { log to it } } } @@ -244,29 +186,92 @@ constructor( val effects: SharedFlow = nodeRequestActions.effects val lastTraceRouteTime: StateFlow = - nodeRequestActions.lastTracerouteTimes.map { it[destNum] }.stateInWhileSubscribed(null) + combine(nodeRequestActions.lastTracerouteTimes, activeNodeId) { map, id -> id?.let { map[it] } } + .stateInWhileSubscribed(null) val lastRequestNeighborsTime: StateFlow = - nodeRequestActions.lastRequestNeighborTimes.map { it[destNum] }.stateInWhileSubscribed(null) + combine(nodeRequestActions.lastRequestNeighborTimes, activeNodeId) { map, id -> id?.let { map[it] } } + .stateInWhileSubscribed(null) + + fun getUser(nodeNum: Int) = nodeRepository.getUser(nodeNum) + + fun deleteLog(uuid: String) = viewModelScope.launch(dispatchers.io) { meshLogRepository.deleteLog(uuid) } + + fun getTracerouteOverlay(requestId: Int): TracerouteOverlay? { + val cached = tracerouteOverlayCache.value[requestId] + if (cached != null) return cached + + val overlay = + serviceRepository.tracerouteResponse.value + ?.takeIf { it.requestId == requestId } + ?.let { response -> + TracerouteOverlay( + requestId = response.requestId, + forwardRoute = response.forwardRoute, + returnRoute = response.returnRoute, + ) + } + ?.takeIf { it.hasRoutes } + + if (overlay != null) { + tracerouteOverlayCache.update { it + (requestId to overlay) } + } + + return overlay + } + + fun tracerouteSnapshotPositions(logUuid: String) = tracerouteSnapshotRepository.getSnapshotPositions(logUuid) + + fun clearTracerouteResponse() = serviceRepository.clearTracerouteResponse() + + fun positionedNodeNums(): Set = + nodeRepository.nodeDBbyNum.value.values.filter { it.validPosition != null }.numSet() + + private fun List.numSet(): Set = map { it.num }.toSet() + + init { + viewModelScope.launch { + serviceRepository.tracerouteResponse.filterNotNull().collect { response -> + val overlay = + TracerouteOverlay( + requestId = response.requestId, + forwardRoute = response.forwardRoute, + returnRoute = response.returnRoute, + ) + if (overlay.hasRoutes) { + tracerouteOverlayCache.update { it + (response.requestId to overlay) } + } + } + } + Logger.d { "MetricsViewModel created" } + } + + fun clearPosition() = viewModelScope.launch(dispatchers.io) { + (manualNodeId.value ?: nodeIdFromRoute)?.let { + meshLogRepository.deleteLogs(it, PortNum.POSITION_APP.value) + } + } fun requestPosition() { - destNum?.let { nodeRequestActions.requestPosition(viewModelScope, it, state.value.node?.user?.long_name ?: "") } + (manualNodeId.value ?: nodeIdFromRoute)?.let { + nodeRequestActions.requestPosition(viewModelScope, it, state.value.node?.user?.long_name ?: "") + } } fun requestTelemetry(type: TelemetryType) { - destNum?.let { + (manualNodeId.value ?: nodeIdFromRoute)?.let { nodeRequestActions.requestTelemetry(viewModelScope, it, state.value.node?.user?.long_name ?: "", type) } } fun requestTraceroute() { - destNum?.let { + (manualNodeId.value ?: nodeIdFromRoute)?.let { nodeRequestActions.requestTraceroute(viewModelScope, it, state.value.node?.user?.long_name ?: "") } } fun requestNeighborInfo() { - destNum?.let { + (manualNodeId.value ?: nodeIdFromRoute)?.let { nodeRequestActions.requestNeighborInfo(viewModelScope, it, state.value.node?.user?.long_name ?: "") } } @@ -278,7 +283,6 @@ constructor( ) } - /** Shows the detail dialog for a traceroute result, with an option to view on the map. */ fun showTracerouteDetail( annotatedMessage: AnnotatedString, requestId: Int, @@ -318,188 +322,17 @@ constructor( } } - init { - initializeFlows() - } - fun setNodeId(id: Int) { - if (destNum != id) { - destNum = id - initializeFlows() + if (manualNodeId.value != id) { + manualNodeId.value = id } } - @Suppress("LongMethod", "CyclomaticComplexMethod") - private fun initializeFlows() { - jobs?.cancel() - val currentDestNum = destNum - jobs = - viewModelScope.launch { - if (currentDestNum != null) { - val logNodeIdFlow = nodeRepository.effectiveLogNodeId(currentDestNum) - - launch { - combine(nodeRepository.nodeDBbyNum, nodeRepository.myNodeInfo) { nodes, myInfo -> - nodes[currentDestNum] to (nodes.keys.firstOrNull() to myInfo) - } - .distinctUntilChanged() - .collect { (node, localData) -> - val (ourNodeNum, myInfo) = localData - // Create a fallback node if not found in database (for hidden clients, etc.) - val actualNode = - node - ?: Node.createFallback(currentDestNum, getString(Res.string.fallback_node_name)) - val pioEnv = if (currentDestNum == ourNodeNum) myInfo?.pioEnv else null - val hwModel = actualNode.user.hw_model.value - val deviceHardware = - deviceHardwareRepository.getDeviceHardwareByModel(hwModel, target = pioEnv) - - _state.update { state -> - state.copy( - node = actualNode, - isLocal = currentDestNum == ourNodeNum, - deviceHardware = deviceHardware.getOrNull(), - reportedTarget = pioEnv, - ) - } - } - } - - launch { - radioConfigRepository.deviceProfileFlow.collect { profile -> - val moduleConfig = profile.module_config - val displayUnits = profile.config?.display?.units - _state.update { state -> - state.copy( - isManaged = profile.config?.security?.is_managed ?: false, - isFahrenheit = - moduleConfig?.telemetry?.environment_display_fahrenheit == true || - (displayUnits == Config.DisplayConfig.DisplayUnits.IMPERIAL), - displayUnits = displayUnits ?: Config.DisplayConfig.DisplayUnits.METRIC, - ) - } - } - } - - launch { - logNodeIdFlow - .flatMapLatest { meshLogRepository.getTelemetryFrom(it) } - .collect { telemetry -> - val device = mutableListOf() - val power = mutableListOf() - val host = mutableListOf() - val env = mutableListOf() - - for (item in telemetry) { - if (item.device_metrics != null) device.add(item) - if (item.power_metrics != null) power.add(item) - if (item.host_metrics != null) host.add(item) - if (item.hasValidEnvironmentMetrics()) env.add(item) - } - - _state.update { state -> - state.copy(deviceMetrics = device, powerMetrics = power, hostMetrics = host) - } - environmentState.update { it.copy(environmentMetrics = env) } - } - } - - launch { - logNodeIdFlow - .flatMapLatest { meshLogRepository.getMeshPacketsFrom(it) } - .collect { meshPackets -> - _state.update { state -> - state.copy(signalMetrics = meshPackets.filter { it.hasValidSignal() }) - } - } - } - - launch { - combine( - meshLogRepository.getRequestLogs(currentDestNum, PortNum.TRACEROUTE_APP), - logNodeIdFlow.flatMapLatest { - meshLogRepository.getLogsFrom(it, PortNum.TRACEROUTE_APP.value) - }, - ) { request, response -> - _state.update { state -> - state.copy(tracerouteRequests = request, tracerouteResults = response) - } - } - .collect {} - } - - launch { - combine( - meshLogRepository.getRequestLogs(currentDestNum, PortNum.NEIGHBORINFO_APP), - logNodeIdFlow.flatMapLatest { - meshLogRepository.getLogsFrom(it, PortNum.NEIGHBORINFO_APP.value) - }, - ) { request, response -> - _state.update { state -> - state.copy(neighborInfoRequests = request, neighborInfoResults = response) - } - } - .collect {} - } - - launch { - logNodeIdFlow - .flatMapLatest { meshLogRepository.getMeshPacketsFrom(it, PortNum.POSITION_APP.value) } - .collect { packets -> - val distinctPositions = - packets - .mapNotNull { it.toPosition() } - .asFlow() - .distinctUntilChanged { old, new -> - old.time == new.time || - (old.latitude_i == new.latitude_i && old.longitude_i == new.longitude_i) - } - .toList() - _state.update { state -> state.copy(positionLogs = distinctPositions) } - } - } - - launch { - logNodeIdFlow - .flatMapLatest { meshLogRepository.getLogsFrom(it, PortNum.PAXCOUNTER_APP.value) } - .collect { logs -> _state.update { state -> state.copy(paxMetrics = logs) } } - } - - launch { - firmwareReleaseRepository.stableRelease.filterNotNull().collect { latestStable -> - _state.update { state -> state.copy(latestStableFirmware = latestStable) } - } - } - - launch { - firmwareReleaseRepository.alphaRelease.filterNotNull().collect { latestAlpha -> - _state.update { state -> state.copy(latestAlphaFirmware = latestAlpha) } - } - } - - launch { - meshLogRepository - .getMyNodeInfo() - .map { it?.firmware_edition } - .distinctUntilChanged() - .collect { firmwareEdition -> - _state.update { state -> state.copy(firmwareEdition = firmwareEdition) } - } - } - - Logger.d { "MetricsViewModel created" } - } else { - Logger.d { "MetricsViewModel: destNum is null, skipping metrics flows initialization." } - } - } - } - override fun onCleared() { super.onCleared() Logger.d { "MetricsViewModel cleared" } } - /** Write the persisted Position data out to a CSV file in the specified location. */ fun savePositionCSV(uri: Uri) = viewModelScope.launch(dispatchers.main) { val positions = state.value.positionLogs writeToUri(uri) { writer -> @@ -518,7 +351,6 @@ constructor( val speed = position.ground_speed val heading = "%.2f".format((position.ground_track ?: 0) * 1e-5) - // date,time,latitude,longitude,altitude,satsInView,speed,heading writer.appendLine( "$rxDateTime,\"$latitude\",\"$longitude\",\"$altitude\",\"$satsInView\",\"$speed\",\"$heading\"", ) @@ -541,12 +373,10 @@ constructor( @Suppress("MagicNumber", "CyclomaticComplexMethod", "ReturnCount") fun decodePaxFromLog(log: MeshLog): ProtoPaxcount? { - // First, try to parse from the binary fromRadio field (robust, like telemetry) try { val packet = log.fromRadio.packet val decoded = packet?.decoded if (packet != null && decoded != null && decoded.portnum == PortNum.PAXCOUNTER_APP) { - // Requests for paxcount (want_response = true) should not be logged as data points. if (decoded.want_response == true) return null val pax = ProtoPaxcount.ADAPTER.decode(decoded.payload) if ((pax.ble ?: 0) != 0 || (pax.wifi ?: 0) != 0 || (pax.uptime ?: 0) != 0) return pax @@ -554,10 +384,9 @@ constructor( } catch (e: IOException) { Logger.e(e) { "Failed to parse Paxcount from binary data" } } - // Fallback: Attempt to parse Paxcount from raw_message as base64 or hex string. try { val base64 = log.raw_message.trim() - if (base64.matches(Regex("^[A-Za-z0-9+/=\r\n]+$"))) { + if (base64.matches(Regex("^[A-Za-z0-9+/=\\r\\n]+$"))) { val bytes = android.util.Base64.decode(base64, android.util.Base64.DEFAULT) return ProtoPaxcount.ADAPTER.decode(bytes) } else if (base64.matches(Regex("^[0-9a-fA-F]+$")) && base64.length % 2 == 0) { diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/NeighborInfoLog.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/NeighborInfoLog.kt index ba8eddf859..6b9dc777f7 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/NeighborInfoLog.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/NeighborInfoLog.kt @@ -45,10 +45,9 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.getNeighborInfoResponse -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.neighbor_info -import org.meshtastic.core.strings.routing_error_no_response +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.neighbor_info +import org.meshtastic.core.resources.routing_error_no_response import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.icon.Groups import org.meshtastic.core.ui.icon.MeshtasticIcons @@ -77,7 +76,7 @@ fun NeighborInfoLogScreen( when (effect) { is NodeRequestEffect.ShowFeedback -> { @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } @@ -137,6 +136,7 @@ fun NeighborInfoLogScreen( ) val text = if (result != null) "Success" else stringResource(Res.string.routing_error_no_response) val icon = if (result != null) MeshtasticIcons.Groups else MeshtasticIcons.PersonOff + val header = stringResource(Res.string.neighbor_info) var expanded by remember { mutableStateOf(false) } Box { @@ -149,10 +149,7 @@ fun NeighborInfoLogScreen( result ?.fromRadio ?.packet - ?.getNeighborInfoResponse( - ::getUsername, - header = getString(Res.string.neighbor_info), - ) + ?.getNeighborInfoResponse(::getUsername, header = header) ?.let { val message = annotateNeighborInfo( diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt index e8ec78445d..f566fd088d 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt @@ -59,14 +59,13 @@ import org.meshtastic.core.common.util.toInstant import org.meshtastic.core.database.entity.MeshLog import org.meshtastic.core.model.TelemetryType import org.meshtastic.core.model.util.formatUptime -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.ble_devices -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.no_pax_metrics_logs -import org.meshtastic.core.strings.pax -import org.meshtastic.core.strings.pax_metrics_log -import org.meshtastic.core.strings.uptime -import org.meshtastic.core.strings.wifi_devices +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.ble_devices +import org.meshtastic.core.resources.no_pax_metrics_logs +import org.meshtastic.core.resources.pax +import org.meshtastic.core.resources.pax_metrics_log +import org.meshtastic.core.resources.uptime +import org.meshtastic.core.resources.wifi_devices import org.meshtastic.core.ui.component.IconInfo import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.Paxcount @@ -189,7 +188,7 @@ fun PaxMetricsScreen(metricsViewModel: MetricsViewModel = hiltViewModel(), onNav when (effect) { is NodeRequestEffect.ShowFeedback -> { @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PositionLog.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PositionLog.kt index 9cada14159..55d7939575 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PositionLog.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PositionLog.kt @@ -65,17 +65,16 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.nowSeconds import org.meshtastic.core.model.util.metersIn import org.meshtastic.core.model.util.toString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.alt -import org.meshtastic.core.strings.clear -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.heading -import org.meshtastic.core.strings.latitude -import org.meshtastic.core.strings.longitude -import org.meshtastic.core.strings.sats -import org.meshtastic.core.strings.save -import org.meshtastic.core.strings.speed -import org.meshtastic.core.strings.timestamp +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.alt +import org.meshtastic.core.resources.clear +import org.meshtastic.core.resources.heading +import org.meshtastic.core.resources.latitude +import org.meshtastic.core.resources.longitude +import org.meshtastic.core.resources.sats +import org.meshtastic.core.resources.save +import org.meshtastic.core.resources.speed +import org.meshtastic.core.resources.timestamp import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.icon.Delete import org.meshtastic.core.ui.icon.MeshtasticIcons @@ -182,7 +181,7 @@ fun PositionLogScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigateU when (effect) { is NodeRequestEffect.ShowFeedback -> { @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt index 7906cc3b8c..bdd89a0593 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt @@ -64,14 +64,13 @@ import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLineCartesianLa import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.TelemetryType -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.channel_1 -import org.meshtastic.core.strings.channel_2 -import org.meshtastic.core.strings.channel_3 -import org.meshtastic.core.strings.current -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.power_metrics_log -import org.meshtastic.core.strings.voltage +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.channel_1 +import org.meshtastic.core.resources.channel_2 +import org.meshtastic.core.resources.channel_3 +import org.meshtastic.core.resources.current +import org.meshtastic.core.resources.power_metrics_log +import org.meshtastic.core.resources.voltage import org.meshtastic.core.ui.theme.GraphColors.Gold import org.meshtastic.core.ui.theme.GraphColors.InfantryBlue import org.meshtastic.feature.node.detail.NodeRequestEffect @@ -121,7 +120,7 @@ fun PowerMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigate when (effect) { is NodeRequestEffect.ShowFeedback -> { @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt index 2a6eea13b3..0cee152ce3 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt @@ -58,13 +58,12 @@ import com.patrykandpatrick.vico.compose.cartesian.data.lineSeries import com.patrykandpatrick.vico.compose.cartesian.layer.LineCartesianLayer import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLineCartesianLayer import org.meshtastic.core.model.TelemetryType -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.rssi -import org.meshtastic.core.strings.rssi_definition -import org.meshtastic.core.strings.signal_quality -import org.meshtastic.core.strings.snr -import org.meshtastic.core.strings.snr_definition +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.rssi +import org.meshtastic.core.resources.rssi_definition +import org.meshtastic.core.resources.signal_quality +import org.meshtastic.core.resources.snr +import org.meshtastic.core.resources.snr_definition import org.meshtastic.core.ui.component.LoraSignalIndicator import org.meshtastic.core.ui.theme.GraphColors.Blue import org.meshtastic.core.ui.theme.GraphColors.Green @@ -98,7 +97,7 @@ fun SignalMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigat when (effect) { is NodeRequestEffect.ShowFeedback -> { @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt index 81da6af260..0846fa7568 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt @@ -52,18 +52,17 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.nowMillis import org.meshtastic.core.model.fullRouteDiscovery import org.meshtastic.core.model.getTracerouteResponse -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.routing_error_no_response -import org.meshtastic.core.strings.traceroute -import org.meshtastic.core.strings.traceroute_diff -import org.meshtastic.core.strings.traceroute_direct -import org.meshtastic.core.strings.traceroute_duration -import org.meshtastic.core.strings.traceroute_hops -import org.meshtastic.core.strings.traceroute_log -import org.meshtastic.core.strings.traceroute_route_back_to_us -import org.meshtastic.core.strings.traceroute_route_towards_dest -import org.meshtastic.core.strings.traceroute_time_and_text +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.routing_error_no_response +import org.meshtastic.core.resources.traceroute +import org.meshtastic.core.resources.traceroute_diff +import org.meshtastic.core.resources.traceroute_direct +import org.meshtastic.core.resources.traceroute_duration +import org.meshtastic.core.resources.traceroute_hops +import org.meshtastic.core.resources.traceroute_log +import org.meshtastic.core.resources.traceroute_route_back_to_us +import org.meshtastic.core.resources.traceroute_route_towards_dest +import org.meshtastic.core.resources.traceroute_time_and_text import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.icon.Group import org.meshtastic.core.ui.icon.MeshtasticIcons @@ -98,7 +97,7 @@ fun TracerouteLogScreen( when (effect) { is NodeRequestEffect.ShowFeedback -> { @Suppress("SpreadOperator") - snackbarHostState.showSnackbar(getString(effect.resource, *effect.args.toTypedArray())) + snackbarHostState.showSnackbar(effect.text.resolve()) } } } @@ -142,6 +141,8 @@ fun TracerouteLogScreen( contentPadding = PaddingValues(horizontal = 16.dp), ) { items(state.tracerouteRequests, key = { it.uuid }) { log -> + val headerTowardsStr = stringResource(Res.string.traceroute_route_towards_dest) + val headerBackStr = stringResource(Res.string.traceroute_route_back_to_us) val result = remember(state.tracerouteRequests, log.fromRadio.packet?.id) { state.tracerouteResults.find { @@ -169,7 +170,7 @@ fun TracerouteLogScreen( res.fromRadio.packet?.getTracerouteResponse( ::getUsername, headerTowards = stringResource(Res.string.traceroute_route_towards_dest), - headerBack = stringResource(Res.string.traceroute_route_back_to_us), + headerBack = headerBackStr, ), statusGreen = statusGreen, statusYellow = statusYellow, @@ -186,7 +187,7 @@ fun TracerouteLogScreen( ?.getTracerouteResponse( ::getUsername, headerTowards = stringResource(Res.string.traceroute_route_towards_dest), - headerBack = stringResource(Res.string.traceroute_route_back_to_us), + headerBack = headerBackStr, ) ?.let { AnnotatedString(it) } } @@ -214,8 +215,8 @@ fun TracerouteLogScreen( ?.packet ?.getTracerouteResponse( ::getUsername, - headerTowards = getString(Res.string.traceroute_route_towards_dest), - headerBack = getString(Res.string.traceroute_route_back_to_us), + headerTowards = headerTowardsStr, + headerBack = headerBackStr, ) ?.let { annotateTraceroute( diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteMapScreen.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteMapScreen.kt index 792b478acc..c42d570354 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteMapScreen.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/TracerouteMapScreen.kt @@ -43,11 +43,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.flow.flowOf import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.fullRouteDiscovery -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.traceroute -import org.meshtastic.core.strings.traceroute_outgoing_route -import org.meshtastic.core.strings.traceroute_return_route -import org.meshtastic.core.strings.traceroute_showing_nodes +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.traceroute +import org.meshtastic.core.resources.traceroute_outgoing_route +import org.meshtastic.core.resources.traceroute_return_route +import org.meshtastic.core.resources.traceroute_showing_nodes import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.Route diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/LogsType.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/LogsType.kt index 502eac84da..930a7b826a 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/LogsType.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/LogsType.kt @@ -29,17 +29,17 @@ import androidx.compose.ui.graphics.vector.ImageVector import org.jetbrains.compose.resources.StringResource import org.meshtastic.core.navigation.NodeDetailRoutes import org.meshtastic.core.navigation.Route -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.device_metrics_log -import org.meshtastic.core.strings.env_metrics_log -import org.meshtastic.core.strings.host_metrics_log -import org.meshtastic.core.strings.neighbor_info -import org.meshtastic.core.strings.node_map -import org.meshtastic.core.strings.pax_metrics_log -import org.meshtastic.core.strings.position_log -import org.meshtastic.core.strings.power_metrics_log -import org.meshtastic.core.strings.signal_quality -import org.meshtastic.core.strings.traceroute_log +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.device_metrics_log +import org.meshtastic.core.resources.env_metrics_log +import org.meshtastic.core.resources.host_metrics_log +import org.meshtastic.core.resources.neighbor_info +import org.meshtastic.core.resources.node_map +import org.meshtastic.core.resources.pax_metrics_log +import org.meshtastic.core.resources.position_log +import org.meshtastic.core.resources.power_metrics_log +import org.meshtastic.core.resources.signal_quality +import org.meshtastic.core.resources.traceroute_log import org.meshtastic.core.ui.icon.MeshtasticIcons import org.meshtastic.core.ui.icon.Paxcount import org.meshtastic.core.ui.icon.Route diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/MetricInfo.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/MetricInfo.kt index fd612abcdb..24d739fc5b 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/MetricInfo.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/MetricInfo.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,11 +14,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.node.model -import androidx.annotation.DrawableRes import androidx.compose.ui.graphics.vector.ImageVector +import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.StringResource internal data class VectorMetricInfo( @@ -31,6 +30,6 @@ internal data class VectorMetricInfo( internal data class DrawableMetricInfo( val label: StringResource, val value: String, - @DrawableRes val icon: Int, + val icon: DrawableResource, val rotateIcon: Float = 0f, ) diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/TimeFrame.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/TimeFrame.kt index e26881b4c4..b775574f01 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/TimeFrame.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/model/TimeFrame.kt @@ -18,13 +18,13 @@ package org.meshtastic.feature.node.model import org.jetbrains.compose.resources.StringResource import org.meshtastic.core.common.util.nowSeconds -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.all_time -import org.meshtastic.core.strings.one_hour_short -import org.meshtastic.core.strings.one_month -import org.meshtastic.core.strings.one_week -import org.meshtastic.core.strings.twenty_four_hours -import org.meshtastic.core.strings.two_weeks +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.all_time +import org.meshtastic.core.resources.one_hour_short +import org.meshtastic.core.resources.one_month +import org.meshtastic.core.resources.one_week +import org.meshtastic.core.resources.twenty_four_hours +import org.meshtastic.core.resources.two_weeks @Suppress("MagicNumber") enum class TimeFrame(val strRes: StringResource, val seconds: Long) { diff --git a/feature/node/src/test/kotlin/org/meshtastic/feature/node/domain/usecase/GetFilteredNodesUseCaseTest.kt b/feature/node/src/test/kotlin/org/meshtastic/feature/node/domain/usecase/GetFilteredNodesUseCaseTest.kt new file mode 100644 index 0000000000..1ddfba0f37 --- /dev/null +++ b/feature/node/src/test/kotlin/org/meshtastic/feature/node/domain/usecase/GetFilteredNodesUseCaseTest.kt @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025-2026 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 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 org.meshtastic.feature.node.domain.usecase + +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.meshtastic.core.data.repository.NodeRepository +import org.meshtastic.core.database.model.Node +import org.meshtastic.core.database.model.NodeSortOption +import org.meshtastic.feature.node.list.NodeFilterState +import org.meshtastic.proto.Config +import org.meshtastic.proto.User + +class GetFilteredNodesUseCaseTest { + + private lateinit var nodeRepository: NodeRepository + private lateinit var useCase: GetFilteredNodesUseCase + + @Before + fun setUp() { + nodeRepository = mockk() + useCase = GetFilteredNodesUseCase(nodeRepository) + } + + private fun createNode( + num: Int, + role: Config.DeviceConfig.Role = Config.DeviceConfig.Role.CLIENT, + ignored: Boolean = false, + name: String = "Node$num", + ): Node { + val user = User(id = "!$num", long_name = name, short_name = "N$num", role = role) + return Node(num = num, user = user, isIgnored = ignored) + } + + @Test + fun `invoke applies repository filters and returns nodes`() = runTest { + // Arrange + val nodes = listOf(createNode(1), createNode(2)) + val filter = NodeFilterState(filterText = "Node", includeUnknown = true) + + every { + nodeRepository.getNodes( + sort = NodeSortOption.LAST_HEARD, + filter = "Node", + includeUnknown = true, + onlyOnline = false, + onlyDirect = false, + ) + } returns flowOf(nodes) + + // Act + val result = useCase(filter, NodeSortOption.LAST_HEARD).first() + + // Assert + assertEquals(2, result.size) + assertEquals(1, result[0].num) + } + + @Test + fun `invoke filters out ignored nodes if showIgnored is false`() = runTest { + // Arrange + val normalNode = createNode(1, ignored = false) + val ignoredNode = createNode(2, ignored = true) + val filter = NodeFilterState(showIgnored = false) + + every { nodeRepository.getNodes(any(), any(), any(), any(), any()) } returns + flowOf(listOf(normalNode, ignoredNode)) + + // Act + val result = useCase(filter, NodeSortOption.LAST_HEARD).first() + + // Assert + assertEquals(1, result.size) + assertEquals(1, result.first().num) + } + + @Test + fun `invoke filters out infrastructure nodes if excludeInfrastructure is true`() = runTest { + // Arrange + val clientNode = createNode(1, role = Config.DeviceConfig.Role.CLIENT) + val routerNode = createNode(2, role = Config.DeviceConfig.Role.ROUTER) + + @Suppress("DEPRECATION") + val repeaterNode = createNode(3, role = Config.DeviceConfig.Role.REPEATER) + val clientBaseNode = createNode(4, role = Config.DeviceConfig.Role.CLIENT_BASE) + val filter = NodeFilterState(excludeInfrastructure = true) + + every { nodeRepository.getNodes(any(), any(), any(), any(), any()) } returns + flowOf(listOf(clientNode, routerNode, repeaterNode, clientBaseNode)) + + // Act + val result = useCase(filter, NodeSortOption.LAST_HEARD).first() + + // Assert + // Should only keep the CLIENT node, others are infrastructure + assertEquals(1, result.size) + assertEquals(1, result.first().num) + } +} diff --git a/feature/node/src/test/kotlin/org/meshtastic/feature/node/metrics/BaseMetricScreenTest.kt b/feature/node/src/test/kotlin/org/meshtastic/feature/node/metrics/BaseMetricScreenTest.kt index e663355808..6166892777 100644 --- a/feature/node/src/test/kotlin/org/meshtastic/feature/node/metrics/BaseMetricScreenTest.kt +++ b/feature/node/src/test/kotlin/org/meshtastic/feature/node/metrics/BaseMetricScreenTest.kt @@ -27,8 +27,8 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.meshtastic.core.model.TelemetryType -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.device_metrics_log +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.device_metrics_log import org.meshtastic.core.ui.theme.AppTheme import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config diff --git a/feature/settings/README.md b/feature/settings/README.md index e2d69c8049..78074fb4d0 100644 --- a/feature/settings/README.md +++ b/feature/settings/README.md @@ -35,7 +35,7 @@ graph TB :feature:settings -.-> :core:prefs :feature:settings -.-> :core:proto :feature:settings -.-> :core:service - :feature:settings -.-> :core:strings + :feature:settings -.-> :core:resources :feature:settings -.-> :core:ui :feature:settings -.-> :core:barcode diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts index 6ad18ff41a..9a7de65a8b 100644 --- a/feature/settings/build.gradle.kts +++ b/feature/settings/build.gradle.kts @@ -39,7 +39,7 @@ dependencies { implementation(projects.core.prefs) implementation(projects.core.proto) implementation(projects.core.service) - implementation(projects.core.strings) + implementation(projects.core.resources) implementation(projects.core.ui) implementation(projects.core.barcode) diff --git a/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/debugging/DebugFiltersTest.kt b/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/debugging/DebugFiltersTest.kt index 124726e0cf..5b31f7012f 100644 --- a/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/debugging/DebugFiltersTest.kt +++ b/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/debugging/DebugFiltersTest.kt @@ -35,10 +35,10 @@ import androidx.test.platform.app.InstrumentationRegistry import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.debug_active_filters -import org.meshtastic.core.strings.debug_filters -import org.meshtastic.core.strings.getString +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.debug_active_filters +import org.meshtastic.core.resources.debug_filters +import org.meshtastic.core.resources.getString import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog @RunWith(AndroidJUnit4::class) diff --git a/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/debugging/DebugSearchTest.kt b/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/debugging/DebugSearchTest.kt index 87f54cb27e..ed1e6a7f5d 100644 --- a/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/debugging/DebugSearchTest.kt +++ b/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/debugging/DebugSearchTest.kt @@ -34,11 +34,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.debug_active_filters -import org.meshtastic.core.strings.debug_default_search -import org.meshtastic.core.strings.debug_filters -import org.meshtastic.core.strings.getString +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.debug_active_filters +import org.meshtastic.core.resources.debug_default_search +import org.meshtastic.core.resources.debug_filters +import org.meshtastic.core.resources.getString import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog import org.meshtastic.feature.settings.debugging.LogSearchManager.SearchMatch import org.meshtastic.feature.settings.debugging.LogSearchManager.SearchState diff --git a/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/radio/component/EditDeviceProfileDialogTest.kt b/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/radio/component/EditDeviceProfileDialogTest.kt index a0449cca48..1f390e44e2 100644 --- a/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/radio/component/EditDeviceProfileDialogTest.kt +++ b/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/radio/component/EditDeviceProfileDialogTest.kt @@ -25,10 +25,10 @@ import org.junit.Assert import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.save +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.getString +import org.meshtastic.core.resources.save import org.meshtastic.proto.DeviceProfile import org.meshtastic.proto.Position diff --git a/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreferenceTest.kt b/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreferenceTest.kt index b25bc354df..9eb31a6e7e 100644 --- a/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreferenceTest.kt +++ b/feature/settings/src/androidTest/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreferenceTest.kt @@ -28,11 +28,11 @@ import org.junit.Assert import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.i_agree -import org.meshtastic.core.strings.map_reporting -import org.meshtastic.core.strings.map_reporting_summary +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.getString +import org.meshtastic.core.resources.i_agree +import org.meshtastic.core.resources.map_reporting +import org.meshtastic.core.resources.map_reporting_summary @RunWith(AndroidJUnit4::class) class MapReportingPreferenceTest { diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/AboutScreen.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/AboutScreen.kt index 2b790c00db..0f872cb910 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/AboutScreen.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/AboutScreen.kt @@ -30,8 +30,8 @@ import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer import com.mikepenz.aboutlibraries.util.withContext import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.acknowledgements +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.acknowledgements import org.meshtastic.core.ui.component.MainAppBar @Composable diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsScreen.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsScreen.kt index 2829346ef5..1887edbb34 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsScreen.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsScreen.kt @@ -74,33 +74,33 @@ import org.meshtastic.core.common.util.toInstant import org.meshtastic.core.database.DatabaseConstants import org.meshtastic.core.navigation.Route import org.meshtastic.core.navigation.SettingsRoutes -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.acknowledgements -import org.meshtastic.core.strings.analytics_okay -import org.meshtastic.core.strings.app_settings -import org.meshtastic.core.strings.app_version -import org.meshtastic.core.strings.bottom_nav_settings -import org.meshtastic.core.strings.choose_theme -import org.meshtastic.core.strings.device_db_cache_limit -import org.meshtastic.core.strings.device_db_cache_limit_summary -import org.meshtastic.core.strings.dynamic -import org.meshtastic.core.strings.export_configuration -import org.meshtastic.core.strings.export_data_csv -import org.meshtastic.core.strings.import_configuration -import org.meshtastic.core.strings.intro_show -import org.meshtastic.core.strings.location_disabled -import org.meshtastic.core.strings.modules_already_unlocked -import org.meshtastic.core.strings.modules_unlocked -import org.meshtastic.core.strings.preferences_language -import org.meshtastic.core.strings.provide_location_to_mesh -import org.meshtastic.core.strings.remotely_administrating -import org.meshtastic.core.strings.save_rangetest -import org.meshtastic.core.strings.system_settings -import org.meshtastic.core.strings.theme -import org.meshtastic.core.strings.theme_dark -import org.meshtastic.core.strings.theme_light -import org.meshtastic.core.strings.theme_system -import org.meshtastic.core.strings.use_homoglyph_characters_encoding +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.acknowledgements +import org.meshtastic.core.resources.analytics_okay +import org.meshtastic.core.resources.app_settings +import org.meshtastic.core.resources.app_version +import org.meshtastic.core.resources.bottom_nav_settings +import org.meshtastic.core.resources.choose_theme +import org.meshtastic.core.resources.device_db_cache_limit +import org.meshtastic.core.resources.device_db_cache_limit_summary +import org.meshtastic.core.resources.dynamic +import org.meshtastic.core.resources.export_configuration +import org.meshtastic.core.resources.export_data_csv +import org.meshtastic.core.resources.import_configuration +import org.meshtastic.core.resources.intro_show +import org.meshtastic.core.resources.location_disabled +import org.meshtastic.core.resources.modules_already_unlocked +import org.meshtastic.core.resources.modules_unlocked +import org.meshtastic.core.resources.preferences_language +import org.meshtastic.core.resources.provide_location_to_mesh +import org.meshtastic.core.resources.remotely_administrating +import org.meshtastic.core.resources.save_rangetest +import org.meshtastic.core.resources.system_settings +import org.meshtastic.core.resources.theme +import org.meshtastic.core.resources.theme_dark +import org.meshtastic.core.resources.theme_light +import org.meshtastic.core.resources.theme_system +import org.meshtastic.core.resources.use_homoglyph_characters_encoding import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.component.MainAppBar diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt index 49fab4e0f9..7a62781694 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt @@ -86,22 +86,22 @@ import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.nowMillis import org.meshtastic.core.common.util.toDate import org.meshtastic.core.common.util.toInstant -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.debug_clear -import org.meshtastic.core.strings.debug_decoded_payload -import org.meshtastic.core.strings.debug_default_search -import org.meshtastic.core.strings.debug_export_failed -import org.meshtastic.core.strings.debug_export_success -import org.meshtastic.core.strings.debug_filters -import org.meshtastic.core.strings.debug_logs_export -import org.meshtastic.core.strings.debug_panel -import org.meshtastic.core.strings.debug_store_logs_summary -import org.meshtastic.core.strings.debug_store_logs_title -import org.meshtastic.core.strings.log_retention_days -import org.meshtastic.core.strings.log_retention_days_quantity -import org.meshtastic.core.strings.log_retention_days_summary -import org.meshtastic.core.strings.log_retention_hours -import org.meshtastic.core.strings.log_retention_never +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.debug_clear +import org.meshtastic.core.resources.debug_decoded_payload +import org.meshtastic.core.resources.debug_default_search +import org.meshtastic.core.resources.debug_export_failed +import org.meshtastic.core.resources.debug_export_success +import org.meshtastic.core.resources.debug_filters +import org.meshtastic.core.resources.debug_logs_export +import org.meshtastic.core.resources.debug_panel +import org.meshtastic.core.resources.debug_store_logs_summary +import org.meshtastic.core.resources.debug_store_logs_title +import org.meshtastic.core.resources.log_retention_days +import org.meshtastic.core.resources.log_retention_days_quantity +import org.meshtastic.core.resources.log_retention_days_summary +import org.meshtastic.core.resources.log_retention_hours +import org.meshtastic.core.resources.log_retention_never import org.meshtastic.core.ui.component.CopyIconButton import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.MainAppBar diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugFilters.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugFilters.kt index 8a393f67ff..9a9addff34 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugFilters.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugFilters.kt @@ -56,16 +56,16 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.debug_active_filters -import org.meshtastic.core.strings.debug_filter_add -import org.meshtastic.core.strings.debug_filter_add_custom -import org.meshtastic.core.strings.debug_filter_clear -import org.meshtastic.core.strings.debug_filter_included -import org.meshtastic.core.strings.debug_filter_preset_title -import org.meshtastic.core.strings.debug_filters -import org.meshtastic.core.strings.match_all -import org.meshtastic.core.strings.match_any +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.debug_active_filters +import org.meshtastic.core.resources.debug_filter_add +import org.meshtastic.core.resources.debug_filter_add_custom +import org.meshtastic.core.resources.debug_filter_clear +import org.meshtastic.core.resources.debug_filter_included +import org.meshtastic.core.resources.debug_filter_preset_title +import org.meshtastic.core.resources.debug_filters +import org.meshtastic.core.resources.match_all +import org.meshtastic.core.resources.match_any import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog @Composable diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugSearch.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugSearch.kt index 82c70982eb..f1db9005bf 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugSearch.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugSearch.kt @@ -52,12 +52,12 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.debug_default_search -import org.meshtastic.core.strings.debug_logs_export -import org.meshtastic.core.strings.debug_search_clear -import org.meshtastic.core.strings.debug_search_next -import org.meshtastic.core.strings.debug_search_prev +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.debug_default_search +import org.meshtastic.core.resources.debug_logs_export +import org.meshtastic.core.resources.debug_search_clear +import org.meshtastic.core.resources.debug_search_next +import org.meshtastic.core.resources.debug_search_prev import org.meshtastic.core.ui.theme.AppTheme import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog import org.meshtastic.feature.settings.debugging.LogSearchManager.SearchMatch diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt index 06b0b19513..161134b163 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt @@ -44,9 +44,9 @@ import org.meshtastic.core.model.getTracerouteResponse import org.meshtastic.core.model.util.decodeOrNull import org.meshtastic.core.model.util.toReadableString import org.meshtastic.core.prefs.meshlog.MeshLogPrefs -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.debug_clear -import org.meshtastic.core.strings.debug_clear_logs_confirm +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.debug_clear +import org.meshtastic.core.resources.debug_clear_logs_confirm import org.meshtastic.core.ui.util.AlertManager import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed import org.meshtastic.proto.AdminMessage diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/filter/FilterSettingsScreen.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/filter/FilterSettingsScreen.kt index e8e953cf69..9a2c1f0eef 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/filter/FilterSettingsScreen.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/filter/FilterSettingsScreen.kt @@ -48,18 +48,18 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add -import org.meshtastic.core.strings.delete -import org.meshtastic.core.strings.filter_add_placeholder -import org.meshtastic.core.strings.filter_enable -import org.meshtastic.core.strings.filter_enable_summary -import org.meshtastic.core.strings.filter_no_words -import org.meshtastic.core.strings.filter_regex_pattern -import org.meshtastic.core.strings.filter_settings -import org.meshtastic.core.strings.filter_whole_word -import org.meshtastic.core.strings.filter_words -import org.meshtastic.core.strings.filter_words_summary +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add +import org.meshtastic.core.resources.delete +import org.meshtastic.core.resources.filter_add_placeholder +import org.meshtastic.core.resources.filter_enable +import org.meshtastic.core.resources.filter_enable_summary +import org.meshtastic.core.resources.filter_no_words +import org.meshtastic.core.resources.filter_regex_pattern +import org.meshtastic.core.resources.filter_settings +import org.meshtastic.core.resources.filter_whole_word +import org.meshtastic.core.resources.filter_words +import org.meshtastic.core.resources.filter_words_summary import org.meshtastic.core.ui.component.MainAppBar @Composable diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/navigation/ConfigRoute.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/navigation/ConfigRoute.kt index 1ad0a5bfef..1821fd6c3e 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/navigation/ConfigRoute.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/navigation/ConfigRoute.kt @@ -31,17 +31,17 @@ import androidx.compose.ui.graphics.vector.ImageVector import org.jetbrains.compose.resources.StringResource import org.meshtastic.core.navigation.Route import org.meshtastic.core.navigation.SettingsRoutes -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.bluetooth -import org.meshtastic.core.strings.channels -import org.meshtastic.core.strings.device -import org.meshtastic.core.strings.display -import org.meshtastic.core.strings.lora -import org.meshtastic.core.strings.network -import org.meshtastic.core.strings.position -import org.meshtastic.core.strings.power -import org.meshtastic.core.strings.security -import org.meshtastic.core.strings.user +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.bluetooth +import org.meshtastic.core.resources.channels +import org.meshtastic.core.resources.device +import org.meshtastic.core.resources.display +import org.meshtastic.core.resources.lora +import org.meshtastic.core.resources.network +import org.meshtastic.core.resources.position +import org.meshtastic.core.resources.power +import org.meshtastic.core.resources.security +import org.meshtastic.core.resources.user import org.meshtastic.proto.AdminMessage import org.meshtastic.proto.DeviceMetadata diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/navigation/ModuleRoute.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/navigation/ModuleRoute.kt index 6c7b2bedb1..cb96d573bc 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/navigation/ModuleRoute.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/navigation/ModuleRoute.kt @@ -35,21 +35,21 @@ import org.jetbrains.compose.resources.StringResource import org.meshtastic.core.model.Capabilities import org.meshtastic.core.navigation.Route import org.meshtastic.core.navigation.SettingsRoutes -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.ambient_lighting -import org.meshtastic.core.strings.audio -import org.meshtastic.core.strings.canned_message -import org.meshtastic.core.strings.detection_sensor -import org.meshtastic.core.strings.external_notification -import org.meshtastic.core.strings.mqtt -import org.meshtastic.core.strings.neighbor_info -import org.meshtastic.core.strings.paxcounter -import org.meshtastic.core.strings.range_test -import org.meshtastic.core.strings.remote_hardware -import org.meshtastic.core.strings.serial -import org.meshtastic.core.strings.status_message -import org.meshtastic.core.strings.store_forward -import org.meshtastic.core.strings.telemetry +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.ambient_lighting +import org.meshtastic.core.resources.audio +import org.meshtastic.core.resources.canned_message +import org.meshtastic.core.resources.detection_sensor +import org.meshtastic.core.resources.external_notification +import org.meshtastic.core.resources.mqtt +import org.meshtastic.core.resources.neighbor_info +import org.meshtastic.core.resources.paxcounter +import org.meshtastic.core.resources.range_test +import org.meshtastic.core.resources.remote_hardware +import org.meshtastic.core.resources.serial +import org.meshtastic.core.resources.status_message +import org.meshtastic.core.resources.store_forward +import org.meshtastic.core.resources.telemetry import org.meshtastic.proto.AdminMessage import org.meshtastic.proto.DeviceMetadata diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseScreen.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseScreen.kt index 7ca93ad0fc..06c5a853ba 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseScreen.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseScreen.kt @@ -41,13 +41,13 @@ import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.entity.NodeEntity -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.clean_node_database_description -import org.meshtastic.core.strings.clean_node_database_title -import org.meshtastic.core.strings.clean_nodes_older_than -import org.meshtastic.core.strings.clean_now -import org.meshtastic.core.strings.clean_unknown_nodes -import org.meshtastic.core.strings.nodes_queued_for_deletion +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.clean_node_database_description +import org.meshtastic.core.resources.clean_node_database_title +import org.meshtastic.core.resources.clean_nodes_older_than +import org.meshtastic.core.resources.clean_now +import org.meshtastic.core.resources.clean_unknown_nodes +import org.meshtastic.core.resources.nodes_queued_for_deletion import org.meshtastic.core.ui.component.NodeChip /** diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseViewModel.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseViewModel.kt index d516543305..344ee08900 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseViewModel.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/CleanNodeDatabaseViewModel.kt @@ -26,11 +26,11 @@ import org.jetbrains.compose.resources.getString import org.meshtastic.core.common.util.nowSeconds import org.meshtastic.core.data.repository.NodeRepository import org.meshtastic.core.database.entity.NodeEntity +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.are_you_sure +import org.meshtastic.core.resources.clean_node_database_confirmation +import org.meshtastic.core.resources.clean_now import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.are_you_sure -import org.meshtastic.core.strings.clean_node_database_confirmation -import org.meshtastic.core.strings.clean_now import org.meshtastic.core.ui.util.AlertManager import javax.inject.Inject import kotlin.time.Duration.Companion.days diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfig.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfig.kt index 679abc664c..e220b5c827 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfig.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfig.kt @@ -54,24 +54,24 @@ import org.meshtastic.core.database.model.Node import org.meshtastic.core.navigation.FirmwareRoutes import org.meshtastic.core.navigation.Route import org.meshtastic.core.navigation.SettingsRoutes -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.administration -import org.meshtastic.core.strings.advanced_title -import org.meshtastic.core.strings.backup_restore -import org.meshtastic.core.strings.clean_node_database_title -import org.meshtastic.core.strings.debug_panel -import org.meshtastic.core.strings.device_configuration -import org.meshtastic.core.strings.export_configuration -import org.meshtastic.core.strings.factory_reset -import org.meshtastic.core.strings.firmware_update_title -import org.meshtastic.core.strings.import_configuration -import org.meshtastic.core.strings.message_device_managed -import org.meshtastic.core.strings.module_settings -import org.meshtastic.core.strings.nodedb_reset -import org.meshtastic.core.strings.preserve_favorites -import org.meshtastic.core.strings.radio_configuration -import org.meshtastic.core.strings.reboot -import org.meshtastic.core.strings.shutdown +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.administration +import org.meshtastic.core.resources.advanced_title +import org.meshtastic.core.resources.backup_restore +import org.meshtastic.core.resources.clean_node_database_title +import org.meshtastic.core.resources.debug_panel +import org.meshtastic.core.resources.device_configuration +import org.meshtastic.core.resources.export_configuration +import org.meshtastic.core.resources.factory_reset +import org.meshtastic.core.resources.firmware_update_title +import org.meshtastic.core.resources.import_configuration +import org.meshtastic.core.resources.message_device_managed +import org.meshtastic.core.resources.module_settings +import org.meshtastic.core.resources.nodedb_reset +import org.meshtastic.core.resources.preserve_favorites +import org.meshtastic.core.resources.radio_configuration +import org.meshtastic.core.resources.reboot +import org.meshtastic.core.resources.shutdown import org.meshtastic.core.ui.component.ListItem import org.meshtastic.core.ui.component.TitledCard import org.meshtastic.core.ui.theme.AppTheme diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt index cf12c81236..3f9adf6ee3 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt @@ -58,15 +58,15 @@ import org.meshtastic.core.navigation.SettingsRoutes import org.meshtastic.core.prefs.analytics.AnalyticsPrefs import org.meshtastic.core.prefs.homoglyph.HomoglyphPrefs import org.meshtastic.core.prefs.map.MapConsentPrefs +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.UiText +import org.meshtastic.core.resources.cant_shutdown import org.meshtastic.core.service.ConnectionState import org.meshtastic.core.service.IMeshService import org.meshtastic.core.service.ServiceRepository -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cant_shutdown import org.meshtastic.core.ui.util.getChannelList import org.meshtastic.feature.settings.navigation.ConfigRoute import org.meshtastic.feature.settings.navigation.ModuleRoute -import org.meshtastic.feature.settings.util.UiText import org.meshtastic.proto.AdminMessage import org.meshtastic.proto.Channel import org.meshtastic.proto.ChannelSettings @@ -683,7 +683,7 @@ constructor( private fun sendError(error: String) = setResponseStateError(UiText.DynamicString(error)) - private fun sendError(id: StringResource) = setResponseStateError(UiText.StringResource(id)) + private fun sendError(id: StringResource) = setResponseStateError(UiText.Resource(id)) private fun setResponseStateError(error: UiText) { _radioConfigState.update { it.copy(responseState = ResponseState.Error(error)) } diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/ResponseState.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/ResponseState.kt index 96e5e3df7c..7da3f8f35c 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/ResponseState.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/ResponseState.kt @@ -16,7 +16,7 @@ */ package org.meshtastic.feature.settings.radio -import org.meshtastic.feature.settings.util.UiText +import org.meshtastic.core.resources.UiText /** Generic sealed class defines each possible state of a response. */ sealed class ResponseState { diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/ChannelConfigScreen.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/ChannelConfigScreen.kt index 71931f30c8..5915c54aa7 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/ChannelConfigScreen.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/ChannelConfigScreen.kt @@ -54,13 +54,13 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.Capabilities import org.meshtastic.core.model.Channel -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.add -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.channel_name -import org.meshtastic.core.strings.channels -import org.meshtastic.core.strings.press_and_drag -import org.meshtastic.core.strings.send +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.add +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.channel_name +import org.meshtastic.core.resources.channels +import org.meshtastic.core.resources.press_and_drag +import org.meshtastic.core.resources.send import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.component.PreferenceFooter import org.meshtastic.core.ui.component.dragContainer diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelCard.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelCard.kt index e89c37df9b..81252fee2a 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelCard.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelCard.kt @@ -29,8 +29,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.delete +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.delete import org.meshtastic.core.ui.component.ChannelItem import org.meshtastic.core.ui.component.SecurityIcon import org.meshtastic.core.ui.theme.AppTheme diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelConfigHeader.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelConfigHeader.kt index 9e7fd028e2..75be99792d 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelConfigHeader.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelConfigHeader.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.settings.radio.channel.component import androidx.compose.foundation.layout.Arrangement @@ -28,10 +27,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.sp import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.channels -import org.meshtastic.core.strings.freq -import org.meshtastic.core.strings.slot +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.channels +import org.meshtastic.core.resources.freq +import org.meshtastic.core.resources.slot import org.meshtastic.core.ui.component.PreferenceCategory import org.meshtastic.core.ui.theme.AppTheme diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelLegend.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelLegend.kt index f88675dd1f..0759ac214c 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelLegend.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/ChannelLegend.kt @@ -42,23 +42,23 @@ import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.Capabilities -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.channel_features -import org.meshtastic.core.strings.downlink_enabled -import org.meshtastic.core.strings.downlink_feature_description -import org.meshtastic.core.strings.icon_meanings -import org.meshtastic.core.strings.info -import org.meshtastic.core.strings.location_sharing -import org.meshtastic.core.strings.manual_position_request -import org.meshtastic.core.strings.periodic_position_broadcast -import org.meshtastic.core.strings.primary -import org.meshtastic.core.strings.primary_channel_feature -import org.meshtastic.core.strings.secondary -import org.meshtastic.core.strings.secondary_channel_position_feature -import org.meshtastic.core.strings.secondary_no_telemetry -import org.meshtastic.core.strings.security_icon_help_dismiss -import org.meshtastic.core.strings.uplink_enabled -import org.meshtastic.core.strings.uplink_feature_description +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.channel_features +import org.meshtastic.core.resources.downlink_enabled +import org.meshtastic.core.resources.downlink_feature_description +import org.meshtastic.core.resources.icon_meanings +import org.meshtastic.core.resources.info +import org.meshtastic.core.resources.location_sharing +import org.meshtastic.core.resources.manual_position_request +import org.meshtastic.core.resources.periodic_position_broadcast +import org.meshtastic.core.resources.primary +import org.meshtastic.core.resources.primary_channel_feature +import org.meshtastic.core.resources.secondary +import org.meshtastic.core.resources.secondary_channel_position_feature +import org.meshtastic.core.resources.secondary_no_telemetry +import org.meshtastic.core.resources.security_icon_help_dismiss +import org.meshtastic.core.resources.uplink_enabled +import org.meshtastic.core.resources.uplink_feature_description import org.meshtastic.core.ui.component.MeshtasticDialog @Composable diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/EditChannelDialog.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/EditChannelDialog.kt index 4674342fcf..835fa95570 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/EditChannelDialog.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/channel/component/EditChannelDialog.kt @@ -34,13 +34,13 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.Channel -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.channel_name -import org.meshtastic.core.strings.default_ -import org.meshtastic.core.strings.downlink_enabled -import org.meshtastic.core.strings.save -import org.meshtastic.core.strings.uplink_enabled +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.channel_name +import org.meshtastic.core.resources.default_ +import org.meshtastic.core.resources.downlink_enabled +import org.meshtastic.core.resources.save +import org.meshtastic.core.resources.uplink_enabled import org.meshtastic.core.ui.component.EditBase64Preference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.MeshtasticDialog diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/AmbientLightingConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/AmbientLightingConfigItemList.kt index e29c41fa4f..fe6efefe9d 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/AmbientLightingConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/AmbientLightingConfigItemList.kt @@ -25,14 +25,14 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.ambient_lighting -import org.meshtastic.core.strings.ambient_lighting_config -import org.meshtastic.core.strings.blue -import org.meshtastic.core.strings.current -import org.meshtastic.core.strings.green -import org.meshtastic.core.strings.led_state -import org.meshtastic.core.strings.red +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.ambient_lighting +import org.meshtastic.core.resources.ambient_lighting_config +import org.meshtastic.core.resources.blue +import org.meshtastic.core.resources.current +import org.meshtastic.core.resources.green +import org.meshtastic.core.resources.led_state +import org.meshtastic.core.resources.red import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.core.ui.component.TitledCard diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/AudioConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/AudioConfigItemList.kt index 6e868478ea..9b009352b0 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/AudioConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/AudioConfigItemList.kt @@ -25,16 +25,16 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.audio -import org.meshtastic.core.strings.audio_config -import org.meshtastic.core.strings.codec2_sample_rate -import org.meshtastic.core.strings.codec_2_enabled -import org.meshtastic.core.strings.i2s_clock -import org.meshtastic.core.strings.i2s_data_in -import org.meshtastic.core.strings.i2s_data_out -import org.meshtastic.core.strings.i2s_word_select -import org.meshtastic.core.strings.ptt_pin +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.audio +import org.meshtastic.core.resources.audio_config +import org.meshtastic.core.resources.codec2_sample_rate +import org.meshtastic.core.resources.codec_2_enabled +import org.meshtastic.core.resources.i2s_clock +import org.meshtastic.core.resources.i2s_data_in +import org.meshtastic.core.resources.i2s_data_out +import org.meshtastic.core.resources.i2s_word_select +import org.meshtastic.core.resources.ptt_pin import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/BluetoothConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/BluetoothConfigItemList.kt index d9d5ecc5a6..f05efd1f82 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/BluetoothConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/BluetoothConfigItemList.kt @@ -25,12 +25,12 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.bluetooth -import org.meshtastic.core.strings.bluetooth_config -import org.meshtastic.core.strings.bluetooth_enabled -import org.meshtastic.core.strings.fixed_pin -import org.meshtastic.core.strings.pairing_mode +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.bluetooth +import org.meshtastic.core.resources.bluetooth_config +import org.meshtastic.core.resources.bluetooth_enabled +import org.meshtastic.core.resources.fixed_pin +import org.meshtastic.core.resources.pairing_mode import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/CannedMessageConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/CannedMessageConfigItemList.kt index bacc82b763..e96e00f0ae 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/CannedMessageConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/CannedMessageConfigItemList.kt @@ -31,21 +31,21 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.allow_input_source -import org.meshtastic.core.strings.canned_message -import org.meshtastic.core.strings.canned_message_config -import org.meshtastic.core.strings.canned_message_enabled -import org.meshtastic.core.strings.generate_input_event_on_ccw -import org.meshtastic.core.strings.generate_input_event_on_cw -import org.meshtastic.core.strings.generate_input_event_on_press -import org.meshtastic.core.strings.gpio_pin_for_rotary_encoder_a_port -import org.meshtastic.core.strings.gpio_pin_for_rotary_encoder_b_port -import org.meshtastic.core.strings.gpio_pin_for_rotary_encoder_press_port -import org.meshtastic.core.strings.messages -import org.meshtastic.core.strings.rotary_encoder_1_enabled -import org.meshtastic.core.strings.send_bell -import org.meshtastic.core.strings.up_down_select_input_enabled +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.allow_input_source +import org.meshtastic.core.resources.canned_message +import org.meshtastic.core.resources.canned_message_config +import org.meshtastic.core.resources.canned_message_enabled +import org.meshtastic.core.resources.generate_input_event_on_ccw +import org.meshtastic.core.resources.generate_input_event_on_cw +import org.meshtastic.core.resources.generate_input_event_on_press +import org.meshtastic.core.resources.gpio_pin_for_rotary_encoder_a_port +import org.meshtastic.core.resources.gpio_pin_for_rotary_encoder_b_port +import org.meshtastic.core.resources.gpio_pin_for_rotary_encoder_press_port +import org.meshtastic.core.resources.messages +import org.meshtastic.core.resources.rotary_encoder_1_enabled +import org.meshtastic.core.resources.send_bell +import org.meshtastic.core.resources.up_down_select_input_enabled import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DetectionSensorConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DetectionSensorConfigItemList.kt index 89ec48a205..e6c8d9a172 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DetectionSensorConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DetectionSensorConfigItemList.kt @@ -29,17 +29,17 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.detection_sensor -import org.meshtastic.core.strings.detection_sensor_config -import org.meshtastic.core.strings.detection_sensor_enabled -import org.meshtastic.core.strings.detection_trigger_type -import org.meshtastic.core.strings.friendly_name -import org.meshtastic.core.strings.gpio_pin_to_monitor -import org.meshtastic.core.strings.minimum_broadcast_seconds -import org.meshtastic.core.strings.send_bell_with_alert_message -import org.meshtastic.core.strings.state_broadcast_seconds -import org.meshtastic.core.strings.use_input_pullup_mode +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.detection_sensor +import org.meshtastic.core.resources.detection_sensor_config +import org.meshtastic.core.resources.detection_sensor_enabled +import org.meshtastic.core.resources.detection_trigger_type +import org.meshtastic.core.resources.friendly_name +import org.meshtastic.core.resources.gpio_pin_to_monitor +import org.meshtastic.core.resources.minimum_broadcast_seconds +import org.meshtastic.core.resources.send_bell_with_alert_message +import org.meshtastic.core.resources.state_broadcast_seconds +import org.meshtastic.core.resources.use_input_pullup_mode import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DeviceConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DeviceConfigItemList.kt index 9ec687e9cd..d2151165f7 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DeviceConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DeviceConfigItemList.kt @@ -64,50 +64,50 @@ import no.nordicsemi.android.common.core.registerReceiver import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.util.toPosixString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.accept -import org.meshtastic.core.strings.are_you_sure -import org.meshtastic.core.strings.button_gpio -import org.meshtastic.core.strings.buzzer_gpio -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.config_device_doubleTapAsButtonPress_summary -import org.meshtastic.core.strings.config_device_ledHeartbeatEnabled_summary -import org.meshtastic.core.strings.config_device_tripleClickAsAdHocPing_summary -import org.meshtastic.core.strings.config_device_tzdef_summary -import org.meshtastic.core.strings.config_device_use_phone_tz -import org.meshtastic.core.strings.device -import org.meshtastic.core.strings.double_tap_as_button_press -import org.meshtastic.core.strings.gpio -import org.meshtastic.core.strings.hardware -import org.meshtastic.core.strings.i_know_what_i_m_doing -import org.meshtastic.core.strings.led_heartbeat -import org.meshtastic.core.strings.nodeinfo_broadcast_interval -import org.meshtastic.core.strings.options -import org.meshtastic.core.strings.rebroadcast_mode -import org.meshtastic.core.strings.rebroadcast_mode_all_desc -import org.meshtastic.core.strings.rebroadcast_mode_all_skip_decoding_desc -import org.meshtastic.core.strings.rebroadcast_mode_core_portnums_only_desc -import org.meshtastic.core.strings.rebroadcast_mode_known_only_desc -import org.meshtastic.core.strings.rebroadcast_mode_local_only_desc -import org.meshtastic.core.strings.rebroadcast_mode_none_desc -import org.meshtastic.core.strings.role -import org.meshtastic.core.strings.role_client_base_desc -import org.meshtastic.core.strings.role_client_desc -import org.meshtastic.core.strings.role_client_hidden_desc -import org.meshtastic.core.strings.role_client_mute_desc -import org.meshtastic.core.strings.role_lost_and_found_desc -import org.meshtastic.core.strings.role_repeater_desc -import org.meshtastic.core.strings.role_router_client_desc -import org.meshtastic.core.strings.role_router_desc -import org.meshtastic.core.strings.role_router_late_desc -import org.meshtastic.core.strings.role_sensor_desc -import org.meshtastic.core.strings.role_tak_desc -import org.meshtastic.core.strings.role_tak_tracker_desc -import org.meshtastic.core.strings.role_tracker_desc -import org.meshtastic.core.strings.router_role_confirmation_text -import org.meshtastic.core.strings.time_zone -import org.meshtastic.core.strings.triple_click_adhoc_ping -import org.meshtastic.core.strings.unrecognized +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.accept +import org.meshtastic.core.resources.are_you_sure +import org.meshtastic.core.resources.button_gpio +import org.meshtastic.core.resources.buzzer_gpio +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.config_device_doubleTapAsButtonPress_summary +import org.meshtastic.core.resources.config_device_ledHeartbeatEnabled_summary +import org.meshtastic.core.resources.config_device_tripleClickAsAdHocPing_summary +import org.meshtastic.core.resources.config_device_tzdef_summary +import org.meshtastic.core.resources.config_device_use_phone_tz +import org.meshtastic.core.resources.device +import org.meshtastic.core.resources.double_tap_as_button_press +import org.meshtastic.core.resources.gpio +import org.meshtastic.core.resources.hardware +import org.meshtastic.core.resources.i_know_what_i_m_doing +import org.meshtastic.core.resources.led_heartbeat +import org.meshtastic.core.resources.nodeinfo_broadcast_interval +import org.meshtastic.core.resources.options +import org.meshtastic.core.resources.rebroadcast_mode +import org.meshtastic.core.resources.rebroadcast_mode_all_desc +import org.meshtastic.core.resources.rebroadcast_mode_all_skip_decoding_desc +import org.meshtastic.core.resources.rebroadcast_mode_core_portnums_only_desc +import org.meshtastic.core.resources.rebroadcast_mode_known_only_desc +import org.meshtastic.core.resources.rebroadcast_mode_local_only_desc +import org.meshtastic.core.resources.rebroadcast_mode_none_desc +import org.meshtastic.core.resources.role +import org.meshtastic.core.resources.role_client_base_desc +import org.meshtastic.core.resources.role_client_desc +import org.meshtastic.core.resources.role_client_hidden_desc +import org.meshtastic.core.resources.role_client_mute_desc +import org.meshtastic.core.resources.role_lost_and_found_desc +import org.meshtastic.core.resources.role_repeater_desc +import org.meshtastic.core.resources.role_router_client_desc +import org.meshtastic.core.resources.role_router_desc +import org.meshtastic.core.resources.role_router_late_desc +import org.meshtastic.core.resources.role_sensor_desc +import org.meshtastic.core.resources.role_tak_desc +import org.meshtastic.core.resources.role_tak_tracker_desc +import org.meshtastic.core.resources.role_tracker_desc +import org.meshtastic.core.resources.router_role_confirmation_text +import org.meshtastic.core.resources.time_zone +import org.meshtastic.core.resources.triple_click_adhoc_ping +import org.meshtastic.core.resources.unrecognized import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.InsetDivider diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DisplayConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DisplayConfigItemList.kt index ea5b8e8b76..a7f05cb6b9 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DisplayConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/DisplayConfigItemList.kt @@ -24,31 +24,31 @@ import androidx.compose.runtime.remember import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.advanced -import org.meshtastic.core.strings.always_point_north -import org.meshtastic.core.strings.bold_heading -import org.meshtastic.core.strings.carousel_interval -import org.meshtastic.core.strings.compass_orientation -import org.meshtastic.core.strings.config_display_auto_screen_carousel_secs_summary -import org.meshtastic.core.strings.config_display_compass_north_top_summary -import org.meshtastic.core.strings.config_display_displaymode_summary -import org.meshtastic.core.strings.config_display_flip_screen_summary -import org.meshtastic.core.strings.config_display_heading_bold_summary -import org.meshtastic.core.strings.config_display_oled_summary -import org.meshtastic.core.strings.config_display_screen_on_secs_summary -import org.meshtastic.core.strings.config_display_units_summary -import org.meshtastic.core.strings.config_display_wake_on_tap_or_motion_summary -import org.meshtastic.core.strings.display -import org.meshtastic.core.strings.display_config -import org.meshtastic.core.strings.display_mode -import org.meshtastic.core.strings.display_time_in_12h_format -import org.meshtastic.core.strings.display_units -import org.meshtastic.core.strings.flip_screen -import org.meshtastic.core.strings.oled_type -import org.meshtastic.core.strings.screen_on_for -import org.meshtastic.core.strings.use_12h_format -import org.meshtastic.core.strings.wake_on_tap_or_motion +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.advanced +import org.meshtastic.core.resources.always_point_north +import org.meshtastic.core.resources.bold_heading +import org.meshtastic.core.resources.carousel_interval +import org.meshtastic.core.resources.compass_orientation +import org.meshtastic.core.resources.config_display_auto_screen_carousel_secs_summary +import org.meshtastic.core.resources.config_display_compass_north_top_summary +import org.meshtastic.core.resources.config_display_displaymode_summary +import org.meshtastic.core.resources.config_display_flip_screen_summary +import org.meshtastic.core.resources.config_display_heading_bold_summary +import org.meshtastic.core.resources.config_display_oled_summary +import org.meshtastic.core.resources.config_display_screen_on_secs_summary +import org.meshtastic.core.resources.config_display_units_summary +import org.meshtastic.core.resources.config_display_wake_on_tap_or_motion_summary +import org.meshtastic.core.resources.display +import org.meshtastic.core.resources.display_config +import org.meshtastic.core.resources.display_mode +import org.meshtastic.core.resources.display_time_in_12h_format +import org.meshtastic.core.resources.display_units +import org.meshtastic.core.resources.flip_screen +import org.meshtastic.core.resources.oled_type +import org.meshtastic.core.resources.screen_on_for +import org.meshtastic.core.resources.use_12h_format +import org.meshtastic.core.resources.wake_on_tap_or_motion import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.core.ui.component.TitledCard diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/EditDeviceProfileDialog.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/EditDeviceProfileDialog.kt index 3346d725c7..1e4abcd116 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/EditDeviceProfileDialog.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/EditDeviceProfileDialog.kt @@ -28,15 +28,15 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.channel_url -import org.meshtastic.core.strings.fixed_position -import org.meshtastic.core.strings.long_name -import org.meshtastic.core.strings.module_settings -import org.meshtastic.core.strings.radio_configuration -import org.meshtastic.core.strings.save -import org.meshtastic.core.strings.short_name +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.channel_url +import org.meshtastic.core.resources.fixed_position +import org.meshtastic.core.resources.long_name +import org.meshtastic.core.resources.module_settings +import org.meshtastic.core.resources.radio_configuration +import org.meshtastic.core.resources.save +import org.meshtastic.core.resources.short_name import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.proto.DeviceProfile diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ExternalNotificationConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ExternalNotificationConfigItemList.kt index da63704d5b..00800c844e 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ExternalNotificationConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ExternalNotificationConfigItemList.kt @@ -45,30 +45,30 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import co.touchlab.kermit.Logger import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.advanced -import org.meshtastic.core.strings.alert_bell_buzzer -import org.meshtastic.core.strings.alert_bell_led -import org.meshtastic.core.strings.alert_bell_vibra -import org.meshtastic.core.strings.alert_message_buzzer -import org.meshtastic.core.strings.alert_message_led -import org.meshtastic.core.strings.alert_message_vibra -import org.meshtastic.core.strings.external_notification -import org.meshtastic.core.strings.external_notification_config -import org.meshtastic.core.strings.external_notification_enabled -import org.meshtastic.core.strings.import_label -import org.meshtastic.core.strings.nag_timeout_seconds -import org.meshtastic.core.strings.notifications_on_alert_bell_receipt -import org.meshtastic.core.strings.notifications_on_message_receipt -import org.meshtastic.core.strings.output_buzzer_gpio -import org.meshtastic.core.strings.output_duration_milliseconds -import org.meshtastic.core.strings.output_led_active_high -import org.meshtastic.core.strings.output_led_gpio -import org.meshtastic.core.strings.output_vibra_gpio -import org.meshtastic.core.strings.play -import org.meshtastic.core.strings.ringtone -import org.meshtastic.core.strings.use_i2s_as_buzzer -import org.meshtastic.core.strings.use_pwm_buzzer +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.advanced +import org.meshtastic.core.resources.alert_bell_buzzer +import org.meshtastic.core.resources.alert_bell_led +import org.meshtastic.core.resources.alert_bell_vibra +import org.meshtastic.core.resources.alert_message_buzzer +import org.meshtastic.core.resources.alert_message_led +import org.meshtastic.core.resources.alert_message_vibra +import org.meshtastic.core.resources.external_notification +import org.meshtastic.core.resources.external_notification_config +import org.meshtastic.core.resources.external_notification_enabled +import org.meshtastic.core.resources.import_label +import org.meshtastic.core.resources.nag_timeout_seconds +import org.meshtastic.core.resources.notifications_on_alert_bell_receipt +import org.meshtastic.core.resources.notifications_on_message_receipt +import org.meshtastic.core.resources.output_buzzer_gpio +import org.meshtastic.core.resources.output_duration_milliseconds +import org.meshtastic.core.resources.output_led_active_high +import org.meshtastic.core.resources.output_led_gpio +import org.meshtastic.core.resources.output_vibra_gpio +import org.meshtastic.core.resources.play +import org.meshtastic.core.resources.ringtone +import org.meshtastic.core.resources.use_i2s_as_buzzer +import org.meshtastic.core.resources.use_pwm_buzzer import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/LoRaConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/LoRaConfigItemList.kt index d7d77b566e..e4f91ece61 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/LoRaConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/LoRaConfigItemList.kt @@ -31,30 +31,30 @@ import org.meshtastic.core.model.Channel import org.meshtastic.core.model.ChannelOption import org.meshtastic.core.model.RegionInfo import org.meshtastic.core.model.numChannels -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.advanced -import org.meshtastic.core.strings.bandwidth -import org.meshtastic.core.strings.coding_rate -import org.meshtastic.core.strings.config_lora_frequency_slot_summary -import org.meshtastic.core.strings.config_lora_hop_limit_summary -import org.meshtastic.core.strings.config_lora_modem_preset_summary -import org.meshtastic.core.strings.config_lora_region_summary -import org.meshtastic.core.strings.frequency_slot -import org.meshtastic.core.strings.hop_limit -import org.meshtastic.core.strings.ignore_mqtt -import org.meshtastic.core.strings.lora -import org.meshtastic.core.strings.modem_preset -import org.meshtastic.core.strings.ok_to_mqtt -import org.meshtastic.core.strings.options -import org.meshtastic.core.strings.override_duty_cycle -import org.meshtastic.core.strings.override_frequency_mhz -import org.meshtastic.core.strings.pa_fan_disabled -import org.meshtastic.core.strings.region_frequency_plan -import org.meshtastic.core.strings.spread_factor -import org.meshtastic.core.strings.sx126x_rx_boosted_gain -import org.meshtastic.core.strings.tx_enabled -import org.meshtastic.core.strings.tx_power_dbm -import org.meshtastic.core.strings.use_modem_preset +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.advanced +import org.meshtastic.core.resources.bandwidth +import org.meshtastic.core.resources.coding_rate +import org.meshtastic.core.resources.config_lora_frequency_slot_summary +import org.meshtastic.core.resources.config_lora_hop_limit_summary +import org.meshtastic.core.resources.config_lora_modem_preset_summary +import org.meshtastic.core.resources.config_lora_region_summary +import org.meshtastic.core.resources.frequency_slot +import org.meshtastic.core.resources.hop_limit +import org.meshtastic.core.resources.ignore_mqtt +import org.meshtastic.core.resources.lora +import org.meshtastic.core.resources.modem_preset +import org.meshtastic.core.resources.ok_to_mqtt +import org.meshtastic.core.resources.options +import org.meshtastic.core.resources.override_duty_cycle +import org.meshtastic.core.resources.override_frequency_mhz +import org.meshtastic.core.resources.pa_fan_disabled +import org.meshtastic.core.resources.region_frequency_plan +import org.meshtastic.core.resources.spread_factor +import org.meshtastic.core.resources.sx126x_rx_boosted_gain +import org.meshtastic.core.resources.tx_enabled +import org.meshtastic.core.resources.tx_power_dbm +import org.meshtastic.core.resources.use_modem_preset import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SignedIntegerEditTextPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/MQTTConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/MQTTConfigItemList.kt index e043cdaa91..4451e9f67a 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/MQTTConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/MQTTConfigItemList.kt @@ -30,20 +30,20 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.address -import org.meshtastic.core.strings.default_mqtt_address -import org.meshtastic.core.strings.encryption_enabled -import org.meshtastic.core.strings.json_output_enabled -import org.meshtastic.core.strings.map_reporting -import org.meshtastic.core.strings.mqtt -import org.meshtastic.core.strings.mqtt_config -import org.meshtastic.core.strings.mqtt_enabled -import org.meshtastic.core.strings.password -import org.meshtastic.core.strings.proxy_to_client_enabled -import org.meshtastic.core.strings.root_topic -import org.meshtastic.core.strings.tls_enabled -import org.meshtastic.core.strings.username +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.address +import org.meshtastic.core.resources.default_mqtt_address +import org.meshtastic.core.resources.encryption_enabled +import org.meshtastic.core.resources.json_output_enabled +import org.meshtastic.core.resources.map_reporting +import org.meshtastic.core.resources.mqtt +import org.meshtastic.core.resources.mqtt_config +import org.meshtastic.core.resources.mqtt_enabled +import org.meshtastic.core.resources.password +import org.meshtastic.core.resources.proxy_to_client_enabled +import org.meshtastic.core.resources.root_topic +import org.meshtastic.core.resources.tls_enabled +import org.meshtastic.core.resources.username import org.meshtastic.core.ui.component.EditPasswordPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreference.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreference.kt index ac1e3a9846..fc33812ea7 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreference.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/MapReportingPreference.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Meshtastic LLC + * Copyright (c) 2025-2026 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.meshtastic.feature.settings.radio.component import androidx.compose.animation.AnimatedVisibility @@ -40,14 +39,14 @@ import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.util.DistanceUnit import org.meshtastic.core.model.util.toDistanceString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.i_agree -import org.meshtastic.core.strings.i_agree_to_share_my_location -import org.meshtastic.core.strings.map_reporting -import org.meshtastic.core.strings.map_reporting_consent_header -import org.meshtastic.core.strings.map_reporting_consent_text -import org.meshtastic.core.strings.map_reporting_interval_seconds -import org.meshtastic.core.strings.map_reporting_summary +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.i_agree +import org.meshtastic.core.resources.i_agree_to_share_my_location +import org.meshtastic.core.resources.map_reporting +import org.meshtastic.core.resources.map_reporting_consent_header +import org.meshtastic.core.resources.map_reporting_consent_text +import org.meshtastic.core.resources.map_reporting_interval_seconds +import org.meshtastic.core.resources.map_reporting_summary import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.core.ui.component.precisionBitsToMeters diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NeighborInfoConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NeighborInfoConfigItemList.kt index 8a18f8eeae..4a2944195c 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NeighborInfoConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NeighborInfoConfigItemList.kt @@ -25,13 +25,13 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.config_device_transmitOverLora_summary -import org.meshtastic.core.strings.neighbor_info -import org.meshtastic.core.strings.neighbor_info_config -import org.meshtastic.core.strings.neighbor_info_enabled -import org.meshtastic.core.strings.transmit_over_lora -import org.meshtastic.core.strings.update_interval_seconds +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.config_device_transmitOverLora_summary +import org.meshtastic.core.resources.neighbor_info +import org.meshtastic.core.resources.neighbor_info_config +import org.meshtastic.core.resources.neighbor_info_enabled +import org.meshtastic.core.resources.transmit_over_lora +import org.meshtastic.core.resources.update_interval_seconds import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.core.ui.component.TitledCard diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt index bb7da7e168..4e30a5f3de 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt @@ -44,35 +44,35 @@ import org.meshtastic.core.barcode.extractWifiCredentials import org.meshtastic.core.barcode.rememberBarcodeScanner import org.meshtastic.core.model.util.handleMeshtasticUri import org.meshtastic.core.nfc.NfcScannerEffect -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.advanced -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.config_network_eth_enabled_summary -import org.meshtastic.core.strings.config_network_udp_enabled_summary -import org.meshtastic.core.strings.config_network_wifi_enabled_summary -import org.meshtastic.core.strings.connection_status -import org.meshtastic.core.strings.error -import org.meshtastic.core.strings.ethernet_config -import org.meshtastic.core.strings.ethernet_enabled -import org.meshtastic.core.strings.ethernet_ip -import org.meshtastic.core.strings.gateway -import org.meshtastic.core.strings.ip -import org.meshtastic.core.strings.ipv4_mode -import org.meshtastic.core.strings.network -import org.meshtastic.core.strings.nfc_disabled -import org.meshtastic.core.strings.ntp_server -import org.meshtastic.core.strings.open_settings -import org.meshtastic.core.strings.password -import org.meshtastic.core.strings.rsyslog_server -import org.meshtastic.core.strings.scan_nfc -import org.meshtastic.core.strings.ssid -import org.meshtastic.core.strings.subnet -import org.meshtastic.core.strings.udp_enabled -import org.meshtastic.core.strings.wifi_config -import org.meshtastic.core.strings.wifi_enabled -import org.meshtastic.core.strings.wifi_ip -import org.meshtastic.core.strings.wifi_qr_code_error -import org.meshtastic.core.strings.wifi_qr_code_scan +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.advanced +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.config_network_eth_enabled_summary +import org.meshtastic.core.resources.config_network_udp_enabled_summary +import org.meshtastic.core.resources.config_network_wifi_enabled_summary +import org.meshtastic.core.resources.connection_status +import org.meshtastic.core.resources.error +import org.meshtastic.core.resources.ethernet_config +import org.meshtastic.core.resources.ethernet_enabled +import org.meshtastic.core.resources.ethernet_ip +import org.meshtastic.core.resources.gateway +import org.meshtastic.core.resources.ip +import org.meshtastic.core.resources.ipv4_mode +import org.meshtastic.core.resources.network +import org.meshtastic.core.resources.nfc_disabled +import org.meshtastic.core.resources.ntp_server +import org.meshtastic.core.resources.open_settings +import org.meshtastic.core.resources.password +import org.meshtastic.core.resources.rsyslog_server +import org.meshtastic.core.resources.scan_nfc +import org.meshtastic.core.resources.ssid +import org.meshtastic.core.resources.subnet +import org.meshtastic.core.resources.udp_enabled +import org.meshtastic.core.resources.wifi_config +import org.meshtastic.core.resources.wifi_enabled +import org.meshtastic.core.resources.wifi_ip +import org.meshtastic.core.resources.wifi_qr_code_error +import org.meshtastic.core.resources.wifi_qr_code_scan import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditIPv4Preference import org.meshtastic.core.ui.component.EditPasswordPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PacketResponseStateDialog.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PacketResponseStateDialog.kt index 9b664650a9..366f8669c1 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PacketResponseStateDialog.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PacketResponseStateDialog.kt @@ -33,10 +33,10 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.coroutines.delay import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.close -import org.meshtastic.core.strings.delivery_confirmed -import org.meshtastic.core.strings.error +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.close +import org.meshtastic.core.resources.delivery_confirmed +import org.meshtastic.core.resources.error import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.feature.settings.radio.ResponseState diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PaxcounterConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PaxcounterConfigItemList.kt index c32a54eb37..b268bbecef 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PaxcounterConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PaxcounterConfigItemList.kt @@ -26,13 +26,13 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.ble_rssi_threshold_defaults_to_80 -import org.meshtastic.core.strings.paxcounter -import org.meshtastic.core.strings.paxcounter_config -import org.meshtastic.core.strings.paxcounter_enabled -import org.meshtastic.core.strings.update_interval_seconds -import org.meshtastic.core.strings.wifi_rssi_threshold_defaults_to_80 +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.ble_rssi_threshold_defaults_to_80 +import org.meshtastic.core.resources.paxcounter +import org.meshtastic.core.resources.paxcounter_config +import org.meshtastic.core.resources.paxcounter_enabled +import org.meshtastic.core.resources.update_interval_seconds +import org.meshtastic.core.resources.wifi_rssi_threshold_defaults_to_80 import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.SignedIntegerEditTextPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PositionConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PositionConfigItemList.kt index 41dfe39448..7b33f74ac2 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PositionConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PositionConfigItemList.kt @@ -40,31 +40,31 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.common.permissions.ble.RequireLocation import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.Position -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.advanced_device_gps -import org.meshtastic.core.strings.altitude -import org.meshtastic.core.strings.broadcast_interval -import org.meshtastic.core.strings.config_position_broadcast_secs_summary -import org.meshtastic.core.strings.config_position_broadcast_smart_minimum_distance_summary -import org.meshtastic.core.strings.config_position_broadcast_smart_minimum_interval_secs_summary -import org.meshtastic.core.strings.config_position_flags_summary -import org.meshtastic.core.strings.config_position_gps_update_interval_summary -import org.meshtastic.core.strings.device_gps -import org.meshtastic.core.strings.fixed_position -import org.meshtastic.core.strings.gps_en_gpio -import org.meshtastic.core.strings.gps_mode -import org.meshtastic.core.strings.gps_receive_gpio -import org.meshtastic.core.strings.gps_transmit_gpio -import org.meshtastic.core.strings.latitude -import org.meshtastic.core.strings.longitude -import org.meshtastic.core.strings.minimum_distance -import org.meshtastic.core.strings.minimum_interval -import org.meshtastic.core.strings.position -import org.meshtastic.core.strings.position_config_set_fixed_from_phone -import org.meshtastic.core.strings.position_flags -import org.meshtastic.core.strings.position_packet -import org.meshtastic.core.strings.smart_position -import org.meshtastic.core.strings.update_interval +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.advanced_device_gps +import org.meshtastic.core.resources.altitude +import org.meshtastic.core.resources.broadcast_interval +import org.meshtastic.core.resources.config_position_broadcast_secs_summary +import org.meshtastic.core.resources.config_position_broadcast_smart_minimum_distance_summary +import org.meshtastic.core.resources.config_position_broadcast_smart_minimum_interval_secs_summary +import org.meshtastic.core.resources.config_position_flags_summary +import org.meshtastic.core.resources.config_position_gps_update_interval_summary +import org.meshtastic.core.resources.device_gps +import org.meshtastic.core.resources.fixed_position +import org.meshtastic.core.resources.gps_en_gpio +import org.meshtastic.core.resources.gps_mode +import org.meshtastic.core.resources.gps_receive_gpio +import org.meshtastic.core.resources.gps_transmit_gpio +import org.meshtastic.core.resources.latitude +import org.meshtastic.core.resources.longitude +import org.meshtastic.core.resources.minimum_distance +import org.meshtastic.core.resources.minimum_interval +import org.meshtastic.core.resources.position +import org.meshtastic.core.resources.position_config_set_fixed_from_phone +import org.meshtastic.core.resources.position_flags +import org.meshtastic.core.resources.position_packet +import org.meshtastic.core.resources.smart_position +import org.meshtastic.core.resources.update_interval import org.meshtastic.core.ui.component.BitwisePreference import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PowerConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PowerConfigItemList.kt index 1321dc885f..6b6b349c1f 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PowerConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/PowerConfigItemList.kt @@ -26,18 +26,18 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.adc_multiplier_override -import org.meshtastic.core.strings.adc_multiplier_override_ratio -import org.meshtastic.core.strings.battery_ina_2xx_i2c_address -import org.meshtastic.core.strings.config_power_is_power_saving_summary -import org.meshtastic.core.strings.enable_power_saving_mode -import org.meshtastic.core.strings.minimum_wake_time_seconds -import org.meshtastic.core.strings.power -import org.meshtastic.core.strings.power_config -import org.meshtastic.core.strings.shutdown_on_power_loss -import org.meshtastic.core.strings.super_deep_sleep_duration_seconds -import org.meshtastic.core.strings.wait_for_bluetooth_duration_seconds +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.adc_multiplier_override +import org.meshtastic.core.resources.adc_multiplier_override_ratio +import org.meshtastic.core.resources.battery_ina_2xx_i2c_address +import org.meshtastic.core.resources.config_power_is_power_saving_summary +import org.meshtastic.core.resources.enable_power_saving_mode +import org.meshtastic.core.resources.minimum_wake_time_seconds +import org.meshtastic.core.resources.power +import org.meshtastic.core.resources.power_config +import org.meshtastic.core.resources.shutdown_on_power_loss +import org.meshtastic.core.resources.super_deep_sleep_duration_seconds +import org.meshtastic.core.resources.wait_for_bluetooth_duration_seconds import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RadioConfigScreenList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RadioConfigScreenList.kt index 0a855a2414..71b5ffb411 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RadioConfigScreenList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RadioConfigScreenList.kt @@ -34,9 +34,9 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.unit.dp import com.squareup.wire.Message import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.discard_changes -import org.meshtastic.core.strings.save_changes +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.discard_changes +import org.meshtastic.core.resources.save_changes import org.meshtastic.core.ui.component.MainAppBar import org.meshtastic.core.ui.component.PreferenceFooter import org.meshtastic.feature.settings.radio.ResponseState diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RangeTestConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RangeTestConfigItemList.kt index 66061c4046..ea78843d07 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RangeTestConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RangeTestConfigItemList.kt @@ -24,12 +24,12 @@ import androidx.compose.runtime.remember import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.range_test -import org.meshtastic.core.strings.range_test_config -import org.meshtastic.core.strings.range_test_enabled -import org.meshtastic.core.strings.save_csv_in_storage_esp32_only -import org.meshtastic.core.strings.sender_message_interval_seconds +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.range_test +import org.meshtastic.core.resources.range_test_config +import org.meshtastic.core.resources.range_test_enabled +import org.meshtastic.core.resources.save_csv_in_storage_esp32_only +import org.meshtastic.core.resources.sender_message_interval_seconds import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.core.ui.component.TitledCard diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RemoteHardwareConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RemoteHardwareConfigItemList.kt index d13f4e2eed..1fba75ddb1 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RemoteHardwareConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/RemoteHardwareConfigItemList.kt @@ -25,12 +25,12 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.allow_undefined_pin_access -import org.meshtastic.core.strings.available_pins -import org.meshtastic.core.strings.remote_hardware -import org.meshtastic.core.strings.remote_hardware_config -import org.meshtastic.core.strings.remote_hardware_enabled +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.allow_undefined_pin_access +import org.meshtastic.core.resources.available_pins +import org.meshtastic.core.resources.remote_hardware +import org.meshtastic.core.resources.remote_hardware_config +import org.meshtastic.core.resources.remote_hardware_enabled import org.meshtastic.core.ui.component.EditListPreference import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.core.ui.component.TitledCard diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/SecurityConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/SecurityConfigItemList.kt index ee78eac503..5610483937 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/SecurityConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/SecurityConfigItemList.kt @@ -42,29 +42,29 @@ import okio.ByteString.Companion.toByteString import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.common.util.nowMillis import org.meshtastic.core.model.util.encodeToString -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.admin_key -import org.meshtastic.core.strings.admin_keys -import org.meshtastic.core.strings.administration -import org.meshtastic.core.strings.config_security_admin_key -import org.meshtastic.core.strings.config_security_debug_log_api_enabled -import org.meshtastic.core.strings.config_security_is_managed -import org.meshtastic.core.strings.config_security_private_key -import org.meshtastic.core.strings.config_security_public_key -import org.meshtastic.core.strings.config_security_serial_enabled -import org.meshtastic.core.strings.debug_log_api_enabled -import org.meshtastic.core.strings.direct_message_key -import org.meshtastic.core.strings.export_keys -import org.meshtastic.core.strings.export_keys_confirmation -import org.meshtastic.core.strings.legacy_admin_channel -import org.meshtastic.core.strings.logs -import org.meshtastic.core.strings.managed_mode -import org.meshtastic.core.strings.private_key -import org.meshtastic.core.strings.public_key -import org.meshtastic.core.strings.regenerate_keys_confirmation -import org.meshtastic.core.strings.regenerate_private_key -import org.meshtastic.core.strings.security -import org.meshtastic.core.strings.serial_console +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.admin_key +import org.meshtastic.core.resources.admin_keys +import org.meshtastic.core.resources.administration +import org.meshtastic.core.resources.config_security_admin_key +import org.meshtastic.core.resources.config_security_debug_log_api_enabled +import org.meshtastic.core.resources.config_security_is_managed +import org.meshtastic.core.resources.config_security_private_key +import org.meshtastic.core.resources.config_security_public_key +import org.meshtastic.core.resources.config_security_serial_enabled +import org.meshtastic.core.resources.debug_log_api_enabled +import org.meshtastic.core.resources.direct_message_key +import org.meshtastic.core.resources.export_keys +import org.meshtastic.core.resources.export_keys_confirmation +import org.meshtastic.core.resources.legacy_admin_channel +import org.meshtastic.core.resources.logs +import org.meshtastic.core.resources.managed_mode +import org.meshtastic.core.resources.private_key +import org.meshtastic.core.resources.public_key +import org.meshtastic.core.resources.regenerate_keys_confirmation +import org.meshtastic.core.resources.regenerate_private_key +import org.meshtastic.core.resources.security +import org.meshtastic.core.resources.serial_console import org.meshtastic.core.ui.component.CopyIconButton import org.meshtastic.core.ui.component.EditBase64Preference import org.meshtastic.core.ui.component.EditListPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/SerialConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/SerialConfigItemList.kt index cdd08e93cc..779030aad4 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/SerialConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/SerialConfigItemList.kt @@ -25,15 +25,15 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.echo_enabled -import org.meshtastic.core.strings.override_console_serial_port -import org.meshtastic.core.strings.serial -import org.meshtastic.core.strings.serial_baud_rate -import org.meshtastic.core.strings.serial_config -import org.meshtastic.core.strings.serial_enabled -import org.meshtastic.core.strings.serial_mode -import org.meshtastic.core.strings.timeout +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.echo_enabled +import org.meshtastic.core.resources.override_console_serial_port +import org.meshtastic.core.resources.serial +import org.meshtastic.core.resources.serial_baud_rate +import org.meshtastic.core.resources.serial_config +import org.meshtastic.core.resources.serial_enabled +import org.meshtastic.core.resources.serial_mode +import org.meshtastic.core.resources.timeout import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ShutdownConfirmationDialog.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ShutdownConfirmationDialog.kt index 53b831ccf2..2176b32be1 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ShutdownConfirmationDialog.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ShutdownConfirmationDialog.kt @@ -34,11 +34,11 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.Node -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.send -import org.meshtastic.core.strings.shutdown_node_name -import org.meshtastic.core.strings.shutdown_warning +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.send +import org.meshtastic.core.resources.shutdown_node_name +import org.meshtastic.core.resources.shutdown_warning import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.core.ui.theme.AppTheme import org.meshtastic.proto.User diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/StatusMessageConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/StatusMessageConfigItemList.kt index 57a4ca4cfb..de0e0b4cc9 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/StatusMessageConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/StatusMessageConfigItemList.kt @@ -32,11 +32,11 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.clear -import org.meshtastic.core.strings.node_status_summary -import org.meshtastic.core.strings.status_message -import org.meshtastic.core.strings.status_message_config +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.clear +import org.meshtastic.core.resources.node_status_summary +import org.meshtastic.core.resources.status_message +import org.meshtastic.core.resources.status_message_config import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.TitledCard import org.meshtastic.feature.settings.radio.RadioConfigViewModel diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/StoreForwardConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/StoreForwardConfigItemList.kt index 76719a80c8..11a75d37e3 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/StoreForwardConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/StoreForwardConfigItemList.kt @@ -25,15 +25,15 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.heartbeat -import org.meshtastic.core.strings.history_return_max -import org.meshtastic.core.strings.history_return_window -import org.meshtastic.core.strings.number_of_records -import org.meshtastic.core.strings.server -import org.meshtastic.core.strings.store_forward -import org.meshtastic.core.strings.store_forward_config -import org.meshtastic.core.strings.store_forward_enabled +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.heartbeat +import org.meshtastic.core.resources.history_return_max +import org.meshtastic.core.resources.history_return_window +import org.meshtastic.core.resources.number_of_records +import org.meshtastic.core.resources.server +import org.meshtastic.core.resources.store_forward +import org.meshtastic.core.resources.store_forward_config +import org.meshtastic.core.resources.store_forward_enabled import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.core.ui.component.TitledCard diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/TelemetryConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/TelemetryConfigItemList.kt index 0c2655b1a5..2921adccd0 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/TelemetryConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/TelemetryConfigItemList.kt @@ -25,21 +25,21 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.model.Capabilities -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.air_quality_metrics_module_enabled -import org.meshtastic.core.strings.air_quality_metrics_update_interval_seconds -import org.meshtastic.core.strings.device_metrics_update_interval_seconds -import org.meshtastic.core.strings.device_telemetry_enabled -import org.meshtastic.core.strings.device_telemetry_enabled_summary -import org.meshtastic.core.strings.environment_metrics_module_enabled -import org.meshtastic.core.strings.environment_metrics_on_screen_enabled -import org.meshtastic.core.strings.environment_metrics_update_interval_seconds -import org.meshtastic.core.strings.environment_metrics_use_fahrenheit -import org.meshtastic.core.strings.power_metrics_module_enabled -import org.meshtastic.core.strings.power_metrics_on_screen_enabled -import org.meshtastic.core.strings.power_metrics_update_interval_seconds -import org.meshtastic.core.strings.telemetry -import org.meshtastic.core.strings.telemetry_config +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.air_quality_metrics_module_enabled +import org.meshtastic.core.resources.air_quality_metrics_update_interval_seconds +import org.meshtastic.core.resources.device_metrics_update_interval_seconds +import org.meshtastic.core.resources.device_telemetry_enabled +import org.meshtastic.core.resources.device_telemetry_enabled_summary +import org.meshtastic.core.resources.environment_metrics_module_enabled +import org.meshtastic.core.resources.environment_metrics_on_screen_enabled +import org.meshtastic.core.resources.environment_metrics_update_interval_seconds +import org.meshtastic.core.resources.environment_metrics_use_fahrenheit +import org.meshtastic.core.resources.power_metrics_module_enabled +import org.meshtastic.core.resources.power_metrics_on_screen_enabled +import org.meshtastic.core.resources.power_metrics_update_interval_seconds +import org.meshtastic.core.resources.telemetry +import org.meshtastic.core.resources.telemetry_config import org.meshtastic.core.ui.component.DropDownPreference import org.meshtastic.core.ui.component.SwitchPreference import org.meshtastic.core.ui.component.TitledCard diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/UserConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/UserConfigItemList.kt index 7a72712311..2a7c673e2d 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/UserConfigItemList.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/UserConfigItemList.kt @@ -31,17 +31,17 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import org.meshtastic.core.database.model.isUnmessageableRole import org.meshtastic.core.model.Capabilities -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.hardware_model -import org.meshtastic.core.strings.licensed_amateur_radio -import org.meshtastic.core.strings.licensed_amateur_radio_text -import org.meshtastic.core.strings.long_name -import org.meshtastic.core.strings.node_id -import org.meshtastic.core.strings.short_name -import org.meshtastic.core.strings.unmessageable -import org.meshtastic.core.strings.unmonitored_or_infrastructure -import org.meshtastic.core.strings.user -import org.meshtastic.core.strings.user_config +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.hardware_model +import org.meshtastic.core.resources.licensed_amateur_radio +import org.meshtastic.core.resources.licensed_amateur_radio_text +import org.meshtastic.core.resources.long_name +import org.meshtastic.core.resources.node_id +import org.meshtastic.core.resources.short_name +import org.meshtastic.core.resources.unmessageable +import org.meshtastic.core.resources.unmonitored_or_infrastructure +import org.meshtastic.core.resources.user +import org.meshtastic.core.resources.user_config import org.meshtastic.core.ui.component.EditTextPreference import org.meshtastic.core.ui.component.RegularPreference import org.meshtastic.core.ui.component.SwitchPreference diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/WarningDialog.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/WarningDialog.kt index a6275b41c7..7148fb738f 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/WarningDialog.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/WarningDialog.kt @@ -22,9 +22,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.Preview import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.cancel -import org.meshtastic.core.strings.send +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.cancel +import org.meshtastic.core.resources.send import org.meshtastic.core.ui.component.MeshtasticDialog import org.meshtastic.core.ui.theme.AppTheme diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/FixedUpdateIntervals.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/FixedUpdateIntervals.kt index b74451a089..636c5af3b0 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/FixedUpdateIntervals.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/FixedUpdateIntervals.kt @@ -21,12 +21,12 @@ package org.meshtastic.feature.settings.util import org.jetbrains.compose.resources.PluralStringResource import org.jetbrains.compose.resources.StringResource import org.meshtastic.core.model.util.TimeConstants -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.interval_always_on -import org.meshtastic.core.strings.interval_unset -import org.meshtastic.core.strings.plurals_hours -import org.meshtastic.core.strings.plurals_minutes -import org.meshtastic.core.strings.plurals_seconds +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.interval_always_on +import org.meshtastic.core.resources.interval_unset +import org.meshtastic.core.resources.plurals_hours +import org.meshtastic.core.resources.plurals_minutes +import org.meshtastic.core.resources.plurals_seconds import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/LanguageUtils.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/LanguageUtils.kt index 6ae1dd6143..2553d25615 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/LanguageUtils.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/LanguageUtils.kt @@ -23,12 +23,12 @@ import androidx.compose.ui.platform.LocalResources import androidx.core.os.LocaleListCompat import co.touchlab.kermit.Logger import org.jetbrains.compose.resources.stringResource -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.fr_HT -import org.meshtastic.core.strings.preferences_system_default -import org.meshtastic.core.strings.pt_BR -import org.meshtastic.core.strings.zh_CN -import org.meshtastic.core.strings.zh_TW +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.fr_HT +import org.meshtastic.core.resources.preferences_system_default +import org.meshtastic.core.resources.pt_BR +import org.meshtastic.core.resources.zh_CN +import org.meshtastic.core.resources.zh_TW import org.xmlpull.v1.XmlPullParser import java.util.Locale diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/UiText.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/UiText.kt deleted file mode 100644 index a894ddfcbe..0000000000 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/UiText.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 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 org.meshtastic.feature.settings.util - -import androidx.compose.runtime.Composable -import org.jetbrains.compose.resources.stringResource - -@Suppress("SpreadOperator") -sealed class UiText { - data class DynamicString(val value: String) : UiText() - - class StringResource(val resId: org.jetbrains.compose.resources.StringResource, vararg val args: Any) : UiText() - - @Composable - fun asString(): String = when (this) { - is DynamicString -> value - is StringResource -> stringResource(resId, *args) - } -} diff --git a/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/HomoglyphSettingTest.kt b/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/HomoglyphSettingTest.kt index f8760d3baf..1c1863346c 100644 --- a/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/HomoglyphSettingTest.kt +++ b/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/HomoglyphSettingTest.kt @@ -25,9 +25,9 @@ import androidx.compose.ui.test.onNodeWithText import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.meshtastic.core.strings.Res -import org.meshtastic.core.strings.getString -import org.meshtastic.core.strings.use_homoglyph_characters_encoding +import org.meshtastic.core.resources.Res +import org.meshtastic.core.resources.getString +import org.meshtastic.core.resources.use_homoglyph_characters_encoding import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import java.util.Locale diff --git a/settings.gradle.kts b/settings.gradle.kts index e522fb5bb9..16c8309fb1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,7 +33,7 @@ include( ":core:prefs", ":core:proto", ":core:service", - ":core:strings", + ":core:resources", ":core:ui", ":feature:intro", ":feature:messaging",