mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
250 lines
12 KiB
Kotlin
250 lines
12 KiB
Kotlin
/*
|
|
* Copyright (c) 2025-2026 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/>.
|
|
*/
|
|
@file:Suppress("Wrapping", "SpacingAroundColon")
|
|
|
|
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
|
|
import androidx.navigation.navDeepLink
|
|
import androidx.navigation.navigation
|
|
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
|
|
import org.meshtastic.core.navigation.Graph
|
|
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
|
|
import org.meshtastic.feature.settings.navigation.ModuleRoute
|
|
import org.meshtastic.feature.settings.radio.CleanNodeDatabaseScreen
|
|
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
|
import org.meshtastic.feature.settings.radio.channel.ChannelConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.AmbientLightingConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.AudioConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.BluetoothConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.CannedMessageConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.DetectionSensorConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.DeviceConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.DisplayConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.ExternalNotificationConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.LoRaConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.MQTTConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.NeighborInfoConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.NetworkConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.PaxcounterConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.PositionConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.PowerConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.RangeTestConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.RemoteHardwareConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.SecurityConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.SerialConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.StatusMessageConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.StoreForwardConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.TAKConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.TelemetryConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.TrafficManagementConfigScreen
|
|
import org.meshtastic.feature.settings.radio.component.UserConfigScreen
|
|
import kotlin.reflect.KClass
|
|
|
|
@Suppress("LongMethod")
|
|
fun NavGraphBuilder.settingsGraph(navController: NavHostController) {
|
|
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(SettingsRoutes.SettingsGraph::class) }
|
|
SettingsScreen(
|
|
settingsViewModel = hiltViewModel(parentEntry),
|
|
viewModel = hiltViewModel(parentEntry),
|
|
onClickNodeChip = {
|
|
navController.navigate(NodesRoutes.NodeDetailGraph(it)) {
|
|
launchSingleTop = true
|
|
restoreState = true
|
|
}
|
|
},
|
|
) {
|
|
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(
|
|
navDeepLink<SettingsRoutes.CleanNodeDb>(
|
|
basePath = "$DEEP_LINK_BASE_URI/settings/radio/clean_node_db",
|
|
),
|
|
),
|
|
) {
|
|
CleanNodeDatabaseScreen()
|
|
}
|
|
|
|
ConfigRoute.entries.forEach { entry ->
|
|
navController.configComposable(
|
|
route = entry.route::class,
|
|
parentGraphRoute = SettingsRoutes.SettingsGraph::class,
|
|
) { viewModel ->
|
|
LaunchedEffect(Unit) { viewModel.setResponseStateLoading(entry) }
|
|
when (entry) {
|
|
ConfigRoute.USER -> UserConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ConfigRoute.CHANNELS -> ChannelConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ConfigRoute.DEVICE -> DeviceConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ConfigRoute.POSITION -> PositionConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ConfigRoute.POWER -> PowerConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ConfigRoute.NETWORK -> NetworkConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ConfigRoute.DISPLAY -> DisplayConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ConfigRoute.LORA -> LoRaConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ConfigRoute.BLUETOOTH -> BluetoothConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ConfigRoute.SECURITY -> SecurityConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
}
|
|
}
|
|
}
|
|
|
|
ModuleRoute.entries.forEach { entry ->
|
|
navController.configComposable(
|
|
route = entry.route::class,
|
|
parentGraphRoute = SettingsRoutes.SettingsGraph::class,
|
|
) { viewModel ->
|
|
LaunchedEffect(Unit) { viewModel.setResponseStateLoading(entry) }
|
|
when (entry) {
|
|
ModuleRoute.MQTT -> MQTTConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.SERIAL -> SerialConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.EXT_NOTIFICATION ->
|
|
ExternalNotificationConfigScreen(viewModel = viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.STORE_FORWARD ->
|
|
StoreForwardConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.RANGE_TEST -> RangeTestConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.TELEMETRY -> TelemetryConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.CANNED_MESSAGE ->
|
|
CannedMessageConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.AUDIO -> AudioConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.REMOTE_HARDWARE ->
|
|
RemoteHardwareConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.NEIGHBOR_INFO ->
|
|
NeighborInfoConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.AMBIENT_LIGHTING ->
|
|
AmbientLightingConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.DETECTION_SENSOR ->
|
|
DetectionSensorConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.PAXCOUNTER -> PaxcounterConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.STATUS_MESSAGE ->
|
|
StatusMessageConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.TRAFFIC_MANAGEMENT ->
|
|
TrafficManagementConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
|
|
ModuleRoute.TAK -> TAKConfigScreen(viewModel, onBack = navController::popBackStack)
|
|
}
|
|
}
|
|
}
|
|
|
|
composable<SettingsRoutes.DebugPanel>(
|
|
deepLinks =
|
|
listOf(navDeepLink<SettingsRoutes.DebugPanel>(basePath = "$DEEP_LINK_BASE_URI/settings/debug_panel")),
|
|
) {
|
|
DebugScreen(onNavigateUp = navController::navigateUp)
|
|
}
|
|
|
|
composable<SettingsRoutes.About> { AboutScreen(onNavigateUp = navController::navigateUp) }
|
|
|
|
composable<SettingsRoutes.FilterSettings> { FilterSettingsScreen(onBack = navController::navigateUp) }
|
|
}
|
|
}
|
|
|
|
context(_: NavGraphBuilder)
|
|
inline fun <reified R : Route, reified G : Graph> NavHostController.configComposable(
|
|
noinline content: @Composable (RadioConfigViewModel) -> Unit,
|
|
) {
|
|
configComposable(route = R::class, parentGraphRoute = G::class, content = content)
|
|
}
|
|
|
|
context(navGraphBuilder: NavGraphBuilder)
|
|
fun <R : Route, G : Graph> NavHostController.configComposable(
|
|
route: KClass<R>,
|
|
parentGraphRoute: KClass<G>,
|
|
content: @Composable (RadioConfigViewModel) -> Unit,
|
|
) {
|
|
navGraphBuilder.composable(route = route) { backStackEntry ->
|
|
val parentEntry = remember(backStackEntry) { getBackStackEntry(parentGraphRoute) }
|
|
content(hiltViewModel(parentEntry))
|
|
}
|
|
}
|