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

This commit is contained in:
Phil Oliver 2025-11-10 19:58:38 -05:00 committed by GitHub
parent d7fff4add2
commit 28590bfcdf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
206 changed files with 1722 additions and 470 deletions

View file

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

View file

@ -47,14 +47,18 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.getString
import org.meshtastic.core.datastore.RecentAddressesDataSource
import org.meshtastic.core.datastore.model.RecentAddress
import org.meshtastic.core.model.util.anonymize
import org.meshtastic.core.service.ServiceRepository
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.meshtastic
import org.meshtastic.core.strings.pairing_completed
import org.meshtastic.core.strings.pairing_failed_try_again
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import timber.log.Timber
import javax.inject.Inject
import org.meshtastic.core.strings.R as Res
/**
* A sealed class is used here to represent the different types of devices that can be displayed in the list. This is
@ -135,8 +139,7 @@ constructor(
val idBytes = txtRecords["id"]
val shortName =
shortNameBytes?.let { String(it, Charsets.UTF_8) }
?: context.getString(Res.string.meshtastic)
shortNameBytes?.let { String(it, Charsets.UTF_8) } ?: getString(Res.string.meshtastic)
val deviceId = idBytes?.let { String(it, Charsets.UTF_8) }?.replace("!", "")
var displayName = recentMap[address] ?: shortName
if (deviceId != null && !displayName.split("_").none { it == deviceId }) {
@ -283,10 +286,10 @@ constructor(
if (state != BluetoothDevice.BOND_BONDING) {
Timber.d("Bonding completed, state=$state")
if (state == BluetoothDevice.BOND_BONDED) {
setErrorText(context.getString(Res.string.pairing_completed))
setErrorText(getString(Res.string.pairing_completed))
changeDeviceAddress("x${device.address}")
} else {
setErrorText(context.getString(Res.string.pairing_failed_try_again))
setErrorText(getString(Res.string.pairing_failed_try_again))
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -33,9 +33,13 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import com.geeksville.mesh.model.DeviceListEntry
import org.meshtastic.core.strings.R as Res
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.add
import org.meshtastic.core.strings.bluetooth
import org.meshtastic.core.strings.network
import org.meshtastic.core.strings.serial
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable

View file

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

View file

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

View file

@ -44,7 +44,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
@ -53,10 +52,14 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.model.Contact
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.sample_message
import org.meshtastic.core.strings.some_username
import org.meshtastic.core.strings.unknown_username
import org.meshtastic.core.ui.component.SecurityIcon
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.proto.AppOnlyProtos
import org.meshtastic.core.strings.R as Res
@Suppress("LongMethod")
@Composable

View file

@ -55,19 +55,37 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.Contact
import org.jetbrains.compose.resources.pluralStringResource
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.database.entity.ContactSettings
import org.meshtastic.core.model.util.formatMuteRemainingTime
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.cancel
import org.meshtastic.core.strings.close_selection
import org.meshtastic.core.strings.conversations
import org.meshtastic.core.strings.currently
import org.meshtastic.core.strings.delete
import org.meshtastic.core.strings.delete_messages
import org.meshtastic.core.strings.delete_selection
import org.meshtastic.core.strings.mute_1_week
import org.meshtastic.core.strings.mute_8_hours
import org.meshtastic.core.strings.mute_always
import org.meshtastic.core.strings.mute_notifications
import org.meshtastic.core.strings.mute_status_always
import org.meshtastic.core.strings.mute_status_muted_for_days
import org.meshtastic.core.strings.mute_status_muted_for_hours
import org.meshtastic.core.strings.mute_status_unmuted
import org.meshtastic.core.strings.okay
import org.meshtastic.core.strings.select_all
import org.meshtastic.core.strings.unmute
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.proto.AppOnlyProtos
import java.util.concurrent.TimeUnit
import org.meshtastic.core.strings.R as Res
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Suppress("LongMethod")

View file

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

View file

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

View file

@ -35,7 +35,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
@ -43,9 +42,15 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.Contact
import com.geeksville.mesh.ui.contact.ContactItem
import com.geeksville.mesh.ui.contact.ContactsViewModel
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.sample_message
import org.meshtastic.core.strings.share
import org.meshtastic.core.strings.share_to
import org.meshtastic.core.strings.some_username
import org.meshtastic.core.strings.unknown_username
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.theme.AppTheme
import org.meshtastic.core.strings.R as Res
@Composable
fun ShareScreen(viewModel: ContactsViewModel = hiltViewModel(), onConfirm: (String) -> Unit, onNavigateUp: () -> Unit) {