From 8b83273a4f9b5b04dcf3c91910d19bc01d4f63fb Mon Sep 17 00:00:00 2001 From: Phil Oliver <3497406+poliver@users.noreply.github.com> Date: Wed, 5 Nov 2025 00:59:45 -0500 Subject: [PATCH] Use `Resources` for string access (#3620) --- .../java/com/geeksville/mesh/MainActivity.kt | 8 +++- .../geeksville/mesh/ui/contact/Contacts.kt | 6 +-- .../com/geeksville/mesh/ui/sharing/Channel.kt | 9 ++-- .../core/ui/component/NodeKeyStatusIcon.kt | 1 - .../org/meshtastic/feature/map/MapView.kt | 47 +++++++++++-------- .../component/FirmwareReleaseSheetContent.kt | 16 ++++++- .../feature/node/component/NodeMenu.kt | 4 +- .../feature/settings/util/UiText.kt | 6 --- 8 files changed, 59 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index a9bbe082a..69f9285d2 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -119,13 +119,17 @@ class MainActivity : AppCompatActivity() { Timber.d("App link data is a channel set") model.requestChannelUrl( url = it, - onFailure = { Toast.makeText(this, Res.string.channel_invalid, Toast.LENGTH_SHORT).show() }, + onFailure = { + Toast.makeText(this, getString(Res.string.channel_invalid), Toast.LENGTH_SHORT).show() + }, ) } else if (it.path?.startsWith("/v/") == true || it.path?.startsWith("/V/") == true) { Timber.d("App link data is a shared contact") model.setSharedContactRequested( url = it, - onFailure = { Toast.makeText(this, Res.string.contact_invalid, Toast.LENGTH_SHORT).show() }, + onFailure = { + Toast.makeText(this, getString(Res.string.contact_invalid), Toast.LENGTH_SHORT).show() + }, ) } else { Timber.d("App link data is not a channel set") diff --git a/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt b/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt index 04681c246..d15997ce5 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt @@ -325,9 +325,9 @@ fun DeleteConfirmationDialog( if (showDialog) { val deleteMessage = pluralStringResource( - id = Res.plurals.delete_messages, - count = selectedCount, - formatArgs = arrayOf(selectedCount), // Pass the count as a format argument + Res.plurals.delete_messages, + selectedCount, + arrayOf(selectedCount), // Pass the count as a format argument ) AlertDialog( diff --git a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt b/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt index 35532fac4..81d82b89a 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt @@ -78,6 +78,7 @@ import androidx.compose.ui.platform.ClipEntry import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction @@ -181,11 +182,12 @@ fun ChannelScreen( } val context = LocalContext.current + val resources = LocalResources.current val barcodeLauncher = rememberLauncherForActivityResult(ScanContract()) { result -> if (result.contents != null) { viewModel.requestChannelUrl(result.contents.toUri()) { - Toast.makeText(context, Res.string.channel_invalid, Toast.LENGTH_SHORT).show() + Toast.makeText(context, resources.getString(Res.string.channel_invalid), Toast.LENGTH_SHORT).show() } } } @@ -223,7 +225,7 @@ fun ChannelScreen( channelSet = channels // Throw away user edits // Tell the user to try again - Toast.makeText(context, Res.string.cant_change_no_radio, Toast.LENGTH_SHORT).show() + Toast.makeText(context, resources.getString(Res.string.cant_change_no_radio), Toast.LENGTH_SHORT).show() } } @@ -312,7 +314,8 @@ fun ChannelScreen( onTrackShare = viewModel::trackShare, onConfirm = { viewModel.requestChannelUrl(it) { - Toast.makeText(context, Res.string.channel_invalid, Toast.LENGTH_SHORT).show() + Toast.makeText(context, resources.getString(Res.string.channel_invalid), Toast.LENGTH_SHORT) + .show() } }, ) diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeKeyStatusIcon.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeKeyStatusIcon.kt index 3bbf85053..723b9b674 100644 --- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeKeyStatusIcon.kt +++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/NodeKeyStatusIcon.kt @@ -106,7 +106,6 @@ fun NodeKeyStatusIcon( imageVector = icon, contentDescription = stringResource( - id = when { mismatchKey -> Res.string.encryption_error hasPKC -> Res.string.encryption_pkc diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt index ccb291220..a864d7cc5 100644 --- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt +++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt @@ -57,6 +57,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView @@ -162,7 +163,7 @@ private fun cacheManagerCallback(onTaskComplete: () -> Unit, onTaskFailed: (Int) private fun Context.purgeTileSource(onResult: (String) -> Unit) { val cache = SqlTileWriterExt() val builder = MaterialAlertDialogBuilder(this) - builder.setTitle(Res.string.map_tile_source) + builder.setTitle(getString(Res.string.map_tile_source)) val sources = cache.sources val sourceList = mutableListOf() for (i in sources.indices) { @@ -177,7 +178,7 @@ private fun Context.purgeTileSource(onResult: (String) -> Unit) { selectedList.remove(i) } } - builder.setPositiveButton(Res.string.clear) { _, _ -> + builder.setPositiveButton(getString(Res.string.clear)) { _, _ -> for (x in selectedList) { val item = sources[x] val b = cache.purgeCache(item.source) @@ -190,7 +191,7 @@ private fun Context.purgeTileSource(onResult: (String) -> Unit) { ) } } - builder.setNegativeButton(Res.string.cancel) { dialog, _ -> dialog.cancel() } + builder.setNegativeButton(getString(Res.string.cancel)) { dialog, _ -> dialog.cancel() } builder.show() } @@ -223,6 +224,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails: var showCurrentCacheInfo by remember { mutableStateOf(false) } val context = LocalContext.current + val resources = LocalResources.current val density = LocalDensity.current val haptic = LocalHapticFeedback.current @@ -262,7 +264,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, Res.string.location_disabled, Toast.LENGTH_SHORT).show() + Toast.makeText(context, resources.getString(Res.string.location_disabled), Toast.LENGTH_SHORT).show() return } Timber.d("user clicked MyLocationNewOverlay ${myLocationOverlay == null}") @@ -323,7 +325,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails: id = u.id title = u.longName snippet = - context.getString( + resources.getString( Res.string.map_node_popup_details, node.gpsString(), formatAgo(node.lastHeard), @@ -331,7 +333,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails: if (node.batteryStr != "") node.batteryStr else "?", ) ourNode?.distanceStr(node, displayUnits)?.let { dist -> - subDescription = context.getString(Res.string.map_subDescription, ourNode.bearing(node), dist) + subDescription = resources.getString(Res.string.map_subDescription, ourNode.bearing(node), dist) } setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) position = nodePosition @@ -352,14 +354,16 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails: fun showDeleteMarkerDialog(waypoint: Waypoint) { val builder = MaterialAlertDialogBuilder(context) - 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) { _, _ -> + builder.setTitle(resources.getString(Res.string.waypoint_delete)) + builder.setNeutralButton(resources.getString(Res.string.cancel)) { _, _ -> + Timber.d("User canceled marker delete dialog") + } + builder.setNegativeButton(resources.getString(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(Res.string.delete_for_everyone) { _, _ -> + builder.setPositiveButton(resources.getString(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 +398,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails: } fun getUsername(id: String?) = if (id == DataPacket.ID_LOCAL) { - context.getString(Res.string.you) + resources.getString(Res.string.you) } else { mapViewModel.getUser(id).longName } @@ -447,22 +451,22 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails: LaunchedEffect(showCurrentCacheInfo) { if (!showCurrentCacheInfo) return@LaunchedEffect - Toast.makeText(context, Res.string.calculating, Toast.LENGTH_SHORT).show() + Toast.makeText(context, resources.getString(Res.string.calculating), Toast.LENGTH_SHORT).show() val cacheManager = CacheManager(map) val cacheCapacity = cacheManager.cacheCapacity() val currentCacheUsage = cacheManager.currentCacheUsage() val mapCacheInfoText = - context.getString( + resources.getString( Res.string.map_cache_info, cacheCapacity / (1024.0 * 1024.0), currentCacheUsage / (1024.0 * 1024.0), ) MaterialAlertDialogBuilder(context) - .setTitle(Res.string.map_cache_manager) + .setTitle(resources.getString(Res.string.map_cache_manager)) .setMessage(mapCacheInfoText) - .setPositiveButton(Res.string.close) { dialog, _ -> + .setPositiveButton(resources.getString(Res.string.close)) { dialog, _ -> showCurrentCacheInfo = false dialog.dismiss() } @@ -524,7 +528,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails: val tileCount: Int = CacheManager(this) .possibleTilesInArea(downloadRegionBoundingBox, zoomLevelMin.toInt(), zoomLevelMax.toInt()) - cacheEstimate = context.getString(Res.string.map_cache_tiles, tileCount) + cacheEstimate = resources.getString(Res.string.map_cache_tiles, tileCount) } val boxOverlayListener = @@ -556,13 +560,18 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails: zoomLevelMax.toInt(), cacheManagerCallback( onTaskComplete = { - Toast.makeText(context, Res.string.map_download_complete, Toast.LENGTH_SHORT).show() + Toast.makeText( + context, + resources.getString(Res.string.map_download_complete), + Toast.LENGTH_SHORT, + ) + .show() writer.onDetach() }, onTaskFailed = { errors -> Toast.makeText( context, - context.getString(Res.string.map_download_errors, errors), + resources.getString(Res.string.map_download_errors, errors), Toast.LENGTH_SHORT, ) .show() @@ -594,7 +603,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails: fun Context.showCacheManagerDialog() { MaterialAlertDialogBuilder(this) - .setTitle(Res.string.map_offline_manager) + .setTitle(resources.getString(Res.string.map_offline_manager)) .setItems( arrayOf( getString(Res.string.map_cache_size), diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt index 8a55d1a47..48f05c8cd 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt @@ -39,6 +39,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.net.toUri @@ -50,6 +51,7 @@ import org.meshtastic.core.strings.R as Res @Composable fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modifier = Modifier) { val context = LocalContext.current + val resources = LocalResources.current Column( modifier = modifier.verticalScroll(rememberScrollState()).padding(16.dp).fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp), @@ -64,7 +66,12 @@ fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modi val intent = Intent(Intent.ACTION_VIEW, firmwareRelease.pageUrl.toUri()) context.startActivity(intent) } catch (e: ActivityNotFoundException) { - Toast.makeText(context, Res.string.error_no_app_to_handle_link, Toast.LENGTH_LONG).show() + Toast.makeText( + context, + resources.getString(Res.string.error_no_app_to_handle_link), + Toast.LENGTH_LONG, + ) + .show() Timber.e(e) } }, @@ -80,7 +87,12 @@ fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modi val intent = Intent(Intent.ACTION_VIEW, firmwareRelease.zipUrl.toUri()) context.startActivity(intent) } catch (e: ActivityNotFoundException) { - Toast.makeText(context, Res.string.error_no_app_to_handle_link, Toast.LENGTH_LONG).show() + Toast.makeText( + context, + resources.getString(Res.string.error_no_app_to_handle_link), + Toast.LENGTH_LONG, + ) + .show() Timber.e(e) } }, diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeMenu.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeMenu.kt index bca0cf51c..e4aff468b 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeMenu.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeMenu.kt @@ -39,7 +39,7 @@ fun NodeActionDialogs( title = Res.string.favorite, text = stringResource( - id = if (node.isFavorite) Res.string.favorite_remove else Res.string.favorite_add, + if (node.isFavorite) Res.string.favorite_remove else Res.string.favorite_add, node.user.longName, ), onConfirm = { @@ -54,7 +54,7 @@ fun NodeActionDialogs( title = Res.string.ignore, text = stringResource( - id = if (node.isIgnored) Res.string.ignore_remove else Res.string.ignore_add, + if (node.isIgnored) Res.string.ignore_remove else Res.string.ignore_add, node.user.longName, ), onConfirm = { diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/UiText.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/UiText.kt index b92ff1546..821b1b151 100644 --- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/UiText.kt +++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/UiText.kt @@ -17,7 +17,6 @@ package org.meshtastic.feature.settings.util -import android.content.Context import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource @@ -33,9 +32,4 @@ sealed class UiText { is DynamicString -> value is StringResource -> stringResource(resId, *args) } - - fun asString(context: Context): String = when (this) { - is DynamicString -> value - is StringResource -> context.getString(resId, *args) - } }