Skip to content
2 changes: 2 additions & 0 deletions app/src/fdroid/java/com/geeksville/mesh/ui/node/NodeMap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.viewinterop.AndroidView
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.map.rememberMapViewWithLifecycle
Expand All @@ -40,6 +41,7 @@ private const val DEG_D = 1e-7

@Composable
fun NodeMapScreen(
navController: NavHostController,
@Suppress("UNUSED_PARAMETER") uiViewModel: UIViewModel = hiltViewModel(),
viewModel: MetricsViewModel = hiltViewModel(),
) {
Expand Down
40 changes: 38 additions & 2 deletions app/src/google/java/com/geeksville/mesh/ui/node/NodeMap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,56 @@

package com.geeksville.mesh.ui.node

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.common.components.MainAppBar
import com.geeksville.mesh.ui.map.MapView

const val DEG_D = 1e-7

@Composable
fun NodeMapScreen(uiViewModel: UIViewModel, metricsViewModel: MetricsViewModel = hiltViewModel()) {
fun NodeMapScreen(
navController: NavHostController,
uiViewModel: UIViewModel,
metricsViewModel: MetricsViewModel = hiltViewModel(),
) {
val state by metricsViewModel.state.collectAsState()
val positions = state.positionLogs
val destNum = state.node?.num
MapView(uiViewModel = uiViewModel, focusedNodeNum = destNum, nodeTrack = positions, navigateToNodeDetails = {})
val ourNodeInfo by uiViewModel.ourNodeInfo.collectAsStateWithLifecycle()
val isConnected by uiViewModel.isConnectedStateFlow.collectAsStateWithLifecycle()

Scaffold(
topBar = {
MainAppBar(
title = state.node?.user?.longName ?: "",
ourNode = ourNodeInfo,
isConnected = isConnected,
showNodeChip = false,
canNavigateUp = true,
onNavigateUp = navController::navigateUp,
actions = {},
onAction = {},
)
},
) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) {
MapView(
uiViewModel = uiViewModel,
focusedNodeNum = destNum,
nodeTrack = positions,
navigateToNodeDetails = {},
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ fun NavGraphBuilder.connectionsGraph(navController: NavHostController, bluetooth
ConnectionsScreen(
bluetoothViewModel = bluetoothViewModel,
radioConfigViewModel = hiltViewModel(parentEntry),
onClickNodeChip = {
navController.navigate(NodesRoutes.NodeDetailGraph(it)) {
launchSingleTop = true
restoreState = true
}
},
onNavigateToSettings = { navController.navigate(SettingsRoutes.Settings()) },
onNavigateToNodeDetails = { navController.navigate(NodesRoutes.NodeDetailGraph(it)) },
onConfigNavigate = { route -> navController.navigate(route) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ fun NavGraphBuilder.contactsGraph(navController: NavHostController, uiViewModel:
) {
ContactsScreen(
uiViewModel,
onClickNodeChip = {
navController.navigate(NodesRoutes.NodeDetailGraph(it)) {
launchSingleTop = true
restoreState = true
}
},
onNavigateToMessages = { navController.navigate(ContactsRoutes.Messages(it)) },
onNavigateToNodeDetails = { navController.navigate(NodesRoutes.NodeDetailGraph(it)) },
onNavigateToShare = { navController.navigate(ChannelsRoutes.ChannelsGraph) },
Expand Down
48 changes: 44 additions & 4 deletions app/src/main/java/com/geeksville/mesh/navigation/MapNavigation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,58 @@

package com.geeksville.mesh.navigation

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import com.geeksville.mesh.R
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.common.components.MainAppBar
import com.geeksville.mesh.ui.map.MapView
import com.geeksville.mesh.ui.node.components.NodeMenuAction

fun NavGraphBuilder.mapGraph(navController: NavHostController, uiViewModel: UIViewModel) {
composable<MapRoutes.Map>(deepLinks = listOf(navDeepLink<MapRoutes.Map>(basePath = "$DEEP_LINK_BASE_URI/map"))) {
MapView(
uiViewModel = uiViewModel,
navigateToNodeDetails = { navController.navigate(NodesRoutes.NodeDetailGraph(it)) },
)
val ourNodeInfo by uiViewModel.ourNodeInfo.collectAsStateWithLifecycle()
val isConnected by uiViewModel.isConnectedStateFlow.collectAsStateWithLifecycle()

Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.map),
ourNode = ourNodeInfo,
isConnected = isConnected,
showNodeChip = ourNodeInfo != null && isConnected,
canNavigateUp = false,
onNavigateUp = {},
actions = {},
onAction = { action ->
when (action) {
is NodeMenuAction.MoreDetails -> {
navController.navigate(NodesRoutes.NodeDetailGraph(action.node.num)) {
launchSingleTop = true
restoreState = true
}
}
else -> {}
}
},
)
},
) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) {
MapView(
uiViewModel = uiViewModel,
navigateToNodeDetails = { navController.navigate(NodesRoutes.NodeDetailGraph(it)) },
)
}
}
}
}
34 changes: 22 additions & 12 deletions app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,12 @@ private inline fun <reified R : Route> NavGraphBuilder.addNodeDetailScreenCompos
navController: NavHostController,
uiViewModel: UIViewModel,
routeInfo: NodeDetailRoute,
crossinline screenContent: @Composable (metricsViewModel: MetricsViewModel, passedUiViewModel: UIViewModel) -> Unit,
crossinline screenContent:
@Composable (
navController: NavHostController,
metricsViewModel: MetricsViewModel,
passedUiViewModel: UIViewModel,
) -> Unit,
) {
composable<R>(
deepLinks =
Expand All @@ -195,68 +200,73 @@ private inline fun <reified R : Route> NavGraphBuilder.addNodeDetailScreenCompos
val parentGraphBackStackEntry =
remember(backStackEntry) { navController.getBackStackEntry(NodesRoutes.NodeDetailGraph::class) }
val metricsViewModel = hiltViewModel<MetricsViewModel>(parentGraphBackStackEntry)
screenContent(metricsViewModel, uiViewModel)
screenContent(navController, metricsViewModel, uiViewModel)
}
}

enum class NodeDetailRoute(
@StringRes val title: Int,
val route: Route,
val icon: ImageVector?,
val screenComposable: @Composable (metricsViewModel: MetricsViewModel, uiViewModel: UIViewModel) -> Unit,
val screenComposable:
@Composable (
navController: NavHostController,
metricsViewModel: MetricsViewModel,
uiViewModel: UIViewModel,
) -> Unit,
) {
DEVICE(
R.string.device,
NodeDetailRoutes.DeviceMetrics,
Icons.Default.Router,
{ metricsVM, _ -> DeviceMetricsScreen(metricsVM) },
{ _, metricsVM, _ -> DeviceMetricsScreen(metricsVM) },
),
NODE_MAP(
R.string.node_map,
NodeDetailRoutes.NodeMap,
Icons.Default.LocationOn,
{ metricsVM, uiVM -> NodeMapScreen(uiVM, metricsVM) },
{ navController, metricsVM, uiVM -> NodeMapScreen(navController, uiVM, metricsVM) },
),
POSITION_LOG(
R.string.position_log,
NodeDetailRoutes.PositionLog,
Icons.Default.LocationOn,
{ metricsVM, _ -> PositionLogScreen(metricsVM) },
{ _, metricsVM, _ -> PositionLogScreen(metricsVM) },
),
ENVIRONMENT(
R.string.environment,
NodeDetailRoutes.EnvironmentMetrics,
Icons.Default.LightMode,
{ metricsVM, _ -> EnvironmentMetricsScreen(metricsVM) },
{ _, metricsVM, _ -> EnvironmentMetricsScreen(metricsVM) },
),
SIGNAL(
R.string.signal,
NodeDetailRoutes.SignalMetrics,
Icons.Default.CellTower,
{ metricsVM, _ -> SignalMetricsScreen(metricsVM) },
{ _, metricsVM, _ -> SignalMetricsScreen(metricsVM) },
),
TRACEROUTE(
R.string.traceroute,
NodeDetailRoutes.TracerouteLog,
Icons.Default.PermScanWifi,
{ metricsVM, _ -> TracerouteLogScreen(viewModel = metricsVM) },
{ _, metricsVM, _ -> TracerouteLogScreen(viewModel = metricsVM) },
),
POWER(
R.string.power,
NodeDetailRoutes.PowerMetrics,
Icons.Default.Power,
{ metricsVM, _ -> PowerMetricsScreen(metricsVM) },
{ _, metricsVM, _ -> PowerMetricsScreen(metricsVM) },
),
HOST(
R.string.host,
NodeDetailRoutes.HostMetricsLog,
Icons.Default.Memory,
{ metricsVM, _ -> HostMetricsLogScreen(metricsVM) },
{ _, metricsVM, _ -> HostMetricsLogScreen(metricsVM) },
),
PAX(
R.string.pax,
NodeDetailRoutes.PaxMetrics,
Icons.Default.People,
{ metricsVM, _ -> PaxMetricsScreen(metricsVM) },
{ _, metricsVM, _ -> PaxMetricsScreen(metricsVM) },
),
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,16 @@ fun NavGraphBuilder.settingsGraph(navController: NavHostController, uiViewModel:
) { backStackEntry ->
val parentEntry =
remember(backStackEntry) { navController.getBackStackEntry(SettingsRoutes.SettingsGraph::class) }
SettingsScreen(uiViewModel = uiViewModel, viewModel = hiltViewModel(parentEntry)) {
SettingsScreen(
uiViewModel = uiViewModel,
viewModel = hiltViewModel(parentEntry),
onClickNodeChip = {
navController.navigate(NodesRoutes.NodeDetailGraph(it)) {
launchSingleTop = true
restoreState = true
}
},
) {
navController.navigate(it) { popUpTo(SettingsRoutes.Settings()) { inclusive = false } }
}
}
Expand Down
57 changes: 37 additions & 20 deletions app/src/main/java/com/geeksville/mesh/ui/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package com.geeksville.mesh.ui
import android.Manifest
import android.os.Build
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
Expand Down Expand Up @@ -87,6 +88,7 @@ import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.navigation.ConnectionsRoutes
import com.geeksville.mesh.navigation.ContactsRoutes
import com.geeksville.mesh.navigation.MapRoutes
import com.geeksville.mesh.navigation.NodeDetailRoutes
import com.geeksville.mesh.navigation.NodesRoutes
import com.geeksville.mesh.navigation.Route
import com.geeksville.mesh.navigation.SettingsRoutes
Expand Down Expand Up @@ -135,7 +137,6 @@ enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector,
NodesRoutes.Nodes::class,
MapRoutes.Map::class,
ConnectionsRoutes.Connections::class,
SettingsRoutes.Settings::class,
)
.any { this.hasRoute(it) }

Expand Down Expand Up @@ -349,26 +350,42 @@ fun MainScreen(
if (sharedContact != null) {
SharedContactDialog(contact = sharedContact, onDismiss = { sharedContact = null })
}
MainAppBar(
viewModel = uIViewModel,
navController = navController,
onAction = { action ->
when (action) {
is NodeMenuAction.MoreDetails -> {
navController.navigate(
NodesRoutes.NodeDetailGraph(action.node.num),
{
launchSingleTop = true
restoreState = true
},
)
}

is NodeMenuAction.Share -> sharedContact = action.node
else -> {}
}
},
)
fun NavDestination.hasGlobalAppBar(): Boolean =
// List of screens to exclude from having the global app bar
listOf(
ConnectionsRoutes.Connections::class,
ContactsRoutes.Contacts::class,
MapRoutes.Map::class,
NodeDetailRoutes.NodeMap::class,
NodesRoutes.Nodes::class,
NodesRoutes.NodeDetail::class,
SettingsRoutes.Settings::class,
)
.none { this.hasRoute(it) }

AnimatedVisibility(visible = currentDestination?.hasGlobalAppBar() ?: true) {
MainAppBar(
viewModel = uIViewModel,
navController = navController,
onAction = { action ->
when (action) {
is NodeMenuAction.MoreDetails -> {
navController.navigate(
NodesRoutes.NodeDetailGraph(action.node.num),
{
launchSingleTop = true
restoreState = true
},
)
}

is NodeMenuAction.Share -> sharedContact = action.node
else -> {}
}
},
)
}

NavHost(
navController = navController,
Expand Down
Loading