From 8e5accd51898838108a79e17d526b8b60cae6636 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Mon, 17 Mar 2025 11:40:08 -0500 Subject: [PATCH] fix #1484: Filter out ignored nodes from map (#1676) * fix #1484: Filter out ignored nodes from map - Created a new `filteredNodeList` `StateFlow` in `UIState.kt` that filters out nodes marked as ignored. - Updated `MapFragment.kt` to use `filteredNodeList` instead of `nodeList`, ensuring that ignored nodes are not displayed on the map. * Refactor: Remove ExperimentalCoroutinesApi opt-in and add it to compiler args - Removes the `@OptIn(ExperimentalCoroutinesApi::class)` annotation from multiple files. - Adds `-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi` to the `freeCompilerArgs` in the `build.gradle` file, enabling the use of Experimental Coroutines API project-wide without the need for per-file opt-ins. - The coroutine api is now applied globally. Signed-off-by: James Rich * detekt Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> --------- Signed-off-by: James Rich Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> --- app/build.gradle | 5 ++++- .../geeksville/mesh/database/MeshLogRepository.kt | 5 +---- .../com/geeksville/mesh/database/NodeRepository.kt | 3 --- .../com/geeksville/mesh/model/MetricsViewModel.kt | 2 -- .../main/java/com/geeksville/mesh/model/UIState.kt | 14 ++++++++++---- .../java/com/geeksville/mesh/ui/map/MapFragment.kt | 2 +- .../mesh/ui/radioconfig/RadioConfigViewModel.kt | 2 -- 7 files changed, 16 insertions(+), 17 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8f23a8fde..2deb6181a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -96,7 +96,10 @@ android { } kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() - freeCompilerArgs += ['-opt-in=kotlin.RequiresOptIn'] + freeCompilerArgs += [ + '-opt-in=kotlin.RequiresOptIn', + '-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi' + ] } lint { abortOnError false diff --git a/app/src/main/java/com/geeksville/mesh/database/MeshLogRepository.kt b/app/src/main/java/com/geeksville/mesh/database/MeshLogRepository.kt index 6c7964048..71d32892d 100644 --- a/app/src/main/java/com/geeksville/mesh/database/MeshLogRepository.kt +++ b/app/src/main/java/com/geeksville/mesh/database/MeshLogRepository.kt @@ -18,12 +18,11 @@ package com.geeksville.mesh.database import com.geeksville.mesh.CoroutineDispatchers -import com.geeksville.mesh.Portnums import com.geeksville.mesh.MeshProtos.MeshPacket +import com.geeksville.mesh.Portnums import com.geeksville.mesh.TelemetryProtos.Telemetry import com.geeksville.mesh.database.dao.MeshLogDao import com.geeksville.mesh.database.entity.MeshLog -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.distinctUntilChanged @@ -54,7 +53,6 @@ class MeshLogRepository @Inject constructor( .toBuilder().setTime((log.received_date / MILLIS_TO_SECONDS).toInt()).build() }.getOrNull() - @OptIn(ExperimentalCoroutinesApi::class) fun getTelemetryFrom(nodeNum: Int): Flow> = meshLogDao.getLogsFrom(nodeNum, Portnums.PortNum.TELEMETRY_APP_VALUE, MAX_MESH_PACKETS) .distinctUntilChanged() @@ -73,7 +71,6 @@ class MeshLogRepository @Inject constructor( * Retrieves MeshPackets matching 'nodeNum' and 'portNum'. * If 'portNum' is not specified, returns all MeshPackets. Otherwise, filters by 'portNum'. */ - @OptIn(ExperimentalCoroutinesApi::class) fun getMeshPacketsFrom( nodeNum: Int, portNum: Int = Portnums.PortNum.UNKNOWN_APP_VALUE, diff --git a/app/src/main/java/com/geeksville/mesh/database/NodeRepository.kt b/app/src/main/java/com/geeksville/mesh/database/NodeRepository.kt index eb8e2765f..ff3513594 100644 --- a/app/src/main/java/com/geeksville/mesh/database/NodeRepository.kt +++ b/app/src/main/java/com/geeksville/mesh/database/NodeRepository.kt @@ -28,7 +28,6 @@ import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.model.Node import com.geeksville.mesh.model.NodeSortOption -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -65,7 +64,6 @@ class NodeRepository @Inject constructor( .map { map -> map.mapValues { (_, it) -> it.toEntity() } } // A map from nodeNum to Node - @OptIn(ExperimentalCoroutinesApi::class) val nodeDBbyNum: StateFlow> = nodeInfoDao.nodeDBbyNum() .mapLatest { map -> map.mapValues { (_, it) -> it.toModel() } } .onEach { @@ -94,7 +92,6 @@ class NodeRepository @Inject constructor( .setHwModel(MeshProtos.HardwareModel.UNSET) .build() - @OptIn(ExperimentalCoroutinesApi::class) fun getNodes( sort: NodeSortOption = NodeSortOption.LAST_HEARD, filter: String = "", diff --git a/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt b/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt index e4c2c7319..ea46bb2a0 100644 --- a/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt @@ -43,7 +43,6 @@ import com.geeksville.mesh.navigation.Route import com.geeksville.mesh.repository.datastore.RadioConfigRepository import com.geeksville.mesh.ui.map.MAP_STYLE_ID import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asFlow @@ -224,7 +223,6 @@ class MetricsViewModel @Inject constructor( private var deviceHardwareList: List = listOf() init { - @OptIn(ExperimentalCoroutinesApi::class) radioConfigRepository.nodeDBbyNum .mapLatest { nodes -> nodes[destNum] } .distinctUntilChanged() diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index b5767d366..857eedad0 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -63,7 +63,6 @@ import com.geeksville.mesh.util.getShortDate import com.geeksville.mesh.util.positionToMeter import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -235,7 +234,6 @@ class UIViewModel @Inject constructor( initialValue = NodesUiState.Empty, ) - @OptIn(ExperimentalCoroutinesApi::class) val nodeList: StateFlow> = nodesUiState.flatMapLatest { state -> nodeDB.getNodes(state.sort, state.filter, state.includeUnknown) }.stateIn( @@ -244,6 +242,16 @@ class UIViewModel @Inject constructor( initialValue = emptyList(), ) + val filteredNodeList: StateFlow> = nodeList.mapLatest { list -> + list.filter { node -> + !node.isIgnored + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = emptyList(), + ) + // hardware info about our local device (can be null) val myNodeInfo: StateFlow get() = nodeDB.myNodeInfo val ourNodeInfo: StateFlow get() = nodeDB.ourNodeInfo @@ -328,11 +336,9 @@ class UIViewModel @Inject constructor( initialValue = emptyList(), ) - @OptIn(ExperimentalCoroutinesApi::class) fun getMessagesFrom(contactKey: String) = packetRepository.getMessagesFrom(contactKey) .mapLatest { list -> list.map { it.toMessage(::getNode) } } - @OptIn(ExperimentalCoroutinesApi::class) val waypoints = packetRepository.getWaypoints().mapLatest { list -> list.associateBy { packet -> packet.data.waypoint!!.id } .filterValues { it.data.waypoint!!.expire > System.currentTimeMillis() / 1000 } diff --git a/app/src/main/java/com/geeksville/mesh/ui/map/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/map/MapFragment.kt index 3cbd08b9a..e339c2448 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/map/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/map/MapFragment.kt @@ -304,7 +304,7 @@ fun MapView( if (permissions.entries.all { it.value }) map.toggleMyLocation() } - val nodes by model.nodeList.collectAsStateWithLifecycle() + val nodes by model.filteredNodeList.collectAsStateWithLifecycle() val waypoints by model.waypoints.collectAsStateWithLifecycle(emptyMap()) val markerIcon = remember { diff --git a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/RadioConfigViewModel.kt b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/RadioConfigViewModel.kt index 1d9a24b7e..4bd7be2a0 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/radioconfig/RadioConfigViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/radioconfig/RadioConfigViewModel.kt @@ -51,7 +51,6 @@ import com.geeksville.mesh.util.UiText import com.google.protobuf.MessageLite import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -103,7 +102,6 @@ class RadioConfigViewModel @Inject constructor( val currentDeviceProfile get() = _currentDeviceProfile.value init { - @OptIn(ExperimentalCoroutinesApi::class) radioConfigRepository.nodeDBbyNum .mapLatest { nodes -> nodes[destNum] ?: nodes.values.firstOrNull() } .distinctUntilChanged()