mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor: migrate preferences to DataStore and decouple core:domain for KMP (#4731)
This commit is contained in:
parent
87fdaa26ff
commit
b9b68d2779
113 changed files with 1790 additions and 1320 deletions
|
|
@ -26,7 +26,7 @@ import org.meshtastic.core.common.BuildConfigProvider
|
|||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.RadioController
|
||||
import org.meshtastic.core.navigation.MapRoutes
|
||||
import org.meshtastic.core.prefs.map.MapPrefs
|
||||
import org.meshtastic.core.repository.MapPrefs
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.core.repository.PacketRepository
|
||||
import org.meshtastic.core.repository.RadioConfigRepository
|
||||
|
|
@ -52,9 +52,9 @@ constructor(
|
|||
val selectedWaypointId: StateFlow<Int?> = _selectedWaypointId.asStateFlow()
|
||||
|
||||
var mapStyleId: Int
|
||||
get() = mapPrefs.mapStyle
|
||||
get() = mapPrefs.mapStyle.value
|
||||
set(value) {
|
||||
mapPrefs.mapStyle = value
|
||||
mapPrefs.setMapStyle(value)
|
||||
}
|
||||
|
||||
val localConfig = radioConfigRepository.localConfigFlow.stateInWhileSubscribed(initialValue = LocalConfig())
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ import org.meshtastic.core.datastore.UiPreferencesDataSource
|
|||
import org.meshtastic.core.model.RadioController
|
||||
import org.meshtastic.core.navigation.MapRoutes
|
||||
import org.meshtastic.core.prefs.map.GoogleMapsPrefs
|
||||
import org.meshtastic.core.prefs.map.MapPrefs
|
||||
import org.meshtastic.core.repository.MapPrefs
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.core.repository.PacketRepository
|
||||
import org.meshtastic.core.repository.RadioConfigRepository
|
||||
|
|
@ -96,9 +96,9 @@ constructor(
|
|||
val selectedWaypointId: StateFlow<Int?> = _selectedWaypointId.asStateFlow()
|
||||
|
||||
private val targetLatLng =
|
||||
googleMapsPrefs.cameraTargetLat
|
||||
googleMapsPrefs.cameraTargetLat.value
|
||||
.takeIf { it != 0.0 }
|
||||
?.let { lat -> googleMapsPrefs.cameraTargetLng.takeIf { it != 0.0 }?.let { lng -> LatLng(lat, lng) } }
|
||||
?.let { lat -> googleMapsPrefs.cameraTargetLng.value.takeIf { it != 0.0 }?.let { lng -> LatLng(lat, lng) } }
|
||||
?: ourNodeInfo.value?.position?.toLatLng()
|
||||
?: LatLng(0.0, 0.0)
|
||||
|
||||
|
|
@ -107,9 +107,9 @@ constructor(
|
|||
position =
|
||||
CameraPosition(
|
||||
targetLatLng,
|
||||
googleMapsPrefs.cameraZoom,
|
||||
googleMapsPrefs.cameraTilt,
|
||||
googleMapsPrefs.cameraBearing,
|
||||
googleMapsPrefs.cameraZoom.value,
|
||||
googleMapsPrefs.cameraTilt.value,
|
||||
googleMapsPrefs.cameraBearing.value,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -222,7 +222,7 @@ constructor(
|
|||
) {
|
||||
_selectedCustomTileProviderUrl.value = null
|
||||
// Also clear from prefs
|
||||
googleMapsPrefs.selectedCustomTileUrl = null
|
||||
googleMapsPrefs.setSelectedCustomTileUrl(null)
|
||||
}
|
||||
|
||||
if (configToRemove.localUri != null) {
|
||||
|
|
@ -238,28 +238,28 @@ constructor(
|
|||
if (!config.isLocal && !isValidTileUrlTemplate(config.urlTemplate)) {
|
||||
Logger.withTag("MapViewModel").w("Attempted to select invalid URL template: ${config.urlTemplate}")
|
||||
_selectedCustomTileProviderUrl.value = null
|
||||
googleMapsPrefs.selectedCustomTileUrl = null
|
||||
googleMapsPrefs.setSelectedCustomTileUrl(null)
|
||||
return
|
||||
}
|
||||
// Use localUri if present, otherwise urlTemplate
|
||||
val selectedUrl = config.localUri ?: config.urlTemplate
|
||||
_selectedCustomTileProviderUrl.value = selectedUrl
|
||||
_selectedGoogleMapType.value = MapType.NONE
|
||||
googleMapsPrefs.selectedCustomTileUrl = selectedUrl
|
||||
googleMapsPrefs.selectedGoogleMapType = null
|
||||
googleMapsPrefs.setSelectedCustomTileUrl(selectedUrl)
|
||||
googleMapsPrefs.setSelectedGoogleMapType(null)
|
||||
} else {
|
||||
_selectedCustomTileProviderUrl.value = null
|
||||
_selectedGoogleMapType.value = MapType.NORMAL
|
||||
googleMapsPrefs.selectedCustomTileUrl = null
|
||||
googleMapsPrefs.selectedGoogleMapType = MapType.NORMAL.name
|
||||
googleMapsPrefs.setSelectedCustomTileUrl(null)
|
||||
googleMapsPrefs.setSelectedGoogleMapType(MapType.NORMAL.name)
|
||||
}
|
||||
}
|
||||
|
||||
fun setSelectedGoogleMapType(mapType: MapType) {
|
||||
_selectedGoogleMapType.value = mapType
|
||||
_selectedCustomTileProviderUrl.value = null // Clear custom selection
|
||||
googleMapsPrefs.selectedGoogleMapType = mapType.name
|
||||
googleMapsPrefs.selectedCustomTileUrl = null
|
||||
googleMapsPrefs.setSelectedGoogleMapType(mapType.name)
|
||||
googleMapsPrefs.setSelectedCustomTileUrl(null)
|
||||
}
|
||||
|
||||
private var currentTileProvider: TileProvider? = null
|
||||
|
|
@ -354,16 +354,16 @@ constructor(
|
|||
|
||||
fun saveCameraPosition(cameraPosition: CameraPosition) {
|
||||
viewModelScope.launch {
|
||||
googleMapsPrefs.cameraTargetLat = cameraPosition.target.latitude
|
||||
googleMapsPrefs.cameraTargetLng = cameraPosition.target.longitude
|
||||
googleMapsPrefs.cameraZoom = cameraPosition.zoom
|
||||
googleMapsPrefs.cameraTilt = cameraPosition.tilt
|
||||
googleMapsPrefs.cameraBearing = cameraPosition.bearing
|
||||
googleMapsPrefs.setCameraTargetLat(cameraPosition.target.latitude)
|
||||
googleMapsPrefs.setCameraTargetLng(cameraPosition.target.longitude)
|
||||
googleMapsPrefs.setCameraZoom(cameraPosition.zoom)
|
||||
googleMapsPrefs.setCameraTilt(cameraPosition.tilt)
|
||||
googleMapsPrefs.setCameraBearing(cameraPosition.bearing)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadPersistedMapType() {
|
||||
val savedCustomUrl = googleMapsPrefs.selectedCustomTileUrl
|
||||
val savedCustomUrl = googleMapsPrefs.selectedCustomTileUrl.value
|
||||
if (savedCustomUrl != null) {
|
||||
// Check if this custom provider still exists
|
||||
if (
|
||||
|
|
@ -375,18 +375,18 @@ constructor(
|
|||
MapType.NONE // MapType.NONE to hide google basemap when using custom provider
|
||||
} else {
|
||||
// The saved custom URL is no longer valid or doesn't exist, remove preference
|
||||
googleMapsPrefs.selectedCustomTileUrl = null
|
||||
googleMapsPrefs.setSelectedCustomTileUrl(null)
|
||||
// Fallback to default Google Map type
|
||||
_selectedGoogleMapType.value = MapType.NORMAL
|
||||
}
|
||||
} else {
|
||||
val savedGoogleMapTypeName = googleMapsPrefs.selectedGoogleMapType
|
||||
val savedGoogleMapTypeName = googleMapsPrefs.selectedGoogleMapType.value
|
||||
try {
|
||||
_selectedGoogleMapType.value = MapType.valueOf(savedGoogleMapTypeName ?: MapType.NORMAL.name)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Logger.e(e) { "Invalid saved Google Map type: $savedGoogleMapTypeName" }
|
||||
_selectedGoogleMapType.value = MapType.NORMAL // Fallback in case of invalid stored name
|
||||
googleMapsPrefs.selectedGoogleMapType = null
|
||||
googleMapsPrefs.setSelectedGoogleMapType(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -399,7 +399,7 @@ constructor(
|
|||
val persistedLayerFiles = layersDir.listFiles()
|
||||
|
||||
if (persistedLayerFiles != null) {
|
||||
val hiddenLayerUrls = googleMapsPrefs.hiddenLayerUrls
|
||||
val hiddenLayerUrls = googleMapsPrefs.hiddenLayerUrls.value
|
||||
val loadedItems =
|
||||
persistedLayerFiles.mapNotNull { file ->
|
||||
if (file.isFile) {
|
||||
|
|
@ -429,7 +429,7 @@ constructor(
|
|||
}
|
||||
|
||||
val networkItems =
|
||||
googleMapsPrefs.networkMapLayers.mapNotNull { networkString ->
|
||||
googleMapsPrefs.networkMapLayers.value.mapNotNull { networkString ->
|
||||
try {
|
||||
val parts = networkString.split("|:|")
|
||||
if (parts.size == 3) {
|
||||
|
|
@ -532,7 +532,7 @@ constructor(
|
|||
_mapLayers.value = _mapLayers.value + newItem
|
||||
|
||||
val networkLayerString = "${newItem.id}|:|${newItem.name}|:|${newItem.uri}"
|
||||
googleMapsPrefs.networkMapLayers = googleMapsPrefs.networkMapLayers + networkLayerString
|
||||
googleMapsPrefs.setNetworkMapLayers(googleMapsPrefs.networkMapLayers.value + networkLayerString)
|
||||
} catch (e: Exception) {
|
||||
_errorFlow.emit("Invalid URL.")
|
||||
}
|
||||
|
|
@ -572,9 +572,9 @@ constructor(
|
|||
|
||||
toggledLayer?.let {
|
||||
if (it.isVisible) {
|
||||
googleMapsPrefs.hiddenLayerUrls -= it.uri.toString()
|
||||
googleMapsPrefs.setHiddenLayerUrls(googleMapsPrefs.hiddenLayerUrls.value - it.uri.toString())
|
||||
} else {
|
||||
googleMapsPrefs.hiddenLayerUrls += it.uri.toString()
|
||||
googleMapsPrefs.setHiddenLayerUrls(googleMapsPrefs.hiddenLayerUrls.value + it.uri.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -584,12 +584,13 @@ constructor(
|
|||
val layerToRemove = _mapLayers.value.find { it.id == layerId }
|
||||
layerToRemove?.uri?.let { uri ->
|
||||
if (layerToRemove.isNetwork) {
|
||||
googleMapsPrefs.networkMapLayers =
|
||||
googleMapsPrefs.networkMapLayers.filterNot { it.startsWith("$layerId|:|") }.toSet()
|
||||
googleMapsPrefs.setNetworkMapLayers(
|
||||
googleMapsPrefs.networkMapLayers.value.filterNot { it.startsWith("$layerId|:|") }.toSet(),
|
||||
)
|
||||
} else {
|
||||
deleteFileToInternalStorage(uri)
|
||||
}
|
||||
googleMapsPrefs.hiddenLayerUrls -= uri.toString()
|
||||
googleMapsPrefs.setHiddenLayerUrls(googleMapsPrefs.hiddenLayerUrls.value - uri.toString())
|
||||
}
|
||||
_mapLayers.value = _mapLayers.value.filterNot { it.id == layerId }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import org.meshtastic.core.common.util.nowSeconds
|
|||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.Node
|
||||
import org.meshtastic.core.model.RadioController
|
||||
import org.meshtastic.core.prefs.map.MapPrefs
|
||||
import org.meshtastic.core.repository.MapPrefs
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.core.repository.PacketRepository
|
||||
import org.meshtastic.core.resources.Res
|
||||
|
|
@ -90,47 +90,48 @@ abstract class BaseMapViewModel(
|
|||
}
|
||||
.stateInWhileSubscribed(initialValue = emptyMap())
|
||||
|
||||
private val showOnlyFavorites = MutableStateFlow(mapPrefs.showOnlyFavorites)
|
||||
private val showOnlyFavorites = MutableStateFlow(mapPrefs.showOnlyFavorites.value)
|
||||
val showOnlyFavoritesOnMap = showOnlyFavorites
|
||||
|
||||
fun toggleOnlyFavorites() {
|
||||
val newValue = !showOnlyFavorites.value
|
||||
showOnlyFavorites.value = newValue
|
||||
mapPrefs.showOnlyFavorites = newValue
|
||||
mapPrefs.setShowOnlyFavorites(newValue)
|
||||
}
|
||||
|
||||
private val showWaypoints = MutableStateFlow(mapPrefs.showWaypointsOnMap)
|
||||
private val showWaypoints = MutableStateFlow(mapPrefs.showWaypointsOnMap.value)
|
||||
val showWaypointsOnMap = showWaypoints
|
||||
|
||||
fun toggleShowWaypointsOnMap() {
|
||||
val newValue = !showWaypoints.value
|
||||
showWaypoints.value = newValue
|
||||
mapPrefs.showWaypointsOnMap = newValue
|
||||
mapPrefs.setShowWaypointsOnMap(newValue)
|
||||
}
|
||||
|
||||
private val showPrecisionCircle = MutableStateFlow(mapPrefs.showPrecisionCircleOnMap)
|
||||
private val showPrecisionCircle = MutableStateFlow(mapPrefs.showPrecisionCircleOnMap.value)
|
||||
val showPrecisionCircleOnMap = showPrecisionCircle
|
||||
|
||||
fun toggleShowPrecisionCircleOnMap() {
|
||||
val newValue = !showPrecisionCircle.value
|
||||
showPrecisionCircle.value = newValue
|
||||
mapPrefs.showPrecisionCircleOnMap = newValue
|
||||
mapPrefs.setShowPrecisionCircleOnMap(newValue)
|
||||
}
|
||||
|
||||
private val lastHeardFilterValue = MutableStateFlow(LastHeardFilter.fromSeconds(mapPrefs.lastHeardFilter))
|
||||
private val lastHeardFilterValue = MutableStateFlow(LastHeardFilter.fromSeconds(mapPrefs.lastHeardFilter.value))
|
||||
val lastHeardFilter = lastHeardFilterValue
|
||||
|
||||
fun setLastHeardFilter(filter: LastHeardFilter) {
|
||||
lastHeardFilterValue.value = filter
|
||||
mapPrefs.lastHeardFilter = filter.seconds
|
||||
mapPrefs.setLastHeardFilter(filter.seconds)
|
||||
}
|
||||
|
||||
private val lastHeardTrackFilterValue = MutableStateFlow(LastHeardFilter.fromSeconds(mapPrefs.lastHeardTrackFilter))
|
||||
private val lastHeardTrackFilterValue =
|
||||
MutableStateFlow(LastHeardFilter.fromSeconds(mapPrefs.lastHeardTrackFilter.value))
|
||||
val lastHeardTrackFilter = lastHeardTrackFilterValue
|
||||
|
||||
fun setLastHeardTrackFilter(filter: LastHeardFilter) {
|
||||
lastHeardTrackFilterValue.value = filter
|
||||
mapPrefs.lastHeardTrackFilter = filter.seconds
|
||||
mapPrefs.setLastHeardTrackFilter(filter.seconds)
|
||||
}
|
||||
|
||||
abstract fun getUser(userId: String?): org.meshtastic.proto.User
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ import kotlinx.coroutines.flow.map
|
|||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import org.meshtastic.core.common.BuildConfigProvider
|
||||
import org.meshtastic.core.data.repository.MeshLogRepository
|
||||
import org.meshtastic.core.database.entity.MeshLog
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.prefs.map.MapPrefs
|
||||
import org.meshtastic.core.repository.MapPrefs
|
||||
import org.meshtastic.core.repository.MeshLogRepository
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.core.ui.util.toPosition
|
||||
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
|
|
@ -81,5 +81,5 @@ constructor(
|
|||
.stateInWhileSubscribed(initialValue = emptyList())
|
||||
|
||||
val tileSource
|
||||
get() = CustomTileSource.getTileSource(mapPrefs.mapStyle)
|
||||
get() = CustomTileSource.getTileSource(mapPrefs.mapStyle.value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import org.meshtastic.core.datastore.UiPreferencesDataSource
|
|||
import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.model.RadioController
|
||||
import org.meshtastic.core.prefs.map.GoogleMapsPrefs
|
||||
import org.meshtastic.core.prefs.map.MapPrefs
|
||||
import org.meshtastic.core.repository.MapPrefs
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.core.repository.PacketRepository
|
||||
import org.meshtastic.core.repository.RadioConfigRepository
|
||||
|
|
@ -72,6 +72,22 @@ class MapViewModelTest {
|
|||
@Before
|
||||
fun setup() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
every { mapPrefs.mapStyle } returns MutableStateFlow(0)
|
||||
every { mapPrefs.showOnlyFavorites } returns MutableStateFlow(false)
|
||||
every { mapPrefs.showWaypointsOnMap } returns MutableStateFlow(true)
|
||||
every { mapPrefs.showPrecisionCircleOnMap } returns MutableStateFlow(true)
|
||||
every { mapPrefs.lastHeardFilter } returns MutableStateFlow(0L)
|
||||
every { mapPrefs.lastHeardTrackFilter } returns MutableStateFlow(0L)
|
||||
|
||||
every { googleMapsPrefs.cameraTargetLat } returns MutableStateFlow(0.0)
|
||||
every { googleMapsPrefs.cameraTargetLng } returns MutableStateFlow(0.0)
|
||||
every { googleMapsPrefs.cameraZoom } returns MutableStateFlow(0f)
|
||||
every { googleMapsPrefs.cameraTilt } returns MutableStateFlow(0f)
|
||||
every { googleMapsPrefs.cameraBearing } returns MutableStateFlow(0f)
|
||||
every { googleMapsPrefs.selectedCustomTileUrl } returns MutableStateFlow(null)
|
||||
every { googleMapsPrefs.selectedGoogleMapType } returns MutableStateFlow(null)
|
||||
every { googleMapsPrefs.hiddenLayerUrls } returns MutableStateFlow(emptySet())
|
||||
|
||||
every { customTileProviderRepository.getCustomTileProviders() } returns flowOf(emptyList())
|
||||
every { radioConfigRepository.deviceProfileFlow } returns flowOf(mockk(relaxed = true))
|
||||
every { uiPreferencesDataSource.theme } returns MutableStateFlow(1)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue