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 2afb93728..d8f0d9845 100644 --- a/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt @@ -1,6 +1,7 @@ package com.geeksville.mesh.model import android.app.Application +import android.content.SharedPreferences import android.net.Uri import androidx.annotation.StringRes import androidx.lifecycle.SavedStateHandle @@ -17,8 +18,10 @@ import com.geeksville.mesh.TelemetryProtos.Telemetry import com.geeksville.mesh.android.Logging import com.geeksville.mesh.database.MeshLogRepository import com.geeksville.mesh.database.entity.MeshLog +import com.geeksville.mesh.model.map.CustomTileSource import com.geeksville.mesh.repository.datastore.RadioConfigRepository import com.geeksville.mesh.ui.Route +import com.geeksville.mesh.ui.map.MAP_STYLE_ID import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -111,6 +114,7 @@ class MetricsViewModel @Inject constructor( private val dispatchers: CoroutineDispatchers, private val meshLogRepository: MeshLogRepository, private val radioConfigRepository: RadioConfigRepository, + private val preferences: SharedPreferences, ) : ViewModel(), Logging { private val destNum = savedStateHandle.toRoute().destNum @@ -119,6 +123,7 @@ class MetricsViewModel @Inject constructor( } fun getUser(nodeNum: Int) = radioConfigRepository.getUser(nodeNum) + val tileSource get() = CustomTileSource.getTileSource(preferences.getInt(MAP_STYLE_ID, 0)) fun deleteLog(uuid: String) = viewModelScope.launch(dispatchers.io) { meshLogRepository.deleteLog(uuid) 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 4341c5bd1..900724ddd 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -28,6 +28,7 @@ import com.geeksville.mesh.database.entity.QuickChatAction import com.geeksville.mesh.repository.datastore.RadioConfigRepository import com.geeksville.mesh.repository.radio.RadioInterfaceService import com.geeksville.mesh.service.MeshService +import com.geeksville.mesh.ui.map.MAP_STYLE_ID import com.geeksville.mesh.util.positionToMeter import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers @@ -43,10 +44,8 @@ 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 @@ -122,15 +121,6 @@ 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, @@ -259,11 +249,9 @@ class UIViewModel @Inject constructor( val nodesWithPosition get() = nodeDB.nodeDBbyNum.value.values.filter { it.validPosition != null } - private val _mapState = MutableStateFlow(MapState.Empty) - val mapState: StateFlow get() = _mapState - - fun updateMapCenterAndZoom(center: GeoPoint, zoom: Double) = - _mapState.update { it.copy(center = center, zoom = zoom) } + var mapStyleId: Int + get() = preferences.getInt(MAP_STYLE_ID, 0) + set(value) = preferences.edit { putInt(MAP_STYLE_ID, value) } fun getUser(userId: String?) = nodeDB.getUser(userId ?: DataPacket.ID_BROADCAST) diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/NodeMap.kt b/app/src/main/java/com/geeksville/mesh/ui/components/NodeMap.kt index 426eccd84..a196b30b6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/NodeMap.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/NodeMap.kt @@ -28,7 +28,7 @@ fun NodeMapScreen( val state by viewModel.state.collectAsStateWithLifecycle() val geoPoints = state.positionLogs.map { GeoPoint(it.latitudeI * DegD, it.longitudeI * DegD) } val cameraView = remember { BoundingBox.fromGeoPoints(geoPoints) } - val mapView = rememberMapViewWithLifecycle(cameraView) + val mapView = rememberMapViewWithLifecycle(cameraView, viewModel.tileSource) AndroidView( modifier = Modifier.fillMaxSize(), 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 6e7277bb0..70137449b 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 @@ -214,10 +214,6 @@ fun MapView( // UI Elements var cacheEstimate by remember { mutableStateOf("") } - // constants - val prefsName = "org.geeksville.osm.prefs" - val mapStyleId = "map_style_id" - var zoomLevelMin by remember { mutableDoubleStateOf(0.0) } var zoomLevelMax by remember { mutableDoubleStateOf(0.0) } @@ -225,20 +221,33 @@ fun MapView( var downloadRegionBoundingBox: BoundingBox? by remember { mutableStateOf(null) } var myLocationOverlay: MyLocationNewOverlay? by remember { mutableStateOf(null) } + var showDownloadButton: Boolean by remember { mutableStateOf(false) } + var showEditWaypointDialog by remember { mutableStateOf(null) } + var showCurrentCacheInfo by remember { mutableStateOf(false) } + val context = LocalContext.current val density = LocalDensity.current - val mPrefs = remember { context.getSharedPreferences(prefsName, Context.MODE_PRIVATE) } val haptic = LocalHapticFeedback.current fun performHapticFeedback() = haptic.performHapticFeedback(HapticFeedbackType.LongPress) val hasGps = remember { context.hasGps() } + fun loadOnlineTileSourceBase(): ITileSource { + val id = model.mapStyleId + debug("mapStyleId from prefs: $id") + return CustomTileSource.getTileSource(id).also { + zoomLevelMax = it.maximumZoomLevel.toDouble() + showDownloadButton = + if (it is OnlineTileSourceBase) it.tileSourcePolicy.acceptsBulkDownload() else false + } + } + val cameraView = remember { val geoPoints = model.nodesWithPosition.map { GeoPoint(it.latitude, it.longitude) } BoundingBox.fromGeoPoints(geoPoints) } - val map = rememberMapViewWithLifecycle(cameraView) + val map = rememberMapViewWithLifecycle(cameraView, loadOnlineTileSourceBase()) val nodeClusterer = remember { RadiusMarkerClusterer(context) } @@ -281,10 +290,6 @@ fun MapView( val nodes by model.nodeList.collectAsStateWithLifecycle() val waypoints by model.waypoints.collectAsStateWithLifecycle(emptyMap()) - var showDownloadButton: Boolean by remember { mutableStateOf(false) } - var showEditWaypointDialog by remember { mutableStateOf(null) } - var showCurrentCacheInfo by remember { mutableStateOf(false) } - val markerIcon = remember { AppCompatResources.getDrawable(context, R.drawable.ic_baseline_location_on_24) } @@ -451,16 +456,6 @@ fun MapView( UpdateMarkers(onNodesChanged(nodes), onWaypointChanged(waypoints.values), nodeClusterer) } - fun loadOnlineTileSourceBase(): ITileSource { - val id = mPrefs.getInt(mapStyleId, 0) - debug("mapStyleId from prefs: $id") - return CustomTileSource.getTileSource(id).also { - zoomLevelMax = it.maximumZoomLevel.toDouble() - showDownloadButton = - if (it is OnlineTileSourceBase) it.tileSourcePolicy.acceptsBulkDownload() else false - } - } - /** * Creates Box overlay showing what area can be downloaded */ @@ -536,10 +531,10 @@ fun MapView( val builder = MaterialAlertDialogBuilder(context) val mapStyles: Array = CustomTileSource.mTileSources.values.toTypedArray() - val mapStyleInt = mPrefs.getInt(mapStyleId, 0) + val mapStyleInt = model.mapStyleId builder.setSingleChoiceItems(mapStyles, mapStyleInt) { dialog, which -> debug("Set mapStyleId pref to $which") - mPrefs.edit().putInt(mapStyleId, which).apply() + model.mapStyleId = which dialog.dismiss() map.setTileSource(loadOnlineTileSourceBase()) } @@ -586,7 +581,6 @@ fun MapView( AndroidView( factory = { map.apply { - setTileSource(loadOnlineTileSourceBase()) setDestroyMode(false) // keeps map instance alive when in the background addMapListener(boxOverlayListener) }