feat: pass users preferred tile source to MapViews

pull/1409/head
andrekir 2024-11-15 07:10:01 -03:00
rodzic aeedd4de43
commit ee75ba3392
4 zmienionych plików z 27 dodań i 40 usunięć

Wyświetl plik

@ -1,6 +1,7 @@
package com.geeksville.mesh.model package com.geeksville.mesh.model
import android.app.Application import android.app.Application
import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
@ -17,8 +18,10 @@ import com.geeksville.mesh.TelemetryProtos.Telemetry
import com.geeksville.mesh.android.Logging import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.database.MeshLogRepository import com.geeksville.mesh.database.MeshLogRepository
import com.geeksville.mesh.database.entity.MeshLog 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.repository.datastore.RadioConfigRepository
import com.geeksville.mesh.ui.Route import com.geeksville.mesh.ui.Route
import com.geeksville.mesh.ui.map.MAP_STYLE_ID
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -111,6 +114,7 @@ class MetricsViewModel @Inject constructor(
private val dispatchers: CoroutineDispatchers, private val dispatchers: CoroutineDispatchers,
private val meshLogRepository: MeshLogRepository, private val meshLogRepository: MeshLogRepository,
private val radioConfigRepository: RadioConfigRepository, private val radioConfigRepository: RadioConfigRepository,
private val preferences: SharedPreferences,
) : ViewModel(), Logging { ) : ViewModel(), Logging {
private val destNum = savedStateHandle.toRoute<Route.NodeDetail>().destNum private val destNum = savedStateHandle.toRoute<Route.NodeDetail>().destNum
@ -119,6 +123,7 @@ class MetricsViewModel @Inject constructor(
} }
fun getUser(nodeNum: Int) = radioConfigRepository.getUser(nodeNum) 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) { fun deleteLog(uuid: String) = viewModelScope.launch(dispatchers.io) {
meshLogRepository.deleteLog(uuid) meshLogRepository.deleteLog(uuid)

Wyświetl plik

@ -28,6 +28,7 @@ import com.geeksville.mesh.database.entity.QuickChatAction
import com.geeksville.mesh.repository.datastore.RadioConfigRepository import com.geeksville.mesh.repository.datastore.RadioConfigRepository
import com.geeksville.mesh.repository.radio.RadioInterfaceService import com.geeksville.mesh.repository.radio.RadioInterfaceService
import com.geeksville.mesh.service.MeshService import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.ui.map.MAP_STYLE_ID
import com.geeksville.mesh.util.positionToMeter import com.geeksville.mesh.util.positionToMeter
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -43,10 +44,8 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.osmdroid.util.GeoPoint
import java.io.BufferedWriter import java.io.BufferedWriter
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.FileWriter 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( data class Contact(
val contactKey: String, val contactKey: String,
val shortName: String, val shortName: String,
@ -259,11 +249,9 @@ class UIViewModel @Inject constructor(
val nodesWithPosition get() = nodeDB.nodeDBbyNum.value.values.filter { it.validPosition != null } val nodesWithPosition get() = nodeDB.nodeDBbyNum.value.values.filter { it.validPosition != null }
private val _mapState = MutableStateFlow(MapState.Empty) var mapStyleId: Int
val mapState: StateFlow<MapState> get() = _mapState get() = preferences.getInt(MAP_STYLE_ID, 0)
set(value) = preferences.edit { putInt(MAP_STYLE_ID, value) }
fun updateMapCenterAndZoom(center: GeoPoint, zoom: Double) =
_mapState.update { it.copy(center = center, zoom = zoom) }
fun getUser(userId: String?) = nodeDB.getUser(userId ?: DataPacket.ID_BROADCAST) fun getUser(userId: String?) = nodeDB.getUser(userId ?: DataPacket.ID_BROADCAST)

Wyświetl plik

@ -28,7 +28,7 @@ fun NodeMapScreen(
val state by viewModel.state.collectAsStateWithLifecycle() val state by viewModel.state.collectAsStateWithLifecycle()
val geoPoints = state.positionLogs.map { GeoPoint(it.latitudeI * DegD, it.longitudeI * DegD) } val geoPoints = state.positionLogs.map { GeoPoint(it.latitudeI * DegD, it.longitudeI * DegD) }
val cameraView = remember { BoundingBox.fromGeoPoints(geoPoints) } val cameraView = remember { BoundingBox.fromGeoPoints(geoPoints) }
val mapView = rememberMapViewWithLifecycle(cameraView) val mapView = rememberMapViewWithLifecycle(cameraView, viewModel.tileSource)
AndroidView( AndroidView(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),

Wyświetl plik

@ -214,10 +214,6 @@ fun MapView(
// UI Elements // UI Elements
var cacheEstimate by remember { mutableStateOf("") } 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 zoomLevelMin by remember { mutableDoubleStateOf(0.0) }
var zoomLevelMax 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 downloadRegionBoundingBox: BoundingBox? by remember { mutableStateOf(null) }
var myLocationOverlay: MyLocationNewOverlay? by remember { mutableStateOf(null) } var myLocationOverlay: MyLocationNewOverlay? by remember { mutableStateOf(null) }
var showDownloadButton: Boolean by remember { mutableStateOf(false) }
var showEditWaypointDialog by remember { mutableStateOf<Waypoint?>(null) }
var showCurrentCacheInfo by remember { mutableStateOf(false) }
val context = LocalContext.current val context = LocalContext.current
val density = LocalDensity.current val density = LocalDensity.current
val mPrefs = remember { context.getSharedPreferences(prefsName, Context.MODE_PRIVATE) }
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
fun performHapticFeedback() = haptic.performHapticFeedback(HapticFeedbackType.LongPress) fun performHapticFeedback() = haptic.performHapticFeedback(HapticFeedbackType.LongPress)
val hasGps = remember { context.hasGps() } 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 cameraView = remember {
val geoPoints = model.nodesWithPosition.map { GeoPoint(it.latitude, it.longitude) } val geoPoints = model.nodesWithPosition.map { GeoPoint(it.latitude, it.longitude) }
BoundingBox.fromGeoPoints(geoPoints) BoundingBox.fromGeoPoints(geoPoints)
} }
val map = rememberMapViewWithLifecycle(cameraView) val map = rememberMapViewWithLifecycle(cameraView, loadOnlineTileSourceBase())
val nodeClusterer = remember { RadiusMarkerClusterer(context) } val nodeClusterer = remember { RadiusMarkerClusterer(context) }
@ -281,10 +290,6 @@ fun MapView(
val nodes by model.nodeList.collectAsStateWithLifecycle() val nodes by model.nodeList.collectAsStateWithLifecycle()
val waypoints by model.waypoints.collectAsStateWithLifecycle(emptyMap()) val waypoints by model.waypoints.collectAsStateWithLifecycle(emptyMap())
var showDownloadButton: Boolean by remember { mutableStateOf(false) }
var showEditWaypointDialog by remember { mutableStateOf<Waypoint?>(null) }
var showCurrentCacheInfo by remember { mutableStateOf(false) }
val markerIcon = remember { val markerIcon = remember {
AppCompatResources.getDrawable(context, R.drawable.ic_baseline_location_on_24) AppCompatResources.getDrawable(context, R.drawable.ic_baseline_location_on_24)
} }
@ -451,16 +456,6 @@ fun MapView(
UpdateMarkers(onNodesChanged(nodes), onWaypointChanged(waypoints.values), nodeClusterer) 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 * Creates Box overlay showing what area can be downloaded
*/ */
@ -536,10 +531,10 @@ fun MapView(
val builder = MaterialAlertDialogBuilder(context) val builder = MaterialAlertDialogBuilder(context)
val mapStyles: Array<CharSequence> = CustomTileSource.mTileSources.values.toTypedArray() val mapStyles: Array<CharSequence> = CustomTileSource.mTileSources.values.toTypedArray()
val mapStyleInt = mPrefs.getInt(mapStyleId, 0) val mapStyleInt = model.mapStyleId
builder.setSingleChoiceItems(mapStyles, mapStyleInt) { dialog, which -> builder.setSingleChoiceItems(mapStyles, mapStyleInt) { dialog, which ->
debug("Set mapStyleId pref to $which") debug("Set mapStyleId pref to $which")
mPrefs.edit().putInt(mapStyleId, which).apply() model.mapStyleId = which
dialog.dismiss() dialog.dismiss()
map.setTileSource(loadOnlineTileSourceBase()) map.setTileSource(loadOnlineTileSourceBase())
} }
@ -586,7 +581,6 @@ fun MapView(
AndroidView( AndroidView(
factory = { factory = {
map.apply { map.apply {
setTileSource(loadOnlineTileSourceBase())
setDestroyMode(false) // keeps map instance alive when in the background setDestroyMode(false) // keeps map instance alive when in the background
addMapListener(boxOverlayListener) addMapListener(boxOverlayListener)
} }