mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Relocate radio config to new top-level Settings screen (#2834)
This commit is contained in:
parent
af6066d788
commit
f1bb020203
52 changed files with 1077 additions and 1189 deletions
|
|
@ -25,8 +25,8 @@ import androidx.navigation.compose.composable
|
|||
import androidx.navigation.navDeepLink
|
||||
import androidx.navigation.navigation
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.radioconfig.components.ChannelConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.LoRaConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.ChannelConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.LoRaConfigScreen
|
||||
import com.geeksville.mesh.ui.sharing.ChannelScreen
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import androidx.navigation.navigation
|
|||
import com.geeksville.mesh.model.BluetoothViewModel
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.connections.ConnectionsScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.LoRaConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.LoRaConfigScreen
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
|
@ -56,7 +56,7 @@ fun NavGraphBuilder.connectionsGraph(
|
|||
uiViewModel = uiViewModel,
|
||||
bluetoothViewModel = bluetoothViewModel,
|
||||
radioConfigViewModel = hiltViewModel(parentEntry),
|
||||
onNavigateToRadioConfig = { navController.navigate(RadioConfigRoutes.RadioConfig()) },
|
||||
onNavigateToSettings = { navController.navigate(SettingsRoutes.Settings()) },
|
||||
onNavigateToNodeDetails = { navController.navigate(NodesRoutes.NodeDetailGraph(it)) },
|
||||
onConfigNavigate = { route -> navController.navigate(route) },
|
||||
)
|
||||
|
|
@ -66,7 +66,7 @@ fun NavGraphBuilder.connectionsGraph(
|
|||
}
|
||||
|
||||
private fun NavGraphBuilder.configRoutes(navController: NavHostController) {
|
||||
composable<RadioConfigRoutes.LoRa> { backStackEntry ->
|
||||
composable<SettingsRoutes.LoRa> { backStackEntry ->
|
||||
val parentEntry =
|
||||
remember(backStackEntry) { navController.getBackStackEntry(ConnectionsRoutes.ConnectionsGraph) }
|
||||
LoRaConfigScreen(hiltViewModel(parentEntry))
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ fun NavDestination.isNodeDetailRoute(): Boolean = NodeDetailRoute.entries.any {
|
|||
|
||||
fun NavDestination.showLongNameTitle(): Boolean = !this.isTopLevel() &&
|
||||
(
|
||||
this.hasRoute<RadioConfigRoutes.RadioConfig>() ||
|
||||
this.hasRoute<SettingsRoutes.Settings>() ||
|
||||
this.hasRoute<NodesRoutes.NodeDetail>() ||
|
||||
this.isConfigRoute() ||
|
||||
this.isNodeDetailRoute()
|
||||
|
|
@ -85,6 +85,6 @@ fun NavGraph(
|
|||
) {
|
||||
DebugScreen()
|
||||
}
|
||||
radioConfigGraph(navController, uIViewModel)
|
||||
settingsGraph(navController, uIViewModel)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,38 +56,40 @@ import com.geeksville.mesh.AdminProtos
|
|||
import com.geeksville.mesh.MeshProtos.DeviceMetadata
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.radioconfig.CleanNodeDatabaseScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.radioconfig.components.AmbientLightingConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.AudioConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.BluetoothConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.CannedMessageConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.ChannelConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.DetectionSensorConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.DeviceConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.DisplayConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.ExternalNotificationConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.LoRaConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.MQTTConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.NeighborInfoConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.NetworkConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.PaxcounterConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.PositionConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.PowerConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.RangeTestConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.RemoteHardwareConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.SecurityConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.SerialConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.StoreForwardConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.TelemetryConfigScreen
|
||||
import com.geeksville.mesh.ui.radioconfig.components.UserConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.CleanNodeDatabaseScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.components.AmbientLightingConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.AudioConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.BluetoothConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.CannedMessageConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.ChannelConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.DetectionSensorConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.DeviceConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.DisplayConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.ExternalNotificationConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.LoRaConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.MQTTConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.NeighborInfoConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.NetworkConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.PaxcounterConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.PositionConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.PowerConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.RangeTestConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.RemoteHardwareConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.SecurityConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.SerialConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.StoreForwardConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.TelemetryConfigScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.components.UserConfigScreen
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
sealed class RadioConfigRoutes {
|
||||
@Serializable data class RadioConfigGraph(val destNum: Int? = null) : Graph
|
||||
sealed class SettingsRoutes {
|
||||
@Serializable data class SettingsGraph(val destNum: Int? = null) : Graph
|
||||
|
||||
@Serializable data class RadioConfig(val destNum: Int? = null) : Route
|
||||
@Serializable data class Settings(val destNum: Int? = null) : Route
|
||||
|
||||
// region radio Config Routes
|
||||
|
||||
@Serializable data object User : Route
|
||||
|
||||
|
|
@ -109,6 +111,10 @@ sealed class RadioConfigRoutes {
|
|||
|
||||
@Serializable data object Security : Route
|
||||
|
||||
// endregion
|
||||
|
||||
// region module config routes
|
||||
|
||||
@Serializable data object MQTT : Route
|
||||
|
||||
@Serializable data object Serial : Route
|
||||
|
|
@ -135,30 +141,35 @@ sealed class RadioConfigRoutes {
|
|||
|
||||
@Serializable data object Paxcounter : Route
|
||||
|
||||
// endregion
|
||||
|
||||
// region advanced config routes
|
||||
|
||||
@Serializable data object CleanNodeDb : Route
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
fun getNavRouteFrom(routeName: String): Route? =
|
||||
ConfigRoute.entries.find { it.name == routeName }?.route ?: ModuleRoute.entries.find { it.name == routeName }?.route
|
||||
|
||||
@Suppress("LongMethod")
|
||||
fun NavGraphBuilder.radioConfigGraph(navController: NavHostController, uiViewModel: UIViewModel) {
|
||||
navigation<RadioConfigRoutes.RadioConfigGraph>(startDestination = RadioConfigRoutes.RadioConfig()) {
|
||||
composable<RadioConfigRoutes.RadioConfig>(
|
||||
deepLinks =
|
||||
listOf(navDeepLink<RadioConfigRoutes.RadioConfig>(basePath = "$DEEP_LINK_BASE_URI/radio_config")),
|
||||
fun NavGraphBuilder.settingsGraph(navController: NavHostController, uiViewModel: UIViewModel) {
|
||||
navigation<SettingsRoutes.SettingsGraph>(startDestination = SettingsRoutes.Settings()) {
|
||||
composable<SettingsRoutes.Settings>(
|
||||
deepLinks = listOf(navDeepLink<SettingsRoutes.Settings>(basePath = "$DEEP_LINK_BASE_URI/settings")),
|
||||
) { backStackEntry ->
|
||||
val parentEntry =
|
||||
remember(backStackEntry) { navController.getBackStackEntry(RadioConfigRoutes.RadioConfigGraph::class) }
|
||||
remember(backStackEntry) { navController.getBackStackEntry(SettingsRoutes.SettingsGraph::class) }
|
||||
RadioConfigScreen(uiViewModel = uiViewModel, viewModel = hiltViewModel(parentEntry)) {
|
||||
navController.navigate(it) { popUpTo(RadioConfigRoutes.RadioConfig()) { inclusive = false } }
|
||||
navController.navigate(it) { popUpTo(SettingsRoutes.Settings()) { inclusive = false } }
|
||||
}
|
||||
}
|
||||
composable<RadioConfigRoutes.CleanNodeDb>(
|
||||
composable<SettingsRoutes.CleanNodeDb>(
|
||||
deepLinks =
|
||||
listOf(
|
||||
navDeepLink<RadioConfigRoutes.CleanNodeDb>(
|
||||
basePath = "$DEEP_LINK_BASE_URI/radio_config/clean_node_db",
|
||||
navDeepLink<SettingsRoutes.CleanNodeDb>(
|
||||
basePath = "$DEEP_LINK_BASE_URI/settings/radio/clean_node_db",
|
||||
),
|
||||
),
|
||||
) {
|
||||
|
|
@ -174,7 +185,7 @@ fun NavGraphBuilder.radioConfigGraph(navController: NavHostController, uiViewMod
|
|||
*
|
||||
* This function simplifies adding screens by handling common tasks like:
|
||||
* - Setting up deep links based on the route's name.
|
||||
* - Retrieving the parent [NavBackStackEntry] for the [RadioConfigRoutes.RadioConfigGraph].
|
||||
* - Retrieving the parent [NavBackStackEntry] for the [SettingsRoutes.SettingsGraph].
|
||||
* - Providing the [RadioConfigViewModel] scoped to the parent graph, which the [screenContent] will use.
|
||||
*
|
||||
* @param R The type of the [Route] object, must be serializable.
|
||||
|
|
@ -191,12 +202,14 @@ private inline fun <reified R : Route> NavGraphBuilder.addRadioConfigScreenCompo
|
|||
composable<R>(
|
||||
deepLinks =
|
||||
listOf(
|
||||
navDeepLink<R>(basePath = "$DEEP_LINK_BASE_URI/radio_config/{destNum}/${routeNameString.lowercase()}"),
|
||||
navDeepLink<R>(basePath = "$DEEP_LINK_BASE_URI/radio_config/${routeNameString.lowercase()}"),
|
||||
navDeepLink<R>(
|
||||
basePath = "$DEEP_LINK_BASE_URI/settings/radio/{destNum}/${routeNameString.lowercase()}",
|
||||
),
|
||||
navDeepLink<R>(basePath = "$DEEP_LINK_BASE_URI/settings/radio/${routeNameString.lowercase()}"),
|
||||
),
|
||||
) { backStackEntry ->
|
||||
val parentEntry =
|
||||
remember(backStackEntry) { navController.getBackStackEntry(RadioConfigRoutes.RadioConfigGraph::class) }
|
||||
remember(backStackEntry) { navController.getBackStackEntry(SettingsRoutes.SettingsGraph::class) }
|
||||
val viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry)
|
||||
screenContent(viewModel)
|
||||
}
|
||||
|
|
@ -206,62 +219,46 @@ private inline fun <reified R : Route> NavGraphBuilder.addRadioConfigScreenCompo
|
|||
private fun NavGraphBuilder.configRoutesScreens(navController: NavHostController) {
|
||||
ConfigRoute.entries.forEach { entry ->
|
||||
when (entry.route) {
|
||||
is RadioConfigRoutes.User ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.User>(
|
||||
is SettingsRoutes.User ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.User>(navController, entry.name, entry.screenComposable)
|
||||
is SettingsRoutes.ChannelConfig ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.ChannelConfig>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.ChannelConfig ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.ChannelConfig>(
|
||||
is SettingsRoutes.Device ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Device>(navController, entry.name, entry.screenComposable)
|
||||
is SettingsRoutes.Position ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Position>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Device ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Device>(
|
||||
is SettingsRoutes.Power ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Power>(navController, entry.name, entry.screenComposable)
|
||||
is SettingsRoutes.Network ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Network>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Position ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Position>(
|
||||
is SettingsRoutes.Display ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Display>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Power ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Power>(
|
||||
is SettingsRoutes.LoRa ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.LoRa>(navController, entry.name, entry.screenComposable)
|
||||
is SettingsRoutes.Bluetooth ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Bluetooth>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Network ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Network>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Display ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Display>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.LoRa ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.LoRa>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Bluetooth ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Bluetooth>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Security ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Security>(
|
||||
is SettingsRoutes.Security ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Security>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
|
|
@ -275,80 +272,68 @@ private fun NavGraphBuilder.configRoutesScreens(navController: NavHostController
|
|||
private fun NavGraphBuilder.moduleRoutesScreens(navController: NavHostController) {
|
||||
ModuleRoute.entries.forEach { entry ->
|
||||
when (entry.route) {
|
||||
is RadioConfigRoutes.MQTT ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.MQTT>(
|
||||
is SettingsRoutes.MQTT ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.MQTT>(navController, entry.name, entry.screenComposable)
|
||||
is SettingsRoutes.Serial ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Serial>(navController, entry.name, entry.screenComposable)
|
||||
is SettingsRoutes.ExtNotification ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.ExtNotification>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Serial ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Serial>(
|
||||
is SettingsRoutes.StoreForward ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.StoreForward>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.ExtNotification ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.ExtNotification>(
|
||||
is SettingsRoutes.RangeTest ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.RangeTest>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.StoreForward ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.StoreForward>(
|
||||
is SettingsRoutes.Telemetry ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Telemetry>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.RangeTest ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.RangeTest>(
|
||||
is SettingsRoutes.CannedMessage ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.CannedMessage>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Telemetry ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Telemetry>(
|
||||
is SettingsRoutes.Audio ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Audio>(navController, entry.name, entry.screenComposable)
|
||||
is SettingsRoutes.RemoteHardware ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.RemoteHardware>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.CannedMessage ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.CannedMessage>(
|
||||
is SettingsRoutes.NeighborInfo ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.NeighborInfo>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Audio ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Audio>(
|
||||
is SettingsRoutes.AmbientLighting ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.AmbientLighting>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.RemoteHardware ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.RemoteHardware>(
|
||||
is SettingsRoutes.DetectionSensor ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.DetectionSensor>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.NeighborInfo ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.NeighborInfo>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.AmbientLighting ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.AmbientLighting>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.DetectionSensor ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.DetectionSensor>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
)
|
||||
is RadioConfigRoutes.Paxcounter ->
|
||||
addRadioConfigScreenComposable<RadioConfigRoutes.Paxcounter>(
|
||||
is SettingsRoutes.Paxcounter ->
|
||||
addRadioConfigScreenComposable<SettingsRoutes.Paxcounter>(
|
||||
navController,
|
||||
entry.name,
|
||||
entry.screenComposable,
|
||||
|
|
@ -366,66 +351,66 @@ enum class ConfigRoute(
|
|||
val type: Int = 0,
|
||||
val screenComposable: @Composable (viewModel: RadioConfigViewModel) -> Unit,
|
||||
) {
|
||||
USER(R.string.user, RadioConfigRoutes.User, Icons.Default.Person, 0, { vm -> UserConfigScreen(vm) }),
|
||||
USER(R.string.user, SettingsRoutes.User, Icons.Default.Person, 0, { vm -> UserConfigScreen(vm) }),
|
||||
CHANNELS(
|
||||
R.string.channels,
|
||||
RadioConfigRoutes.ChannelConfig,
|
||||
SettingsRoutes.ChannelConfig,
|
||||
Icons.AutoMirrored.Default.List,
|
||||
0,
|
||||
{ vm -> ChannelConfigScreen(vm) },
|
||||
),
|
||||
DEVICE(
|
||||
R.string.device,
|
||||
RadioConfigRoutes.Device,
|
||||
SettingsRoutes.Device,
|
||||
Icons.Default.Router,
|
||||
AdminProtos.AdminMessage.ConfigType.DEVICE_CONFIG_VALUE,
|
||||
{ vm -> DeviceConfigScreen(vm) },
|
||||
),
|
||||
POSITION(
|
||||
R.string.position,
|
||||
RadioConfigRoutes.Position,
|
||||
SettingsRoutes.Position,
|
||||
Icons.Default.LocationOn,
|
||||
AdminProtos.AdminMessage.ConfigType.POSITION_CONFIG_VALUE,
|
||||
{ vm -> PositionConfigScreen(vm) },
|
||||
),
|
||||
POWER(
|
||||
R.string.power,
|
||||
RadioConfigRoutes.Power,
|
||||
SettingsRoutes.Power,
|
||||
Icons.Default.Power,
|
||||
AdminProtos.AdminMessage.ConfigType.POWER_CONFIG_VALUE,
|
||||
{ vm -> PowerConfigScreen(vm) },
|
||||
),
|
||||
NETWORK(
|
||||
R.string.network,
|
||||
RadioConfigRoutes.Network,
|
||||
SettingsRoutes.Network,
|
||||
Icons.Default.Wifi,
|
||||
AdminProtos.AdminMessage.ConfigType.NETWORK_CONFIG_VALUE,
|
||||
{ vm -> NetworkConfigScreen(vm) },
|
||||
),
|
||||
DISPLAY(
|
||||
R.string.display,
|
||||
RadioConfigRoutes.Display,
|
||||
SettingsRoutes.Display,
|
||||
Icons.Default.DisplaySettings,
|
||||
AdminProtos.AdminMessage.ConfigType.DISPLAY_CONFIG_VALUE,
|
||||
{ vm -> DisplayConfigScreen(vm) },
|
||||
),
|
||||
LORA(
|
||||
R.string.lora,
|
||||
RadioConfigRoutes.LoRa,
|
||||
SettingsRoutes.LoRa,
|
||||
Icons.Default.CellTower,
|
||||
AdminProtos.AdminMessage.ConfigType.LORA_CONFIG_VALUE,
|
||||
{ vm -> LoRaConfigScreen(vm) },
|
||||
),
|
||||
BLUETOOTH(
|
||||
R.string.bluetooth,
|
||||
RadioConfigRoutes.Bluetooth,
|
||||
SettingsRoutes.Bluetooth,
|
||||
Icons.Default.Bluetooth,
|
||||
AdminProtos.AdminMessage.ConfigType.BLUETOOTH_CONFIG_VALUE,
|
||||
{ vm -> BluetoothConfigScreen(vm) },
|
||||
),
|
||||
SECURITY(
|
||||
R.string.security,
|
||||
RadioConfigRoutes.Security,
|
||||
SettingsRoutes.Security,
|
||||
Icons.Default.Security,
|
||||
AdminProtos.AdminMessage.ConfigType.SECURITY_CONFIG_VALUE,
|
||||
{ vm -> SecurityConfigScreen(vm) },
|
||||
|
|
@ -454,91 +439,91 @@ enum class ModuleRoute(
|
|||
) {
|
||||
MQTT(
|
||||
R.string.mqtt,
|
||||
RadioConfigRoutes.MQTT,
|
||||
SettingsRoutes.MQTT,
|
||||
Icons.Default.Cloud,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.MQTT_CONFIG_VALUE,
|
||||
{ vm -> MQTTConfigScreen(vm) },
|
||||
),
|
||||
SERIAL(
|
||||
R.string.serial,
|
||||
RadioConfigRoutes.Serial,
|
||||
SettingsRoutes.Serial,
|
||||
Icons.Default.Usb,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.SERIAL_CONFIG_VALUE,
|
||||
{ vm -> SerialConfigScreen(vm) },
|
||||
),
|
||||
EXT_NOTIFICATION(
|
||||
R.string.external_notification,
|
||||
RadioConfigRoutes.ExtNotification,
|
||||
SettingsRoutes.ExtNotification,
|
||||
Icons.Default.Notifications,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.EXTNOTIF_CONFIG_VALUE,
|
||||
{ vm -> ExternalNotificationConfigScreen(vm) },
|
||||
),
|
||||
STORE_FORWARD(
|
||||
R.string.store_forward,
|
||||
RadioConfigRoutes.StoreForward,
|
||||
SettingsRoutes.StoreForward,
|
||||
Icons.AutoMirrored.Default.Forward,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.STOREFORWARD_CONFIG_VALUE,
|
||||
{ vm -> StoreForwardConfigScreen(vm) },
|
||||
),
|
||||
RANGE_TEST(
|
||||
R.string.range_test,
|
||||
RadioConfigRoutes.RangeTest,
|
||||
SettingsRoutes.RangeTest,
|
||||
Icons.Default.Speed,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.RANGETEST_CONFIG_VALUE,
|
||||
{ vm -> RangeTestConfigScreen(vm) },
|
||||
),
|
||||
TELEMETRY(
|
||||
R.string.telemetry,
|
||||
RadioConfigRoutes.Telemetry,
|
||||
SettingsRoutes.Telemetry,
|
||||
Icons.Default.DataUsage,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.TELEMETRY_CONFIG_VALUE,
|
||||
{ vm -> TelemetryConfigScreen(vm) },
|
||||
),
|
||||
CANNED_MESSAGE(
|
||||
R.string.canned_message,
|
||||
RadioConfigRoutes.CannedMessage,
|
||||
SettingsRoutes.CannedMessage,
|
||||
Icons.AutoMirrored.Default.Message,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.CANNEDMSG_CONFIG_VALUE,
|
||||
{ vm -> CannedMessageConfigScreen(vm) },
|
||||
),
|
||||
AUDIO(
|
||||
R.string.audio,
|
||||
RadioConfigRoutes.Audio,
|
||||
SettingsRoutes.Audio,
|
||||
Icons.AutoMirrored.Default.VolumeUp,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.AUDIO_CONFIG_VALUE,
|
||||
{ vm -> AudioConfigScreen(vm) },
|
||||
),
|
||||
REMOTE_HARDWARE(
|
||||
R.string.remote_hardware,
|
||||
RadioConfigRoutes.RemoteHardware,
|
||||
SettingsRoutes.RemoteHardware,
|
||||
Icons.Default.SettingsRemote,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.REMOTEHARDWARE_CONFIG_VALUE,
|
||||
{ vm -> RemoteHardwareConfigScreen(vm) },
|
||||
),
|
||||
NEIGHBOR_INFO(
|
||||
R.string.neighbor_info,
|
||||
RadioConfigRoutes.NeighborInfo,
|
||||
SettingsRoutes.NeighborInfo,
|
||||
Icons.Default.People,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.NEIGHBORINFO_CONFIG_VALUE,
|
||||
{ vm -> NeighborInfoConfigScreen(vm) },
|
||||
),
|
||||
AMBIENT_LIGHTING(
|
||||
R.string.ambient_lighting,
|
||||
RadioConfigRoutes.AmbientLighting,
|
||||
SettingsRoutes.AmbientLighting,
|
||||
Icons.Default.LightMode,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.AMBIENTLIGHTING_CONFIG_VALUE,
|
||||
{ vm -> AmbientLightingConfigScreen(vm) },
|
||||
),
|
||||
DETECTION_SENSOR(
|
||||
R.string.detection_sensor,
|
||||
RadioConfigRoutes.DetectionSensor,
|
||||
SettingsRoutes.DetectionSensor,
|
||||
Icons.Default.Sensors,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.DETECTIONSENSOR_CONFIG_VALUE,
|
||||
{ vm -> DetectionSensorConfigScreen(vm) },
|
||||
),
|
||||
PAXCOUNTER(
|
||||
R.string.paxcounter,
|
||||
RadioConfigRoutes.Paxcounter,
|
||||
SettingsRoutes.Paxcounter,
|
||||
Icons.Default.PermScanWifi,
|
||||
AdminProtos.AdminMessage.ModuleConfigType.PAXCOUNTER_CONFIG_VALUE,
|
||||
{ vm -> PaxcounterConfigScreen(vm) },
|
||||
|
|
@ -34,7 +34,6 @@ import androidx.compose.foundation.layout.safeDrawingPadding
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.QrCode2
|
||||
import androidx.compose.material.icons.rounded.Wifi
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
|
|
@ -90,8 +89,8 @@ import com.geeksville.mesh.navigation.ContactsRoutes
|
|||
import com.geeksville.mesh.navigation.MapRoutes
|
||||
import com.geeksville.mesh.navigation.NavGraph
|
||||
import com.geeksville.mesh.navigation.NodesRoutes
|
||||
import com.geeksville.mesh.navigation.RadioConfigRoutes
|
||||
import com.geeksville.mesh.navigation.Route
|
||||
import com.geeksville.mesh.navigation.SettingsRoutes
|
||||
import com.geeksville.mesh.repository.radio.MeshActivity
|
||||
import com.geeksville.mesh.service.ConnectionState
|
||||
import com.geeksville.mesh.service.MeshService
|
||||
|
|
@ -104,6 +103,7 @@ import com.geeksville.mesh.ui.common.icons.Conversations
|
|||
import com.geeksville.mesh.ui.common.icons.Map
|
||||
import com.geeksville.mesh.ui.common.icons.MeshtasticIcons
|
||||
import com.geeksville.mesh.ui.common.icons.Nodes
|
||||
import com.geeksville.mesh.ui.common.icons.Settings
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusBlue
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
|
||||
import com.geeksville.mesh.ui.connections.DeviceType
|
||||
|
|
@ -120,7 +120,7 @@ enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector,
|
|||
Conversations(R.string.conversations, MeshtasticIcons.Conversations, ContactsRoutes.ContactsGraph),
|
||||
Nodes(R.string.nodes, MeshtasticIcons.Nodes, NodesRoutes.NodesGraph),
|
||||
Map(R.string.map, MeshtasticIcons.Map, MapRoutes.Map),
|
||||
Share(R.string.bottom_nav_share, Icons.Rounded.QrCode2, ChannelsRoutes.ChannelsGraph),
|
||||
Settings(R.string.bottom_nav_settings, MeshtasticIcons.Settings, SettingsRoutes.SettingsGraph()),
|
||||
Connections(R.string.connections, Icons.Rounded.Wifi, ConnectionsRoutes.ConnectionsGraph),
|
||||
;
|
||||
|
||||
|
|
@ -150,7 +150,6 @@ fun MainScreen(
|
|||
) {
|
||||
val navController = rememberNavController()
|
||||
val connectionState by uIViewModel.connectionState.collectAsStateWithLifecycle()
|
||||
val localConfig by uIViewModel.localConfig.collectAsStateWithLifecycle()
|
||||
val requestChannelSet by uIViewModel.requestChannelSet.collectAsStateWithLifecycle()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
|
|
@ -205,7 +204,7 @@ fun MainScreen(
|
|||
text = { Text(text = message) },
|
||||
onConfirm = {
|
||||
if (compromisedKeys) {
|
||||
navController.navigate(RadioConfigRoutes.Security)
|
||||
navController.navigate(SettingsRoutes.Security)
|
||||
}
|
||||
uIViewModel.clearClientNotification(notification)
|
||||
},
|
||||
|
|
@ -348,13 +347,11 @@ fun MainScreen(
|
|||
}
|
||||
MainAppBar(
|
||||
viewModel = uIViewModel,
|
||||
isManaged = localConfig.security.isManaged,
|
||||
navController = navController,
|
||||
onAction = { action ->
|
||||
if (action is MainMenuAction) {
|
||||
when (action) {
|
||||
MainMenuAction.DEBUG -> navController.navigate(Route.DebugPanel)
|
||||
MainMenuAction.RADIO_CONFIG -> navController.navigate(RadioConfigRoutes.RadioConfig())
|
||||
MainMenuAction.QUICK_CHAT -> navController.navigate(ContactsRoutes.QuickChat)
|
||||
MainMenuAction.SHOW_INTRO -> uIViewModel.onMainMenuAction(action)
|
||||
else -> onAction(action)
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ import com.geeksville.mesh.model.Node
|
|||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.navigation.ContactsRoutes
|
||||
import com.geeksville.mesh.navigation.NodesRoutes
|
||||
import com.geeksville.mesh.navigation.RadioConfigRoutes
|
||||
import com.geeksville.mesh.navigation.Route
|
||||
import com.geeksville.mesh.navigation.SettingsRoutes
|
||||
import com.geeksville.mesh.navigation.showLongNameTitle
|
||||
import com.geeksville.mesh.ui.TopLevelDestination.Companion.isTopLevel
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.debug.DebugMenuActions
|
||||
import com.geeksville.mesh.ui.node.components.NodeChip
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigMenuActions
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigMenuActions
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
|
@ -70,7 +70,6 @@ fun MainAppBar(
|
|||
modifier: Modifier = Modifier,
|
||||
viewModel: UIViewModel = hiltViewModel(),
|
||||
navController: NavHostController,
|
||||
isManaged: Boolean,
|
||||
onAction: (Any?) -> Unit,
|
||||
) {
|
||||
val backStackEntry by navController.currentBackStackEntryAsState()
|
||||
|
|
@ -119,11 +118,11 @@ fun MainAppBar(
|
|||
actions = {
|
||||
currentDestination?.let {
|
||||
when {
|
||||
it.isTopLevel() -> MainMenuActions(isManaged, onAction)
|
||||
it.isTopLevel() -> MainMenuActions(onAction)
|
||||
|
||||
currentDestination.hasRoute<Route.DebugPanel>() -> DebugMenuActions()
|
||||
|
||||
currentDestination.hasRoute<RadioConfigRoutes.RadioConfig>() ->
|
||||
currentDestination.hasRoute<SettingsRoutes.Settings>() ->
|
||||
RadioConfigMenuActions(viewModel = viewModel)
|
||||
|
||||
else -> {}
|
||||
|
|
@ -208,7 +207,6 @@ private fun TopBarActions(
|
|||
|
||||
enum class MainMenuAction(@StringRes val stringRes: Int) {
|
||||
DEBUG(R.string.debug_panel),
|
||||
RADIO_CONFIG(R.string.radio_configuration),
|
||||
EXPORT_RANGETEST(R.string.save_rangetest),
|
||||
THEME(R.string.theme),
|
||||
LANGUAGE(R.string.preferences_language),
|
||||
|
|
@ -217,7 +215,7 @@ enum class MainMenuAction(@StringRes val stringRes: Int) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun MainMenuActions(isManaged: Boolean, onAction: (MainMenuAction) -> Unit) {
|
||||
private fun MainMenuActions(onAction: (MainMenuAction) -> Unit) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = { showMenu = true }) {
|
||||
Icon(imageVector = Icons.Default.MoreVert, contentDescription = stringResource(R.string.overflow_menu))
|
||||
|
|
@ -235,11 +233,7 @@ private fun MainMenuActions(isManaged: Boolean, onAction: (MainMenuAction) -> Un
|
|||
onAction(action)
|
||||
showMenu = false
|
||||
},
|
||||
enabled =
|
||||
when (action) {
|
||||
MainMenuAction.RADIO_CONFIG -> !isManaged
|
||||
else -> true
|
||||
},
|
||||
enabled = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -257,7 +251,7 @@ private fun MainAppBarPreview(@PreviewParameter(BooleanProvider::class) canNavig
|
|||
showNodeChip = true,
|
||||
canNavigateUp = canNavigateUp,
|
||||
onNavigateUp = {},
|
||||
actions = { MainMenuActions(isManaged = false, onAction = {}) },
|
||||
actions = { MainMenuActions(onAction = {}) },
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ import com.geeksville.mesh.channelSet
|
|||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.model.Channel
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.radioconfig.components.ChannelSelection
|
||||
import com.geeksville.mesh.ui.settings.radio.components.ChannelSelection
|
||||
|
||||
@Composable
|
||||
fun ScannedQrCodeDialog(viewModel: UIViewModel, incoming: ChannelSet) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.connections.components
|
||||
package com.geeksville.mesh.ui.common.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -35,8 +35,8 @@ import androidx.compose.ui.unit.dp
|
|||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun TitledCard(title: String, content: @Composable ColumnScope.() -> Unit) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
fun TitledCard(title: String, modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
|
||||
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
title,
|
||||
modifier = Modifier.padding(horizontal = 16.dp).fillMaxWidth(),
|
||||
|
|
@ -49,6 +49,6 @@ fun TitledCard(title: String, content: @Composable ColumnScope.() -> Unit) {
|
|||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
fun TitledCardPreview() {
|
||||
private fun TitledCardPreview() {
|
||||
AppTheme { Surface { TitledCard(title = "Title") { Box(modifier = Modifier.fillMaxWidth().height(100.dp)) {} } } }
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.common.icons
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* This is from Material Symbols.
|
||||
*
|
||||
* @see
|
||||
* [settings](https://fonts.google.com/icons?selected=Material+Symbols+Rounded:settings:FILL@0;wght@400;GRAD@0;opsz@24&icon.style=Rounded&icon.query=settings&icon.set=Material+Symbols&icon.size=24&icon.color=%23e3e3e3&icon.platform=android)
|
||||
*/
|
||||
val MeshtasticIcons.Settings: ImageVector
|
||||
get() {
|
||||
if (settings != null) {
|
||||
return settings!!
|
||||
}
|
||||
settings =
|
||||
ImageVector.Builder(
|
||||
name = "Settings",
|
||||
defaultWidth = 24.dp,
|
||||
defaultHeight = 24.dp,
|
||||
viewportWidth = 960f,
|
||||
viewportHeight = 960f,
|
||||
)
|
||||
.apply {
|
||||
path(fill = SolidColor(Color(0xFFE3E3E3))) {
|
||||
moveTo(433f, 880f)
|
||||
quadToRelative(-27f, 0f, -46.5f, -18f)
|
||||
reflectiveQuadTo(363f, 818f)
|
||||
lineToRelative(-9f, -66f)
|
||||
quadToRelative(-13f, -5f, -24.5f, -12f)
|
||||
reflectiveQuadTo(307f, 725f)
|
||||
lineToRelative(-62f, 26f)
|
||||
quadToRelative(-25f, 11f, -50f, 2f)
|
||||
reflectiveQuadToRelative(-39f, -32f)
|
||||
lineToRelative(-47f, -82f)
|
||||
quadToRelative(-14f, -23f, -8f, -49f)
|
||||
reflectiveQuadToRelative(27f, -43f)
|
||||
lineToRelative(53f, -40f)
|
||||
quadToRelative(-1f, -7f, -1f, -13.5f)
|
||||
verticalLineToRelative(-27f)
|
||||
quadToRelative(0f, -6.5f, 1f, -13.5f)
|
||||
lineToRelative(-53f, -40f)
|
||||
quadToRelative(-21f, -17f, -27f, -43f)
|
||||
reflectiveQuadToRelative(8f, -49f)
|
||||
lineToRelative(47f, -82f)
|
||||
quadToRelative(14f, -23f, 39f, -32f)
|
||||
reflectiveQuadToRelative(50f, 2f)
|
||||
lineToRelative(62f, 26f)
|
||||
quadToRelative(11f, -8f, 23f, -15f)
|
||||
reflectiveQuadToRelative(24f, -12f)
|
||||
lineToRelative(9f, -66f)
|
||||
quadToRelative(4f, -26f, 23.5f, -44f)
|
||||
reflectiveQuadToRelative(46.5f, -18f)
|
||||
horizontalLineToRelative(94f)
|
||||
quadToRelative(27f, 0f, 46.5f, 18f)
|
||||
reflectiveQuadToRelative(23.5f, 44f)
|
||||
lineToRelative(9f, 66f)
|
||||
quadToRelative(13f, 5f, 24.5f, 12f)
|
||||
reflectiveQuadToRelative(22.5f, 15f)
|
||||
lineToRelative(62f, -26f)
|
||||
quadToRelative(25f, -11f, 50f, -2f)
|
||||
reflectiveQuadToRelative(39f, 32f)
|
||||
lineToRelative(47f, 82f)
|
||||
quadToRelative(14f, 23f, 8f, 49f)
|
||||
reflectiveQuadToRelative(-27f, 43f)
|
||||
lineToRelative(-53f, 40f)
|
||||
quadToRelative(1f, 7f, 1f, 13.5f)
|
||||
verticalLineToRelative(27f)
|
||||
quadToRelative(0f, 6.5f, -2f, 13.5f)
|
||||
lineToRelative(53f, 40f)
|
||||
quadToRelative(21f, 17f, 27f, 43f)
|
||||
reflectiveQuadToRelative(-8f, 49f)
|
||||
lineToRelative(-48f, 82f)
|
||||
quadToRelative(-14f, 23f, -39f, 32f)
|
||||
reflectiveQuadToRelative(-50f, -2f)
|
||||
lineToRelative(-60f, -26f)
|
||||
quadToRelative(-11f, 8f, -23f, 15f)
|
||||
reflectiveQuadToRelative(-24f, 12f)
|
||||
lineToRelative(-9f, 66f)
|
||||
quadToRelative(-4f, 26f, -23.5f, 44f)
|
||||
reflectiveQuadTo(527f, 880f)
|
||||
horizontalLineToRelative(-94f)
|
||||
close()
|
||||
moveTo(440f, 800f)
|
||||
horizontalLineToRelative(79f)
|
||||
lineToRelative(14f, -106f)
|
||||
quadToRelative(31f, -8f, 57.5f, -23.5f)
|
||||
reflectiveQuadTo(639f, 633f)
|
||||
lineToRelative(99f, 41f)
|
||||
lineToRelative(39f, -68f)
|
||||
lineToRelative(-86f, -65f)
|
||||
quadToRelative(5f, -14f, 7f, -29.5f)
|
||||
reflectiveQuadToRelative(2f, -31.5f)
|
||||
quadToRelative(0f, -16f, -2f, -31.5f)
|
||||
reflectiveQuadToRelative(-7f, -29.5f)
|
||||
lineToRelative(86f, -65f)
|
||||
lineToRelative(-39f, -68f)
|
||||
lineToRelative(-99f, 42f)
|
||||
quadToRelative(-22f, -23f, -48.5f, -38.5f)
|
||||
reflectiveQuadTo(533f, 266f)
|
||||
lineToRelative(-13f, -106f)
|
||||
horizontalLineToRelative(-79f)
|
||||
lineToRelative(-14f, 106f)
|
||||
quadToRelative(-31f, 8f, -57.5f, 23.5f)
|
||||
reflectiveQuadTo(321f, 327f)
|
||||
lineToRelative(-99f, -41f)
|
||||
lineToRelative(-39f, 68f)
|
||||
lineToRelative(86f, 64f)
|
||||
quadToRelative(-5f, 15f, -7f, 30f)
|
||||
reflectiveQuadToRelative(-2f, 32f)
|
||||
quadToRelative(0f, 16f, 2f, 31f)
|
||||
reflectiveQuadToRelative(7f, 30f)
|
||||
lineToRelative(-86f, 65f)
|
||||
lineToRelative(39f, 68f)
|
||||
lineToRelative(99f, -42f)
|
||||
quadToRelative(22f, 23f, 48.5f, 38.5f)
|
||||
reflectiveQuadTo(427f, 694f)
|
||||
lineToRelative(13f, 106f)
|
||||
close()
|
||||
moveTo(482f, 620f)
|
||||
quadToRelative(58f, 0f, 99f, -41f)
|
||||
reflectiveQuadToRelative(41f, -99f)
|
||||
quadToRelative(0f, -58f, -41f, -99f)
|
||||
reflectiveQuadToRelative(-99f, -41f)
|
||||
quadToRelative(-59f, 0f, -99.5f, 41f)
|
||||
reflectiveQuadTo(342f, 480f)
|
||||
quadToRelative(0f, 58f, 40.5f, 99f)
|
||||
reflectiveQuadToRelative(99.5f, 41f)
|
||||
close()
|
||||
moveTo(480f, 480f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
return settings!!
|
||||
}
|
||||
|
||||
private var settings: ImageVector? = null
|
||||
|
|
@ -94,8 +94,8 @@ import com.geeksville.mesh.model.NO_DEVICE_SELECTED
|
|||
import com.geeksville.mesh.model.Node
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.navigation.ConfigRoute
|
||||
import com.geeksville.mesh.navigation.RadioConfigRoutes
|
||||
import com.geeksville.mesh.navigation.Route
|
||||
import com.geeksville.mesh.navigation.SettingsRoutes
|
||||
import com.geeksville.mesh.navigation.getNavRouteFrom
|
||||
import com.geeksville.mesh.service.ConnectionState
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
|
|
@ -103,8 +103,8 @@ import com.geeksville.mesh.ui.connections.components.BLEDevices
|
|||
import com.geeksville.mesh.ui.connections.components.CurrentlyConnectedCard
|
||||
import com.geeksville.mesh.ui.connections.components.NetworkDevices
|
||||
import com.geeksville.mesh.ui.connections.components.UsbDevices
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.radioconfig.components.PacketResponseStateDialog
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.components.PacketResponseStateDialog
|
||||
import com.geeksville.mesh.ui.sharing.SharedContactDialog
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
|
|
@ -129,7 +129,7 @@ fun ConnectionsScreen(
|
|||
scanModel: BTScanModel = hiltViewModel(),
|
||||
bluetoothViewModel: BluetoothViewModel = hiltViewModel(),
|
||||
radioConfigViewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
onNavigateToRadioConfig: () -> Unit,
|
||||
onNavigateToSettings: () -> Unit,
|
||||
onNavigateToNodeDetails: (Int) -> Unit,
|
||||
onConfigNavigate: (Route) -> Unit,
|
||||
) {
|
||||
|
|
@ -167,8 +167,8 @@ fun ConnectionsScreen(
|
|||
getNavRouteFrom(radioConfigState.route)?.let { route ->
|
||||
isWaiting = false
|
||||
radioConfigViewModel.clearPacketResponse()
|
||||
if (route == RadioConfigRoutes.LoRa) {
|
||||
onConfigNavigate(RadioConfigRoutes.LoRa)
|
||||
if (route == SettingsRoutes.LoRa) {
|
||||
onConfigNavigate(SettingsRoutes.LoRa)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -260,7 +260,7 @@ fun ConnectionsScreen(
|
|||
node = node,
|
||||
onNavigateToNodeDetails = onNavigateToNodeDetails,
|
||||
onSetShowSharedContact = { showSharedContact = it },
|
||||
onNavigateToRadioConfig = onNavigateToRadioConfig,
|
||||
onNavigateToSettings = onNavigateToSettings,
|
||||
onClickDisconnect = { scanModel.disconnect() },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import com.geeksville.mesh.R
|
|||
import com.geeksville.mesh.model.BTScanModel
|
||||
import com.geeksville.mesh.model.DeviceListEntry
|
||||
import com.geeksville.mesh.service.ConnectionState
|
||||
import com.geeksville.mesh.ui.common.components.TitledCard
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.MultiplePermissionsState
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ fun CurrentlyConnectedCard(
|
|||
modifier: Modifier = Modifier,
|
||||
onNavigateToNodeDetails: (Int) -> Unit,
|
||||
onSetShowSharedContact: (Node) -> Unit,
|
||||
onNavigateToRadioConfig: () -> Unit,
|
||||
onNavigateToSettings: () -> Unit,
|
||||
onClickDisconnect: () -> Unit,
|
||||
) {
|
||||
Card(modifier = modifier) {
|
||||
|
|
@ -98,7 +98,7 @@ fun CurrentlyConnectedCard(
|
|||
}
|
||||
}
|
||||
|
||||
IconButton(enabled = true, onClick = onNavigateToRadioConfig) {
|
||||
IconButton(enabled = true, onClick = onNavigateToSettings) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = stringResource(id = R.string.radio_configuration),
|
||||
|
|
@ -142,7 +142,7 @@ private fun CurrentlyConnectedCardPreview() {
|
|||
),
|
||||
onNavigateToNodeDetails = {},
|
||||
onSetShowSharedContact = {},
|
||||
onNavigateToRadioConfig = {},
|
||||
onNavigateToSettings = {},
|
||||
onClickDisconnect = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import com.geeksville.mesh.model.BTScanModel
|
|||
import com.geeksville.mesh.model.DeviceListEntry
|
||||
import com.geeksville.mesh.repository.network.NetworkRepository
|
||||
import com.geeksville.mesh.service.ConnectionState
|
||||
import com.geeksville.mesh.ui.common.components.TitledCard
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.connections.isIPAddress
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import com.geeksville.mesh.R
|
|||
import com.geeksville.mesh.model.BTScanModel
|
||||
import com.geeksville.mesh.model.DeviceListEntry
|
||||
import com.geeksville.mesh.service.ConnectionState
|
||||
import com.geeksville.mesh.ui.common.components.TitledCard
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -141,8 +141,8 @@ import com.geeksville.mesh.model.Node
|
|||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.model.isUnmessageableRole
|
||||
import com.geeksville.mesh.navigation.NodeDetailRoutes
|
||||
import com.geeksville.mesh.navigation.RadioConfigRoutes
|
||||
import com.geeksville.mesh.navigation.Route
|
||||
import com.geeksville.mesh.navigation.SettingsRoutes
|
||||
import com.geeksville.mesh.service.ServiceAction
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.preview.NodePreviewParameterProvider
|
||||
|
|
@ -153,7 +153,7 @@ import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
|||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
|
||||
import com.geeksville.mesh.ui.node.components.NodeActionDialogs
|
||||
import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
||||
import com.geeksville.mesh.ui.radioconfig.NavCard
|
||||
import com.geeksville.mesh.ui.settings.radio.NavCard
|
||||
import com.geeksville.mesh.ui.sharing.SharedContactDialog
|
||||
import com.geeksville.mesh.util.UnitConversions
|
||||
import com.geeksville.mesh.util.UnitConversions.toTempString
|
||||
|
|
@ -440,7 +440,7 @@ private fun AdministrationSection(
|
|||
icon = Icons.Default.Settings,
|
||||
enabled = metricsState.isLocal || node.metadata != null,
|
||||
) {
|
||||
onAction(NodeDetailAction.Navigate(RadioConfigRoutes.RadioConfig(node.num)))
|
||||
onAction(NodeDetailAction.Navigate(SettingsRoutes.Settings(node.num)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.settings.components
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.rounded.Android
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
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.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun SettingsItem(
|
||||
text: String,
|
||||
enabled: Boolean,
|
||||
leadingIcon: ImageVector? = null,
|
||||
trailingIcon: ImageVector? = Icons.AutoMirrored.Rounded.KeyboardArrowRight,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Card(
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp, horizontal = 16.dp),
|
||||
) {
|
||||
leadingIcon?.let {
|
||||
Icon(imageVector = it, contentDescription = text, modifier = Modifier.size(24.dp))
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
}
|
||||
Text(text = text, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f))
|
||||
|
||||
trailingIcon?.let {
|
||||
Icon(imageVector = it, contentDescription = null, modifier = Modifier.wrapContentSize())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SettingsItemPreview() {
|
||||
AppTheme { SettingsItem(text = "Text", leadingIcon = Icons.Rounded.Android, enabled = true) {} }
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SettingsItemDisabledPreview() {
|
||||
AppTheme { SettingsItem(text = "Text", leadingIcon = Icons.Rounded.Android, enabled = false) {} }
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig
|
||||
package com.geeksville.mesh.ui.settings.radio
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig
|
||||
package com.geeksville.mesh.ui.settings.radio
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig
|
||||
package com.geeksville.mesh.ui.settings.radio
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
|
|
@ -72,13 +72,15 @@ import com.geeksville.mesh.model.UIViewModel
|
|||
import com.geeksville.mesh.navigation.AdminRoute
|
||||
import com.geeksville.mesh.navigation.ConfigRoute
|
||||
import com.geeksville.mesh.navigation.ModuleRoute
|
||||
import com.geeksville.mesh.navigation.RadioConfigRoutes
|
||||
import com.geeksville.mesh.navigation.Route
|
||||
import com.geeksville.mesh.navigation.SettingsRoutes
|
||||
import com.geeksville.mesh.navigation.getNavRouteFrom
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.TitledCard
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.radioconfig.components.EditDeviceProfileDialog
|
||||
import com.geeksville.mesh.ui.radioconfig.components.PacketResponseStateDialog
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItem
|
||||
import com.geeksville.mesh.ui.settings.radio.components.EditDeviceProfileDialog
|
||||
import com.geeksville.mesh.ui.settings.radio.components.PacketResponseStateDialog
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
|
|
@ -105,6 +107,7 @@ fun RadioConfigScreen(
|
|||
nodeName?.let { uiViewModel.setTitle(it) }
|
||||
|
||||
val excludedModulesUnlocked by uiViewModel.excludedModulesUnlocked.collectAsStateWithLifecycle()
|
||||
val localConfig by uiViewModel.localConfig.collectAsStateWithLifecycle()
|
||||
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
var isWaiting by remember { mutableStateOf(false) }
|
||||
|
|
@ -177,6 +180,7 @@ fun RadioConfigScreen(
|
|||
RadioConfigItemList(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
isManaged = localConfig.security.isManaged,
|
||||
excludedModulesUnlocked = excludedModulesUnlocked,
|
||||
onRouteClick = { route ->
|
||||
isWaiting = true
|
||||
|
|
@ -272,9 +276,11 @@ private fun NavButton(@StringRes title: Int, enabled: Boolean, onClick: () -> Un
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun RadioConfigItemList(
|
||||
state: RadioConfigState,
|
||||
isManaged: Boolean,
|
||||
excludedModulesUnlocked: Boolean = false,
|
||||
modifier: Modifier = Modifier,
|
||||
onRouteClick: (Enum<*>) -> Unit = {},
|
||||
|
|
@ -282,7 +288,7 @@ private fun RadioConfigItemList(
|
|||
onExport: () -> Unit = {},
|
||||
onNavigate: (Route) -> Unit,
|
||||
) {
|
||||
val enabled = state.connected && !state.responseState.isWaiting()
|
||||
val enabled = state.connected && !state.responseState.isWaiting() && !isManaged
|
||||
var modules by remember { mutableStateOf(ModuleRoute.filterExcludedFrom(state.metadata)) }
|
||||
LaunchedEffect(excludedModulesUnlocked) {
|
||||
if (excludedModulesUnlocked) {
|
||||
|
|
@ -291,44 +297,72 @@ private fun RadioConfigItemList(
|
|||
modules = ModuleRoute.filterExcludedFrom(state.metadata)
|
||||
}
|
||||
}
|
||||
LazyColumn(modifier = modifier, contentPadding = PaddingValues(horizontal = 16.dp)) {
|
||||
item { PreferenceCategory(stringResource(R.string.radio_configuration)) }
|
||||
items(ConfigRoute.filterExcludedFrom(state.metadata)) {
|
||||
NavCard(title = stringResource(it.title), icon = it.icon, enabled = enabled) { onRouteClick(it) }
|
||||
LazyColumn(modifier = modifier, contentPadding = PaddingValues(16.dp)) {
|
||||
item {
|
||||
TitledCard(title = stringResource(R.string.radio_configuration)) {
|
||||
if (isManaged) {
|
||||
ManagedMessage()
|
||||
}
|
||||
|
||||
ConfigRoute.filterExcludedFrom(state.metadata).forEach {
|
||||
SettingsItem(text = stringResource(it.title), leadingIcon = it.icon, enabled = enabled) {
|
||||
onRouteClick(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item { PreferenceCategory(stringResource(R.string.module_settings)) }
|
||||
items(modules) {
|
||||
NavCard(title = stringResource(it.title), icon = it.icon, enabled = enabled) { onRouteClick(it) }
|
||||
item {
|
||||
TitledCard(title = stringResource(R.string.module_settings), modifier = Modifier.padding(top = 16.dp)) {
|
||||
if (isManaged) {
|
||||
ManagedMessage()
|
||||
}
|
||||
|
||||
modules.forEach {
|
||||
SettingsItem(text = stringResource(it.title), leadingIcon = it.icon, enabled = enabled) {
|
||||
onRouteClick(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.isLocal) {
|
||||
item {
|
||||
PreferenceCategory(stringResource(R.string.backup_restore))
|
||||
NavCard(
|
||||
title = stringResource(R.string.import_configuration),
|
||||
icon = Icons.Default.Download,
|
||||
enabled = enabled,
|
||||
onClick = onImport,
|
||||
)
|
||||
NavCard(
|
||||
title = stringResource(R.string.export_configuration),
|
||||
icon = Icons.Default.Upload,
|
||||
enabled = enabled,
|
||||
onClick = onExport,
|
||||
)
|
||||
TitledCard(title = stringResource(R.string.backup_restore), modifier = Modifier.padding(top = 16.dp)) {
|
||||
if (isManaged) {
|
||||
ManagedMessage()
|
||||
}
|
||||
|
||||
SettingsItem(
|
||||
text = stringResource(R.string.import_configuration),
|
||||
leadingIcon = Icons.Default.Download,
|
||||
enabled = enabled,
|
||||
onClick = onImport,
|
||||
)
|
||||
SettingsItem(
|
||||
text = stringResource(R.string.export_configuration),
|
||||
leadingIcon = Icons.Default.Upload,
|
||||
enabled = enabled,
|
||||
onClick = onExport,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items(AdminRoute.entries) { NavButton(it.title, enabled) { onRouteClick(it) } }
|
||||
|
||||
item {
|
||||
PreferenceCategory("Advanced")
|
||||
NavCard(
|
||||
title = stringResource(R.string.clean_node_database_title),
|
||||
enabled = enabled,
|
||||
onClick = { onNavigate(RadioConfigRoutes.CleanNodeDb) },
|
||||
)
|
||||
TitledCard(title = "Advanced", modifier = Modifier.padding(top = 16.dp)) {
|
||||
if (isManaged) {
|
||||
ManagedMessage()
|
||||
}
|
||||
|
||||
SettingsItem(
|
||||
text = stringResource(R.string.clean_node_database_title),
|
||||
enabled = enabled,
|
||||
onClick = { onNavigate(SettingsRoutes.CleanNodeDb) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -362,5 +396,28 @@ fun RadioConfigMenuActions(modifier: Modifier = Modifier, viewModel: UIViewModel
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun RadioSettingsScreenPreview() = AppTheme {
|
||||
RadioConfigItemList(state = RadioConfigState(isLocal = true, connected = true), onNavigate = { _ -> })
|
||||
RadioConfigItemList(
|
||||
state = RadioConfigState(isLocal = true, connected = true),
|
||||
isManaged = false,
|
||||
onNavigate = { _ -> },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ManagedMessage() {
|
||||
Text(
|
||||
text = stringResource(R.string.message_device_managed),
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
color = MaterialTheme.colorScheme.StatusRed,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun RadioSettingsScreenManagedPreview() = AppTheme {
|
||||
RadioConfigItemList(
|
||||
state = RadioConfigState(isLocal = true, connected = true),
|
||||
isManaged = true,
|
||||
onNavigate = { _ -> },
|
||||
)
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig
|
||||
package com.geeksville.mesh.ui.settings.radio
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Application
|
||||
|
|
@ -56,7 +56,7 @@ import com.geeksville.mesh.moduleConfig
|
|||
import com.geeksville.mesh.navigation.AdminRoute
|
||||
import com.geeksville.mesh.navigation.ConfigRoute
|
||||
import com.geeksville.mesh.navigation.ModuleRoute
|
||||
import com.geeksville.mesh.navigation.RadioConfigRoutes
|
||||
import com.geeksville.mesh.navigation.SettingsRoutes
|
||||
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
|
||||
import com.geeksville.mesh.repository.location.LocationRepository
|
||||
import com.geeksville.mesh.service.ConnectionState
|
||||
|
|
@ -108,7 +108,7 @@ constructor(
|
|||
private val meshService: IMeshService?
|
||||
get() = radioConfigRepository.meshService
|
||||
|
||||
private val destNum = savedStateHandle.toRoute<RadioConfigRoutes.RadioConfig>().destNum
|
||||
private val destNum = savedStateHandle.toRoute<SettingsRoutes.Settings>().destNum
|
||||
private val _destNode = MutableStateFlow<Node?>(null)
|
||||
val destNode: StateFlow<Node?>
|
||||
get() = _destNode
|
||||
|
|
@ -15,17 +15,18 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig
|
||||
package com.geeksville.mesh.ui.settings.radio
|
||||
|
||||
import com.geeksville.mesh.util.UiText
|
||||
|
||||
/**
|
||||
* Generic sealed class defines each possible state of a response.
|
||||
*/
|
||||
/** Generic sealed class defines each possible state of a response. */
|
||||
sealed class ResponseState<out T> {
|
||||
data object Empty : ResponseState<Nothing>()
|
||||
|
||||
data class Loading(var total: Int = 1, var completed: Int = 0) : ResponseState<Nothing>()
|
||||
|
||||
data class Success<T>(val result: T) : ResponseState<T>()
|
||||
|
||||
data class Error(val error: UiText) : ResponseState<Nothing>()
|
||||
|
||||
fun isWaiting() = this !is Empty
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -40,19 +40,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun AmbientLightingConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun AmbientLightingConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
AmbientLightingConfigItemList(
|
||||
|
|
@ -61,7 +56,7 @@ fun AmbientLightingConfigScreen(
|
|||
onSaveClicked = { ambientLightingInput ->
|
||||
val config = moduleConfig { ambientLighting = ambientLightingInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -74,9 +69,7 @@ fun AmbientLightingConfigItemList(
|
|||
val focusManager = LocalFocusManager.current
|
||||
var ambientLightingInput by rememberSaveable { mutableStateOf(ambientLightingConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.ambient_lighting_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -84,9 +77,7 @@ fun AmbientLightingConfigItemList(
|
|||
title = stringResource(R.string.led_state),
|
||||
checked = ambientLightingInput.ledState,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
ambientLightingInput = ambientLightingInput.copy { ledState = it }
|
||||
}
|
||||
onCheckedChange = { ambientLightingInput = ambientLightingInput.copy { ledState = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -97,9 +88,7 @@ fun AmbientLightingConfigItemList(
|
|||
value = ambientLightingInput.current,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
ambientLightingInput = ambientLightingInput.copy { current = it }
|
||||
}
|
||||
onValueChanged = { ambientLightingInput = ambientLightingInput.copy { current = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +98,7 @@ fun AmbientLightingConfigItemList(
|
|||
value = ambientLightingInput.red,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { ambientLightingInput = ambientLightingInput.copy { red = it } }
|
||||
onValueChanged = { ambientLightingInput = ambientLightingInput.copy { red = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +108,7 @@ fun AmbientLightingConfigItemList(
|
|||
value = ambientLightingInput.green,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { ambientLightingInput = ambientLightingInput.copy { green = it } }
|
||||
onValueChanged = { ambientLightingInput = ambientLightingInput.copy { green = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +118,7 @@ fun AmbientLightingConfigItemList(
|
|||
value = ambientLightingInput.blue,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { ambientLightingInput = ambientLightingInput.copy { blue = it } }
|
||||
onValueChanged = { ambientLightingInput = ambientLightingInput.copy { blue = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +132,7 @@ fun AmbientLightingConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(ambientLightingInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -155,6 +144,6 @@ private fun AmbientLightingConfigPreview() {
|
|||
AmbientLightingConfigItemList(
|
||||
ambientLightingConfig = ModuleConfigProtos.ModuleConfig.AmbientLightingConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
onSaveClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -42,19 +42,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun AudioConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
AudioConfigItemList(
|
||||
|
|
@ -63,22 +58,17 @@ fun AudioConfigScreen(
|
|||
onSaveClicked = { audioInput ->
|
||||
val config = moduleConfig { audio = audioInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun AudioConfigItemList(
|
||||
audioConfig: AudioConfig,
|
||||
enabled: Boolean,
|
||||
onSaveClicked: (AudioConfig) -> Unit,
|
||||
) {
|
||||
fun AudioConfigItemList(audioConfig: AudioConfig, enabled: Boolean, onSaveClicked: (AudioConfig) -> Unit) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
var audioInput by rememberSaveable { mutableStateOf(audioConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.audio_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -86,7 +76,7 @@ fun AudioConfigItemList(
|
|||
title = stringResource(R.string.codec_2_enabled),
|
||||
checked = audioInput.codec2Enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { audioInput = audioInput.copy { codec2Enabled = it } }
|
||||
onCheckedChange = { audioInput = audioInput.copy { codec2Enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -97,7 +87,7 @@ fun AudioConfigItemList(
|
|||
value = audioInput.pttPin,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { audioInput = audioInput.copy { pttPin = it } }
|
||||
onValueChanged = { audioInput = audioInput.copy { pttPin = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -105,11 +95,12 @@ fun AudioConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.codec2_sample_rate),
|
||||
enabled = enabled,
|
||||
items = AudioConfig.Audio_Baud.entries
|
||||
items =
|
||||
AudioConfig.Audio_Baud.entries
|
||||
.filter { it != AudioConfig.Audio_Baud.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = audioInput.bitrate,
|
||||
onItemSelected = { audioInput = audioInput.copy { bitrate = it } }
|
||||
onItemSelected = { audioInput = audioInput.copy { bitrate = it } },
|
||||
)
|
||||
}
|
||||
item { Divider() }
|
||||
|
|
@ -120,7 +111,7 @@ fun AudioConfigItemList(
|
|||
value = audioInput.i2SWs,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { audioInput = audioInput.copy { i2SWs = it } }
|
||||
onValueChanged = { audioInput = audioInput.copy { i2SWs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +121,7 @@ fun AudioConfigItemList(
|
|||
value = audioInput.i2SSd,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { audioInput = audioInput.copy { i2SSd = it } }
|
||||
onValueChanged = { audioInput = audioInput.copy { i2SSd = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +131,7 @@ fun AudioConfigItemList(
|
|||
value = audioInput.i2SDin,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { audioInput = audioInput.copy { i2SDin = it } }
|
||||
onValueChanged = { audioInput = audioInput.copy { i2SDin = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +141,7 @@ fun AudioConfigItemList(
|
|||
value = audioInput.i2SSck,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { audioInput = audioInput.copy { i2SSck = it } }
|
||||
onValueChanged = { audioInput = audioInput.copy { i2SSck = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +155,7 @@ fun AudioConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(audioInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -173,9 +164,5 @@ fun AudioConfigItemList(
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun AudioConfigPreview() {
|
||||
AudioConfigItemList(
|
||||
audioConfig = AudioConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
)
|
||||
AudioConfigItemList(audioConfig = AudioConfig.getDefaultInstance(), enabled = true, onSaveClicked = {})
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -41,19 +41,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun BluetoothConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun BluetoothConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
BluetoothConfigItemList(
|
||||
|
|
@ -62,7 +57,7 @@ fun BluetoothConfigScreen(
|
|||
onSaveClicked = { bluetoothInput ->
|
||||
val config = config { bluetooth = bluetoothInput }
|
||||
viewModel.setConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -75,9 +70,7 @@ fun BluetoothConfigItemList(
|
|||
val focusManager = LocalFocusManager.current
|
||||
var bluetoothInput by rememberSaveable { mutableStateOf(bluetoothConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.bluetooth_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -85,7 +78,7 @@ fun BluetoothConfigItemList(
|
|||
title = stringResource(R.string.bluetooth_enabled),
|
||||
checked = bluetoothInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { bluetoothInput = bluetoothInput.copy { this.enabled = it } }
|
||||
onCheckedChange = { bluetoothInput = bluetoothInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -94,11 +87,12 @@ fun BluetoothConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.pairing_mode),
|
||||
enabled = enabled,
|
||||
items = BluetoothConfig.PairingMode.entries
|
||||
items =
|
||||
BluetoothConfig.PairingMode.entries
|
||||
.filter { it != BluetoothConfig.PairingMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = bluetoothInput.mode,
|
||||
onItemSelected = { bluetoothInput = bluetoothInput.copy { mode = it } }
|
||||
onItemSelected = { bluetoothInput = bluetoothInput.copy { mode = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -113,7 +107,7 @@ fun BluetoothConfigItemList(
|
|||
if (it.toString().length == 6) { // ensure 6 digits
|
||||
bluetoothInput = bluetoothInput.copy { fixedPin = it }
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +121,7 @@ fun BluetoothConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(bluetoothInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -136,9 +130,5 @@ fun BluetoothConfigItemList(
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun BluetoothConfigPreview() {
|
||||
BluetoothConfigItemList(
|
||||
bluetoothConfig = BluetoothConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
)
|
||||
BluetoothConfigItemList(bluetoothConfig = BluetoothConfig.getDefaultInstance(), enabled = true, onSaveClicked = {})
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -44,19 +44,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun CannedMessageConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
CannedMessageConfigItemList(
|
||||
|
|
@ -71,7 +66,7 @@ fun CannedMessageConfigScreen(
|
|||
val config = moduleConfig { cannedMessage = cannedMessageInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -86,9 +81,7 @@ fun CannedMessageConfigItemList(
|
|||
var messagesInput by rememberSaveable { mutableStateOf(messages) }
|
||||
var cannedMessageInput by rememberSaveable { mutableStateOf(cannedMessageConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.canned_message_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -96,9 +89,7 @@ fun CannedMessageConfigItemList(
|
|||
title = stringResource(R.string.canned_message_enabled),
|
||||
checked = cannedMessageInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
cannedMessageInput = cannedMessageInput.copy { this.enabled = it }
|
||||
}
|
||||
onCheckedChange = { cannedMessageInput = cannedMessageInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -108,9 +99,7 @@ fun CannedMessageConfigItemList(
|
|||
title = stringResource(R.string.rotary_encoder_1_enabled),
|
||||
checked = cannedMessageInput.rotary1Enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
cannedMessageInput = cannedMessageInput.copy { rotary1Enabled = it }
|
||||
}
|
||||
onCheckedChange = { cannedMessageInput = cannedMessageInput.copy { rotary1Enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -121,9 +110,7 @@ fun CannedMessageConfigItemList(
|
|||
value = cannedMessageInput.inputbrokerPinA,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
cannedMessageInput = cannedMessageInput.copy { inputbrokerPinA = it }
|
||||
}
|
||||
onValueChanged = { cannedMessageInput = cannedMessageInput.copy { inputbrokerPinA = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -133,9 +120,7 @@ fun CannedMessageConfigItemList(
|
|||
value = cannedMessageInput.inputbrokerPinB,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
cannedMessageInput = cannedMessageInput.copy { inputbrokerPinB = it }
|
||||
}
|
||||
onValueChanged = { cannedMessageInput = cannedMessageInput.copy { inputbrokerPinB = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -145,9 +130,7 @@ fun CannedMessageConfigItemList(
|
|||
value = cannedMessageInput.inputbrokerPinPress,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
cannedMessageInput = cannedMessageInput.copy { inputbrokerPinPress = it }
|
||||
}
|
||||
onValueChanged = { cannedMessageInput = cannedMessageInput.copy { inputbrokerPinPress = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -155,13 +138,12 @@ fun CannedMessageConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.generate_input_event_on_press),
|
||||
enabled = enabled,
|
||||
items = CannedMessageConfig.InputEventChar.entries
|
||||
items =
|
||||
CannedMessageConfig.InputEventChar.entries
|
||||
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = cannedMessageInput.inputbrokerEventPress,
|
||||
onItemSelected = {
|
||||
cannedMessageInput = cannedMessageInput.copy { inputbrokerEventPress = it }
|
||||
}
|
||||
onItemSelected = { cannedMessageInput = cannedMessageInput.copy { inputbrokerEventPress = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -170,13 +152,12 @@ fun CannedMessageConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.generate_input_event_on_cw),
|
||||
enabled = enabled,
|
||||
items = CannedMessageConfig.InputEventChar.entries
|
||||
items =
|
||||
CannedMessageConfig.InputEventChar.entries
|
||||
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = cannedMessageInput.inputbrokerEventCw,
|
||||
onItemSelected = {
|
||||
cannedMessageInput = cannedMessageInput.copy { inputbrokerEventCw = it }
|
||||
}
|
||||
onItemSelected = { cannedMessageInput = cannedMessageInput.copy { inputbrokerEventCw = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -185,13 +166,12 @@ fun CannedMessageConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.generate_input_event_on_ccw),
|
||||
enabled = enabled,
|
||||
items = CannedMessageConfig.InputEventChar.entries
|
||||
items =
|
||||
CannedMessageConfig.InputEventChar.entries
|
||||
.filter { it != CannedMessageConfig.InputEventChar.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = cannedMessageInput.inputbrokerEventCcw,
|
||||
onItemSelected = {
|
||||
cannedMessageInput = cannedMessageInput.copy { inputbrokerEventCcw = it }
|
||||
}
|
||||
onItemSelected = { cannedMessageInput = cannedMessageInput.copy { inputbrokerEventCcw = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -201,9 +181,7 @@ fun CannedMessageConfigItemList(
|
|||
title = stringResource(R.string.up_down_select_input_enabled),
|
||||
checked = cannedMessageInput.updown1Enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
cannedMessageInput = cannedMessageInput.copy { updown1Enabled = it }
|
||||
}
|
||||
onCheckedChange = { cannedMessageInput = cannedMessageInput.copy { updown1Enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -215,13 +193,10 @@ fun CannedMessageConfigItemList(
|
|||
maxSize = 63, // allow_input_source max_size:16
|
||||
enabled = enabled,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
cannedMessageInput = cannedMessageInput.copy { allowInputSource = it }
|
||||
}
|
||||
onValueChanged = { cannedMessageInput = cannedMessageInput.copy { allowInputSource = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -230,9 +205,7 @@ fun CannedMessageConfigItemList(
|
|||
title = stringResource(R.string.send_bell),
|
||||
checked = cannedMessageInput.sendBell,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
cannedMessageInput = cannedMessageInput.copy { sendBell = it }
|
||||
}
|
||||
onCheckedChange = { cannedMessageInput = cannedMessageInput.copy { sendBell = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -244,11 +217,10 @@ fun CannedMessageConfigItemList(
|
|||
maxSize = 200, // messages max_size:201
|
||||
enabled = enabled,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { messagesInput = it }
|
||||
onValueChanged = { messagesInput = it },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +235,7 @@ fun CannedMessageConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(messagesInput, cannedMessageInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
|
|
@ -79,7 +79,7 @@ import com.geeksville.mesh.ui.common.components.SecurityIcon
|
|||
import com.geeksville.mesh.ui.common.components.dragContainer
|
||||
import com.geeksville.mesh.ui.common.components.dragDropItemsIndexed
|
||||
import com.geeksville.mesh.ui.common.components.rememberDragDropState
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
private fun ChannelItem(
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -44,19 +44,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun DetectionSensorConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
DetectionSensorConfigItemList(
|
||||
|
|
@ -65,7 +60,7 @@ fun DetectionSensorConfigScreen(
|
|||
onSaveClicked = { detectionSensorInput ->
|
||||
val config = moduleConfig { detectionSensor = detectionSensorInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -79,9 +74,7 @@ fun DetectionSensorConfigItemList(
|
|||
val focusManager = LocalFocusManager.current
|
||||
var detectionSensorInput by rememberSaveable { mutableStateOf(detectionSensorConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.detection_sensor_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -89,9 +82,7 @@ fun DetectionSensorConfigItemList(
|
|||
title = stringResource(R.string.detection_sensor_enabled),
|
||||
checked = detectionSensorInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
detectionSensorInput = detectionSensorInput.copy { this.enabled = it }
|
||||
}
|
||||
onCheckedChange = { detectionSensorInput = detectionSensorInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -102,9 +93,7 @@ fun DetectionSensorConfigItemList(
|
|||
value = detectionSensorInput.minimumBroadcastSecs,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
detectionSensorInput = detectionSensorInput.copy { minimumBroadcastSecs = it }
|
||||
}
|
||||
onValueChanged = { detectionSensorInput = detectionSensorInput.copy { minimumBroadcastSecs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -114,9 +103,7 @@ fun DetectionSensorConfigItemList(
|
|||
value = detectionSensorInput.stateBroadcastSecs,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
detectionSensorInput = detectionSensorInput.copy { stateBroadcastSecs = it }
|
||||
}
|
||||
onValueChanged = { detectionSensorInput = detectionSensorInput.copy { stateBroadcastSecs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -125,9 +112,7 @@ fun DetectionSensorConfigItemList(
|
|||
title = stringResource(R.string.send_bell_with_alert_message),
|
||||
checked = detectionSensorInput.sendBell,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
detectionSensorInput = detectionSensorInput.copy { sendBell = it }
|
||||
}
|
||||
onCheckedChange = { detectionSensorInput = detectionSensorInput.copy { sendBell = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -139,13 +124,10 @@ fun DetectionSensorConfigItemList(
|
|||
maxSize = 19, // name max_size:20
|
||||
enabled = enabled,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
detectionSensorInput = detectionSensorInput.copy { name = it }
|
||||
}
|
||||
onValueChanged = { detectionSensorInput = detectionSensorInput.copy { name = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -155,9 +137,7 @@ fun DetectionSensorConfigItemList(
|
|||
value = detectionSensorInput.monitorPin,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
detectionSensorInput = detectionSensorInput.copy { monitorPin = it }
|
||||
}
|
||||
onValueChanged = { detectionSensorInput = detectionSensorInput.copy { monitorPin = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -165,13 +145,12 @@ fun DetectionSensorConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.detection_trigger_type),
|
||||
enabled = enabled,
|
||||
items = ModuleConfig.DetectionSensorConfig.TriggerType.entries
|
||||
items =
|
||||
ModuleConfig.DetectionSensorConfig.TriggerType.entries
|
||||
.filter { it != ModuleConfig.DetectionSensorConfig.TriggerType.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = detectionSensorInput.detectionTriggerType,
|
||||
onItemSelected = {
|
||||
detectionSensorInput = detectionSensorInput.copy { detectionTriggerType = it }
|
||||
}
|
||||
onItemSelected = { detectionSensorInput = detectionSensorInput.copy { detectionTriggerType = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -181,9 +160,7 @@ fun DetectionSensorConfigItemList(
|
|||
title = stringResource(R.string.use_input_pullup_mode),
|
||||
checked = detectionSensorInput.usePullup,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
detectionSensorInput = detectionSensorInput.copy { usePullup = it }
|
||||
}
|
||||
onCheckedChange = { detectionSensorInput = detectionSensorInput.copy { usePullup = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -198,7 +175,7 @@ fun DetectionSensorConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(detectionSensorInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -210,6 +187,6 @@ private fun DetectionSensorConfigPreview() {
|
|||
DetectionSensorConfigItemList(
|
||||
detectionSensorConfig = ModuleConfig.DetectionSensorConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
onSaveClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -58,47 +58,44 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
private val DeviceConfig.Role.stringRes: Int
|
||||
get() = when (this) {
|
||||
DeviceConfig.Role.CLIENT -> R.string.role_client
|
||||
DeviceConfig.Role.CLIENT_MUTE -> R.string.role_client_mute
|
||||
DeviceConfig.Role.ROUTER -> R.string.role_router
|
||||
DeviceConfig.Role.ROUTER_CLIENT -> R.string.role_router_client
|
||||
DeviceConfig.Role.REPEATER -> R.string.role_repeater
|
||||
DeviceConfig.Role.TRACKER -> R.string.role_tracker
|
||||
DeviceConfig.Role.SENSOR -> R.string.role_sensor
|
||||
DeviceConfig.Role.TAK -> R.string.role_tak
|
||||
DeviceConfig.Role.CLIENT_HIDDEN -> R.string.role_client_hidden
|
||||
DeviceConfig.Role.LOST_AND_FOUND -> R.string.role_lost_and_found
|
||||
DeviceConfig.Role.TAK_TRACKER -> R.string.role_tak_tracker
|
||||
DeviceConfig.Role.ROUTER_LATE -> R.string.role_router_late
|
||||
else -> R.string.unrecognized
|
||||
}
|
||||
get() =
|
||||
when (this) {
|
||||
DeviceConfig.Role.CLIENT -> R.string.role_client
|
||||
DeviceConfig.Role.CLIENT_MUTE -> R.string.role_client_mute
|
||||
DeviceConfig.Role.ROUTER -> R.string.role_router
|
||||
DeviceConfig.Role.ROUTER_CLIENT -> R.string.role_router_client
|
||||
DeviceConfig.Role.REPEATER -> R.string.role_repeater
|
||||
DeviceConfig.Role.TRACKER -> R.string.role_tracker
|
||||
DeviceConfig.Role.SENSOR -> R.string.role_sensor
|
||||
DeviceConfig.Role.TAK -> R.string.role_tak
|
||||
DeviceConfig.Role.CLIENT_HIDDEN -> R.string.role_client_hidden
|
||||
DeviceConfig.Role.LOST_AND_FOUND -> R.string.role_lost_and_found
|
||||
DeviceConfig.Role.TAK_TRACKER -> R.string.role_tak_tracker
|
||||
DeviceConfig.Role.ROUTER_LATE -> R.string.role_router_late
|
||||
else -> R.string.unrecognized
|
||||
}
|
||||
|
||||
private val DeviceConfig.RebroadcastMode.stringRes: Int
|
||||
get() = when (this) {
|
||||
DeviceConfig.RebroadcastMode.ALL -> R.string.rebroadcast_mode_all
|
||||
DeviceConfig.RebroadcastMode.ALL_SKIP_DECODING -> R.string.rebroadcast_mode_all_skip_decoding
|
||||
DeviceConfig.RebroadcastMode.LOCAL_ONLY -> R.string.rebroadcast_mode_local_only
|
||||
DeviceConfig.RebroadcastMode.KNOWN_ONLY -> R.string.rebroadcast_mode_known_only
|
||||
DeviceConfig.RebroadcastMode.NONE -> R.string.rebroadcast_mode_none
|
||||
DeviceConfig.RebroadcastMode.CORE_PORTNUMS_ONLY -> R.string.rebroadcast_mode_core_portnums_only
|
||||
else -> R.string.unrecognized
|
||||
}
|
||||
get() =
|
||||
when (this) {
|
||||
DeviceConfig.RebroadcastMode.ALL -> R.string.rebroadcast_mode_all
|
||||
DeviceConfig.RebroadcastMode.ALL_SKIP_DECODING -> R.string.rebroadcast_mode_all_skip_decoding
|
||||
DeviceConfig.RebroadcastMode.LOCAL_ONLY -> R.string.rebroadcast_mode_local_only
|
||||
DeviceConfig.RebroadcastMode.KNOWN_ONLY -> R.string.rebroadcast_mode_known_only
|
||||
DeviceConfig.RebroadcastMode.NONE -> R.string.rebroadcast_mode_none
|
||||
DeviceConfig.RebroadcastMode.CORE_PORTNUMS_ONLY -> R.string.rebroadcast_mode_core_portnums_only
|
||||
else -> R.string.unrecognized
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeviceConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
DeviceConfigItemList(
|
||||
|
|
@ -107,94 +104,62 @@ fun DeviceConfigScreen(
|
|||
onSaveClicked = { deviceInput ->
|
||||
val config = config { device = deviceInput }
|
||||
viewModel.setConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun RouterRoleConfirmationDialog(
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
) {
|
||||
fun RouterRoleConfirmationDialog(onDismiss: () -> Unit, onConfirm: () -> Unit) {
|
||||
val dialogTitle = stringResource(R.string.are_you_sure)
|
||||
val annotatedDialogText = AnnotatedString.fromHtml(
|
||||
htmlString = stringResource(R.string.router_role_confirmation_text),
|
||||
linkStyles = TextLinkStyles(style = SpanStyle(color = Color.Blue))
|
||||
)
|
||||
val annotatedDialogText =
|
||||
AnnotatedString.fromHtml(
|
||||
htmlString = stringResource(R.string.router_role_confirmation_text),
|
||||
linkStyles = TextLinkStyles(style = SpanStyle(color = Color.Blue)),
|
||||
)
|
||||
|
||||
var confirmed by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
AlertDialog(
|
||||
title = {
|
||||
Text(text = dialogTitle)
|
||||
},
|
||||
title = { Text(text = dialogTitle) },
|
||||
text = {
|
||||
Column {
|
||||
Text(text = annotatedDialogText)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(true) {
|
||||
confirmed = !confirmed
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
modifier = Modifier.fillMaxWidth().clickable(true) { confirmed = !confirmed },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Checkbox(
|
||||
checked = confirmed,
|
||||
onCheckedChange = { confirmed = it }
|
||||
)
|
||||
Checkbox(checked = confirmed, onCheckedChange = { confirmed = it })
|
||||
Text(stringResource(R.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(R.string.accept)) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = onDismiss
|
||||
) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } },
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun DeviceConfigItemList(
|
||||
deviceConfig: DeviceConfig,
|
||||
enabled: Boolean,
|
||||
onSaveClicked: (DeviceConfig) -> Unit,
|
||||
) {
|
||||
fun DeviceConfigItemList(deviceConfig: DeviceConfig, enabled: Boolean, onSaveClicked: (DeviceConfig) -> Unit) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
var deviceInput by rememberSaveable { mutableStateOf(deviceConfig) }
|
||||
var selectedRole by rememberSaveable { mutableStateOf(deviceInput.role) }
|
||||
val infrastructureRoles = listOf(
|
||||
DeviceConfig.Role.ROUTER,
|
||||
DeviceConfig.Role.REPEATER,
|
||||
)
|
||||
val infrastructureRoles = listOf(DeviceConfig.Role.ROUTER, DeviceConfig.Role.REPEATER)
|
||||
if (selectedRole != deviceInput.role) {
|
||||
if (selectedRole in infrastructureRoles) {
|
||||
RouterRoleConfirmationDialog(
|
||||
onDismiss = { selectedRole = deviceInput.role },
|
||||
onConfirm = {
|
||||
deviceInput = deviceInput.copy { role = selectedRole }
|
||||
}
|
||||
onConfirm = { deviceInput = deviceInput.copy { role = selectedRole } },
|
||||
)
|
||||
} else {
|
||||
deviceInput = deviceInput.copy { role = selectedRole }
|
||||
}
|
||||
}
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.device_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -202,9 +167,7 @@ fun DeviceConfigItemList(
|
|||
title = stringResource(R.string.role),
|
||||
enabled = enabled,
|
||||
selectedItem = deviceInput.role,
|
||||
onItemSelected = {
|
||||
selectedRole = it
|
||||
},
|
||||
onItemSelected = { selectedRole = it },
|
||||
summary = stringResource(id = deviceInput.role.stringRes),
|
||||
)
|
||||
HorizontalDivider()
|
||||
|
|
@ -216,9 +179,7 @@ fun DeviceConfigItemList(
|
|||
value = deviceInput.buttonGpio,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
deviceInput = deviceInput.copy { buttonGpio = it }
|
||||
}
|
||||
onValueChanged = { deviceInput = deviceInput.copy { buttonGpio = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -228,9 +189,7 @@ fun DeviceConfigItemList(
|
|||
value = deviceInput.buzzerGpio,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
deviceInput = deviceInput.copy { buzzerGpio = it }
|
||||
}
|
||||
onValueChanged = { deviceInput = deviceInput.copy { buzzerGpio = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -251,9 +210,7 @@ fun DeviceConfigItemList(
|
|||
value = deviceInput.nodeInfoBroadcastSecs,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
deviceInput = deviceInput.copy { nodeInfoBroadcastSecs = it }
|
||||
}
|
||||
onValueChanged = { deviceInput = deviceInput.copy { nodeInfoBroadcastSecs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +220,7 @@ fun DeviceConfigItemList(
|
|||
summary = stringResource(id = R.string.config_device_doubleTapAsButtonPress_summary),
|
||||
checked = deviceInput.doubleTapAsButtonPress,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { deviceInput = deviceInput.copy { doubleTapAsButtonPress = it } }
|
||||
onCheckedChange = { deviceInput = deviceInput.copy { doubleTapAsButtonPress = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
|
@ -274,7 +231,7 @@ fun DeviceConfigItemList(
|
|||
summary = stringResource(id = R.string.config_device_disableTripleClick_summary),
|
||||
checked = deviceInput.disableTripleClick,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { deviceInput = deviceInput.copy { disableTripleClick = it } }
|
||||
onCheckedChange = { deviceInput = deviceInput.copy { disableTripleClick = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
|
@ -286,13 +243,10 @@ fun DeviceConfigItemList(
|
|||
maxSize = 64, // tzdef max_size:65
|
||||
enabled = enabled,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
deviceInput = deviceInput.copy { tzdef = it }
|
||||
},
|
||||
onValueChanged = { deviceInput = deviceInput.copy { tzdef = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +256,7 @@ fun DeviceConfigItemList(
|
|||
summary = stringResource(id = R.string.config_device_ledHeartbeatDisabled_summary),
|
||||
checked = deviceInput.ledHeartbeatDisabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { deviceInput = deviceInput.copy { ledHeartbeatDisabled = it } }
|
||||
onCheckedChange = { deviceInput = deviceInput.copy { ledHeartbeatDisabled = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
|
@ -317,7 +271,7 @@ fun DeviceConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(deviceInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -326,9 +280,5 @@ fun DeviceConfigItemList(
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun DeviceConfigPreview() {
|
||||
DeviceConfigItemList(
|
||||
deviceConfig = DeviceConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
)
|
||||
DeviceConfigItemList(deviceConfig = DeviceConfig.getDefaultInstance(), enabled = true, onSaveClicked = {})
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -41,7 +41,7 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -45,7 +45,7 @@ import com.geeksville.mesh.R
|
|||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.google.protobuf.Descriptors
|
||||
|
||||
private const val SupportedFields = 7
|
||||
private const val SUPPORTED_FIELDS = 7
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
|
|
@ -58,10 +58,13 @@ fun EditDeviceProfileDialog(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val state = remember {
|
||||
val fields = deviceProfile.descriptorForType.fields
|
||||
.filter { it.number < SupportedFields } // TODO add ringtone & canned messages
|
||||
mutableStateMapOf<Descriptors.FieldDescriptor, Boolean>()
|
||||
.apply { putAll(fields.associateWith(deviceProfile::hasField)) }
|
||||
val fields =
|
||||
deviceProfile.descriptorForType.fields.filter {
|
||||
it.number < SUPPORTED_FIELDS
|
||||
} // TODO add ringtone & canned messages
|
||||
mutableStateMapOf<Descriptors.FieldDescriptor, Boolean>().apply {
|
||||
putAll(fields.associateWith(deviceProfile::hasField))
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
|
|
@ -71,38 +74,36 @@ fun EditDeviceProfileDialog(
|
|||
Column(modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge.copy(
|
||||
style =
|
||||
MaterialTheme.typography.titleLarge.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp),
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp),
|
||||
)
|
||||
HorizontalDivider()
|
||||
state.keys.sortedBy { it.number }.forEach { field ->
|
||||
SwitchPreference(
|
||||
title = field.name,
|
||||
checked = state[field] == true,
|
||||
enabled = deviceProfile.hasField(field),
|
||||
onCheckedChange = { state[field] = it },
|
||||
padding = PaddingValues(0.dp)
|
||||
)
|
||||
}
|
||||
state.keys
|
||||
.sortedBy { it.number }
|
||||
.forEach { field ->
|
||||
SwitchPreference(
|
||||
title = field.name,
|
||||
checked = state[field] == true,
|
||||
enabled = deviceProfile.hasField(field),
|
||||
onCheckedChange = { state[field] = it },
|
||||
padding = PaddingValues(0.dp),
|
||||
)
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
FlowRow(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
|
||||
modifier = modifier.fillMaxWidth().padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
TextButton(
|
||||
modifier = modifier.weight(1f),
|
||||
onClick = onDismiss
|
||||
) { Text(stringResource(R.string.cancel)) }
|
||||
TextButton(modifier = modifier.weight(1f), onClick = onDismiss) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
Button(
|
||||
modifier = modifier.weight(1f),
|
||||
onClick = {
|
||||
|
|
@ -115,9 +116,11 @@ fun EditDeviceProfileDialog(
|
|||
onConfirm(builder.build())
|
||||
},
|
||||
enabled = state.values.any { it },
|
||||
) { Text(stringResource(R.string.save)) }
|
||||
) {
|
||||
Text(stringResource(R.string.save))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -44,19 +44,14 @@ import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.common.components.TextDividerPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun ExternalNotificationConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
ExternalNotificationConfigItemList(
|
||||
|
|
@ -71,7 +66,7 @@ fun ExternalNotificationConfigScreen(
|
|||
val config = moduleConfig { externalNotification = extNotificationInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -86,9 +81,7 @@ fun ExternalNotificationConfigItemList(
|
|||
var ringtoneInput by rememberSaveable { mutableStateOf(ringtone) }
|
||||
var externalNotificationInput by rememberSaveable { mutableStateOf(extNotificationConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.external_notification_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -96,9 +89,7 @@ fun ExternalNotificationConfigItemList(
|
|||
title = stringResource(R.string.external_notification_enabled),
|
||||
checked = externalNotificationInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput = externalNotificationInput.copy { this.enabled = it }
|
||||
}
|
||||
onCheckedChange = { externalNotificationInput = externalNotificationInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -109,9 +100,7 @@ fun ExternalNotificationConfigItemList(
|
|||
title = stringResource(R.string.alert_message_led),
|
||||
checked = externalNotificationInput.alertMessage,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput = externalNotificationInput.copy { alertMessage = it }
|
||||
}
|
||||
onCheckedChange = { externalNotificationInput = externalNotificationInput.copy { alertMessage = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -122,9 +111,8 @@ fun ExternalNotificationConfigItemList(
|
|||
checked = externalNotificationInput.alertMessageBuzzer,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput =
|
||||
externalNotificationInput.copy { alertMessageBuzzer = it }
|
||||
}
|
||||
externalNotificationInput = externalNotificationInput.copy { alertMessageBuzzer = it }
|
||||
},
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -135,9 +123,8 @@ fun ExternalNotificationConfigItemList(
|
|||
checked = externalNotificationInput.alertMessageVibra,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput =
|
||||
externalNotificationInput.copy { alertMessageVibra = it }
|
||||
}
|
||||
externalNotificationInput = externalNotificationInput.copy { alertMessageVibra = it }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -148,9 +135,7 @@ fun ExternalNotificationConfigItemList(
|
|||
title = stringResource(R.string.alert_bell_led),
|
||||
checked = externalNotificationInput.alertBell,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput = externalNotificationInput.copy { alertBell = it }
|
||||
}
|
||||
onCheckedChange = { externalNotificationInput = externalNotificationInput.copy { alertBell = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -161,9 +146,8 @@ fun ExternalNotificationConfigItemList(
|
|||
checked = externalNotificationInput.alertBellBuzzer,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput =
|
||||
externalNotificationInput.copy { alertBellBuzzer = it }
|
||||
}
|
||||
externalNotificationInput = externalNotificationInput.copy { alertBellBuzzer = it }
|
||||
},
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -174,9 +158,8 @@ fun ExternalNotificationConfigItemList(
|
|||
checked = externalNotificationInput.alertBellVibra,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput =
|
||||
externalNotificationInput.copy { alertBellVibra = it }
|
||||
}
|
||||
externalNotificationInput = externalNotificationInput.copy { alertBellVibra = it }
|
||||
},
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -187,23 +170,19 @@ fun ExternalNotificationConfigItemList(
|
|||
value = externalNotificationInput.output,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
externalNotificationInput = externalNotificationInput.copy { output = it }
|
||||
}
|
||||
onValueChanged = { externalNotificationInput = externalNotificationInput.copy { output = it } },
|
||||
)
|
||||
}
|
||||
|
||||
if (externalNotificationInput.output != 0) {
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.output_led_active_high),
|
||||
checked = externalNotificationInput.active,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput = externalNotificationInput.copy { active = it }
|
||||
}
|
||||
)
|
||||
}
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.output_led_active_high),
|
||||
checked = externalNotificationInput.active,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { externalNotificationInput = externalNotificationInput.copy { active = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
|
|
@ -213,23 +192,19 @@ fun ExternalNotificationConfigItemList(
|
|||
value = externalNotificationInput.outputBuzzer,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
externalNotificationInput = externalNotificationInput.copy { outputBuzzer = it }
|
||||
}
|
||||
onValueChanged = { externalNotificationInput = externalNotificationInput.copy { outputBuzzer = it } },
|
||||
)
|
||||
}
|
||||
|
||||
if (externalNotificationInput.outputBuzzer != 0) {
|
||||
item {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_pwm_buzzer),
|
||||
checked = externalNotificationInput.usePwm,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput = externalNotificationInput.copy { usePwm = it }
|
||||
}
|
||||
)
|
||||
}
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.use_pwm_buzzer),
|
||||
checked = externalNotificationInput.usePwm,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { externalNotificationInput = externalNotificationInput.copy { usePwm = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
||||
|
|
@ -239,9 +214,7 @@ fun ExternalNotificationConfigItemList(
|
|||
value = externalNotificationInput.outputVibra,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
externalNotificationInput = externalNotificationInput.copy { outputVibra = it }
|
||||
}
|
||||
onValueChanged = { externalNotificationInput = externalNotificationInput.copy { outputVibra = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -251,9 +224,7 @@ fun ExternalNotificationConfigItemList(
|
|||
value = externalNotificationInput.outputMs,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
externalNotificationInput = externalNotificationInput.copy { outputMs = it }
|
||||
}
|
||||
onValueChanged = { externalNotificationInput = externalNotificationInput.copy { outputMs = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -263,9 +234,7 @@ fun ExternalNotificationConfigItemList(
|
|||
value = externalNotificationInput.nagTimeout,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
externalNotificationInput = externalNotificationInput.copy { nagTimeout = it }
|
||||
}
|
||||
onValueChanged = { externalNotificationInput = externalNotificationInput.copy { nagTimeout = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -276,11 +245,10 @@ fun ExternalNotificationConfigItemList(
|
|||
maxSize = 230, // ringtone max_size:231
|
||||
enabled = enabled,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { ringtoneInput = it }
|
||||
onValueChanged = { ringtoneInput = it },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +259,7 @@ fun ExternalNotificationConfigItemList(
|
|||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
externalNotificationInput = externalNotificationInput.copy { useI2SAsBuzzer = it }
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -307,7 +275,7 @@ fun ExternalNotificationConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(ringtoneInput, externalNotificationInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -48,19 +48,14 @@ import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SignedIntegerEditTextPreference
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun LoRaConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun LoRaConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
LoRaConfigItemList(
|
||||
|
|
@ -86,13 +81,9 @@ fun LoRaConfigItemList(
|
|||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
var loraInput by rememberSaveable { mutableStateOf(loraConfig) }
|
||||
val primaryChannel by remember(loraInput) {
|
||||
mutableStateOf(Channel(primarySettings, loraInput))
|
||||
}
|
||||
val primaryChannel by remember(loraInput) { mutableStateOf(Channel(primarySettings, loraInput)) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.lora_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -100,7 +91,7 @@ fun LoRaConfigItemList(
|
|||
title = stringResource(R.string.use_modem_preset),
|
||||
checked = loraInput.usePreset,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { loraInput = loraInput.copy { usePreset = it } }
|
||||
onCheckedChange = { loraInput = loraInput.copy { usePreset = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -110,11 +101,12 @@ fun LoRaConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.modem_preset),
|
||||
enabled = enabled && loraInput.usePreset,
|
||||
items = LoRaConfig.ModemPreset.entries
|
||||
items =
|
||||
LoRaConfig.ModemPreset.entries
|
||||
.filter { it != LoRaConfig.ModemPreset.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = loraInput.modemPreset,
|
||||
onItemSelected = { loraInput = loraInput.copy { modemPreset = it } }
|
||||
onItemSelected = { loraInput = loraInput.copy { modemPreset = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -125,7 +117,7 @@ fun LoRaConfigItemList(
|
|||
value = loraInput.bandwidth,
|
||||
enabled = enabled && !loraInput.usePreset,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { bandwidth = it } }
|
||||
onValueChanged = { loraInput = loraInput.copy { bandwidth = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +127,7 @@ fun LoRaConfigItemList(
|
|||
value = loraInput.spreadFactor,
|
||||
enabled = enabled && !loraInput.usePreset,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { spreadFactor = it } }
|
||||
onValueChanged = { loraInput = loraInput.copy { spreadFactor = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +137,7 @@ fun LoRaConfigItemList(
|
|||
value = loraInput.codingRate,
|
||||
enabled = enabled && !loraInput.usePreset,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { codingRate = it } }
|
||||
onValueChanged = { loraInput = loraInput.copy { codingRate = it } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +148,7 @@ fun LoRaConfigItemList(
|
|||
value = loraInput.frequencyOffset,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { frequencyOffset = it } }
|
||||
onValueChanged = { loraInput = loraInput.copy { frequencyOffset = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +158,7 @@ fun LoRaConfigItemList(
|
|||
enabled = enabled,
|
||||
items = RegionInfo.entries.map { it.regionCode to it.description },
|
||||
selectedItem = loraInput.region,
|
||||
onItemSelected = { loraInput = loraInput.copy { region = it } }
|
||||
onItemSelected = { loraInput = loraInput.copy { region = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -177,7 +169,7 @@ fun LoRaConfigItemList(
|
|||
value = loraInput.hopLimit,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { loraInput = loraInput.copy { hopLimit = it } }
|
||||
onValueChanged = { loraInput = loraInput.copy { hopLimit = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +178,7 @@ fun LoRaConfigItemList(
|
|||
title = stringResource(R.string.tx_enabled),
|
||||
checked = loraInput.txEnabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { loraInput = loraInput.copy { txEnabled = it } }
|
||||
onCheckedChange = { loraInput = loraInput.copy { txEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -213,7 +205,7 @@ fun LoRaConfigItemList(
|
|||
if (it <= loraInput.numChannels) { // total num of LoRa channels
|
||||
loraInput = loraInput.copy { channelNum = it }
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +214,7 @@ fun LoRaConfigItemList(
|
|||
title = stringResource(R.string.override_duty_cycle),
|
||||
checked = loraInput.overrideDutyCycle,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { loraInput = loraInput.copy { overrideDutyCycle = it } }
|
||||
onCheckedChange = { loraInput = loraInput.copy { overrideDutyCycle = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -235,11 +227,12 @@ fun LoRaConfigItemList(
|
|||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValuesChanged = { list ->
|
||||
loraInput = loraInput.copy {
|
||||
ignoreIncoming.clear()
|
||||
ignoreIncoming.addAll(list.filter { it != 0 })
|
||||
}
|
||||
}
|
||||
loraInput =
|
||||
loraInput.copy {
|
||||
ignoreIncoming.clear()
|
||||
ignoreIncoming.addAll(list.filter { it != 0 })
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +241,7 @@ fun LoRaConfigItemList(
|
|||
title = stringResource(R.string.sx126x_rx_boosted_gain),
|
||||
checked = loraInput.sx126XRxBoostedGain,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { loraInput = loraInput.copy { sx126XRxBoostedGain = it } }
|
||||
onCheckedChange = { loraInput = loraInput.copy { sx126XRxBoostedGain = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -257,11 +250,16 @@ fun LoRaConfigItemList(
|
|||
var isFocused by remember { mutableStateOf(false) }
|
||||
EditTextPreference(
|
||||
title = stringResource(R.string.override_frequency_mhz),
|
||||
value = if (isFocused || loraInput.overrideFrequency != 0f) loraInput.overrideFrequency else primaryChannel.radioFreq,
|
||||
value =
|
||||
if (isFocused || loraInput.overrideFrequency != 0f) {
|
||||
loraInput.overrideFrequency
|
||||
} else {
|
||||
primaryChannel.radioFreq
|
||||
},
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onFocusChanged = { isFocused = it.isFocused },
|
||||
onValueChanged = { loraInput = loraInput.copy { overrideFrequency = it } }
|
||||
onValueChanged = { loraInput = loraInput.copy { overrideFrequency = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -271,7 +269,7 @@ fun LoRaConfigItemList(
|
|||
title = stringResource(R.string.pa_fan_disabled),
|
||||
checked = loraInput.paFanDisabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { loraInput = loraInput.copy { paFanDisabled = it } }
|
||||
onCheckedChange = { loraInput = loraInput.copy { paFanDisabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -282,7 +280,7 @@ fun LoRaConfigItemList(
|
|||
title = stringResource(R.string.ignore_mqtt),
|
||||
checked = loraInput.ignoreMqtt,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { loraInput = loraInput.copy { ignoreMqtt = it } }
|
||||
onCheckedChange = { loraInput = loraInput.copy { ignoreMqtt = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -292,7 +290,7 @@ fun LoRaConfigItemList(
|
|||
title = stringResource(R.string.ok_to_mqtt),
|
||||
checked = loraInput.configOkToMqtt,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { loraInput = loraInput.copy { configOkToMqtt = it } }
|
||||
onCheckedChange = { loraInput = loraInput.copy { configOkToMqtt = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -307,7 +305,7 @@ fun LoRaConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(loraInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -320,6 +318,6 @@ private fun LoRaConfigPreview() {
|
|||
loraConfig = Channel.default.loraConfig,
|
||||
primarySettings = Channel.default.settings,
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
onSaveClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
@file:Suppress("LongMethod")
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -46,7 +46,7 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun MQTTConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -48,8 +48,9 @@ import com.geeksville.mesh.util.DistanceUnit
|
|||
import com.geeksville.mesh.util.toDistanceString
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private const val PositionPrecisionMin = 12
|
||||
private const val PositionPrecisionMax = 15
|
||||
private const val POSITION_PRECISION_MIN = 12
|
||||
private const val POSITION_PRECISION_MAX = 15
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun MapReportingPreference(
|
||||
|
|
@ -62,13 +63,11 @@ fun MapReportingPreference(
|
|||
publishIntervalSecs: Int = 3600,
|
||||
onPublishIntervalSecsChanged: (Int) -> Unit = {},
|
||||
enabled: Boolean,
|
||||
focusManager: FocusManager
|
||||
focusManager: FocusManager,
|
||||
) {
|
||||
Column {
|
||||
var showMapReportingWarning by rememberSaveable { mutableStateOf(mapReportingEnabled) }
|
||||
LaunchedEffect(mapReportingEnabled) {
|
||||
showMapReportingWarning = mapReportingEnabled
|
||||
}
|
||||
LaunchedEffect(mapReportingEnabled) { showMapReportingWarning = mapReportingEnabled }
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.map_reporting),
|
||||
summary = stringResource(R.string.map_reporting_summary),
|
||||
|
|
@ -81,21 +80,13 @@ fun MapReportingPreference(
|
|||
} else if (!checked) {
|
||||
onMapReportingEnabledChanged(false)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
AnimatedVisibility(showMapReportingWarning) {
|
||||
Card(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.map_reporting_consent_header),
|
||||
modifier = Modifier.padding(16.dp),
|
||||
)
|
||||
Card(modifier = Modifier.padding(16.dp)) {
|
||||
Text(text = stringResource(R.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(R.string.map_reporting_consent_text), modifier = Modifier.padding(16.dp))
|
||||
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.i_agree),
|
||||
|
|
@ -118,18 +109,14 @@ fun MapReportingPreference(
|
|||
value = positionPrecision.toFloat(),
|
||||
onValueChange = { onPositionPrecisionChanged(it.roundToInt()) },
|
||||
enabled = enabled,
|
||||
valueRange = PositionPrecisionMin.toFloat()..PositionPrecisionMax.toFloat(),
|
||||
steps = PositionPrecisionMax - PositionPrecisionMin - 1,
|
||||
valueRange = POSITION_PRECISION_MIN.toFloat()..POSITION_PRECISION_MAX.toFloat(),
|
||||
steps = POSITION_PRECISION_MAX - POSITION_PRECISION_MIN - 1,
|
||||
)
|
||||
val precisionMeters = precisionBitsToMeters(positionPrecision).toInt()
|
||||
val unit = DistanceUnit.Companion.getFromLocale()
|
||||
Text(
|
||||
text = precisionMeters.toDistanceString(unit),
|
||||
modifier = Modifier.Companion.padding(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp
|
||||
),
|
||||
modifier = Modifier.Companion.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
fontSize = MaterialTheme.typography.bodyLarge.fontSize,
|
||||
overflow = TextOverflow.Companion.Ellipsis,
|
||||
maxLines = 1,
|
||||
|
|
@ -161,6 +148,6 @@ fun MapReportingPreview() {
|
|||
positionPrecision = 5,
|
||||
onPositionPrecisionChanged = {},
|
||||
enabled = true,
|
||||
focusManager = focusManager
|
||||
focusManager = focusManager,
|
||||
)
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -40,19 +40,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun NeighborInfoConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun NeighborInfoConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
NeighborInfoConfigItemList(
|
||||
|
|
@ -61,7 +56,7 @@ fun NeighborInfoConfigScreen(
|
|||
onSaveClicked = { neighborInfoInput ->
|
||||
val config = moduleConfig { neighborInfo = neighborInfoInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -74,9 +69,7 @@ fun NeighborInfoConfigItemList(
|
|||
val focusManager = LocalFocusManager.current
|
||||
var neighborInfoInput by rememberSaveable { mutableStateOf(neighborInfoConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.neighbor_info_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -84,9 +77,7 @@ fun NeighborInfoConfigItemList(
|
|||
title = stringResource(R.string.neighbor_info_enabled),
|
||||
checked = neighborInfoInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
neighborInfoInput = neighborInfoInput.copy { this.enabled = it }
|
||||
}
|
||||
onCheckedChange = { neighborInfoInput = neighborInfoInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -97,9 +88,7 @@ fun NeighborInfoConfigItemList(
|
|||
value = neighborInfoInput.updateInterval,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
neighborInfoInput = neighborInfoInput.copy { updateInterval = it }
|
||||
}
|
||||
onValueChanged = { neighborInfoInput = neighborInfoInput.copy { updateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -109,9 +98,7 @@ fun NeighborInfoConfigItemList(
|
|||
summary = stringResource(id = R.string.config_device_transmitOverLora_summary),
|
||||
checked = neighborInfoInput.transmitOverLora,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
neighborInfoInput = neighborInfoInput.copy { transmitOverLora = it }
|
||||
}
|
||||
onCheckedChange = { neighborInfoInput = neighborInfoInput.copy { transmitOverLora = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
|
@ -126,7 +113,7 @@ fun NeighborInfoConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(neighborInfoInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -138,6 +125,6 @@ private fun NeighborInfoConfigPreview() {
|
|||
NeighborInfoConfigItemList(
|
||||
neighborInfoConfig = ModuleConfigProtos.ModuleConfig.NeighborInfoConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
onSaveClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
|
|
@ -54,30 +54,20 @@ import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SimpleAlertDialog
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import com.journeyapps.barcodescanner.ScanContract
|
||||
import com.journeyapps.barcodescanner.ScanOptions
|
||||
|
||||
@Composable
|
||||
private fun ScanErrorDialog(
|
||||
onDismiss: () -> Unit = {}
|
||||
) = SimpleAlertDialog(
|
||||
title = R.string.error,
|
||||
text = R.string.wifi_qr_code_error,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
private fun ScanErrorDialog(onDismiss: () -> Unit = {}) =
|
||||
SimpleAlertDialog(title = R.string.error, text = R.string.wifi_qr_code_error, onDismiss = onDismiss)
|
||||
|
||||
@Composable
|
||||
fun NetworkConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
NetworkConfigItemList(
|
||||
|
|
@ -88,13 +78,13 @@ fun NetworkConfigScreen(
|
|||
onSaveClicked = { networkInput ->
|
||||
val config = config { network = networkInput }
|
||||
viewModel.setConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun extractWifiCredentials(qrCode: String) = Regex("""WIFI:S:(.*?);.*?P:(.*?);""")
|
||||
.find(qrCode)?.destructured
|
||||
?.let { (ssid, password) -> ssid to password } ?: (null to null)
|
||||
private fun extractWifiCredentials(qrCode: String) =
|
||||
Regex("""WIFI:S:(.*?);.*?P:(.*?);""").find(qrCode)?.destructured?.let { (ssid, password) -> ssid to password }
|
||||
?: (null to null)
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
|
@ -113,33 +103,34 @@ fun NetworkConfigItemList(
|
|||
ScanErrorDialog { showScanErrorDialog = false }
|
||||
}
|
||||
|
||||
val barcodeLauncher = rememberLauncherForActivityResult(ScanContract()) { result ->
|
||||
if (result.contents != null) {
|
||||
val (ssid, psk) = extractWifiCredentials(result.contents)
|
||||
if (ssid != null && psk != null) {
|
||||
networkInput = networkInput.copy {
|
||||
wifiSsid = ssid
|
||||
wifiPsk = psk
|
||||
val barcodeLauncher =
|
||||
rememberLauncherForActivityResult(ScanContract()) { result ->
|
||||
if (result.contents != null) {
|
||||
val (ssid, psk) = extractWifiCredentials(result.contents)
|
||||
if (ssid != null && psk != null) {
|
||||
networkInput =
|
||||
networkInput.copy {
|
||||
wifiSsid = ssid
|
||||
wifiPsk = psk
|
||||
}
|
||||
} else {
|
||||
showScanErrorDialog = true
|
||||
}
|
||||
} else {
|
||||
showScanErrorDialog = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun zxingScan() {
|
||||
val zxingScan = ScanOptions().apply {
|
||||
setCameraId(0)
|
||||
setPrompt("")
|
||||
setBeepEnabled(false)
|
||||
setDesiredBarcodeFormats(ScanOptions.QR_CODE)
|
||||
}
|
||||
val zxingScan =
|
||||
ScanOptions().apply {
|
||||
setCameraId(0)
|
||||
setPrompt("")
|
||||
setBeepEnabled(false)
|
||||
setDesiredBarcodeFormats(ScanOptions.QR_CODE)
|
||||
}
|
||||
barcodeLauncher.launch(zxingScan)
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.network_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -147,7 +138,7 @@ fun NetworkConfigItemList(
|
|||
title = stringResource(R.string.wifi_enabled),
|
||||
checked = networkInput.wifiEnabled,
|
||||
enabled = enabled && hasWifi,
|
||||
onCheckedChange = { networkInput = networkInput.copy { wifiEnabled = it } }
|
||||
onCheckedChange = { networkInput = networkInput.copy { wifiEnabled = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
|
@ -159,13 +150,10 @@ fun NetworkConfigItemList(
|
|||
maxSize = 32, // wifi_ssid max_size:33
|
||||
enabled = enabled && hasWifi,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
networkInput = networkInput.copy { wifiSsid = it }
|
||||
}
|
||||
onValueChanged = { networkInput = networkInput.copy { wifiSsid = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -176,17 +164,14 @@ fun NetworkConfigItemList(
|
|||
maxSize = 64, // wifi_psk max_size:65
|
||||
enabled = enabled && hasWifi,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { networkInput = networkInput.copy { wifiPsk = it } }
|
||||
onValueChanged = { networkInput = networkInput.copy { wifiPsk = it } },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Button(
|
||||
onClick = { zxingScan() },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp)
|
||||
.height(48.dp),
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp).height(48.dp),
|
||||
enabled = enabled && hasWifi,
|
||||
) {
|
||||
Text(text = stringResource(R.string.wifi_qr_code_scan))
|
||||
|
|
@ -198,7 +183,7 @@ fun NetworkConfigItemList(
|
|||
title = stringResource(R.string.ethernet_enabled),
|
||||
checked = networkInput.ethEnabled,
|
||||
enabled = enabled && hasEthernet,
|
||||
onCheckedChange = { networkInput = networkInput.copy { ethEnabled = it } }
|
||||
onCheckedChange = { networkInput = networkInput.copy { ethEnabled = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
|
@ -210,13 +195,10 @@ fun NetworkConfigItemList(
|
|||
maxSize = 32, // ntp_server max_size:33
|
||||
enabled = enabled,
|
||||
isError = networkInput.ntpServer.isEmpty(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
networkInput = networkInput.copy { ntpServer = it }
|
||||
}
|
||||
onValueChanged = { networkInput = networkInput.copy { ntpServer = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -227,13 +209,10 @@ fun NetworkConfigItemList(
|
|||
maxSize = 32, // rsyslog_server max_size:33
|
||||
enabled = enabled,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
networkInput = networkInput.copy { rsyslogServer = it }
|
||||
}
|
||||
onValueChanged = { networkInput = networkInput.copy { rsyslogServer = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -241,11 +220,12 @@ fun NetworkConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.ipv4_mode),
|
||||
enabled = enabled,
|
||||
items = NetworkConfig.AddressMode.entries
|
||||
items =
|
||||
NetworkConfig.AddressMode.entries
|
||||
.filter { it != NetworkConfig.AddressMode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = networkInput.addressMode,
|
||||
onItemSelected = { networkInput = networkInput.copy { addressMode = it } }
|
||||
onItemSelected = { networkInput = networkInput.copy { addressMode = it } },
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
|
@ -259,7 +239,7 @@ fun NetworkConfigItemList(
|
|||
onValueChanged = {
|
||||
val ipv4 = networkInput.ipv4Config.copy { ip = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +252,7 @@ fun NetworkConfigItemList(
|
|||
onValueChanged = {
|
||||
val ipv4 = networkInput.ipv4Config.copy { gateway = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -285,7 +265,7 @@ fun NetworkConfigItemList(
|
|||
onValueChanged = {
|
||||
val ipv4 = networkInput.ipv4Config.copy { subnet = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -298,14 +278,12 @@ fun NetworkConfigItemList(
|
|||
onValueChanged = {
|
||||
val ipv4 = networkInput.ipv4Config.copy { dns = it }
|
||||
networkInput = networkInput.copy { ipv4Config = ipv4 }
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
if (hasEthernet || hasWifi) {
|
||||
item {
|
||||
PreferenceCategory(text = stringResource(R.string.udp_config))
|
||||
}
|
||||
item { PreferenceCategory(text = stringResource(R.string.udp_config)) }
|
||||
|
||||
item {
|
||||
SwitchPreference(
|
||||
|
|
@ -313,11 +291,8 @@ fun NetworkConfigItemList(
|
|||
checked = networkInput.enabledProtocols == 1,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
networkInput =
|
||||
networkInput.copy {
|
||||
if (it) enabledProtocols = 1 else enabledProtocols = 0
|
||||
}
|
||||
}
|
||||
networkInput = networkInput.copy { if (it) enabledProtocols = 1 else enabledProtocols = 0 }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +308,7 @@ fun NetworkConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(networkInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -347,7 +322,7 @@ private fun NetworkConfigPreview() {
|
|||
hasEthernet = true,
|
||||
networkConfig = NetworkConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
onSaveClicked = {},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
|
|
@ -37,35 +37,24 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.radioconfig.ResponseState
|
||||
import com.geeksville.mesh.ui.settings.radio.ResponseState
|
||||
|
||||
@Composable
|
||||
fun <T> PacketResponseStateDialog(
|
||||
state: ResponseState<T>,
|
||||
onDismiss: () -> Unit = {},
|
||||
onComplete: () -> Unit = {},
|
||||
) {
|
||||
fun <T> PacketResponseStateDialog(state: ResponseState<T>, onDismiss: () -> Unit = {}, onComplete: () -> Unit = {}) {
|
||||
val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
title = {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
if (state is ResponseState.Loading) {
|
||||
val progress by animateFloatAsState(
|
||||
targetValue = state.completed.toFloat() / state.total.toFloat(),
|
||||
label = "progress",
|
||||
)
|
||||
val progress by
|
||||
animateFloatAsState(
|
||||
targetValue = state.completed.toFloat() / state.total.toFloat(),
|
||||
label = "progress",
|
||||
)
|
||||
Text("%.0f%%".format(progress * 100))
|
||||
LinearProgressIndicator(
|
||||
progress = progress,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp),
|
||||
)
|
||||
LinearProgressIndicator(progress = progress, modifier = Modifier.fillMaxWidth().padding(top = 8.dp))
|
||||
if (state.total == state.completed) onComplete()
|
||||
}
|
||||
if (state is ResponseState.Success) {
|
||||
|
|
@ -79,10 +68,8 @@ fun <T> PacketResponseStateDialog(
|
|||
},
|
||||
confirmButton = {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
modifier = Modifier.fillMaxWidth().padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
|
|
@ -91,20 +78,17 @@ fun <T> PacketResponseStateDialog(
|
|||
backDispatcher?.onBackPressed()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
) { Text(stringResource(R.string.close)) }
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
) {
|
||||
Text(stringResource(R.string.close))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PacketResponseStateDialogPreview() {
|
||||
PacketResponseStateDialog(
|
||||
state = ResponseState.Loading(
|
||||
total = 17,
|
||||
completed = 5,
|
||||
),
|
||||
)
|
||||
PacketResponseStateDialog(state = ResponseState.Loading(total = 17, completed = 5))
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -40,19 +40,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun PaxcounterConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun PaxcounterConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
PaxcounterConfigItemList(
|
||||
|
|
@ -61,7 +56,7 @@ fun PaxcounterConfigScreen(
|
|||
onSaveClicked = { paxcounterConfigInput ->
|
||||
val config = moduleConfig { paxcounter = paxcounterConfigInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -75,9 +70,7 @@ fun PaxcounterConfigItemList(
|
|||
val focusManager = LocalFocusManager.current
|
||||
var paxcounterInput by rememberSaveable { mutableStateOf(paxcounterConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.paxcounter_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -85,9 +78,7 @@ fun PaxcounterConfigItemList(
|
|||
title = stringResource(R.string.paxcounter_enabled),
|
||||
checked = paxcounterInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
paxcounterInput = paxcounterInput.copy { this.enabled = it }
|
||||
}
|
||||
onCheckedChange = { paxcounterInput = paxcounterInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -98,9 +89,7 @@ fun PaxcounterConfigItemList(
|
|||
value = paxcounterInput.paxcounterUpdateInterval,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
paxcounterInput = paxcounterInput.copy { paxcounterUpdateInterval = it }
|
||||
}
|
||||
onValueChanged = { paxcounterInput = paxcounterInput.copy { paxcounterUpdateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -110,9 +99,7 @@ fun PaxcounterConfigItemList(
|
|||
value = paxcounterInput.wifiThreshold,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
paxcounterInput = paxcounterInput.copy { wifiThreshold = it }
|
||||
}
|
||||
onValueChanged = { paxcounterInput = paxcounterInput.copy { wifiThreshold = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -122,9 +109,7 @@ fun PaxcounterConfigItemList(
|
|||
value = paxcounterInput.bleThreshold,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
paxcounterInput = paxcounterInput.copy { bleThreshold = it }
|
||||
}
|
||||
onValueChanged = { paxcounterInput = paxcounterInput.copy { bleThreshold = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +123,7 @@ fun PaxcounterConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(paxcounterInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -150,6 +135,6 @@ private fun PaxcounterConfigPreview() {
|
|||
PaxcounterConfigItemList(
|
||||
paxcounterConfig = ModuleConfigProtos.ModuleConfig.PaxcounterConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
onSaveClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
|
|
@ -54,7 +54,7 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -40,7 +40,7 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -40,19 +40,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun RangeTestConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun RangeTestConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
RangeTestConfigItemList(
|
||||
|
|
@ -61,7 +56,7 @@ fun RangeTestConfigScreen(
|
|||
onSaveClicked = { rangeTestInput ->
|
||||
val config = moduleConfig { rangeTest = rangeTestInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -74,9 +69,7 @@ fun RangeTestConfigItemList(
|
|||
val focusManager = LocalFocusManager.current
|
||||
var rangeTestInput by rememberSaveable { mutableStateOf(rangeTestConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.range_test_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -84,7 +77,7 @@ fun RangeTestConfigItemList(
|
|||
title = stringResource(R.string.range_test_enabled),
|
||||
checked = rangeTestInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { rangeTestInput = rangeTestInput.copy { this.enabled = it } }
|
||||
onCheckedChange = { rangeTestInput = rangeTestInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -95,7 +88,7 @@ fun RangeTestConfigItemList(
|
|||
value = rangeTestInput.sender,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { rangeTestInput = rangeTestInput.copy { sender = it } }
|
||||
onValueChanged = { rangeTestInput = rangeTestInput.copy { sender = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +97,7 @@ fun RangeTestConfigItemList(
|
|||
title = stringResource(R.string.save_csv_in_storage_esp32_only),
|
||||
checked = rangeTestInput.save,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { rangeTestInput = rangeTestInput.copy { save = it } }
|
||||
onCheckedChange = { rangeTestInput = rangeTestInput.copy { save = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -119,7 +112,7 @@ fun RangeTestConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(rangeTestInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -128,9 +121,5 @@ fun RangeTestConfigItemList(
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun RangeTestConfig() {
|
||||
RangeTestConfigItemList(
|
||||
rangeTestConfig = RangeTestConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
)
|
||||
RangeTestConfigItemList(rangeTestConfig = RangeTestConfig.getDefaultInstance(), enabled = true, onSaveClicked = {})
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -40,19 +40,14 @@ import com.geeksville.mesh.ui.common.components.EditListPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun RemoteHardwareConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun RemoteHardwareConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
RemoteHardwareConfigItemList(
|
||||
|
|
@ -61,7 +56,7 @@ fun RemoteHardwareConfigScreen(
|
|||
onSaveClicked = { remoteHardwareInput ->
|
||||
val config = moduleConfig { remoteHardware = remoteHardwareInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -74,9 +69,7 @@ fun RemoteHardwareConfigItemList(
|
|||
val focusManager = LocalFocusManager.current
|
||||
var remoteHardwareInput by rememberSaveable { mutableStateOf(remoteHardwareConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.remote_hardware_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -84,9 +77,7 @@ fun RemoteHardwareConfigItemList(
|
|||
title = stringResource(R.string.remote_hardware_enabled),
|
||||
checked = remoteHardwareInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
remoteHardwareInput = remoteHardwareInput.copy { this.enabled = it }
|
||||
}
|
||||
onCheckedChange = { remoteHardwareInput = remoteHardwareInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -96,9 +87,7 @@ fun RemoteHardwareConfigItemList(
|
|||
title = stringResource(R.string.allow_undefined_pin_access),
|
||||
checked = remoteHardwareInput.allowUndefinedPinAccess,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
remoteHardwareInput = remoteHardwareInput.copy { allowUndefinedPinAccess = it }
|
||||
}
|
||||
onCheckedChange = { remoteHardwareInput = remoteHardwareInput.copy { allowUndefinedPinAccess = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -111,11 +100,12 @@ fun RemoteHardwareConfigItemList(
|
|||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValuesChanged = { list ->
|
||||
remoteHardwareInput = remoteHardwareInput.copy {
|
||||
availablePins.clear()
|
||||
availablePins.addAll(list)
|
||||
}
|
||||
}
|
||||
remoteHardwareInput =
|
||||
remoteHardwareInput.copy {
|
||||
availablePins.clear()
|
||||
availablePins.addAll(list)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +119,7 @@ fun RemoteHardwareConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(remoteHardwareInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -141,6 +131,6 @@ private fun RemoteHardwareConfigPreview() {
|
|||
RemoteHardwareConfigItemList(
|
||||
remoteHardwareConfig = RemoteHardwareConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
onSaveClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
|
|
@ -58,24 +58,19 @@ import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.node.NodeActionButton
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import com.geeksville.mesh.util.encodeToString
|
||||
import com.geeksville.mesh.util.toByteString
|
||||
import com.google.protobuf.ByteString
|
||||
import java.security.SecureRandom
|
||||
|
||||
@Composable
|
||||
fun SecurityConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun SecurityConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
val node by viewModel.destNode.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
SecurityConfigItemList(
|
||||
|
|
@ -86,9 +81,7 @@ fun SecurityConfigScreen(
|
|||
val config = config { security = securityInput }
|
||||
viewModel.setConfig(config)
|
||||
},
|
||||
onExport = { uri, securityConfig ->
|
||||
viewModel.exportSecurityConfig(uri, securityConfig)
|
||||
},
|
||||
onExport = { uri, securityConfig -> viewModel.exportSecurityConfig(uri, securityConfig) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -114,13 +107,12 @@ fun SecurityConfigItemList(
|
|||
}
|
||||
}
|
||||
|
||||
val exportConfigLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
it.data?.data?.let { uri -> onExport(uri, securityConfig) }
|
||||
val exportConfigLauncher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
it.data?.data?.let { uri -> onExport(uri, securityConfig) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var showKeyGenerationDialog by rememberSaveable { mutableStateOf(false) }
|
||||
PrivateKeyRegenerateDialog(
|
||||
|
|
@ -131,7 +123,7 @@ fun SecurityConfigItemList(
|
|||
showKeyGenerationDialog = false
|
||||
onConfirm(securityInput)
|
||||
},
|
||||
onDismiss = { showKeyGenerationDialog = false }
|
||||
onDismiss = { showKeyGenerationDialog = false },
|
||||
)
|
||||
var showEditSecurityConfigDialog by rememberSaveable { mutableStateOf(false) }
|
||||
if (showEditSecurityConfigDialog) {
|
||||
|
|
@ -143,14 +135,15 @@ fun SecurityConfigItemList(
|
|||
TextButton(
|
||||
onClick = {
|
||||
showEditSecurityConfigDialog = false
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/*"
|
||||
putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"${user?.shortName}_keys_${System.currentTimeMillis()}.json"
|
||||
)
|
||||
}
|
||||
val intent =
|
||||
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/*"
|
||||
putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"${user?.shortName}_keys_${System.currentTimeMillis()}.json",
|
||||
)
|
||||
}
|
||||
exportConfigLauncher.launch(intent)
|
||||
},
|
||||
) {
|
||||
|
|
@ -160,9 +153,7 @@ fun SecurityConfigItemList(
|
|||
)
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.security_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -177,11 +168,7 @@ fun SecurityConfigItemList(
|
|||
securityInput = securityInput.copy { publicKey = it }
|
||||
}
|
||||
},
|
||||
trailingIcon = {
|
||||
CopyIconButton(
|
||||
valueToCopy = securityInput.publicKey.encodeToString(),
|
||||
)
|
||||
}
|
||||
trailingIcon = { CopyIconButton(valueToCopy = securityInput.publicKey.encodeToString()) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -196,11 +183,7 @@ fun SecurityConfigItemList(
|
|||
securityInput = securityInput.copy { privateKey = it }
|
||||
}
|
||||
},
|
||||
trailingIcon = {
|
||||
CopyIconButton(
|
||||
valueToCopy = securityInput.privateKey.encodeToString(),
|
||||
)
|
||||
}
|
||||
trailingIcon = { CopyIconButton(valueToCopy = securityInput.privateKey.encodeToString()) },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -210,9 +193,7 @@ fun SecurityConfigItemList(
|
|||
title = stringResource(R.string.regenerate_private_key),
|
||||
enabled = enabled,
|
||||
icon = Icons.TwoTone.Warning,
|
||||
onClick = {
|
||||
showKeyGenerationDialog = true
|
||||
}
|
||||
onClick = { showKeyGenerationDialog = true },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -222,9 +203,7 @@ fun SecurityConfigItemList(
|
|||
title = stringResource(R.string.export_keys),
|
||||
enabled = enabled,
|
||||
icon = Icons.TwoTone.Warning,
|
||||
onClick = {
|
||||
showEditSecurityConfigDialog = true
|
||||
}
|
||||
onClick = { showEditSecurityConfigDialog = true },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -236,10 +215,11 @@ fun SecurityConfigItemList(
|
|||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValuesChanged = {
|
||||
securityInput = securityInput.copy {
|
||||
adminKey.clear()
|
||||
adminKey.addAll(it)
|
||||
}
|
||||
securityInput =
|
||||
securityInput.copy {
|
||||
adminKey.clear()
|
||||
adminKey.addAll(it)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -249,9 +229,7 @@ fun SecurityConfigItemList(
|
|||
title = stringResource(R.string.managed_mode),
|
||||
checked = securityInput.isManaged,
|
||||
enabled = enabled && securityInput.adminKeyCount > 0,
|
||||
onCheckedChange = {
|
||||
securityInput = securityInput.copy { isManaged = it }
|
||||
}
|
||||
onCheckedChange = { securityInput = securityInput.copy { isManaged = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -261,7 +239,7 @@ fun SecurityConfigItemList(
|
|||
title = stringResource(R.string.serial_console),
|
||||
checked = securityInput.serialEnabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { securityInput = securityInput.copy { serialEnabled = it } }
|
||||
onCheckedChange = { securityInput = securityInput.copy { serialEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -271,9 +249,7 @@ fun SecurityConfigItemList(
|
|||
title = stringResource(R.string.debug_log_api_enabled),
|
||||
checked = securityInput.debugLogApiEnabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
securityInput = securityInput.copy { debugLogApiEnabled = it }
|
||||
}
|
||||
onCheckedChange = { securityInput = securityInput.copy { debugLogApiEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -283,9 +259,7 @@ fun SecurityConfigItemList(
|
|||
title = stringResource(R.string.legacy_admin_channel),
|
||||
checked = securityInput.adminChannelEnabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
securityInput = securityInput.copy { adminChannelEnabled = it }
|
||||
}
|
||||
onCheckedChange = { securityInput = securityInput.copy { adminChannelEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -300,7 +274,7 @@ fun SecurityConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onConfirm(securityInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -323,34 +297,27 @@ fun PrivateKeyRegenerateDialog(
|
|||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
securityInput = securityInput.copy {
|
||||
clearPrivateKey()
|
||||
clearPublicKey()
|
||||
// Generate a random "f" value
|
||||
val f = ByteArray(32).apply {
|
||||
SecureRandom().nextBytes(this)
|
||||
securityInput =
|
||||
securityInput.copy {
|
||||
clearPrivateKey()
|
||||
clearPublicKey()
|
||||
// Generate a random "f" value
|
||||
val f = ByteArray(32).apply { SecureRandom().nextBytes(this) }
|
||||
// Adjust the value to make it valid as an "s" value for eval().
|
||||
// According to the specification we need to mask off the 3
|
||||
// right-most bits of f[0], mask off the left-most bit of f[31],
|
||||
// and set the second to left-most bit of f[31].
|
||||
f[0] = (f[0].toInt() and 0xF8).toByte()
|
||||
f[31] = ((f[31].toInt() and 0x7F) or 0x40).toByte()
|
||||
privateKey = ByteString.copyFrom(f)
|
||||
}
|
||||
// Adjust the value to make it valid as an "s" value for eval().
|
||||
// According to the specification we need to mask off the 3
|
||||
// right-most bits of f[0], mask off the left-most bit of f[31],
|
||||
// and set the second to left-most bit of f[31].
|
||||
f[0] = (f[0].toInt() and 0xF8).toByte()
|
||||
f[31] = ((f[31].toInt() and 0x7F) or 0x40).toByte()
|
||||
privateKey = ByteString.copyFrom(f)
|
||||
}
|
||||
onConfirm(securityInput)
|
||||
},
|
||||
) {
|
||||
Text(stringResource(R.string.okay))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = onDismiss,
|
||||
) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -358,9 +325,5 @@ fun PrivateKeyRegenerateDialog(
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SecurityConfigPreview() {
|
||||
SecurityConfigItemList(
|
||||
securityConfig = SecurityConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onConfirm = {},
|
||||
)
|
||||
SecurityConfigItemList(securityConfig = SecurityConfig.getDefaultInstance(), enabled = true, onConfirm = {})
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -41,19 +41,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun SerialConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun SerialConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
SerialConfigItemList(
|
||||
|
|
@ -62,22 +57,17 @@ fun SerialConfigScreen(
|
|||
onSaveClicked = { serialInput ->
|
||||
val config = moduleConfig { serial = serialInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun SerialConfigItemList(
|
||||
serialConfig: SerialConfig,
|
||||
enabled: Boolean,
|
||||
onSaveClicked: (SerialConfig) -> Unit,
|
||||
) {
|
||||
fun SerialConfigItemList(serialConfig: SerialConfig, enabled: Boolean, onSaveClicked: (SerialConfig) -> Unit) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
var serialInput by rememberSaveable { mutableStateOf(serialConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.serial_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -85,7 +75,7 @@ fun SerialConfigItemList(
|
|||
title = stringResource(R.string.serial_enabled),
|
||||
checked = serialInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { serialInput = serialInput.copy { this.enabled = it } }
|
||||
onCheckedChange = { serialInput = serialInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -95,7 +85,7 @@ fun SerialConfigItemList(
|
|||
title = stringResource(R.string.echo_enabled),
|
||||
checked = serialInput.echo,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { serialInput = serialInput.copy { echo = it } }
|
||||
onCheckedChange = { serialInput = serialInput.copy { echo = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -106,7 +96,7 @@ fun SerialConfigItemList(
|
|||
value = serialInput.rxd,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { serialInput = serialInput.copy { rxd = it } }
|
||||
onValueChanged = { serialInput = serialInput.copy { rxd = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +106,7 @@ fun SerialConfigItemList(
|
|||
value = serialInput.txd,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { serialInput = serialInput.copy { txd = it } }
|
||||
onValueChanged = { serialInput = serialInput.copy { txd = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -124,11 +114,12 @@ fun SerialConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.serial_baud_rate),
|
||||
enabled = enabled,
|
||||
items = SerialConfig.Serial_Baud.entries
|
||||
items =
|
||||
SerialConfig.Serial_Baud.entries
|
||||
.filter { it != SerialConfig.Serial_Baud.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = serialInput.baud,
|
||||
onItemSelected = { serialInput = serialInput.copy { baud = it } }
|
||||
onItemSelected = { serialInput = serialInput.copy { baud = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -139,7 +130,7 @@ fun SerialConfigItemList(
|
|||
value = serialInput.timeout,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { serialInput = serialInput.copy { timeout = it } }
|
||||
onValueChanged = { serialInput = serialInput.copy { timeout = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -147,11 +138,12 @@ fun SerialConfigItemList(
|
|||
DropDownPreference(
|
||||
title = stringResource(R.string.serial_mode),
|
||||
enabled = enabled,
|
||||
items = SerialConfig.Serial_Mode.entries
|
||||
items =
|
||||
SerialConfig.Serial_Mode.entries
|
||||
.filter { it != SerialConfig.Serial_Mode.UNRECOGNIZED }
|
||||
.map { it to it.name },
|
||||
selectedItem = serialInput.mode,
|
||||
onItemSelected = { serialInput = serialInput.copy { mode = it } }
|
||||
onItemSelected = { serialInput = serialInput.copy { mode = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -161,9 +153,7 @@ fun SerialConfigItemList(
|
|||
title = stringResource(R.string.override_console_serial_port),
|
||||
checked = serialInput.overrideConsoleSerialPort,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
serialInput = serialInput.copy { overrideConsoleSerialPort = it }
|
||||
}
|
||||
onCheckedChange = { serialInput = serialInput.copy { overrideConsoleSerialPort = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -178,7 +168,7 @@ fun SerialConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(serialInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -187,9 +177,5 @@ fun SerialConfigItemList(
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SerialConfigPreview() {
|
||||
SerialConfigItemList(
|
||||
serialConfig = SerialConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
)
|
||||
SerialConfigItemList(serialConfig = SerialConfig.getDefaultInstance(), enabled = true, onSaveClicked = {})
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -40,19 +40,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun StoreForwardConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun StoreForwardConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
StoreForwardConfigItemList(
|
||||
|
|
@ -61,7 +56,7 @@ fun StoreForwardConfigScreen(
|
|||
onSaveClicked = { storeForwardInput ->
|
||||
val config = moduleConfig { storeForward = storeForwardInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -74,9 +69,7 @@ fun StoreForwardConfigItemList(
|
|||
val focusManager = LocalFocusManager.current
|
||||
var storeForwardInput by rememberSaveable { mutableStateOf(storeForwardConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.store_forward_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -84,9 +77,7 @@ fun StoreForwardConfigItemList(
|
|||
title = stringResource(R.string.store_forward_enabled),
|
||||
checked = storeForwardInput.enabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
storeForwardInput = storeForwardInput.copy { this.enabled = it }
|
||||
}
|
||||
onCheckedChange = { storeForwardInput = storeForwardInput.copy { this.enabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -96,7 +87,7 @@ fun StoreForwardConfigItemList(
|
|||
title = stringResource(R.string.heartbeat),
|
||||
checked = storeForwardInput.heartbeat,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { storeForwardInput = storeForwardInput.copy { heartbeat = it } }
|
||||
onCheckedChange = { storeForwardInput = storeForwardInput.copy { heartbeat = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -107,7 +98,7 @@ fun StoreForwardConfigItemList(
|
|||
value = storeForwardInput.records,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { storeForwardInput = storeForwardInput.copy { records = it } }
|
||||
onValueChanged = { storeForwardInput = storeForwardInput.copy { records = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -117,9 +108,7 @@ fun StoreForwardConfigItemList(
|
|||
value = storeForwardInput.historyReturnMax,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
storeForwardInput = storeForwardInput.copy { historyReturnMax = it }
|
||||
}
|
||||
onValueChanged = { storeForwardInput = storeForwardInput.copy { historyReturnMax = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -129,9 +118,7 @@ fun StoreForwardConfigItemList(
|
|||
value = storeForwardInput.historyReturnWindow,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
storeForwardInput = storeForwardInput.copy { historyReturnWindow = it }
|
||||
}
|
||||
onValueChanged = { storeForwardInput = storeForwardInput.copy { historyReturnWindow = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +127,7 @@ fun StoreForwardConfigItemList(
|
|||
title = stringResource(R.string.server),
|
||||
checked = storeForwardInput.isServer,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { storeForwardInput = storeForwardInput.copy { isServer = it } }
|
||||
onCheckedChange = { storeForwardInput = storeForwardInput.copy { isServer = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -155,7 +142,7 @@ fun StoreForwardConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(storeForwardInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -167,6 +154,6 @@ private fun StoreForwardConfigPreview() {
|
|||
StoreForwardConfigItemList(
|
||||
storeForwardConfig = StoreForwardConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
onSaveClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -40,19 +40,14 @@ import com.geeksville.mesh.ui.common.components.EditTextPreference
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
||||
@Composable
|
||||
fun TelemetryConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun TelemetryConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
TelemetryConfigItemList(
|
||||
|
|
@ -61,7 +56,7 @@ fun TelemetryConfigScreen(
|
|||
onSaveClicked = { telemetryInput ->
|
||||
val config = moduleConfig { telemetry = telemetryInput }
|
||||
viewModel.setModuleConfig(config)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -74,9 +69,7 @@ fun TelemetryConfigItemList(
|
|||
val focusManager = LocalFocusManager.current
|
||||
var telemetryInput by rememberSaveable { mutableStateOf(telemetryConfig) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.telemetry_config)) }
|
||||
|
||||
item {
|
||||
|
|
@ -85,9 +78,7 @@ fun TelemetryConfigItemList(
|
|||
value = telemetryInput.deviceUpdateInterval,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
telemetryInput = telemetryInput.copy { deviceUpdateInterval = it }
|
||||
}
|
||||
onValueChanged = { telemetryInput = telemetryInput.copy { deviceUpdateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -97,9 +88,7 @@ fun TelemetryConfigItemList(
|
|||
value = telemetryInput.environmentUpdateInterval,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
telemetryInput = telemetryInput.copy { environmentUpdateInterval = it }
|
||||
}
|
||||
onValueChanged = { telemetryInput = telemetryInput.copy { environmentUpdateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -108,9 +97,7 @@ fun TelemetryConfigItemList(
|
|||
title = stringResource(R.string.environment_metrics_module_enabled),
|
||||
checked = telemetryInput.environmentMeasurementEnabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
telemetryInput = telemetryInput.copy { environmentMeasurementEnabled = it }
|
||||
}
|
||||
onCheckedChange = { telemetryInput = telemetryInput.copy { environmentMeasurementEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -120,9 +107,7 @@ fun TelemetryConfigItemList(
|
|||
title = stringResource(R.string.environment_metrics_on_screen_enabled),
|
||||
checked = telemetryInput.environmentScreenEnabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
telemetryInput = telemetryInput.copy { environmentScreenEnabled = it }
|
||||
}
|
||||
onCheckedChange = { telemetryInput = telemetryInput.copy { environmentScreenEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -132,9 +117,7 @@ fun TelemetryConfigItemList(
|
|||
title = stringResource(R.string.environment_metrics_use_fahrenheit),
|
||||
checked = telemetryInput.environmentDisplayFahrenheit,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
telemetryInput = telemetryInput.copy { environmentDisplayFahrenheit = it }
|
||||
}
|
||||
onCheckedChange = { telemetryInput = telemetryInput.copy { environmentDisplayFahrenheit = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -144,9 +127,7 @@ fun TelemetryConfigItemList(
|
|||
title = stringResource(R.string.air_quality_metrics_module_enabled),
|
||||
checked = telemetryInput.airQualityEnabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
telemetryInput = telemetryInput.copy { airQualityEnabled = it }
|
||||
}
|
||||
onCheckedChange = { telemetryInput = telemetryInput.copy { airQualityEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -157,9 +138,7 @@ fun TelemetryConfigItemList(
|
|||
value = telemetryInput.airQualityInterval,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
telemetryInput = telemetryInput.copy { airQualityInterval = it }
|
||||
}
|
||||
onValueChanged = { telemetryInput = telemetryInput.copy { airQualityInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -168,9 +147,7 @@ fun TelemetryConfigItemList(
|
|||
title = stringResource(R.string.power_metrics_module_enabled),
|
||||
checked = telemetryInput.powerMeasurementEnabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
telemetryInput = telemetryInput.copy { powerMeasurementEnabled = it }
|
||||
}
|
||||
onCheckedChange = { telemetryInput = telemetryInput.copy { powerMeasurementEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -181,9 +158,7 @@ fun TelemetryConfigItemList(
|
|||
value = telemetryInput.powerUpdateInterval,
|
||||
enabled = enabled,
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
telemetryInput = telemetryInput.copy { powerUpdateInterval = it }
|
||||
}
|
||||
onValueChanged = { telemetryInput = telemetryInput.copy { powerUpdateInterval = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -192,9 +167,7 @@ fun TelemetryConfigItemList(
|
|||
title = stringResource(R.string.power_metrics_on_screen_enabled),
|
||||
checked = telemetryInput.powerScreenEnabled,
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
telemetryInput = telemetryInput.copy { powerScreenEnabled = it }
|
||||
}
|
||||
onCheckedChange = { telemetryInput = telemetryInput.copy { powerScreenEnabled = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -209,7 +182,7 @@ fun TelemetryConfigItemList(
|
|||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(telemetryInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -218,9 +191,5 @@ fun TelemetryConfigItemList(
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun TelemetryConfigPreview() {
|
||||
TelemetryConfigItemList(
|
||||
telemetryConfig = TelemetryConfig.getDefaultInstance(),
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
)
|
||||
TelemetryConfigItemList(telemetryConfig = TelemetryConfig.getDefaultInstance(), enabled = true, onSaveClicked = {})
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.radioconfig.components
|
||||
package com.geeksville.mesh.ui.settings.radio.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -46,20 +46,15 @@ import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.RegularPreference
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import com.geeksville.mesh.user
|
||||
|
||||
@Composable
|
||||
fun UserConfigScreen(
|
||||
viewModel: RadioConfigViewModel = hiltViewModel(),
|
||||
) {
|
||||
fun UserConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.responseState.isWaiting()) {
|
||||
PacketResponseStateDialog(
|
||||
state = state.responseState,
|
||||
onDismiss = viewModel::clearPacketResponse,
|
||||
)
|
||||
PacketResponseStateDialog(state = state.responseState, onDismiss = viewModel::clearPacketResponse)
|
||||
}
|
||||
|
||||
UserConfigItemList(
|
||||
|
|
@ -85,18 +80,10 @@ fun UserConfigItemList(
|
|||
val validLongName = userInput.longName.isNotBlank()
|
||||
val validShortName = userInput.shortName.isNotBlank()
|
||||
val validNames = validLongName && validShortName
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
item { PreferenceCategory(text = stringResource(R.string.user_config)) }
|
||||
|
||||
item {
|
||||
RegularPreference(
|
||||
title = stringResource(R.string.node_id),
|
||||
subtitle = userInput.id,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
item { RegularPreference(title = stringResource(R.string.node_id), subtitle = userInput.id, onClick = {}) }
|
||||
item { HorizontalDivider() }
|
||||
|
||||
item {
|
||||
|
|
@ -106,13 +93,10 @@ fun UserConfigItemList(
|
|||
maxSize = 39, // long_name max_size:40
|
||||
enabled = enabled,
|
||||
isError = !validLongName,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = {
|
||||
userInput = userInput.copy { longName = it }
|
||||
}
|
||||
onValueChanged = { userInput = userInput.copy { longName = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -123,11 +107,10 @@ fun UserConfigItemList(
|
|||
maxSize = 4, // short_name max_size:5
|
||||
enabled = enabled,
|
||||
isError = !validShortName,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Text, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||
onValueChanged = { userInput = userInput.copy { shortName = it } }
|
||||
onValueChanged = { userInput = userInput.copy { shortName = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +118,7 @@ fun UserConfigItemList(
|
|||
RegularPreference(
|
||||
title = stringResource(R.string.hardware_model),
|
||||
subtitle = userInput.hwModel.name,
|
||||
onClick = {}
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -144,12 +127,11 @@ fun UserConfigItemList(
|
|||
SwitchPreference(
|
||||
title = stringResource(R.string.unmessageable),
|
||||
summary = stringResource(R.string.unmonitored_or_infrastructure),
|
||||
checked = userInput.isUnmessagable || (
|
||||
firmwareVersion < DeviceVersion("2.6.9") &&
|
||||
userInput.role.isUnmessageableRole()
|
||||
),
|
||||
checked =
|
||||
userInput.isUnmessagable ||
|
||||
(firmwareVersion < DeviceVersion("2.6.9") && userInput.role.isUnmessageableRole()),
|
||||
enabled = userInput.hasIsUnmessagable() || firmwareVersion >= DeviceVersion("2.6.9"),
|
||||
onCheckedChange = { userInput = userInput.copy { isUnmessagable = it } }
|
||||
onCheckedChange = { userInput = userInput.copy { isUnmessagable = it } },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +143,7 @@ fun UserConfigItemList(
|
|||
summary = stringResource(R.string.licensed_amateur_radio_text),
|
||||
checked = userInput.isLicensed,
|
||||
enabled = enabled,
|
||||
onCheckedChange = { userInput = userInput.copy { isLicensed = it } }
|
||||
onCheckedChange = { userInput = userInput.copy { isLicensed = it } },
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
|
|
@ -172,10 +154,11 @@ fun UserConfigItemList(
|
|||
onCancelClicked = {
|
||||
focusManager.clearFocus()
|
||||
userInput = userConfig
|
||||
}, onSaveClicked = {
|
||||
},
|
||||
onSaveClicked = {
|
||||
focusManager.clearFocus()
|
||||
onSaveClicked(userInput)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -185,7 +168,8 @@ fun UserConfigItemList(
|
|||
@Composable
|
||||
private fun UserConfigPreview() {
|
||||
UserConfigItemList(
|
||||
userConfig = user {
|
||||
userConfig =
|
||||
user {
|
||||
id = "!a280d9c8"
|
||||
longName = "Meshtastic d9c8"
|
||||
shortName = "d9c8"
|
||||
|
|
@ -193,9 +177,7 @@ private fun UserConfigPreview() {
|
|||
isLicensed = false
|
||||
},
|
||||
enabled = true,
|
||||
onSaveClicked = { },
|
||||
metadata = deviceMetadata {
|
||||
firmwareVersion = "2.8.0"
|
||||
}
|
||||
onSaveClicked = {},
|
||||
metadata = deviceMetadata { firmwareVersion = "2.8.0" },
|
||||
)
|
||||
}
|
||||
|
|
@ -104,9 +104,9 @@ import com.geeksville.mesh.navigation.getNavRouteFrom
|
|||
import com.geeksville.mesh.service.ConnectionState
|
||||
import com.geeksville.mesh.ui.common.components.AdaptiveTwoPane
|
||||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.radioconfig.components.ChannelSelection
|
||||
import com.geeksville.mesh.ui.radioconfig.components.PacketResponseStateDialog
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.components.ChannelSelection
|
||||
import com.geeksville.mesh.ui.settings.radio.components.PacketResponseStateDialog
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue