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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
153 changed files with 1403 additions and 1350 deletions

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(),

View file

@ -65,12 +65,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.model.util.metersIn
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.MainAppBar
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.proto.ConfigProtos.Config.DisplayConfig.DisplayUnits
import org.meshtastic.proto.MeshProtos
import java.text.DateFormat
import org.meshtastic.core.strings.R as Res
@Composable
private fun RowScope.PositionText(text: String, weight: Float) {
@ -91,15 +91,15 @@ private const val WEIGHT_40 = .40f
@Composable
private fun HeaderItem(compactWidth: Boolean) {
Row(modifier = Modifier.fillMaxWidth().padding(8.dp), horizontalArrangement = Arrangement.SpaceBetween) {
PositionText(stringResource(R.string.latitude), WEIGHT_20)
PositionText(stringResource(R.string.longitude), WEIGHT_20)
PositionText(stringResource(R.string.sats), WEIGHT_10)
PositionText(stringResource(R.string.alt), WEIGHT_15)
PositionText(stringResource(Res.string.latitude), WEIGHT_20)
PositionText(stringResource(Res.string.longitude), WEIGHT_20)
PositionText(stringResource(Res.string.sats), WEIGHT_10)
PositionText(stringResource(Res.string.alt), WEIGHT_15)
if (!compactWidth) {
PositionText(stringResource(R.string.speed), WEIGHT_15)
PositionText(stringResource(R.string.heading), WEIGHT_15)
PositionText(stringResource(Res.string.speed), WEIGHT_15)
PositionText(stringResource(Res.string.heading), WEIGHT_15)
}
PositionText(stringResource(R.string.timestamp), WEIGHT_40)
PositionText(stringResource(Res.string.timestamp), WEIGHT_40)
}
}
@ -143,15 +143,15 @@ private fun ActionButtons(
enabled = clearButtonEnabled,
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.error),
) {
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(R.string.clear))
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(Res.string.clear))
Spacer(Modifier.width(8.dp))
Text(text = stringResource(R.string.clear))
Text(text = stringResource(Res.string.clear))
}
OutlinedButton(modifier = Modifier.weight(1f), onClick = onSave, enabled = saveButtonEnabled) {
Icon(imageVector = Icons.Default.Save, contentDescription = stringResource(R.string.save))
Icon(imageVector = Icons.Default.Save, contentDescription = stringResource(Res.string.save))
Spacer(Modifier.width(8.dp))
Text(text = stringResource(R.string.save))
Text(text = stringResource(Res.string.save))
}
}
}

View file

