Alias strings R to Res (#3619)

This commit is contained in:
Phil Oliver
2025-11-04 22:32:42 -05:00
committed by GitHub
parent a687328f08
commit 0833a6767e
153 changed files with 1403 additions and 1350 deletions

View File

@@ -45,12 +45,12 @@ import com.geeksville.mesh.ui.MainScreen
import dagger.hilt.android.AndroidEntryPoint
import org.meshtastic.core.datastore.UiPreferencesDataSource
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.ui.theme.MODE_DYNAMIC
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() {
@@ -119,13 +119,13 @@ class MainActivity : AppCompatActivity() {
Timber.d("App link data is a channel set")
model.requestChannelUrl(
url = it,
onFailure = { Toast.makeText(this, R.string.channel_invalid, Toast.LENGTH_SHORT).show() },
onFailure = { Toast.makeText(this, Res.string.channel_invalid, Toast.LENGTH_SHORT).show() },
)
} else if (it.path?.startsWith("/v/") == true || it.path?.startsWith("/V/") == true) {
Timber.d("App link data is a shared contact")
model.setSharedContactRequested(
url = it,
onFailure = { Toast.makeText(this, R.string.contact_invalid, Toast.LENGTH_SHORT).show() },
onFailure = { Toast.makeText(this, Res.string.contact_invalid, Toast.LENGTH_SHORT).show() },
)
} else {
Timber.d("App link data is not a channel set")

View File

@@ -51,10 +51,10 @@ 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.R
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,7 +135,8 @@ constructor(
val idBytes = txtRecords["id"]
val shortName =
shortNameBytes?.let { String(it, Charsets.UTF_8) } ?: context.getString(R.string.meshtastic)
shortNameBytes?.let { String(it, Charsets.UTF_8) }
?: context.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 }) {
@@ -282,10 +283,10 @@ constructor(
if (state != BluetoothDevice.BOND_BONDING) {
Timber.d("Bonding completed, state=$state")
if (state == BluetoothDevice.BOND_BONDED) {
setErrorText(context.getString(R.string.pairing_completed))
setErrorText(context.getString(Res.string.pairing_completed))
changeDeviceAddress("x${device.address}")
} else {
setErrorText(context.getString(R.string.pairing_failed_try_again))
setErrorText(context.getString(Res.string.pairing_failed_try_again))
}
}
}

View File

@@ -51,7 +51,6 @@ 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.R
import org.meshtastic.core.ui.component.toSharedContact
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import org.meshtastic.proto.AdminProtos
@@ -59,6 +58,7 @@ 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 +178,7 @@ constructor(
.filterNotNull()
.onEach {
showAlert(
title = app.getString(R.string.client_notification),
title = app.getString(Res.string.client_notification),
message = it,
onConfirm = { serviceRepository.clearErrorMessage() },
dismissable = false,

View File

@@ -44,7 +44,6 @@ 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.feature.map.node.NodeMapScreen
import org.meshtastic.feature.map.node.NodeMapViewModel
import org.meshtastic.feature.node.detail.NodeDetailScreen
@@ -58,6 +57,7 @@ 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) {
@@ -204,49 +204,49 @@ enum class NodeDetailRoute(
val screenComposable: @Composable (metricsViewModel: MetricsViewModel, onNavigateUp: () -> Unit) -> Unit,
) {
DEVICE(
R.string.device,
Res.string.device,
NodeDetailRoutes.DeviceMetrics,
Icons.Default.Router,
{ metricsVM, onNavigateUp -> DeviceMetricsScreen(metricsVM, onNavigateUp) },
),
POSITION_LOG(
R.string.position_log,
Res.string.position_log,
NodeDetailRoutes.PositionLog,
Icons.Default.LocationOn,
{ metricsVM, onNavigateUp -> PositionLogScreen(metricsVM, onNavigateUp) },
),
ENVIRONMENT(
R.string.environment,
Res.string.environment,
NodeDetailRoutes.EnvironmentMetrics,
Icons.Default.LightMode,
{ metricsVM, onNavigateUp -> EnvironmentMetricsScreen(metricsVM, onNavigateUp) },
),
SIGNAL(
R.string.signal,
Res.string.signal,
NodeDetailRoutes.SignalMetrics,
Icons.Default.CellTower,
{ metricsVM, onNavigateUp -> SignalMetricsScreen(metricsVM, onNavigateUp) },
),
TRACEROUTE(
R.string.traceroute,
Res.string.traceroute,
NodeDetailRoutes.TracerouteLog,
Icons.Default.PermScanWifi,
{ metricsVM, onNavigateUp -> TracerouteLogScreen(viewModel = metricsVM, onNavigateUp = onNavigateUp) },
),
POWER(
R.string.power,
Res.string.power,
NodeDetailRoutes.PowerMetrics,
Icons.Default.Power,
{ metricsVM, onNavigateUp -> PowerMetricsScreen(metricsVM, onNavigateUp) },
),
HOST(
R.string.host,
Res.string.host,
NodeDetailRoutes.HostMetricsLog,
Icons.Default.Memory,
{ metricsVM, onNavigateUp -> HostMetricsLogScreen(metricsVM, onNavigateUp) },
),
PAX(
R.string.pax,
Res.string.pax,
NodeDetailRoutes.PaxMetrics,
Icons.Default.People,
{ metricsVM, onNavigateUp -> PaxMetricsScreen(metricsVM, onNavigateUp) },

View File

@@ -85,7 +85,6 @@ 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.R
import org.meshtastic.proto.AdminProtos
import org.meshtastic.proto.AppOnlyProtos
import org.meshtastic.proto.ChannelProtos
@@ -114,6 +113,7 @@ 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.
@@ -224,7 +224,7 @@ class MeshService : Service() {
private fun getSenderName(packet: DataPacket?): String {
val name = nodeDBbyID[packet?.from]?.user?.longName
return name ?: getString(R.string.unknown_username)
return name ?: getString(Res.string.unknown_username)
}
/** start our location requests (if they weren't already running) */
@@ -269,7 +269,7 @@ class MeshService : Service() {
serviceNotifications.showAlertNotification(
contactKey,
getSenderName(dataPacket),
dataPacket.alert ?: getString(R.string.critical_alert),
dataPacket.alert ?: getString(Res.string.critical_alert),
)
}
@@ -278,7 +278,7 @@ class MeshService : Service() {
when (dataPacket.dataType) {
Portnums.PortNum.TEXT_MESSAGE_APP_VALUE -> dataPacket.text!!
Portnums.PortNum.WAYPOINT_APP_VALUE -> {
getString(R.string.waypoint_received, dataPacket.waypoint!!.name)
getString(Res.string.waypoint_received, dataPacket.waypoint!!.name)
}
else -> return
@@ -786,7 +786,7 @@ class MeshService : Service() {
val u = MeshProtos.Routing.parseFrom(data.payload)
if (u.errorReason == MeshProtos.Routing.Error.DUTY_CYCLE_LIMIT) {
serviceRepository.setErrorMessage(getString(R.string.error_duty_cycle))
serviceRepository.setErrorMessage(getString(Res.string.error_duty_cycle))
}
handleAckNak(data.requestId, fromId, u.errorReasonValue, dataPacket.relayNode)
@@ -1317,10 +1317,10 @@ class MeshService : Service() {
private fun updateServiceStatusNotification(telemetry: TelemetryProtos.Telemetry? = null): Notification {
val notificationSummary =
when (connectionStateHolder.getState()) {
ConnectionState.CONNECTED -> getString(R.string.connected_count).format(numOnlineNodes)
ConnectionState.CONNECTED -> getString(Res.string.connected_count).format(numOnlineNodes)
ConnectionState.DISCONNECTED -> getString(R.string.disconnected)
ConnectionState.DEVICE_SLEEP -> getString(R.string.device_sleeping)
ConnectionState.DISCONNECTED -> getString(Res.string.disconnected)
ConnectionState.DEVICE_SLEEP -> getString(Res.string.device_sleeping)
}
return serviceNotifications.updateServiceStateNotification(
summaryString = notificationSummary,

View File

@@ -43,11 +43,11 @@ 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.R
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.
@@ -79,54 +79,58 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
object ServiceState :
NotificationType(
"my_service",
R.string.meshtastic_service_notifications,
Res.string.meshtastic_service_notifications,
NotificationManager.IMPORTANCE_MIN,
)
object DirectMessage :
NotificationType(
"my_messages",
R.string.meshtastic_messages_notifications,
Res.string.meshtastic_messages_notifications,
NotificationManager.IMPORTANCE_HIGH,
)
object BroadcastMessage :
NotificationType(
"my_broadcasts",
R.string.meshtastic_broadcast_notifications,
Res.string.meshtastic_broadcast_notifications,
NotificationManager.IMPORTANCE_DEFAULT,
)
object Alert :
NotificationType(
"my_alerts",
R.string.meshtastic_alerts_notifications,
Res.string.meshtastic_alerts_notifications,
NotificationManager.IMPORTANCE_HIGH,
)
object NewNode :
NotificationType(
"new_nodes",
R.string.meshtastic_new_nodes_notifications,
Res.string.meshtastic_new_nodes_notifications,
NotificationManager.IMPORTANCE_DEFAULT,
)
object LowBatteryLocal :
NotificationType(
"low_battery",
R.string.meshtastic_low_battery_notifications,
Res.string.meshtastic_low_battery_notifications,
NotificationManager.IMPORTANCE_DEFAULT,
)
object LowBatteryRemote :
NotificationType(
"low_battery_remote",
R.string.meshtastic_low_battery_temporary_remote_notifications,
Res.string.meshtastic_low_battery_temporary_remote_notifications,
NotificationManager.IMPORTANCE_DEFAULT,
)
object Client :
NotificationType("client_notifications", R.string.client_notification, NotificationManager.IMPORTANCE_HIGH)
NotificationType(
"client_notifications",
Res.string.client_notification,
NotificationManager.IMPORTANCE_HIGH,
)
companion object {
// A list of all types for easy initialization.
@@ -241,7 +245,7 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
null
}
cachedMessage = message ?: cachedMessage ?: context.getString(R.string.no_local_stats)
cachedMessage = message ?: cachedMessage ?: context.getString(Res.string.no_local_stats)
val notification =
createServiceStateNotification(
@@ -277,7 +281,7 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
override fun showClientNotification(clientNotification: MeshProtos.ClientNotification) {
val notification =
createClientNotification(context.getString(R.string.client_notification), clientNotification.message)
createClientNotification(context.getString(Res.string.client_notification), clientNotification.message)
notificationManager.notify(clientNotification.toString().hashCode(), notification)
}
@@ -357,7 +361,7 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
}
private fun createNewNodeSeenNotification(name: String, message: String?): Notification {
val title = context.getString(R.string.new_node_seen).format(name)
val title = context.getString(Res.string.new_node_seen).format(name)
val builder =
commonBuilder(NotificationType.NewNode)
.setCategory(Notification.CATEGORY_STATUS)
@@ -375,9 +379,9 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
private fun createLowBatteryNotification(node: NodeEntity, isRemote: Boolean): Notification {
val type = if (isRemote) NotificationType.LowBatteryRemote else NotificationType.LowBatteryLocal
val title = context.getString(R.string.low_battery_title).format(node.shortName)
val title = context.getString(Res.string.low_battery_title).format(node.shortName)
val message =
context.getString(R.string.low_battery_message).format(node.longName, node.deviceMetrics.batteryLevel)
context.getString(Res.string.low_battery_message).format(node.longName, node.deviceMetrics.batteryLevel)
return commonBuilder(type)
.setCategory(Notification.CATEGORY_STATUS)
@@ -427,7 +431,7 @@ class MeshServiceNotificationsImpl @Inject constructor(@ApplicationContext priva
}
private fun createReplyAction(contactKey: String): NotificationCompat.Action {
val replyLabel = context.getString(R.string.reply)
val replyLabel = context.getString(Res.string.reply)
val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(replyLabel).build()
val replyIntent =

View File

@@ -108,7 +108,6 @@ 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.R
import org.meshtastic.core.ui.component.MultipleChoiceAlertDialog
import org.meshtastic.core.ui.component.SimpleAlertDialog
import org.meshtastic.core.ui.icon.Conversations
@@ -123,13 +122,14 @@ 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) {
Conversations(R.string.conversations, MeshtasticIcons.Conversations, ContactsRoutes.ContactsGraph),
Nodes(R.string.nodes, MeshtasticIcons.Nodes, NodesRoutes.NodesGraph),
Map(R.string.map, MeshtasticIcons.Map, MapRoutes.Map),
Settings(R.string.bottom_nav_settings, MeshtasticIcons.Settings, SettingsRoutes.SettingsGraph()),
Connections(R.string.connections, Icons.Rounded.Wifi, ConnectionsRoutes.ConnectionsGraph),
Conversations(Res.string.conversations, MeshtasticIcons.Conversations, ContactsRoutes.ContactsGraph),
Nodes(Res.string.nodes, MeshtasticIcons.Nodes, NodesRoutes.NodesGraph),
Map(Res.string.map, MeshtasticIcons.Map, MapRoutes.Map),
Settings(Res.string.bottom_nav_settings, MeshtasticIcons.Settings, SettingsRoutes.SettingsGraph()),
Connections(Res.string.connections, Icons.Rounded.Wifi, ConnectionsRoutes.ConnectionsGraph),
;
companion object {
@@ -195,13 +195,13 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode
var message = notification.message
val compromisedKeys =
if (notification.hasLowEntropyKey() || notification.hasDuplicatedPublicKey()) {
message = stringResource(R.string.compromised_keys)
message = stringResource(Res.string.compromised_keys)
true
} else {
false
}
SimpleAlertDialog(
title = R.string.client_notification,
title = Res.string.client_notification,
text = { Text(text = message) },
onConfirm = {
if (compromisedKeys) {
@@ -216,13 +216,13 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode
val traceRouteResponse by uIViewModel.tracerouteResponse.observeAsState()
traceRouteResponse?.let { response ->
SimpleAlertDialog(
title = R.string.traceroute,
title = Res.string.traceroute,
text = {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
Text(text = annotateTraceroute(response))
}
},
dismissText = stringResource(R.string.okay),
dismissText = stringResource(Res.string.okay),
onDismiss = { uIViewModel.clearTracerouteResponse() },
)
}
@@ -278,9 +278,10 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode
Text(
if (isConnectionsRoute) {
when (connectionState) {
ConnectionState.CONNECTED -> stringResource(R.string.connected)
ConnectionState.DEVICE_SLEEP -> stringResource(R.string.device_sleeping)
ConnectionState.DISCONNECTED -> stringResource(R.string.disconnected)
ConnectionState.CONNECTED -> stringResource(Res.string.connected)
ConnectionState.DEVICE_SLEEP ->
stringResource(Res.string.device_sleeping)
ConnectionState.DISCONNECTED -> stringResource(Res.string.disconnected)
}
} else {
stringResource(destination.label)
@@ -423,8 +424,8 @@ private fun VersionChecks(viewModel: UIViewModel) {
val isOld = info.minAppVersion > BuildConfig.VERSION_CODE && BuildConfig.DEBUG.not()
if (isOld) {
viewModel.showAlert(
resources.getString(R.string.app_too_old),
resources.getString(R.string.must_update),
resources.getString(Res.string.app_too_old),
resources.getString(Res.string.must_update),
dismissable = false,
onConfirm = {
val service = viewModel.meshService ?: return@showAlert
@@ -435,8 +436,8 @@ private fun VersionChecks(viewModel: UIViewModel) {
myFirmwareVersion?.let {
val curVer = DeviceVersion(it)
if (curVer < MeshService.absoluteMinDeviceVersion) {
val title = resources.getString(R.string.firmware_too_old)
val message = resources.getString(R.string.firmware_old)
val title = resources.getString(Res.string.firmware_too_old)
val message = resources.getString(Res.string.firmware_old)
viewModel.showAlert(
title = title,
html = message,
@@ -447,9 +448,9 @@ private fun VersionChecks(viewModel: UIViewModel) {
},
)
} else if (curVer < MeshService.minDeviceVersion) {
val title = resources.getString(R.string.should_update_firmware)
val title = resources.getString(Res.string.should_update_firmware)
val message =
resources.getString(R.string.should_update, latestStableFirmwareRelease.asString)
resources.getString(Res.string.should_update, latestStableFirmwareRelease.asString)
viewModel.showAlert(title = title, message = message, dismissable = false, onConfirm = {})
}
}

View File

@@ -65,7 +65,6 @@ import kotlinx.coroutines.delay
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
import org.meshtastic.core.service.ConnectionState
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.TitledCard
@@ -74,6 +73,7 @@ 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")
@@ -153,18 +153,18 @@ fun ConnectionsScreen(
LaunchedEffect(connectionState, regionUnset) {
when (connectionState) {
ConnectionState.CONNECTED -> {
if (regionUnset) R.string.must_set_region else R.string.connected
if (regionUnset) Res.string.must_set_region else Res.string.connected
}
ConnectionState.DISCONNECTED -> R.string.not_connected
ConnectionState.DEVICE_SLEEP -> R.string.connected_sleeping
ConnectionState.DISCONNECTED -> Res.string.not_connected
ConnectionState.DEVICE_SLEEP -> Res.string.connected_sleeping
}.let { scanModel.setErrorText(context.getString(it)) }
}
Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.connections),
title = stringResource(Res.string.connections),
ourNode = ourNode,
showNodeChip = ourNode != null && connectionState.isConnected(),
canNavigateUp = false,
@@ -190,7 +190,7 @@ fun ConnectionsScreen(
) {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
ourNode?.let { node ->
TitledCard(title = stringResource(R.string.connected_device)) {
TitledCard(title = stringResource(Res.string.connected_device)) {
CurrentlyConnectedInfo(
node = node,
onNavigateToNodeDetails = onNavigateToNodeDetails,
@@ -203,7 +203,7 @@ fun ConnectionsScreen(
TitledCard(title = null) {
ListItem(
leadingIcon = Icons.Rounded.Language,
text = stringResource(R.string.set_your_region),
text = stringResource(Res.string.set_your_region),
) {
isWaiting = true
radioConfigViewModel.setResponseStateLoading(ConfigRoute.LORA)
@@ -272,7 +272,7 @@ fun ConnectionsScreen(
bondedBleDevices.none { it is DeviceListEntry.Ble && it.bonded }
if (showWarningNotPaired) {
Text(
text = stringResource(R.string.warning_not_paired),
text = stringResource(Res.string.warning_not_paired),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(horizontal = 16.dp),

View File

@@ -52,8 +52,8 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.MultiplePermissionsState
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import org.meshtastic.core.service.ConnectionState
import org.meshtastic.core.strings.R
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
@@ -118,12 +118,12 @@ fun BLEDevices(
val context = LocalContext.current
EmptyStateContent(
imageVector = Icons.Rounded.BluetoothDisabled,
text = stringResource(R.string.bluetooth_disabled),
text = stringResource(Res.string.bluetooth_disabled),
actionButton = {
val intent = Intent(ACTION_BLUETOOTH_SETTINGS)
if (intent.resolveActivity(context.packageManager) != null) {
Button(onClick = { settingsLauncher.launch(intent) }) {
Text(text = stringResource(R.string.open_settings))
Text(text = stringResource(Res.string.open_settings))
}
}
},
@@ -142,9 +142,9 @@ fun BLEDevices(
Row(modifier = Modifier.alpha(if (isScanning) 0f else 1f)) {
Icon(
imageVector = Icons.Rounded.Search,
contentDescription = stringResource(R.string.scan),
contentDescription = stringResource(Res.string.scan),
)
Text(stringResource(R.string.scan))
Text(stringResource(Res.string.scan))
}
if (isScanning) {
@@ -159,22 +159,22 @@ fun BLEDevices(
imageVector = Icons.Rounded.BluetoothDisabled,
text =
if (isScanning) {
stringResource(R.string.scanning_bluetooth)
stringResource(Res.string.scanning_bluetooth)
} else {
stringResource(R.string.no_ble_devices)
stringResource(Res.string.no_ble_devices)
},
actionButton = scanButton,
)
} else {
bondedDevices.Section(
title = stringResource(R.string.bluetooth_paired_devices),
title = stringResource(Res.string.bluetooth_paired_devices),
connectionState = connectionState,
selectedDevice = selectedDevice,
onSelect = scanModel::onSelected,
)
availableDevices.Section(
title = stringResource(R.string.bluetooth_available_devices),
title = stringResource(Res.string.bluetooth_available_devices),
connectionState = connectionState,
selectedDevice = selectedDevice,
onSelect = scanModel::onSelected,
@@ -189,13 +189,13 @@ fun BLEDevices(
EmptyStateContent(
text =
if (permissionsState.shouldShowRationale) {
stringResource(R.string.permission_missing)
stringResource(Res.string.permission_missing)
} else {
stringResource(R.string.permission_missing_31)
stringResource(Res.string.permission_missing_31)
},
actionButton = {
Button(onClick = { checkPermissionsAndScan(permissionsState, scanModel, bluetoothEnabled) }) {
Text(text = stringResource(R.string.grant_permissions))
Text(text = stringResource(Res.string.grant_permissions))
}
},
)

View File

@@ -34,8 +34,8 @@ 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.meshtastic.core.strings.R
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Suppress("LambdaParameterEventTrailing")
@Composable
@@ -59,9 +59,9 @@ fun ConnectionsSegmentedBar(
}
private enum class Item(val imageVector: ImageVector, @StringRes val textRes: Int, val deviceType: DeviceType) {
BLUETOOTH(imageVector = Icons.Rounded.Bluetooth, textRes = R.string.bluetooth, deviceType = DeviceType.BLE),
NETWORK(imageVector = Icons.Rounded.Wifi, textRes = R.string.network, deviceType = DeviceType.TCP),
SERIAL(imageVector = Icons.Rounded.Usb, textRes = R.string.serial, deviceType = DeviceType.USB),
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),
}
@Preview(showBackground = true)

View File

@@ -37,7 +37,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MaterialBatteryInfo
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.core.ui.theme.AppTheme
@@ -45,6 +44,7 @@ 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
@@ -75,7 +75,7 @@ fun CurrentlyConnectedInfo(
node.metadata?.firmwareVersion?.let { firmwareVersion ->
Text(
text = stringResource(R.string.firmware_version, firmwareVersion),
text = stringResource(Res.string.firmware_version, firmwareVersion),
style = MaterialTheme.typography.bodySmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@@ -94,7 +94,7 @@ fun CurrentlyConnectedInfo(
),
onClick = onClickDisconnect,
) {
Text(stringResource(R.string.disconnect))
Text(stringResource(Res.string.disconnect))
}
}
}

View File

@@ -35,7 +35,7 @@ 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
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
@@ -55,10 +55,10 @@ fun DeviceListItem(connected: Boolean, device: DeviceListEntry, onSelect: () ->
val contentDescription =
when (device) {
is DeviceListEntry.Ble -> stringResource(R.string.bluetooth)
is DeviceListEntry.Usb -> stringResource(R.string.serial)
is DeviceListEntry.Tcp -> stringResource(R.string.network)
is DeviceListEntry.Mock -> stringResource(R.string.add)
is DeviceListEntry.Ble -> stringResource(Res.string.bluetooth)
is DeviceListEntry.Usb -> stringResource(Res.string.serial)
is DeviceListEntry.Tcp -> stringResource(Res.string.network)
is DeviceListEntry.Mock -> stringResource(Res.string.add)
}
val useSelectable = modifier == Modifier

View File

@@ -57,9 +57,9 @@ import com.geeksville.mesh.repository.network.NetworkRepository
import com.geeksville.mesh.ui.connections.isIPAddress
import kotlinx.coroutines.launch
import org.meshtastic.core.service.ConnectionState
import org.meshtastic.core.strings.R
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")
@@ -102,8 +102,11 @@ fun NetworkDevices(
Column(verticalArrangement = Arrangement.spacedBy(16.dp), horizontalAlignment = Alignment.CenterHorizontally) {
val addButton: @Composable () -> Unit = {
Button(onClick = { showSearchDialog = true }) {
Icon(imageVector = Icons.Rounded.Add, contentDescription = stringResource(R.string.add_network_device))
Text(stringResource(R.string.add_network_device))
Icon(
imageVector = Icons.Rounded.Add,
contentDescription = stringResource(Res.string.add_network_device),
)
Text(stringResource(Res.string.add_network_device))
}
}
@@ -111,14 +114,14 @@ fun NetworkDevices(
discoveredNetworkDevices.isEmpty() && recentNetworkDevices.isEmpty() -> {
EmptyStateContent(
imageVector = Icons.Rounded.Wifi,
text = stringResource(R.string.no_network_devices),
text = stringResource(Res.string.no_network_devices),
actionButton = addButton,
)
}
else -> {
if (recentNetworkDevices.isNotEmpty()) {
TitledCard(title = stringResource(R.string.recent_network_devices)) {
TitledCard(title = stringResource(Res.string.recent_network_devices)) {
recentNetworkDevices.forEach { device ->
DeviceListItem(
connected =
@@ -140,7 +143,7 @@ fun NetworkDevices(
}
if (discoveredNetworkDevices.isNotEmpty()) {
TitledCard(title = stringResource(R.string.discovered_network_devices)) {
TitledCard(title = stringResource(Res.string.discovered_network_devices)) {
discoveredNetworkDevices.forEach { device ->
DeviceListItem(
connected =
@@ -179,7 +182,7 @@ private fun AddDeviceDialog(
state = ipState,
labelPosition = TextFieldLabelPosition.Above(),
lineLimits = TextFieldLineLimits.SingleLine,
label = { Text(stringResource(R.string.ip_address)) },
label = { Text(stringResource(Res.string.ip_address)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal, imeAction = ImeAction.Next),
modifier = Modifier.weight(.7f),
)
@@ -189,7 +192,7 @@ private fun AddDeviceDialog(
labelPosition = TextFieldLabelPosition.Above(),
placeholder = { Text(NetworkRepository.SERVICE_PORT.toString()) },
lineLimits = TextFieldLineLimits.SingleLine,
label = { Text(stringResource(R.string.ip_port)) },
label = { Text(stringResource(Res.string.ip_port)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal, imeAction = ImeAction.Done),
modifier = Modifier.weight(.3f),
)
@@ -197,7 +200,7 @@ private fun AddDeviceDialog(
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(modifier = Modifier.weight(1f), onClick = { onHideDialog() }) {
Text(stringResource(R.string.cancel))
Text(stringResource(Res.string.cancel))
}
Button(
@@ -226,7 +229,7 @@ private fun AddDeviceDialog(
}
},
) {
Text(stringResource(R.string.add_network_device))
Text(stringResource(Res.string.add_network_device))
}
}
}
@@ -241,8 +244,8 @@ private fun ConfirmDeleteDialog(
) {
AlertDialog(
onDismissRequest = onHideDialog,
title = { Text(stringResource(R.string.delete)) },
text = { Text(stringResource(R.string.confirm_delete_node)) },
title = { Text(stringResource(Res.string.delete)) },
text = { Text(stringResource(Res.string.confirm_delete_node)) },
confirmButton = {
Button(
onClick = {
@@ -250,10 +253,10 @@ private fun ConfirmDeleteDialog(
onHideDialog()
},
) {
Text(stringResource(R.string.delete))
Text(stringResource(Res.string.delete))
}
},
dismissButton = { Button(onClick = { onHideDialog() }) { Text(stringResource(R.string.cancel)) } },
dismissButton = { Button(onClick = { onHideDialog() }) { Text(stringResource(Res.string.cancel)) } },
)
}

View File

@@ -26,9 +26,9 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.model.DeviceListEntry
import org.meshtastic.core.service.ConnectionState
import org.meshtastic.core.strings.R
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(
@@ -54,7 +54,7 @@ private fun UsbDevices(
) {
when {
usbDevices.isEmpty() ->
EmptyStateContent(imageVector = Icons.Rounded.UsbOff, text = stringResource(R.string.no_usb_devices))
EmptyStateContent(imageVector = Icons.Rounded.UsbOff, text = stringResource(Res.string.no_usb_devices))
else ->
TitledCard(title = null) {

View File

@@ -53,10 +53,10 @@ 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.meshtastic.core.strings.R
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
@@ -197,10 +197,10 @@ private fun ContactItemPreview() {
val sampleContact =
Contact(
contactKey = "0^all",
shortName = stringResource(R.string.some_username),
longName = stringResource(R.string.unknown_username),
shortName = stringResource(Res.string.some_username),
longName = stringResource(Res.string.unknown_username),
lastMessageTime = "Mon",
lastMessageText = stringResource(R.string.sample_message),
lastMessageText = stringResource(Res.string.sample_message),
unreadCount = 2,
messageCount = 10,
isMuted = true,

View File

@@ -64,10 +64,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.Contact
import org.meshtastic.core.database.entity.ContactSettings
import org.meshtastic.core.model.util.formatMuteRemainingTime
import org.meshtastic.core.strings.R
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")
@@ -143,7 +143,7 @@ fun ContactsScreen(
Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.conversations),
title = stringResource(Res.string.conversations),
ourNode = ourNode,
showNodeChip = ourNode != null && connectionState.isConnected(),
canNavigateUp = false,
@@ -232,10 +232,10 @@ fun MuteNotificationsDialog(
// Options for mute duration
val muteOptions = remember {
listOf(
R.string.unmute to 0L,
R.string.mute_8_hours to TimeUnit.HOURS.toMillis(8),
R.string.mute_1_week to TimeUnit.DAYS.toMillis(7),
R.string.mute_always to Long.MAX_VALUE,
Res.string.unmute to 0L,
Res.string.mute_8_hours to TimeUnit.HOURS.toMillis(8),
Res.string.mute_1_week to TimeUnit.DAYS.toMillis(7),
Res.string.mute_always to Long.MAX_VALUE,
)
}
@@ -244,7 +244,7 @@ fun MuteNotificationsDialog(
AlertDialog(
onDismissRequest = onDismiss, // Dismiss the dialog when clicked outside
title = { Text(text = stringResource(R.string.mute_notifications)) },
title = { Text(text = stringResource(Res.string.mute_notifications)) },
text = {
Column {
// Show current mute status
@@ -258,19 +258,20 @@ fun MuteNotificationsDialog(
if (remaining > 0) {
val (days, hours) = formatMuteRemainingTime(remaining)
if (days >= 1) {
stringResource(R.string.mute_status_muted_for_days, days, hours)
stringResource(Res.string.mute_status_muted_for_days, days, hours)
} else {
stringResource(R.string.mute_status_muted_for_hours, hours)
stringResource(Res.string.mute_status_muted_for_hours, hours)
}
} else {
stringResource(R.string.mute_status_unmuted)
stringResource(Res.string.mute_status_unmuted)
}
}
settings.muteUntil == Long.MAX_VALUE -> stringResource(R.string.mute_status_always)
else -> stringResource(R.string.mute_status_unmuted)
settings.muteUntil == Long.MAX_VALUE ->
stringResource(Res.string.mute_status_always)
else -> stringResource(Res.string.mute_status_unmuted)
}
Text(
text = stringResource(R.string.currently) + " " + statusText,
text = stringResource(Res.string.currently) + " " + statusText,
modifier = Modifier.padding(bottom = 8.dp),
)
}
@@ -300,14 +301,14 @@ fun MuteNotificationsDialog(
onDismiss() // Dismiss the dialog after confirming
},
) {
Text(stringResource(R.string.okay))
Text(stringResource(Res.string.okay))
}
},
dismissButton = {
Button(
onClick = onDismiss, // Dismiss the dialog on cancel
) {
Text(stringResource(R.string.cancel))
Text(stringResource(Res.string.cancel))
}
},
)
@@ -324,7 +325,7 @@ fun DeleteConfirmationDialog(
if (showDialog) {
val deleteMessage =
pluralStringResource(
id = R.plurals.delete_messages,
id = Res.plurals.delete_messages,
count = selectedCount,
formatArgs = arrayOf(selectedCount), // Pass the count as a format argument
)
@@ -342,10 +343,10 @@ fun DeleteConfirmationDialog(
onDismiss() // Dismiss the dialog after confirming
},
) {
Text(stringResource(R.string.delete))
Text(stringResource(Res.string.delete))
}
},
dismissButton = { Button(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } },
dismissButton = { Button(onClick = onDismiss) { Text(stringResource(Res.string.cancel)) } },
properties =
DialogProperties(
dismissOnClickOutside = true, // Allow dismissing by clicking outside
@@ -369,7 +370,7 @@ fun SelectionToolbar(
title = { Text(text = "$selectedCount") },
navigationIcon = {
IconButton(onClick = onCloseSelection) {
Icon(Icons.Default.Close, contentDescription = stringResource(R.string.close_selection))
Icon(Icons.Default.Close, contentDescription = stringResource(Res.string.close_selection))
}
},
actions = {
@@ -390,10 +391,10 @@ fun SelectionToolbar(
)
}
IconButton(onClick = onDeleteSelected) {
Icon(Icons.Default.Delete, contentDescription = stringResource(R.string.delete_selection))
Icon(Icons.Default.Delete, contentDescription = stringResource(Res.string.delete_selection))
}
IconButton(onClick = onSelectAll) {
Icon(Icons.Default.SelectAll, contentDescription = stringResource(R.string.select_all))
Icon(Icons.Default.SelectAll, contentDescription = stringResource(Res.string.select_all))
}
},
)

View File

@@ -34,11 +34,11 @@ 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.R
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
@@ -87,7 +87,7 @@ constructor(
val shortName = user.shortName
val longName =
if (toBroadcast) {
channelSet.getChannel(data.channel)?.name ?: context.getString(R.string.channel_name)
channelSet.getChannel(data.channel)?.name ?: context.getString(Res.string.channel_name)
} else {
user.longName
}

View File

@@ -101,7 +101,6 @@ 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.R
import org.meshtastic.core.ui.component.AdaptiveTwoPane
import org.meshtastic.core.ui.component.ChannelSelection
import org.meshtastic.core.ui.component.MainAppBar
@@ -117,6 +116,7 @@ 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
@@ -185,7 +185,7 @@ fun ChannelScreen(
rememberLauncherForActivityResult(ScanContract()) { result ->
if (result.contents != null) {
viewModel.requestChannelUrl(result.contents.toUri()) {
Toast.makeText(context, R.string.channel_invalid, Toast.LENGTH_SHORT).show()
Toast.makeText(context, Res.string.channel_invalid, Toast.LENGTH_SHORT).show()
}
}
}
@@ -223,7 +223,7 @@ fun ChannelScreen(
channelSet = channels // Throw away user edits
// Tell the user to try again
Toast.makeText(context, R.string.cant_change_no_radio, Toast.LENGTH_SHORT).show()
Toast.makeText(context, Res.string.cant_change_no_radio, Toast.LENGTH_SHORT).show()
}
}
@@ -241,8 +241,8 @@ fun ChannelScreen(
channelSet = channels // throw away any edits
showResetDialog = false
},
title = { Text(text = stringResource(R.string.reset_to_defaults)) },
text = { Text(text = stringResource(R.string.are_you_sure_change_default)) },
title = { Text(text = stringResource(Res.string.reset_to_defaults)) },
text = { Text(text = stringResource(Res.string.are_you_sure_change_default)) },
confirmButton = {
TextButton(
onClick = {
@@ -257,7 +257,7 @@ fun ChannelScreen(
showResetDialog = false
},
) {
Text(text = stringResource(R.string.apply))
Text(text = stringResource(Res.string.apply))
}
},
dismissButton = {
@@ -267,7 +267,7 @@ fun ChannelScreen(
showResetDialog = false
},
) {
Text(text = stringResource(R.string.cancel))
Text(text = stringResource(Res.string.cancel))
}
},
)
@@ -312,7 +312,7 @@ fun ChannelScreen(
onTrackShare = viewModel::trackShare,
onConfirm = {
viewModel.requestChannelUrl(it) {
Toast.makeText(context, R.string.channel_invalid, Toast.LENGTH_SHORT).show()
Toast.makeText(context, Res.string.channel_invalid, Toast.LENGTH_SHORT).show()
}
},
)
@@ -320,13 +320,13 @@ fun ChannelScreen(
item {
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
SegmentedButton(
label = { Text(text = stringResource(R.string.replace)) },
label = { Text(text = stringResource(Res.string.replace)) },
onClick = { shouldAddChannelsState = false },
selected = !shouldAddChannelsState,
shape = SegmentedButtonDefaults.itemShape(0, 2),
)
SegmentedButton(
label = { Text(text = stringResource(R.string.add)) },
label = { Text(text = stringResource(Res.string.add)) },
onClick = { shouldAddChannelsState = true },
selected = shouldAddChannelsState,
shape = SegmentedButtonDefaults.itemShape(1, 2),
@@ -345,12 +345,12 @@ fun ChannelScreen(
item {
PreferenceFooter(
enabled = enabled,
negativeText = R.string.reset,
negativeText = Res.string.reset,
onNegativeClicked = {
focusManager.clearFocus()
showResetDialog = true
},
positiveText = R.string.scan,
positiveText = Res.string.scan,
onPositiveClicked = {
focusManager.clearFocus()
if (cameraPermissionState.status.isGranted) {
@@ -400,11 +400,11 @@ private fun EditChannelUrl(
},
modifier = modifier.fillMaxWidth(),
enabled = enabled,
label = { Text(stringResource(R.string.url)) },
label = { Text(stringResource(Res.string.url)) },
isError = isError,
shape = RoundedCornerShape(8.dp),
trailingIcon = {
val label = stringResource(R.string.url)
val label = stringResource(Res.string.url)
val isUrlEqual = valueState == channelUrl
IconButton(
onClick = {
@@ -440,9 +440,9 @@ private fun EditChannelUrl(
},
contentDescription =
when {
isError -> stringResource(R.string.copy)
!isUrlEqual -> stringResource(R.string.send)
else -> stringResource(R.string.copy)
isError -> stringResource(Res.string.copy)
!isUrlEqual -> stringResource(Res.string.send)
else -> stringResource(Res.string.copy)
},
tint =
if (isError) {
@@ -470,7 +470,7 @@ private fun QrCodeImage(
painter =
channelSet.qrCode(shouldAddChannel)?.let { BitmapPainter(it.asImageBitmap()) }
?: painterResource(id = org.meshtastic.core.ui.R.drawable.qrcode),
contentDescription = stringResource(R.string.qr_code),
contentDescription = stringResource(Res.string.qr_code),
modifier = modifier,
contentScale = ContentScale.Inside,
alpha = if (enabled) 1.0f else 0.7f,
@@ -518,7 +518,7 @@ private fun ChannelListView(
enabled = enabled,
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.onSurface),
) {
Text(text = stringResource(R.string.edit))
Text(text = stringResource(Res.string.edit))
}
},
second = {
@@ -543,13 +543,13 @@ private fun ModemPresetInfo(modemPresetName: String, onClick: () -> Unit) {
verticalAlignment = Alignment.CenterVertically,
) {
Column(modifier = Modifier.weight(1f).padding(16.dp)) {
Text(text = stringResource(R.string.modem_preset), fontSize = 16.sp)
Text(text = stringResource(Res.string.modem_preset), fontSize = 16.sp)
Text(text = modemPresetName, fontSize = 14.sp)
}
Spacer(modifier = Modifier.width(16.dp))
Icon(
imageVector = Icons.Default.ChevronRight,
contentDescription = stringResource(R.string.navigate_into_label),
contentDescription = stringResource(Res.string.navigate_into_label),
modifier = Modifier.padding(end = 16.dp),
)
}

View File

@@ -43,9 +43,9 @@ 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.meshtastic.core.strings.R
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) {
@@ -61,7 +61,7 @@ fun ShareScreen(contacts: List<Contact>, onConfirm: (String) -> Unit, onNavigate
Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.share_to),
title = stringResource(Res.string.share_to),
ourNode = null,
showNodeChip = false,
canNavigateUp = true,
@@ -92,7 +92,10 @@ fun ShareScreen(contacts: List<Contact>, onConfirm: (String) -> Unit, onNavigate
modifier = Modifier.fillMaxWidth().padding(24.dp),
enabled = selectedContact.isNotEmpty(),
) {
Icon(imageVector = Icons.AutoMirrored.Default.Send, contentDescription = stringResource(R.string.share))
Icon(
imageVector = Icons.AutoMirrored.Default.Send,
contentDescription = stringResource(Res.string.share),
)
}
}
}
@@ -107,10 +110,10 @@ private fun ShareScreenPreview() {
listOf(
Contact(
contactKey = "0^all",
shortName = stringResource(R.string.some_username),
longName = stringResource(R.string.unknown_username),
shortName = stringResource(Res.string.some_username),
longName = stringResource(Res.string.unknown_username),
lastMessageTime = "3 minutes ago",
lastMessageText = stringResource(R.string.sample_message),
lastMessageText = stringResource(Res.string.sample_message),
unreadCount = 2,
messageCount = 10,
isMuted = true,

View File

@@ -20,30 +20,30 @@ package org.meshtastic.core.database.model
import androidx.annotation.StringRes
import org.meshtastic.core.database.entity.Reaction
import org.meshtastic.core.model.MessageStatus
import org.meshtastic.core.strings.R
import org.meshtastic.proto.MeshProtos.Routing
import org.meshtastic.core.strings.R as Res
@Suppress("CyclomaticComplexMethod")
@StringRes
fun getStringResFrom(routingError: Int): Int = when (routingError) {
Routing.Error.NONE_VALUE -> R.string.routing_error_none
Routing.Error.NO_ROUTE_VALUE -> R.string.routing_error_no_route
Routing.Error.GOT_NAK_VALUE -> R.string.routing_error_got_nak
Routing.Error.TIMEOUT_VALUE -> R.string.routing_error_timeout
Routing.Error.NO_INTERFACE_VALUE -> R.string.routing_error_no_interface
Routing.Error.MAX_RETRANSMIT_VALUE -> R.string.routing_error_max_retransmit
Routing.Error.NO_CHANNEL_VALUE -> R.string.routing_error_no_channel
Routing.Error.TOO_LARGE_VALUE -> R.string.routing_error_too_large
Routing.Error.NO_RESPONSE_VALUE -> R.string.routing_error_no_response
Routing.Error.DUTY_CYCLE_LIMIT_VALUE -> R.string.routing_error_duty_cycle_limit
Routing.Error.BAD_REQUEST_VALUE -> R.string.routing_error_bad_request
Routing.Error.NOT_AUTHORIZED_VALUE -> R.string.routing_error_not_authorized
Routing.Error.PKI_FAILED_VALUE -> R.string.routing_error_pki_failed
Routing.Error.PKI_UNKNOWN_PUBKEY_VALUE -> R.string.routing_error_pki_unknown_pubkey
Routing.Error.ADMIN_BAD_SESSION_KEY_VALUE -> R.string.routing_error_admin_bad_session_key
Routing.Error.ADMIN_PUBLIC_KEY_UNAUTHORIZED_VALUE -> R.string.routing_error_admin_public_key_unauthorized
Routing.Error.RATE_LIMIT_EXCEEDED_VALUE -> R.string.routing_error_rate_limit_exceeded
else -> R.string.unrecognized
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
Routing.Error.TIMEOUT_VALUE -> Res.string.routing_error_timeout
Routing.Error.NO_INTERFACE_VALUE -> Res.string.routing_error_no_interface
Routing.Error.MAX_RETRANSMIT_VALUE -> Res.string.routing_error_max_retransmit
Routing.Error.NO_CHANNEL_VALUE -> Res.string.routing_error_no_channel
Routing.Error.TOO_LARGE_VALUE -> Res.string.routing_error_too_large
Routing.Error.NO_RESPONSE_VALUE -> Res.string.routing_error_no_response
Routing.Error.DUTY_CYCLE_LIMIT_VALUE -> Res.string.routing_error_duty_cycle_limit
Routing.Error.BAD_REQUEST_VALUE -> Res.string.routing_error_bad_request
Routing.Error.NOT_AUTHORIZED_VALUE -> Res.string.routing_error_not_authorized
Routing.Error.PKI_FAILED_VALUE -> Res.string.routing_error_pki_failed
Routing.Error.PKI_UNKNOWN_PUBKEY_VALUE -> Res.string.routing_error_pki_unknown_pubkey
Routing.Error.ADMIN_BAD_SESSION_KEY_VALUE -> Res.string.routing_error_admin_bad_session_key
Routing.Error.ADMIN_PUBLIC_KEY_UNAUTHORIZED_VALUE -> Res.string.routing_error_admin_public_key_unauthorized
Routing.Error.RATE_LIMIT_EXCEEDED_VALUE -> Res.string.routing_error_rate_limit_exceeded
else -> Res.string.unrecognized
}
data class Message(
@@ -67,12 +67,12 @@ data class Message(
val relayNode: Int? = null,
) {
fun getStatusStringRes(): Pair<Int, Int> {
val title = if (routingError > 0) R.string.error else R.string.message_delivery_status
val title = if (routingError > 0) Res.string.error else Res.string.message_delivery_status
val text =
when (status) {
MessageStatus.RECEIVED -> R.string.delivery_confirmed
MessageStatus.QUEUED -> R.string.message_status_queued
MessageStatus.ENROUTE -> R.string.message_status_enroute
MessageStatus.RECEIVED -> Res.string.delivery_confirmed
MessageStatus.QUEUED -> Res.string.message_status_queued
MessageStatus.ENROUTE -> Res.string.message_status_enroute
else -> getStringResFrom(routingError)
}
return title to text

View File

@@ -18,14 +18,14 @@
package org.meshtastic.core.database.model
import androidx.annotation.StringRes
import org.meshtastic.core.strings.R
import org.meshtastic.core.strings.R as Res
enum class NodeSortOption(val sqlValue: String, @StringRes val stringRes: Int) {
LAST_HEARD("last_heard", R.string.node_sort_last_heard),
ALPHABETICAL("alpha", R.string.node_sort_alpha),
DISTANCE("distance", R.string.node_sort_distance),
HOPS_AWAY("hops_away", R.string.node_sort_hops_away),
CHANNEL("channel", R.string.node_sort_channel),
VIA_MQTT("via_mqtt", R.string.node_sort_via_mqtt),
VIA_FAVORITE("via_favorite", R.string.node_sort_via_favorite),
LAST_HEARD("last_heard", Res.string.node_sort_last_heard),
ALPHABETICAL("alpha", Res.string.node_sort_alpha),
DISTANCE("distance", Res.string.node_sort_distance),
HOPS_AWAY("hops_away", Res.string.node_sort_hops_away),
CHANNEL("channel", Res.string.node_sort_channel),
VIA_MQTT("via_mqtt", Res.string.node_sort_via_mqtt),
VIA_FAVORITE("via_favorite", Res.string.node_sort_via_favorite),
}

View File

@@ -18,11 +18,11 @@
package org.meshtastic.core.model
import androidx.annotation.StringRes
import org.meshtastic.core.strings.R
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
@@ -297,13 +297,13 @@ enum class RegionInfo(
}
enum class ChannelOption(val modemPreset: ModemPreset, @StringRes val labelRes: Int, val bandwidth: Float) {
VERY_LONG_SLOW(ModemPreset.VERY_LONG_SLOW, R.string.label_very_long_slow, .0625f),
LONG_FAST(ModemPreset.LONG_FAST, R.string.label_long_fast, .250f),
LONG_MODERATE(ModemPreset.LONG_MODERATE, R.string.label_long_moderate, .125f),
LONG_SLOW(ModemPreset.LONG_SLOW, R.string.label_long_slow, .125f),
MEDIUM_FAST(ModemPreset.MEDIUM_FAST, R.string.label_medium_fast, .250f),
MEDIUM_SLOW(ModemPreset.MEDIUM_SLOW, R.string.label_medium_slow, .250f),
SHORT_TURBO(ModemPreset.SHORT_TURBO, R.string.label_short_turbo, bandwidth = .500f),
SHORT_FAST(ModemPreset.SHORT_FAST, R.string.label_short_fast, .250f),
SHORT_SLOW(ModemPreset.SHORT_SLOW, R.string.label_short_slow, .250f),
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),
LONG_SLOW(ModemPreset.LONG_SLOW, Res.string.label_long_slow, .125f),
MEDIUM_FAST(ModemPreset.MEDIUM_FAST, Res.string.label_medium_fast, .250f),
MEDIUM_SLOW(ModemPreset.MEDIUM_SLOW, Res.string.label_medium_slow, .250f),
SHORT_TURBO(ModemPreset.SHORT_TURBO, Res.string.label_short_turbo, bandwidth = .500f),
SHORT_FAST(ModemPreset.SHORT_FAST, Res.string.label_short_fast, .250f),
SHORT_SLOW(ModemPreset.SHORT_SLOW, Res.string.label_short_slow, .250f),
}

View File

@@ -28,6 +28,7 @@ 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
@@ -38,7 +39,7 @@ fun MeshProtos.Position.formatPositionTime(dateFormat: DateFormat): String {
val isOlderThanSixMonths = time * SECONDS_TO_MILLIS < sixMonthsAgo
val timeText =
if (isOlderThanSixMonths) {
stringResource(org.meshtastic.core.strings.R.string.unknown_age)
stringResource(Res.string.unknown_age)
} else {
dateFormat.format(time * SECONDS_TO_MILLIS)
}

View File

@@ -28,12 +28,12 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.meshtastic.core.model.Channel
import org.meshtastic.core.strings.R
import org.meshtastic.proto.AppOnlyProtos.ChannelSet
import org.meshtastic.proto.ConfigProtos
import org.meshtastic.proto.channelSet
import org.meshtastic.proto.channelSettings
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@RunWith(AndroidJUnit4::class)
class ScannedQrCodeDialogTest {
@@ -82,7 +82,7 @@ class ScannedQrCodeDialogTest {
testScannedQrCodeDialog()
// Verify that the dialog title is displayed
onNodeWithText(getString(R.string.new_channel_rcvd)).assertIsDisplayed()
onNodeWithText(getString(Res.string.new_channel_rcvd)).assertIsDisplayed()
}
}
@@ -92,8 +92,8 @@ class ScannedQrCodeDialogTest {
testScannedQrCodeDialog()
// Verify that the "Add" and "Replace" buttons are displayed
onNodeWithText(getString(R.string.add)).assertIsDisplayed()
onNodeWithText(getString(R.string.replace)).assertIsDisplayed()
onNodeWithText(getString(Res.string.add)).assertIsDisplayed()
onNodeWithText(getString(Res.string.replace)).assertIsDisplayed()
}
}
@@ -103,8 +103,8 @@ class ScannedQrCodeDialogTest {
testScannedQrCodeDialog()
// Verify the "Cancel" and "Accept" buttons are displayed
onNodeWithText(getString(R.string.cancel)).assertIsDisplayed()
onNodeWithText(getString(R.string.accept)).assertIsDisplayed()
onNodeWithText(getString(Res.string.cancel)).assertIsDisplayed()
onNodeWithText(getString(Res.string.accept)).assertIsDisplayed()
}
}
@@ -115,7 +115,7 @@ class ScannedQrCodeDialogTest {
testScannedQrCodeDialog(onDismiss = { onDismissClicked = true })
// Click the "Cancel" button
onNodeWithText(getString(R.string.cancel)).performClick()
onNodeWithText(getString(Res.string.cancel)).performClick()
}
// Verify onDismiss is called
@@ -129,7 +129,7 @@ class ScannedQrCodeDialogTest {
testScannedQrCodeDialog(onConfirm = { actualChannelSet = it })
// Click the "Accept" button
onNodeWithText(getString(R.string.accept)).performClick()
onNodeWithText(getString(Res.string.accept)).performClick()
}
// Verify onConfirm is called with the correct ChannelSet
@@ -143,8 +143,8 @@ class ScannedQrCodeDialogTest {
testScannedQrCodeDialog(onConfirm = { actualChannelSet = it })
// Click the "Add" button then the "Accept" button
onNodeWithText(getString(R.string.add)).performClick()
onNodeWithText(getString(R.string.accept)).performClick()
onNodeWithText(getString(Res.string.add)).performClick()
onNodeWithText(getString(Res.string.accept)).performClick()
}
// Verify onConfirm is called with the correct ChannelSet

View File

@@ -37,7 +37,7 @@ 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
import org.meshtastic.core.strings.R as Res
@Composable
fun SimpleAlertDialog(
@@ -72,7 +72,7 @@ fun SimpleAlertDialog(
Text(text = message.orEmpty())
}
},
confirmButton = { TextButton(onClick = onConfirmRequest) { Text(stringResource(R.string.okay)) } },
confirmButton = { TextButton(onClick = onConfirmRequest) { Text(stringResource(Res.string.okay)) } },
)
}

View File

@@ -38,7 +38,7 @@ 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
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -90,9 +90,9 @@ fun BitwisePreference(
}
PreferenceFooter(
enabled = enabled,
negativeText = R.string.clear,
negativeText = Res.string.clear,
onNegativeClicked = { onItemSelected(0) },
positiveText = R.string.close,
positiveText = Res.string.close,
onPositiveClicked = { expanded = false },
)
}

View File

@@ -63,6 +63,7 @@ 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
@@ -127,17 +128,14 @@ fun AddContactFAB(
}
},
) {
Icon(
imageVector = Icons.TwoTone.QrCodeScanner,
contentDescription = stringResource(org.meshtastic.core.strings.R.string.scan_qr_code),
)
Icon(imageVector = Icons.TwoTone.QrCodeScanner, contentDescription = stringResource(Res.string.scan_qr_code))
}
}
@Composable
private fun QrCodeImage(uri: Uri, modifier: Modifier = Modifier) = Image(
painter = uri.qrCode?.let { BitmapPainter(it.asImageBitmap()) } ?: painterResource(id = R.drawable.qrcode),
contentDescription = stringResource(org.meshtastic.core.strings.R.string.qr_code),
contentDescription = stringResource(Res.string.qr_code),
modifier = modifier,
contentScale = ContentScale.Inside,
)
@@ -165,7 +163,7 @@ fun SharedContactDialog(contact: Node?, onDismiss: () -> Unit) {
val sharedContact = AdminProtos.SharedContact.newBuilder().setUser(contact.user).setNodeNum(contact.num).build()
val uri = sharedContact.getSharedContactUrl()
SimpleAlertDialog(
title = org.meshtastic.core.strings.R.string.share_contact,
title = Res.string.share_contact,
text = {
Column {
Text(contact.user.longName)

View File

@@ -29,10 +29,14 @@ 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
import org.meshtastic.core.strings.R as Res
@Composable
fun CopyIconButton(valueToCopy: String, modifier: Modifier = Modifier, label: String = stringResource(R.string.copy)) {
fun CopyIconButton(
valueToCopy: String,
modifier: Modifier = Modifier,
label: String = stringResource(Res.string.copy),
) {
val clipboardManager = LocalClipboard.current
val coroutineScope = rememberCoroutineScope()
IconButton(

View File

@@ -48,7 +48,7 @@ import com.google.protobuf.ByteString
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
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@Composable
@@ -77,8 +77,8 @@ fun EditBase64Preference(
val (icon, description) =
when {
isError -> Icons.TwoTone.Close to stringResource(R.string.error)
onGenerateKey != null && !isFocused -> Icons.TwoTone.Refresh to stringResource(R.string.reset)
isError -> Icons.TwoTone.Close to stringResource(Res.string.error)
onGenerateKey != null && !isFocused -> Icons.TwoTone.Refresh to stringResource(Res.string.reset)
else -> null to null
}
Column(modifier = modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {

View File

@@ -41,11 +41,11 @@ 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.meshtastic.core.strings.R
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
@@ -84,7 +84,7 @@ inline fun <reified T> EditListPreference(
) {
Icon(
imageVector = Icons.TwoTone.Close,
contentDescription = stringResource(R.string.delete),
contentDescription = stringResource(Res.string.delete),
modifier = Modifier.wrapContentSize(),
)
}
@@ -119,7 +119,7 @@ inline fun <reified T> EditListPreference(
}
is RemoteHardwarePin -> {
EditTextPreference(
title = stringResource(R.string.gpio_pin),
title = stringResource(Res.string.gpio_pin),
value = value.gpioPin,
enabled = enabled,
keyboardActions = keyboardActions,
@@ -131,7 +131,7 @@ inline fun <reified T> EditListPreference(
},
)
EditTextPreference(
title = stringResource(R.string.name),
title = stringResource(Res.string.name),
value = value.name,
maxSize = 14, // name max_size:15
enabled = enabled,
@@ -146,7 +146,7 @@ inline fun <reified T> EditListPreference(
trailingIcon = trailingIcon,
)
DropDownPreference(
title = stringResource(R.string.type),
title = stringResource(Res.string.type),
enabled = enabled,
items =
RemoteHardwarePinType.entries
@@ -176,7 +176,7 @@ inline fun <reified T> EditListPreference(
},
enabled = maxCount > listState.size,
) {
Text(text = stringResource(R.string.add))
Text(text = stringResource(Res.string.add))
}
}
}
@@ -186,7 +186,7 @@ inline fun <reified T> EditListPreference(
private fun EditListPreferencePreview() {
Column {
EditListPreference(
title = stringResource(R.string.ignore_incoming),
title = stringResource(Res.string.ignore_incoming),
summary = "This is a summary",
list = listOf(12345, 67890),
maxCount = 4,

View File

@@ -35,7 +35,7 @@ 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
import org.meshtastic.core.strings.R as Res
@Composable
fun EditPasswordPreference(
@@ -67,9 +67,9 @@ fun EditPasswordPreference(
imageVector = if (isPasswordVisible) Icons.TwoTone.VisibilityOff else Icons.TwoTone.VisibilityOff,
contentDescription =
if (isPasswordVisible) {
stringResource(R.string.hide_password)
stringResource(Res.string.hide_password)
} else {
stringResource(R.string.show_password)
stringResource(Res.string.show_password)
},
)
}

View File

@@ -44,7 +44,7 @@ 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
import org.meshtastic.core.strings.R as Res
@Composable
fun SignedIntegerEditTextPreference(
@@ -234,7 +234,7 @@ fun EditTextPreference(
{
Icon(
imageVector = Icons.TwoTone.Info,
contentDescription = stringResource(R.string.error),
contentDescription = stringResource(Res.string.error),
tint = MaterialTheme.colorScheme.error,
)
}

View File

@@ -57,7 +57,6 @@ 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.meshtastic.core.strings.R
import org.meshtastic.core.ui.theme.IAQColors.IAQDangerouslyPolluted
import org.meshtastic.core.ui.theme.IAQColors.IAQExcellent
import org.meshtastic.core.ui.theme.IAQColors.IAQExtremelyPolluted
@@ -66,6 +65,7 @@ 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) {
@@ -135,7 +135,7 @@ fun IndoorAirQuality(iaq: Int?, displayMode: IaqDisplayMode = IaqDisplayMode.Pil
Icon(
imageVector =
if (iaqEnum.range.first < 100) Icons.Default.ThumbUp else Icons.Filled.Warning,
contentDescription = stringResource(R.string.air_quality_icon),
contentDescription = stringResource(Res.string.air_quality_icon),
tint = Color.White,
)
}
@@ -191,7 +191,7 @@ fun IndoorAirQuality(iaq: Int?, displayMode: IaqDisplayMode = IaqDisplayMode.Pil
shape = RoundedCornerShape(16.dp),
text = { IAQScale() },
confirmButton = {
TextButton(onClick = { isLegendOpen = false }) { Text(text = stringResource(R.string.close)) }
TextButton(onClick = { isLegendOpen = false }) { Text(text = stringResource(Res.string.close)) }
},
)
}
@@ -207,7 +207,7 @@ fun IndoorAirQuality(iaq: Int?, displayMode: IaqDisplayMode = IaqDisplayMode.Pil
fun IAQScale(modifier: Modifier = Modifier) {
Column(modifier = modifier.padding(16.dp), horizontalAlignment = Alignment.Start) {
Text(
text = stringResource(R.string.indoor_air_quality_iaq),
text = stringResource(Res.string.indoor_air_quality_iaq),
style =
MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold, textAlign = TextAlign.Center),
modifier = Modifier.fillMaxWidth(),

View File

@@ -45,11 +45,11 @@ 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.meshtastic.core.strings.R
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
@@ -63,10 +63,10 @@ enum class Quality(
@Stable val imageVector: ImageVector,
@Stable val color: @Composable () -> Color,
) {
NONE(R.string.none_quality, Icons.Default.SignalCellularAlt1Bar, { colorScheme.StatusRed }),
BAD(R.string.bad, Icons.Default.SignalCellularAlt2Bar, { colorScheme.StatusOrange }),
FAIR(R.string.fair, Icons.Default.SignalCellularAlt, { colorScheme.StatusYellow }),
GOOD(R.string.good, Icons.Default.SignalCellular4Bar, { colorScheme.StatusGreen }),
NONE(Res.string.none_quality, Icons.Default.SignalCellularAlt1Bar, { colorScheme.StatusRed }),
BAD(Res.string.bad, Icons.Default.SignalCellularAlt2Bar, { colorScheme.StatusOrange }),
FAIR(Res.string.fair, Icons.Default.SignalCellularAlt, { colorScheme.StatusYellow }),
GOOD(Res.string.good, Icons.Default.SignalCellular4Bar, { colorScheme.StatusGreen }),
}
/**
@@ -85,14 +85,14 @@ fun NodeSignalQuality(snr: Float, rssi: Int, modifier: Modifier = Modifier) {
Snr(snr)
Rssi(rssi)
Text(
text = "${stringResource(R.string.signal)} ${stringResource(quality.nameRes)}",
text = "${stringResource(Res.string.signal)} ${stringResource(quality.nameRes)}",
style = MaterialTheme.typography.labelSmall,
maxLines = 1,
)
Icon(
modifier = Modifier.size(20.dp),
imageVector = quality.imageVector,
contentDescription = stringResource(R.string.signal_quality),
contentDescription = stringResource(Res.string.signal_quality),
tint = quality.color.invoke(),
)
}
@@ -119,11 +119,11 @@ fun LoraSignalIndicator(snr: Float, rssi: Int, contentColor: Color = MaterialThe
Icon(
modifier = Modifier.size(20.dp),
imageVector = quality.imageVector,
contentDescription = stringResource(R.string.signal_quality),
contentDescription = stringResource(Res.string.signal_quality),
tint = quality.color.invoke(),
)
Text(
text = "${stringResource(R.string.signal)} ${stringResource(quality.nameRes)}",
text = "${stringResource(Res.string.signal)} ${stringResource(quality.nameRes)}",
style = MaterialTheme.typography.labelSmall,
color = contentColor,
)
@@ -142,7 +142,7 @@ fun Snr(snr: Float) {
}
Text(
text = "%s %.2fdB".format(stringResource(R.string.snr), snr),
text = "%s %.2fdB".format(stringResource(Res.string.snr), snr),
color = color,
style = MaterialTheme.typography.labelSmall,
)
@@ -159,7 +159,7 @@ fun Rssi(rssi: Int) {
Quality.BAD.color.invoke()
}
Text(
text = "%s %ddBm".format(stringResource(R.string.rssi), rssi),
text = "%s %ddBm".format(stringResource(Res.string.rssi), rssi),
color = color,
style = MaterialTheme.typography.labelSmall,
)

View File

@@ -40,10 +40,10 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
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
@@ -75,7 +75,7 @@ fun MainAppBar(
IconButton(onClick = onNavigateUp) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.navigate_back),
contentDescription = stringResource(Res.string.navigate_back),
)
}
}
@@ -85,7 +85,7 @@ fun MainAppBar(
Icon(
imageVector =
ImageVector.vectorResource(id = org.meshtastic.core.ui.R.drawable.ic_meshtastic),
contentDescription = stringResource(R.string.application_icon),
contentDescription = stringResource(Res.string.application_icon),
)
}
}

View File

@@ -38,7 +38,6 @@ 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.meshtastic.core.strings.R
import org.meshtastic.core.ui.icon.BatteryEmpty
import org.meshtastic.core.ui.icon.BatteryUnknown
import org.meshtastic.core.ui.icon.MeshtasticIcons
@@ -46,6 +45,7 @@ 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
@@ -70,7 +70,7 @@ fun MaterialBatteryInfo(
modifier = Modifier.size(SIZE_ICON.dp),
imageVector = MeshtasticIcons.BatteryUnknown,
tint = contentColor,
contentDescription = stringResource(R.string.unknown),
contentDescription = stringResource(Res.string.unknown),
)
} else if (level > 100) {
Icon(

View File

@@ -40,7 +40,6 @@ 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.meshtastic.core.strings.R
import org.meshtastic.core.ui.icon.MeshtasticIcons
import org.meshtastic.core.ui.icon.SignalCellular0Bar
import org.meshtastic.core.ui.icon.SignalCellular1Bar
@@ -52,6 +51,7 @@ 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
@@ -116,7 +116,7 @@ fun MaterialBluetoothSignalInfo(rssi: Int, modifier: Modifier = Modifier) {
MaterialSignalInfo(
modifier = modifier,
signalBars = getBluetoothSignalBars(rssi = rssi),
signalStrengthValue = stringResource(R.string.dbm_value, rssi),
signalStrengthValue = stringResource(Res.string.dbm_value, rssi),
typeIcon = Icons.Rounded.Bluetooth,
)
}

View File

@@ -61,11 +61,11 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import com.google.protobuf.ByteString
import org.meshtastic.core.model.Channel
import org.meshtastic.core.strings.R
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.
@@ -85,9 +85,9 @@ fun NodeKeyStatusIcon(
if (showEncryptionDialog) {
val (title, text) =
when {
mismatchKey -> R.string.encryption_error to R.string.encryption_error_text
hasPKC -> R.string.encryption_pkc to R.string.encryption_pkc_text
else -> R.string.encryption_psk to R.string.encryption_psk_text
mismatchKey -> Res.string.encryption_error to Res.string.encryption_error_text
hasPKC -> Res.string.encryption_pkc to Res.string.encryption_pkc_text
else -> Res.string.encryption_psk to Res.string.encryption_psk_text
}
KeyStatusDialog(title, text, publicKey) { showEncryptionDialog = false }
}
@@ -108,9 +108,9 @@ fun NodeKeyStatusIcon(
stringResource(
id =
when {
mismatchKey -> R.string.encryption_error
hasPKC -> R.string.encryption_pkc
else -> R.string.encryption_psk
mismatchKey -> Res.string.encryption_error
hasPKC -> Res.string.encryption_pkc
else -> Res.string.encryption_psk
},
),
tint = tint,
@@ -140,27 +140,27 @@ enum class NodeKeySecurityState(
PKM(
icon = Icons.Default.KeyOff,
color = { colorScheme.StatusRed },
descriptionResId = R.string.encryption_error,
helpTextResId = R.string.encryption_error_text,
title = R.string.encryption_error,
descriptionResId = Res.string.encryption_error,
helpTextResId = Res.string.encryption_error_text,
title = Res.string.encryption_error,
),
// State for public key encryption
PKC(
icon = Icons.Default.Lock,
color = { colorScheme.StatusGreen },
title = R.string.encryption_pkc,
helpTextResId = R.string.encryption_pkc_text,
descriptionResId = R.string.encryption_pkc,
title = Res.string.encryption_pkc,
helpTextResId = Res.string.encryption_pkc_text,
descriptionResId = Res.string.encryption_pkc,
),
// State for shared key encryption
PSK(
icon = Icons.Default.LockOpen,
color = { colorScheme.StatusYellow },
title = R.string.encryption_psk,
helpTextResId = R.string.encryption_psk_text,
descriptionResId = R.string.encryption_psk,
title = Res.string.encryption_psk,
helpTextResId = Res.string.encryption_psk_text,
descriptionResId = Res.string.encryption_psk,
),
}
@@ -172,7 +172,7 @@ private fun KeyStatusDialog(@StringRes title: Int, @StringRes text: Int, key: By
onDismissRequest = onDismiss,
title = {
if (showAll) {
Text(stringResource(R.string.show_all_key_title))
Text(stringResource(Res.string.show_all_key_title))
} else {
Text(stringResource(title))
}
@@ -184,10 +184,10 @@ private fun KeyStatusDialog(@StringRes title: Int, @StringRes text: Int, key: By
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = stringResource(text), textAlign = TextAlign.Center)
Spacer(Modifier.height(16.dp))
if (key != null && title == R.string.encryption_pkc) {
if (key != null && title == Res.string.encryption_pkc) {
val keyString = Base64.encodeToString(key.toByteArray(), Base64.NO_WRAP)
Text(
text = stringResource(R.string.config_security_public_key) + ":",
text = stringResource(Res.string.config_security_public_key) + ":",
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(8.dp))
@@ -208,13 +208,13 @@ private fun KeyStatusDialog(@StringRes title: Int, @StringRes text: Int, key: By
TextButton(onClick = { showAll = !showAll }) {
Text(
if (showAll) {
stringResource(R.string.security_icon_help_show_less)
stringResource(Res.string.security_icon_help_show_less)
} else {
stringResource(R.string.security_icon_help_show_all)
stringResource(Res.string.security_icon_help_show_all)
},
)
}
TextButton(onClick = onDismiss) { Text(stringResource(R.string.security_icon_help_dismiss)) }
TextButton(onClick = onDismiss) { Text(stringResource(Res.string.security_icon_help_dismiss)) }
}
},
)
@@ -256,7 +256,9 @@ private fun AllKeyStates() {
@PreviewLightDark
@Composable
private fun KeyStatusDialogErrorPreview() {
AppTheme { KeyStatusDialog(title = R.string.encryption_error, text = R.string.encryption_error_text, key = null) }
AppTheme {
KeyStatusDialog(title = Res.string.encryption_error, text = Res.string.encryption_error_text, key = null)
}
}
@PreviewLightDark
@@ -264,8 +266,8 @@ private fun KeyStatusDialogErrorPreview() {
private fun KeyStatusDialogPkcPreview() {
AppTheme {
KeyStatusDialog(
title = R.string.encryption_pkc,
text = R.string.encryption_pkc_text,
title = Res.string.encryption_pkc,
text = Res.string.encryption_pkc_text,
key = Channel.getRandomKey(),
)
}
@@ -274,7 +276,7 @@ private fun KeyStatusDialogPkcPreview() {
@PreviewLightDark
@Composable
private fun KeyStatusDialogPskPreview() {
AppTheme { KeyStatusDialog(title = R.string.encryption_psk, text = R.string.encryption_psk_text, key = null) }
AppTheme { KeyStatusDialog(title = Res.string.encryption_psk, text = Res.string.encryption_psk_text, key = null) }
}
@Preview

View File

@@ -33,9 +33,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.meshtastic.core.model.util.DistanceUnit
import org.meshtastic.core.model.util.toDistanceString
import org.meshtastic.core.strings.R
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
@@ -58,7 +58,7 @@ fun PositionPrecisionPreference(
Column(modifier = modifier) {
SwitchPreference(
title = stringResource(R.string.position_enabled),
title = stringResource(Res.string.position_enabled),
checked = value != POSITION_DISABLED,
enabled = enabled,
onCheckedChange = { enabled ->
@@ -69,7 +69,7 @@ fun PositionPrecisionPreference(
)
if (value != POSITION_DISABLED) {
SwitchPreference(
title = stringResource(R.string.precise_location),
title = stringResource(Res.string.precise_location),
checked = value == POSITION_ENABLED,
enabled = enabled,
onCheckedChange = { enabled ->

View File

@@ -62,13 +62,13 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.meshtastic.core.model.Channel
import org.meshtastic.core.model.util.getChannel
import org.meshtastic.core.strings.R
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
@@ -96,8 +96,8 @@ enum class SecurityState(
SECURE(
icon = Icons.Filled.Lock,
color = { colorScheme.StatusGreen },
descriptionResId = R.string.security_icon_secure,
helpTextResId = R.string.security_icon_help_green_lock,
descriptionResId = Res.string.security_icon_secure,
helpTextResId = Res.string.security_icon_help_green_lock,
),
/**
@@ -107,8 +107,8 @@ enum class SecurityState(
INSECURE_NO_PRECISE(
icon = Icons.Filled.LockOpen,
color = { colorScheme.StatusYellow },
descriptionResId = R.string.security_icon_insecure_no_precise,
helpTextResId = R.string.security_icon_help_yellow_open_lock,
descriptionResId = Res.string.security_icon_insecure_no_precise,
helpTextResId = Res.string.security_icon_help_yellow_open_lock,
),
/**
@@ -118,8 +118,8 @@ enum class SecurityState(
INSECURE_PRECISE_ONLY(
icon = Icons.Filled.LockOpen,
color = { colorScheme.StatusRed },
descriptionResId = R.string.security_icon_insecure_precise_only,
helpTextResId = R.string.security_icon_help_red_open_lock,
descriptionResId = Res.string.security_icon_insecure_precise_only,
helpTextResId = Res.string.security_icon_help_red_open_lock,
),
/**
@@ -129,8 +129,8 @@ enum class SecurityState(
INSECURE_PRECISE_MQTT_WARNING(
icon = Icons.Filled.LockOpen,
color = { colorScheme.StatusRed },
descriptionResId = R.string.security_icon_warning_precise_mqtt,
helpTextResId = R.string.security_icon_help_warning_precise_mqtt,
descriptionResId = Res.string.security_icon_warning_precise_mqtt,
helpTextResId = Res.string.security_icon_help_warning_precise_mqtt,
badgeIcon = Icons.Filled.Warning,
badgeIconColor = { colorScheme.StatusYellow },
),
@@ -163,7 +163,7 @@ private fun SecurityIconDisplay(
) {
Icon(
imageVector = badgeIcon,
contentDescription = stringResource(R.string.security_icon_badge_warning_description),
contentDescription = stringResource(Res.string.security_icon_badge_warning_description),
tint = badgeIconColor ?: colorScheme.onError, // Default for contrast
modifier = Modifier.size(16.dp), // Adjusted badge icon size
)
@@ -211,7 +211,7 @@ private fun determineSecurityState(
@Composable
fun SecurityIcon(
securityState: SecurityState,
baseContentDescription: String = stringResource(R.string.security_icon_description),
baseContentDescription: String = stringResource(Res.string.security_icon_description),
externalOnClick: (() -> Unit)? = null,
) {
var showHelpDialog by rememberSaveable { mutableStateOf(false) }
@@ -252,7 +252,7 @@ fun SecurityIcon(
isLowEntropyKey: Boolean,
isPreciseLocation: Boolean = false,
isMqttEnabled: Boolean = false,
baseContentDescription: String = stringResource(R.string.security_icon_description),
baseContentDescription: String = stringResource(Res.string.security_icon_description),
externalOnClick: (() -> Unit)? = null,
) {
val securityState = determineSecurityState(isLowEntropyKey, isPreciseLocation, isMqttEnabled)
@@ -285,7 +285,7 @@ val Channel.isMqttEnabled: Boolean
@Composable
fun SecurityIcon(
channel: Channel,
baseContentDescription: String = stringResource(R.string.security_icon_description),
baseContentDescription: String = stringResource(Res.string.security_icon_description),
externalOnClick: (() -> Unit)? = null,
) = SecurityIcon(
isLowEntropyKey = channel.isLowEntropyKey,
@@ -305,7 +305,7 @@ fun SecurityIcon(
fun SecurityIcon(
channelSettings: ChannelSettings,
loraConfig: LoRaConfig,
baseContentDescription: String = stringResource(R.string.security_icon_description),
baseContentDescription: String = stringResource(Res.string.security_icon_description),
externalOnClick: (() -> Unit)? = null,
) {
val channel = Channel(channelSettings, loraConfig)
@@ -331,7 +331,7 @@ fun SecurityIcon(
fun SecurityIcon(
channelSet: AppOnlyProtos.ChannelSet,
channelIndex: Int,
baseContentDescription: String = stringResource(R.string.security_icon_description),
baseContentDescription: String = stringResource(Res.string.security_icon_description),
externalOnClick: (() -> Unit)? = null,
) {
channelSet.getChannel(channelIndex)?.let { channel ->
@@ -357,7 +357,7 @@ fun SecurityIcon(
fun SecurityIcon(
channelSet: AppOnlyProtos.ChannelSet,
channelName: String,
baseContentDescription: String = stringResource(R.string.security_icon_description),
baseContentDescription: String = stringResource(Res.string.security_icon_description),
externalOnClick: (() -> Unit)? = null,
) {
val channelByNameMap =
@@ -394,9 +394,9 @@ private fun SecurityHelpDialog(securityState: SecurityState, onDismiss: () -> Un
title = {
Text(
if (showAll) {
stringResource(R.string.security_icon_help_title_all)
stringResource(Res.string.security_icon_help_title_all)
} else {
stringResource(R.string.security_icon_help_title)
stringResource(Res.string.security_icon_help_title)
},
)
},
@@ -416,13 +416,13 @@ private fun SecurityHelpDialog(securityState: SecurityState, onDismiss: () -> Un
TextButton(onClick = { showAll = !showAll }) {
Text(
if (showAll) {
stringResource(R.string.security_icon_help_show_less)
stringResource(Res.string.security_icon_help_show_less)
} else {
stringResource(R.string.security_icon_help_show_all)
stringResource(Res.string.security_icon_help_show_all)
},
)
}
TextButton(onClick = onDismiss) { Text(stringResource(R.string.security_icon_help_dismiss)) }
TextButton(onClick = onDismiss) { Text(stringResource(Res.string.security_icon_help_dismiss)) }
}
},
)

View File

@@ -34,9 +34,9 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
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
@@ -51,14 +51,14 @@ fun SignalInfo(
) {
val text =
if (isThisNode) {
stringResource(R.string.channel_air_util)
stringResource(Res.string.channel_air_util)
.format(node.deviceMetrics.channelUtilization, node.deviceMetrics.airUtilTx)
} else {
buildList {
val hopsString =
"%s: %s"
.format(
stringResource(R.string.hops_away),
stringResource(Res.string.hops_away),
if (node.hopsAway == -1) {
"?"
} else {
@@ -98,11 +98,11 @@ fun SignalInfo(
Icon(
modifier = Modifier.size(20.dp),
imageVector = quality.imageVector,
contentDescription = stringResource(R.string.signal_quality),
contentDescription = stringResource(Res.string.signal_quality),
tint = quality.color.invoke(),
)
Text(
text = "${stringResource(R.string.signal)} ${stringResource(quality.nameRes)}",
text = "${stringResource(Res.string.signal)} ${stringResource(quality.nameRes)}",
style = MaterialTheme.typography.labelSmall,
color = contentColor,
maxLines = 1,

View File

@@ -32,8 +32,8 @@ 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.meshtastic.core.strings.R
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun SimpleAlertDialog(
@@ -51,7 +51,7 @@ fun SimpleAlertDialog(
modifier = Modifier.padding(horizontal = 16.dp),
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.onSurface),
) {
Text(text = dismissText ?: stringResource(R.string.cancel))
Text(text = dismissText ?: stringResource(Res.string.cancel))
}
},
confirmButton = {
@@ -61,7 +61,7 @@ fun SimpleAlertDialog(
modifier = Modifier.padding(horizontal = 16.dp),
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.onSurface),
) {
Text(text = confirmText ?: stringResource(R.string.okay))
Text(text = confirmText ?: stringResource(Res.string.okay))
}
}
},
@@ -101,5 +101,5 @@ fun SimpleAlertDialog(
@PreviewLightDark
@Composable
private fun SimpleAlertDialogPreview() {
AppTheme { SimpleAlertDialog(title = R.string.message, text = R.string.sample_message) }
AppTheme { SimpleAlertDialog(title = Res.string.message, text = Res.string.sample_message) }
}

View File

@@ -52,12 +52,12 @@ import androidx.compose.ui.window.DialogProperties
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.model.Channel
import org.meshtastic.core.strings.R
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(
@@ -189,7 +189,7 @@ fun ScannedQrCodeDialog(
) {
item {
Text(
text = stringResource(R.string.new_channel_rcvd),
text = stringResource(Res.string.new_channel_rcvd),
modifier = Modifier.padding(20.dp),
style = MaterialTheme.typography.titleLarge,
)
@@ -239,7 +239,7 @@ fun ScannedQrCodeDialog(
modifier = Modifier.height(48.dp).weight(1f),
colors = if (!shouldReplace) selectedColors else unselectedColors,
) {
Text(text = stringResource(R.string.add))
Text(text = stringResource(Res.string.add))
}
OutlinedButton(
@@ -248,7 +248,7 @@ fun ScannedQrCodeDialog(
enabled = incoming.hasLoraConfig(),
colors = if (shouldReplace) selectedColors else unselectedColors,
) {
Text(text = stringResource(R.string.replace))
Text(text = stringResource(Res.string.replace))
}
}
}
@@ -261,7 +261,7 @@ fun ScannedQrCodeDialog(
) {
TextButton(onClick = { onDismiss() }) {
Text(
text = stringResource(R.string.cancel),
text = stringResource(Res.string.cancel),
color = MaterialTheme.colorScheme.onSurface,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
@@ -277,7 +277,7 @@ fun ScannedQrCodeDialog(
enabled = selectedChannelSet.settingsCount in 1..8,
) {
Text(
text = stringResource(R.string.accept),
text = stringResource(Res.string.accept),
color = MaterialTheme.colorScheme.onSurface,
overflow = TextOverflow.Ellipsis,
maxLines = 1,

View File

@@ -26,11 +26,11 @@ 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.meshtastic.core.strings.R
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
@@ -45,14 +45,14 @@ fun SharedContactDialog(
val node = unfilteredNodes.find { it.num == nodeNum }
SimpleAlertDialog(
title = R.string.import_shared_contact,
title = Res.string.import_shared_contact,
text = {
Column {
if (node != null) {
Text(text = stringResource(R.string.import_known_shared_contact_text))
Text(text = stringResource(Res.string.import_known_shared_contact_text))
if (node.user.publicKey.size() > 0 && node.user.publicKey != sharedContact.user?.publicKey) {
Text(
text = stringResource(R.string.public_key_changed),
text = stringResource(Res.string.public_key_changed),
color = MaterialTheme.colorScheme.error,
)
}
@@ -63,9 +63,9 @@ fun SharedContactDialog(
}
}
},
dismissText = stringResource(R.string.cancel),
dismissText = stringResource(Res.string.cancel),
onDismiss = onDismiss,
confirmText = stringResource(R.string.import_label),
confirmText = stringResource(Res.string.import_label),
onConfirm = {
viewModel.addSharedContact(sharedContact)
onDismiss()

View File

@@ -28,8 +28,8 @@ 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.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.AutoLinkText
import org.meshtastic.core.strings.R as Res
@Composable
fun AnalyticsIntro(modifier: Modifier = Modifier) {
@@ -39,17 +39,21 @@ fun AnalyticsIntro(modifier: Modifier = Modifier) {
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
val textModifier = Modifier.fillMaxWidth().align(Alignment.CenterHorizontally)
Text(modifier = textModifier, textAlign = TextAlign.Center, text = stringResource(R.string.analytics_notice))
Text(modifier = textModifier, textAlign = TextAlign.Center, text = stringResource(R.string.analytics_platforms))
AutoLinkText(stringResource(R.string.firebase_link))
AutoLinkText(stringResource(R.string.datadog_link))
Text(modifier = textModifier, textAlign = TextAlign.Center, text = stringResource(Res.string.analytics_notice))
Text(
modifier = textModifier,
textAlign = TextAlign.Center,
text = stringResource(Res.string.analytics_platforms),
)
AutoLinkText(stringResource(Res.string.firebase_link))
AutoLinkText(stringResource(Res.string.datadog_link))
Text(
modifier = textModifier,
textAlign = TextAlign.Center,
text = stringResource(R.string.for_more_information_see_our_privacy_policy),
text = stringResource(Res.string.for_more_information_see_our_privacy_policy),
)
AutoLinkText(text = stringResource(R.string.privacy_url))
AutoLinkText(text = stringResource(Res.string.privacy_url))
}
}

View File

@@ -34,7 +34,7 @@ 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
import org.meshtastic.core.strings.R as Res
/**
* Screen for explaining and guiding the user to configure critical alert settings. This screen is part of the app
@@ -50,8 +50,8 @@ internal fun CriticalAlertsScreen(onSkip: () -> Unit, onConfigure: () -> Unit) {
IntroBottomBar(
onSkip = onSkip,
onConfigure = onConfigure,
configureButtonText = stringResource(R.string.configure_critical_alerts),
skipButtonText = stringResource(R.string.skip),
configureButtonText = stringResource(Res.string.configure_critical_alerts),
skipButtonText = stringResource(Res.string.skip),
)
},
) { innerPadding ->
@@ -61,13 +61,13 @@ internal fun CriticalAlertsScreen(onSkip: () -> Unit, onConfigure: () -> Unit) {
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(R.string.critical_alerts),
text = stringResource(Res.string.critical_alerts),
style = MaterialTheme.typography.headlineLarge.copy(fontWeight = FontWeight.Bold),
textAlign = TextAlign.Center,
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(R.string.critical_alerts_dnd_request_text),
text = stringResource(Res.string.critical_alerts_dnd_request_text),
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center,
)

View File

@@ -26,7 +26,7 @@ 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
import org.meshtastic.core.strings.R as Res
/**
* Screen for configuring location permissions during the app introduction. It explains why location permissions are
@@ -42,8 +42,8 @@ internal fun LocationScreen(showNextButton: Boolean, onSkip: () -> Unit, onConfi
val context = LocalContext.current
val annotatedString =
context.createClickableAnnotatedString(
fullTextRes = R.string.phone_location_description,
linkTextRes = R.string.settings,
fullTextRes = Res.string.phone_location_description,
linkTextRes = Res.string.settings,
tag = SETTINGS_TAG,
)
@@ -51,34 +51,34 @@ internal fun LocationScreen(showNextButton: Boolean, onSkip: () -> Unit, onConfi
listOf(
FeatureUIData(
icon = Icons.Outlined.LocationOn,
titleRes = R.string.share_location,
subtitleRes = R.string.share_location_description,
titleRes = Res.string.share_location,
subtitleRes = Res.string.share_location_description,
),
FeatureUIData(
icon = Icons.Outlined.Router,
titleRes = R.string.distance_measurements,
subtitleRes = R.string.distance_measurements_description,
titleRes = Res.string.distance_measurements,
subtitleRes = Res.string.distance_measurements_description,
),
FeatureUIData(
icon = Icons.Outlined.Router, // Consider a different icon if appropriate
titleRes = R.string.distance_filters,
subtitleRes = R.string.distance_filters_description,
titleRes = Res.string.distance_filters,
subtitleRes = Res.string.distance_filters_description,
),
FeatureUIData(
icon = Icons.Outlined.LocationOn, // Consider a different icon if appropriate
titleRes = R.string.mesh_map_location,
subtitleRes = R.string.mesh_map_location_description,
titleRes = Res.string.mesh_map_location,
subtitleRes = Res.string.mesh_map_location_description,
),
)
}
PermissionScreenLayout(
headlineRes = R.string.phone_location,
headlineRes = Res.string.phone_location,
annotatedDescription = annotatedString,
features = features,
onSkip = onSkip,
onConfigure = onConfigure,
configureButtonTextRes = if (showNextButton) R.string.next else R.string.configure_location_permissions,
configureButtonTextRes = if (showNextButton) Res.string.next else Res.string.configure_location_permissions,
onAnnotationClick = {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.fromParts("package", context.packageName, null)

View File

@@ -37,7 +37,7 @@ 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
import org.meshtastic.core.strings.R as Res
/**
* Screen for configuring notification permissions during the app introduction. It explains why notification permissions
@@ -53,8 +53,8 @@ internal fun NotificationsScreen(showNextButton: Boolean, onSkip: () -> Unit, on
val context = LocalContext.current
val annotatedString =
context.createClickableAnnotatedString(
fullTextRes = R.string.notification_permissions_description,
linkTextRes = R.string.settings,
fullTextRes = Res.string.notification_permissions_description,
linkTextRes = Res.string.settings,
tag = SETTINGS_TAG,
)
@@ -62,41 +62,44 @@ internal fun NotificationsScreen(showNextButton: Boolean, onSkip: () -> Unit, on
listOf(
FeatureUIData(
icon = Icons.Outlined.Message,
titleRes = R.string.incoming_messages,
subtitleRes = R.string.notifications_for_channel_and_direct_messages,
titleRes = Res.string.incoming_messages,
subtitleRes = Res.string.notifications_for_channel_and_direct_messages,
),
FeatureUIData(
icon = Icons.Outlined.SpeakerPhone,
titleRes = R.string.new_nodes,
subtitleRes = R.string.notifications_for_newly_discovered_nodes,
titleRes = Res.string.new_nodes,
subtitleRes = Res.string.notifications_for_newly_discovered_nodes,
),
FeatureUIData(
icon = Icons.Outlined.BatteryAlert,
titleRes = R.string.low_battery,
subtitleRes = R.string.notifications_for_low_battery_alerts,
titleRes = Res.string.low_battery,
subtitleRes = Res.string.notifications_for_low_battery_alerts,
),
)
}
PermissionScreenLayout(
headlineRes = R.string.app_notifications,
headlineRes = Res.string.app_notifications,
annotatedDescription = annotatedString,
features = features,
additionalContent = {
Text(
text = stringResource(R.string.critical_alerts),
text = stringResource(Res.string.critical_alerts),
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold),
textAlign = TextAlign.Center,
)
Spacer(modifier = Modifier.height(16.dp))
FeatureRow(
feature =
FeatureUIData(icon = Icons.Filled.Notifications, subtitleRes = R.string.critical_alerts_description),
FeatureUIData(
icon = Icons.Filled.Notifications,
subtitleRes = Res.string.critical_alerts_description,
),
)
},
onSkip = onSkip,
onConfigure = onConfigure,
configureButtonTextRes = if (showNextButton) R.string.next else R.string.configure_notification_permissions,
configureButtonTextRes = if (showNextButton) Res.string.next else Res.string.configure_notification_permissions,
onAnnotationClick = {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.fromParts("package", context.packageName, null)

View File

@@ -43,7 +43,7 @@ import androidx.compose.ui.text.TextLayoutResult
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
import org.meshtastic.core.strings.R as Res
/**
* A generic layout for screens within the app introduction flow. It typically presents a headline, a descriptive text
@@ -93,7 +93,7 @@ internal fun PermissionScreenLayout(
onSkip = onSkip,
onConfigure = onConfigure,
configureButtonText = stringResource(configureButtonTextRes),
skipButtonText = stringResource(R.string.skip),
skipButtonText = stringResource(Res.string.skip),
)
},
) { innerPadding ->

View File

@@ -40,7 +40,7 @@ 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 org.meshtastic.core.strings.R
import org.meshtastic.core.strings.R as Res
/**
* The initial welcome screen for the app introduction flow. It displays a brief overview of the app's key features.
@@ -53,18 +53,18 @@ internal fun WelcomeScreen(onGetStarted: () -> Unit) {
listOf(
FeatureUIData(
icon = Icons.Outlined.SettingsInputAntenna,
titleRes = R.string.stay_connected_anywhere,
subtitleRes = R.string.communicate_off_the_grid,
titleRes = Res.string.stay_connected_anywhere,
subtitleRes = Res.string.communicate_off_the_grid,
),
FeatureUIData(
icon = Icons.Outlined.Hub,
titleRes = R.string.create_your_own_networks,
subtitleRes = R.string.easily_set_up_private_mesh_networks,
titleRes = Res.string.create_your_own_networks,
subtitleRes = Res.string.easily_set_up_private_mesh_networks,
),
FeatureUIData(
icon = Icons.Outlined.NearMe,
titleRes = R.string.track_and_share_locations,
subtitleRes = R.string.share_your_location_in_real_time,
titleRes = Res.string.track_and_share_locations,
subtitleRes = Res.string.share_your_location_in_real_time,
),
)
}
@@ -75,7 +75,7 @@ internal fun WelcomeScreen(onGetStarted: () -> Unit) {
onSkip = {}, // No skip on welcome
onConfigure = onGetStarted,
skipButtonText = "", // Not shown
configureButtonText = stringResource(R.string.get_started),
configureButtonText = stringResource(Res.string.get_started),
showSkipButton = false, // Explicitly hide skip for welcome
)
},
@@ -86,12 +86,12 @@ internal fun WelcomeScreen(onGetStarted: () -> Unit) {
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(R.string.intro_welcome),
text = stringResource(Res.string.intro_welcome),
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold),
textAlign = TextAlign.Center,
)
Text(
text = stringResource(R.string.meshtastic),
text = stringResource(Res.string.meshtastic),
style = MaterialTheme.typography.headlineLarge.copy(fontWeight = FontWeight.Bold),
textAlign = TextAlign.Center,
)

View File

@@ -71,7 +71,6 @@ import org.meshtastic.core.database.entity.Packet
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DataPacket
import org.meshtastic.core.model.util.formatAgo
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.cluster.RadiusMarkerClusterer
import org.meshtastic.feature.map.component.CacheLayout
import org.meshtastic.feature.map.component.DownloadButton
@@ -104,6 +103,7 @@ import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
import timber.log.Timber
import java.io.File
import java.text.DateFormat
import org.meshtastic.core.strings.R as Res
@Composable
private fun MapView.UpdateMarkers(
@@ -162,7 +162,7 @@ private fun cacheManagerCallback(onTaskComplete: () -> Unit, onTaskFailed: (Int)
private fun Context.purgeTileSource(onResult: (String) -> Unit) {
val cache = SqlTileWriterExt()
val builder = MaterialAlertDialogBuilder(this)
builder.setTitle(R.string.map_tile_source)
builder.setTitle(Res.string.map_tile_source)
val sources = cache.sources
val sourceList = mutableListOf<String>()
for (i in sources.indices) {
@@ -177,20 +177,20 @@ private fun Context.purgeTileSource(onResult: (String) -> Unit) {
selectedList.remove(i)
}
}
builder.setPositiveButton(R.string.clear) { _, _ ->
builder.setPositiveButton(Res.string.clear) { _, _ ->
for (x in selectedList) {
val item = sources[x]
val b = cache.purgeCache(item.source)
onResult(
if (b) {
getString(R.string.map_purge_success, item.source)
getString(Res.string.map_purge_success, item.source)
} else {
getString(R.string.map_purge_fail)
getString(Res.string.map_purge_fail)
},
)
}
}
builder.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.cancel() }
builder.setNegativeButton(Res.string.cancel) { dialog, _ -> dialog.cancel() }
builder.show()
}
@@ -262,7 +262,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
fun MapView.toggleMyLocation() {
if (context.gpsDisabled()) {
Timber.d("Telling user we need location turned on for MyLocationNewOverlay")
Toast.makeText(context, R.string.location_disabled, Toast.LENGTH_SHORT).show()
Toast.makeText(context, Res.string.location_disabled, Toast.LENGTH_SHORT).show()
return
}
Timber.d("user clicked MyLocationNewOverlay ${myLocationOverlay == null}")
@@ -324,14 +324,14 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
title = u.longName
snippet =
context.getString(
R.string.map_node_popup_details,
Res.string.map_node_popup_details,
node.gpsString(),
formatAgo(node.lastHeard),
formatAgo(p.time),
if (node.batteryStr != "") node.batteryStr else "?",
)
ourNode?.distanceStr(node, displayUnits)?.let { dist ->
subDescription = context.getString(R.string.map_subDescription, ourNode.bearing(node), dist)
subDescription = context.getString(Res.string.map_subDescription, ourNode.bearing(node), dist)
}
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
position = nodePosition
@@ -352,14 +352,14 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
fun showDeleteMarkerDialog(waypoint: Waypoint) {
val builder = MaterialAlertDialogBuilder(context)
builder.setTitle(R.string.waypoint_delete)
builder.setNeutralButton(R.string.cancel) { _, _ -> Timber.d("User canceled marker delete dialog") }
builder.setNegativeButton(R.string.delete_for_me) { _, _ ->
builder.setTitle(Res.string.waypoint_delete)
builder.setNeutralButton(Res.string.cancel) { _, _ -> Timber.d("User canceled marker delete dialog") }
builder.setNegativeButton(Res.string.delete_for_me) { _, _ ->
Timber.d("User deleted waypoint ${waypoint.id} for me")
mapViewModel.deleteWaypoint(waypoint.id)
}
if (waypoint.lockedTo in setOf(0, mapViewModel.myNodeNum ?: 0) && isConnected) {
builder.setPositiveButton(R.string.delete_for_everyone) { _, _ ->
builder.setPositiveButton(Res.string.delete_for_everyone) { _, _ ->
Timber.d("User deleted waypoint ${waypoint.id} for everyone")
mapViewModel.sendWaypoint(waypoint.copy { expire = 1 })
mapViewModel.deleteWaypoint(waypoint.id)
@@ -394,7 +394,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
}
fun getUsername(id: String?) = if (id == DataPacket.ID_LOCAL) {
context.getString(R.string.you)
context.getString(Res.string.you)
} else {
mapViewModel.getUser(id).longName
}
@@ -434,7 +434,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
MarkerWithLabel(this, label, emoji).apply {
id = "${pt.id}"
title = "${pt.name} (${getUsername(waypoint.data.from)}$lock)"
snippet = "[$time] ${pt.description} " + stringResource(R.string.expires) + ": $expireTimeStr"
snippet = "[$time] ${pt.description} " + stringResource(Res.string.expires) + ": $expireTimeStr"
position = GeoPoint(pt.latitudeI * 1e-7, pt.longitudeI * 1e-7)
setVisible(false) // This seems to be always false, was this intended?
setOnLongClickListener {
@@ -447,22 +447,22 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
LaunchedEffect(showCurrentCacheInfo) {
if (!showCurrentCacheInfo) return@LaunchedEffect
Toast.makeText(context, R.string.calculating, Toast.LENGTH_SHORT).show()
Toast.makeText(context, Res.string.calculating, Toast.LENGTH_SHORT).show()
val cacheManager = CacheManager(map)
val cacheCapacity = cacheManager.cacheCapacity()
val currentCacheUsage = cacheManager.currentCacheUsage()
val mapCacheInfoText =
context.getString(
R.string.map_cache_info,
Res.string.map_cache_info,
cacheCapacity / (1024.0 * 1024.0),
currentCacheUsage / (1024.0 * 1024.0),
)
MaterialAlertDialogBuilder(context)
.setTitle(R.string.map_cache_manager)
.setTitle(Res.string.map_cache_manager)
.setMessage(mapCacheInfoText)
.setPositiveButton(R.string.close) { dialog, _ ->
.setPositiveButton(Res.string.close) { dialog, _ ->
showCurrentCacheInfo = false
dialog.dismiss()
}
@@ -524,7 +524,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
val tileCount: Int =
CacheManager(this)
.possibleTilesInArea(downloadRegionBoundingBox, zoomLevelMin.toInt(), zoomLevelMax.toInt())
cacheEstimate = context.getString(R.string.map_cache_tiles, tileCount)
cacheEstimate = context.getString(Res.string.map_cache_tiles, tileCount)
}
val boxOverlayListener =
@@ -556,13 +556,13 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
zoomLevelMax.toInt(),
cacheManagerCallback(
onTaskComplete = {
Toast.makeText(context, R.string.map_download_complete, Toast.LENGTH_SHORT).show()
Toast.makeText(context, Res.string.map_download_complete, Toast.LENGTH_SHORT).show()
writer.onDetach()
},
onTaskFailed = { errors ->
Toast.makeText(
context,
context.getString(R.string.map_download_errors, errors),
context.getString(Res.string.map_download_errors, errors),
Toast.LENGTH_SHORT,
)
.show()
@@ -594,13 +594,13 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
fun Context.showCacheManagerDialog() {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.map_offline_manager)
.setTitle(Res.string.map_offline_manager)
.setItems(
arrayOf<CharSequence>(
getString(R.string.map_cache_size),
getString(R.string.map_download_region),
getString(R.string.map_clear_tiles),
getString(R.string.cancel),
getString(Res.string.map_cache_size),
getString(Res.string.map_download_region),
getString(Res.string.map_clear_tiles),
getString(Res.string.cancel),
),
) { dialog, which ->
when (which) {
@@ -652,13 +652,13 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
MapButton(
onClick = ::showMapStyleDialog,
icon = Icons.Outlined.Layers,
contentDescription = R.string.map_style_selection,
contentDescription = Res.string.map_style_selection,
)
Box(modifier = Modifier) {
MapButton(
onClick = { mapFilterExpanded = true },
icon = Icons.Outlined.Tune,
contentDescription = R.string.map_filter,
contentDescription = Res.string.map_filter,
)
DropdownMenu(
expanded = mapFilterExpanded,
@@ -678,7 +678,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
tint = MaterialTheme.colorScheme.onSurface,
)
Text(
text = stringResource(R.string.only_favorites),
text = stringResource(Res.string.only_favorites),
modifier = Modifier.weight(1f),
)
Checkbox(
@@ -703,7 +703,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
tint = MaterialTheme.colorScheme.onSurface,
)
Text(
text = stringResource(R.string.show_waypoints),
text = stringResource(Res.string.show_waypoints),
modifier = Modifier.weight(1f),
)
Checkbox(
@@ -728,7 +728,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
tint = MaterialTheme.colorScheme.onSurface,
)
Text(
text = stringResource(R.string.show_precision_circle),
text = stringResource(Res.string.show_precision_circle),
modifier = Modifier.weight(1f),
)
Checkbox(
@@ -750,7 +750,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
} else {
Icons.Default.LocationDisabled
},
contentDescription = stringResource(R.string.toggle_my_position),
contentDescription = stringResource(Res.string.toggle_my_position),
) {
if (locationPermissionsState.allPermissionsGranted) {
map.toggleMyLocation()

View File

@@ -36,7 +36,7 @@ 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.meshtastic.core.strings.R
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalLayoutApi::class)
@Composable
@@ -55,7 +55,7 @@ fun CacheLayout(
.padding(8.dp),
) {
Text(
text = stringResource(R.string.map_select_download_region),
text = stringResource(Res.string.map_select_download_region),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.headlineSmall,
@@ -64,7 +64,7 @@ fun CacheLayout(
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.map_tile_download_estimate) + " " + cacheEstimate,
text = stringResource(Res.string.map_tile_download_estimate) + " " + cacheEstimate,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyLarge,
@@ -75,10 +75,10 @@ fun CacheLayout(
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
Button(onClick = onCancelDownload, modifier = Modifier.weight(1f)) {
Text(text = stringResource(R.string.cancel), color = MaterialTheme.colorScheme.onPrimary)
Text(text = stringResource(Res.string.cancel), color = MaterialTheme.colorScheme.onPrimary)
}
Button(onClick = onExecuteJob, modifier = Modifier.weight(1f)) {
Text(text = stringResource(R.string.map_start_download), color = MaterialTheme.colorScheme.onPrimary)
Text(text = stringResource(Res.string.map_start_download), color = MaterialTheme.colorScheme.onPrimary)
}
}
}

View File

@@ -31,7 +31,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.res.stringResource
import org.meshtastic.core.strings.R
import org.meshtastic.core.strings.R as Res
@Composable
fun DownloadButton(enabled: Boolean, onClick: () -> Unit) {
@@ -51,7 +51,7 @@ fun DownloadButton(enabled: Boolean, onClick: () -> Unit) {
FloatingActionButton(onClick = onClick, contentColor = MaterialTheme.colorScheme.primary) {
Icon(
imageVector = Icons.Default.Download,
contentDescription = stringResource(R.string.map_download_region),
contentDescription = stringResource(Res.string.map_download_region),
modifier = Modifier.scale(1.25f),
)
}

View File

@@ -64,7 +64,6 @@ 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.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.emoji.EmojiPickerDialog
import org.meshtastic.core.ui.theme.AppTheme
@@ -74,6 +73,7 @@ import org.meshtastic.proto.waypoint
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@OptIn(ExperimentalLayoutApi::class)
@@ -86,7 +86,7 @@ fun EditWaypointDialog(
modifier: Modifier = Modifier,
) {
var waypointInput by remember { mutableStateOf(waypoint) }
val title = if (waypoint.id == 0) R.string.waypoint_new else R.string.waypoint_edit
val title = if (waypoint.id == 0) Res.string.waypoint_new else Res.string.waypoint_edit
@Suppress("MagicNumber")
val emoji = if (waypointInput.icon == 0) 128205 else waypointInput.icon
@@ -145,7 +145,7 @@ fun EditWaypointDialog(
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp),
)
EditTextPreference(
title = stringResource(R.string.name),
title = stringResource(Res.string.name),
value = waypointInput.name,
maxSize = 29,
enabled = true,
@@ -168,7 +168,7 @@ fun EditWaypointDialog(
},
)
EditTextPreference(
title = stringResource(R.string.description),
title = stringResource(Res.string.description),
value = waypointInput.description,
maxSize = 99,
enabled = true,
@@ -182,8 +182,8 @@ fun EditWaypointDialog(
modifier = Modifier.fillMaxWidth().size(48.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Image(imageVector = Icons.Default.Lock, contentDescription = stringResource(R.string.locked))
Text(stringResource(R.string.locked))
Image(imageVector = Icons.Default.Lock, contentDescription = stringResource(Res.string.locked))
Text(stringResource(Res.string.locked))
Switch(
modifier = Modifier.fillMaxWidth().wrapContentWidth(Alignment.End),
checked = waypointInput.lockedTo != 0,
@@ -230,9 +230,9 @@ fun EditWaypointDialog(
) {
Image(
imageVector = Icons.Default.CalendarMonth,
contentDescription = stringResource(R.string.expires),
contentDescription = stringResource(Res.string.expires),
)
Text(stringResource(R.string.expires))
Text(stringResource(Res.string.expires))
Switch(
modifier = Modifier.fillMaxWidth().wrapContentWidth(Alignment.End),
checked = waypointInput.expire != Int.MAX_VALUE && waypointInput.expire != 0,
@@ -266,7 +266,7 @@ fun EditWaypointDialog(
verticalAlignment = Alignment.CenterVertically,
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Button(onClick = { datePickerDialog.show() }) { Text(stringResource(R.string.date)) }
Button(onClick = { datePickerDialog.show() }) { Text(stringResource(Res.string.date)) }
Text(
modifier = Modifier.padding(top = 4.dp),
text = "$selectedDate",
@@ -275,7 +275,7 @@ fun EditWaypointDialog(
)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Button(onClick = { timePickerDialog.show() }) { Text(stringResource(R.string.time)) }
Button(onClick = { timePickerDialog.show() }) { Text(stringResource(Res.string.time)) }
Text(
modifier = Modifier.padding(top = 4.dp),
text = "$selectedTime",
@@ -294,7 +294,7 @@ fun EditWaypointDialog(
verticalArrangement = Arrangement.Center,
) {
TextButton(modifier = modifier.weight(1f), onClick = onDismissRequest) {
Text(stringResource(R.string.cancel))
Text(stringResource(Res.string.cancel))
}
if (waypoint.id != 0) {
Button(
@@ -302,11 +302,11 @@ fun EditWaypointDialog(
onClick = { onDeleteClicked(waypointInput) },
enabled = waypointInput.name.isNotEmpty(),
) {
Text(stringResource(R.string.delete))
Text(stringResource(Res.string.delete))
}
}
Button(modifier = modifier.weight(1f), onClick = { onSendClicked(waypointInput) }, enabled = true) {
Text(stringResource(R.string.send))
Text(stringResource(Res.string.send))
}
}
},

View File

@@ -29,8 +29,8 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun MapButton(
@@ -57,5 +57,5 @@ fun MapButton(icon: ImageVector, contentDescription: String?, modifier: Modifier
@PreviewLightDark
@Composable
private fun MapButtonPreview() {
AppTheme { MapButton(icon = Icons.Outlined.Layers, contentDescription = R.string.map_style_selection) }
AppTheme { MapButton(icon = Icons.Outlined.Layers, contentDescription = Res.string.map_style_selection) }
}

View File

@@ -97,7 +97,6 @@ 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.proto.formatPositionTime
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.feature.map.component.ClusterItemsListDialog
import org.meshtastic.feature.map.component.CustomMapLayersSheet
@@ -114,6 +113,7 @@ import org.meshtastic.proto.copy
import org.meshtastic.proto.waypoint
import timber.log.Timber
import java.text.DateFormat
import org.meshtastic.core.strings.R as Res
private const val MIN_TRACK_POINT_DISTANCE_METERS = 20f
private const val DEG_D = 1e-7
@@ -381,7 +381,7 @@ fun MapView(
} else {
MarkerInfoWindowComposable(
state = markerState,
title = stringResource(R.string.position),
title = stringResource(Res.string.position),
snippet = formatAgo(position.time),
zIndex = alpha,
infoContent = {
@@ -394,7 +394,7 @@ fun MapView(
) {
Icon(
imageVector = androidx.compose.material.icons.Icons.Default.TripOrigin,
contentDescription = stringResource(R.string.track_point),
contentDescription = stringResource(Res.string.track_point),
tint = color,
)
}
@@ -639,25 +639,28 @@ private fun PositionInfoWindowContent(
Card {
Column(modifier = Modifier.padding(8.dp)) {
PositionRow(label = stringResource(R.string.latitude), value = "%.5f".format(position.latitudeI * DEG_D))
PositionRow(label = stringResource(R.string.longitude), value = "%.5f".format(position.longitudeI * DEG_D))
PositionRow(label = stringResource(R.string.sats), value = position.satsInView.toString())
PositionRow(label = stringResource(Res.string.latitude), value = "%.5f".format(position.latitudeI * DEG_D))
PositionRow(
label = stringResource(R.string.alt),
label = stringResource(Res.string.longitude),
value = "%.5f".format(position.longitudeI * DEG_D),
)
PositionRow(label = stringResource(Res.string.sats), value = position.satsInView.toString())
PositionRow(
label = stringResource(Res.string.alt),
value = position.altitude.metersIn(displayUnits).toString(displayUnits),
)
PositionRow(label = stringResource(R.string.speed), value = speedFromPosition(position, displayUnits))
PositionRow(label = stringResource(Res.string.speed), value = speedFromPosition(position, displayUnits))
PositionRow(
label = stringResource(R.string.heading),
label = stringResource(Res.string.heading),
value = "%.0f°".format(position.groundTrack * HEADING_DEG),
)
PositionRow(label = stringResource(R.string.timestamp), value = position.formatPositionTime(dateFormat))
PositionRow(label = stringResource(Res.string.timestamp), value = position.formatPositionTime(dateFormat))
}
}
}

View File

@@ -31,9 +31,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.feature.map.model.NodeClusterItem
import org.meshtastic.core.strings.R as Res
@Composable
fun ClusterItemsListDialog(
@@ -43,7 +43,7 @@ fun ClusterItemsListDialog(
) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(text = stringResource(R.string.nodes_at_this_location)) },
title = { Text(text = stringResource(Res.string.nodes_at_this_location)) },
text = {
// Use a LazyColumn for potentially long lists of items
LazyColumn(contentPadding = PaddingValues(vertical = 8.dp)) {
@@ -52,7 +52,7 @@ fun ClusterItemsListDialog(
}
}
},
confirmButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.okay)) } },
confirmButton = { TextButton(onClick = onDismiss) { Text(stringResource(Res.string.okay)) } },
)
}

View File

@@ -39,8 +39,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.MapLayerItem
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@Composable
@@ -55,7 +55,7 @@ fun CustomMapLayersSheet(
item {
Text(
modifier = Modifier.padding(16.dp),
text = stringResource(R.string.manage_map_layers),
text = stringResource(Res.string.manage_map_layers),
style = MaterialTheme.typography.headlineSmall,
)
HorizontalDivider()
@@ -63,7 +63,7 @@ fun CustomMapLayersSheet(
item {
Text(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 0.dp),
text = stringResource(R.string.map_layer_formats),
text = stringResource(Res.string.map_layer_formats),
style = MaterialTheme.typography.bodySmall,
)
}
@@ -72,7 +72,7 @@ fun CustomMapLayersSheet(
item {
Text(
modifier = Modifier.padding(16.dp),
text = stringResource(R.string.no_map_layers_loaded),
text = stringResource(Res.string.no_map_layers_loaded),
style = MaterialTheme.typography.bodyMedium,
)
}
@@ -93,9 +93,9 @@ fun CustomMapLayersSheet(
contentDescription =
stringResource(
if (layer.isVisible) {
R.string.hide_layer
Res.string.hide_layer
} else {
R.string.show_layer
Res.string.show_layer
},
),
)
@@ -103,7 +103,7 @@ fun CustomMapLayersSheet(
IconButton(onClick = { onRemoveLayer(layer.id) }) {
Icon(
imageVector = Icons.Filled.Delete,
contentDescription = stringResource(R.string.remove_layer),
contentDescription = stringResource(Res.string.remove_layer),
)
}
}
@@ -114,7 +114,7 @@ fun CustomMapLayersSheet(
}
item {
Button(modifier = Modifier.fillMaxWidth().padding(16.dp), onClick = onAddLayerClicked) {
Text(stringResource(R.string.add_layer))
Text(stringResource(Res.string.add_layer))
}
}
}

View File

@@ -53,8 +53,8 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.flow.collectLatest
import org.meshtastic.core.data.model.CustomTileProviderConfig
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.MapViewModel
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@Composable
@@ -89,7 +89,7 @@ fun CustomTileProviderManagerSheet(mapViewModel: MapViewModel) {
LazyColumn(contentPadding = PaddingValues(bottom = 16.dp)) {
item {
Text(
text = stringResource(R.string.manage_custom_tile_sources),
text = stringResource(Res.string.manage_custom_tile_sources),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(16.dp),
)
@@ -99,7 +99,7 @@ fun CustomTileProviderManagerSheet(mapViewModel: MapViewModel) {
if (customTileProviders.isEmpty()) {
item {
Text(
text = stringResource(R.string.no_custom_tile_sources_found),
text = stringResource(Res.string.no_custom_tile_sources_found),
modifier = Modifier.padding(16.dp),
style = MaterialTheme.typography.bodyMedium,
)
@@ -119,13 +119,13 @@ fun CustomTileProviderManagerSheet(mapViewModel: MapViewModel) {
) {
Icon(
Icons.Filled.Edit,
contentDescription = stringResource(R.string.edit_custom_tile_source),
contentDescription = stringResource(Res.string.edit_custom_tile_source),
)
}
IconButton(onClick = { mapViewModel.removeCustomTileProvider(config.id) }) {
Icon(
Icons.Filled.Delete,
contentDescription = stringResource(R.string.delete_custom_tile_source),
contentDescription = stringResource(Res.string.delete_custom_tile_source),
)
}
}
@@ -143,7 +143,7 @@ fun CustomTileProviderManagerSheet(mapViewModel: MapViewModel) {
},
modifier = Modifier.fillMaxWidth().padding(16.dp),
) {
Text(stringResource(R.string.add_custom_tile_source))
Text(stringResource(Res.string.add_custom_tile_source))
}
}
}
@@ -163,10 +163,10 @@ private fun AddEditCustomTileProviderDialog(
var urlError by remember { mutableStateOf<String?>(null) }
val customTileProviders by mapViewModel.customTileProviderConfigs.collectAsStateWithLifecycle()
val emptyNameError = stringResource(R.string.name_cannot_be_empty)
val providerNameExistsError = stringResource(R.string.provider_name_exists)
val urlCannotBeEmptyError = stringResource(R.string.url_cannot_be_empty)
val urlMustContainPlaceholdersError = stringResource(R.string.url_must_contain_placeholders)
val emptyNameError = stringResource(Res.string.name_cannot_be_empty)
val providerNameExistsError = stringResource(Res.string.provider_name_exists)
val urlCannotBeEmptyError = stringResource(Res.string.url_cannot_be_empty)
val urlMustContainPlaceholdersError = stringResource(Res.string.url_must_contain_placeholders)
fun validateAndSave() {
val currentNameError =
@@ -186,9 +186,9 @@ private fun AddEditCustomTileProviderDialog(
title = {
Text(
if (config == null) {
stringResource(R.string.add_custom_tile_source)
stringResource(Res.string.add_custom_tile_source)
} else {
stringResource(R.string.edit_custom_tile_source)
stringResource(Res.string.edit_custom_tile_source)
},
)
},
@@ -200,7 +200,7 @@ private fun AddEditCustomTileProviderDialog(
name = it
nameError = null
},
label = { Text(stringResource(R.string.name)) },
label = { Text(stringResource(Res.string.name)) },
isError = nameError != null,
supportingText = { nameError?.let { Text(it) } },
singleLine = true,
@@ -211,13 +211,13 @@ private fun AddEditCustomTileProviderDialog(
url = it
urlError = null
},
label = { Text(stringResource(R.string.url_template)) },
label = { Text(stringResource(Res.string.url_template)) },
isError = urlError != null,
supportingText = {
if (urlError != null) {
Text(urlError!!)
} else {
Text(stringResource(R.string.url_template_hint))
Text(stringResource(Res.string.url_template_hint))
}
},
singleLine = false,
@@ -225,8 +225,8 @@ private fun AddEditCustomTileProviderDialog(
)
}
},
confirmButton = { Button(onClick = { validateAndSave() }) { Text(stringResource(R.string.save)) } },
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } },
confirmButton = { Button(onClick = { validateAndSave() }) { Text(stringResource(Res.string.save)) } },
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(Res.string.cancel)) } },
)
}

View File

@@ -62,7 +62,6 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.emoji.EmojiPickerDialog
import org.meshtastic.proto.MeshProtos.Waypoint
import org.meshtastic.proto.copy
@@ -70,6 +69,7 @@ import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.TimeZone
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("LongMethod", "CyclomaticComplexMethod", "MagicNumber")
@@ -82,7 +82,7 @@ fun EditWaypointDialog(
modifier: Modifier = Modifier,
) {
var waypointInput by remember { mutableStateOf(waypoint) }
val title = if (waypoint.id == 0) R.string.waypoint_new else R.string.waypoint_edit
val title = if (waypoint.id == 0) Res.string.waypoint_new else Res.string.waypoint_edit
val defaultEmoji = 0x1F4CD // 📍 Round Pushpin
val currentEmojiCodepoint = if (waypointInput.icon == 0) defaultEmoji else waypointInput.icon
var showEmojiPickerView by remember { mutableStateOf(false) }
@@ -149,7 +149,7 @@ fun EditWaypointDialog(
OutlinedTextField(
value = waypointInput.name,
onValueChange = { waypointInput = waypointInput.copy { name = it.take(29) } },
label = { Text(stringResource(R.string.name)) },
label = { Text(stringResource(Res.string.name)) },
singleLine = true,
keyboardOptions =
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Next),
@@ -170,7 +170,7 @@ fun EditWaypointDialog(
OutlinedTextField(
value = waypointInput.description,
onValueChange = { waypointInput = waypointInput.copy { description = it.take(99) } },
label = { Text(stringResource(R.string.description)) },
label = { Text(stringResource(Res.string.description)) },
keyboardOptions =
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { /* Handle next/done focus */ }),
@@ -187,10 +187,10 @@ fun EditWaypointDialog(
Row(verticalAlignment = Alignment.CenterVertically) {
Image(
imageVector = Icons.Default.Lock,
contentDescription = stringResource(R.string.locked),
contentDescription = stringResource(Res.string.locked),
)
Spacer(modifier = Modifier.width(8.dp))
Text(stringResource(R.string.locked))
Text(stringResource(Res.string.locked))
}
Switch(
checked = waypointInput.lockedTo != 0,
@@ -206,10 +206,10 @@ fun EditWaypointDialog(
Row(verticalAlignment = Alignment.CenterVertically) {
Image(
imageVector = Icons.Default.CalendarMonth,
contentDescription = stringResource(R.string.expires),
contentDescription = stringResource(Res.string.expires),
)
Spacer(modifier = Modifier.width(8.dp))
Text(stringResource(R.string.expires))
Text(stringResource(Res.string.expires))
}
Switch(
checked = isExpiryEnabled,
@@ -285,7 +285,7 @@ fun EditWaypointDialog(
verticalAlignment = Alignment.CenterVertically,
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Button(onClick = { datePickerDialog.show() }) { Text(stringResource(R.string.date)) }
Button(onClick = { datePickerDialog.show() }) { Text(stringResource(Res.string.date)) }
Text(
modifier = Modifier.padding(top = 4.dp),
text = selectedDateString,
@@ -293,7 +293,7 @@ fun EditWaypointDialog(
)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Button(onClick = { timePickerDialog.show() }) { Text(stringResource(R.string.time)) }
Button(onClick = { timePickerDialog.show() }) { Text(stringResource(Res.string.time)) }
Text(
modifier = Modifier.padding(top = 4.dp),
text = selectedTimeString,
@@ -314,15 +314,15 @@ fun EditWaypointDialog(
onClick = { onDeleteClicked(waypointInput) },
modifier = Modifier.padding(end = 8.dp),
) {
Text(stringResource(R.string.delete), color = MaterialTheme.colorScheme.error)
Text(stringResource(Res.string.delete), color = MaterialTheme.colorScheme.error)
}
}
Spacer(modifier = Modifier.weight(1f)) // Pushes delete to left and cancel/send to right
TextButton(onClick = onDismissRequest, modifier = Modifier.padding(end = 8.dp)) {
Text(stringResource(R.string.cancel))
Text(stringResource(Res.string.cancel))
}
Button(onClick = { onSendClicked(waypointInput) }, enabled = waypointInput.name.isNotBlank()) {
Text(stringResource(R.string.send))
Text(stringResource(Res.string.send))
}
}
},

View File

@@ -33,9 +33,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.stringResource
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.feature.map.MapViewModel
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
@@ -69,7 +69,7 @@ fun MapControlsOverlay(
if (isNodeMap) {
MapButton(
icon = Icons.Outlined.Tune,
contentDescription = stringResource(R.string.map_filter),
contentDescription = stringResource(Res.string.map_filter),
onClick = onToggleMapFilterMenu,
)
NodeMapFilterDropdown(
@@ -81,7 +81,7 @@ fun MapControlsOverlay(
Box {
MapButton(
icon = Icons.Outlined.Tune,
contentDescription = stringResource(R.string.map_filter),
contentDescription = stringResource(Res.string.map_filter),
onClick = onToggleMapFilterMenu,
)
MapFilterDropdown(
@@ -95,7 +95,7 @@ fun MapControlsOverlay(
Box {
MapButton(
icon = Icons.Outlined.Map,
contentDescription = stringResource(R.string.map_tile_source),
contentDescription = stringResource(Res.string.map_tile_source),
onClick = onToggleMapTypeMenu,
)
MapTypeDropdown(
@@ -108,7 +108,7 @@ fun MapControlsOverlay(
MapButton(
icon = Icons.Outlined.Layers,
contentDescription = stringResource(R.string.manage_map_layers),
contentDescription = stringResource(Res.string.manage_map_layers),
onClick = onManageLayersClicked,
)
@@ -121,7 +121,7 @@ fun MapControlsOverlay(
} else {
Icons.Outlined.MyLocation
},
contentDescription = stringResource(R.string.toggle_my_position),
contentDescription = stringResource(Res.string.toggle_my_position),
onClick = onToggleLocationTracking,
)
}
@@ -137,7 +137,7 @@ private fun CompassButton(onClick: () -> Unit, bearing: Float, isFollowing: Bool
modifier = Modifier.rotate(-bearing),
icon = icon,
iconTint = MaterialTheme.colorScheme.StatusRed.takeIf { bearing == 0f },
contentDescription = stringResource(R.string.orient_north),
contentDescription = stringResource(Res.string.orient_north),
onClick = onClick,
)
}

View File

@@ -40,20 +40,20 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.LastHeardFilter
import org.meshtastic.feature.map.MapViewModel
import kotlin.math.roundToInt
import org.meshtastic.core.strings.R as Res
@Composable
internal fun MapFilterDropdown(expanded: Boolean, onDismissRequest: () -> Unit, mapViewModel: MapViewModel) {
val mapFilterState by mapViewModel.mapFilterStateFlow.collectAsStateWithLifecycle()
DropdownMenu(expanded = expanded, onDismissRequest = onDismissRequest) {
DropdownMenuItem(
text = { Text(stringResource(R.string.only_favorites)) },
text = { Text(stringResource(Res.string.only_favorites)) },
onClick = { mapViewModel.toggleOnlyFavorites() },
leadingIcon = {
Icon(imageVector = Icons.Filled.Star, contentDescription = stringResource(R.string.only_favorites))
Icon(imageVector = Icons.Filled.Star, contentDescription = stringResource(Res.string.only_favorites))
},
trailingIcon = {
Checkbox(
@@ -63,10 +63,10 @@ internal fun MapFilterDropdown(expanded: Boolean, onDismissRequest: () -> Unit,
},
)
DropdownMenuItem(
text = { Text(stringResource(R.string.show_waypoints)) },
text = { Text(stringResource(Res.string.show_waypoints)) },
onClick = { mapViewModel.toggleShowWaypointsOnMap() },
leadingIcon = {
Icon(imageVector = Icons.Filled.Place, contentDescription = stringResource(R.string.show_waypoints))
Icon(imageVector = Icons.Filled.Place, contentDescription = stringResource(Res.string.show_waypoints))
},
trailingIcon = {
Checkbox(
@@ -76,12 +76,12 @@ internal fun MapFilterDropdown(expanded: Boolean, onDismissRequest: () -> Unit,
},
)
DropdownMenuItem(
text = { Text(stringResource(R.string.show_precision_circle)) },
text = { Text(stringResource(Res.string.show_precision_circle)) },
onClick = { mapViewModel.toggleShowPrecisionCircleOnMap() },
leadingIcon = {
Icon(
imageVector = Icons.Outlined.RadioButtonUnchecked, // Placeholder icon
contentDescription = stringResource(R.string.show_precision_circle),
contentDescription = stringResource(Res.string.show_precision_circle),
)
},
trailingIcon = {
@@ -100,7 +100,7 @@ internal fun MapFilterDropdown(expanded: Boolean, onDismissRequest: () -> Unit,
Text(
text =
stringResource(
R.string.last_heard_filter_label,
Res.string.last_heard_filter_label,
stringResource(mapFilterState.lastHeardFilter.label),
),
style = MaterialTheme.typography.labelLarge,
@@ -131,7 +131,7 @@ internal fun NodeMapFilterDropdown(expanded: Boolean, onDismissRequest: () -> Un
Text(
text =
stringResource(
R.string.last_heard_filter_label,
Res.string.last_heard_filter_label,
stringResource(mapFilterState.lastHeardTrackFilter.label),
),
style = MaterialTheme.typography.labelLarge,

View File

@@ -29,8 +29,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.maps.android.compose.MapType
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.MapViewModel
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@Composable
@@ -46,10 +46,10 @@ internal fun MapTypeDropdown(
val googleMapTypes =
listOf(
stringResource(R.string.map_type_normal) to MapType.NORMAL,
stringResource(R.string.map_type_satellite) to MapType.SATELLITE,
stringResource(R.string.map_type_terrain) to MapType.TERRAIN,
stringResource(R.string.map_type_hybrid) to MapType.HYBRID,
stringResource(Res.string.map_type_normal) to MapType.NORMAL,
stringResource(Res.string.map_type_satellite) to MapType.SATELLITE,
stringResource(Res.string.map_type_terrain) to MapType.TERRAIN,
stringResource(Res.string.map_type_hybrid) to MapType.HYBRID,
)
DropdownMenu(expanded = expanded, onDismissRequest = onDismissRequest) {
@@ -62,7 +62,7 @@ internal fun MapTypeDropdown(
},
trailingIcon =
if (selectedCustomUrl == null && selectedGoogleMapType == type) {
{ Icon(Icons.Filled.Check, contentDescription = stringResource(R.string.selected_map_type)) }
{ Icon(Icons.Filled.Check, contentDescription = stringResource(Res.string.selected_map_type)) }
} else {
null
},
@@ -83,7 +83,7 @@ internal fun MapTypeDropdown(
{
Icon(
Icons.Filled.Check,
contentDescription = stringResource(R.string.selected_map_type),
contentDescription = stringResource(Res.string.selected_map_type),
)
}
} else {
@@ -94,7 +94,7 @@ internal fun MapTypeDropdown(
}
HorizontalDivider()
DropdownMenuItem(
text = { Text(stringResource(R.string.manage_custom_tile_sources)) },
text = { Text(stringResource(Res.string.manage_custom_tile_sources)) },
onClick = {
onManageCustomTileProvidersClicked()
onDismissRequest()

View File

@@ -24,9 +24,9 @@ import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.LatLng
import com.google.maps.android.compose.Marker
import com.google.maps.android.compose.rememberUpdatedMarkerState
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.BaseMapViewModel
import org.meshtastic.proto.MeshProtos
import org.meshtastic.core.strings.R as Res
private const val DEG_D = 1e-7
@@ -60,7 +60,7 @@ fun WaypointMarkers(
if (waypoint.lockedTo == 0 || waypoint.lockedTo == myNodeNum || !isConnected) {
onEditWaypointRequest(waypoint)
} else {
Toast.makeText(context, context.getString(R.string.locked), Toast.LENGTH_SHORT).show()
Toast.makeText(context, context.getString(Res.string.locked), Toast.LENGTH_SHORT).show()
}
},
)

View File

@@ -35,23 +35,23 @@ import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DataPacket
import org.meshtastic.core.prefs.map.MapPrefs
import org.meshtastic.core.service.ServiceRepository
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import org.meshtastic.proto.MeshProtos
import timber.log.Timber
import java.util.concurrent.TimeUnit
import org.meshtastic.core.strings.R as Res
@Suppress("MagicNumber")
sealed class LastHeardFilter(val seconds: Long, @StringRes val label: Int) {
data object Any : LastHeardFilter(0L, R.string.any)
data object Any : LastHeardFilter(0L, Res.string.any)
data object OneHour : LastHeardFilter(TimeUnit.HOURS.toSeconds(1), R.string.one_hour)
data object OneHour : LastHeardFilter(TimeUnit.HOURS.toSeconds(1), Res.string.one_hour)
data object EightHours : LastHeardFilter(TimeUnit.HOURS.toSeconds(8), R.string.eight_hours)
data object EightHours : LastHeardFilter(TimeUnit.HOURS.toSeconds(8), Res.string.eight_hours)
data object OneDay : LastHeardFilter(TimeUnit.DAYS.toSeconds(1), R.string.one_day)
data object OneDay : LastHeardFilter(TimeUnit.DAYS.toSeconds(1), Res.string.one_day)
data object TwoDays : LastHeardFilter(TimeUnit.DAYS.toSeconds(2), R.string.two_days)
data object TwoDays : LastHeardFilter(TimeUnit.DAYS.toSeconds(2), Res.string.two_days)
companion object {
fun fromSeconds(seconds: Long): LastHeardFilter = entries.find { it.seconds == seconds } ?: Any

View File

@@ -26,8 +26,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.strings.R as Res
@Composable
fun MapScreen(
@@ -44,7 +44,7 @@ fun MapScreen(
modifier = modifier,
topBar = {
MainAppBar(
title = stringResource(R.string.map),
title = stringResource(Res.string.map),
ourNode = ourNodeInfo,
showNodeChip = ourNodeInfo != null && isConnected,
canNavigateUp = false,

View File

@@ -100,13 +100,13 @@ 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.R
import org.meshtastic.core.ui.component.NodeKeyStatusIcon
import org.meshtastic.core.ui.component.SecurityIcon
import org.meshtastic.core.ui.component.SharedContactDialog
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.proto.AppOnlyProtos
import java.nio.charset.StandardCharsets
import org.meshtastic.core.strings.R as Res
private const val MESSAGE_CHARACTER_LIMIT_BYTES = 200
private const val SNIPPET_CHARACTER_LIMIT = 50
@@ -160,7 +160,7 @@ fun MessageScreen(
Triple(index, id, name)
}
val (channelIndex, nodeId, rawChannelName) = channelInfo
val unknownChannelText = stringResource(R.string.unknown_channel)
val unknownChannelText = stringResource(Res.string.unknown_channel)
val channelName = rawChannelName ?: unknownChannelText
val title =
@@ -350,7 +350,10 @@ private fun BoxScope.ScrollToBottomFab(coroutineScope: CoroutineScope, listState
}
},
) {
Icon(imageVector = Icons.Default.ArrowDownward, contentDescription = stringResource(R.string.scroll_to_bottom))
Icon(
imageVector = Icons.Default.ArrowDownward,
contentDescription = stringResource(Res.string.scroll_to_bottom),
)
}
}
@@ -367,7 +370,7 @@ private fun ReplySnippet(originalMessage: Message?, onClearReply: () -> Unit, ou
originalMessage?.let { message ->
val isFromLocalUser = message.node.user.id == DataPacket.ID_LOCAL
val replyingToNodeUser = if (isFromLocalUser) ourNode?.user else message.node.user
val unknownUserText = stringResource(R.string.unknown)
val unknownUserText = stringResource(Res.string.unknown)
Row(
modifier =
@@ -380,11 +383,11 @@ private fun ReplySnippet(originalMessage: Message?, onClearReply: () -> Unit, ou
) {
Icon(
imageVector = Icons.AutoMirrored.Default.Reply,
contentDescription = stringResource(R.string.reply), // Decorative
contentDescription = stringResource(Res.string.reply), // Decorative
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
text = stringResource(R.string.replying_to, replyingToNodeUser?.shortName ?: unknownUserText),
text = stringResource(Res.string.replying_to, replyingToNodeUser?.shortName ?: unknownUserText),
style = MaterialTheme.typography.labelMedium,
)
Text(
@@ -397,7 +400,7 @@ private fun ReplySnippet(originalMessage: Message?, onClearReply: () -> Unit, ou
IconButton(onClick = onClearReply) {
Icon(
Icons.Filled.Close,
contentDescription = stringResource(R.string.cancel_reply), // Specific action
contentDescription = stringResource(Res.string.cancel_reply), // Specific action
)
}
}
@@ -489,15 +492,15 @@ private fun String.limitBytes(maxBytes: Int): String {
*/
@Composable
private fun DeleteMessageDialog(count: Int, onConfirm: () -> Unit, onDismiss: () -> Unit) {
val deleteMessagesString = pluralStringResource(R.plurals.delete_messages, count, count)
val deleteMessagesString = pluralStringResource(Res.plurals.delete_messages, count, count)
AlertDialog(
onDismissRequest = onDismiss,
shape = RoundedCornerShape(16.dp),
title = { Text(stringResource(R.string.delete_messages_title)) },
title = { Text(stringResource(Res.string.delete_messages_title)) },
text = { Text(text = deleteMessagesString) },
confirmButton = { TextButton(onClick = onConfirm) { Text(stringResource(R.string.delete)) } },
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } },
confirmButton = { TextButton(onClick = onConfirm) { Text(stringResource(Res.string.delete)) } },
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(Res.string.cancel)) } },
)
}
@@ -526,19 +529,19 @@ private fun ActionModeTopBar(selectedCount: Int, onAction: (MessageMenuAction) -
IconButton(onClick = { onAction(MessageMenuAction.Dismiss) }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.clear_selection),
contentDescription = stringResource(Res.string.clear_selection),
)
}
},
actions = {
IconButton(onClick = { onAction(MessageMenuAction.ClipboardCopy) }) {
Icon(imageVector = Icons.Default.ContentCopy, contentDescription = stringResource(R.string.copy))
Icon(imageVector = Icons.Default.ContentCopy, contentDescription = stringResource(Res.string.copy))
}
IconButton(onClick = { onAction(MessageMenuAction.Delete) }) {
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(R.string.delete))
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(Res.string.delete))
}
IconButton(onClick = { onAction(MessageMenuAction.SelectAll) }) {
Icon(imageVector = Icons.Default.SelectAll, contentDescription = stringResource(R.string.select_all))
Icon(imageVector = Icons.Default.SelectAll, contentDescription = stringResource(Res.string.select_all))
}
},
)
@@ -580,7 +583,7 @@ private fun MessageTopBar(
IconButton(onClick = onNavigateBack) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.navigate_back),
contentDescription = stringResource(Res.string.navigate_back),
)
}
},
@@ -609,7 +612,7 @@ private fun MessageTopBarActions(
var expanded by remember { mutableStateOf(false) }
Box {
IconButton(onClick = { expanded = true }, enabled = true) {
Icon(imageVector = Icons.Default.MoreVert, contentDescription = stringResource(R.string.overflow_menu))
Icon(imageVector = Icons.Default.MoreVert, contentDescription = stringResource(Res.string.overflow_menu))
}
OverFlowMenu(
expanded = expanded,
@@ -633,9 +636,9 @@ private fun OverFlowMenu(
DropdownMenu(expanded = expanded, onDismissRequest = onDismiss) {
val quickChatToggleTitle =
if (showQuickChat) {
stringResource(R.string.quick_chat_hide)
stringResource(Res.string.quick_chat_hide)
} else {
stringResource(R.string.quick_chat_show)
stringResource(Res.string.quick_chat_show)
}
DropdownMenuItem(
text = { Text(quickChatToggleTitle) },
@@ -656,7 +659,7 @@ private fun OverFlowMenu(
},
)
DropdownMenuItem(
text = { Text(stringResource(R.string.quick_chat)) },
text = { Text(stringResource(Res.string.quick_chat)) },
onClick = {
onDismiss()
onNavigateToQuickChatOptions()
@@ -664,7 +667,7 @@ private fun OverFlowMenu(
leadingIcon = {
Icon(
imageVector = Icons.Default.ChatBubbleOutline,
contentDescription = stringResource(R.string.quick_chat),
contentDescription = stringResource(Res.string.quick_chat),
)
},
)
@@ -686,7 +689,7 @@ private fun QuickChatRow(
actions: List<QuickChatAction>,
onClick: (QuickChatAction) -> Unit,
) {
val alertActionMessage = stringResource(R.string.alert_bell_text)
val alertActionMessage = stringResource(Res.string.alert_bell_text)
val alertAction =
remember(alertActionMessage) {
// Memoize if content is static
@@ -741,11 +744,11 @@ private fun MessageInput(
modifier = modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 4.dp),
state = textFieldState,
lineLimits = TextFieldLineLimits.MultiLine(1, MAX_LINES),
label = { Text(stringResource(R.string.message_input_label)) },
label = { Text(stringResource(Res.string.message_input_label)) },
enabled = isEnabled,
shape = RoundedCornerShape(ROUNDED_CORNER_PERCENT.toFloat()),
isError = isOverLimit,
placeholder = { Text(stringResource(R.string.type_a_message)) },
placeholder = { Text(stringResource(Res.string.type_a_message)) },
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences),
supportingText = {
if (isEnabled) { // Only show supporting text if input is enabled
@@ -769,7 +772,10 @@ private fun MessageInput(
// cursor position and multi-byte characters, likely outside simple inputTransformation.
trailingIcon = {
IconButton(onClick = { if (canSend) onSendMessage() }, enabled = canSend) {
Icon(imageVector = Icons.AutoMirrored.Default.Send, contentDescription = stringResource(R.string.send))
Icon(
imageVector = Icons.AutoMirrored.Default.Send,
contentDescription = stringResource(Res.string.send),
)
}
},
)

View File

@@ -57,9 +57,9 @@ 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.R
import org.meshtastic.feature.messaging.component.MessageItem
import org.meshtastic.feature.messaging.component.ReactionDialog
import org.meshtastic.core.strings.R as Res
@Composable
fun DeliveryInfo(
@@ -73,13 +73,13 @@ fun DeliveryInfo(
onDismissRequest = onDismiss,
dismissButton = {
FilledTonalButton(onClick = onDismiss, modifier = Modifier.padding(horizontal = 16.dp)) {
Text(text = stringResource(R.string.close))
Text(text = stringResource(Res.string.close))
}
},
confirmButton = {
if (resendOption) {
FilledTonalButton(onClick = onConfirm, modifier = Modifier.padding(horizontal = 16.dp)) {
Text(text = stringResource(R.string.resend))
Text(text = stringResource(Res.string.resend))
}
}
},
@@ -102,7 +102,7 @@ fun DeliveryInfo(
}
relayNodeName?.let {
Text(
text = stringResource(R.string.relayed_by, it),
text = stringResource(Res.string.relayed_by, it),
modifier = Modifier.padding(top = 8.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium,

View File

@@ -72,12 +72,12 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.database.entity.QuickChatAction
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.dragContainer
import org.meshtastic.core.ui.component.dragDropItemsIndexed
import org.meshtastic.core.ui.component.rememberDragDropState
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun QuickChatScreen(
@@ -98,7 +98,7 @@ fun QuickChatScreen(
Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.quick_chat),
title = stringResource(Res.string.quick_chat),
ourNode = null,
showNodeChip = false,
canNavigateUp = true,
@@ -137,7 +137,7 @@ fun QuickChatScreen(
onClick = { showActionDialog = QuickChatAction(position = actions.size) },
modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp),
) {
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add))
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(Res.string.add))
}
}
}
@@ -166,7 +166,7 @@ private fun EditQuickChatDialog(
var actionInput by remember { mutableStateOf(action) }
val newQuickChat = remember { action.uuid == 0L }
val isInstant = actionInput.mode == QuickChatAction.Mode.Instant
val title = if (newQuickChat) R.string.quick_chat_new else R.string.quick_chat_edit
val title = if (newQuickChat) Res.string.quick_chat_new else Res.string.quick_chat_edit
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
@@ -192,7 +192,7 @@ private fun EditQuickChatDialog(
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextFieldWithCounter(
label = stringResource(R.string.name),
label = stringResource(Res.string.name),
value = actionInput.name,
maxSize = 5,
singleLine = true,
@@ -204,7 +204,7 @@ private fun EditQuickChatDialog(
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextFieldWithCounter(
label = stringResource(R.string.message),
label = stringResource(Res.string.message),
value = actionInput.message,
maxSize = 200,
getSize = { it.toByteArray().size + 1 },
@@ -220,9 +220,9 @@ private fun EditQuickChatDialog(
val (text, icon) =
if (isInstant) {
R.string.quick_chat_instant to Icons.Default.FastForward
Res.string.quick_chat_instant to Icons.Default.FastForward
} else {
R.string.quick_chat_append to Icons.Default.Add
Res.string.quick_chat_append to Icons.Default.Add
}
Row(verticalAlignment = Alignment.CenterVertically) {
@@ -255,7 +255,7 @@ private fun EditQuickChatDialog(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
TextButton(modifier = Modifier.weight(1f), onClick = onDismiss) {
Text(stringResource(R.string.cancel))
Text(stringResource(Res.string.cancel))
}
if (!newQuickChat) {
@@ -266,7 +266,7 @@ private fun EditQuickChatDialog(
onDismiss()
},
) {
Text(text = stringResource(R.string.delete))
Text(text = stringResource(Res.string.delete))
}
}
@@ -278,7 +278,7 @@ private fun EditQuickChatDialog(
},
enabled = actionInput.name.isNotEmpty() && actionInput.message.isNotEmpty(),
) {
Text(text = stringResource(R.string.save))
Text(text = stringResource(Res.string.save))
}
}
},
@@ -328,7 +328,7 @@ private fun QuickChatItem(
if (action.mode == QuickChatAction.Mode.Instant) {
Icon(
imageVector = Icons.Default.FastForward,
contentDescription = stringResource(R.string.quick_chat_instant),
contentDescription = stringResource(Res.string.quick_chat_instant),
)
}
},
@@ -339,12 +339,12 @@ private fun QuickChatItem(
IconButton(onClick = { onEdit(action) }, modifier = Modifier.size(48.dp)) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = stringResource(R.string.quick_chat_edit),
contentDescription = stringResource(Res.string.quick_chat_edit),
)
}
Icon(
imageVector = Icons.Default.DragHandle,
contentDescription = stringResource(R.string.quick_chat),
contentDescription = stringResource(Res.string.quick_chat),
)
}
},

View File

@@ -40,8 +40,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import org.meshtastic.core.model.MessageStatus
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.emoji.EmojiPickerDialog
import org.meshtastic.core.strings.R as Res
@Composable
internal fun ReactionButton(onSendReaction: (String) -> Unit = {}) {
@@ -56,7 +56,7 @@ internal fun ReactionButton(onSendReaction: (String) -> Unit = {}) {
)
}
IconButton(onClick = { showEmojiPickerDialog = true }) {
Icon(imageVector = Icons.Default.EmojiEmotions, contentDescription = stringResource(R.string.react))
Icon(imageVector = Icons.Default.EmojiEmotions, contentDescription = stringResource(Res.string.react))
}
}
@@ -64,7 +64,7 @@ internal fun ReactionButton(onSendReaction: (String) -> Unit = {}) {
private fun ReplyButton(onClick: () -> Unit = {}) = IconButton(
onClick = onClick,
content = {
Icon(imageVector = Icons.AutoMirrored.Filled.Reply, contentDescription = stringResource(R.string.reply))
Icon(imageVector = Icons.AutoMirrored.Filled.Reply, contentDescription = stringResource(Res.string.reply))
},
)
@@ -82,7 +82,7 @@ private fun MessageStatusButton(onStatusClick: () -> Unit = {}, status: MessageS
MessageStatus.ERROR -> Icons.TwoTone.CloudOff
else -> Icons.TwoTone.Warning
},
contentDescription = stringResource(R.string.message_delivery_status),
contentDescription = stringResource(Res.string.message_delivery_status),
)
}
}

View File

@@ -53,7 +53,6 @@ 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.R
import org.meshtastic.core.ui.component.AutoLinkText
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.core.ui.component.Rssi
@@ -61,6 +60,7 @@ import org.meshtastic.core.ui.component.Snr
import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.ui.theme.MessageItemColors
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
@@ -144,7 +144,7 @@ internal fun MessageItem(
if (message.viaMqtt) {
Icon(
Icons.Default.Cloud,
contentDescription = stringResource(R.string.via_mqtt),
contentDescription = stringResource(Res.string.via_mqtt),
modifier = Modifier.size(16.dp),
)
}
@@ -179,7 +179,7 @@ internal fun MessageItem(
}
} else {
Text(
text = stringResource(R.string.hops_away_template, message.hopsAway),
text = stringResource(Res.string.hops_away_template, message.hopsAway),
style = MaterialTheme.typography.labelSmall,
)
}
@@ -228,7 +228,7 @@ private fun OriginalMessageSnippet(
) {
Icon(
Icons.Default.FormatQuote,
contentDescription = stringResource(R.string.reply), // Add to strings.xml
contentDescription = stringResource(Res.string.reply), // Add to strings.xml
)
Text(
text = originalMessageNode.user.shortName,
@@ -254,7 +254,7 @@ private fun OriginalMessageSnippet(
private fun MessageItemPreview() {
val sent =
Message(
text = stringResource(R.string.sample_message),
text = stringResource(Res.string.sample_message),
time = "10:00",
fromLocal = true,
status = MessageStatus.DELIVERED,

View File

@@ -33,7 +33,6 @@ import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DeviceVersion
import org.meshtastic.core.navigation.SettingsRoutes
import org.meshtastic.core.service.ServiceAction
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.InsetDivider
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.core.ui.component.TitledCard
@@ -44,6 +43,7 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusYellow
import org.meshtastic.feature.node.model.MetricsState
import org.meshtastic.feature.node.model.NodeDetailAction
import org.meshtastic.proto.MeshProtos
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@Composable
@@ -54,9 +54,9 @@ fun AdministrationSection(
onFirmwareSelect: (FirmwareRelease) -> Unit,
modifier: Modifier = Modifier,
) {
TitledCard(stringResource(R.string.administration), modifier = modifier) {
TitledCard(stringResource(Res.string.administration), modifier = modifier) {
ListItem(
text = stringResource(R.string.request_metadata),
text = stringResource(Res.string.request_metadata),
leadingIcon = Icons.Default.Memory,
trailingIcon = null,
onClick = { onAction(NodeDetailAction.TriggerServiceAction(ServiceAction.GetDeviceMetadata(node.num))) },
@@ -65,7 +65,7 @@ fun AdministrationSection(
InsetDivider()
ListItem(
text = stringResource(R.string.remote_admin),
text = stringResource(Res.string.remote_admin),
leadingIcon = Icons.Default.Settings,
enabled = metricsState.isLocal || node.metadata != null,
) {
@@ -75,7 +75,7 @@ fun AdministrationSection(
val firmwareVersion = node.metadata?.firmwareVersion
val firmwareEdition = metricsState.firmwareEdition
if (firmwareVersion != null || (firmwareEdition != null && metricsState.isLocal)) {
TitledCard(stringResource(R.string.firmware)) {
TitledCard(stringResource(Res.string.firmware)) {
firmwareEdition?.let {
val icon =
when (it) {
@@ -84,7 +84,7 @@ fun AdministrationSection(
}
ListItem(
text = stringResource(R.string.firmware_edition),
text = stringResource(Res.string.firmware_edition),
leadingIcon = icon,
supportingText = it.name,
copyable = true,
@@ -101,7 +101,7 @@ fun AdministrationSection(
InsetDivider()
ListItem(
text = stringResource(R.string.installed_firmware_version),
text = stringResource(Res.string.installed_firmware_version),
leadingIcon = Icons.Default.Memory,
supportingText = firmwareVersion.substringBeforeLast("."),
copyable = true,
@@ -112,7 +112,7 @@ fun AdministrationSection(
InsetDivider()
ListItem(
text = stringResource(R.string.latest_stable_firmware),
text = stringResource(Res.string.latest_stable_firmware),
leadingIcon = Icons.Default.Memory,
supportingText = latestStable.id.substringBeforeLast(".").replace("v", ""),
copyable = true,
@@ -124,7 +124,7 @@ fun AdministrationSection(
InsetDivider()
ListItem(
text = stringResource(R.string.latest_alpha_firmware),
text = stringResource(Res.string.latest_alpha_firmware),
leadingIcon = Icons.Default.Memory,
supportingText = latestAlpha.id.substringBeforeLast(".").replace("v", ""),
copyable = true,

View File

@@ -34,12 +34,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.InsetDivider
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.core.ui.component.SwitchListItem
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.feature.node.model.NodeDetailAction
import org.meshtastic.core.strings.R as Res
@Composable
fun DeviceActions(
@@ -67,9 +67,9 @@ fun DeviceActions(
onConfirmIgnore = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.Ignore(it))) },
onConfirmRemove = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.Remove(it))) },
)
TitledCard(title = stringResource(R.string.actions), modifier = modifier) {
TitledCard(title = stringResource(Res.string.actions), modifier = modifier) {
ListItem(
text = stringResource(R.string.share_contact),
text = stringResource(Res.string.share_contact),
leadingIcon = Icons.Rounded.QrCode2,
trailingIcon = null,
onClick = { onAction(NodeDetailAction.ShareContact) },
@@ -82,7 +82,7 @@ fun DeviceActions(
InsetDivider()
SwitchListItem(
text = stringResource(R.string.favorite),
text = stringResource(Res.string.favorite),
leadingIcon = if (node.isFavorite) Icons.Default.Star else Icons.Default.StarBorder,
leadingIconTint = if (node.isFavorite) Color.Yellow else LocalContentColor.current,
checked = node.isFavorite,
@@ -92,7 +92,7 @@ fun DeviceActions(
InsetDivider()
SwitchListItem(
text = stringResource(R.string.ignore),
text = stringResource(Res.string.ignore),
leadingIcon =
if (node.isIgnored) Icons.AutoMirrored.Outlined.VolumeMute else Icons.AutoMirrored.Default.VolumeUp,
checked = node.isIgnored,
@@ -102,7 +102,7 @@ fun DeviceActions(
InsetDivider()
ListItem(
text = stringResource(R.string.remove),
text = stringResource(Res.string.remove),
leadingIcon = Icons.Rounded.Delete,
trailingIcon = null,
onClick = { displayRemoveDialog = true },

View File

@@ -44,13 +44,13 @@ import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import org.meshtastic.core.model.DeviceHardware
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.InsetDivider
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.feature.node.model.MetricsState
import org.meshtastic.core.strings.R as Res
@Composable
fun DeviceDetailsSection(state: MetricsState, modifier: Modifier = Modifier) {
@@ -58,7 +58,7 @@ fun DeviceDetailsSection(state: MetricsState, modifier: Modifier = Modifier) {
val deviceHardware = state.deviceHardware ?: return
val hwModelName = deviceHardware.displayName
val isSupported = deviceHardware.activelySupported
TitledCard(stringResource(R.string.device), modifier = modifier) {
TitledCard(stringResource(Res.string.device), modifier = modifier) {
Spacer(modifier = Modifier.height(16.dp))
Box(
@@ -77,7 +77,7 @@ fun DeviceDetailsSection(state: MetricsState, modifier: Modifier = Modifier) {
InsetDivider()
ListItem(
text = stringResource(R.string.hardware),
text = stringResource(Res.string.hardware),
leadingIcon = Icons.Default.Router,
supportingText = hwModelName,
copyable = true,
@@ -89,9 +89,9 @@ fun DeviceDetailsSection(state: MetricsState, modifier: Modifier = Modifier) {
ListItem(
text =
if (isSupported) {
stringResource(R.string.supported)
stringResource(Res.string.supported)
} else {
stringResource(R.string.supported_by_community)
stringResource(Res.string.supported_by_community)
},
leadingIcon =
if (isSupported) {

View File

@@ -25,8 +25,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun DistanceInfo(
@@ -37,7 +37,7 @@ fun DistanceInfo(
IconInfo(
modifier = modifier,
icon = Icons.Rounded.SocialDistance,
contentDescription = stringResource(R.string.distance),
contentDescription = stringResource(Res.string.distance),
text = distance,
contentColor = contentColor,
)

View File

@@ -25,23 +25,23 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import org.meshtastic.core.model.util.metersIn
import org.meshtastic.core.model.util.toString
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.icon.Elevation
import org.meshtastic.core.ui.icon.MeshtasticIcons
import org.meshtastic.proto.ConfigProtos.Config.DisplayConfig.DisplayUnits
import org.meshtastic.core.strings.R as Res
@Composable
fun ElevationInfo(
modifier: Modifier = Modifier,
altitude: Int,
system: DisplayUnits,
suffix: String = stringResource(R.string.elevation_suffix),
suffix: String = stringResource(Res.string.elevation_suffix),
contentColor: Color = MaterialTheme.colorScheme.onSurface,
) {
IconInfo(
modifier = modifier,
icon = MeshtasticIcons.Elevation,
contentDescription = stringResource(R.string.altitude),
contentDescription = stringResource(Res.string.altitude),
text = altitude.metersIn(system).toString(system) + " " + suffix,
contentColor = contentColor,
)

View File

@@ -41,10 +41,10 @@ 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.R
import org.meshtastic.feature.node.model.DrawableMetricInfo
import org.meshtastic.feature.node.model.VectorMetricInfo
import org.meshtastic.proto.ConfigProtos
import org.meshtastic.core.strings.R as Res
@Suppress("CyclomaticComplexMethod", "LongMethod")
@Composable
@@ -60,7 +60,7 @@ internal fun EnvironmentMetrics(
if (hasTemperature()) {
add(
VectorMetricInfo(
R.string.temperature,
Res.string.temperature,
temperature.toTempString(isFahrenheit),
Icons.Default.Thermostat,
),
@@ -69,7 +69,7 @@ internal fun EnvironmentMetrics(
if (hasRelativeHumidity()) {
add(
VectorMetricInfo(
R.string.humidity,
Res.string.humidity,
"%.0f%%".format(relativeHumidity),
Icons.Default.WaterDrop,
),
@@ -78,7 +78,7 @@ internal fun EnvironmentMetrics(
if (hasBarometricPressure()) {
add(
VectorMetricInfo(
R.string.pressure,
Res.string.pressure,
"%.0f hPa".format(barometricPressure),
Icons.Default.Speed,
),
@@ -87,38 +87,38 @@ internal fun EnvironmentMetrics(
if (hasGasResistance()) {
add(
VectorMetricInfo(
R.string.gas_resistance,
Res.string.gas_resistance,
"%.0f MΩ".format(gasResistance),
Icons.Default.BlurOn,
),
)
}
if (hasVoltage()) {
add(VectorMetricInfo(R.string.voltage, "%.2fV".format(voltage), Icons.Default.Bolt))
add(VectorMetricInfo(Res.string.voltage, "%.2fV".format(voltage), Icons.Default.Bolt))
}
if (hasCurrent()) {
add(VectorMetricInfo(R.string.current, "%.1fmA".format(current), Icons.Default.Power))
add(VectorMetricInfo(Res.string.current, "%.1fmA".format(current), Icons.Default.Power))
}
if (hasIaq()) add(VectorMetricInfo(R.string.iaq, iaq.toString(), Icons.Default.Air))
if (hasIaq()) add(VectorMetricInfo(Res.string.iaq, iaq.toString(), Icons.Default.Air))
if (hasDistance()) {
add(
VectorMetricInfo(
R.string.distance,
Res.string.distance,
distance.toSmallDistanceString(displayUnits),
Icons.Default.Height,
),
)
}
if (hasLux()) add(VectorMetricInfo(R.string.lux, "%.0f lx".format(lux), Icons.Default.LightMode))
if (hasLux()) add(VectorMetricInfo(Res.string.lux, "%.0f lx".format(lux), Icons.Default.LightMode))
if (hasUvLux()) {
add(VectorMetricInfo(R.string.uv_lux, "%.0f lx".format(uvLux), Icons.Default.LightMode))
add(VectorMetricInfo(Res.string.uv_lux, "%.0f lx".format(uvLux), Icons.Default.LightMode))
}
if (hasWindSpeed()) {
@Suppress("MagicNumber")
val normalizedBearing = (windDirection + 180) % 360
add(
VectorMetricInfo(
R.string.wind,
Res.string.wind,
windSpeed.toSpeedString(displayUnits),
Icons.Outlined.Navigation,
normalizedBearing.toFloat(),
@@ -126,7 +126,7 @@ internal fun EnvironmentMetrics(
)
}
if (hasWeight()) {
add(VectorMetricInfo(R.string.weight, "%.2f kg".format(weight), Icons.Default.Scale))
add(VectorMetricInfo(Res.string.weight, "%.2f kg".format(weight), Icons.Default.Scale))
}
}
}
@@ -139,7 +139,7 @@ internal fun EnvironmentMetrics(
val dewPoint = UnitConversions.calculateDewPoint(temperature, relativeHumidity)
add(
DrawableMetricInfo(
R.string.dew_point,
Res.string.dew_point,
dewPoint.toTempString(isFahrenheit),
org.meshtastic.feature.node.R.drawable.ic_outlined_dew_point_24,
),
@@ -148,7 +148,7 @@ internal fun EnvironmentMetrics(
if (hasSoilTemperature()) {
add(
DrawableMetricInfo(
R.string.soil_temperature,
Res.string.soil_temperature,
soilTemperature.toTempString(isFahrenheit),
org.meshtastic.feature.node.R.drawable.soil_temperature,
),
@@ -157,7 +157,7 @@ internal fun EnvironmentMetrics(
if (hasSoilMoisture()) {
add(
DrawableMetricInfo(
R.string.soil_moisture,
Res.string.soil_moisture,
"%d%%".format(soilMoisture),
org.meshtastic.feature.node.R.drawable.soil_moisture,
),
@@ -166,7 +166,7 @@ internal fun EnvironmentMetrics(
if (hasRadiation()) {
add(
DrawableMetricInfo(
R.string.radiation,
Res.string.radiation,
"%.1f µR/h".format(radiation),
org.meshtastic.feature.node.R.drawable.ic_filled_radioactive_24,
),

View File

@@ -44,8 +44,8 @@ import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import com.mikepenz.markdown.m3.Markdown
import org.meshtastic.core.database.entity.FirmwareRelease
import org.meshtastic.core.strings.R
import timber.log.Timber
import org.meshtastic.core.strings.R as Res
@Composable
fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modifier = Modifier) {
@@ -64,15 +64,15 @@ fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modi
val intent = Intent(Intent.ACTION_VIEW, firmwareRelease.pageUrl.toUri())
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, R.string.error_no_app_to_handle_link, Toast.LENGTH_LONG).show()
Toast.makeText(context, Res.string.error_no_app_to_handle_link, Toast.LENGTH_LONG).show()
Timber.e(e)
}
},
modifier = Modifier.weight(1f),
) {
Icon(imageVector = Icons.Default.Link, contentDescription = stringResource(R.string.view_release))
Icon(imageVector = Icons.Default.Link, contentDescription = stringResource(Res.string.view_release))
Spacer(modifier = Modifier.width(8.dp))
Text(text = stringResource(R.string.view_release))
Text(text = stringResource(Res.string.view_release))
}
Button(
onClick = {
@@ -80,15 +80,15 @@ fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modi
val intent = Intent(Intent.ACTION_VIEW, firmwareRelease.zipUrl.toUri())
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, R.string.error_no_app_to_handle_link, Toast.LENGTH_LONG).show()
Toast.makeText(context, Res.string.error_no_app_to_handle_link, Toast.LENGTH_LONG).show()
Timber.e(e)
}
},
modifier = Modifier.weight(1f),
) {
Icon(imageVector = Icons.Default.Download, contentDescription = stringResource(R.string.download))
Icon(imageVector = Icons.Default.Download, contentDescription = stringResource(Res.string.download))
Spacer(modifier = Modifier.width(8.dp))
Text(text = stringResource(R.string.download))
Text(text = stringResource(Res.string.download))
}
}
}

View File

@@ -28,6 +28,7 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import org.meshtastic.core.model.util.formatAgo
import org.meshtastic.core.ui.R
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun LastHeardInfo(
@@ -39,7 +40,7 @@ fun LastHeardInfo(
IconInfo(
modifier = modifier,
icon = ImageVector.vectorResource(id = R.drawable.ic_antenna_24),
contentDescription = stringResource(org.meshtastic.core.strings.R.string.node_sort_last_heard),
contentDescription = stringResource(Res.string.node_sort_last_heard),
text = formatAgo(lastHeard, currentTimeMillis),
contentColor = contentColor,
)

View File

@@ -38,12 +38,12 @@ import kotlinx.coroutines.launch
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.util.GPSFormat
import org.meshtastic.core.model.util.formatAgo
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.BasicListItem
import org.meshtastic.core.ui.component.icon
import org.meshtastic.core.ui.theme.AppTheme
import timber.log.Timber
import java.net.URLEncoder
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalFoundationApi::class)
@Composable
@@ -56,7 +56,7 @@ fun LinkedCoordinatesItem(node: Node) {
val coordinates = GPSFormat.toDec(node.latitude, node.longitude)
BasicListItem(
text = stringResource(R.string.last_position_update),
text = stringResource(Res.string.last_position_update),
leadingIcon = Icons.Default.LocationOn,
supportingText = "$ago$coordinates",
trailingContent = Icons.AutoMirrored.Rounded.KeyboardArrowRight.icon(),

View File

@@ -24,12 +24,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.feature.node.model.LogsType
import org.meshtastic.feature.node.model.MetricsState
import org.meshtastic.feature.node.model.NodeDetailAction
import org.meshtastic.core.strings.R as Res
@Composable
@Suppress("MultipleEmitters")
@@ -41,13 +41,13 @@ fun MetricsSection(
modifier: Modifier = Modifier,
) {
if (node.hasEnvironmentMetrics) {
TitledCard(stringResource(R.string.environment), modifier = modifier) {}
TitledCard(stringResource(Res.string.environment), modifier = modifier) {}
EnvironmentMetrics(node, isFahrenheit = metricsState.isFahrenheit, displayUnits = metricsState.displayUnits)
Spacer(modifier = Modifier.height(8.dp))
}
if (node.hasPowerMetrics) {
TitledCard(stringResource(R.string.power), modifier = modifier) {}
TitledCard(stringResource(Res.string.power), modifier = modifier) {}
PowerMetrics(node)
Spacer(modifier = Modifier.height(8.dp))
}
@@ -55,7 +55,7 @@ fun MetricsSection(
val nonPositionLogs = availableLogs.filter { it != LogsType.NODE_MAP && it != LogsType.POSITIONS }
if (nonPositionLogs.isNotEmpty()) {
TitledCard(title = stringResource(R.string.logs), modifier = modifier) {
TitledCard(title = stringResource(Res.string.logs), modifier = modifier) {
nonPositionLogs.forEach { type ->
ListItem(text = stringResource(type.titleRes), leadingIcon = type.icon) {
onAction(NodeDetailAction.Navigate(type.route))

View File

@@ -44,32 +44,32 @@ import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.util.formatAgo
import org.meshtastic.core.model.util.formatUptime
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.InsetDivider
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.feature.node.model.isEffectivelyUnmessageable
import org.meshtastic.core.strings.R as Res
@Composable
fun NodeDetailsSection(node: Node, modifier: Modifier = Modifier) {
TitledCard(title = stringResource(R.string.details), modifier = modifier) {
TitledCard(title = stringResource(Res.string.details), modifier = modifier) {
if (node.mismatchKey) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Default.KeyOff,
contentDescription = stringResource(R.string.encryption_error),
contentDescription = stringResource(Res.string.encryption_error),
tint = Color.Red,
)
Spacer(Modifier.width(12.dp))
Text(
text = stringResource(R.string.encryption_error),
text = stringResource(Res.string.encryption_error),
style = MaterialTheme.typography.titleLarge.copy(color = Color.Red),
textAlign = TextAlign.Center,
)
}
Spacer(Modifier.height(16.dp))
Text(
text = stringResource(R.string.encryption_error_text),
text = stringResource(Res.string.encryption_error_text),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
)
@@ -82,7 +82,7 @@ fun NodeDetailsSection(node: Node, modifier: Modifier = Modifier) {
@Composable
private fun MainNodeDetails(node: Node) {
ListItem(
text = stringResource(R.string.long_name),
text = stringResource(Res.string.long_name),
leadingIcon = Icons.TwoTone.Person,
supportingText = node.user.longName.ifEmpty { "???" },
copyable = true,
@@ -92,7 +92,7 @@ private fun MainNodeDetails(node: Node) {
InsetDivider()
ListItem(
text = stringResource(R.string.short_name),
text = stringResource(Res.string.short_name),
leadingIcon = Icons.Outlined.Person,
supportingText = node.user.shortName.ifEmpty { "???" },
copyable = true,
@@ -102,7 +102,7 @@ private fun MainNodeDetails(node: Node) {
InsetDivider()
ListItem(
text = stringResource(R.string.node_number),
text = stringResource(Res.string.node_number),
leadingIcon = Icons.Default.Numbers,
supportingText = node.num.toUInt().toString(),
copyable = true,
@@ -112,7 +112,7 @@ private fun MainNodeDetails(node: Node) {
InsetDivider()
ListItem(
text = stringResource(R.string.user_id),
text = stringResource(Res.string.user_id),
leadingIcon = Icons.Default.Person,
supportingText = node.user.id,
copyable = true,
@@ -122,7 +122,7 @@ private fun MainNodeDetails(node: Node) {
InsetDivider()
ListItem(
text = stringResource(R.string.role),
text = stringResource(Res.string.role),
leadingIcon = Icons.Default.Work,
supportingText = node.user.role.name,
trailingIcon = null,
@@ -132,7 +132,7 @@ private fun MainNodeDetails(node: Node) {
InsetDivider()
ListItem(
text = stringResource(R.string.unmonitored_or_infrastructure),
text = stringResource(Res.string.unmonitored_or_infrastructure),
leadingIcon = Icons.Outlined.NoCell,
trailingIcon = null,
)
@@ -141,7 +141,7 @@ private fun MainNodeDetails(node: Node) {
InsetDivider()
ListItem(
text = stringResource(R.string.uptime),
text = stringResource(Res.string.uptime),
leadingIcon = Icons.Default.CheckCircle,
supportingText = formatUptime(node.deviceMetrics.uptimeSeconds),
trailingIcon = null,
@@ -151,7 +151,7 @@ private fun MainNodeDetails(node: Node) {
InsetDivider()
ListItem(
text = stringResource(R.string.node_sort_last_heard),
text = stringResource(Res.string.node_sort_last_heard),
leadingIcon = Icons.Default.History,
supportingText = formatAgo(node.lastHeard),
trailingIcon = null,

View File

@@ -62,8 +62,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.NodeSortOption
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Suppress("LongParameterList")
@Composable
@@ -114,7 +114,7 @@ fun NodeFilterTextField(
.padding(vertical = 16.dp, horizontal = 24.dp),
) {
Text(
text = stringResource(R.string.node_filter_ignored),
text = stringResource(Res.string.node_filter_ignored),
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.fillMaxWidth(),
@@ -135,20 +135,20 @@ private fun NodeFilterTextField(filterText: String, onTextChange: (String) -> Un
value = filterText,
placeholder = {
Text(
text = stringResource(R.string.node_filter_placeholder),
text = stringResource(Res.string.node_filter_placeholder),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.35F),
)
},
leadingIcon = {
Icon(Icons.Default.Search, contentDescription = stringResource(R.string.node_filter_placeholder))
Icon(Icons.Default.Search, contentDescription = stringResource(Res.string.node_filter_placeholder))
},
onValueChange = onTextChange,
trailingIcon = {
if (filterText.isNotEmpty() || isFocused) {
Icon(
Icons.Default.Clear,
contentDescription = stringResource(R.string.desc_node_filter_clear),
contentDescription = stringResource(Res.string.desc_node_filter_clear),
modifier =
Modifier.clickable {
onTextChange("")
@@ -177,7 +177,7 @@ private fun NodeSortButton(
IconButton(onClick = { expanded = true }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.Sort,
contentDescription = stringResource(R.string.node_sort_button),
contentDescription = stringResource(Res.string.node_sort_button),
modifier = Modifier.heightIn(max = 48.dp),
tint = MaterialTheme.colorScheme.onSurface,
)
@@ -188,7 +188,7 @@ private fun NodeSortButton(
onDismissRequest = { expanded = false },
modifier = Modifier.background(MaterialTheme.colorScheme.background.copy(alpha = 1f)),
) {
DropdownMenuTitle(text = stringResource(R.string.node_sort_title))
DropdownMenuTitle(text = stringResource(Res.string.node_sort_title))
NodeSortOption.entries.forEach { sort ->
DropdownMenuRadio(
@@ -200,28 +200,28 @@ private fun NodeSortButton(
HorizontalDivider(modifier = Modifier.padding(MenuDefaults.DropdownMenuItemContentPadding))
DropdownMenuTitle(text = stringResource(R.string.node_filter_title))
DropdownMenuTitle(text = stringResource(Res.string.node_filter_title))
DropdownMenuCheck(
text = stringResource(R.string.node_filter_include_unknown),
text = stringResource(Res.string.node_filter_include_unknown),
checked = toggles.includeUnknown,
onClick = toggles.onToggleIncludeUnknown,
)
DropdownMenuCheck(
text = stringResource(R.string.node_filter_only_online),
text = stringResource(Res.string.node_filter_only_online),
checked = toggles.onlyOnline,
onClick = toggles.onToggleOnlyOnline,
)
DropdownMenuCheck(
text = stringResource(R.string.node_filter_only_direct),
text = stringResource(Res.string.node_filter_only_direct),
checked = toggles.onlyDirect,
onClick = toggles.onToggleOnlyDirect,
)
DropdownMenuCheck(
text = stringResource(R.string.node_filter_show_ignored),
text = stringResource(Res.string.node_filter_show_ignored),
checked = toggles.showIgnored,
onClick = toggles.onToggleShowIgnored,
trailing =

View File

@@ -49,7 +49,6 @@ import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.database.model.isUnmessageableRole
import org.meshtastic.core.model.util.toDistanceString
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MaterialBatteryInfo
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.core.ui.component.NodeKeyStatusIcon
@@ -57,6 +56,7 @@ import org.meshtastic.core.ui.component.SignalInfo
import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.proto.ConfigProtos.Config.DisplayConfig
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Suppress("LongMethod", "CyclomaticComplexMethod")
@@ -74,7 +74,7 @@ fun NodeItem(
) {
val isFavorite = remember(thatNode) { thatNode.isFavorite }
val isIgnored = thatNode.isIgnored
val longName = thatNode.user.longName.ifEmpty { stringResource(R.string.unknown_username) }
val longName = thatNode.user.longName.ifEmpty { stringResource(Res.string.unknown_username) }
val isThisNode = remember(thatNode) { thisNode?.num == thatNode.num }
val system = remember(distanceUnits) { DisplayConfig.DisplayUnits.forNumber(distanceUnits) }
val distance =
@@ -165,7 +165,7 @@ fun NodeItem(
ElevationInfo(
altitude = position.altitude,
system = system,
suffix = stringResource(R.string.elevation_suffix),
suffix = stringResource(Res.string.elevation_suffix),
contentColor = contentColor,
)
val satCount = position.satsInView

View File

@@ -20,8 +20,8 @@ package org.meshtastic.feature.node.component
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.SimpleAlertDialog
import org.meshtastic.core.strings.R as Res
@Composable
fun NodeActionDialogs(
@@ -36,10 +36,10 @@ fun NodeActionDialogs(
) {
if (displayFavoriteDialog) {
SimpleAlertDialog(
title = R.string.favorite,
title = Res.string.favorite,
text =
stringResource(
id = if (node.isFavorite) R.string.favorite_remove else R.string.favorite_add,
id = if (node.isFavorite) Res.string.favorite_remove else Res.string.favorite_add,
node.user.longName,
),
onConfirm = {
@@ -51,10 +51,10 @@ fun NodeActionDialogs(
}
if (displayIgnoreDialog) {
SimpleAlertDialog(
title = R.string.ignore,
title = Res.string.ignore,
text =
stringResource(
id = if (node.isIgnored) R.string.ignore_remove else R.string.ignore_add,
id = if (node.isIgnored) Res.string.ignore_remove else Res.string.ignore_add,
node.user.longName,
),
onConfirm = {
@@ -66,8 +66,8 @@ fun NodeActionDialogs(
}
if (displayRemoveDialog) {
SimpleAlertDialog(
title = R.string.remove,
text = R.string.remove_node_text,
title = Res.string.remove,
text = Res.string.remove_node_text,
onConfirm = {
onDismissMenuRequest()
onConfirmRemove(node)

View File

@@ -40,10 +40,10 @@ 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.meshtastic.core.strings.R
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
@Suppress("LongMethod")
@OptIn(ExperimentalMaterial3Api::class)
@@ -58,9 +58,9 @@ fun NodeStatusIcons(isThisNode: Boolean, isUnmessageable: Boolean, isFavorite: B
Text(
stringResource(
if (isConnected) {
R.string.connected
Res.string.connected
} else {
R.string.disconnected
Res.string.disconnected
},
),
)
@@ -72,14 +72,14 @@ fun NodeStatusIcons(isThisNode: Boolean, isUnmessageable: Boolean, isFavorite: B
@Suppress("MagicNumber")
Icon(
imageVector = Icons.TwoTone.CloudDone,
contentDescription = stringResource(R.string.connected),
contentDescription = stringResource(Res.string.connected),
modifier = Modifier.size(24.dp), // Smaller size for badge
tint = MaterialTheme.colorScheme.StatusGreen,
)
} else {
Icon(
imageVector = Icons.TwoTone.CloudOff,
contentDescription = stringResource(R.string.not_connected),
contentDescription = stringResource(Res.string.not_connected),
modifier = Modifier.size(24.dp), // Smaller size for badge
tint = MaterialTheme.colorScheme.StatusRed,
)
@@ -90,13 +90,13 @@ fun NodeStatusIcons(isThisNode: Boolean, isUnmessageable: Boolean, isFavorite: B
if (isUnmessageable) {
TooltipBox(
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = { PlainTooltip { Text(stringResource(R.string.unmonitored_or_infrastructure)) } },
tooltip = { PlainTooltip { Text(stringResource(Res.string.unmonitored_or_infrastructure)) } },
state = rememberTooltipState(),
) {
IconButton(onClick = {}, modifier = Modifier.size(24.dp)) {
Icon(
imageVector = Icons.Rounded.NoCell,
contentDescription = stringResource(R.string.unmessageable),
contentDescription = stringResource(Res.string.unmessageable),
modifier = Modifier.size(24.dp), // Smaller size for badge
)
}
@@ -105,13 +105,13 @@ fun NodeStatusIcons(isThisNode: Boolean, isUnmessageable: Boolean, isFavorite: B
if (isFavorite && !isThisNode) {
TooltipBox(
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
tooltip = { PlainTooltip { Text(stringResource(R.string.favorite)) } },
tooltip = { PlainTooltip { Text(stringResource(Res.string.favorite)) } },
state = rememberTooltipState(),
) {
IconButton(onClick = {}, modifier = Modifier.size(24.dp)) {
Icon(
imageVector = Icons.Rounded.Star,
contentDescription = stringResource(R.string.favorite),
contentDescription = stringResource(Res.string.favorite),
modifier = Modifier.size(24.dp), // Smaller size for badge
tint = MaterialTheme.colorScheme.StatusYellow,
)

View File

@@ -38,13 +38,13 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.core.strings.R as Res
@Composable
fun NotesSection(node: Node, onSaveNotes: (Int, String) -> Unit, modifier: Modifier = Modifier) {
if (node.isFavorite) {
TitledCard(title = stringResource(R.string.notes), modifier = modifier) {
TitledCard(title = stringResource(Res.string.notes), modifier = modifier) {
val originalNotes = node.notes
var notes by remember(node.notes) { mutableStateOf(node.notes) }
val edited = notes.trim() != originalNotes.trim()
@@ -54,7 +54,7 @@ fun NotesSection(node: Node, onSaveNotes: (Int, String) -> Unit, modifier: Modif
value = notes,
onValueChange = { notes = it },
modifier = Modifier.fillMaxWidth().padding(8.dp),
placeholder = { Text(stringResource(R.string.add_a_note)) },
placeholder = { Text(stringResource(Res.string.add_a_note)) },
trailingIcon = {
IconButton(
onClick = {
@@ -63,7 +63,7 @@ fun NotesSection(node: Node, onSaveNotes: (Int, String) -> Unit, modifier: Modif
},
enabled = edited,
) {
Icon(imageVector = Icons.Default.Save, contentDescription = stringResource(R.string.save))
Icon(imageVector = Icons.Default.Save, contentDescription = stringResource(Res.string.save))
}
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),

View File

@@ -28,13 +28,13 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.util.toDistanceString
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.InsetDivider
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.feature.node.model.LogsType
import org.meshtastic.feature.node.model.MetricsState
import org.meshtastic.feature.node.model.NodeDetailAction
import org.meshtastic.core.strings.R as Res
/**
* Displays node position details, last update time, distance, and related actions like requesting position and
@@ -51,7 +51,7 @@ fun PositionSection(
) {
val distance = ourNode?.distance(node)?.takeIf { it > 0 }?.toDistanceString(metricsState.displayUnits)
val hasValidPosition = node.latitude != 0.0 || node.longitude != 0.0
TitledCard(title = stringResource(R.string.position), modifier = modifier) {
TitledCard(title = stringResource(Res.string.position), modifier = modifier) {
// Current position coordinates (linked)
if (hasValidPosition) {
InlineMap(node = node, Modifier.fillMaxWidth().height(200.dp))
@@ -64,7 +64,7 @@ fun PositionSection(
InsetDivider()
ListItem(
text = stringResource(R.string.node_sort_distance),
text = stringResource(Res.string.node_sort_distance),
leadingIcon = Icons.Default.SocialDistance,
supportingText = distance,
copyable = true,
@@ -76,7 +76,7 @@ fun PositionSection(
// Exchange position action
ListItem(
text = stringResource(R.string.exchange_position),
text = stringResource(Res.string.exchange_position),
leadingIcon = Icons.Default.LocationOn,
trailingIcon = null,
onClick = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.RequestPosition(node))) },

View File

@@ -29,8 +29,8 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
import org.meshtastic.feature.node.model.VectorMetricInfo
import org.meshtastic.core.strings.R as Res
/**
* Displays environmental metrics for a node, including temperature, humidity, pressure, and other sensor data.
@@ -46,16 +46,16 @@ internal fun PowerMetrics(node: Node) {
buildList {
with(node.powerMetrics) {
if (ch1Voltage != 0f) {
add(VectorMetricInfo(R.string.channel_1, "%.2fV".format(ch1Voltage), Icons.Default.Bolt))
add(VectorMetricInfo(R.string.channel_1, "%.1fmA".format(ch1Current), Icons.Default.Power))
add(VectorMetricInfo(Res.string.channel_1, "%.2fV".format(ch1Voltage), Icons.Default.Bolt))
add(VectorMetricInfo(Res.string.channel_1, "%.1fmA".format(ch1Current), Icons.Default.Power))
}
if (ch2Voltage != 0f) {
add(VectorMetricInfo(R.string.channel_2, "%.2fV".format(ch2Voltage), Icons.Default.Bolt))
add(VectorMetricInfo(R.string.channel_2, "%.1fmA".format(ch2Current), Icons.Default.Power))
add(VectorMetricInfo(Res.string.channel_2, "%.2fV".format(ch2Voltage), Icons.Default.Bolt))
add(VectorMetricInfo(Res.string.channel_2, "%.1fmA".format(ch2Current), Icons.Default.Power))
}
if (ch3Voltage != 0f) {
add(VectorMetricInfo(R.string.channel_3, "%.2fV".format(ch3Voltage), Icons.Default.Bolt))
add(VectorMetricInfo(R.string.channel_3, "%.1fmA".format(ch3Current), Icons.Default.Power))
add(VectorMetricInfo(Res.string.channel_3, "%.2fV".format(ch3Voltage), Icons.Default.Bolt))
add(VectorMetricInfo(Res.string.channel_3, "%.1fmA".format(ch3Current), Icons.Default.Power))
}
}
}

View File

@@ -23,17 +23,17 @@ import androidx.compose.material.icons.filled.Person
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.InsetDivider
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.feature.node.model.NodeDetailAction
import org.meshtastic.feature.node.model.isEffectivelyUnmessageable
import org.meshtastic.core.strings.R as Res
@Composable
internal fun RemoteDeviceActions(node: Node, lastTracerouteTime: Long?, onAction: (NodeDetailAction) -> Unit) {
if (!node.isEffectivelyUnmessageable) {
ListItem(
text = stringResource(R.string.direct_message),
text = stringResource(Res.string.direct_message),
leadingIcon = Icons.AutoMirrored.TwoTone.Message,
trailingIcon = null,
onClick = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.DirectMessage(node))) },
@@ -43,7 +43,7 @@ internal fun RemoteDeviceActions(node: Node, lastTracerouteTime: Long?, onAction
}
ListItem(
text = stringResource(R.string.exchange_userinfo),
text = stringResource(Res.string.exchange_userinfo),
leadingIcon = Icons.Default.Person,
trailingIcon = null,
onClick = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.RequestUserInfo(node))) },

View File

@@ -26,6 +26,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewLightDark
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun SatelliteCountInfo(
@@ -36,7 +37,7 @@ fun SatelliteCountInfo(
IconInfo(
modifier = modifier,
icon = Icons.TwoTone.SatelliteAlt,
contentDescription = stringResource(org.meshtastic.core.strings.R.string.sats),
contentDescription = stringResource(Res.string.sats),
text = "$satCount",
contentColor = contentColor,
)

View File

@@ -34,15 +34,15 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.BasicListItem
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
private const val COOL_DOWN_TIME_MS = 30000L
@Composable
fun TracerouteButton(
text: String = stringResource(R.string.traceroute),
text: String = stringResource(Res.string.traceroute),
lastTracerouteTime: Long?,
onClick: () -> Unit,
) {

View File

@@ -60,7 +60,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DeviceVersion
import org.meshtastic.core.service.ConnectionState
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.AddContactFAB
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.rememberTimeTickWithLifecycle
@@ -70,6 +69,7 @@ import org.meshtastic.feature.node.component.NodeActionDialogs
import org.meshtastic.feature.node.component.NodeFilterTextField
import org.meshtastic.feature.node.component.NodeItem
import org.meshtastic.proto.AdminProtos
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Suppress("LongMethod", "CyclomaticComplexMethod")
@@ -95,8 +95,8 @@ fun NodeListScreen(viewModel: NodeListViewModel = hiltViewModel(), navigateToNod
Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.nodes),
subtitle = stringResource(R.string.node_count_template, onlineNodeCount, totalNodeCount),
title = stringResource(Res.string.nodes),
subtitle = stringResource(Res.string.node_count_template, onlineNodeCount, totalNodeCount),
ourNode = ourNode,
showNodeChip = false,
canNavigateUp = false,
@@ -233,7 +233,7 @@ private fun ContextMenu(
contentDescription = null,
)
},
text = { Text(stringResource(if (isFavorite) R.string.remove_favorite else R.string.add_favorite)) },
text = { Text(stringResource(if (isFavorite) Res.string.remove_favorite else Res.string.add_favorite)) },
)
DropdownMenuItem(
@@ -250,7 +250,7 @@ private fun ContextMenu(
},
text = {
Text(
text = stringResource(if (isIgnored) R.string.remove_ignored else R.string.ignore),
text = stringResource(if (isIgnored) Res.string.remove_ignored else Res.string.ignore),
color = MaterialTheme.colorScheme.StatusRed,
)
},
@@ -271,7 +271,7 @@ private fun ContextMenu(
},
text = {
Text(
text = stringResource(R.string.remove),
text = stringResource(Res.string.remove),
color = if (isIgnored) Color.Unspecified else MaterialTheme.colorScheme.StatusRed,
)
},

View File

@@ -56,11 +56,11 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.meshtastic.core.strings.R
import org.meshtastic.feature.node.metrics.CommonCharts.DATE_TIME_MINUTE_FORMAT
import org.meshtastic.feature.node.metrics.CommonCharts.MAX_PERCENT_VALUE
import org.meshtastic.feature.node.metrics.CommonCharts.MS_PER_SEC
import java.text.DateFormat
import org.meshtastic.core.strings.R as Res
object CommonCharts {
val DATE_TIME_FORMAT: DateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
@@ -93,7 +93,7 @@ fun ChartHeader(amount: Int) {
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "$amount ${stringResource(R.string.logs)}",
text = "$amount ${stringResource(Res.string.logs)}",
modifier = Modifier.wrapContentWidth(),
style = TextStyle(fontWeight = FontWeight.Bold),
fontSize = MaterialTheme.typography.labelLarge.fontSize,
@@ -254,7 +254,7 @@ fun Legend(legendData: List<LegendData>, displayInfoIcon: Boolean = true, prompt
Icon(
imageVector = Icons.Default.Info,
modifier = Modifier.clickable { promptInfoDialog() },
contentDescription = stringResource(R.string.info),
contentDescription = stringResource(Res.string.info),
)
}
@@ -272,7 +272,11 @@ fun Legend(legendData: List<LegendData>, displayInfoIcon: Boolean = true, prompt
fun LegendInfoDialog(pairedRes: List<Pair<Int, Int>>, onDismiss: () -> Unit) {
AlertDialog(
title = {
Text(text = stringResource(R.string.info), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
Text(
text = stringResource(Res.string.info),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
)
},
text = {
Column {
@@ -289,7 +293,7 @@ fun LegendInfoDialog(pairedRes: List<Pair<Int, Int>>, onDismiss: () -> Unit) {
}
},
onDismissRequest = onDismiss,
confirmButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.close)) } },
confirmButton = { TextButton(onClick = onDismiss) { Text(stringResource(Res.string.close)) } },
shape = RoundedCornerShape(16.dp),
)
}
@@ -322,8 +326,8 @@ private fun LegendLabel(text: String, color: Color, isLine: Boolean = false) {
private fun LegendPreview() {
val data =
listOf(
LegendData(nameRes = R.string.rssi, color = Color.Red),
LegendData(nameRes = R.string.snr, color = Color.Green),
LegendData(nameRes = Res.string.rssi, color = Color.Red),
LegendData(nameRes = Res.string.snr, color = Color.Green),
)
Legend(legendData = data, promptInfoDialog = {})
}

View File

@@ -60,7 +60,6 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.MaterialBatteryInfo
import org.meshtastic.core.ui.component.OptionLabel
@@ -77,6 +76,7 @@ import org.meshtastic.feature.node.metrics.GraphUtil.plotPoint
import org.meshtastic.feature.node.model.TimeFrame
import org.meshtastic.proto.TelemetryProtos
import org.meshtastic.proto.TelemetryProtos.Telemetry
import org.meshtastic.core.strings.R as Res
private const val CHART_WEIGHT = 1f
private const val Y_AXIS_WEIGHT = 0.1f
@@ -98,15 +98,15 @@ private enum class Device(val color: Color) {
private val LEGEND_DATA =
listOf(
LegendData(nameRes = R.string.battery, color = Device.BATTERY.color, isLine = true, environmentMetric = null),
LegendData(nameRes = Res.string.battery, color = Device.BATTERY.color, isLine = true, environmentMetric = null),
LegendData(
nameRes = R.string.channel_utilization,
nameRes = Res.string.channel_utilization,
color = Device.CH_UTIL.color,
isLine = false,
environmentMetric = null,
),
LegendData(
nameRes = R.string.air_utilization,
nameRes = Res.string.air_utilization,
color = Device.AIR_UTIL.color,
isLine = false,
environmentMetric = null,
@@ -138,8 +138,8 @@ fun DeviceMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigat
LegendInfoDialog(
pairedRes =
listOf(
Pair(R.string.channel_utilization, R.string.ch_util_definition),
Pair(R.string.air_utilization, R.string.air_util_definition),
Pair(Res.string.channel_utilization, Res.string.ch_util_definition),
Pair(Res.string.air_utilization, Res.string.air_util_definition),
),
onDismiss = { displayInfoDialog = false },
)
@@ -341,7 +341,7 @@ private fun DeviceMetricsCard(telemetry: Telemetry) {
/* Channel Utilization and Air Utilization Tx */
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
val text =
stringResource(R.string.channel_air_util)
stringResource(Res.string.channel_air_util)
.format(deviceMetrics.channelUtilization, deviceMetrics.airUtilTx)
Text(
text = text,
@@ -404,8 +404,8 @@ private fun DeviceMetricsScreenPreview() {
LegendInfoDialog(
pairedRes =
listOf(
Pair(R.string.channel_utilization, R.string.ch_util_definition),
Pair(R.string.air_utilization, R.string.air_util_definition),
Pair(Res.string.channel_utilization, Res.string.ch_util_definition),
Pair(Res.string.air_utilization, Res.string.air_util_definition),
),
onDismiss = { displayInfoDialog = false },
)

View File

@@ -40,11 +40,11 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R
import org.meshtastic.feature.node.metrics.GraphUtil.createPath
import org.meshtastic.feature.node.metrics.GraphUtil.drawPathWithGradient
import org.meshtastic.feature.node.model.TimeFrame
import org.meshtastic.proto.TelemetryProtos.Telemetry
import org.meshtastic.core.strings.R as Res
private const val CHART_WEIGHT = 1f
private const val Y_AXIS_WEIGHT = 0.1f
@@ -53,13 +53,13 @@ private const val Y_AXIS_WEIGHT = 0.1f
private val LEGEND_DATA_1 =
listOf(
LegendData(
nameRes = R.string.temperature,
nameRes = Res.string.temperature,
color = Environment.TEMPERATURE.color,
isLine = true,
environmentMetric = Environment.TEMPERATURE,
),
LegendData(
nameRes = R.string.humidity,
nameRes = Res.string.humidity,
color = Environment.HUMIDITY.color,
isLine = true,
environmentMetric = Environment.HUMIDITY,
@@ -68,25 +68,25 @@ private val LEGEND_DATA_1 =
private val LEGEND_DATA_2 =
listOf(
LegendData(
nameRes = R.string.iaq,
nameRes = Res.string.iaq,
color = Environment.IAQ.color,
isLine = true,
environmentMetric = Environment.IAQ,
),
LegendData(
nameRes = R.string.baro_pressure,
nameRes = Res.string.baro_pressure,
color = Environment.BAROMETRIC_PRESSURE.color,
isLine = true,
environmentMetric = Environment.BAROMETRIC_PRESSURE,
),
LegendData(
nameRes = R.string.lux,
nameRes = Res.string.lux,
color = Environment.LUX.color,
isLine = true,
environmentMetric = Environment.LUX,
),
LegendData(
nameRes = R.string.uv_lux,
nameRes = Res.string.uv_lux,
color = Environment.UV_LUX.color,
isLine = true,
environmentMetric = Environment.UV_LUX,
@@ -96,13 +96,13 @@ private val LEGEND_DATA_2 =
private val LEGEND_DATA_3 =
listOf(
LegendData(
nameRes = R.string.soil_temperature,
nameRes = Res.string.soil_temperature,
color = Environment.SOIL_TEMPERATURE.color,
isLine = true,
environmentMetric = Environment.SOIL_TEMPERATURE,
),
LegendData(
nameRes = R.string.soil_moisture,
nameRes = Res.string.soil_moisture,
color = Environment.SOIL_MOISTURE.color,
isLine = true,
environmentMetric = Environment.SOIL_MOISTURE,

View File

@@ -50,7 +50,6 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.model.util.UnitConversions.celsiusToFahrenheit
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.IaqDisplayMode
import org.meshtastic.core.ui.component.IndoorAirQuality
import org.meshtastic.core.ui.component.MainAppBar
@@ -62,6 +61,7 @@ import org.meshtastic.feature.node.model.TimeFrame
import org.meshtastic.proto.TelemetryProtos
import org.meshtastic.proto.TelemetryProtos.Telemetry
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@Composable
fun EnvironmentMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigateUp: () -> Unit) {
@@ -106,7 +106,7 @@ fun EnvironmentMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNa
Column(modifier = Modifier.padding(innerPadding)) {
if (displayInfoDialog) {
LegendInfoDialog(
pairedRes = listOf(Pair(R.string.iaq, R.string.iaq_definition)),
pairedRes = listOf(Pair(Res.string.iaq, Res.string.iaq_definition)),
onDismiss = { displayInfoDialog = false },
)
}
@@ -140,7 +140,7 @@ private fun TemperatureDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics, e
if (!temperature.isNaN()) {
val textFormat = if (environmentDisplayFahrenheit) "%s %.1f°F" else "%s %.1f°C"
Text(
text = textFormat.format(stringResource(R.string.temperature), temperature),
text = textFormat.format(stringResource(Res.string.temperature), temperature),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@@ -161,7 +161,7 @@ private fun HumidityAndBarometricPressureDisplay(envMetrics: TelemetryProtos.Env
if (hasHumidity) {
val humidity = envMetrics.relativeHumidity!!
Text(
text = "%s %.2f%%".format(stringResource(R.string.humidity), humidity),
text = "%s %.2f%%".format(stringResource(Res.string.humidity), humidity),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
modifier = Modifier.padding(vertical = 0.dp),
@@ -192,7 +192,8 @@ private fun SoilMetricsDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics, e
envMetrics.soilMoisture?.let { soilMoistureValue ->
if (soilMoistureValue != Int.MIN_VALUE) {
Text(
text = soilMoistureTextFormat.format(stringResource(R.string.soil_moisture), soilMoistureValue),
text =
soilMoistureTextFormat.format(stringResource(Res.string.soil_moisture), soilMoistureValue),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@@ -203,7 +204,7 @@ private fun SoilMetricsDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics, e
Text(
text =
soilTemperatureTextFormat.format(
stringResource(R.string.soil_temperature),
stringResource(Res.string.soil_temperature),
soilTemperature,
),
color = MaterialTheme.colorScheme.onSurface,
@@ -225,7 +226,7 @@ private fun LuxUVLuxDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics) {
if (hasLux) {
val luxValue = envMetrics.lux!!
Text(
text = "%s %.0f lx".format(stringResource(R.string.lux), luxValue),
text = "%s %.0f lx".format(stringResource(Res.string.lux), luxValue),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@@ -233,7 +234,7 @@ private fun LuxUVLuxDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics) {
if (hasUvLux) {
val uvLuxValue = envMetrics.uvLux!!
Text(
text = "%s %.0f UVlx".format(stringResource(R.string.uv_lux), uvLuxValue),
text = "%s %.0f UVlx".format(stringResource(Res.string.uv_lux), uvLuxValue),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@@ -252,7 +253,7 @@ private fun VoltageCurrentDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics
if (hasVoltage) {
val voltage = envMetrics.voltage!!
Text(
text = "%s %.2f V".format(stringResource(R.string.voltage), voltage),
text = "%s %.2f V".format(stringResource(Res.string.voltage), voltage),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@@ -260,7 +261,7 @@ private fun VoltageCurrentDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics
if (hasCurrent) {
val current = envMetrics.current!!
Text(
text = "%s %.2f mA".format(stringResource(R.string.current), current),
text = "%s %.2f mA".format(stringResource(Res.string.current), current),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@@ -279,7 +280,7 @@ private fun GasCompositionDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics
if (iaqValue != null && iaqValue != Int.MIN_VALUE) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = stringResource(R.string.iaq),
text = stringResource(Res.string.iaq),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@@ -289,7 +290,7 @@ private fun GasCompositionDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics
}
if (gasResistance != null && !gasResistance.isNaN()) {
Text(
text = "%s %.2f Ohm".format(stringResource(R.string.gas_resistance), gasResistance),
text = "%s %.2f Ohm".format(stringResource(Res.string.gas_resistance), gasResistance),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
@@ -301,7 +302,7 @@ private fun GasCompositionDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics
// Spacer(modifier = Modifier.height(4.dp))
// Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
// Text(
// text = "%s %.0f ppm".format(stringResource(R.string.co2), co2),
// text = "%s %.0f ppm".format(stringResource(Res.string.co2), co2),
// color = MaterialTheme.colorScheme.onSurface,
// fontSize = MaterialTheme.typography.labelLarge.fontSize,
// )
@@ -311,7 +312,7 @@ private fun GasCompositionDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics
// Spacer(modifier = Modifier.height(4.dp))
// Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
// Text(
// text = "%s %.0f ppb".format(stringResource(R.string.tvoc), tvoc),
// text = "%s %.0f ppb".format(stringResource(Res.string.tvoc), tvoc),
// color = MaterialTheme.colorScheme.onSurface,
// fontSize = MaterialTheme.typography.labelLarge.fontSize,
// )
@@ -325,7 +326,7 @@ private fun RadiationDisplay(envMetrics: TelemetryProtos.EnvironmentMetrics) {
if (!radiation.isNaN() && radiation > 0f) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Text(
text = "%s %.2f µR/h".format(stringResource(R.string.radiation), radiation),
text = "%s %.2f µR/h".format(stringResource(Res.string.radiation), radiation),
color = MaterialTheme.colorScheme.onSurface,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)

View File

@@ -55,12 +55,12 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.model.util.formatUptime
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.feature.node.metrics.CommonCharts.DATE_TIME_FORMAT
import org.meshtastic.proto.TelemetryProtos
import java.text.DecimalFormat
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalFoundationApi::class)
@Composable
@@ -113,36 +113,36 @@ fun HostMetricsItem(modifier: Modifier = Modifier, telemetry: TelemetryProtos.Te
fontSize = MaterialTheme.typography.labelLarge.fontSize,
)
LogLine(
label = stringResource(R.string.uptime),
label = stringResource(Res.string.uptime),
value = formatUptime(hostMetrics.uptimeSeconds),
modifier = Modifier.fillMaxWidth(),
)
LogLine(
label = stringResource(R.string.free_memory),
label = stringResource(Res.string.free_memory),
value = formatBytes(hostMetrics.freememBytes),
modifier = Modifier.fillMaxWidth(),
)
LogLine(
label = stringResource(R.string.disk_free_indexed, 1),
label = stringResource(Res.string.disk_free_indexed, 1),
value = formatBytes(hostMetrics.diskfree1Bytes),
modifier = Modifier.fillMaxWidth(),
)
if (hostMetrics.hasDiskfree2Bytes()) {
LogLine(
label = stringResource(R.string.disk_free_indexed, 2),
label = stringResource(Res.string.disk_free_indexed, 2),
value = formatBytes(hostMetrics.diskfree2Bytes),
modifier = Modifier.fillMaxWidth(),
)
}
if (hostMetrics.hasDiskfree3Bytes()) {
LogLine(
label = stringResource(R.string.disk_free_indexed, 3),
label = stringResource(Res.string.disk_free_indexed, 3),
value = formatBytes(hostMetrics.diskfree3Bytes),
modifier = Modifier.fillMaxWidth(),
)
}
LogLine(
label = stringResource(R.string.load_indexed, 1),
label = stringResource(Res.string.load_indexed, 1),
value = (hostMetrics.load1 / 100.0).toString(),
modifier = Modifier.fillMaxWidth(),
)
@@ -154,7 +154,7 @@ fun HostMetricsItem(modifier: Modifier = Modifier, telemetry: TelemetryProtos.Te
strokeCap = ProgressIndicatorDefaults.LinearStrokeCap,
)
LogLine(
label = stringResource(R.string.load_indexed, 5),
label = stringResource(Res.string.load_indexed, 5),
value = (hostMetrics.load5 / 100.0).toString(),
modifier = Modifier.fillMaxWidth(),
)
@@ -166,7 +166,7 @@ fun HostMetricsItem(modifier: Modifier = Modifier, telemetry: TelemetryProtos.Te
strokeCap = ProgressIndicatorDefaults.LinearStrokeCap,
)
LogLine(
label = stringResource(R.string.load_indexed, 15),
label = stringResource(Res.string.load_indexed, 15),
value = (hostMetrics.load15 / 100.0).toString(),
modifier = Modifier.fillMaxWidth(),
)
@@ -178,7 +178,7 @@ fun HostMetricsItem(modifier: Modifier = Modifier, telemetry: TelemetryProtos.Te
strokeCap = ProgressIndicatorDefaults.LinearStrokeCap,
)
if (hostMetrics.hasUserString()) {
Text(text = stringResource(R.string.user_string), style = MaterialTheme.typography.bodyMedium)
Text(text = stringResource(Res.string.user_string), style = MaterialTheme.typography.bodyMedium)
Text(text = hostMetrics.userString, style = TextStyle(fontFamily = FontFamily.Monospace))
}
}

View File

@@ -52,7 +52,6 @@ import org.meshtastic.core.proto.safeNumber
import org.meshtastic.core.proto.toPosition
import org.meshtastic.core.service.ServiceAction
import org.meshtastic.core.service.ServiceRepository
import org.meshtastic.core.strings.R
import org.meshtastic.feature.node.model.MetricsState
import org.meshtastic.feature.node.model.TimeFrame
import org.meshtastic.proto.MeshProtos
@@ -66,6 +65,7 @@ import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.Locale
import javax.inject.Inject
import org.meshtastic.core.strings.R as Res
private const val DEFAULT_ID_SUFFIX_LENGTH = 4
@@ -99,7 +99,7 @@ constructor(
private fun createFallbackNode(nodeNum: Int): Node {
val userId = DataPacket.nodeNumToDefaultId(nodeNum)
val safeUserId = userId.padStart(DEFAULT_ID_SUFFIX_LENGTH, '0').takeLast(DEFAULT_ID_SUFFIX_LENGTH)
val longName = app.getString(R.string.fallback_node_name) + " $safeUserId"
val longName = app.getString(Res.string.fallback_node_name) + " $safeUserId"
val defaultUser =
MeshProtos.User.newBuilder()
.setId(userId)

View File

@@ -56,7 +56,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.database.entity.MeshLog
import org.meshtastic.core.model.util.formatUptime
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.OptionLabel
import org.meshtastic.core.ui.component.SlidingSelector
@@ -65,15 +64,16 @@ import org.meshtastic.proto.PaxcountProtos
import org.meshtastic.proto.Portnums.PortNum
import java.text.DateFormat
import java.util.Date
import org.meshtastic.core.strings.R as Res
private const val CHART_WEIGHT = 1f
private const val Y_AXIS_WEIGHT = 0.1f
private const val CHART_WIDTH_RATIO = CHART_WEIGHT / (CHART_WEIGHT + Y_AXIS_WEIGHT + Y_AXIS_WEIGHT)
private enum class PaxSeries(val color: Color, val legendRes: Int) {
PAX(Color.Black, R.string.pax),
BLE(Color.Cyan, R.string.ble_devices),
WIFI(Color.Green, R.string.wifi_devices),
PAX(Color.Black, Res.string.pax),
BLE(Color.Cyan, Res.string.ble_devices),
WIFI(Color.Green, Res.string.wifi_devices),
}
@Suppress("LongMethod")
@@ -228,7 +228,7 @@ fun PaxMetricsScreen(metricsViewModel: MetricsViewModel = hiltViewModel(), onNav
// List
if (paxMetrics.isEmpty()) {
Text(
text = stringResource(R.string.no_pax_metrics_logs),
text = stringResource(Res.string.no_pax_metrics_logs),
modifier = Modifier.fillMaxSize().padding(16.dp),
textAlign = TextAlign.Center,
)
@@ -314,7 +314,7 @@ fun PaxMetricsItem(log: MeshLog, pax: PaxcountProtos.Paxcount, dateFormat: DateF
modifier = Modifier.weight(1f, fill = true),
)
Text(
text = stringResource(R.string.uptime) + ": " + formatUptime(pax.uptime),
text = stringResource(Res.string.uptime) + ": " + formatUptime(pax.uptime),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.End,
modifier = Modifier.alignByBaseline(),

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