Decouple MapView from UiViewModel (#3213)

This commit is contained in:
Phil Oliver 2025-09-26 16:34:36 -04:00 committed by GitHub
parent 3d94391bb1
commit af8e1daa5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 102 additions and 86 deletions

View file

@ -386,32 +386,6 @@ constructor(
initialValue = emptyList(),
)
fun generatePacketId(): Int? {
return try {
meshService?.packetId
} catch (ex: RemoteException) {
errormsg("RemoteException: ${ex.message}")
return null
}
}
fun sendWaypoint(wpt: MeshProtos.Waypoint, contactKey: String = "0${DataPacket.ID_BROADCAST}") {
// contactKey: unique contact key filter (channel)+(nodeId)
val channel = contactKey[0].digitToIntOrNull()
val dest = if (channel != null) contactKey.substring(1) else contactKey
val p = DataPacket(dest, channel ?: 0, wpt)
if (wpt.id != 0) sendDataPacket(p)
}
private fun sendDataPacket(p: DataPacket) {
try {
meshService?.send(p)
} catch (ex: RemoteException) {
errormsg("Send DataPacket error: ${ex.message}")
}
}
private val _sharedContactRequested: MutableStateFlow<AdminProtos.SharedContact?> = MutableStateFlow(null)
val sharedContactRequested: StateFlow<AdminProtos.SharedContact?>
get() = _sharedContactRequested.asStateFlow()
@ -426,8 +400,6 @@ constructor(
fun deleteContacts(contacts: List<String>) =
viewModelScope.launch(Dispatchers.IO) { packetRepository.deleteContacts(contacts) }
fun deleteWaypoint(id: Int) = viewModelScope.launch(Dispatchers.IO) { packetRepository.deleteWaypoint(id) }
// Connection state to our radio device
val connectionState
get() = serviceRepository.connectionState
@ -470,9 +442,6 @@ constructor(
val isManaged: Boolean
get() = config.device.isManaged || config.security.isManaged
val myNodeNum
get() = myNodeInfo.value?.myNodeNum
override fun onCleared() {
super.onCleared()
debug("ViewModel cleared")

View file

@ -21,16 +21,14 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.map.MapScreen
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.MapRoutes
import org.meshtastic.core.navigation.NodesRoutes
fun NavGraphBuilder.mapGraph(navController: NavHostController, uiViewModel: UIViewModel) {
fun NavGraphBuilder.mapGraph(navController: NavHostController) {
composable<MapRoutes.Map>(deepLinks = listOf(navDeepLink<MapRoutes.Map>(basePath = "$DEEP_LINK_BASE_URI/map"))) {
MapScreen(
uiViewModel = uiViewModel,
onClickNodeChip = {
navController.navigate(NodesRoutes.NodeDetailGraph(it)) {
launchSingleTop = true

View file

@ -412,7 +412,7 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode
) {
contactsGraph(navController, uiViewModel = uIViewModel)
nodesGraph(navController, uiViewModel = uIViewModel)
mapGraph(navController, uiViewModel = uIViewModel)
mapGraph(navController)
channelsGraph(navController, uiViewModel = uIViewModel)
connectionsGraph(navController)
settingsGraph(navController)

View file

@ -17,11 +17,14 @@
package com.geeksville.mesh.ui.map
import android.os.RemoteException
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.database.NodeRepository
import com.geeksville.mesh.database.PacketRepository
import com.geeksville.mesh.service.ServiceRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@ -29,18 +32,26 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.meshtastic.core.database.entity.Packet
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DataPacket
import org.meshtastic.core.prefs.map.MapPrefs
import timber.log.Timber
@Suppress("TooManyFunctions")
abstract class BaseMapViewModel(
protected val mapPrefs: MapPrefs,
nodeRepository: NodeRepository,
packetRepository: PacketRepository,
serviceRepository: ServiceRepository,
private val packetRepository: PacketRepository,
private val serviceRepository: ServiceRepository,
) : ViewModel() {
val myNodeInfo = nodeRepository.myNodeInfo
val myNodeNum
get() = myNodeInfo.value?.myNodeNum
val nodes: StateFlow<List<Node>> =
nodeRepository
.getNodes()
@ -94,6 +105,34 @@ abstract class BaseMapViewModel(
showPrecisionCircleOnMap.value = !current
}
fun generatePacketId(): Int? {
return try {
serviceRepository.meshService?.packetId
} catch (ex: RemoteException) {
Timber.e("RemoteException: ${ex.message}")
return null
}
}
fun deleteWaypoint(id: Int) = viewModelScope.launch(Dispatchers.IO) { packetRepository.deleteWaypoint(id) }
fun sendWaypoint(wpt: MeshProtos.Waypoint, contactKey: String = "0${DataPacket.ID_BROADCAST}") {
// contactKey: unique contact key filter (channel)+(nodeId)
val channel = contactKey[0].digitToIntOrNull()
val dest = if (channel != null) contactKey.substring(1) else contactKey
val p = DataPacket(dest, channel ?: 0, wpt)
if (wpt.id != 0) sendDataPacket(p)
}
private fun sendDataPacket(p: DataPacket) {
try {
serviceRepository.meshService?.send(p)
} catch (ex: RemoteException) {
Timber.e("Send DataPacket error: ${ex.message}")
}
}
data class MapFilterState(val onlyFavorites: Boolean, val showWaypoints: Boolean, val showPrecisionCircle: Boolean)
val mapFilterStateFlow: StateFlow<MapFilterState> =

View file

@ -26,7 +26,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.common.components.MainAppBar
import com.geeksville.mesh.ui.node.components.NodeMenuAction
import org.meshtastic.core.strings.R
@ -35,7 +34,6 @@ import org.meshtastic.core.strings.R
fun MapScreen(
onClickNodeChip: (Int) -> Unit,
navigateToNodeDetails: (Int) -> Unit,
uiViewModel: UIViewModel,
modifier: Modifier = Modifier,
mapViewModel: MapViewModel = hiltViewModel(),
) {
@ -64,11 +62,7 @@ fun MapScreen(
},
) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) {
MapView(
uiViewModel = uiViewModel,
mapViewModel = mapViewModel,
navigateToNodeDetails = navigateToNodeDetails,
)
MapView(mapViewModel = mapViewModel, navigateToNodeDetails = navigateToNodeDetails)
}
}
}