@ -60,7 +60,6 @@ import androidx.compose.ui.text.font.FontWeight
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.OptionLabel
import org.meshtastic.core.ui.component.SlidingSelector
@ -73,6 +72,7 @@ import org.meshtastic.feature.node.model.TimeFrame
import org.meshtastic.proto.TelemetryProtos.Telemetry
import kotlin.math.ceil
import kotlin.math.floor
import org.meshtastic.core.strings.R as Res
@Suppress("MagicNumber")
private enum class Power(val color: Color, val min: Float, val max: Float) {
@ -84,9 +84,9 @@ private enum class Power(val color: Color, val min: Float, val max: Float) {
}
private enum class PowerChannel(@StringRes val strRes: Int) {
ONE(R.string.channel_1),
TWO(R.string.channel_2),
THREE(R.string.channel_3),
ONE(Res.string.channel_1),
TWO(Res.string.channel_2),
THREE(Res.string.channel_3),
}
private const val CHART_WEIGHT = 1f
@ -112,8 +112,8 @@ fun minMaxGraphVoltage(valueMin: Float, valueMax: Float): Pair<Float, Float> {
private val LEGEND_DATA =
listOf(
LegendData(nameRes = R.string.current, color = Power.CURRENT.color, isLine = true, environmentMetric = null),
LegendData(nameRes = R.string.voltage, color = VOLTAGE_COLOR, isLine = true, environmentMetric = null),
LegendData(nameRes = Res.string.current, color = Power.CURRENT.color, isLine = true, environmentMetric = null),
LegendData(nameRes = Res.string.voltage, color = VOLTAGE_COLOR, isLine = true, environmentMetric = null),
)
@Composable
@ -321,21 +321,21 @@ private fun PowerMetricsCard(telemetry: Telemetry) {
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
if (telemetry.powerMetrics.hasCh1Current() || telemetry.powerMetrics.hasCh1Voltage()) {
PowerChannelColumn(
R.string.channel_1,
Res.string.channel_1,
telemetry.powerMetrics.ch1Voltage,
telemetry.powerMetrics.ch1Current,
)
}
if (telemetry.powerMetrics.hasCh2Current() || telemetry.powerMetrics.hasCh2Voltage()) {
PowerChannelColumn(
R.string.channel_2,
Res.string.channel_2,
telemetry.powerMetrics.ch2Voltage,
telemetry.powerMetrics.ch2Current,
)
}
if (telemetry.powerMetrics.hasCh3Current() || telemetry.powerMetrics.hasCh3Voltage()) {
PowerChannelColumn(
R.string.channel_3,
Res.string.channel_3,
telemetry.powerMetrics.ch3Voltage,
telemetry.powerMetrics.ch3Current,
)

View file

@ -57,7 +57,6 @@ import androidx.compose.ui.text.font.FontWeight
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.LoraSignalIndicator
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.OptionLabel
@ -68,6 +67,7 @@ import org.meshtastic.feature.node.metrics.CommonCharts.MS_PER_SEC
import org.meshtastic.feature.node.metrics.GraphUtil.plotPoint
import org.meshtastic.feature.node.model.TimeFrame
import org.meshtastic.proto.MeshProtos.MeshPacket
import org.meshtastic.core.strings.R as Res
@Suppress("MagicNumber")
private enum class Metric(val color: Color, val min: Float, val max: Float) {
@ -85,8 +85,8 @@ private const val CHART_WIDTH_RATIO = CHART_WEIGHT / (CHART_WEIGHT + Y_AXIS_WEIG
private val LEGEND_DATA =
listOf(
LegendData(nameRes = R.string.rssi, color = Metric.RSSI.color, environmentMetric = null),
LegendData(nameRes = R.string.snr, color = Metric.SNR.color, environmentMetric = null),
LegendData(nameRes = Res.string.rssi, color = Metric.RSSI.color, environmentMetric = null),
LegendData(nameRes = Res.string.snr, color = Metric.SNR.color, environmentMetric = null),
)
@Composable
@ -114,8 +114,8 @@ fun SignalMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigat
LegendInfoDialog(
pairedRes =
listOf(
Pair(R.string.snr, R.string.snr_definition),
Pair(R.string.rssi, R.string.rssi_definition),
Pair(Res.string.snr, Res.string.snr_definition),
Pair(Res.string.rssi, Res.string.rssi_definition),
),
onDismiss = { displayInfoDialog = false },
)

View file

@ -64,7 +64,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.model.fullRouteDiscovery
import org.meshtastic.core.model.getTracerouteResponse
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.SNR_FAIR_THRESHOLD
import org.meshtastic.core.ui.component.SNR_GOOD_THRESHOLD
@ -76,6 +75,7 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusYellow
import org.meshtastic.feature.node.metrics.CommonCharts.MS_PER_SEC
import org.meshtastic.proto.MeshProtos
import java.text.DateFormat
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalFoundationApi::class)
@Suppress("LongMethod")
@ -95,7 +95,7 @@ fun TracerouteLogScreen(
if (showDialog != null) {
val message = showDialog ?: AnnotatedString("") // Should not be null if dialog is shown
SimpleAlertDialog(
title = R.string.traceroute,
title = Res.string.traceroute,
text = { SelectionContainer { Text(text = message) } },
onDismiss = { showDialog = null },
)
@ -185,11 +185,11 @@ private fun DeleteItem(onClick: () -> Unit) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = stringResource(R.string.delete),
contentDescription = stringResource(Res.string.delete),
tint = MaterialTheme.colorScheme.error,
)
Spacer(modifier = Modifier.width(12.dp))
Text(text = stringResource(R.string.delete), color = MaterialTheme.colorScheme.error)
Text(text = stringResource(Res.string.delete), color = MaterialTheme.colorScheme.error)
}
},
)
@ -202,7 +202,7 @@ private fun TracerouteItem(icon: ImageVector, text: String, modifier: Modifier =
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = icon, contentDescription = stringResource(R.string.traceroute))
Icon(imageVector = icon, contentDescription = stringResource(Res.string.traceroute))
Spacer(modifier = Modifier.width(8.dp))
Text(text = text, style = MaterialTheme.typography.bodyLarge)
}
@ -213,23 +213,23 @@ private fun TracerouteItem(icon: ImageVector, text: String, modifier: Modifier =
@Composable
private fun MeshProtos.RouteDiscovery?.getTextAndIcon(): Pair<String, ImageVector> = when {
this == null -> {
stringResource(R.string.routing_error_no_response) to Icons.Default.PersonOff
stringResource(Res.string.routing_error_no_response) to Icons.Default.PersonOff
}
// A direct route means the sender and receiver are the only two nodes in the route.
routeCount <= 2 && routeBackCount <= 2 -> { // also check routeBackCount for direct to be more robust
stringResource(R.string.traceroute_direct) to Icons.Default.Group
stringResource(Res.string.traceroute_direct) to Icons.Default.Group
}
routeCount == routeBackCount -> {
val hops = routeCount - 2
pluralStringResource(R.plurals.traceroute_hops, hops, hops) to Icons.Default.Groups
pluralStringResource(Res.plurals.traceroute_hops, hops, hops) to Icons.Default.Groups
}
else -> {
// Asymmetric route
val towards = maxOf(0, routeCount - 2)
val back = maxOf(0, routeBackCount - 2)
stringResource(R.string.traceroute_diff, towards, back) to Icons.Default.Groups
stringResource(Res.string.traceroute_diff, towards, back) to Icons.Default.Groups
}
}

View file

@ -31,16 +31,16 @@ import androidx.compose.material.icons.filled.Thermostat
import androidx.compose.ui.graphics.vector.ImageVector
import org.meshtastic.core.navigation.NodeDetailRoutes
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.strings.R
import org.meshtastic.core.strings.R as Res
enum class LogsType(@StringRes val titleRes: Int, val icon: ImageVector, val route: Route) {
DEVICE(R.string.device_metrics_log, Icons.Default.ChargingStation, NodeDetailRoutes.DeviceMetrics),
NODE_MAP(R.string.node_map, Icons.Default.Map, NodeDetailRoutes.NodeMap),
POSITIONS(R.string.position_log, Icons.Default.LocationOn, NodeDetailRoutes.PositionLog),
ENVIRONMENT(R.string.env_metrics_log, Icons.Default.Thermostat, NodeDetailRoutes.EnvironmentMetrics),
SIGNAL(R.string.sig_metrics_log, Icons.Default.SignalCellularAlt, NodeDetailRoutes.SignalMetrics),
POWER(R.string.power_metrics_log, Icons.Default.Power, NodeDetailRoutes.PowerMetrics),
TRACEROUTE(R.string.traceroute_log, Icons.Default.Route, NodeDetailRoutes.TracerouteLog),
HOST(R.string.host_metrics_log, Icons.Default.Memory, NodeDetailRoutes.HostMetricsLog),
PAX(R.string.pax_metrics_log, Icons.Default.People, NodeDetailRoutes.PaxMetrics),
DEVICE(Res.string.device_metrics_log, Icons.Default.ChargingStation, NodeDetailRoutes.DeviceMetrics),
NODE_MAP(Res.string.node_map, Icons.Default.Map, NodeDetailRoutes.NodeMap),
POSITIONS(Res.string.position_log, Icons.Default.LocationOn, NodeDetailRoutes.PositionLog),
ENVIRONMENT(Res.string.env_metrics_log, Icons.Default.Thermostat, NodeDetailRoutes.EnvironmentMetrics),
SIGNAL(Res.string.sig_metrics_log, Icons.Default.SignalCellularAlt, NodeDetailRoutes.SignalMetrics),
POWER(Res.string.power_metrics_log, Icons.Default.Power, NodeDetailRoutes.PowerMetrics),
TRACEROUTE(Res.string.traceroute_log, Icons.Default.Route, NodeDetailRoutes.TracerouteLog),
HOST(Res.string.host_metrics_log, Icons.Default.Memory, NodeDetailRoutes.HostMetricsLog),
PAX(Res.string.pax_metrics_log, Icons.Default.People, NodeDetailRoutes.PaxMetrics),
}

View file

@ -24,11 +24,11 @@ import org.meshtastic.core.database.entity.FirmwareRelease
import org.meshtastic.core.database.entity.MeshLog
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DeviceHardware
import org.meshtastic.core.strings.R
import org.meshtastic.proto.ConfigProtos
import org.meshtastic.proto.MeshProtos
import org.meshtastic.proto.TelemetryProtos
import java.util.concurrent.TimeUnit
import org.meshtastic.core.strings.R as Res
data class MetricsState(
val isLocal: Boolean = false,
@ -88,12 +88,12 @@ data class MetricsState(
/** Supported time frames used to display data. */
@Suppress("MagicNumber")
enum class TimeFrame(val seconds: Long, @StringRes val strRes: Int) {
TWENTY_FOUR_HOURS(TimeUnit.DAYS.toSeconds(1), R.string.twenty_four_hours),
FORTY_EIGHT_HOURS(TimeUnit.DAYS.toSeconds(2), R.string.forty_eight_hours),
ONE_WEEK(TimeUnit.DAYS.toSeconds(7), R.string.one_week),
TWO_WEEKS(TimeUnit.DAYS.toSeconds(14), R.string.two_weeks),
FOUR_WEEKS(TimeUnit.DAYS.toSeconds(28), R.string.four_weeks),
MAX(0L, R.string.max),
TWENTY_FOUR_HOURS(TimeUnit.DAYS.toSeconds(1), Res.string.twenty_four_hours),
FORTY_EIGHT_HOURS(TimeUnit.DAYS.toSeconds(2), Res.string.forty_eight_hours),
ONE_WEEK(TimeUnit.DAYS.toSeconds(7), Res.string.one_week),
TWO_WEEKS(TimeUnit.DAYS.toSeconds(14), Res.string.two_weeks),
FOUR_WEEKS(TimeUnit.DAYS.toSeconds(28), Res.string.four_weeks),
MAX(0L, Res.string.max),
;
fun calculateOldestTime(): Long = if (this == MAX) {

View file

@ -36,8 +36,8 @@ import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.meshtastic.core.strings.R
import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog
import org.meshtastic.core.strings.R as Res
@RunWith(AndroidJUnit4::class)
class DebugFiltersTest {
@ -47,7 +47,7 @@ class DebugFiltersTest {
@Test
fun debugFilterBar_showsFilterButtonAndMenu() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val filterLabel = context.getString(R.string.debug_filters)
val filterLabel = context.getString(Res.string.debug_filters)
composeTestRule.setContent {
var filterTexts by remember { mutableStateOf(listOf<String>()) }
var customFilterText by remember { mutableStateOf("") }
@ -77,7 +77,7 @@ class DebugFiltersTest {
@Test
fun debugFilterBar_addCustomFilter_displaysActiveFilter() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val activeFiltersLabel = context.getString(R.string.debug_active_filters)
val activeFiltersLabel = context.getString(Res.string.debug_active_filters)
composeTestRule.setContent {
var filterTexts by remember { mutableStateOf(listOf<String>()) }
var customFilterText by remember { mutableStateOf("") }
@ -109,7 +109,7 @@ class DebugFiltersTest {
@Test
fun debugActiveFilters_clearAllFilters_removesFilters() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val activeFiltersLabel = context.getString(R.string.debug_active_filters)
val activeFiltersLabel = context.getString(Res.string.debug_active_filters)
composeTestRule.setContent {
var filterTexts by remember { mutableStateOf(listOf("A", "B")) }
DebugActiveFilters(

View file

@ -36,10 +36,10 @@ import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.meshtastic.core.strings.R
import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog
import org.meshtastic.feature.settings.debugging.LogSearchManager.SearchMatch
import org.meshtastic.feature.settings.debugging.LogSearchManager.SearchState
import org.meshtastic.core.strings.R as Res
@RunWith(AndroidJUnit4::class)
class DebugSearchTest {
@ -49,7 +49,7 @@ class DebugSearchTest {
@Test
fun debugSearchBar_showsPlaceholder() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val placeholder = context.getString(R.string.debug_default_search)
val placeholder = context.getString(Res.string.debug_default_search)
composeTestRule.setContent {
DebugSearchBar(
searchState = SearchState(),
@ -65,7 +65,7 @@ class DebugSearchTest {
@Test
fun debugSearchBar_showsClearButtonWhenTextEntered() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val placeholder = context.getString(R.string.debug_default_search)
val placeholder = context.getString(Res.string.debug_default_search)
composeTestRule.setContent {
var searchText by remember { mutableStateOf("test") }
DebugSearchBar(
@ -113,7 +113,7 @@ class DebugSearchTest {
@Test
fun debugFilterBar_showsFilterButtonAndMenu() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val filterLabel = context.getString(R.string.debug_filters)
val filterLabel = context.getString(Res.string.debug_filters)
composeTestRule.setContent {
var filterTexts by remember { mutableStateOf(listOf<String>()) }
var customFilterText by remember { mutableStateOf("") }
@ -143,7 +143,7 @@ class DebugSearchTest {
@Test
fun debugFilterBar_addCustomFilter_displaysActiveFilter() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val activeFiltersLabel = context.getString(R.string.debug_active_filters)
val activeFiltersLabel = context.getString(Res.string.debug_active_filters)
composeTestRule.setContent {
var filterTexts by remember { mutableStateOf(listOf<String>()) }
var customFilterText by remember { mutableStateOf("") }
@ -173,7 +173,7 @@ class DebugSearchTest {
@Test
fun debugActiveFilters_clearAllFilters_removesFilters() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val activeFiltersLabel = context.getString(R.string.debug_active_filters)
val activeFiltersLabel = context.getString(Res.string.debug_active_filters)
composeTestRule.setContent {
var filterTexts by remember { mutableStateOf(listOf("A", "B")) }
DebugActiveFilters(

View file

@ -27,10 +27,10 @@ import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.meshtastic.core.strings.R
import org.meshtastic.proto.ClientOnlyProtos.DeviceProfile
import org.meshtastic.proto.deviceProfile
import org.meshtastic.proto.position
import org.meshtastic.core.strings.R as Res
@RunWith(AndroidJUnit4::class)
class EditDeviceProfileDialogTest {
@ -77,8 +77,8 @@ class EditDeviceProfileDialogTest {
testEditDeviceProfileDialog()
// Verify the "Cancel" and "Save" buttons are displayed
onNodeWithText(getString(R.string.cancel)).assertIsDisplayed()
onNodeWithText(getString(R.string.save)).assertIsDisplayed()
onNodeWithText(getString(Res.string.cancel)).assertIsDisplayed()
onNodeWithText(getString(Res.string.save)).assertIsDisplayed()
}
}
@ -89,7 +89,7 @@ class EditDeviceProfileDialogTest {
testEditDeviceProfileDialog(onDismiss = { onDismissClicked = true })
// Click the "Cancel" button
onNodeWithText(getString(R.string.cancel)).performClick()
onNodeWithText(getString(Res.string.cancel)).performClick()
}
// Verify onDismiss is called
@ -102,7 +102,7 @@ class EditDeviceProfileDialogTest {
composeTestRule.apply {
testEditDeviceProfileDialog(onConfirm = { actualDeviceProfile = it })
onNodeWithText(getString(R.string.save)).performClick()
onNodeWithText(getString(Res.string.save)).performClick()
}
// Verify onConfirm is called with the correct DeviceProfile

View file

@ -29,7 +29,7 @@ import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.meshtastic.core.strings.R
import org.meshtastic.core.strings.R as Res
@RunWith(AndroidJUnit4::class)
class MapReportingPreferenceTest {
@ -69,8 +69,8 @@ class MapReportingPreferenceTest {
composeTestRule.apply {
testMapReportingPreference()
// Verify that the dialog title is displayed
onNodeWithText(getString(R.string.map_reporting)).assertIsDisplayed()
onNodeWithText(getString(R.string.map_reporting_summary)).assertIsDisplayed()
onNodeWithText(getString(Res.string.map_reporting)).assertIsDisplayed()
onNodeWithText(getString(Res.string.map_reporting_summary)).assertIsDisplayed()
}
}
@ -78,16 +78,16 @@ class MapReportingPreferenceTest {
fun testMapReportingPreference_toggleMapReporting() {
composeTestRule.apply {
testMapReportingPreference()
onNodeWithText(getString(R.string.i_agree)).assertIsNotDisplayed()
onNodeWithText(getString(R.string.map_reporting)).performClick()
onNodeWithText(getString(Res.string.i_agree)).assertIsNotDisplayed()
onNodeWithText(getString(Res.string.map_reporting)).performClick()
Assert.assertFalse(mapReportingEnabled)
Assert.assertFalse(shouldReportLocation)
onNodeWithText(getString(R.string.i_agree)).assertIsDisplayed()
onNodeWithText(getString(R.string.i_agree)).performClick()
onNodeWithText(getString(Res.string.i_agree)).assertIsDisplayed()
onNodeWithText(getString(Res.string.i_agree)).performClick()
Assert.assertTrue(shouldReportLocation)
Assert.assertTrue(mapReportingEnabled)
onNodeWithText(getString(R.string.map_reporting)).performClick()
onNodeWithText(getString(R.string.i_agree)).assertIsNotDisplayed()
onNodeWithText(getString(Res.string.map_reporting)).performClick()
onNodeWithText(getString(Res.string.i_agree)).assertIsNotDisplayed()
Assert.assertTrue(shouldReportLocation)
Assert.assertFalse(mapReportingEnabled)
}

View file

@ -64,7 +64,6 @@ import com.google.accompanist.permissions.rememberMultiplePermissionsState
import kotlinx.coroutines.delay
import org.meshtastic.core.common.gpsDisabled
import org.meshtastic.core.navigation.Route
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.MultipleChoiceAlertDialog
@ -83,6 +82,7 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlin.time.Duration.Companion.seconds
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalPermissionsApi::class)
@Suppress("LongMethod", "CyclomaticComplexMethod")
@ -139,9 +139,9 @@ fun SettingsScreen(
EditDeviceProfileDialog(
title =
if (deviceProfile != null) {
stringResource(R.string.import_configuration)
stringResource(Res.string.import_configuration)
} else {
stringResource(R.string.export_configuration)
stringResource(Res.string.export_configuration)
},
deviceProfile = deviceProfile ?: viewModel.currentDeviceProfile,
onConfirm = {
@ -186,13 +186,13 @@ fun SettingsScreen(
Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.bottom_nav_settings),
title = stringResource(Res.string.bottom_nav_settings),
subtitle =
if (state.isLocal) {
ourNode?.user?.longName
} else {
val remoteName = viewModel.destNode.value?.user?.longName ?: ""
stringResource(R.string.remotely_administrating, remoteName)
stringResource(Res.string.remotely_administrating, remoteName)
},
ourNode = ourNode,
showNodeChip = ourNode != null && isConnected && state.isLocal,
@ -233,11 +233,11 @@ fun SettingsScreen(
val context = LocalContext.current
val resources = LocalResources.current
TitledCard(title = stringResource(R.string.app_settings), modifier = Modifier.padding(top = 16.dp)) {
TitledCard(title = stringResource(Res.string.app_settings), modifier = Modifier.padding(top = 16.dp)) {
if (state.analyticsAvailable) {
val allowed by viewModel.analyticsAllowedFlow.collectAsStateWithLifecycle(false)
SwitchListItem(
text = stringResource(R.string.analytics_okay),
text = stringResource(Res.string.analytics_okay),
checked = allowed,
leadingIcon = Icons.Default.BugReport,
onClick = { viewModel.toggleAnalyticsAllowed() },
@ -257,7 +257,7 @@ fun SettingsScreen(
} else {
Toast.makeText(
context,
resources.getString(R.string.location_disabled),
resources.getString(Res.string.location_disabled),
Toast.LENGTH_LONG,
)
.show()
@ -272,7 +272,7 @@ fun SettingsScreen(
}
SwitchListItem(
text = stringResource(R.string.provide_location_to_mesh),
text = stringResource(Res.string.provide_location_to_mesh),
leadingIcon = Icons.Rounded.LocationOn,
enabled = !isGpsDisabled,
checked = provideLocation,
@ -286,7 +286,7 @@ fun SettingsScreen(
// picker for these devices.
val useInAppLangPicker = Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
ListItem(
text = stringResource(R.string.preferences_language),
text = stringResource(Res.string.preferences_language),
leadingIcon = Icons.Rounded.Language,
trailingIcon = if (useInAppLangPicker) null else Icons.AutoMirrored.Rounded.KeyboardArrowRight,
) {
@ -304,7 +304,7 @@ fun SettingsScreen(
}
ListItem(
text = stringResource(R.string.theme),
text = stringResource(Res.string.theme),
leadingIcon = Icons.Rounded.FormatPaint,
trailingIcon = null,
) {
@ -320,7 +320,7 @@ fun SettingsScreen(
}
}
ListItem(
text = stringResource(R.string.save_rangetest),
text = stringResource(Res.string.save_rangetest),
leadingIcon = Icons.Rounded.Output,
trailingIcon = null,
) {
@ -340,7 +340,7 @@ fun SettingsScreen(
}
}
ListItem(
text = stringResource(R.string.export_data_csv),
text = stringResource(Res.string.export_data_csv),
leadingIcon = Icons.Rounded.Output,
trailingIcon = null,
) {
@ -354,7 +354,7 @@ fun SettingsScreen(
}
ListItem(
text = stringResource(R.string.intro_show),
text = stringResource(Res.string.intro_show),
leadingIcon = Icons.Rounded.WavingHand,
trailingIcon = null,
) {
@ -362,7 +362,7 @@ fun SettingsScreen(
}
ListItem(
text = stringResource(R.string.system_settings),
text = stringResource(Res.string.system_settings),
leadingIcon = Icons.Rounded.AppSettingsAlt,
trailingIcon = null,
) {
@ -405,7 +405,7 @@ private fun AppVersionButton(
}
ListItem(
text = stringResource(R.string.app_version),
text = stringResource(Res.string.app_version),
leadingIcon = Icons.Rounded.Memory,
supportingText = appVersionName,
trailingIcon = null,
@ -415,14 +415,14 @@ private fun AppVersionButton(
when {
clickCount == UNLOCKED_CLICK_COUNT && excludedModulesUnlocked -> {
clickCount = 0
Toast.makeText(context, resources.getString(R.string.modules_already_unlocked), Toast.LENGTH_LONG)
Toast.makeText(context, resources.getString(Res.string.modules_already_unlocked), Toast.LENGTH_LONG)
.show()
}
clickCount == UNLOCK_CLICK_COUNT -> {
clickCount = 0
onUnlockExcludedModules()
Toast.makeText(context, resources.getString(R.string.modules_unlocked), Toast.LENGTH_LONG).show()
Toast.makeText(context, resources.getString(Res.string.modules_unlocked), Toast.LENGTH_LONG).show()
}
}
}
@ -439,7 +439,7 @@ private fun LanguagePickerDialog(onDismiss: () -> Unit) {
}
MultipleChoiceAlertDialog(
title = stringResource(R.string.preferences_language),
title = stringResource(Res.string.preferences_language),
message = "",
choices = choices,
onDismissRequest = onDismiss,
@ -451,16 +451,16 @@ private fun ThemePickerDialog(onClickTheme: (Int) -> Unit, onDismiss: () -> Unit
val resources = LocalResources.current
val themeMap = remember {
mapOf(
resources.getString(R.string.dynamic) to MODE_DYNAMIC,
resources.getString(R.string.theme_light) to AppCompatDelegate.MODE_NIGHT_NO,
resources.getString(R.string.theme_dark) to AppCompatDelegate.MODE_NIGHT_YES,
resources.getString(R.string.theme_system) to AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM,
resources.getString(Res.string.dynamic) to MODE_DYNAMIC,
resources.getString(Res.string.theme_light) to AppCompatDelegate.MODE_NIGHT_NO,
resources.getString(Res.string.theme_dark) to AppCompatDelegate.MODE_NIGHT_YES,
resources.getString(Res.string.theme_system) to AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM,
)
.mapValues { (_, value) -> { onClickTheme(value) } }
}
MultipleChoiceAlertDialog(
title = stringResource(R.string.choose_theme),
title = stringResource(Res.string.choose_theme),
message = "",
choices = themeMap,
onDismissRequest = onDismiss,

View file

@ -81,7 +81,6 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.CopyIconButton
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.SimpleAlertDialog
@ -95,6 +94,7 @@ import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import org.meshtastic.core.strings.R as Res
private val REGEX_ANNOTATED_NODE_ID = Regex("\\(![0-9a-fA-F]{8}\\)$", RegexOption.MULTILINE)
@ -147,7 +147,7 @@ fun DebugScreen(onNavigateUp: () -> Unit, viewModel: DebugViewModel = hiltViewMo
Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.debug_panel),
title = stringResource(Res.string.debug_panel),
ourNode = null,
showNodeChip = false,
canNavigateUp = true,
@ -349,12 +349,12 @@ fun DebugMenuActions(deleteLogs: () -> Unit, modifier: Modifier = Modifier) {
var showDeleteLogsDialog by remember { mutableStateOf(false) }
IconButton(onClick = { showDeleteLogsDialog = true }, modifier = modifier.padding(4.dp)) {
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(R.string.debug_clear))
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(Res.string.debug_clear))
}
if (showDeleteLogsDialog) {
SimpleAlertDialog(
title = R.string.debug_clear,
text = R.string.debug_clear_logs_confirm,
title = Res.string.debug_clear,
text = Res.string.debug_clear_logs_confirm,
onConfirm = {
showDeleteLogsDialog = false
deleteLogs()
@ -397,14 +397,18 @@ private suspend fun exportAllLogsToUri(context: Context, targetUri: Uri, logs: L
} ?: run { throw IOException("Unable to open output stream for URI: $targetUri") }
withContext(Dispatchers.Main) {
Toast.makeText(context, context.getString(R.string.debug_export_success, logs.size), Toast.LENGTH_LONG)
Toast.makeText(
context,
context.getString(Res.string.debug_export_success, logs.size),
Toast.LENGTH_LONG,
)
.show()
}
} catch (e: IOException) {
withContext(Dispatchers.Main) {
Toast.makeText(
context,
context.getString(R.string.debug_export_failed, e.message ?: ""),
context.getString(Res.string.debug_export_failed, e.message ?: ""),
Toast.LENGTH_LONG,
)
.show()
@ -426,7 +430,7 @@ private fun DecodedPayloadBlock(
Column(modifier = modifier) {
Text(
text = stringResource(R.string.debug_decoded_payload),
text = stringResource(Res.string.debug_decoded_payload),
style = commonTextStyle,
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp),
)
@ -595,11 +599,11 @@ private fun DebugMenuActionsPreview() {
IconButton(onClick = { /* Preview only */ }, modifier = Modifier.padding(4.dp)) {
Icon(
imageVector = Icons.Outlined.FileDownload,
contentDescription = stringResource(R.string.debug_logs_export),
contentDescription = stringResource(Res.string.debug_logs_export),
)
}
IconButton(onClick = { /* Preview only */ }, modifier = Modifier.padding(4.dp)) {
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(R.string.debug_clear))
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(Res.string.debug_clear))
}
}
}
@ -637,7 +641,7 @@ private fun DebugScreenEmptyPreview() {
Text(text = "Filters", style = TextStyle(fontWeight = FontWeight.Bold))
Icon(
imageVector = Icons.TwoTone.FilterAltOff,
contentDescription = stringResource(R.string.debug_filters),
contentDescription = stringResource(Res.string.debug_filters),
)
}
}

View file

@ -56,8 +56,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import org.meshtastic.core.strings.R
import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog
import org.meshtastic.core.strings.R as Res
@Composable
fun DebugCustomFilterInput(
@ -95,7 +95,7 @@ fun DebugCustomFilterInput(
},
enabled = customFilterText.isNotBlank(),
) {
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.debug_filter_add))
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(Res.string.debug_filter_add))
}
}
}
@ -144,7 +144,7 @@ internal fun DebugPresetFilters(
if (filter in filterTexts) {
Icon(
imageVector = Icons.Filled.Done,
contentDescription = stringResource(R.string.debug_filter_included),
contentDescription = stringResource(Res.string.debug_filter_included),
)
}
},
@ -173,7 +173,10 @@ internal fun DebugFilterBar(
Box {
TextButton(onClick = { showFilterMenu = !showFilterMenu }) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = stringResource(R.string.debug_filters), style = TextStyle(fontWeight = FontWeight.Bold))
Text(
text = stringResource(Res.string.debug_filters),
style = TextStyle(fontWeight = FontWeight.Bold),
)
Icon(
imageVector =
if (filterTexts.isNotEmpty()) {
@ -181,7 +184,7 @@ internal fun DebugFilterBar(
} else {
Icons.TwoTone.FilterAltOff
},
contentDescription = stringResource(R.string.debug_filters),
contentDescription = stringResource(Res.string.debug_filters),
)
}
}
@ -231,7 +234,7 @@ internal fun DebugActiveFilters(
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(R.string.debug_active_filters),
text = stringResource(Res.string.debug_active_filters),
style = TextStyle(fontWeight = FontWeight.Bold),
)
TextButton(
@ -247,16 +250,16 @@ internal fun DebugActiveFilters(
) {
Text(
if (filterMode == FilterMode.OR) {
stringResource(R.string.match_any)
stringResource(Res.string.match_any)
} else {
stringResource(R.string.match_all)
stringResource(Res.string.match_all)
},
)
}
IconButton(onClick = { onFilterTextsChange(emptyList()) }) {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = stringResource(R.string.debug_filter_clear),
contentDescription = stringResource(Res.string.debug_filter_clear),
)
}
}

View file

@ -53,11 +53,11 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog
import org.meshtastic.feature.settings.debugging.LogSearchManager.SearchMatch
import org.meshtastic.feature.settings.debugging.LogSearchManager.SearchState
import org.meshtastic.core.strings.R as Res
@Composable
internal fun DebugSearchNavigation(
@ -79,14 +79,14 @@ internal fun DebugSearchNavigation(
IconButton(onClick = onPreviousMatch, enabled = searchState.hasMatches, modifier = Modifier.size(32.dp)) {
Icon(
imageVector = Icons.Default.KeyboardArrowUp,
contentDescription = stringResource(R.string.debug_search_prev),
contentDescription = stringResource(Res.string.debug_search_prev),
modifier = Modifier.size(16.dp),
)
}
IconButton(onClick = onNextMatch, enabled = searchState.hasMatches, modifier = Modifier.size(32.dp)) {
Icon(
imageVector = Icons.Default.KeyboardArrowDown,
contentDescription = stringResource(R.string.debug_search_next),
contentDescription = stringResource(Res.string.debug_search_next),
modifier = Modifier.size(16.dp),
)
}
@ -106,7 +106,7 @@ internal fun DebugSearchBar(
value = searchState.searchText,
onValueChange = onSearchTextChange,
modifier = modifier.then(Modifier.padding(end = 8.dp)),
placeholder = { Text(stringResource(R.string.debug_default_search)) },
placeholder = { Text(stringResource(Res.string.debug_default_search)) },
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions =
@ -132,7 +132,7 @@ internal fun DebugSearchBar(
IconButton(onClick = onClearSearch, modifier = Modifier.size(32.dp)) {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = stringResource(R.string.debug_search_clear),
contentDescription = stringResource(Res.string.debug_search_clear),
modifier = Modifier.size(16.dp),
)
}
@ -188,7 +188,7 @@ internal fun DebugSearchState(
IconButton(onClick = onExport, modifier = Modifier) {
Icon(
imageVector = Icons.Outlined.FileDownload,
contentDescription = stringResource(R.string.debug_logs_export),
contentDescription = stringResource(Res.string.debug_logs_export),
modifier = Modifier.size(24.dp),
)
}

View file

@ -32,57 +32,57 @@ import androidx.compose.material.icons.filled.Wifi
import androidx.compose.ui.graphics.vector.ImageVector
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
import org.meshtastic.core.strings.R
import org.meshtastic.proto.AdminProtos
import org.meshtastic.proto.MeshProtos.DeviceMetadata
import org.meshtastic.core.strings.R as Res
enum class ConfigRoute(@StringRes val title: Int, val route: Route, val icon: ImageVector?, val type: Int = 0) {
USER(R.string.user, SettingsRoutes.User, Icons.Default.Person, 0),
CHANNELS(R.string.channels, SettingsRoutes.ChannelConfig, Icons.AutoMirrored.Default.List, 0),
USER(Res.string.user, SettingsRoutes.User, Icons.Default.Person, 0),
CHANNELS(Res.string.channels, SettingsRoutes.ChannelConfig, Icons.AutoMirrored.Default.List, 0),
DEVICE(
R.string.device,
Res.string.device,
SettingsRoutes.Device,
Icons.Default.Router,
AdminProtos.AdminMessage.ConfigType.DEVICE_CONFIG_VALUE,
),
POSITION(
R.string.position,
Res.string.position,
SettingsRoutes.Position,
Icons.Default.LocationOn,
AdminProtos.AdminMessage.ConfigType.POSITION_CONFIG_VALUE,
),
POWER(
R.string.power,
Res.string.power,
SettingsRoutes.Power,
Icons.Default.Power,
AdminProtos.AdminMessage.ConfigType.POWER_CONFIG_VALUE,
),
NETWORK(
R.string.network,
Res.string.network,
SettingsRoutes.Network,
Icons.Default.Wifi,
AdminProtos.AdminMessage.ConfigType.NETWORK_CONFIG_VALUE,
),
DISPLAY(
R.string.display,
Res.string.display,
SettingsRoutes.Display,
Icons.Default.DisplaySettings,
AdminProtos.AdminMessage.ConfigType.DISPLAY_CONFIG_VALUE,
),
LORA(
R.string.lora,
Res.string.lora,
SettingsRoutes.LoRa,
Icons.Default.CellTower,
AdminProtos.AdminMessage.ConfigType.LORA_CONFIG_VALUE,
),
BLUETOOTH(
R.string.bluetooth,
Res.string.bluetooth,
SettingsRoutes.Bluetooth,
Icons.Default.Bluetooth,
AdminProtos.AdminMessage.ConfigType.BLUETOOTH_CONFIG_VALUE,
),
SECURITY(
R.string.security,
Res.string.security,
SettingsRoutes.Security,
Icons.Default.Security,
AdminProtos.AdminMessage.ConfigType.SECURITY_CONFIG_VALUE,

View file

@ -35,85 +35,85 @@ import androidx.compose.material.icons.filled.Usb
import androidx.compose.ui.graphics.vector.ImageVector
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
import org.meshtastic.core.strings.R
import org.meshtastic.proto.AdminProtos
import org.meshtastic.proto.MeshProtos.DeviceMetadata
import org.meshtastic.core.strings.R as Res
enum class ModuleRoute(@StringRes val title: Int, val route: Route, val icon: ImageVector?, val type: Int = 0) {
MQTT(
R.string.mqtt,
Res.string.mqtt,
SettingsRoutes.MQTT,
Icons.Default.Cloud,
AdminProtos.AdminMessage.ModuleConfigType.MQTT_CONFIG_VALUE,
),
SERIAL(
R.string.serial,
Res.string.serial,
SettingsRoutes.Serial,
Icons.Default.Usb,
AdminProtos.AdminMessage.ModuleConfigType.SERIAL_CONFIG_VALUE,
),
EXT_NOTIFICATION(
R.string.external_notification,
Res.string.external_notification,
SettingsRoutes.ExtNotification,
Icons.Default.Notifications,
AdminProtos.AdminMessage.ModuleConfigType.EXTNOTIF_CONFIG_VALUE,
),
STORE_FORWARD(
R.string.store_forward,
Res.string.store_forward,
SettingsRoutes.StoreForward,
Icons.AutoMirrored.Default.Forward,
AdminProtos.AdminMessage.ModuleConfigType.STOREFORWARD_CONFIG_VALUE,
),
RANGE_TEST(
R.string.range_test,
Res.string.range_test,
SettingsRoutes.RangeTest,
Icons.Default.Speed,
AdminProtos.AdminMessage.ModuleConfigType.RANGETEST_CONFIG_VALUE,
),
TELEMETRY(
R.string.telemetry,
Res.string.telemetry,
SettingsRoutes.Telemetry,
Icons.Default.DataUsage,
AdminProtos.AdminMessage.ModuleConfigType.TELEMETRY_CONFIG_VALUE,
),
CANNED_MESSAGE(
R.string.canned_message,
Res.string.canned_message,
SettingsRoutes.CannedMessage,
Icons.AutoMirrored.Default.Message,
AdminProtos.AdminMessage.ModuleConfigType.CANNEDMSG_CONFIG_VALUE,
),
AUDIO(
R.string.audio,
Res.string.audio,
SettingsRoutes.Audio,
Icons.AutoMirrored.Default.VolumeUp,
AdminProtos.AdminMessage.ModuleConfigType.AUDIO_CONFIG_VALUE,
),
REMOTE_HARDWARE(
R.string.remote_hardware,
Res.string.remote_hardware,
SettingsRoutes.RemoteHardware,
Icons.Default.SettingsRemote,
AdminProtos.AdminMessage.ModuleConfigType.REMOTEHARDWARE_CONFIG_VALUE,
),
NEIGHBOR_INFO(
R.string.neighbor_info,
Res.string.neighbor_info,
SettingsRoutes.NeighborInfo,
Icons.Default.People,
AdminProtos.AdminMessage.ModuleConfigType.NEIGHBORINFO_CONFIG_VALUE,
),
AMBIENT_LIGHTING(
R.string.ambient_lighting,
Res.string.ambient_lighting,
SettingsRoutes.AmbientLighting,
Icons.Default.LightMode,
AdminProtos.AdminMessage.ModuleConfigType.AMBIENTLIGHTING_CONFIG_VALUE,
),
DETECTION_SENSOR(
R.string.detection_sensor,
Res.string.detection_sensor,
SettingsRoutes.DetectionSensor,
Icons.Default.Sensors,
AdminProtos.AdminMessage.ModuleConfigType.DETECTIONSENSOR_CONFIG_VALUE,
),
PAXCOUNTER(
R.string.paxcounter,
Res.string.paxcounter,
SettingsRoutes.Paxcounter,
Icons.Default.PermScanWifi,
AdminProtos.AdminMessage.ModuleConfigType.PAXCOUNTER_CONFIG_VALUE,

View file

@ -47,8 +47,8 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import org.meshtastic.core.database.entity.NodeEntity
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.core.strings.R as Res
/**
* Composable screen for cleaning the node database. Allows users to specify criteria for deleting nodes. The list of
@ -75,8 +75,8 @@ fun CleanNodeDatabaseScreen(viewModel: CleanNodeDatabaseViewModel = hiltViewMode
}
Column(modifier = Modifier.padding(16.dp).verticalScroll(rememberScrollState())) {
Text(stringResource(R.string.clean_node_database_title))
Text(stringResource(R.string.clean_node_database_description), style = MaterialTheme.typography.bodySmall)
Text(stringResource(Res.string.clean_node_database_title))
Text(stringResource(Res.string.clean_node_database_description), style = MaterialTheme.typography.bodySmall)
Spacer(modifier = Modifier.height(16.dp))
DaysThresholdFilter(
@ -100,7 +100,7 @@ fun CleanNodeDatabaseScreen(viewModel: CleanNodeDatabaseViewModel = hiltViewMode
modifier = Modifier.fillMaxWidth(),
enabled = nodesToDelete.isNotEmpty(),
) {
Text(stringResource(R.string.clean_now))
Text(stringResource(Res.string.clean_now))
}
}
}
@ -129,7 +129,7 @@ private fun DaysThresholdFilter(olderThanDays: Float, onlyUnknownNodes: Boolean,
Column(modifier = Modifier.fillMaxWidth()) {
Text(
modifier = Modifier.padding(bottom = 8.dp),
text = stringResource(R.string.clean_nodes_older_than, olderThanDays.toInt()),
text = stringResource(Res.string.clean_nodes_older_than, olderThanDays.toInt()),
)
Slider(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
@ -150,7 +150,7 @@ private fun DaysThresholdFilter(olderThanDays: Float, onlyUnknownNodes: Boolean,
@Composable
private fun UnknownNodesFilter(onlyUnknownNodes: Boolean, onCheckedChanged: (Boolean) -> Unit) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(R.string.clean_unknown_nodes))
Text(stringResource(Res.string.clean_unknown_nodes))
Spacer(Modifier.weight(1f))
Switch(checked = onlyUnknownNodes, onCheckedChange = onCheckedChanged)
}
@ -164,7 +164,7 @@ private fun UnknownNodesFilter(onlyUnknownNodes: Boolean, onCheckedChanged: (Boo
@Composable
private fun NodesDeletionPreview(nodesToDelete: List<NodeEntity>) {
Text(
stringResource(R.string.nodes_queued_for_deletion, nodesToDelete.size),
stringResource(Res.string.nodes_queued_for_deletion, nodesToDelete.size),
modifier = Modifier.padding(bottom = 16.dp),
)
FlowRow(
@ -189,9 +189,9 @@ private fun NodesDeletionPreview(nodesToDelete: List<NodeEntity>) {
private fun ConfirmationDialog(nodesToDeleteCount: Int, onConfirm: () -> Unit, onDismiss: () -> Unit) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(stringResource(R.string.are_you_sure)) },
text = { Text(stringResource(R.string.clean_node_database_confirmation, nodesToDeleteCount)) },
confirmButton = { Button(onClick = onConfirm) { Text(stringResource(R.string.clean_now)) } },
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } },
title = { Text(stringResource(Res.string.are_you_sure)) },
text = { Text(stringResource(Res.string.clean_node_database_confirmation, nodesToDeleteCount)) },
confirmButton = { Button(onClick = onConfirm) { Text(stringResource(Res.string.clean_now)) } },
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(Res.string.cancel)) } },
)
}

View file

@ -44,7 +44,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.ListItem
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.core.ui.theme.AppTheme
@ -52,6 +51,7 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.feature.settings.navigation.ConfigRoute
import org.meshtastic.feature.settings.navigation.ModuleRoute
import org.meshtastic.feature.settings.radio.component.WarningDialog
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
@ -76,7 +76,7 @@ fun RadioConfigItemList(
}
Column {
TitledCard(title = stringResource(R.string.radio_configuration)) {
TitledCard(title = stringResource(Res.string.radio_configuration)) {
if (isManaged) {
ManagedMessage()
}
@ -85,7 +85,7 @@ fun RadioConfigItemList(
}
}
TitledCard(title = stringResource(R.string.device_configuration), modifier = Modifier.padding(top = 16.dp)) {
TitledCard(title = stringResource(Res.string.device_configuration), modifier = Modifier.padding(top = 16.dp)) {
if (isManaged) {
ManagedMessage()
}
@ -94,7 +94,7 @@ fun RadioConfigItemList(
}
}
TitledCard(title = stringResource(R.string.module_settings), modifier = Modifier.padding(top = 16.dp)) {
TitledCard(title = stringResource(Res.string.module_settings), modifier = Modifier.padding(top = 16.dp)) {
if (isManaged) {
ManagedMessage()
}
@ -106,19 +106,19 @@ fun RadioConfigItemList(
}
if (state.isLocal) {
TitledCard(title = stringResource(R.string.backup_restore), modifier = Modifier.padding(top = 16.dp)) {
TitledCard(title = stringResource(Res.string.backup_restore), modifier = Modifier.padding(top = 16.dp)) {
if (isManaged) {
ManagedMessage()
}
ListItem(
text = stringResource(R.string.import_configuration),
text = stringResource(Res.string.import_configuration),
leadingIcon = Icons.Default.Download,
enabled = enabled,
onClick = onImport,
)
ListItem(
text = stringResource(R.string.export_configuration),
text = stringResource(Res.string.export_configuration),
leadingIcon = Icons.Default.Upload,
enabled = enabled,
onClick = onExport,
@ -126,7 +126,7 @@ fun RadioConfigItemList(
}
}
TitledCard(title = stringResource(R.string.administration), modifier = Modifier.padding(top = 16.dp)) {
TitledCard(title = stringResource(Res.string.administration), modifier = Modifier.padding(top = 16.dp)) {
AdminRoute.entries.forEach { route ->
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
@ -148,20 +148,20 @@ fun RadioConfigItemList(
}
}
TitledCard(title = stringResource(R.string.advanced_title), modifier = Modifier.padding(top = 16.dp)) {
TitledCard(title = stringResource(Res.string.advanced_title), modifier = Modifier.padding(top = 16.dp)) {
if (isManaged) {
ManagedMessage()
}
ListItem(
text = stringResource(R.string.clean_node_database_title),
text = stringResource(Res.string.clean_node_database_title),
leadingIcon = Icons.Rounded.CleaningServices,
enabled = enabled,
onClick = { onNavigate(SettingsRoutes.CleanNodeDb) },
)
ListItem(
text = stringResource(R.string.debug_panel),
text = stringResource(Res.string.debug_panel),
leadingIcon = Icons.Rounded.BugReport,
enabled = enabled,
onClick = { onNavigate(SettingsRoutes.DebugPanel) },
@ -170,10 +170,10 @@ fun RadioConfigItemList(
}
enum class AdminRoute(val icon: ImageVector, @StringRes val title: Int) {
REBOOT(Icons.Rounded.RestartAlt, R.string.reboot),
SHUTDOWN(Icons.Rounded.PowerSettingsNew, R.string.shutdown),
FACTORY_RESET(Icons.Rounded.Restore, R.string.factory_reset),
NODEDB_RESET(Icons.Rounded.Storage, R.string.nodedb_reset),
REBOOT(Icons.Rounded.RestartAlt, Res.string.reboot),
SHUTDOWN(Icons.Rounded.PowerSettingsNew, Res.string.shutdown),
FACTORY_RESET(Icons.Rounded.Restore, Res.string.factory_reset),
NODEDB_RESET(Icons.Rounded.Storage, Res.string.nodedb_reset),
}
@Preview(showBackground = true)
@ -189,7 +189,7 @@ private fun RadioSettingsScreenPreview() = AppTheme {
@Composable
private fun ManagedMessage() {
Text(
text = stringResource(R.string.message_device_managed),
text = stringResource(Res.string.message_device_managed),
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
color = MaterialTheme.colorScheme.StatusRed,
)

View file

@ -63,7 +63,6 @@ import org.meshtastic.core.proto.getChannelList
import org.meshtastic.core.service.ConnectionState
import org.meshtastic.core.service.IMeshService
import org.meshtastic.core.service.ServiceRepository
import org.meshtastic.core.strings.R
import org.meshtastic.feature.settings.navigation.ConfigRoute
import org.meshtastic.feature.settings.navigation.ModuleRoute
import org.meshtastic.feature.settings.util.UiText
@ -82,6 +81,7 @@ import org.meshtastic.proto.moduleConfig
import timber.log.Timber
import java.io.FileOutputStream
import javax.inject.Inject
import org.meshtastic.core.strings.R as Res
/** Data class that represents the current RadioConfig state. */
data class RadioConfigState(
@ -384,7 +384,7 @@ constructor(
AdminRoute.SHUTDOWN.name ->
with(radioConfigState.value) {
if (metadata != null && !metadata.canShutdown) {
sendError(R.string.cant_shutdown)
sendError(Res.string.cant_shutdown)
} else {
requestShutdown(destNum)
}

View file

@ -55,7 +55,6 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.model.Channel
import org.meshtastic.core.model.DeviceVersion
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.PreferenceFooter
import org.meshtastic.core.ui.component.dragContainer
import org.meshtastic.core.ui.component.dragDropItemsIndexed
@ -71,6 +70,7 @@ import org.meshtastic.feature.settings.radio.component.PacketResponseStateDialog
import org.meshtastic.proto.ChannelProtos.ChannelSettings
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig
import org.meshtastic.proto.channelSettings
import org.meshtastic.core.strings.R as Res
@Composable
fun ChannelConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
@ -81,7 +81,7 @@ fun ChannelConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
}
ChannelConfigScreen(
title = stringResource(R.string.channels),
title = stringResource(Res.string.channels),
onBack = onBack,
settingsList = state.channelList,
loraConfig = state.radioConfig.lora,
@ -164,7 +164,7 @@ private fun ChannelConfigScreen(
},
modifier = Modifier.padding(16.dp),
) {
Icon(Icons.TwoTone.Add, stringResource(R.string.add))
Icon(Icons.TwoTone.Add, stringResource(Res.string.add))
}
}
},
@ -186,7 +186,7 @@ private fun ChannelConfigScreen(
},
)
Text(
text = stringResource(R.string.press_and_drag),
text = stringResource(Res.string.press_and_drag),
fontSize = 11.sp,
modifier = Modifier.padding(start = 16.dp),
)
@ -221,13 +221,13 @@ private fun ChannelConfigScreen(
item {
PreferenceFooter(
enabled = enabled && isEditing,
negativeText = stringResource(R.string.cancel),
negativeText = stringResource(Res.string.cancel),
onNegativeClicked = {
focusManager.clearFocus()
settingsListInput.clear()
settingsListInput.addAll(settingsList)
},
positiveText = stringResource(R.string.send),
positiveText = stringResource(Res.string.send),
onPositiveClicked = {
focusManager.clearFocus()
onPositiveClicked(settingsListInput)
@ -294,7 +294,7 @@ private fun ChannelConfigScreenPreview() {
psk = Channel.default.settings.psk
name = Channel.default.name
},
channelSettings { name = stringResource(R.string.channel_name) },
channelSettings { name = stringResource(Res.string.channel_name) },
),
loraConfig = Channel.default.loraConfig,
firmwareVersion = "1.3.2",

View file

@ -30,7 +30,6 @@ 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.component.ChannelItem
import org.meshtastic.core.ui.component.SecurityIcon
import org.meshtastic.core.ui.theme.AppTheme
@ -38,6 +37,7 @@ import org.meshtastic.proto.ChannelProtos.ChannelSettings
import org.meshtastic.proto.ConfigKt.loRaConfig
import org.meshtastic.proto.ConfigProtos.Config.LoRaConfig
import org.meshtastic.proto.channelSettings
import org.meshtastic.core.strings.R as Res
@Composable
internal fun ChannelCard(
@ -76,7 +76,7 @@ internal fun ChannelCard(
IconButton(onClick = { onDeleteClick() }) {
Icon(
imageVector = Icons.TwoTone.Close,
contentDescription = stringResource(R.string.delete),
contentDescription = stringResource(Res.string.delete),
modifier = Modifier.wrapContentSize(),
)
}

View file

@ -28,9 +28,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.PreferenceCategory
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
internal fun ChannelConfigHeader(frequency: Float, slot: Int) {
@ -39,10 +39,10 @@ internal fun ChannelConfigHeader(frequency: Float, slot: Int) {
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
PreferenceCategory(text = stringResource(R.string.channels))
PreferenceCategory(text = stringResource(Res.string.channels))
Column {
Text(text = "${stringResource(R.string.freq)}: ${frequency}MHz", fontSize = 11.sp)
Text(text = "${stringResource(R.string.slot)}: $slot", fontSize = 11.sp)
Text(text = "${stringResource(Res.string.freq)}: ${frequency}MHz", fontSize = 11.sp)
Text(text = "${stringResource(Res.string.slot)}: $slot", fontSize = 11.sp)
}
}
}

View file

@ -46,7 +46,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.meshtastic.core.model.DeviceVersion
import org.meshtastic.core.strings.R
import org.meshtastic.core.strings.R as Res
/**
* At this firmware version periodic position sharing on a secondary channel was implemented. To enable this feature the
@ -62,18 +62,18 @@ internal enum class ChannelIcons(
) {
LOCATION(
icon = Icons.Filled.LocationOn,
descriptionResId = R.string.location_sharing,
additionalInfoResId = R.string.periodic_position_broadcast,
descriptionResId = Res.string.location_sharing,
additionalInfoResId = Res.string.periodic_position_broadcast,
),
UPLINK(
icon = Icons.Filled.CloudUpload,
descriptionResId = R.string.uplink_enabled,
additionalInfoResId = R.string.uplink_feature_description,
descriptionResId = Res.string.uplink_enabled,
additionalInfoResId = Res.string.uplink_feature_description,
),
DOWNLINK(
icon = Icons.Filled.CloudDownload,
descriptionResId = R.string.downlink_enabled,
additionalInfoResId = R.string.downlink_feature_description,
descriptionResId = Res.string.downlink_enabled,
additionalInfoResId = Res.string.downlink_feature_description,
),
}
@ -84,15 +84,15 @@ internal fun ChannelLegend(onClick: () -> Unit) {
horizontalArrangement = Arrangement.SpaceEvenly,
) {
Row {
Icon(imageVector = Icons.Filled.Info, contentDescription = stringResource(R.string.info))
Icon(imageVector = Icons.Filled.Info, contentDescription = stringResource(Res.string.info))
Text(
text = stringResource(R.string.primary),
text = stringResource(Res.string.primary),
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(start = 16.dp),
)
}
Text(
text = stringResource(R.string.secondary),
text = stringResource(Res.string.secondary),
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(start = 16.dp),
)
@ -104,29 +104,29 @@ internal fun ChannelLegendDialog(firmwareVersion: DeviceVersion, onDismiss: () -
AlertDialog(
modifier = Modifier.fillMaxSize(),
onDismissRequest = onDismiss,
title = { Text(stringResource(R.string.channel_features)) },
title = { Text(stringResource(Res.string.channel_features)) },
text = {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
Text(
text = stringResource(R.string.primary),
text = stringResource(Res.string.primary),
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.titleMedium,
)
Text(
text = "- ${stringResource(R.string.primary_channel_feature)}",
text = "- ${stringResource(Res.string.primary_channel_feature)}",
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.bodyMedium,
)
Text(
text = stringResource(R.string.secondary),
text = stringResource(Res.string.secondary),
color = MaterialTheme.colorScheme.onBackground,
style = MaterialTheme.typography.titleMedium,
)
Text(
text = "- ${stringResource(R.string.secondary_no_telemetry)}",
text = "- ${stringResource(Res.string.secondary_no_telemetry)}",
color = MaterialTheme.colorScheme.onBackground,
style = MaterialTheme.typography.bodyMedium,
)
@ -134,9 +134,9 @@ internal fun ChannelLegendDialog(firmwareVersion: DeviceVersion, onDismiss: () -
text =
if (firmwareVersion >= DeviceVersion(asString = SECONDARY_CHANNEL_EPOCH)) {
/* 2.6.10+ */
"- ${stringResource(R.string.secondary_channel_position_feature)}"
"- ${stringResource(Res.string.secondary_channel_position_feature)}"
} else {
"- ${stringResource(R.string.manual_position_request)}"
"- ${stringResource(Res.string.manual_position_request)}"
},
color = MaterialTheme.colorScheme.onBackground,
style = MaterialTheme.typography.bodyMedium,
@ -150,7 +150,7 @@ internal fun ChannelLegendDialog(firmwareVersion: DeviceVersion, onDismiss: () -
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
TextButton(onClick = onDismiss) { Text(stringResource(R.string.security_icon_help_dismiss)) }
TextButton(onClick = onDismiss) { Text(stringResource(Res.string.security_icon_help_dismiss)) }
}
},
)
@ -158,7 +158,7 @@ internal fun ChannelLegendDialog(firmwareVersion: DeviceVersion, onDismiss: () -
@Composable
private fun IconDefinitions() {
Text(text = stringResource(R.string.icon_meanings), style = MaterialTheme.typography.titleLarge)
Text(text = stringResource(Res.string.icon_meanings), style = MaterialTheme.typography.titleLarge)
ChannelIcons.entries.forEach { icon ->
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = icon.icon, contentDescription = stringResource(icon.descriptionResId))

View file

@ -43,7 +43,6 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.meshtastic.core.model.Channel
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.EditBase64Preference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.PositionPrecisionPreference
@ -51,6 +50,7 @@ import org.meshtastic.core.ui.component.SwitchPreference
import org.meshtastic.proto.ChannelProtos
import org.meshtastic.proto.channelSettings
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@Composable
@ -59,7 +59,7 @@ fun EditChannelDialog(
onAddClick: (ChannelProtos.ChannelSettings) -> Unit,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
modemPresetName: String = stringResource(R.string.default_),
modemPresetName: String = stringResource(Res.string.default_),
) {
val focusManager = LocalFocusManager.current
var isFocused by remember { mutableStateOf(false) }
@ -72,7 +72,7 @@ fun EditChannelDialog(
text = {
Column(modifier.fillMaxWidth()) {
EditTextPreference(
title = stringResource(R.string.channel_name),
title = stringResource(Res.string.channel_name),
value = if (isFocused) channelInput.name else channelInput.name.ifEmpty { modemPresetName },
maxSize = 11, // name max_size:12
enabled = true,
@ -105,7 +105,7 @@ fun EditChannelDialog(
)
SwitchPreference(
title = stringResource(R.string.uplink_enabled),
title = stringResource(Res.string.uplink_enabled),
checked = channelInput.uplinkEnabled,
enabled = true,
onCheckedChange = { channelInput = channelInput.copy { uplinkEnabled = it } },
@ -113,7 +113,7 @@ fun EditChannelDialog(
)
SwitchPreference(
title = stringResource(R.string.downlink_enabled),
title = stringResource(Res.string.downlink_enabled),
checked = channelInput.downlinkEnabled,
enabled = true,
onCheckedChange = { channelInput = channelInput.copy { downlinkEnabled = it } },
@ -136,10 +136,10 @@ fun EditChannelDialog(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
TextButton(modifier = modifier.weight(1f), onClick = onDismissRequest) {
Text(stringResource(R.string.cancel))
Text(stringResource(Res.string.cancel))
}
Button(modifier = modifier.weight(1f), onClick = { onAddClick(channelInput) }, enabled = true) {
Text(stringResource(R.string.save))
Text(stringResource(Res.string.save))
}
}
},

View file

@ -26,13 +26,13 @@ import androidx.compose.ui.platform.LocalFocusManager
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.EditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import org.meshtastic.core.strings.R as Res
@Composable
fun AmbientLightingConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -42,7 +42,7 @@ fun AmbientLightingConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
val focusManager = LocalFocusManager.current
RadioConfigScreenList(
title = stringResource(R.string.ambient_lighting),
title = stringResource(Res.string.ambient_lighting),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -54,9 +54,9 @@ fun AmbientLightingConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
},
) {
item {
TitledCard(title = stringResource(R.string.ambient_lighting_config)) {
TitledCard(title = stringResource(Res.string.ambient_lighting_config)) {
SwitchPreference(
title = stringResource(R.string.led_state),
title = stringResource(Res.string.led_state),
checked = formState.value.ledState,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { ledState = it } },
@ -64,21 +64,21 @@ fun AmbientLightingConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.current),
title = stringResource(Res.string.current),
value = formState.value.current,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { formState.value = formState.value.copy { current = it } },
)
EditTextPreference(
title = stringResource(R.string.red),
title = stringResource(Res.string.red),
value = formState.value.red,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { formState.value = formState.value.copy { red = it } },
)
EditTextPreference(
title = stringResource(R.string.green),
title = stringResource(Res.string.green),
value = formState.value.green,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -86,7 +86,7 @@ fun AmbientLightingConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
)
EditTextPreference(
title = stringResource(R.string.blue),
title = stringResource(Res.string.blue),
value = formState.value.blue,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),

View file

@ -26,7 +26,6 @@ import androidx.compose.ui.platform.LocalFocusManager
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.DropDownPreference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
@ -35,6 +34,7 @@ import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig.AudioConfig
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import org.meshtastic.core.strings.R as Res
@Composable
fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -44,7 +44,7 @@ fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
val focusManager = LocalFocusManager.current
RadioConfigScreenList(
title = stringResource(R.string.audio),
title = stringResource(Res.string.audio),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -56,9 +56,9 @@ fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
},
) {
item {
TitledCard(title = stringResource(R.string.audio_config)) {
TitledCard(title = stringResource(Res.string.audio_config)) {
SwitchPreference(
title = stringResource(R.string.codec_2_enabled),
title = stringResource(Res.string.codec_2_enabled),
checked = formState.value.codec2Enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { codec2Enabled = it } },
@ -66,14 +66,14 @@ fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.ptt_pin),
title = stringResource(Res.string.ptt_pin),
value = formState.value.pttPin,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { formState.value = formState.value.copy { pttPin = it } },
)
DropDownPreference(
title = stringResource(R.string.codec2_sample_rate),
title = stringResource(Res.string.codec2_sample_rate),
enabled = state.connected,
items =
AudioConfig.Audio_Baud.entries
@ -84,28 +84,28 @@ fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.i2s_word_select),
title = stringResource(Res.string.i2s_word_select),
value = formState.value.i2SWs,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { formState.value = formState.value.copy { i2SWs = it } },
)
EditTextPreference(
title = stringResource(R.string.i2s_data_in),
title = stringResource(Res.string.i2s_data_in),
value = formState.value.i2SSd,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { formState.value = formState.value.copy { i2SSd = it } },
)
EditTextPreference(
title = stringResource(R.string.i2s_data_out),
title = stringResource(Res.string.i2s_data_out),
value = formState.value.i2SDin,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { formState.value = formState.value.copy { i2SDin = it } },
)
EditTextPreference(
title = stringResource(R.string.i2s_clock),
title = stringResource(Res.string.i2s_clock),
value = formState.value.i2SSck,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),

View file

@ -26,7 +26,6 @@ import androidx.compose.ui.platform.LocalFocusManager
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.DropDownPreference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
@ -35,6 +34,7 @@ import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.proto.ConfigProtos.Config.BluetoothConfig
import org.meshtastic.proto.config
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@Composable
fun BluetoothConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -44,7 +44,7 @@ fun BluetoothConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onB
val focusManager = LocalFocusManager.current
RadioConfigScreenList(
title = stringResource(R.string.bluetooth),
title = stringResource(Res.string.bluetooth),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -56,9 +56,9 @@ fun BluetoothConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onB
},
) {
item {
TitledCard(title = stringResource(R.string.bluetooth_config)) {
TitledCard(title = stringResource(Res.string.bluetooth_config)) {
SwitchPreference(
title = stringResource(R.string.bluetooth_enabled),
title = stringResource(Res.string.bluetooth_enabled),
checked = formState.value.enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
@ -66,7 +66,7 @@ fun BluetoothConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onB
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.pairing_mode),
title = stringResource(Res.string.pairing_mode),
enabled = state.connected,
items =
BluetoothConfig.PairingMode.entries
@ -77,7 +77,7 @@ fun BluetoothConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onB
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.fixed_pin),
title = stringResource(Res.string.fixed_pin),
value = formState.value.fixedPin,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),

View file

@ -32,7 +32,6 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.DropDownPreference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
@ -41,6 +40,7 @@ import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig.CannedMessageConfig
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import org.meshtastic.core.strings.R as Res
@Composable
fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -52,7 +52,7 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
val focusManager = LocalFocusManager.current
RadioConfigScreenList(
title = stringResource(R.string.canned_message),
title = stringResource(Res.string.canned_message),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -69,9 +69,9 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
},
) {
item {
TitledCard(title = stringResource(R.string.canned_message_config)) {
TitledCard(title = stringResource(Res.string.canned_message_config)) {
SwitchPreference(
title = stringResource(R.string.canned_message_enabled),
title = stringResource(Res.string.canned_message_enabled),
checked = formState.value.enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
@ -79,7 +79,7 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.rotary_encoder_1_enabled),
title = stringResource(Res.string.rotary_encoder_1_enabled),
checked = formState.value.rotary1Enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { rotary1Enabled = it } },
@ -87,28 +87,28 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.gpio_pin_for_rotary_encoder_a_port),
title = stringResource(Res.string.gpio_pin_for_rotary_encoder_a_port),
value = formState.value.inputbrokerPinA,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { formState.value = formState.value.copy { inputbrokerPinA = it } },
)
EditTextPreference(
title = stringResource(R.string.gpio_pin_for_rotary_encoder_b_port),
title = stringResource(Res.string.gpio_pin_for_rotary_encoder_b_port),
value = formState.value.inputbrokerPinB,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { formState.value = formState.value.copy { inputbrokerPinB = it } },
)
EditTextPreference(
title = stringResource(R.string.gpio_pin_for_rotary_encoder_press_port),
title = stringResource(Res.string.gpio_pin_for_rotary_encoder_press_port),
value = formState.value.inputbrokerPinPress,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
onValueChanged = { formState.value = formState.value.copy { inputbrokerPinPress = it } },
)
DropDownPreference(
title = stringResource(R.string.generate_input_event_on_press),
title = stringResource(Res.string.generate_input_event_on_press),
enabled = state.connected,
items =
CannedMessageConfig.InputEventChar.entries
@ -119,7 +119,7 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.generate_input_event_on_cw),
title = stringResource(Res.string.generate_input_event_on_cw),
enabled = state.connected,
items =
CannedMessageConfig.InputEventChar.entries
@ -130,7 +130,7 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.generate_input_event_on_ccw),
title = stringResource(Res.string.generate_input_event_on_ccw),
enabled = state.connected,
items =
CannedMessageConfig.InputEventChar.entries
@ -141,7 +141,7 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.up_down_select_input_enabled),
title = stringResource(Res.string.up_down_select_input_enabled),
checked = formState.value.updown1Enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { updown1Enabled = it } },
@ -149,7 +149,7 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.allow_input_source),
title = stringResource(Res.string.allow_input_source),
value = formState.value.allowInputSource,
maxSize = 63, // allow_input_source max_size:16
enabled = state.connected,
@ -160,7 +160,7 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
onValueChanged = { formState.value = formState.value.copy { allowInputSource = it } },
)
SwitchPreference(
title = stringResource(R.string.send_bell),
title = stringResource(Res.string.send_bell),
checked = formState.value.sendBell,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { sendBell = it } },
@ -168,7 +168,7 @@ fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.messages),
title = stringResource(Res.string.messages),
value = messagesInput,
maxSize = 200, // messages max_size:201
enabled = state.connected,

View file

@ -31,7 +31,6 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.DropDownPreference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
@ -43,6 +42,7 @@ import org.meshtastic.feature.settings.util.toDisplayString
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import org.meshtastic.core.strings.R as Res
@Composable
fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -53,7 +53,7 @@ fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
val context = LocalContext.current
RadioConfigScreenList(
title = stringResource(R.string.detection_sensor),
title = stringResource(Res.string.detection_sensor),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -65,9 +65,9 @@ fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
},
) {
item {
TitledCard(title = stringResource(R.string.detection_sensor_config)) {
TitledCard(title = stringResource(Res.string.detection_sensor_config)) {
SwitchPreference(
title = stringResource(R.string.detection_sensor_enabled),
title = stringResource(Res.string.detection_sensor_enabled),
checked = formState.value.enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
@ -78,7 +78,7 @@ fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
IntervalConfiguration.DETECTION_SENSOR_MINIMUM.allowedIntervals
}
DropDownPreference(
title = stringResource(R.string.minimum_broadcast_seconds),
title = stringResource(Res.string.minimum_broadcast_seconds),
selectedItem = formState.value.minimumBroadcastSecs.toLong(),
enabled = state.connected,
items = minimumBroadcastIntervals.map { it.value to it.toDisplayString(context = context) },
@ -87,7 +87,7 @@ fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
val stateBroadcastIntervals = remember { IntervalConfiguration.DETECTION_SENSOR_STATE.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.state_broadcast_seconds),
title = stringResource(Res.string.state_broadcast_seconds),
selectedItem = formState.value.stateBroadcastSecs.toLong(),
enabled = state.connected,
items = stateBroadcastIntervals.map { it.value to it.toDisplayString(context = context) },
@ -95,7 +95,7 @@ fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.send_bell_with_alert_message),
title = stringResource(Res.string.send_bell_with_alert_message),
checked = formState.value.sendBell,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { sendBell = it } },
@ -103,7 +103,7 @@ fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.friendly_name),
title = stringResource(Res.string.friendly_name),
value = formState.value.name,
maxSize = 19, // name max_size:20
enabled = state.connected,
@ -116,7 +116,7 @@ fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
HorizontalDivider()
val pins = remember { gpioPins }
DropDownPreference(
title = stringResource(R.string.gpio_pin_to_monitor),
title = stringResource(Res.string.gpio_pin_to_monitor),
items = pins,
selectedItem = formState.value.monitorPin,
enabled = state.connected,
@ -124,7 +124,7 @@ fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.detection_trigger_type),
title = stringResource(Res.string.detection_trigger_type),
enabled = state.connected,
items =
ModuleConfig.DetectionSensorConfig.TriggerType.entries
@ -135,7 +135,7 @@ fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.use_input_pullup_mode),
title = stringResource(Res.string.use_input_pullup_mode),
checked = formState.value.usePullup,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { usePullup = it } },

View file

@ -66,7 +66,6 @@ import androidx.compose.ui.text.input.KeyboardType
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.DropDownPreference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.InsetDivider
@ -80,35 +79,36 @@ import org.meshtastic.proto.ConfigProtos.Config.DeviceConfig
import org.meshtastic.proto.config
import org.meshtastic.proto.copy
import java.time.ZoneId
import org.meshtastic.core.strings.R as Res
private val DeviceConfig.Role.description: Int
get() =
when (this) {
DeviceConfig.Role.CLIENT -> R.string.role_client_desc
DeviceConfig.Role.CLIENT_MUTE -> R.string.role_client_mute_desc
DeviceConfig.Role.ROUTER -> R.string.role_router_desc
DeviceConfig.Role.ROUTER_CLIENT -> R.string.role_router_client_desc
DeviceConfig.Role.REPEATER -> R.string.role_repeater_desc
DeviceConfig.Role.TRACKER -> R.string.role_tracker_desc
DeviceConfig.Role.SENSOR -> R.string.role_sensor_desc
DeviceConfig.Role.TAK -> R.string.role_tak_desc
DeviceConfig.Role.CLIENT_HIDDEN -> R.string.role_client_hidden_desc
DeviceConfig.Role.LOST_AND_FOUND -> R.string.role_lost_and_found_desc
DeviceConfig.Role.TAK_TRACKER -> R.string.role_tak_tracker_desc
DeviceConfig.Role.ROUTER_LATE -> R.string.role_router_late_desc
else -> R.string.unrecognized
DeviceConfig.Role.CLIENT -> Res.string.role_client_desc
DeviceConfig.Role.CLIENT_MUTE -> Res.string.role_client_mute_desc
DeviceConfig.Role.ROUTER -> Res.string.role_router_desc
DeviceConfig.Role.ROUTER_CLIENT -> Res.string.role_router_client_desc
DeviceConfig.Role.REPEATER -> Res.string.role_repeater_desc
DeviceConfig.Role.TRACKER -> Res.string.role_tracker_desc
DeviceConfig.Role.SENSOR -> Res.string.role_sensor_desc
DeviceConfig.Role.TAK -> Res.string.role_tak_desc
DeviceConfig.Role.CLIENT_HIDDEN -> Res.string.role_client_hidden_desc
DeviceConfig.Role.LOST_AND_FOUND -> Res.string.role_lost_and_found_desc
DeviceConfig.Role.TAK_TRACKER -> Res.string.role_tak_tracker_desc
DeviceConfig.Role.ROUTER_LATE -> Res.string.role_router_late_desc
else -> Res.string.unrecognized
}
private val DeviceConfig.RebroadcastMode.description: Int
get() =
when (this) {
DeviceConfig.RebroadcastMode.ALL -> R.string.rebroadcast_mode_all_desc
DeviceConfig.RebroadcastMode.ALL_SKIP_DECODING -> R.string.rebroadcast_mode_all_skip_decoding_desc
DeviceConfig.RebroadcastMode.LOCAL_ONLY -> R.string.rebroadcast_mode_local_only_desc
DeviceConfig.RebroadcastMode.KNOWN_ONLY -> R.string.rebroadcast_mode_known_only_desc
DeviceConfig.RebroadcastMode.NONE -> R.string.rebroadcast_mode_none_desc
DeviceConfig.RebroadcastMode.CORE_PORTNUMS_ONLY -> R.string.rebroadcast_mode_core_portnums_only_desc
else -> R.string.unrecognized
DeviceConfig.RebroadcastMode.ALL -> Res.string.rebroadcast_mode_all_desc
DeviceConfig.RebroadcastMode.ALL_SKIP_DECODING -> Res.string.rebroadcast_mode_all_skip_decoding_desc
DeviceConfig.RebroadcastMode.LOCAL_ONLY -> Res.string.rebroadcast_mode_local_only_desc
DeviceConfig.RebroadcastMode.KNOWN_ONLY -> Res.string.rebroadcast_mode_known_only_desc
DeviceConfig.RebroadcastMode.NONE -> Res.string.rebroadcast_mode_none_desc
DeviceConfig.RebroadcastMode.CORE_PORTNUMS_ONLY -> Res.string.rebroadcast_mode_core_portnums_only_desc
else -> Res.string.unrecognized
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@ -132,7 +132,7 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
val focusManager = LocalFocusManager.current
val context = LocalContext.current
RadioConfigScreenList(
title = stringResource(R.string.device),
title = stringResource(Res.string.device),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -144,9 +144,9 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
},
) {
item {
TitledCard(title = stringResource(R.string.options)) {
TitledCard(title = stringResource(Res.string.options)) {
DropDownPreference(
title = stringResource(R.string.role),
title = stringResource(Res.string.role),
enabled = state.connected,
selectedItem = formState.value.role,
onItemSelected = { selectedRole = it },
@ -156,7 +156,7 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.rebroadcast_mode),
title = stringResource(Res.string.rebroadcast_mode),
enabled = state.connected,
selectedItem = formState.value.rebroadcastMode,
onItemSelected = { formState.value = formState.value.copy { rebroadcastMode = it } },
@ -167,7 +167,7 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
val nodeInfoBroadcastIntervals = remember { IntervalConfiguration.NODE_INFO_BROADCAST.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.nodeinfo_broadcast_interval),
title = stringResource(Res.string.nodeinfo_broadcast_interval),
selectedItem = formState.value.nodeInfoBroadcastSecs.toLong(),
enabled = state.connected,
items = nodeInfoBroadcastIntervals.map { it.value to it.toDisplayString(context = context) },
@ -177,10 +177,10 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
}
item {
TitledCard(title = stringResource(R.string.hardware)) {
TitledCard(title = stringResource(Res.string.hardware)) {
SwitchPreference(
title = stringResource(R.string.double_tap_as_button_press),
summary = stringResource(R.string.config_device_doubleTapAsButtonPress_summary),
title = stringResource(Res.string.double_tap_as_button_press),
summary = stringResource(Res.string.config_device_doubleTapAsButtonPress_summary),
checked = formState.value.doubleTapAsButtonPress,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { doubleTapAsButtonPress = it } },
@ -190,8 +190,8 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
InsetDivider()
SwitchPreference(
title = stringResource(R.string.triple_click_adhoc_ping),
summary = stringResource(R.string.config_device_tripleClickAsAdHocPing_summary),
title = stringResource(Res.string.triple_click_adhoc_ping),
summary = stringResource(Res.string.config_device_tripleClickAsAdHocPing_summary),
checked = !formState.value.disableTripleClick,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { disableTripleClick = !it } },
@ -201,8 +201,8 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
InsetDivider()
SwitchPreference(
title = stringResource(R.string.led_heartbeat),
summary = stringResource(R.string.config_device_ledHeartbeatEnabled_summary),
title = stringResource(Res.string.led_heartbeat),
summary = stringResource(Res.string.config_device_ledHeartbeatEnabled_summary),
checked = !formState.value.ledHeartbeatDisabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { ledHeartbeatDisabled = !it } },
@ -211,7 +211,7 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
}
}
item {
TitledCard(title = stringResource(R.string.time_zone)) {
TitledCard(title = stringResource(Res.string.time_zone)) {
val context = LocalContext.current
val appTzPosixString by
produceState(initialValue = ZoneId.systemDefault().toPosixString()) {
@ -230,7 +230,7 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
EditTextPreference(
title = "",
value = formState.value.tzdef,
summary = stringResource(R.string.config_device_tzdef_summary),
summary = stringResource(Res.string.config_device_tzdef_summary),
maxSize = 64, // tzdef max_size:65
enabled = state.connected,
isError = false,
@ -257,15 +257,15 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
Spacer(modifier = Modifier.width(8.dp))
Text(text = stringResource(R.string.config_device_use_phone_tz))
Text(text = stringResource(Res.string.config_device_use_phone_tz))
}
}
}
item {
TitledCard(title = stringResource(R.string.gpio)) {
TitledCard(title = stringResource(Res.string.gpio)) {
EditTextPreference(
title = stringResource(R.string.button_gpio),
title = stringResource(Res.string.button_gpio),
value = formState.value.buttonGpio,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -275,7 +275,7 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.buzzer_gpio),
title = stringResource(Res.string.buzzer_gpio),
value = formState.value.buzzerGpio,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -288,10 +288,10 @@ fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack
@Composable
fun RouterRoleConfirmationDialog(onDismiss: () -> Unit, onConfirm: () -> Unit) {
val dialogTitle = stringResource(R.string.are_you_sure)
val dialogTitle = stringResource(Res.string.are_you_sure)
val annotatedDialogText =
AnnotatedString.fromHtml(
htmlString = stringResource(R.string.router_role_confirmation_text),
htmlString = stringResource(Res.string.router_role_confirmation_text),
linkStyles = TextLinkStyles(style = SpanStyle(color = Color.Blue)),
)
@ -307,14 +307,14 @@ fun RouterRoleConfirmationDialog(onDismiss: () -> Unit, onConfirm: () -> Unit) {
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(checked = confirmed, onCheckedChange = { confirmed = it })
Text(stringResource(R.string.i_know_what_i_m_doing))
Text(stringResource(Res.string.i_know_what_i_m_doing))
}
}
},
onDismissRequest = onDismiss,
confirmButton = {
TextButton(onClick = onConfirm, enabled = confirmed) { Text(stringResource(R.string.accept)) }
TextButton(onClick = onConfirm, enabled = confirmed) { Text(stringResource(Res.string.accept)) }
},
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } },
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(Res.string.cancel)) } },
)
}

View file

@ -26,7 +26,6 @@ import androidx.compose.ui.platform.LocalContext
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.DropDownPreference
import org.meshtastic.core.ui.component.SwitchPreference
import org.meshtastic.core.ui.component.TitledCard
@ -36,6 +35,7 @@ import org.meshtastic.feature.settings.util.toDisplayString
import org.meshtastic.proto.ConfigProtos.Config.DisplayConfig
import org.meshtastic.proto.config
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@Composable
fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -45,7 +45,7 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
val context = LocalContext.current
RadioConfigScreenList(
title = stringResource(R.string.display),
title = stringResource(Res.string.display),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -57,10 +57,10 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
},
) {
item {
TitledCard(title = stringResource(R.string.display_config)) {
TitledCard(title = stringResource(Res.string.display_config)) {
SwitchPreference(
title = stringResource(R.string.always_point_north),
summary = stringResource(R.string.config_display_compass_north_top_summary),
title = stringResource(Res.string.always_point_north),
summary = stringResource(Res.string.config_display_compass_north_top_summary),
checked = formState.value.compassNorthTop,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { compassNorthTop = it } },
@ -68,8 +68,8 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.use_12h_format),
summary = stringResource(R.string.display_time_in_12h_format),
title = stringResource(Res.string.use_12h_format),
summary = stringResource(Res.string.display_time_in_12h_format),
enabled = state.connected,
checked = formState.value.use12HClock,
onCheckedChange = { formState.value = formState.value.copy { use12HClock = it } },
@ -77,8 +77,8 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.bold_heading),
summary = stringResource(R.string.config_display_heading_bold_summary),
title = stringResource(Res.string.bold_heading),
summary = stringResource(Res.string.config_display_heading_bold_summary),
checked = formState.value.headingBold,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { headingBold = it } },
@ -86,8 +86,8 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.display_units),
summary = stringResource(R.string.config_display_units_summary),
title = stringResource(Res.string.display_units),
summary = stringResource(Res.string.config_display_units_summary),
enabled = state.connected,
items =
DisplayConfig.DisplayUnits.entries
@ -99,12 +99,12 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
}
}
item {
TitledCard(title = stringResource(R.string.advanced)) {
TitledCard(title = stringResource(Res.string.advanced)) {
val screenOnIntervals = remember { IntervalConfiguration.DISPLAY_SCREEN_ON.allowedIntervals }
val carouselIntervals = remember { IntervalConfiguration.DISPLAY_CAROUSEL.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.screen_on_for),
summary = stringResource(R.string.config_display_screen_on_secs_summary),
title = stringResource(Res.string.screen_on_for),
summary = stringResource(Res.string.config_display_screen_on_secs_summary),
enabled = state.connected,
items = screenOnIntervals.map { it to it.toDisplayString(context = context) },
selectedItem =
@ -114,8 +114,8 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.carousel_interval),
summary = stringResource(R.string.config_display_auto_screen_carousel_secs_summary),
title = stringResource(Res.string.carousel_interval),
summary = stringResource(Res.string.config_display_auto_screen_carousel_secs_summary),
enabled = state.connected,
items = carouselIntervals.map { it to it.toDisplayString(context = context) },
selectedItem =
@ -127,8 +127,8 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.wake_on_tap_or_motion),
summary = stringResource(R.string.config_display_wake_on_tap_or_motion_summary),
title = stringResource(Res.string.wake_on_tap_or_motion),
summary = stringResource(Res.string.config_display_wake_on_tap_or_motion_summary),
checked = formState.value.wakeOnTapOrMotion,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { wakeOnTapOrMotion = it } },
@ -136,8 +136,8 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.flip_screen),
summary = stringResource(R.string.config_display_flip_screen_summary),
title = stringResource(Res.string.flip_screen),
summary = stringResource(Res.string.config_display_flip_screen_summary),
checked = formState.value.flipScreen,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { flipScreen = it } },
@ -145,8 +145,8 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.display_mode),
summary = stringResource(R.string.config_display_displaymode_summary),
title = stringResource(Res.string.display_mode),
summary = stringResource(Res.string.config_display_displaymode_summary),
enabled = state.connected,
items =
DisplayConfig.DisplayMode.entries
@ -157,8 +157,8 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.oled_type),
summary = stringResource(R.string.config_display_oled_summary),
title = stringResource(Res.string.oled_type),
summary = stringResource(Res.string.config_display_oled_summary),
enabled = state.connected,
items =
DisplayConfig.OledType.entries
@ -169,7 +169,7 @@ fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.compass_orientation),
title = stringResource(Res.string.compass_orientation),
enabled = state.connected,
items =
DisplayConfig.CompassOrientation.entries

View file

@ -41,9 +41,9 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.protobuf.Descriptors
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.SwitchPreference
import org.meshtastic.proto.ClientOnlyProtos.DeviceProfile
import org.meshtastic.core.strings.R as Res
private const val SUPPORTED_FIELDS = 7
@ -102,7 +102,7 @@ fun EditDeviceProfileDialog(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
TextButton(modifier = modifier.weight(1f), onClick = onDismiss) {
Text(stringResource(R.string.cancel))
Text(stringResource(Res.string.cancel))
}
Button(
modifier = modifier.weight(1f),
@ -117,7 +117,7 @@ fun EditDeviceProfileDialog(
},
enabled = state.values.any { it },
) {
Text(stringResource(R.string.save))
Text(stringResource(Res.string.save))
}
}
},

View file

@ -34,7 +34,6 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.DropDownPreference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
@ -45,6 +44,7 @@ import org.meshtastic.feature.settings.util.gpioPins
import org.meshtastic.feature.settings.util.toDisplayString
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import org.meshtastic.core.strings.R as Res
@Composable
fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -57,7 +57,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
val context = LocalContext.current
RadioConfigScreenList(
title = stringResource(R.string.external_notification),
title = stringResource(Res.string.external_notification),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -74,9 +74,9 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
},
) {
item {
TitledCard(title = stringResource(R.string.external_notification_config)) {
TitledCard(title = stringResource(Res.string.external_notification_config)) {
SwitchPreference(
title = stringResource(R.string.external_notification_enabled),
title = stringResource(Res.string.external_notification_enabled),
checked = formState.value.enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
@ -86,9 +86,9 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
}
item {
TitledCard(title = stringResource(R.string.notifications_on_message_receipt)) {
TitledCard(title = stringResource(Res.string.notifications_on_message_receipt)) {
SwitchPreference(
title = stringResource(R.string.alert_message_led),
title = stringResource(Res.string.alert_message_led),
checked = formState.value.alertMessage,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { alertMessage = it } },
@ -96,7 +96,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.alert_message_buzzer),
title = stringResource(Res.string.alert_message_buzzer),
checked = formState.value.alertMessageBuzzer,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { alertMessageBuzzer = it } },
@ -104,7 +104,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.alert_message_vibra),
title = stringResource(Res.string.alert_message_vibra),
checked = formState.value.alertMessageVibra,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { alertMessageVibra = it } },
@ -114,9 +114,9 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
}
item {
TitledCard(title = stringResource(R.string.notifications_on_alert_bell_receipt)) {
TitledCard(title = stringResource(Res.string.notifications_on_alert_bell_receipt)) {
SwitchPreference(
title = stringResource(R.string.alert_bell_led),
title = stringResource(Res.string.alert_bell_led),
checked = formState.value.alertBell,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { alertBell = it } },
@ -124,7 +124,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.alert_bell_buzzer),
title = stringResource(Res.string.alert_bell_buzzer),
checked = formState.value.alertBellBuzzer,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { alertBellBuzzer = it } },
@ -132,7 +132,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.alert_bell_vibra),
title = stringResource(Res.string.alert_bell_vibra),
checked = formState.value.alertBellVibra,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { alertBellVibra = it } },
@ -142,10 +142,10 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
}
item {
TitledCard(title = stringResource(R.string.advanced)) {
TitledCard(title = stringResource(Res.string.advanced)) {
val gpio = remember { gpioPins }
DropDownPreference(
title = stringResource(R.string.output_led_gpio),
title = stringResource(Res.string.output_led_gpio),
items = gpio,
selectedItem = formState.value.output,
enabled = state.connected,
@ -154,7 +154,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
if (formState.value.output != 0) {
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.output_led_active_high),
title = stringResource(Res.string.output_led_active_high),
checked = formState.value.active,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { active = it } },
@ -163,7 +163,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
}
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.output_buzzer_gpio),
title = stringResource(Res.string.output_buzzer_gpio),
items = gpio,
selectedItem = formState.value.outputBuzzer,
enabled = state.connected,
@ -172,7 +172,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
if (formState.value.outputBuzzer != 0) {
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.use_pwm_buzzer),
title = stringResource(Res.string.use_pwm_buzzer),
checked = formState.value.usePwm,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { usePwm = it } },
@ -181,7 +181,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
}
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.output_vibra_gpio),
title = stringResource(Res.string.output_vibra_gpio),
items = gpio,
selectedItem = formState.value.outputVibra,
enabled = state.connected,
@ -190,7 +190,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
HorizontalDivider()
val outputItems = remember { IntervalConfiguration.OUTPUT.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.output_duration_milliseconds),
title = stringResource(Res.string.output_duration_milliseconds),
items = outputItems.map { it.value to it.toDisplayString(context = context) },
selectedItem = formState.value.outputMs,
enabled = state.connected,
@ -199,7 +199,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
HorizontalDivider()
val nagItems = remember { IntervalConfiguration.NAG_TIMEOUT.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.nag_timeout_seconds),
title = stringResource(Res.string.nag_timeout_seconds),
items = nagItems.map { it.value to it.toDisplayString(context = context) },
selectedItem = formState.value.nagTimeout,
enabled = state.connected,
@ -207,7 +207,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.ringtone),
title = stringResource(Res.string.ringtone),
value = ringtoneInput,
maxSize = 230, // ringtone max_size:231
enabled = state.connected,
@ -219,7 +219,7 @@ fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewM
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.use_i2s_as_buzzer),
title = stringResource(Res.string.use_i2s_as_buzzer),
checked = formState.value.useI2SAsBuzzer,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { useI2SAsBuzzer = it } },

View file

@ -32,7 +32,6 @@ import org.meshtastic.core.model.Channel
import org.meshtastic.core.model.ChannelOption
import org.meshtastic.core.model.RegionInfo
import org.meshtastic.core.model.numChannels
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.DropDownPreference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.SignedIntegerEditTextPreference
@ -42,6 +41,7 @@ import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.feature.settings.util.hopLimits
import org.meshtastic.proto.config
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@Composable
fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
@ -54,7 +54,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
val focusManager = LocalFocusManager.current
RadioConfigScreenList(
title = stringResource(R.string.lora),
title = stringResource(Res.string.lora),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -66,10 +66,10 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
},
) {
item {
TitledCard(title = stringResource(R.string.options)) {
TitledCard(title = stringResource(Res.string.options)) {
DropDownPreference(
title = stringResource(R.string.region_frequency_plan),
summary = stringResource(R.string.config_lora_region_summary),
title = stringResource(Res.string.region_frequency_plan),
summary = stringResource(Res.string.config_lora_region_summary),
enabled = state.connected,
items = RegionInfo.entries.map { it.regionCode to it.description },
selectedItem = formState.value.region,
@ -77,7 +77,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.use_modem_preset),
title = stringResource(Res.string.use_modem_preset),
checked = formState.value.usePreset,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { usePreset = it } },
@ -86,8 +86,8 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
HorizontalDivider()
if (formState.value.usePreset) {
DropDownPreference(
title = stringResource(R.string.modem_preset),
summary = stringResource(R.string.config_lora_modem_preset_summary),
title = stringResource(Res.string.modem_preset),
summary = stringResource(Res.string.config_lora_modem_preset_summary),
enabled = state.connected && formState.value.usePreset,
items = ChannelOption.entries.map { it.modemPreset to stringResource(it.labelRes) },
selectedItem = formState.value.modemPreset,
@ -95,7 +95,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
)
} else {
EditTextPreference(
title = stringResource(R.string.bandwidth),
title = stringResource(Res.string.bandwidth),
value = formState.value.bandwidth,
enabled = state.connected && !formState.value.usePreset,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -103,7 +103,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.spread_factor),
title = stringResource(Res.string.spread_factor),
value = formState.value.spreadFactor,
enabled = state.connected && !formState.value.usePreset,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -111,7 +111,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.coding_rate),
title = stringResource(Res.string.coding_rate),
value = formState.value.codingRate,
enabled = state.connected && !formState.value.usePreset,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -122,9 +122,9 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
}
item {
TitledCard(title = stringResource(R.string.advanced)) {
TitledCard(title = stringResource(Res.string.advanced)) {
SwitchPreference(
title = stringResource(R.string.ignore_mqtt),
title = stringResource(Res.string.ignore_mqtt),
checked = formState.value.ignoreMqtt,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { ignoreMqtt = it } },
@ -132,7 +132,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.ok_to_mqtt),
title = stringResource(Res.string.ok_to_mqtt),
checked = formState.value.configOkToMqtt,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { configOkToMqtt = it } },
@ -140,7 +140,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.tx_enabled),
title = stringResource(Res.string.tx_enabled),
checked = formState.value.txEnabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { txEnabled = it } },
@ -149,8 +149,8 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
HorizontalDivider()
val hopLimitItems = remember { hopLimits }
DropDownPreference(
title = stringResource(R.string.hop_limit),
summary = stringResource(R.string.config_lora_hop_limit_summary),
title = stringResource(Res.string.hop_limit),
summary = stringResource(Res.string.config_lora_hop_limit_summary),
items = hopLimitItems,
selectedItem = formState.value.hopLimit,
onItemSelected = { formState.value = formState.value.copy { hopLimit = it } },
@ -159,8 +159,8 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
HorizontalDivider()
var isFocusedSlot by remember { mutableStateOf(false) }
EditTextPreference(
title = stringResource(R.string.frequency_slot),
summary = stringResource(R.string.config_lora_frequency_slot_summary),
title = stringResource(Res.string.frequency_slot),
summary = stringResource(Res.string.config_lora_frequency_slot_summary),
value =
if (isFocusedSlot || formState.value.channelNum != 0) {
formState.value.channelNum
@ -178,7 +178,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.sx126x_rx_boosted_gain),
title = stringResource(Res.string.sx126x_rx_boosted_gain),
checked = formState.value.sx126XRxBoostedGain,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { sx126XRxBoostedGain = it } },
@ -187,7 +187,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
HorizontalDivider()
var isFocusedOverride by remember { mutableStateOf(false) }
EditTextPreference(
title = stringResource(R.string.override_frequency_mhz),
title = stringResource(Res.string.override_frequency_mhz),
value =
if (isFocusedOverride || formState.value.overrideFrequency != 0f) {
formState.value.overrideFrequency
@ -201,7 +201,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
)
HorizontalDivider()
SignedIntegerEditTextPreference(
title = stringResource(R.string.tx_power_dbm),
title = stringResource(Res.string.tx_power_dbm),
value = formState.value.txPower,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -210,7 +210,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
if (viewModel.hasPaFan) {
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.pa_fan_disabled),
title = stringResource(Res.string.pa_fan_disabled),
checked = formState.value.paFanDisabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { paFanDisabled = it } },

View file

@ -31,7 +31,6 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.EditPasswordPreference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
@ -39,6 +38,7 @@ import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import org.meshtastic.core.strings.R as Res
@Composable
fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -66,7 +66,7 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
val focusManager = LocalFocusManager.current
RadioConfigScreenList(
title = stringResource(R.string.mqtt),
title = stringResource(Res.string.mqtt),
onBack = onBack,
configState = formState,
enabled = state.connected && consentValid,
@ -78,9 +78,9 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
},
) {
item {
TitledCard(title = stringResource(R.string.mqtt_config)) {
TitledCard(title = stringResource(Res.string.mqtt_config)) {
SwitchPreference(
title = stringResource(R.string.mqtt_enabled),
title = stringResource(Res.string.mqtt_enabled),
checked = formState.value.enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
@ -88,7 +88,7 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.address),
title = stringResource(Res.string.address),
value = formState.value.address,
maxSize = 63, // address max_size:64
enabled = state.connected,
@ -100,7 +100,7 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.username),
title = stringResource(Res.string.username),
value = formState.value.username,
maxSize = 63, // username max_size:64
enabled = state.connected,
@ -112,7 +112,7 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
EditPasswordPreference(
title = stringResource(R.string.password),
title = stringResource(Res.string.password),
value = formState.value.password,
maxSize = 63, // password max_size:64
enabled = state.connected,
@ -121,7 +121,7 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.encryption_enabled),
title = stringResource(Res.string.encryption_enabled),
checked = formState.value.encryptionEnabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { encryptionEnabled = it } },
@ -129,18 +129,18 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.json_output_enabled),
title = stringResource(Res.string.json_output_enabled),
checked = formState.value.jsonEnabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { jsonEnabled = it } },
containerColor = CardDefaults.cardColors().containerColor,
)
HorizontalDivider()
val defaultAddress = stringResource(R.string.default_mqtt_address)
val defaultAddress = stringResource(Res.string.default_mqtt_address)
val isDefault = formState.value.address.isEmpty() || formState.value.address.contains(defaultAddress)
val enforceTls = isDefault && formState.value.proxyToClientEnabled
SwitchPreference(
title = stringResource(R.string.tls_enabled),
title = stringResource(Res.string.tls_enabled),
checked = formState.value.tlsEnabled || enforceTls,
enabled = state.connected && !enforceTls,
onCheckedChange = { formState.value = formState.value.copy { tlsEnabled = it } },
@ -148,7 +148,7 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.root_topic),
title = stringResource(Res.string.root_topic),
value = formState.value.root,
maxSize = 31, // root max_size:32
enabled = state.connected,
@ -160,7 +160,7 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.proxy_to_client_enabled),
title = stringResource(Res.string.proxy_to_client_enabled),
checked = formState.value.proxyToClientEnabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { proxyToClientEnabled = it } },
@ -170,7 +170,7 @@ fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
}
item {
TitledCard(title = stringResource(R.string.map_reporting)) {
TitledCard(title = stringResource(Res.string.map_reporting)) {
MapReportingPreference(
mapReportingEnabled = formState.value.mapReportingEnabled,
onMapReportingEnabledChanged = {

View file

@ -41,13 +41,13 @@ 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 org.meshtastic.core.ui.component.DropDownPreference
import org.meshtastic.core.ui.component.SwitchPreference
import org.meshtastic.core.ui.component.precisionBitsToMeters
import org.meshtastic.feature.settings.util.IntervalConfiguration
import org.meshtastic.feature.settings.util.toDisplayString
import kotlin.math.roundToInt
import org.meshtastic.core.strings.R as Res
private const val POSITION_PRECISION_MIN = 12
private const val POSITION_PRECISION_MAX = 15
@ -70,8 +70,8 @@ fun MapReportingPreference(
var showMapReportingWarning by rememberSaveable { mutableStateOf(mapReportingEnabled) }
LaunchedEffect(mapReportingEnabled) { showMapReportingWarning = mapReportingEnabled }
SwitchPreference(
title = stringResource(R.string.map_reporting),
summary = stringResource(R.string.map_reporting_summary),
title = stringResource(Res.string.map_reporting),
summary = stringResource(Res.string.map_reporting_summary),
checked = showMapReportingWarning,
enabled = enabled,
onCheckedChange = { checked ->
@ -85,13 +85,13 @@ fun MapReportingPreference(
)
AnimatedVisibility(showMapReportingWarning) {
Card(modifier = Modifier.padding(16.dp)) {
Text(text = stringResource(R.string.map_reporting_consent_header), modifier = Modifier.padding(16.dp))
Text(text = stringResource(Res.string.map_reporting_consent_header), modifier = Modifier.padding(16.dp))
HorizontalDivider()
Text(stringResource(R.string.map_reporting_consent_text), modifier = Modifier.padding(16.dp))
Text(stringResource(Res.string.map_reporting_consent_text), modifier = Modifier.padding(16.dp))
SwitchPreference(
title = stringResource(R.string.i_agree),
summary = stringResource(R.string.i_agree_to_share_my_location),
title = stringResource(Res.string.i_agree),
summary = stringResource(Res.string.i_agree_to_share_my_location),
checked = shouldReportLocation,
enabled = enabled,
onCheckedChange = { checked ->
@ -125,7 +125,7 @@ fun MapReportingPreference(
val publishItems = remember { IntervalConfiguration.BROADCAST_MEDIUM.allowedIntervals }
DropDownPreference(
modifier = Modifier.padding(bottom = 16.dp),
title = stringResource(R.string.map_reporting_interval_seconds),
title = stringResource(Res.string.map_reporting_interval_seconds),
items = publishItems.map { it.value to it.toDisplayString(context = context) },
selectedItem = publishIntervalSecs,
enabled = enabled,

View file

@ -26,13 +26,13 @@ import androidx.compose.ui.platform.LocalFocusManager
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.EditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
import org.meshtastic.core.ui.component.TitledCard
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import org.meshtastic.core.strings.R as Res
@Composable
fun NeighborInfoConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -42,7 +42,7 @@ fun NeighborInfoConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
val focusManager = LocalFocusManager.current
RadioConfigScreenList(
title = stringResource(R.string.neighbor_info),
title = stringResource(Res.string.neighbor_info),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -54,9 +54,9 @@ fun NeighborInfoConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
},
) {
item {
TitledCard(title = stringResource(R.string.neighbor_info_config)) {
TitledCard(title = stringResource(Res.string.neighbor_info_config)) {
SwitchPreference(
title = stringResource(R.string.neighbor_info_enabled),
title = stringResource(Res.string.neighbor_info_enabled),
checked = formState.value.enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
@ -64,7 +64,7 @@ fun NeighborInfoConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.update_interval_seconds),
title = stringResource(Res.string.update_interval_seconds),
value = formState.value.updateInterval,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -72,8 +72,8 @@ fun NeighborInfoConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(),
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.transmit_over_lora),
summary = stringResource(R.string.config_device_transmitOverLora_summary),
title = stringResource(Res.string.transmit_over_lora),
summary = stringResource(Res.string.config_device_transmitOverLora_summary),
checked = formState.value.transmitOverLora,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { transmitOverLora = it } },

View file

@ -42,7 +42,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.DropDownPreference
import org.meshtastic.core.ui.component.EditIPv4Preference
import org.meshtastic.core.ui.component.EditPasswordPreference
@ -55,10 +54,11 @@ import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.proto.ConfigProtos.Config.NetworkConfig
import org.meshtastic.proto.config
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@Composable
private fun ScanErrorDialog(onDismiss: () -> Unit = {}) =
SimpleAlertDialog(title = R.string.error, text = R.string.wifi_qr_code_error, onDismiss = onDismiss)
SimpleAlertDialog(title = Res.string.error, text = Res.string.wifi_qr_code_error, onDismiss = onDismiss)
@Composable
fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -100,7 +100,7 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
val focusManager = LocalFocusManager.current
RadioConfigScreenList(
title = stringResource(R.string.network),
title = stringResource(Res.string.network),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -118,11 +118,11 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
connectionStatus.ethernet?.status?.isConnected == true
) {
item {
TitledCard(title = stringResource(R.string.connection_status)) {
TitledCard(title = stringResource(Res.string.connection_status)) {
connectionStatus.wifi?.let { wifiStatus ->
if (wifiStatus.status.isConnected) {
ListItem(
text = stringResource(R.string.wifi_ip),
text = stringResource(Res.string.wifi_ip),
supportingText = formatIpAddress(wifiStatus.status.ipAddress),
trailingIcon = null,
)
@ -131,7 +131,7 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
connectionStatus.ethernet?.let { ethernetStatus ->
if (ethernetStatus.status.isConnected) {
ListItem(
text = stringResource(R.string.ethernet_ip),
text = stringResource(Res.string.ethernet_ip),
supportingText = formatIpAddress(ethernetStatus.status.ipAddress),
trailingIcon = null,
)
@ -143,10 +143,10 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
}
if (state.metadata?.hasWifi == true) {
item {
TitledCard(title = stringResource(R.string.wifi_config)) {
TitledCard(title = stringResource(Res.string.wifi_config)) {
SwitchPreference(
title = stringResource(R.string.wifi_enabled),
summary = stringResource(R.string.config_network_wifi_enabled_summary),
title = stringResource(Res.string.wifi_enabled),
summary = stringResource(Res.string.config_network_wifi_enabled_summary),
checked = formState.value.wifiEnabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { wifiEnabled = it } },
@ -154,7 +154,7 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.ssid),
title = stringResource(Res.string.ssid),
value = formState.value.wifiSsid,
maxSize = 32, // wifi_ssid max_size:33
enabled = state.connected,
@ -166,7 +166,7 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
EditPasswordPreference(
title = stringResource(R.string.password),
title = stringResource(Res.string.password),
value = formState.value.wifiPsk,
maxSize = 64, // wifi_psk max_size:65
enabled = state.connected,
@ -179,17 +179,17 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp).height(48.dp),
enabled = state.connected,
) {
Text(text = stringResource(R.string.wifi_qr_code_scan))
Text(text = stringResource(Res.string.wifi_qr_code_scan))
}
}
}
}
if (state.metadata?.hasEthernet == true) {
item {
TitledCard(title = stringResource(R.string.ethernet_config)) {
TitledCard(title = stringResource(Res.string.ethernet_config)) {
SwitchPreference(
title = stringResource(R.string.ethernet_enabled),
summary = stringResource(R.string.config_network_eth_enabled_summary),
title = stringResource(Res.string.ethernet_enabled),
summary = stringResource(Res.string.config_network_eth_enabled_summary),
checked = formState.value.ethEnabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { ethEnabled = it } },
@ -201,10 +201,10 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
if (state.metadata?.hasEthernet == true || state.metadata?.hasWifi == true) {
item {
TitledCard(title = stringResource(R.string.udp_config)) {
TitledCard(title = stringResource(Res.string.udp_config)) {
SwitchPreference(
title = stringResource(R.string.udp_enabled),
summary = stringResource(R.string.config_network_udp_enabled_summary),
title = stringResource(Res.string.udp_enabled),
summary = stringResource(Res.string.config_network_udp_enabled_summary),
checked = formState.value.enabledProtocols == 1,
enabled = state.connected,
onCheckedChange = {
@ -218,9 +218,9 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
}
item {
TitledCard(title = stringResource(R.string.advanced)) {
TitledCard(title = stringResource(Res.string.advanced)) {
EditTextPreference(
title = stringResource(R.string.ntp_server),
title = stringResource(Res.string.ntp_server),
value = formState.value.ntpServer,
maxSize = 32, // ntp_server max_size:33
enabled = state.connected,
@ -232,7 +232,7 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.rsyslog_server),
title = stringResource(Res.string.rsyslog_server),
value = formState.value.rsyslogServer,
maxSize = 32, // rsyslog_server max_size:33
enabled = state.connected,
@ -244,7 +244,7 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.ipv4_mode),
title = stringResource(Res.string.ipv4_mode),
enabled = state.connected,
items =
NetworkConfig.AddressMode.entries
@ -255,7 +255,7 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
EditIPv4Preference(
title = stringResource(R.string.ip),
title = stringResource(Res.string.ip),
value = formState.value.ipv4Config.ip,
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -266,7 +266,7 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
EditIPv4Preference(
title = stringResource(R.string.gateway),
title = stringResource(Res.string.gateway),
value = formState.value.ipv4Config.gateway,
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -277,7 +277,7 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
)
HorizontalDivider()
EditIPv4Preference(
title = stringResource(R.string.subnet),
title = stringResource(Res.string.subnet),
value = formState.value.ipv4Config.subnet,
enabled = state.connected && formState.value.addressMode == NetworkConfig.AddressMode.STATIC,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),

View file

@ -36,8 +36,8 @@ 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.feature.settings.radio.ResponseState
import org.meshtastic.core.strings.R as Res
@Composable
fun <T> PacketResponseStateDialog(state: ResponseState<T>, onDismiss: () -> Unit = {}, onComplete: () -> Unit = {}) {
@ -58,10 +58,10 @@ fun <T> PacketResponseStateDialog(state: ResponseState<T>, onDismiss: () -> Unit
if (state.total == state.completed) onComplete()
}
if (state is ResponseState.Success) {
Text(text = stringResource(R.string.delivery_confirmed))
Text(text = stringResource(Res.string.delivery_confirmed))
}
if (state is ResponseState.Error) {
Text(text = stringResource(R.string.error), minLines = 2)
Text(text = stringResource(Res.string.error), minLines = 2)
Text(text = state.error.asString())
}
}
@ -80,7 +80,7 @@ fun <T> PacketResponseStateDialog(state: ResponseState<T>, onDismiss: () -> Unit
},
modifier = Modifier.padding(top = 16.dp),
) {
Text(stringResource(R.string.close))
Text(stringResource(Res.string.close))
}
}
},

View file

@ -28,7 +28,6 @@ import androidx.compose.ui.platform.LocalFocusManager
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.DropDownPreference
import org.meshtastic.core.ui.component.SignedIntegerEditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
@ -38,6 +37,7 @@ import org.meshtastic.feature.settings.util.IntervalConfiguration
import org.meshtastic.feature.settings.util.toDisplayString
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import org.meshtastic.core.strings.R as Res
@Composable
fun PaxcounterConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -48,7 +48,7 @@ fun PaxcounterConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), on
val context = LocalContext.current
RadioConfigScreenList(
title = stringResource(R.string.paxcounter),
title = stringResource(Res.string.paxcounter),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -60,9 +60,9 @@ fun PaxcounterConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), on
},
) {
item {
TitledCard(title = stringResource(R.string.paxcounter_config)) {
TitledCard(title = stringResource(Res.string.paxcounter_config)) {
SwitchPreference(
title = stringResource(R.string.paxcounter_enabled),
title = stringResource(Res.string.paxcounter_enabled),
checked = formState.value.enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
@ -71,7 +71,7 @@ fun PaxcounterConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), on
HorizontalDivider()
val items = remember { IntervalConfiguration.PAX_COUNTER.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.update_interval_seconds),
title = stringResource(Res.string.update_interval_seconds),
selectedItem = formState.value.paxcounterUpdateInterval.toLong(),
enabled = state.connected,
items = items.map { it.value to it.toDisplayString(context = context) },
@ -81,7 +81,7 @@ fun PaxcounterConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), on
)
HorizontalDivider()
SignedIntegerEditTextPreference(
title = stringResource(R.string.wifi_rssi_threshold_defaults_to_80),
title = stringResource(Res.string.wifi_rssi_threshold_defaults_to_80),
value = formState.value.wifiThreshold,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -89,7 +89,7 @@ fun PaxcounterConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), on
)
HorizontalDivider()
SignedIntegerEditTextPreference(
title = stringResource(R.string.ble_rssi_threshold_defaults_to_80),
title = stringResource(Res.string.ble_rssi_threshold_defaults_to_80),
value = formState.value.bleThreshold,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),

View file

@ -44,7 +44,6 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberPermissionState
import kotlinx.coroutines.launch
import org.meshtastic.core.model.Position
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.BitwisePreference
import org.meshtastic.core.ui.component.DropDownPreference
import org.meshtastic.core.ui.component.EditTextPreference
@ -58,6 +57,7 @@ import org.meshtastic.feature.settings.util.toDisplayString
import org.meshtastic.proto.ConfigProtos.Config.PositionConfig
import org.meshtastic.proto.config
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalPermissionsApi::class)
@Composable
@ -121,7 +121,7 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
val focusManager = LocalFocusManager.current
val context = LocalContext.current
RadioConfigScreenList(
title = stringResource(R.string.position),
title = stringResource(Res.string.position),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -143,11 +143,11 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
},
) {
item {
TitledCard(title = stringResource(R.string.position_packet)) {
TitledCard(title = stringResource(Res.string.position_packet)) {
val items = remember { IntervalConfiguration.POSITION_BROADCAST.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.broadcast_interval),
summary = stringResource(R.string.config_position_broadcast_secs_summary),
title = stringResource(Res.string.broadcast_interval),
summary = stringResource(Res.string.config_position_broadcast_secs_summary),
enabled = state.connected,
items = items.map { it to it.toDisplayString(context = context) },
selectedItem =
@ -158,7 +158,7 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.smart_position),
title = stringResource(Res.string.smart_position),
checked = formState.value.positionBroadcastSmartEnabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { positionBroadcastSmartEnabled = it } },
@ -168,9 +168,9 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
HorizontalDivider()
val smartItems = remember { IntervalConfiguration.SMART_BROADCAST_MINIMUM.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.minimum_interval),
title = stringResource(Res.string.minimum_interval),
summary =
stringResource(R.string.config_position_broadcast_smart_minimum_interval_secs_summary),
stringResource(Res.string.config_position_broadcast_smart_minimum_interval_secs_summary),
enabled = state.connected,
items = smartItems.map { it to it.toDisplayString(context = context) },
selectedItem =
@ -183,8 +183,8 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.minimum_distance),
summary = stringResource(R.string.config_position_broadcast_smart_minimum_distance_summary),
title = stringResource(Res.string.minimum_distance),
summary = stringResource(Res.string.config_position_broadcast_smart_minimum_distance_summary),
value = formState.value.broadcastSmartMinimumDistance,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -196,9 +196,9 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
}
}
item {
TitledCard(title = stringResource(R.string.device_gps)) {
TitledCard(title = stringResource(Res.string.device_gps)) {
SwitchPreference(
title = stringResource(R.string.fixed_position),
title = stringResource(Res.string.fixed_position),
checked = formState.value.fixedPosition,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { fixedPosition = it } },
@ -207,7 +207,7 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
if (formState.value.fixedPosition) {
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.latitude),
title = stringResource(Res.string.latitude),
value = locationInput.latitude,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -219,7 +219,7 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.longitude),
title = stringResource(Res.string.longitude),
value = locationInput.longitude,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -231,7 +231,7 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.altitude),
title = stringResource(Res.string.altitude),
value = locationInput.altitude,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -242,12 +242,12 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
enabled = state.connected,
onClick = { coroutineScope.launch { locationPermissionState.launchPermissionRequest() } },
) {
Text(text = stringResource(R.string.position_config_set_fixed_from_phone))
Text(text = stringResource(Res.string.position_config_set_fixed_from_phone))
}
} else {
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.gps_mode),
title = stringResource(Res.string.gps_mode),
enabled = state.connected,
items =
PositionConfig.GpsMode.entries
@ -259,8 +259,8 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
HorizontalDivider()
val items = remember { IntervalConfiguration.GPS_UPDATE.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.update_interval),
summary = stringResource(R.string.config_position_gps_update_interval_summary),
title = stringResource(Res.string.update_interval),
summary = stringResource(Res.string.config_position_gps_update_interval_summary),
enabled = state.connected,
items = items.map { it to it.toDisplayString(context = context) },
selectedItem =
@ -273,10 +273,10 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
}
}
item {
TitledCard(title = stringResource(R.string.position_flags)) {
TitledCard(title = stringResource(Res.string.position_flags)) {
BitwisePreference(
title = stringResource(R.string.position_flags),
summary = stringResource(R.string.config_position_flags_summary),
title = stringResource(Res.string.position_flags),
summary = stringResource(Res.string.config_position_flags_summary),
value = formState.value.positionFlags,
enabled = state.connected,
items =
@ -291,10 +291,10 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
}
}
item {
TitledCard(title = stringResource(R.string.advanced_device_gps)) {
TitledCard(title = stringResource(Res.string.advanced_device_gps)) {
val pins = remember { gpioPins }
DropDownPreference(
title = stringResource(R.string.gps_receive_gpio),
title = stringResource(Res.string.gps_receive_gpio),
enabled = state.connected,
items = pins,
selectedItem = formState.value.rxGpio,
@ -302,7 +302,7 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.gps_transmit_gpio),
title = stringResource(Res.string.gps_transmit_gpio),
enabled = state.connected,
items = pins,
selectedItem = formState.value.txGpio,
@ -310,7 +310,7 @@ fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
)
HorizontalDivider()
DropDownPreference(
title = stringResource(R.string.gps_en_gpio),
title = stringResource(Res.string.gps_en_gpio),
enabled = state.connected,
items = pins,
selectedItem = formState.value.gpsEnGpio,

View file

@ -28,7 +28,6 @@ import androidx.compose.ui.platform.LocalFocusManager
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.DropDownPreference
import org.meshtastic.core.ui.component.EditTextPreference
import org.meshtastic.core.ui.component.SwitchPreference
@ -38,6 +37,7 @@ import org.meshtastic.feature.settings.util.IntervalConfiguration
import org.meshtastic.feature.settings.util.toDisplayString
import org.meshtastic.proto.config
import org.meshtastic.proto.copy
import org.meshtastic.core.strings.R as Res
@Composable
fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -48,7 +48,7 @@ fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
val context = LocalContext.current
RadioConfigScreenList(
title = stringResource(R.string.power),
title = stringResource(Res.string.power),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -60,10 +60,10 @@ fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
},
) {
item {
TitledCard(title = stringResource(R.string.power_config)) {
TitledCard(title = stringResource(Res.string.power_config)) {
SwitchPreference(
title = stringResource(R.string.enable_power_saving_mode),
summary = stringResource(R.string.config_power_is_power_saving_summary),
title = stringResource(Res.string.enable_power_saving_mode),
summary = stringResource(Res.string.config_power_is_power_saving_summary),
checked = formState.value.isPowerSaving,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { isPowerSaving = it } },
@ -72,7 +72,7 @@ fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
HorizontalDivider()
val items = remember { IntervalConfiguration.ALL.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.shutdown_on_power_loss),
title = stringResource(Res.string.shutdown_on_power_loss),
selectedItem = formState.value.onBatteryShutdownAfterSecs.toLong(),
enabled = state.connected,
items = items.map { it.value to it.toDisplayString(context = context) },
@ -82,7 +82,7 @@ fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.adc_multiplier_override),
title = stringResource(Res.string.adc_multiplier_override),
checked = formState.value.adcMultiplierOverride > 0f,
enabled = state.connected,
onCheckedChange = {
@ -93,7 +93,7 @@ fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
if (formState.value.adcMultiplierOverride > 0f) {
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.adc_multiplier_override_ratio),
title = stringResource(Res.string.adc_multiplier_override_ratio),
value = formState.value.adcMultiplierOverride,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
@ -103,7 +103,7 @@ fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
HorizontalDivider()
val waitBluetoothItems = remember { IntervalConfiguration.NAG_TIMEOUT.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.wait_for_bluetooth_duration_seconds),
title = stringResource(Res.string.wait_for_bluetooth_duration_seconds),
selectedItem = formState.value.waitBluetoothSecs.toLong(),
enabled = state.connected,
items = waitBluetoothItems.map { it.value to it.toDisplayString(context = context) },
@ -112,7 +112,7 @@ fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
HorizontalDivider()
val sdsSecsItems = remember { IntervalConfiguration.ALL.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.super_deep_sleep_duration_seconds),
title = stringResource(Res.string.super_deep_sleep_duration_seconds),
selectedItem = formState.value.sdsSecs.toLong(),
onItemSelected = { formState.value = formState.value.copy { sdsSecs = it.toInt() } },
enabled = state.connected,
@ -121,7 +121,7 @@ fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
HorizontalDivider()
val minWakeItems = remember { IntervalConfiguration.NAG_TIMEOUT.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.minimum_wake_time_seconds),
title = stringResource(Res.string.minimum_wake_time_seconds),
selectedItem = formState.value.minWakeSecs.toLong(),
enabled = state.connected,
items = minWakeItems.map { it.value to it.toDisplayString(context = context) },
@ -129,7 +129,7 @@ fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack:
)
HorizontalDivider()
EditTextPreference(
title = stringResource(R.string.battery_ina_2xx_i2c_address),
title = stringResource(Res.string.battery_ina_2xx_i2c_address),
value = formState.value.deviceBatteryInaAddress,
enabled = state.connected,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),

View file

@ -41,10 +41,10 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.google.protobuf.MessageLite
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.PreferenceFooter
import org.meshtastic.feature.settings.radio.ResponseState
import org.meshtastic.core.strings.R as Res
@Composable
fun <T : MessageLite> RadioConfigScreenList(
@ -106,12 +106,12 @@ fun <T : MessageLite> RadioConfigScreenList(
) {
PreferenceFooter(
enabled = enabled && configState.isDirty,
negativeText = stringResource(R.string.discard_changes),
negativeText = stringResource(Res.string.discard_changes),
onNegativeClicked = {
focusManager.clearFocus()
configState.reset()
},
positiveText = stringResource(R.string.save_changes),
positiveText = stringResource(Res.string.save_changes),
onPositiveClicked = {
focusManager.clearFocus()
onSave(configState.value)

View file

@ -26,7 +26,6 @@ import androidx.compose.ui.platform.LocalContext
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.DropDownPreference
import org.meshtastic.core.ui.component.SwitchPreference
import org.meshtastic.core.ui.component.TitledCard
@ -35,6 +34,7 @@ import org.meshtastic.feature.settings.util.IntervalConfiguration
import org.meshtastic.feature.settings.util.toDisplayString
import org.meshtastic.proto.copy
import org.meshtastic.proto.moduleConfig
import org.meshtastic.core.strings.R as Res
@Composable
fun RangeTestConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
@ -44,7 +44,7 @@ fun RangeTestConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onB
val context = LocalContext.current
RadioConfigScreenList(
title = stringResource(R.string.range_test),
title = stringResource(Res.string.range_test),
onBack = onBack,
configState = formState,
enabled = state.connected,
@ -56,9 +56,9 @@ fun RangeTestConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onB
},
) {
item {
TitledCard(title = stringResource(R.string.range_test_config)) {
TitledCard(title = stringResource(Res.string.range_test_config)) {
SwitchPreference(
title = stringResource(R.string.range_test_enabled),
title = stringResource(Res.string.range_test_enabled),
checked = formState.value.enabled,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { this.enabled = it } },
@ -67,7 +67,7 @@ fun RangeTestConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onB
HorizontalDivider()
val rangeItems = remember { IntervalConfiguration.RANGE_TEST_SENDER.allowedIntervals }
DropDownPreference(
title = stringResource(R.string.sender_message_interval_seconds),
title = stringResource(Res.string.sender_message_interval_seconds),
selectedItem = formState.value.sender.toLong(),
enabled = state.connected,
items = rangeItems.map { it.value to it.toDisplayString(context = context) },
@ -75,7 +75,7 @@ fun RangeTestConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onB
)
HorizontalDivider()
SwitchPreference(
title = stringResource(R.string.save_csv_in_storage_esp32_only),
title = stringResource(Res.string.save_csv_in_storage_esp32_only),
checked = formState.value.save,
enabled = state.connected,
onCheckedChange = { formState.value = formState.value.copy { save = it } },

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