2020-03-11 14:45:49 -07:00
|
|
|
package com.geeksville.mesh.ui
|
|
|
|
|
|
2022-04-22 12:28:04 +02:00
|
|
|
import android.content.Context
|
2022-08-24 11:08:39 -04:00
|
|
|
import android.content.SharedPreferences
|
2020-03-30 11:56:59 -07:00
|
|
|
import android.os.Bundle
|
2020-04-07 11:27:51 -07:00
|
|
|
import android.view.LayoutInflater
|
|
|
|
|
import android.view.View
|
|
|
|
|
import android.view.ViewGroup
|
2021-02-21 11:34:43 +08:00
|
|
|
import androidx.core.content.ContextCompat
|
2020-04-08 09:53:04 -07:00
|
|
|
import androidx.fragment.app.activityViewModels
|
2020-03-30 10:26:16 -07:00
|
|
|
import com.geeksville.android.Logging
|
2022-08-23 20:05:19 -04:00
|
|
|
import com.geeksville.mesh.BuildConfig
|
2020-04-08 09:53:04 -07:00
|
|
|
import com.geeksville.mesh.NodeInfo
|
2020-03-11 18:13:44 -07:00
|
|
|
import com.geeksville.mesh.R
|
2022-08-24 12:16:57 -04:00
|
|
|
import com.geeksville.mesh.databinding.MapViewBinding
|
2020-04-08 09:53:04 -07:00
|
|
|
import com.geeksville.mesh.model.UIViewModel
|
2021-03-16 00:05:28 +05:00
|
|
|
import com.geeksville.util.formatAgo
|
2022-08-24 12:16:57 -04:00
|
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
2022-02-08 13:50:21 -08:00
|
|
|
import dagger.hilt.android.AndroidEntryPoint
|
2022-08-23 20:05:19 -04:00
|
|
|
import org.osmdroid.api.IMapController
|
|
|
|
|
import org.osmdroid.config.Configuration
|
|
|
|
|
import org.osmdroid.tileprovider.tilesource.OnlineTileSourceBase
|
|
|
|
|
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
2022-08-23 20:27:14 -04:00
|
|
|
import org.osmdroid.util.BoundingBox
|
2022-08-23 20:05:19 -04:00
|
|
|
import org.osmdroid.util.GeoPoint
|
|
|
|
|
import org.osmdroid.views.CustomZoomButtonsController
|
|
|
|
|
import org.osmdroid.views.MapView
|
|
|
|
|
import org.osmdroid.views.overlay.CopyrightOverlay
|
2022-08-23 22:32:32 -04:00
|
|
|
import org.osmdroid.views.overlay.Marker
|
2020-03-30 12:47:01 -07:00
|
|
|
|
2022-02-08 13:50:21 -08:00
|
|
|
@AndroidEntryPoint
|
2020-04-07 11:27:51 -07:00
|
|
|
class MapFragment : ScreenFragment("Map"), Logging {
|
2020-04-07 12:13:50 -07:00
|
|
|
|
2022-08-24 12:16:57 -04:00
|
|
|
private lateinit var binding: MapViewBinding
|
2022-08-23 20:05:19 -04:00
|
|
|
private lateinit var map: MapView
|
|
|
|
|
private lateinit var mapController: IMapController
|
2022-08-24 11:08:39 -04:00
|
|
|
private lateinit var mPrefs: SharedPreferences
|
2020-04-08 09:53:04 -07:00
|
|
|
private val model: UIViewModel by activityViewModels()
|
|
|
|
|
|
2022-08-24 11:30:42 -04:00
|
|
|
private val defaultMinZoom = 3.0
|
2022-08-24 16:47:50 -04:00
|
|
|
|
|
|
|
|
private val nodeZoomLevel = 8.5
|
2022-08-23 20:05:19 -04:00
|
|
|
private val defaultZoomSpeed = 3000L
|
2022-08-24 11:08:39 -04:00
|
|
|
private val prefsName = "org.andnav.osm.prefs"
|
|
|
|
|
private val prefsZoomLevelDouble = "prefsZoomLevelDouble"
|
|
|
|
|
private val prefsTileSource = "prefsTileSource"
|
2022-08-24 11:30:42 -04:00
|
|
|
private val mapStyleId = "map_style_id"
|
|
|
|
|
private val mapTag = "mapView"
|
|
|
|
|
private val uiPrefs = "ui-prefs"
|
2022-02-23 19:47:30 -05:00
|
|
|
|
2020-04-07 12:13:50 -07:00
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
override fun onCreateView(
|
|
|
|
|
inflater: LayoutInflater, container: ViewGroup?,
|
|
|
|
|
savedInstanceState: Bundle?
|
|
|
|
|
): View {
|
2022-08-24 23:15:48 -04:00
|
|
|
binding = MapViewBinding.inflate(inflater)
|
2022-08-24 12:16:57 -04:00
|
|
|
return binding.root
|
2022-08-24 11:08:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onViewCreated(viewIn: View, savedInstanceState: Bundle?) {
|
|
|
|
|
super.onViewCreated(viewIn, savedInstanceState)
|
|
|
|
|
Configuration.getInstance().userAgentValue =
|
|
|
|
|
BuildConfig.APPLICATION_ID // Required to get online tiles
|
2022-08-24 23:15:48 -04:00
|
|
|
map = viewIn.findViewById(R.id.map)
|
2022-08-24 11:08:39 -04:00
|
|
|
mPrefs = context!!.getSharedPreferences(prefsName, Context.MODE_PRIVATE)
|
|
|
|
|
|
|
|
|
|
addCopyright() // Copyright is required for certain map sources
|
|
|
|
|
setupMapProperties()
|
|
|
|
|
loadOnlineTileSourceBase()
|
|
|
|
|
mapController = map.controller
|
2022-08-24 14:27:12 -04:00
|
|
|
map.let {
|
|
|
|
|
if (view != null) {
|
|
|
|
|
binding.fabStyleToggle.setOnClickListener {
|
|
|
|
|
chooseMapStyle()
|
|
|
|
|
}
|
|
|
|
|
model.nodeDB.nodes.value?.let { nodes ->
|
|
|
|
|
onNodesChanged(nodes.values)
|
|
|
|
|
}
|
2022-08-24 11:08:39 -04:00
|
|
|
}
|
2022-08-24 14:27:12 -04:00
|
|
|
}
|
2022-08-24 11:08:39 -04:00
|
|
|
|
2022-08-24 14:27:12 -04:00
|
|
|
// Any times nodes change update our map
|
|
|
|
|
model.nodeDB.nodes.observe(viewLifecycleOwner) { nodes ->
|
|
|
|
|
onNodesChanged(nodes.values)
|
2022-08-24 11:08:39 -04:00
|
|
|
}
|
2022-08-24 14:27:12 -04:00
|
|
|
zoomToNodes(mapController)
|
2022-08-23 20:05:19 -04:00
|
|
|
}
|
2022-02-16 13:56:17 -05:00
|
|
|
|
2022-08-24 12:16:57 -04:00
|
|
|
private fun chooseMapStyle() {
|
|
|
|
|
/// Prepare dialog and its items
|
|
|
|
|
val builder = MaterialAlertDialogBuilder(context!!)
|
|
|
|
|
builder.setTitle(getString(R.string.preferences_map_style))
|
|
|
|
|
val mapStyles by lazy { resources.getStringArray(R.array.map_styles) }
|
|
|
|
|
|
|
|
|
|
/// Load preferences and its value
|
|
|
|
|
val prefs = UIViewModel.getPreferences(context!!)
|
|
|
|
|
val editor: SharedPreferences.Editor = prefs.edit()
|
2022-08-24 13:38:06 -04:00
|
|
|
val mapStyleInt = prefs.getInt(mapStyleId, 1)
|
|
|
|
|
debug("mapStyleId from prefs: $mapStyleInt")
|
2022-08-24 12:16:57 -04:00
|
|
|
|
2022-08-24 13:38:06 -04:00
|
|
|
builder.setSingleChoiceItems(mapStyles, mapStyleInt) { dialog, which ->
|
2022-08-24 12:16:57 -04:00
|
|
|
debug("Set mapStyleId pref to $which")
|
2022-08-24 13:38:06 -04:00
|
|
|
editor.putInt(mapStyleId, which)
|
2022-08-24 12:16:57 -04:00
|
|
|
editor.apply()
|
|
|
|
|
dialog.dismiss()
|
|
|
|
|
}
|
|
|
|
|
val dialog = builder.create()
|
|
|
|
|
dialog.show()
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
private fun onNodesChanged(nodes: Collection<NodeInfo>) {
|
|
|
|
|
val nodesWithPosition = nodes.filter { it.validPosition != null }
|
2022-02-16 13:56:17 -05:00
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
/**
|
|
|
|
|
* Using the latest nodedb, generate GeoPoint
|
|
|
|
|
*/
|
|
|
|
|
// Find all nodes with valid locations
|
2020-04-07 12:13:50 -07:00
|
|
|
|
2022-08-23 22:32:32 -04:00
|
|
|
nodesWithPosition.map { node ->
|
2022-08-23 20:05:19 -04:00
|
|
|
val p = node.position!!
|
|
|
|
|
debug("Showing on map: $node")
|
|
|
|
|
val f = GeoPoint(p.latitude, p.longitude)
|
2022-02-16 10:22:59 -05:00
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
node.user?.let {
|
2022-08-23 22:32:32 -04:00
|
|
|
val marker = Marker(map)
|
|
|
|
|
marker.title = it.longName + " " + formatAgo(p.time)
|
|
|
|
|
marker.setAnchor(Marker.ANCHOR_BOTTOM, Marker.ANCHOR_CENTER)
|
|
|
|
|
marker.position = f
|
|
|
|
|
marker.icon = ContextCompat.getDrawable(
|
|
|
|
|
requireActivity(),
|
|
|
|
|
R.drawable.ic_twotone_person_pin_24
|
|
|
|
|
)
|
2022-08-24 11:08:39 -04:00
|
|
|
map.overlays.add(marker)
|
2022-08-23 20:05:19 -04:00
|
|
|
}
|
|
|
|
|
f
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-07 12:13:50 -07:00
|
|
|
|
2022-08-24 11:08:39 -04:00
|
|
|
/**
|
|
|
|
|
* Adds copyright to map depending on what source is showing
|
|
|
|
|
*/
|
|
|
|
|
private fun addCopyright() {
|
2022-08-23 20:05:19 -04:00
|
|
|
val copyrightNotice: String =
|
|
|
|
|
map.tileProvider.tileSource.copyrightNotice
|
|
|
|
|
val copyrightOverlay = CopyrightOverlay(context)
|
|
|
|
|
copyrightOverlay.setCopyrightNotice(copyrightNotice)
|
|
|
|
|
map.overlays.add(copyrightOverlay)
|
2021-02-06 22:08:49 -08:00
|
|
|
}
|
|
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
private fun setupMapProperties() {
|
|
|
|
|
if (this::map.isInitialized) {
|
2022-08-24 23:15:48 -04:00
|
|
|
map.setDestroyMode(false)
|
2022-08-24 11:08:39 -04:00
|
|
|
map.isTilesScaledToDpi =
|
2022-08-24 13:38:06 -04:00
|
|
|
true // scales the map tiles to the display density of the screen
|
2022-08-24 11:08:39 -04:00
|
|
|
map.minZoomLevel =
|
|
|
|
|
defaultMinZoom // sets the minimum zoom level (the furthest out you can zoom)
|
2022-08-23 20:05:19 -04:00
|
|
|
map.setMultiTouchControls(true) // Sets gesture controls to true
|
|
|
|
|
map.zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER) // Disables default +/- button for zooming
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun zoomToNodes(controller: IMapController) {
|
|
|
|
|
val points: MutableList<GeoPoint> = mutableListOf()
|
2021-03-16 10:04:01 +05:00
|
|
|
val nodesWithPosition =
|
|
|
|
|
model.nodeDB.nodes.value?.values?.filter { it.validPosition != null }
|
2022-08-23 20:05:19 -04:00
|
|
|
if ((nodesWithPosition != null) && nodesWithPosition.isNotEmpty()) {
|
2022-08-24 16:47:50 -04:00
|
|
|
if (nodesWithPosition.size >= 2) {
|
2022-02-05 22:01:46 -05:00
|
|
|
// Multiple nodes, make them all fit on the map view
|
2022-02-05 22:03:51 -05:00
|
|
|
nodesWithPosition.forEach {
|
|
|
|
|
points.add(
|
2022-08-23 20:05:19 -04:00
|
|
|
GeoPoint(
|
2022-08-24 16:47:50 -04:00
|
|
|
it.position!!.latitude,
|
|
|
|
|
it.position!!.longitude
|
2022-02-05 22:03:51 -05:00
|
|
|
)
|
|
|
|
|
)
|
2022-02-05 22:01:46 -05:00
|
|
|
}
|
2022-08-24 16:47:50 -04:00
|
|
|
map.zoomToBoundingBox(
|
|
|
|
|
BoundingBox.fromGeoPoints(points),
|
|
|
|
|
true,
|
|
|
|
|
15,
|
|
|
|
|
nodeZoomLevel,
|
|
|
|
|
defaultZoomSpeed
|
|
|
|
|
)
|
2022-02-05 20:21:42 -05:00
|
|
|
} else {
|
|
|
|
|
// Only one node, just zoom in on it
|
|
|
|
|
val it = nodesWithPosition[0].position!!
|
2022-08-24 16:47:50 -04:00
|
|
|
points.add(GeoPoint(it.latitude, it.longitude))
|
|
|
|
|
controller.animateTo(points[0], nodeZoomLevel, defaultZoomSpeed)
|
2022-02-17 10:16:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
private fun loadOnlineTileSourceBase(): OnlineTileSourceBase {
|
2022-08-24 11:30:42 -04:00
|
|
|
val prefs = context?.getSharedPreferences(uiPrefs, Context.MODE_PRIVATE)
|
|
|
|
|
val mapSourceId = prefs?.getInt(mapStyleId, 1)
|
2022-08-23 20:05:19 -04:00
|
|
|
debug("mapStyleId from prefs: $mapSourceId")
|
|
|
|
|
val mapSource = when (mapSourceId) {
|
|
|
|
|
0 -> TileSourceFactory.MAPNIK
|
|
|
|
|
1 -> TileSourceFactory.USGS_TOPO
|
|
|
|
|
2 -> TileSourceFactory.USGS_SAT
|
|
|
|
|
3 -> TileSourceFactory.OpenTopo
|
|
|
|
|
4 -> TileSourceFactory.ROADS_OVERLAY_NL
|
|
|
|
|
6 -> TileSourceFactory.ChartbundleENRH
|
|
|
|
|
7 -> TileSourceFactory.ChartbundleWAC
|
2022-08-24 23:15:48 -04:00
|
|
|
else -> TileSourceFactory.OpenTopo
|
2022-03-06 19:52:49 -05:00
|
|
|
}
|
2022-08-23 20:05:19 -04:00
|
|
|
return mapSource
|
2022-02-16 10:22:59 -05:00
|
|
|
}
|
|
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
override fun onPause() {
|
2022-08-24 11:08:39 -04:00
|
|
|
val edit = mPrefs.edit()
|
|
|
|
|
edit.putString(prefsTileSource, loadOnlineTileSourceBase().name())
|
|
|
|
|
edit.putFloat(prefsZoomLevelDouble, map.zoomLevelDouble.toFloat())
|
|
|
|
|
edit.commit()
|
|
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
map.onPause()
|
|
|
|
|
super.onPause()
|
2022-02-22 22:05:26 -05:00
|
|
|
}
|
|
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
override fun onResume() {
|
|
|
|
|
super.onResume()
|
2022-08-24 11:08:39 -04:00
|
|
|
val tileSourceName = mPrefs.getString(
|
|
|
|
|
prefsTileSource,
|
|
|
|
|
TileSourceFactory.DEFAULT_TILE_SOURCE.name()
|
|
|
|
|
)
|
|
|
|
|
try {
|
2022-08-24 23:15:48 -04:00
|
|
|
map.setTileSource(matchOnlineTileSourceBase(tileSourceName!!))
|
2022-08-24 11:08:39 -04:00
|
|
|
} catch (e: IllegalArgumentException) {
|
|
|
|
|
map.setTileSource(TileSourceFactory.DEFAULT_TILE_SOURCE)
|
|
|
|
|
}
|
2022-08-24 23:15:48 -04:00
|
|
|
map.onResume()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun matchOnlineTileSourceBase(name: String): OnlineTileSourceBase {
|
|
|
|
|
val tileSourceBase = when (name) {
|
|
|
|
|
TileSourceFactory.MAPNIK.name() -> TileSourceFactory.MAPNIK
|
|
|
|
|
TileSourceFactory.USGS_TOPO.name() -> TileSourceFactory.USGS_TOPO
|
|
|
|
|
TileSourceFactory.USGS_SAT.name() -> TileSourceFactory.USGS_SAT
|
|
|
|
|
TileSourceFactory.ROADS_OVERLAY_NL.name() -> TileSourceFactory.ROADS_OVERLAY_NL
|
|
|
|
|
TileSourceFactory.ChartbundleENRH.name() -> TileSourceFactory.ChartbundleENRH
|
|
|
|
|
TileSourceFactory.ChartbundleWAC.name() -> TileSourceFactory.ChartbundleWAC
|
|
|
|
|
else -> TileSourceFactory.DEFAULT_TILE_SOURCE
|
|
|
|
|
}
|
|
|
|
|
return tileSourceBase
|
|
|
|
|
|
2022-02-17 15:22:22 -05:00
|
|
|
}
|
|
|
|
|
|
2022-08-23 20:05:19 -04:00
|
|
|
override fun onDestroy() {
|
2022-08-24 11:08:39 -04:00
|
|
|
super.onDestroyView()
|
2022-08-23 20:05:19 -04:00
|
|
|
map.onDetach()
|
2022-02-17 15:22:22 -05:00
|
|
|
}
|
2020-03-11 14:45:49 -07:00
|
|
|
}
|
2020-04-07 11:27:51 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|