feat(map): replace Google Maps + OSMDroid with unified MapLibre Compose Multiplatform

Replace the dual flavor-specific map implementations (Google Maps for google,
OSMDroid for fdroid) with a single MapLibre Compose Multiplatform implementation
in feature:map/commonMain, eliminating ~8,500 lines of duplicated code.

Key changes:
- Add maplibre-compose v0.12.1 dependency (KMP: Android, Desktop, iOS)
- Create unified MapViewModel with camera persistence via MapCameraPrefs
- Create MapScreen, MaplibreMapContent, NodeTrackLayers, TracerouteLayers,
  InlineMap, NodeTrackMap, TracerouteMap, NodeMapScreen in commonMain
- Create MapStyle enum with predefined OpenFreeMap tile styles
- Create GeoJsonConverters for Node/Waypoint/Position to GeoJSON
- Move TracerouteMapScreen from feature:node/androidMain to commonMain
- Wire navigation to use direct imports instead of CompositionLocal providers
- Delete 61 flavor-specific map files (google + fdroid source sets)
- Remove 8 CompositionLocal map providers from core:ui
- Remove SharedMapViewModel (replaced by new MapViewModel)
- Remove dead google-maps and osmdroid entries from version catalog
- Add MapViewModelTest with 10 test cases in commonTest

Baseline verified: spotlessCheck, detekt, assembleGoogleDebug, allTests all pass.
This commit is contained in:
James Rich 2026-04-12 18:25:15 -05:00
parent a2763bdfeb
commit 598cae564e
86 changed files with 1653 additions and 8333 deletions

View file

@ -23,6 +23,7 @@ import org.meshtastic.core.repository.AppPreferences
import org.meshtastic.core.repository.CustomEmojiPrefs
import org.meshtastic.core.repository.FilterPrefs
import org.meshtastic.core.repository.HomoglyphPrefs
import org.meshtastic.core.repository.MapCameraPrefs
import org.meshtastic.core.repository.MapConsentPrefs
import org.meshtastic.core.repository.MapPrefs
import org.meshtastic.core.repository.MapTileProviderPrefs
@ -198,6 +199,56 @@ class FakeMapPrefs : MapPrefs {
}
}
class FakeMapCameraPrefs : MapCameraPrefs {
override val cameraLat = MutableStateFlow(0.0)
override fun setCameraLat(value: Double) {
cameraLat.value = value
}
override val cameraLng = MutableStateFlow(0.0)
override fun setCameraLng(value: Double) {
cameraLng.value = value
}
override val cameraZoom = MutableStateFlow(7f)
override fun setCameraZoom(value: Float) {
cameraZoom.value = value
}
override val cameraTilt = MutableStateFlow(0f)
override fun setCameraTilt(value: Float) {
cameraTilt.value = value
}
override val cameraBearing = MutableStateFlow(0f)
override fun setCameraBearing(value: Float) {
cameraBearing.value = value
}
override val selectedStyleUri = MutableStateFlow("")
override fun setSelectedStyleUri(value: String) {
selectedStyleUri.value = value
}
override val hiddenLayerUrls = MutableStateFlow(emptySet<String>())
override fun setHiddenLayerUrls(value: Set<String>) {
hiddenLayerUrls.value = value
}
override val networkMapLayers = MutableStateFlow(emptySet<String>())
override fun setNetworkMapLayers(value: Set<String>) {
networkMapLayers.value = value
}
}
class FakeMapConsentPrefs : MapConsentPrefs {
private val consent = mutableMapOf<Int?, MutableStateFlow<Boolean>>()
@ -264,6 +315,7 @@ class FakeAppPreferences : AppPreferences {
override val emoji = FakeCustomEmojiPrefs()
override val ui = FakeUiPrefs()
override val map = FakeMapPrefs()
override val mapCamera = FakeMapCameraPrefs()
override val mapConsent = FakeMapConsentPrefs()
override val mapTileProvider = FakeMapTileProviderPrefs()
override val radio = FakeRadioPrefs()