diff --git a/app/src/main/java/com/geeksville/mesh/database/entity/NodeEntity.kt b/app/src/main/java/com/geeksville/mesh/database/entity/NodeEntity.kt index 56b6b127..1a11d890 100644 --- a/app/src/main/java/com/geeksville/mesh/database/entity/NodeEntity.kt +++ b/app/src/main/java/com/geeksville/mesh/database/entity/NodeEntity.kt @@ -4,6 +4,7 @@ import android.graphics.Color import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey +import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig import com.geeksville.mesh.DeviceMetrics import com.geeksville.mesh.EnvironmentMetrics import com.geeksville.mesh.MeshProtos @@ -13,7 +14,10 @@ import com.geeksville.mesh.PaxcountProtos import com.geeksville.mesh.Position import com.geeksville.mesh.TelemetryProtos import com.geeksville.mesh.copy +import com.geeksville.mesh.util.bearing +import com.geeksville.mesh.util.GPSFormat import com.geeksville.mesh.util.latLongToMeter +import com.geeksville.mesh.util.toDistanceString import com.google.protobuf.ByteString @Suppress("MagicNumber") @@ -108,6 +112,26 @@ data class NodeEntity( else latLongToMeter(latitude, longitude, o.latitude, o.longitude).toInt() } + // @return a nice human readable string for the distance, or null for unknown + fun distanceStr(o: NodeEntity, displayUnits: Int = 0): String? = distance(o)?.let { dist -> + val system = DisplayConfig.DisplayUnits.forNumber(displayUnits) + return if (dist > 0) dist.toDistanceString(system) else null + } + + // @return bearing to the other position in degrees + fun bearing(o: NodeEntity?): Int? { + return if (validPosition == null || o?.validPosition == null) null + else bearing(latitude, longitude, o.latitude, o.longitude).toInt() + } + + fun gpsString(gpsFormat: Int): String = when (gpsFormat) { + DisplayConfig.GpsCoordinateFormat.DEC_VALUE -> GPSFormat.toDEC(latitude, longitude) + DisplayConfig.GpsCoordinateFormat.DMS_VALUE -> GPSFormat.toDMS(latitude, longitude) + DisplayConfig.GpsCoordinateFormat.UTM_VALUE -> GPSFormat.toUTM(latitude, longitude) + DisplayConfig.GpsCoordinateFormat.MGRS_VALUE -> GPSFormat.toMGRS(latitude, longitude) + else -> GPSFormat.toDEC(latitude, longitude) + } + private fun TelemetryProtos.EnvironmentMetrics.getDisplayString(isFahrenheit: Boolean): String { val temp = if (temperature != 0f) { if (isFahrenheit) { 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 60c90ca3..b15f9103 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -186,8 +186,8 @@ class UIViewModel @Inject constructor( private val _quickChatActions = MutableStateFlow>(emptyList()) val quickChatActions: StateFlow> = _quickChatActions - private val _focusedNode = MutableStateFlow(null) - val focusedNode: StateFlow = _focusedNode + private val _focusedNode = MutableStateFlow(null) + val focusedNode: StateFlow = _focusedNode private val nodeFilterText = MutableStateFlow("") private val nodeSortOption = MutableStateFlow(NodeSortOption.LAST_HEARD) @@ -747,7 +747,7 @@ class UIViewModel @Inject constructor( _currentTab.value = tab } - fun focusUserNode(node: NodeInfo?) { + fun focusUserNode(node: NodeEntity?) { _currentTab.value = 1 _focusedNode.value = node } diff --git a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt index 326af2b3..c6e4d484 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt @@ -26,7 +26,6 @@ import com.geeksville.mesh.DataPacket import com.geeksville.mesh.android.Logging import com.geeksville.mesh.R import com.geeksville.mesh.database.entity.QuickChatAction -import com.geeksville.mesh.database.entity.toNodeInfo import com.geeksville.mesh.databinding.MessagesFragmentBinding import com.geeksville.mesh.model.Message import com.geeksville.mesh.model.UIViewModel @@ -297,7 +296,7 @@ class MessagesFragment : Fragment(), Logging { private fun openNodeInfo(msg: Message) = lifecycleScope.launch { model.nodeList.firstOrNull()?.find { it.user.id == msg.user.id }?.let { node -> parentFragmentManager.popBackStack() - model.focusUserNode(node.toNodeInfo()) + model.focusUserNode(node) } } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt index 5847bf75..db613894 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt @@ -26,7 +26,6 @@ import com.geeksville.mesh.DataPacket import com.geeksville.mesh.R import com.geeksville.mesh.android.Logging import com.geeksville.mesh.database.entity.NodeEntity -import com.geeksville.mesh.database.entity.toNodeInfo import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.ui.components.NodeFilterTextField import com.geeksville.mesh.ui.components.rememberTimeTickWithLifecycle @@ -175,7 +174,6 @@ fun NodesScreen( } items(nodes, key = { it.num }) { node -> - val nodeInfo = node.toNodeInfo() NodeItem( thisNode = ourNode, thatNode = node, @@ -187,7 +185,7 @@ fun NodesScreen( focusManager.clearFocus() chipClicked(node) }, - blinking = nodeInfo == focusedNode, + blinking = node == focusedNode, expanded = state.showDetails, currentTimeMillis = currentTimeMillis, ) 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 4514aef6..12b861e5 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 @@ -38,7 +38,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.geeksville.mesh.BuildConfig import com.geeksville.mesh.DataPacket import com.geeksville.mesh.MeshProtos.Waypoint -import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.R import com.geeksville.mesh.android.BuildUtils.debug import com.geeksville.mesh.android.Logging @@ -47,8 +46,8 @@ import com.geeksville.mesh.android.gpsDisabled import com.geeksville.mesh.android.hasGps import com.geeksville.mesh.android.hasLocationPermission import com.geeksville.mesh.copy +import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.database.entity.Packet -import com.geeksville.mesh.database.entity.toNodeInfo import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.model.map.CustomTileSource import com.geeksville.mesh.model.map.MarkerWithLabel @@ -61,8 +60,6 @@ import com.geeksville.mesh.util.zoomIn import com.geeksville.mesh.waypoint import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.mapLatest import org.osmdroid.bonuspack.utils.BonusPackHelper.getBitmapFromVectorDrawable import org.osmdroid.config.Configuration import org.osmdroid.events.DelayedMapListener @@ -330,10 +327,7 @@ fun MapView( if (permissions.entries.all { it.value }) map.toggleMyLocation() } - @OptIn(ExperimentalCoroutinesApi::class) - val nodes by model.nodeList - .mapLatest { list -> list.map { it.toNodeInfo() } } - .collectAsStateWithLifecycle(emptyList()) + val nodes by model.nodeList.collectAsStateWithLifecycle() val waypoints by model.waypoints.collectAsStateWithLifecycle(emptyMap()) var showDownloadButton: Boolean by remember { mutableStateOf(false) } @@ -344,21 +338,21 @@ fun MapView( AppCompatResources.getDrawable(context, R.drawable.ic_baseline_location_on_24) } - fun MapView.onNodesChanged(nodes: Collection): List { + fun MapView.onNodesChanged(nodes: Collection): List { val nodesWithPosition = nodes.filter { it.validPosition != null } - val ourNode = model.ourNodeInfo.value?.toNodeInfo() + val ourNode = model.ourNodeInfo.value val gpsFormat = model.config.display.gpsFormat.number val displayUnits = model.config.display.units.number return nodesWithPosition.map { node -> - val (p, u) = node.position!! to node.user!! - val nodePosition = GeoPoint(p.latitude, p.longitude) + val (p, u) = node.position to node.user + val nodePosition = GeoPoint(node.latitude, node.longitude) MarkerWithLabel( mapView = this, label = "${u.shortName} ${formatAgo(p.time)}" ).apply { id = u.id title = "${u.longName} ${node.batteryStr}" - snippet = p.gpsString(gpsFormat) + snippet = node.gpsString(gpsFormat) ourNode?.distanceStr(node, displayUnits)?.let { dist -> subDescription = context.getString(R.string.map_subDescription, ourNode.bearing(node), dist)