Make :core:strings a Compose Multiplatform library (#3617)

This commit is contained in:
Phil Oliver
2025-11-10 19:58:38 -05:00
committed by GitHub
parent d7fff4add2
commit 28590bfcdf
206 changed files with 1722 additions and 470 deletions

View File

@@ -87,42 +87,42 @@ android {
androidResources.localeFilters.addAll(
listOf(
"en",
"ar-rSA",
"b+sr+Latn",
"bg-rBG",
"ca-rES",
"cs-rCZ",
"de-rDE",
"el-rGR",
"es-rES",
"et-rEE",
"fi-rFI",
"fr-rFR",
"ga-rIE",
"gl-rES",
"hr-rHR",
"ht-rHT",
"hu-rHU",
"is-rIS",
"it-rIT",
"iw-rIL",
"ja-rJP",
"ko-rKR",
"lt-rLT",
"nl-rNL",
"no-rNO",
"pl-rPL",
"ar",
"bg",
"ca",
"cs",
"de",
"el",
"es",
"et",
"fi",
"fr",
"ga",
"gl",
"hr",
"ht",
"hu",
"is",
"it",
"iw",
"ja",
"ko",
"lt",
"nl",
"no",
"pl",
"pt",
"pt-rBR",
"pt-rPT",
"ro-rRO",
"ru-rRU",
"sk-rSK",
"sl-rSI",
"sq-rAL",
"ro",
"ru",
"sk",
"sl",
"sq",
"sr",
"srp",
"sv-rSE",
"tr-rTR",
"uk-rUA",
"sv",
"tr",
"uk",
"zh-rCN",
"zh-rTW",
),

View File

@@ -46,13 +46,15 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.meshtastic.core.datastore.UiPreferencesDataSource
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.strings.contact_invalid
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.ui.theme.MODE_DYNAMIC
import org.meshtastic.core.ui.util.showToast
import org.meshtastic.feature.intro.AppIntroductionScreen
import timber.log.Timber
import javax.inject.Inject
import org.meshtastic.core.strings.R as Res
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

View File

@@ -47,14 +47,18 @@ 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.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.strings.pairing_completed
import org.meshtastic.core.strings.pairing_failed_try_again
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import timber.log.Timber
import javax.inject.Inject
import org.meshtastic.core.strings.R as Res
/**
* A sealed class is used here to represent the different types of devices that can be displayed in the list. This is
@@ -135,8 +139,7 @@ constructor(
val idBytes = txtRecords["id"]
val shortName =
shortNameBytes?.let { String(it, Charsets.UTF_8) }
?: context.getString(Res.string.meshtastic)
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 }) {
@@ -283,10 +286,10 @@ constructor(
if (state != BluetoothDevice.BOND_BONDING) {
Timber.d("Bonding completed, state=$state")
if (state == BluetoothDevice.BOND_BONDED) {
setErrorText(context.getString(Res.string.pairing_completed))
setErrorText(getString(Res.string.pairing_completed))
changeDeviceAddress("x${device.address}")
} else {
setErrorText(context.getString(Res.string.pairing_failed_try_again))
setErrorText(getString(Res.string.pairing_failed_try_again))
}
}
}

View File

@@ -39,6 +39,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
import org.jetbrains.compose.resources.getString
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import org.meshtastic.core.data.repository.FirmwareReleaseRepository
import org.meshtastic.core.data.repository.MeshLogRepository
@@ -51,6 +52,8 @@ import org.meshtastic.core.model.util.toChannelSet
import org.meshtastic.core.service.IMeshService
import org.meshtastic.core.service.MeshServiceNotifications
import org.meshtastic.core.service.ServiceRepository
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.client_notification
import org.meshtastic.core.ui.component.toSharedContact
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import org.meshtastic.proto.AdminProtos
@@ -58,7 +61,6 @@ import org.meshtastic.proto.AppOnlyProtos
import org.meshtastic.proto.MeshProtos
import timber.log.Timber
import javax.inject.Inject
import org.meshtastic.core.strings.R as Res
// Given a human name, strip out the first letter of the first three words and return that as the
// initials for
@@ -178,7 +180,7 @@ constructor(
.filterNotNull()
.onEach {
showAlert(
title = app.getString(Res.string.client_notification),
title = getString(Res.string.client_notification),
message = it,
onConfirm = { serviceRepository.clearErrorMessage() },
dismissable = false,

View File

@@ -17,7 +17,6 @@
package com.geeksville.mesh.navigation
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CellTower
import androidx.compose.material.icons.filled.LightMode
@@ -39,11 +38,22 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation
import androidx.navigation.navDeepLink
import org.jetbrains.compose.resources.StringResource
import org.meshtastic.core.navigation.ContactsRoutes
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.R
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.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.feature.map.node.NodeMapScreen
import org.meshtastic.feature.map.node.NodeMapViewModel
import org.meshtastic.feature.node.detail.NodeDetailScreen
@@ -57,7 +67,6 @@ import org.meshtastic.feature.node.metrics.PositionLogScreen
import org.meshtastic.feature.node.metrics.PowerMetricsScreen
import org.meshtastic.feature.node.metrics.SignalMetricsScreen
import org.meshtastic.feature.node.metrics.TracerouteLogScreen
import org.meshtastic.core.strings.R as Res
fun NavGraphBuilder.nodesGraph(navController: NavHostController) {
navigation<NodesRoutes.NodesGraph>(startDestination = NodesRoutes.Nodes) {
@@ -198,7 +207,7 @@ private inline fun <reified R : Route> NavGraphBuilder.addNodeDetailScreenCompos
}
enum class NodeDetailRoute(
@StringRes val title: Int,
val title: StringResource,
val route: Route,
val icon: ImageVector?,
val screenComposable: @Composable (metricsViewModel: MetricsViewModel, onNavigateUp: () -> Unit) -> Unit,

View File

@@ -37,6 +37,7 @@ import com.geeksville.mesh.util.ignoreException
import com.geeksville.mesh.util.toRemoteExceptions
import com.google.protobuf.ByteString
import com.google.protobuf.InvalidProtocolBufferException
import com.meshtastic.core.strings.getString
import dagger.Lazy
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CancellationException
@@ -86,6 +87,14 @@ import org.meshtastic.core.service.MeshServiceNotifications
import org.meshtastic.core.service.SERVICE_NOTIFY_ID
import org.meshtastic.core.service.ServiceAction
import org.meshtastic.core.service.ServiceRepository
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.connected_count
import org.meshtastic.core.strings.critical_alert
import org.meshtastic.core.strings.device_sleeping
import org.meshtastic.core.strings.disconnected
import org.meshtastic.core.strings.error_duty_cycle
import org.meshtastic.core.strings.unknown_username
import org.meshtastic.core.strings.waypoint_received
import org.meshtastic.proto.AdminProtos
import org.meshtastic.proto.AppOnlyProtos
import org.meshtastic.proto.ChannelProtos
@@ -113,7 +122,6 @@ import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import kotlin.math.absoluteValue
import org.meshtastic.core.strings.R as Res
/**
* Handles all the communication with android apps. Also keeps an internal model of the network state.

View File

@@ -28,7 +28,6 @@ import android.content.Intent
import android.graphics.Color
import android.media.AudioAttributes
import android.media.RingtoneManager
import androidx.annotation.StringRes
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import androidx.core.app.RemoteInput
@@ -37,17 +36,32 @@ import androidx.core.net.toUri
import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R.raw
import com.geeksville.mesh.service.ReplyReceiver.Companion.KEY_TEXT_REPLY
import com.meshtastic.core.strings.getString
import dagger.hilt.android.qualifiers.ApplicationContext
import org.jetbrains.compose.resources.StringResource
import org.meshtastic.core.database.entity.NodeEntity
import org.meshtastic.core.model.util.formatUptime
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
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.low_battery_message
import org.meshtastic.core.strings.low_battery_title
import org.meshtastic.core.strings.meshtastic_alerts_notifications
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.new_node_seen
import org.meshtastic.core.strings.no_local_stats
import org.meshtastic.core.strings.reply
import org.meshtastic.proto.MeshProtos
import org.meshtastic.proto.TelemetryProtos
import org.meshtastic.proto.TelemetryProtos.LocalStats
import javax.inject.Inject
import org.meshtastic.core.strings.R as Res
/**
* Manages the creation and display of all app notifications.
@@ -73,7 +87,7 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
*/
private sealed class NotificationType(
val channelId: String,
@StringRes val channelNameRes: Int,
val channelNameRes: StringResource,
val importance: Int,
) {
object ServiceState :

View File

@@ -21,7 +21,6 @@ package com.geeksville.mesh.ui
import android.Manifest
import android.os.Build
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
@@ -71,8 +70,6 @@ import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavDestination
@@ -100,6 +97,9 @@ import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.getString
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.model.DeviceVersion
import org.meshtastic.core.navigation.ConnectionsRoutes
import org.meshtastic.core.navigation.ContactsRoutes
@@ -108,6 +108,25 @@ import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
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.client_notification
import org.meshtastic.core.strings.compromised_keys
import org.meshtastic.core.strings.connected
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.ui.component.MultipleChoiceAlertDialog
import org.meshtastic.core.ui.component.SimpleAlertDialog
import org.meshtastic.core.ui.icon.Conversations
@@ -122,9 +141,8 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
import org.meshtastic.feature.node.metrics.annotateTraceroute
import org.meshtastic.proto.MeshProtos
import timber.log.Timber
import org.meshtastic.core.strings.R as Res
enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector, val route: Route) {
enum class TopLevelDestination(val label: StringResource, val icon: ImageVector, val route: Route) {
Conversations(Res.string.conversations, MeshtasticIcons.Conversations, ContactsRoutes.ContactsGraph),
Nodes(Res.string.nodes, MeshtasticIcons.Nodes, NodesRoutes.NodesGraph),
Map(Res.string.map, MeshtasticIcons.Map, MapRoutes.Map),
@@ -392,7 +410,6 @@ private fun VersionChecks(viewModel: UIViewModel) {
val connectionState by viewModel.connectionState.collectAsStateWithLifecycle()
val myNodeInfo by viewModel.myNodeInfo.collectAsStateWithLifecycle()
val context = LocalContext.current
val resources = LocalResources.current
val myFirmwareVersion = myNodeInfo?.firmwareVersion
@@ -424,8 +441,8 @@ private fun VersionChecks(viewModel: UIViewModel) {
val isOld = info.minAppVersion > BuildConfig.VERSION_CODE && BuildConfig.DEBUG.not()
if (isOld) {
viewModel.showAlert(
resources.getString(Res.string.app_too_old),
resources.getString(Res.string.must_update),
getString(Res.string.app_too_old),
getString(Res.string.must_update),
dismissable = false,
onConfirm = {
val service = viewModel.meshService ?: return@showAlert
@@ -436,8 +453,8 @@ private fun VersionChecks(viewModel: UIViewModel) {
myFirmwareVersion?.let {
val curVer = DeviceVersion(it)
if (curVer < MeshService.absoluteMinDeviceVersion) {
val title = resources.getString(Res.string.firmware_too_old)
val message = resources.getString(Res.string.firmware_old)
val title = getString(Res.string.firmware_too_old)
val message = getString(Res.string.firmware_old)
viewModel.showAlert(
title = title,
html = message,
@@ -448,9 +465,8 @@ private fun VersionChecks(viewModel: UIViewModel) {
},
)
} else if (curVer < MeshService.minDeviceVersion) {
val title = resources.getString(Res.string.should_update_firmware)
val message =
resources.getString(Res.string.should_update, latestStableFirmwareRelease.asString)
val title = getString(Res.string.should_update_firmware)
val message = getString(Res.string.should_update, latestStableFirmwareRelease.asString)
viewModel.showAlert(title = title, message = message, dismissable = false, onConfirm = {})
}
}

View File

@@ -46,8 +46,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -62,9 +60,20 @@ import com.geeksville.mesh.ui.connections.components.NetworkDevices
import com.geeksville.mesh.ui.connections.components.UsbDevices
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import kotlinx.coroutines.delay
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.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.connections
import org.meshtastic.core.strings.must_set_region
import org.meshtastic.core.strings.not_connected
import org.meshtastic.core.strings.set_your_region
import org.meshtastic.core.strings.warning_not_paired
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.TitledCard
@@ -73,7 +82,6 @@ import org.meshtastic.feature.settings.navigation.getNavRouteFrom
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.feature.settings.radio.component.PacketResponseStateDialog
import org.meshtastic.proto.ConfigProtos
import org.meshtastic.core.strings.R as Res
fun String?.isIPAddress(): Boolean = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
@Suppress("DEPRECATION")
@@ -104,7 +112,6 @@ fun ConnectionsScreen(
val connectionState by
connectionsViewModel.connectionState.collectAsStateWithLifecycle(ConnectionState.DISCONNECTED)
val scanning by scanModel.spinner.collectAsStateWithLifecycle(false)
val context = LocalContext.current
val ourNode by connectionsViewModel.ourNodeInfo.collectAsStateWithLifecycle()
val selectedDevice by scanModel.selectedNotNullFlow.collectAsStateWithLifecycle()
val bluetoothState by connectionsViewModel.bluetoothState.collectAsStateWithLifecycle()
@@ -158,7 +165,7 @@ fun ConnectionsScreen(
ConnectionState.DISCONNECTED -> Res.string.not_connected
ConnectionState.DEVICE_SLEEP -> Res.string.connected_sleeping
}.let { scanModel.setErrorText(context.getString(it)) }
}.let { scanModel.setErrorText(getString(it)) }
}
Scaffold(

View File

@@ -43,7 +43,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.BTScanModel
@@ -51,9 +50,20 @@ import com.geeksville.mesh.model.DeviceListEntry
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.MultiplePermissionsState
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.service.ConnectionState
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.bluetooth_available_devices
import org.meshtastic.core.strings.bluetooth_disabled
import org.meshtastic.core.strings.bluetooth_paired_devices
import org.meshtastic.core.strings.grant_permissions
import org.meshtastic.core.strings.no_ble_devices
import org.meshtastic.core.strings.open_settings
import org.meshtastic.core.strings.permission_missing
import org.meshtastic.core.strings.permission_missing_31
import org.meshtastic.core.strings.scan
import org.meshtastic.core.strings.scanning_bluetooth
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.core.strings.R as Res
/**
* Composable that displays a list of Bluetooth Low Energy (BLE) devices and allows scanning. It handles Bluetooth

View File

@@ -17,7 +17,6 @@
package com.geeksville.mesh.ui.connections.components
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Bluetooth
import androidx.compose.material.icons.rounded.Usb
@@ -30,12 +29,16 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
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.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Suppress("LambdaParameterEventTrailing")
@Composable
@@ -58,7 +61,7 @@ fun ConnectionsSegmentedBar(
}
}
private enum class Item(val imageVector: ImageVector, @StringRes val textRes: Int, val deviceType: DeviceType) {
private enum class Item(val imageVector: ImageVector, val textRes: StringResource, val deviceType: DeviceType) {
BLUETOOTH(imageVector = Icons.Rounded.Bluetooth, textRes = Res.string.bluetooth, deviceType = DeviceType.BLE),
NETWORK(imageVector = Icons.Rounded.Wifi, textRes = Res.string.network, deviceType = DeviceType.TCP),
SERIAL(imageVector = Icons.Rounded.Usb, textRes = Res.string.serial, deviceType = DeviceType.USB),

View File

@@ -32,11 +32,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
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.Node
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.disconnect
import org.meshtastic.core.strings.firmware_version
import org.meshtastic.core.ui.component.MaterialBatteryInfo
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.core.ui.theme.AppTheme
@@ -44,7 +47,6 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.proto.MeshProtos
import org.meshtastic.proto.PaxcountProtos
import org.meshtastic.proto.TelemetryProtos
import org.meshtastic.core.strings.R as Res
/** Converts Bluetooth RSSI to a 0-4 bar signal strength level. */
@Composable

View File

@@ -33,9 +33,13 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import com.geeksville.mesh.model.DeviceListEntry
import org.meshtastic.core.strings.R as Res
import org.jetbrains.compose.resources.stringResource
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
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable

View File

@@ -46,7 +46,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.PreviewLightDark
@@ -56,10 +55,20 @@ import com.geeksville.mesh.model.DeviceListEntry
import com.geeksville.mesh.repository.network.NetworkRepository
import com.geeksville.mesh.ui.connections.isIPAddress
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.stringResource
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.cancel
import org.meshtastic.core.strings.confirm_delete_node
import org.meshtastic.core.strings.delete
import org.meshtastic.core.strings.discovered_network_devices
import org.meshtastic.core.strings.ip_address
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.TitledCard
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("MagicNumber", "LongMethod")

View File

@@ -21,14 +21,15 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.UsbOff
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.model.DeviceListEntry
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.service.ConnectionState
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.no_usb_devices
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun UsbDevices(

View File

@@ -44,7 +44,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
@@ -53,10 +52,14 @@ import androidx.compose.ui.text.style.TextOverflow
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.ui.component.SecurityIcon
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.proto.AppOnlyProtos
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@Composable

View File

@@ -55,19 +55,37 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.Contact
import org.jetbrains.compose.resources.pluralStringResource
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.database.entity.ContactSettings
import org.meshtastic.core.model.util.formatMuteRemainingTime
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.cancel
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.ui.component.MainAppBar
import org.meshtastic.proto.AppOnlyProtos
import java.util.concurrent.TimeUnit
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Suppress("LongMethod")

View File

@@ -17,15 +17,14 @@
package com.geeksville.mesh.ui.contact
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.geeksville.mesh.model.Contact
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.getString
import org.meshtastic.core.data.repository.NodeRepository
import org.meshtastic.core.data.repository.PacketRepository
import org.meshtastic.core.data.repository.RadioConfigRepository
@@ -34,17 +33,17 @@ import org.meshtastic.core.model.DataPacket
import org.meshtastic.core.model.util.getChannel
import org.meshtastic.core.model.util.getShortDate
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
import kotlin.collections.map
import org.meshtastic.core.strings.R as Res
@HiltViewModel
class ContactsViewModel
@Inject
constructor(
@ApplicationContext private val context: Context,
private val nodeRepository: NodeRepository,
private val packetRepository: PacketRepository,
radioConfigRepository: RadioConfigRepository,
@@ -87,7 +86,7 @@ constructor(
val shortName = user.shortName
val longName =
if (toBroadcast) {
channelSet.getChannel(data.channel)?.name ?: context.getString(Res.string.channel_name)
channelSet.getChannel(data.channel)?.name ?: getString(Res.string.channel_name)
} else {
user.longName
}

View File

@@ -78,7 +78,6 @@ import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
@@ -94,12 +93,31 @@ import com.google.accompanist.permissions.rememberPermissionState
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.model.Channel
import org.meshtastic.core.model.util.getChannelUrl
import org.meshtastic.core.model.util.qrCode
import org.meshtastic.core.model.util.toChannelSet
import org.meshtastic.core.navigation.Route
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.channel_invalid
import org.meshtastic.core.strings.copy
import org.meshtastic.core.strings.edit
import org.meshtastic.core.strings.modem_preset
import org.meshtastic.core.strings.navigate_into_label
import org.meshtastic.core.strings.qr_code
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.scan
import org.meshtastic.core.strings.send
import org.meshtastic.core.strings.url
import org.meshtastic.core.ui.component.AdaptiveTwoPane
import org.meshtastic.core.ui.component.ChannelSelection
import org.meshtastic.core.ui.component.MainAppBar
@@ -116,7 +134,6 @@ import org.meshtastic.proto.ConfigProtos
import org.meshtastic.proto.channelSet
import org.meshtastic.proto.copy
import timber.log.Timber
import org.meshtastic.core.strings.R as Res
/**
* Composable screen for managing and sharing Meshtastic channels. Allows users to view, edit, and share channel

View File

@@ -35,7 +35,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
@@ -43,9 +42,15 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
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.ui.component.MainAppBar
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun ShareScreen(viewModel: ContactsViewModel = hiltViewModel(), onConfirm: (String) -> Unit, onNavigateUp: () -> Unit) {

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
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 <https://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name" translatable="false">Meshtastic</string>
</resources>

View File

@@ -112,5 +112,10 @@ gradlePlugin {
implementationClass = "SpotlessConventionPlugin"
}
register("kmpLibrary") {
id = libs.plugins.meshtastic.kmp.library.get().pluginId
implementationClass = "KmpLibraryConventionPlugin"
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
import com.android.build.api.dsl.androidLibrary
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import java.io.FileInputStream
import java.util.Properties
class KmpLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
apply(plugin = "org.jetbrains.kotlin.multiplatform")
apply(plugin = "org.jetbrains.kotlin.plugin.compose")
apply(plugin = "com.android.kotlin.multiplatform.library")
apply(plugin = "meshtastic.detekt")
apply(plugin = "meshtastic.spotless")
apply(plugin = "com.autonomousapps.dependency-analysis")
val configPropertiesFile = rootProject.file("config.properties")
val configProperties = Properties()
if (configPropertiesFile.exists()) {
FileInputStream(configPropertiesFile).use { configProperties.load(it) }
}
extensions.configure<KotlinMultiplatformExtension> {
@Suppress("UnstableApiUsage")
androidLibrary {
compileSdk = configProperties.getProperty("COMPILE_SDK").toInt()
minSdk = configProperties.getProperty("MIN_SDK").toInt()
}
}
}
}
}

View File

@@ -19,8 +19,10 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.kotlin.multiplatform.library) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.compose) apply false
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.compose.multiplatform) apply false
alias(libs.plugins.datadog) apply false
alias(libs.plugins.devtools.ksp) apply false
alias(libs.plugins.firebase.crashlytics) apply false
@@ -30,6 +32,7 @@ plugins {
alias(libs.plugins.room) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.multiplatform) apply false
alias(libs.plugins.kotlin.parcelize) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.ktorfit) apply false

View File

@@ -17,15 +17,37 @@
package org.meshtastic.core.database.model
import androidx.annotation.StringRes
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.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_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.proto.MeshProtos.Routing
import org.meshtastic.core.strings.R as Res
@Suppress("CyclomaticComplexMethod")
@StringRes
fun getStringResFrom(routingError: Int): Int = when (routingError) {
fun getStringResFrom(routingError: Int): StringResource = when (routingError) {
Routing.Error.NONE_VALUE -> Res.string.routing_error_none
Routing.Error.NO_ROUTE_VALUE -> Res.string.routing_error_no_route
Routing.Error.GOT_NAK_VALUE -> Res.string.routing_error_got_nak
@@ -66,7 +88,7 @@ data class Message(
val viaMqtt: Boolean = false,
val relayNode: Int? = null,
) {
fun getStatusStringRes(): Pair<Int, Int> {
fun getStatusStringRes(): Pair<StringResource, StringResource> {
val title = if (routingError > 0) Res.string.error else Res.string.message_delivery_status
val text =
when (status) {

View File

@@ -17,10 +17,17 @@
package org.meshtastic.core.database.model
import androidx.annotation.StringRes
import org.meshtastic.core.strings.R as Res
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
enum class NodeSortOption(val sqlValue: String, @StringRes val stringRes: Int) {
enum class NodeSortOption(val sqlValue: String, val stringRes: StringResource) {
LAST_HEARD("last_heard", Res.string.node_sort_last_heard),
ALPHABETICAL("alpha", Res.string.node_sort_alpha),
DISTANCE("distance", Res.string.node_sort_distance),

View File

@@ -17,12 +17,21 @@
package org.meshtastic.core.model
import androidx.annotation.StringRes
import org.jetbrains.compose.resources.StringResource
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_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.proto.ConfigProtos.Config.LoRaConfig
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig.ModemPreset
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig.RegionCode
import kotlin.math.floor
import org.meshtastic.core.strings.R as Res
/** hash a string into an integer using the djb2 algorithm by Dan Bernstein http://www.cse.yorku.ca/~oz/hash.html */
private fun hash(name: String): UInt { // using UInt instead of Long to match RadioInterface.cpp results
@@ -296,7 +305,7 @@ enum class RegionInfo(
}
}
enum class ChannelOption(val modemPreset: ModemPreset, @StringRes val labelRes: Int, val bandwidth: Float) {
enum class ChannelOption(val modemPreset: ModemPreset, val labelRes: StringResource, val bandwidth: Float) {
VERY_LONG_SLOW(ModemPreset.VERY_LONG_SLOW, Res.string.label_very_long_slow, .0625f),
LONG_FAST(ModemPreset.LONG_FAST, Res.string.label_long_fast, .250f),
LONG_MODERATE(ModemPreset.LONG_MODERATE, Res.string.label_long_moderate, .125f),

View File

@@ -14,7 +14,30 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import com.android.build.api.dsl.androidLibrary
plugins { alias(libs.plugins.meshtastic.android.library) }
plugins {
alias(libs.plugins.meshtastic.kmp.library)
alias(libs.plugins.compose.multiplatform)
}
android { namespace = "org.meshtastic.core.strings" }
kotlin {
@Suppress("UnstableApiUsage")
androidLibrary {
namespace = "org.meshtastic.core.strings"
androidResources.enable = true
}
sourceSets {
commonMain.dependencies {
implementation(compose.runtime)
// API because consuming modules will always need this dependency
api(compose.components.resources)
}
}
}
compose.resources {
publicResClass = true
packageOfResClass = "org.meshtastic.core.strings"
}

View File

@@ -0,0 +1,39 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.meshtastic.core.strings
import android.content.Context
import android.content.res.Resources
import kotlinx.coroutines.runBlocking
import org.jetbrains.compose.resources.StringResource
fun Context.getString(stringResource: StringResource): String = runBlocking {
org.jetbrains.compose.resources.getString(stringResource)
}
fun Context.getString(stringResource: StringResource, vararg formatArgs: Any): String = runBlocking {
org.jetbrains.compose.resources.getString(stringResource, *formatArgs)
}
fun Resources.getString(stringResource: StringResource): String = runBlocking {
org.jetbrains.compose.resources.getString(stringResource)
}
fun Resources.getString(stringResource: StringResource, vararg formatArgs: Any): String = runBlocking {
org.jetbrains.compose.resources.getString(stringResource, *formatArgs)
}

View File

@@ -22,7 +22,6 @@
<string name="zh_CN" translatable="false">简体中文</string>
<string name="zh_TW" translatable="false">繁體中文</string>
<string name="app_name" translatable="false">Meshtastic</string>
<string name="some_username" translatable="false">SKH</string>
<string name="sample_message" translatable="false">hey I found the cache, it is over here next to the big tiger. I\'m kinda scared.</string>

View File

@@ -29,7 +29,6 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
@@ -37,7 +36,9 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R as Res
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.okay
@Composable
fun SimpleAlertDialog(

View File

@@ -38,7 +38,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.R as Res
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.clear
import org.meshtastic.core.strings.close
@OptIn(ExperimentalMaterial3Api::class)
@Composable

View File

@@ -17,7 +17,6 @@
package org.meshtastic.core.ui.component
import androidx.annotation.StringRes
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.material3.Icon
@@ -28,11 +27,12 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
@Composable
fun ClickableTextField(
@StringRes label: Int,
label: StringResource,
enabled: Boolean,
trailingIcon: ImageVector,
value: String,

View File

@@ -40,7 +40,6 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
@@ -55,15 +54,19 @@ import com.google.zxing.WriterException
import com.journeyapps.barcodescanner.BarcodeEncoder
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DeviceVersion
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.qr_code
import org.meshtastic.core.strings.scan_qr_code
import org.meshtastic.core.strings.share_contact
import org.meshtastic.core.ui.R
import org.meshtastic.core.ui.share.SharedContactDialog
import org.meshtastic.proto.AdminProtos
import org.meshtastic.proto.MeshProtos
import timber.log.Timber
import java.net.MalformedURLException
import org.meshtastic.core.strings.R as Res
/**
* Composable FloatingActionButton to initiate scanning a QR code for adding a contact. Handles camera permission

View File

@@ -27,9 +27,10 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.res.stringResource
import kotlinx.coroutines.launch
import org.meshtastic.core.strings.R as Res
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.copy
@Composable
fun CopyIconButton(

View File

@@ -39,16 +39,18 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.protobuf.ByteString
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.model.Channel
import org.meshtastic.core.model.util.encodeToString
import org.meshtastic.core.model.util.toByteString
import org.meshtastic.core.strings.R as Res
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.error
import org.meshtastic.core.strings.reset
@Suppress("LongMethod")
@Composable

View File

@@ -35,17 +35,23 @@ import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.protobuf.ByteString
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.proto.ModuleConfigProtos.RemoteHardwarePin
import org.meshtastic.proto.ModuleConfigProtos.RemoteHardwarePinType
import org.meshtastic.proto.copy
import org.meshtastic.proto.remoteHardwarePin
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@Composable

View File

@@ -29,13 +29,15 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import org.meshtastic.core.strings.R as Res
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
@Composable
fun EditPasswordPreference(

View File

@@ -38,13 +38,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusState
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R as Res
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.error
@Composable
fun SignedIntegerEditTextPreference(

View File

@@ -51,12 +51,16 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
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.ui.theme.IAQColors.IAQDangerouslyPolluted
import org.meshtastic.core.ui.theme.IAQColors.IAQExcellent
import org.meshtastic.core.ui.theme.IAQColors.IAQExtremelyPolluted
@@ -65,7 +69,6 @@ import org.meshtastic.core.ui.theme.IAQColors.IAQHeavilyPolluted
import org.meshtastic.core.ui.theme.IAQColors.IAQLightlyPolluted
import org.meshtastic.core.ui.theme.IAQColors.IAQModeratelyPolluted
import org.meshtastic.core.ui.theme.IAQColors.IAQSeverelyPolluted
import org.meshtastic.core.strings.R as Res
@Suppress("MagicNumber")
enum class Iaq(val color: Color, val description: String, val range: IntRange) {

View File

@@ -43,13 +43,22 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
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.ui.theme.StatusColors.StatusGreen
import org.meshtastic.core.ui.theme.StatusColors.StatusOrange
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.core.ui.theme.StatusColors.StatusYellow
import org.meshtastic.core.strings.R as Res
const val SNR_GOOD_THRESHOLD = -7f
const val SNR_FAIR_THRESHOLD = -15f
@@ -59,7 +68,7 @@ const val RSSI_FAIR_THRESHOLD = -126
@Stable
enum class Quality(
@Stable val nameRes: Int,
@Stable val nameRes: StringResource,
@Stable val imageVector: ImageVector,
@Stable val color: @Composable () -> Color,
) {

View File

@@ -33,17 +33,20 @@ 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.stringResource
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.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.application_icon
import org.meshtastic.core.strings.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
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable

View File

@@ -33,11 +33,13 @@ import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
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.unknown
import org.meshtastic.core.ui.icon.BatteryEmpty
import org.meshtastic.core.ui.icon.BatteryUnknown
import org.meshtastic.core.ui.icon.MeshtasticIcons
@@ -45,7 +47,6 @@ import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
import org.meshtastic.core.ui.theme.StatusColors.StatusOrange
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.core.strings.R as Res
private const val FORMAT = "%d%%"
private const val SIZE_ICON = 20

View File

@@ -35,11 +35,13 @@ import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
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.ui.icon.MeshtasticIcons
import org.meshtastic.core.ui.icon.SignalCellular0Bar
import org.meshtastic.core.ui.icon.SignalCellular1Bar
@@ -51,7 +53,6 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
import org.meshtastic.core.ui.theme.StatusColors.StatusOrange
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.core.ui.theme.StatusColors.StatusYellow
import org.meshtastic.core.strings.R as Res
private const val SIZE_ICON = 20

View File

@@ -18,7 +18,6 @@
package org.meshtastic.core.ui.component
import android.util.Base64
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -53,19 +52,32 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import com.google.protobuf.ByteString
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.model.Channel
import org.meshtastic.core.strings.R
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.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.ui.theme.AppTheme
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.core.ui.theme.StatusColors.StatusYellow
import org.meshtastic.core.strings.R as Res
/**
* function to display information about the current node's encryption key.
@@ -131,9 +143,9 @@ fun NodeKeyStatusIcon(
enum class NodeKeySecurityState(
@Stable val icon: ImageVector,
@Stable val color: @Composable () -> Color,
@StringRes val descriptionResId: Int,
@StringRes val helpTextResId: Int,
@Stable val title: Int,
val descriptionResId: StringResource,
val helpTextResId: StringResource,
@Stable val title: StringResource,
) {
// State for public key mismatch
PKM(
@@ -164,7 +176,7 @@ enum class NodeKeySecurityState(
}
@Composable
private fun KeyStatusDialog(@StringRes title: Int, @StringRes text: Int, key: ByteString?, onDismiss: () -> Unit = {}) {
private fun KeyStatusDialog(title: StringResource, text: StringResource, key: ByteString?, onDismiss: () -> Unit = {}) {
var showAll by rememberSaveable { mutableStateOf(false) }
AlertDialog(
modifier = Modifier,

View File

@@ -27,15 +27,17 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
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 kotlin.math.pow
import kotlin.math.roundToInt
import org.meshtastic.core.strings.R as Res
private const val POSITION_ENABLED = 32
private const val POSITION_DISABLED = 0

View File

@@ -17,7 +17,6 @@
package org.meshtastic.core.ui.component
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@@ -29,17 +28,18 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
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
@Deprecated(message = "Use overload that accepts Strings for button text.")
@Composable
fun PreferenceFooter(
enabled: Boolean,
@StringRes negativeText: Int,
negativeText: StringResource,
onNegativeClicked: () -> Unit,
@StringRes positiveText: Int,
positiveText: StringResource,
onPositiveClicked: () -> Unit,
modifier: Modifier = Modifier,
) {

View File

@@ -19,7 +19,6 @@
package org.meshtastic.core.ui.component
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -57,18 +56,34 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
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.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.ui.theme.StatusColors.StatusGreen
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.core.ui.theme.StatusColors.StatusYellow
import org.meshtastic.proto.AppOnlyProtos
import org.meshtastic.proto.ChannelProtos.ChannelSettings
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig
import org.meshtastic.core.strings.R as Res
private const val PRECISE_POSITION_BITS = 32
@@ -87,8 +102,8 @@ private const val PRECISE_POSITION_BITS = 32
enum class SecurityState(
@Stable val icon: ImageVector,
@Stable val color: @Composable () -> Color,
@StringRes val descriptionResId: Int,
@StringRes val helpTextResId: Int,
val descriptionResId: StringResource,
val helpTextResId: StringResource,
@Stable val badgeIcon: ImageVector? = null,
@Stable val badgeIconColor: @Composable () -> Color? = { null },
) {

View File

@@ -28,15 +28,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
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.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.channel_air_util
import org.meshtastic.core.strings.hops_away
import org.meshtastic.core.strings.signal
import org.meshtastic.core.strings.signal_quality
import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
const val MAX_VALID_SNR = 100F
const val MAX_VALID_RSSI = 0

View File

@@ -17,7 +17,6 @@
package org.meshtastic.core.ui.component
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -28,16 +27,21 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
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.cancel
import org.meshtastic.core.strings.message
import org.meshtastic.core.strings.okay
import org.meshtastic.core.strings.sample_message
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun SimpleAlertDialog(
@StringRes title: Int,
title: StringResource,
text: @Composable (() -> Unit)? = null,
confirmText: String? = null,
onConfirm: (() -> Unit)? = null,
@@ -74,8 +78,8 @@ fun SimpleAlertDialog(
@Composable
fun SimpleAlertDialog(
@StringRes title: Int,
@StringRes text: Int,
title: StringResource,
text: StringResource,
onConfirm: (() -> Unit)? = null,
onDismiss: () -> Unit = {},
) = SimpleAlertDialog(
@@ -87,7 +91,7 @@ fun SimpleAlertDialog(
@Composable
fun SimpleAlertDialog(
@StringRes title: Int,
title: StringResource,
text: String,
onConfirm: (() -> Unit)? = null,
onDismiss: () -> Unit = {},

View File

@@ -43,7 +43,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.unit.dp
@@ -51,13 +50,19 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
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.cancel
import org.meshtastic.core.strings.new_channel_rcvd
import org.meshtastic.core.strings.replace
import org.meshtastic.core.ui.component.ChannelSelection
import org.meshtastic.proto.AppOnlyProtos.ChannelSet
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig.ModemPreset
import org.meshtastic.proto.channelSet
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@Composable
fun ScannedQrCodeDialog(

View File

@@ -23,14 +23,19 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
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.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.ui.component.SimpleAlertDialog
import org.meshtastic.core.ui.component.compareUsers
import org.meshtastic.core.ui.component.userFieldsToString
import org.meshtastic.proto.AdminProtos
import org.meshtastic.core.strings.R as Res
/** A dialog for importing a shared contact that was scanned from a QR code. */
@Composable

View File

@@ -19,16 +19,17 @@ package org.meshtastic.core.ui.util
import android.content.Context
import android.widget.Toast
import androidx.annotation.StringRes
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.getString
suspend fun Context.showToast(@StringRes resId: Int) {
showToast(getString(resId))
suspend fun Context.showToast(stringResource: StringResource) {
showToast(getString(stringResource))
}
suspend fun Context.showToast(text: CharSequence) {
suspend fun Context.showToast(stringResource: StringResource, vararg formatArgs: Any) {
Toast.makeText(this, getString(stringResource, formatArgs), Toast.LENGTH_SHORT).show()
}
suspend fun Context.showToast(text: String) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
}
suspend fun Context.showToast(@StringRes resId: Int, vararg formatArgs: Any) {
Toast.makeText(this, getString(resId, formatArgs), Toast.LENGTH_SHORT).show()
}

View File

@@ -18,7 +18,9 @@
package org.meshtastic.core.ui.util
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.unknown_age
import org.meshtastic.proto.ChannelProtos
import org.meshtastic.proto.ChannelProtos.ChannelSettings
import org.meshtastic.proto.MeshProtos
@@ -28,7 +30,6 @@ import org.meshtastic.proto.channel
import org.meshtastic.proto.channelSettings
import java.text.DateFormat
import kotlin.time.Duration.Companion.days
import org.meshtastic.core.strings.R as Res
private const val SECONDS_TO_MILLIS = 1000L

View File

@@ -2,7 +2,7 @@ pull_request_labels:
- automation
- l10n
files:
- source: /**/src/main/res/values/strings.xml
- source: /**/src/commonMain/composeResources/values/strings.xml
translation: /%original_path%-%android_code%/strings.xml
translate_attributes: 0
content_segmentation: 0

View File

@@ -24,12 +24,18 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
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.ui.component.AutoLinkText
import org.meshtastic.core.strings.R as Res
@Composable
fun AnalyticsIntro(modifier: Modifier = Modifier) {

View File

@@ -30,11 +30,15 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R as Res
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
/**
* Screen for explaining and guiding the user to configure critical alert settings. This screen is part of the app

View File

@@ -17,8 +17,8 @@
package org.meshtastic.feature.intro
import androidx.annotation.StringRes
import androidx.compose.ui.graphics.vector.ImageVector
import org.jetbrains.compose.resources.StringResource
/**
* Data class representing the UI elements for a feature row in the app introduction.
@@ -29,6 +29,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
*/
internal data class FeatureUIData(
val icon: ImageVector,
@StringRes val titleRes: Int? = null,
@StringRes val subtitleRes: Int,
val titleRes: StringResource? = null,
val subtitleRes: StringResource,
)

View File

@@ -18,7 +18,6 @@
package org.meshtastic.feature.intro
import android.content.Context
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@@ -29,13 +28,14 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
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
/** Tag used for identifying clickable annotations in text, specifically for linking to settings. */
internal const val SETTINGS_TAG = "settings_link_tag"
@@ -80,8 +80,8 @@ internal fun FeatureRow(feature: FeatureUIData) {
*/
@Composable
internal fun Context.createClickableAnnotatedString(
@StringRes fullTextRes: Int,
@StringRes linkTextRes: Int,
fullTextRes: StringResource,
linkTextRes: StringResource,
tag: String,
): AnnotatedString {
val fullText = stringResource(fullTextRes)

View File

@@ -26,7 +26,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.R as Res
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
/**
* Screen for configuring location permissions during the app introduction. It explains why location permissions are

View File

@@ -33,11 +33,24 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R as Res
import org.jetbrains.compose.resources.stringResource
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.critical_alerts
import org.meshtastic.core.strings.critical_alerts_description
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
/**
* Screen for configuring notification permissions during the app introduction. It explains why notification permissions

Some files were not shown because too many files have changed in this diff Show More