feat: settings rework (#4678)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2026-03-02 08:51:05 -06:00 committed by GitHub
parent b2b21e10e2
commit fdd07f893f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 941 additions and 306 deletions

View file

@ -19,8 +19,11 @@
package com.geeksville.mesh.navigation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
@ -32,7 +35,11 @@ import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
import org.meshtastic.feature.settings.AboutScreen
import org.meshtastic.feature.settings.AdministrationScreen
import org.meshtastic.feature.settings.DeviceConfigurationScreen
import org.meshtastic.feature.settings.ModuleConfigurationScreen
import org.meshtastic.feature.settings.SettingsScreen
import org.meshtastic.feature.settings.SettingsViewModel
import org.meshtastic.feature.settings.debugging.DebugScreen
import org.meshtastic.feature.settings.filter.FilterSettingsScreen
import org.meshtastic.feature.settings.navigation.ConfigRoute
@ -76,6 +83,7 @@ fun NavGraphBuilder.settingsGraph(navController: NavHostController) {
val parentEntry =
remember(backStackEntry) { navController.getBackStackEntry(SettingsRoutes.SettingsGraph::class) }
SettingsScreen(
settingsViewModel = hiltViewModel(parentEntry),
viewModel = hiltViewModel(parentEntry),
onClickNodeChip = {
navController.navigate(NodesRoutes.NodeDetailGraph(it)) {
@ -84,10 +92,39 @@ fun NavGraphBuilder.settingsGraph(navController: NavHostController) {
}
},
) {
navController.navigate(it) { popUpTo(SettingsRoutes.Settings()) { inclusive = false } }
navController.navigate(it)
}
}
composable<SettingsRoutes.DeviceConfiguration> { backStackEntry ->
val parentEntry =
remember(backStackEntry) { navController.getBackStackEntry(SettingsRoutes.SettingsGraph::class) }
DeviceConfigurationScreen(
viewModel = hiltViewModel(parentEntry),
onBack = navController::popBackStack,
onNavigate = { route -> navController.navigate(route) },
)
}
composable<SettingsRoutes.ModuleConfiguration> { backStackEntry ->
val parentEntry =
remember(backStackEntry) { navController.getBackStackEntry(SettingsRoutes.SettingsGraph::class) }
val settingsViewModel: SettingsViewModel = hiltViewModel(parentEntry)
val excludedModulesUnlocked by settingsViewModel.excludedModulesUnlocked.collectAsStateWithLifecycle()
ModuleConfigurationScreen(
viewModel = hiltViewModel(parentEntry),
excludedModulesUnlocked = excludedModulesUnlocked,
onBack = navController::popBackStack,
onNavigate = { route -> navController.navigate(route) },
)
}
composable<SettingsRoutes.Administration> { backStackEntry ->
val parentEntry =
remember(backStackEntry) { navController.getBackStackEntry(SettingsRoutes.SettingsGraph::class) }
AdministrationScreen(viewModel = hiltViewModel(parentEntry), onBack = navController::popBackStack)
}
composable<SettingsRoutes.CleanNodeDb>(
deepLinks =
listOf(
@ -104,6 +141,7 @@ fun NavGraphBuilder.settingsGraph(navController: NavHostController) {
route = entry.route::class,
parentGraphRoute = SettingsRoutes.SettingsGraph::class,
) { viewModel ->
LaunchedEffect(Unit) { viewModel.setResponseStateLoading(entry) }
when (entry) {
ConfigRoute.USER -> UserConfigScreen(viewModel, onBack = navController::popBackStack)
@ -133,6 +171,7 @@ fun NavGraphBuilder.settingsGraph(navController: NavHostController) {
route = entry.route::class,
parentGraphRoute = SettingsRoutes.SettingsGraph::class,
) { viewModel ->
LaunchedEffect(Unit) { viewModel.setResponseStateLoading(entry) }
when (entry) {
ModuleRoute.MQTT -> MQTTConfigScreen(viewModel, onBack = navController::popBackStack)

View file

@ -27,6 +27,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
@ -35,7 +36,6 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import no.nordicsemi.android.common.core.simpleSharedFlow
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import org.meshtastic.core.ble.BleError
import org.meshtastic.core.ble.BluetoothRepository
@ -82,10 +82,10 @@ constructor(
private val _connectionState = MutableStateFlow<ConnectionState>(ConnectionState.Disconnected)
val connectionState: StateFlow<ConnectionState> = _connectionState.asStateFlow()
private val _receivedData = simpleSharedFlow<ByteArray>()
private val _receivedData = MutableSharedFlow<ByteArray>(extraBufferCapacity = 64)
val receivedData: SharedFlow<ByteArray> = _receivedData
private val _connectionError = simpleSharedFlow<BleError>()
private val _connectionError = MutableSharedFlow<BleError>(extraBufferCapacity = 64)
val connectionError: SharedFlow<BleError> = _connectionError.asSharedFlow()
// Thread-safe StateFlow for tracking device address changes
@ -371,7 +371,7 @@ constructor(
serviceScope.handledLaunch { handleSendToRadio(a) }
}
private val _meshActivity = simpleSharedFlow<MeshActivity>()
private val _meshActivity = MutableSharedFlow<MeshActivity>(extraBufferCapacity = 64)
val meshActivity: SharedFlow<MeshActivity> = _meshActivity.asSharedFlow()
private fun emitSendActivity() {

View file

@ -94,6 +94,10 @@ constructor(
radioConfigRepository?.channelSetFlow?.onEach { channelSet.value = it }?.launchIn(scope)
}
fun getCachedLocalConfig(): LocalConfig = localConfig.value
fun getCachedChannelSet(): ChannelSet = channelSet.value
@VisibleForTesting internal constructor() : this(null, null, null, null)
fun getCurrentPacketId(): Long = currentPacketId.get()

View file

@ -31,10 +31,8 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import org.meshtastic.core.common.hasLocationPermission
import org.meshtastic.core.common.util.handledLaunch
import org.meshtastic.core.common.util.toRemoteExceptions
@ -249,9 +247,7 @@ class MeshService : Service() {
override fun send(p: DataPacket) = toRemoteExceptions { router.actionHandler.handleSend(p, myNodeNum) }
override fun getConfig(): ByteArray = toRemoteExceptions {
runBlocking { radioConfigRepository.localConfigFlow.first().encode() }
}
override fun getConfig(): ByteArray = toRemoteExceptions { commandSender.getCachedLocalConfig().encode() }
override fun setConfig(payload: ByteArray) = toRemoteExceptions {
router.actionHandler.handleSetConfig(payload, myNodeNum)
@ -310,7 +306,7 @@ class MeshService : Service() {
}
override fun getChannelSet(): ByteArray = toRemoteExceptions {
runBlocking { radioConfigRepository.channelSetFlow.first().encode() }
commandSender.getCachedChannelSet().encode()
}
override fun getNodes(): List<NodeInfo> = nodeManager.getNodes()