feat: preserve map center and zoom state in ViewModel

closes #1150
pull/1276/head
andrekir 2024-09-30 19:13:56 -03:00
rodzic bfa2eaef11
commit 3bd4132089
2 zmienionych plików z 46 dodań i 19 usunięć

Wyświetl plik

@ -24,7 +24,6 @@ import com.geeksville.mesh.database.QuickChatActionRepository
import com.geeksville.mesh.database.entity.NodeEntity
import com.geeksville.mesh.database.entity.Packet
import com.geeksville.mesh.database.entity.QuickChatAction
import com.geeksville.mesh.database.entity.toNodeInfo
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
import com.geeksville.mesh.repository.radio.RadioInterfaceService
import com.geeksville.mesh.service.MeshService
@ -43,8 +42,10 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.osmdroid.util.GeoPoint
import java.io.BufferedWriter
import java.io.FileNotFoundException
import java.io.FileWriter
@ -119,6 +120,15 @@ data class NodesUiState(
}
}
data class MapState(
val center: GeoPoint? = null,
val zoom: Double = 0.0,
) {
companion object {
val Empty = MapState()
}
}
data class Contact(
val contactKey: String,
val shortName: String,
@ -231,8 +241,13 @@ class UIViewModel @Inject constructor(
val myNodeInfo: StateFlow<MyNodeInfo?> get() = nodeDB.myNodeInfo
val ourNodeInfo: StateFlow<NodeEntity?> get() = nodeDB.ourNodeInfo
// FIXME only used in MapFragment
val initialNodes get() = nodeDB.nodeDBbyNum.value.values.map { it.toNodeInfo() }
val nodesWithPosition get() = nodeDB.nodeDBbyNum.value.values.filter { it.validPosition != null }
private val _mapState = MutableStateFlow(MapState.Empty)
val mapState: StateFlow<MapState> get() = _mapState
fun updateMapCenterAndZoom(center: GeoPoint, zoom: Double) =
_mapState.update { it.copy(center = center, zoom = zoom) }
fun getUser(userId: String?) = nodeDB.getUser(userId) ?: user {
id = userId.orEmpty()

Wyświetl plik

@ -64,6 +64,7 @@ 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
import org.osmdroid.events.MapEventsReceiver
import org.osmdroid.events.MapListener
import org.osmdroid.events.ScrollEvent
@ -238,6 +239,21 @@ private fun Context.purgeTileSource(onResult: (String) -> Unit) {
builder.show()
}
private const val INACTIVITY_DELAY_MILLIS = 500L
private fun MapView.addMapEventListener(onEvent: () -> Unit) {
addMapListener(DelayedMapListener(object : MapListener {
override fun onScroll(event: ScrollEvent): Boolean {
onEvent()
return true
}
override fun onZoom(event: ZoomEvent): Boolean {
onEvent()
return true
}
}, INACTIVITY_DELAY_MILLIS))
}
@Composable
fun MapView(
model: UIViewModel = viewModel(),
@ -265,6 +281,7 @@ fun MapView(
val hasGps = remember { context.hasGps() }
val map = rememberMapViewWithLifecycle(context)
val state by model.mapState.collectAsStateWithLifecycle()
fun MapView.toggleMyLocation() {
if (context.gpsDisabled()) {
@ -473,15 +490,17 @@ fun MapView(
}
fun MapView.zoomToNodes() {
val nodeMarkers = onNodesChanged(model.initialNodes)
if (nodeMarkers.isNotEmpty()) {
val box = BoundingBox.fromGeoPoints(nodeMarkers.map { it.position })
if (state.center == null) {
val geoPoints = model.nodesWithPosition.map { GeoPoint(it.latitude, it.longitude) }
val box = BoundingBox.fromGeoPoints(geoPoints)
val center = GeoPoint(box.centerLatitude, box.centerLongitude)
val maximumZoomLevel = tileProvider.tileSource.maximumZoomLevel.toDouble()
val finalZoomLevel = minOf(box.requiredZoomLevel() * 0.8, maximumZoomLevel)
val finalZoomLevel = minOf(box.requiredZoomLevel() * 0.8, maxZoomLevel)
controller.setCenter(center)
controller.setZoom(finalZoomLevel)
} else controller.zoomIn()
} else {
controller.setCenter(state.center)
controller.setZoom(state.zoom)
}
}
fun loadOnlineTileSourceBase(): ITileSource {
@ -624,16 +643,9 @@ fun MapView(
minZoomLevel = 1.5
// Disables default +/- button for zooming
zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER)
addMapListener(object : MapListener {
override fun onScroll(event: ScrollEvent): Boolean {
if (downloadRegionBoundingBox != null) generateBoxOverlay()
return true
}
override fun onZoom(event: ZoomEvent): Boolean {
return false
}
})
addMapEventListener {
model.updateMapCenterAndZoom(map.projection.currentCenter, map.zoomLevelDouble)
}
zoomToNodes()
}
},