2020-03-11 14:45:49 -07:00
|
|
|
package com.geeksville.mesh.ui
|
|
|
|
|
|
2022-02-18 10:58:59 -05:00
|
|
|
import android.app.AlertDialog
|
2020-03-30 15:00:18 -07:00
|
|
|
import android.graphics.Color
|
2020-03-30 11:56:59 -07:00
|
|
|
import android.os.Bundle
|
2022-02-17 10:16:58 -05:00
|
|
|
import android.os.Handler
|
2020-04-07 11:27:51 -07:00
|
|
|
import android.view.LayoutInflater
|
|
|
|
|
import android.view.View
|
|
|
|
|
import android.view.ViewGroup
|
2022-02-18 10:58:59 -05:00
|
|
|
import android.widget.EditText
|
2022-02-20 21:06:59 -05:00
|
|
|
import android.widget.TextView
|
2022-02-22 21:46:13 -05:00
|
|
|
import android.widget.Toast
|
2021-02-21 11:34:43 +08:00
|
|
|
import androidx.core.content.ContextCompat
|
2022-02-05 13:26:08 -05:00
|
|
|
import androidx.core.graphics.drawable.toBitmap
|
2022-02-04 21:58:00 -05:00
|
|
|
import androidx.core.view.isVisible
|
2020-04-08 09:53:04 -07:00
|
|
|
import androidx.fragment.app.activityViewModels
|
2020-07-07 14:55:01 -07:00
|
|
|
import androidx.lifecycle.Observer
|
2020-04-11 13:20:30 -07:00
|
|
|
import com.geeksville.android.GeeksvilleApplication
|
2020-03-30 10:26:16 -07:00
|
|
|
import com.geeksville.android.Logging
|
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-02-17 11:41:05 -05:00
|
|
|
import com.geeksville.mesh.databinding.MapNotAllowedBinding
|
|
|
|
|
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-02-17 10:16:58 -05:00
|
|
|
import com.mapbox.bindgen.Value
|
|
|
|
|
import com.mapbox.common.*
|
2022-02-22 21:46:13 -05:00
|
|
|
import com.mapbox.geojson.*
|
2022-02-05 20:21:42 -05:00
|
|
|
import com.mapbox.maps.*
|
2022-02-05 22:01:46 -05:00
|
|
|
import com.mapbox.maps.dsl.cameraOptions
|
2022-02-04 21:58:00 -05:00
|
|
|
import com.mapbox.maps.extension.style.expressions.generated.Expression
|
2022-02-05 13:26:08 -05:00
|
|
|
import com.mapbox.maps.extension.style.layers.addLayer
|
2022-02-04 21:58:00 -05:00
|
|
|
import com.mapbox.maps.extension.style.layers.generated.SymbolLayer
|
|
|
|
|
import com.mapbox.maps.extension.style.layers.properties.generated.IconAnchor
|
|
|
|
|
import com.mapbox.maps.extension.style.layers.properties.generated.TextAnchor
|
|
|
|
|
import com.mapbox.maps.extension.style.layers.properties.generated.TextJustify
|
2022-02-05 13:26:08 -05:00
|
|
|
import com.mapbox.maps.extension.style.sources.addSource
|
2022-02-04 21:58:00 -05:00
|
|
|
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
|
2022-02-05 20:21:42 -05:00
|
|
|
import com.mapbox.maps.plugin.animation.MapAnimationOptions
|
2022-02-05 22:01:46 -05:00
|
|
|
import com.mapbox.maps.plugin.animation.flyTo
|
2022-02-22 21:46:13 -05:00
|
|
|
import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions
|
2022-02-17 10:16:58 -05:00
|
|
|
import com.mapbox.maps.plugin.gestures.OnMapClickListener
|
2022-02-16 10:22:59 -05:00
|
|
|
import com.mapbox.maps.plugin.gestures.OnMapLongClickListener
|
2022-02-05 16:44:39 -05:00
|
|
|
import com.mapbox.maps.plugin.gestures.gestures
|
2022-02-17 10:16:58 -05:00
|
|
|
import com.mapbox.maps.viewannotation.ViewAnnotationManager
|
2022-02-08 13:50:21 -08:00
|
|
|
import dagger.hilt.android.AndroidEntryPoint
|
2020-03-30 12:47:01 -07:00
|
|
|
|
2020-03-11 14:45:49 -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-02-17 10:16:58 -05:00
|
|
|
|
|
|
|
|
private val tileStore: TileStore by lazy {
|
|
|
|
|
TileStore.create().also {
|
|
|
|
|
// Set default access token for the created tile store instance
|
|
|
|
|
it.setOption(
|
|
|
|
|
TileStoreOptions.MAPBOX_ACCESS_TOKEN,
|
|
|
|
|
TileDataDomain.MAPS,
|
|
|
|
|
Value(getString(R.string.mapbox_access_token))
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-17 16:12:12 -05:00
|
|
|
//TODO: Setup menu when creating region for offline maps (On long press set a point to center region, then click that point to bring up menu)
|
|
|
|
|
//TODO: View Offline Regions (This will allow you to select the region and the map will zoom to it)
|
|
|
|
|
//TODO: Manage Offline Regions (Allow you to edit the name, delete, & select region)
|
|
|
|
|
//TODO: Update download animation
|
|
|
|
|
|
2022-02-17 10:16:58 -05:00
|
|
|
private val resourceOptions: ResourceOptions by lazy {
|
|
|
|
|
ResourceOptions.Builder().applyDefaultParams(requireContext()).tileStore(tileStore).build()
|
|
|
|
|
}
|
|
|
|
|
private val offlineManager: OfflineManager by lazy {
|
|
|
|
|
OfflineManager(resourceOptions)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private lateinit var handler: Handler
|
2022-02-17 11:41:05 -05:00
|
|
|
private lateinit var binding: MapViewBinding
|
|
|
|
|
private lateinit var mapNotAllowedBinding: MapNotAllowedBinding
|
|
|
|
|
private lateinit var viewAnnotationManager: ViewAnnotationManager
|
2022-02-20 21:06:59 -05:00
|
|
|
private lateinit var pointLat: String
|
|
|
|
|
private lateinit var pointLong: String
|
2022-02-22 21:46:13 -05:00
|
|
|
private lateinit var userStyleURI: String
|
2022-02-17 10:16:58 -05:00
|
|
|
|
2022-02-22 21:46:13 -05:00
|
|
|
|
|
|
|
|
private lateinit var point: Point
|
2022-02-17 10:16:58 -05:00
|
|
|
|
2020-04-08 09:53:04 -07:00
|
|
|
private val model: UIViewModel by activityViewModels()
|
|
|
|
|
|
2020-04-07 12:13:50 -07:00
|
|
|
private val nodeSourceId = "node-positions"
|
|
|
|
|
private val nodeLayerId = "node-layer"
|
|
|
|
|
private val labelLayerId = "label-layer"
|
|
|
|
|
private val markerImageId = "my-marker-image"
|
2022-02-22 21:46:13 -05:00
|
|
|
private val userPointImageId = "user-image"
|
2020-04-07 12:13:50 -07:00
|
|
|
|
2022-02-17 10:16:58 -05:00
|
|
|
private var stylePackCancelable: Cancelable? = null
|
|
|
|
|
private var tilePackCancelable: Cancelable? = null
|
|
|
|
|
|
2022-02-16 10:22:59 -05:00
|
|
|
|
|
|
|
|
private val userTouchPositionId = "user-touch-position"
|
|
|
|
|
private val userTouchLayerId = "user-touch-layer"
|
2022-02-05 20:21:42 -05:00
|
|
|
private var nodePositions = GeoJsonSource(GeoJsonSource.Builder(nodeSourceId))
|
2020-04-07 12:13:50 -07:00
|
|
|
|
2022-02-17 10:16:58 -05:00
|
|
|
|
2022-02-16 13:56:17 -05:00
|
|
|
private val userTouchPosition = GeoJsonSource(GeoJsonSource.Builder(userTouchPositionId))
|
|
|
|
|
|
|
|
|
|
|
2022-02-04 21:58:00 -05:00
|
|
|
private val nodeLayer = SymbolLayer(nodeLayerId, nodeSourceId)
|
|
|
|
|
.iconImage(markerImageId)
|
|
|
|
|
.iconAnchor(IconAnchor.BOTTOM)
|
2022-02-05 12:32:31 -05:00
|
|
|
.iconAllowOverlap(true)
|
2020-04-07 12:13:50 -07:00
|
|
|
|
2022-02-16 10:22:59 -05:00
|
|
|
private val userTouchLayer = SymbolLayer(userTouchLayerId, userTouchPositionId)
|
2022-02-17 17:43:06 -05:00
|
|
|
.iconImage(userPointImageId)
|
2022-02-16 10:22:59 -05:00
|
|
|
.iconAnchor(IconAnchor.BOTTOM)
|
|
|
|
|
.iconAllowOverlap(true)
|
|
|
|
|
|
2022-02-04 21:58:00 -05:00
|
|
|
private val labelLayer = SymbolLayer(labelLayerId, nodeSourceId)
|
|
|
|
|
.textField(Expression.get("name"))
|
2022-02-05 13:26:08 -05:00
|
|
|
.textSize(12.0)
|
2022-02-04 21:58:00 -05:00
|
|
|
.textColor(Color.RED)
|
2022-02-05 13:26:08 -05:00
|
|
|
.textAnchor(TextAnchor.TOP)
|
|
|
|
|
//.textVariableAnchor(TextAnchor.TOP) //TODO investigate need for variable anchor vs normal anchor
|
2022-02-04 21:58:00 -05:00
|
|
|
.textJustify(TextJustify.AUTO)
|
|
|
|
|
.textAllowOverlap(true)
|
2020-04-07 12:13:50 -07:00
|
|
|
|
|
|
|
|
|
2022-02-22 21:46:13 -05:00
|
|
|
private fun onNodesChanged(nodes: Collection<NodeInfo>) {
|
2020-04-08 09:53:04 -07:00
|
|
|
val nodesWithPosition = nodes.filter { it.validPosition != null }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Using the latest nodedb, generate geojson features
|
|
|
|
|
*/
|
|
|
|
|
fun getCurrentNodes(): FeatureCollection {
|
|
|
|
|
// Find all nodes with valid locations
|
2020-04-07 12:13:50 -07:00
|
|
|
|
2020-04-08 09:53:04 -07:00
|
|
|
val locations = nodesWithPosition.map { node ->
|
|
|
|
|
val p = node.position!!
|
|
|
|
|
debug("Showing on map: $node")
|
2020-04-07 12:13:50 -07:00
|
|
|
|
2020-04-08 09:53:04 -07:00
|
|
|
val f = Feature.fromGeometry(
|
|
|
|
|
Point.fromLngLat(
|
|
|
|
|
p.longitude,
|
|
|
|
|
p.latitude
|
|
|
|
|
)
|
2020-04-07 12:13:50 -07:00
|
|
|
)
|
2020-04-08 09:53:04 -07:00
|
|
|
node.user?.let {
|
2021-03-16 10:04:01 +05:00
|
|
|
f.addStringProperty("name", it.longName + " " + formatAgo(p.time))
|
2020-04-08 09:53:04 -07:00
|
|
|
}
|
|
|
|
|
f
|
2020-04-07 12:13:50 -07:00
|
|
|
}
|
|
|
|
|
|
2020-04-08 09:53:04 -07:00
|
|
|
return FeatureCollection.fromFeatures(locations)
|
|
|
|
|
}
|
2022-02-05 20:21:42 -05:00
|
|
|
nodePositions.featureCollection(getCurrentNodes())
|
2021-02-06 22:08:49 -08:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 22:01:46 -05:00
|
|
|
private fun zoomToNodes(map: MapboxMap) {
|
|
|
|
|
val points: MutableList<Point> = mutableListOf()
|
2021-03-16 10:04:01 +05:00
|
|
|
val nodesWithPosition =
|
|
|
|
|
model.nodeDB.nodes.value?.values?.filter { it.validPosition != null }
|
2021-02-06 22:08:49 -08:00
|
|
|
if (nodesWithPosition != null && nodesWithPosition.isNotEmpty()) {
|
2022-02-05 22:01:46 -05:00
|
|
|
val unit = if (nodesWithPosition.size >= 2) {
|
2022-02-05 20:21:42 -05:00
|
|
|
|
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(
|
|
|
|
|
Point.fromLngLat(
|
|
|
|
|
it.position!!.longitude,
|
|
|
|
|
it.position!!.latitude
|
|
|
|
|
)
|
|
|
|
|
)
|
2022-02-05 22:01:46 -05:00
|
|
|
}
|
|
|
|
|
map.cameraForCoordinates(points)
|
2022-02-05 20:21:42 -05:00
|
|
|
} else {
|
|
|
|
|
// Only one node, just zoom in on it
|
|
|
|
|
val it = nodesWithPosition[0].position!!
|
2022-02-05 22:01:46 -05:00
|
|
|
points.add(Point.fromLngLat(it.longitude, it.latitude))
|
|
|
|
|
map.cameraForCoordinates(points)
|
|
|
|
|
cameraOptions {
|
|
|
|
|
this.zoom(9.0)
|
|
|
|
|
this.center(points[0])
|
|
|
|
|
}
|
2022-02-05 20:21:42 -05:00
|
|
|
}
|
2022-02-05 22:01:46 -05:00
|
|
|
map.flyTo(
|
|
|
|
|
unit,
|
|
|
|
|
MapAnimationOptions.mapAnimationOptions { duration(1000) })
|
2021-02-06 22:08:49 -08:00
|
|
|
}
|
2020-04-07 12:13:50 -07:00
|
|
|
}
|
|
|
|
|
|
2020-04-07 11:27:51 -07:00
|
|
|
override fun onCreateView(
|
|
|
|
|
inflater: LayoutInflater, container: ViewGroup?,
|
|
|
|
|
savedInstanceState: Bundle?
|
2022-02-17 11:41:05 -05:00
|
|
|
): View {
|
2020-04-11 13:20:30 -07:00
|
|
|
// We can't allow mapbox if user doesn't want analytics
|
2022-02-17 11:41:05 -05:00
|
|
|
return if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) {
|
|
|
|
|
// Mapbox Access token
|
|
|
|
|
binding = MapViewBinding.inflate(inflater, container, false)
|
|
|
|
|
binding.root
|
|
|
|
|
} else {
|
|
|
|
|
mapNotAllowedBinding = MapNotAllowedBinding.inflate(inflater, container, false)
|
|
|
|
|
mapNotAllowedBinding.root
|
|
|
|
|
}
|
2020-04-11 13:20:30 -07:00
|
|
|
}
|
2020-03-11 14:45:49 -07:00
|
|
|
|
2020-04-11 13:20:30 -07:00
|
|
|
var mapView: MapView? = null
|
2020-07-07 14:55:01 -07:00
|
|
|
|
2022-02-17 10:16:58 -05:00
|
|
|
|
|
|
|
|
private fun showDownloadedRegions() {
|
|
|
|
|
// Get a list of tile regions that are currently available.
|
|
|
|
|
tileStore.getAllTileRegions { expected ->
|
|
|
|
|
if (expected.isValue) {
|
|
|
|
|
expected.value?.let { tileRegionList ->
|
|
|
|
|
debug("Existing tile regions: $tileRegionList")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
expected.error?.let { tileRegionError ->
|
|
|
|
|
debug("TileRegionError: $tileRegionError")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Get a list of style packs that are currently available.
|
|
|
|
|
offlineManager.getAllStylePacks { expected ->
|
|
|
|
|
if (expected.isValue) {
|
|
|
|
|
expected.value?.let { stylePackList ->
|
|
|
|
|
debug("Existing style packs: $stylePackList")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
expected.error?.let { stylePackError ->
|
|
|
|
|
debug("StylePackError: $stylePackError")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun removeOfflineRegions() {
|
|
|
|
|
// Remove the tile region with the tile region ID.
|
|
|
|
|
// Note this will not remove the downloaded tile packs, instead, it will just mark the tileset
|
|
|
|
|
// not a part of a tile region. The tiles still exists as a predictive cache in TileStore.
|
|
|
|
|
tileStore.removeTileRegion(TILE_REGION_ID)
|
|
|
|
|
|
|
|
|
|
// Set the disk quota to zero, so that tile regions are fully evicted
|
|
|
|
|
// when removed. The TileStore is also used when `ResourceOptions.isLoadTilePacksFromNetwork`
|
|
|
|
|
// is `true`, and also by the Navigation SDK.
|
|
|
|
|
// This removes the tiles that do not belong to any tile regions.
|
|
|
|
|
tileStore.setOption(TileStoreOptions.DISK_QUOTA, Value(0))
|
|
|
|
|
|
|
|
|
|
// Remove the style pack with the style url.
|
|
|
|
|
// Note this will not remove the downloaded style pack, instead, it will just mark the resources
|
|
|
|
|
// not a part of the existing style pack. The resources still exists as disk cache.
|
2022-02-20 19:11:03 -05:00
|
|
|
offlineManager.removeStylePack(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString())
|
2022-02-17 10:16:58 -05:00
|
|
|
|
|
|
|
|
MapboxMap.clearData(resourceOptions) {
|
|
|
|
|
it.error?.let { error ->
|
|
|
|
|
debug(error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-08 12:19:35 +08:00
|
|
|
/**
|
|
|
|
|
* Mapbox native code can crash painfully if you ever call a mapbox view function while the view is not actively being show
|
|
|
|
|
*/
|
|
|
|
|
private val isViewVisible: Boolean
|
2022-02-04 21:58:00 -05:00
|
|
|
get() = mapView?.isVisible == true
|
2021-02-08 12:19:35 +08:00
|
|
|
|
2020-06-10 17:12:57 -07:00
|
|
|
override fun onViewCreated(viewIn: View, savedInstanceState: Bundle?) {
|
2020-06-10 17:19:17 -07:00
|
|
|
super.onViewCreated(viewIn, savedInstanceState)
|
2020-03-30 11:56:59 -07:00
|
|
|
|
2022-02-18 10:58:59 -05:00
|
|
|
// binding.fabStyleToggle.setOnClickListener {
|
|
|
|
|
//
|
|
|
|
|
// //TODO: Setup Style menu for satellite view, street view, & outdoor view
|
|
|
|
|
// }
|
2022-02-17 17:12:17 -05:00
|
|
|
binding.downloadRegion.setOnClickListener {
|
2022-02-18 10:58:59 -05:00
|
|
|
// Display menu for download region
|
2022-02-20 21:06:59 -05:00
|
|
|
this.downloadRegionDialogFragment()
|
2022-02-17 17:12:17 -05:00
|
|
|
}
|
2020-04-11 13:20:30 -07:00
|
|
|
// We might not have a real mapview if running with analytics
|
|
|
|
|
if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) {
|
2020-06-10 17:12:57 -07:00
|
|
|
val vIn = viewIn.findViewById<MapView>(R.id.mapView)
|
|
|
|
|
mapView = vIn
|
2020-06-10 12:49:05 -07:00
|
|
|
mapView?.let { v ->
|
2021-02-02 10:47:54 +08:00
|
|
|
|
2020-06-10 12:49:05 -07:00
|
|
|
// Each time the pane is shown start fetching new map info (we do this here instead of
|
|
|
|
|
// onCreate because getMapAsync can die in native code if the view goes away)
|
2020-07-07 14:55:01 -07:00
|
|
|
|
2022-02-04 21:58:00 -05:00
|
|
|
val map = v.getMapboxMap()
|
|
|
|
|
if (view != null) { // it might have gone away by now
|
|
|
|
|
val markerIcon =
|
|
|
|
|
ContextCompat.getDrawable(
|
|
|
|
|
requireActivity(),
|
|
|
|
|
R.drawable.ic_twotone_person_pin_24
|
2022-02-05 13:26:08 -05:00
|
|
|
)!!.toBitmap()
|
2022-02-04 21:58:00 -05:00
|
|
|
|
2022-02-05 13:26:08 -05:00
|
|
|
map.loadStyleUri(Style.OUTDOORS) {
|
2022-02-05 16:44:39 -05:00
|
|
|
if (it.isStyleLoaded) {
|
|
|
|
|
it.addSource(nodePositions)
|
|
|
|
|
it.addImage(markerImageId, markerIcon)
|
|
|
|
|
it.addLayer(nodeLayer)
|
|
|
|
|
it.addLayer(labelLayer)
|
|
|
|
|
}
|
2022-02-05 13:26:08 -05:00
|
|
|
}
|
2022-02-04 21:58:00 -05:00
|
|
|
|
2022-02-05 16:44:39 -05:00
|
|
|
v.gestures.rotateEnabled = false
|
2022-02-16 10:22:59 -05:00
|
|
|
v.gestures.addOnMapLongClickListener(this.longClick)
|
2022-02-17 15:22:22 -05:00
|
|
|
v.gestures.addOnMapClickListener(this.click)
|
2022-02-05 12:32:31 -05:00
|
|
|
|
2022-02-04 21:58:00 -05:00
|
|
|
// Provide initial positions
|
|
|
|
|
model.nodeDB.nodes.value?.let { nodes ->
|
2022-02-22 21:46:13 -05:00
|
|
|
onNodesChanged(nodes.values)
|
2022-02-04 21:58:00 -05:00
|
|
|
}
|
2020-04-11 13:20:30 -07:00
|
|
|
}
|
2022-02-04 21:58:00 -05:00
|
|
|
|
|
|
|
|
// Any times nodes change update our map
|
|
|
|
|
model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { nodes ->
|
|
|
|
|
if (isViewVisible)
|
2022-02-22 21:46:13 -05:00
|
|
|
onNodesChanged(nodes.values)
|
2022-02-04 21:58:00 -05:00
|
|
|
})
|
2022-02-17 10:16:58 -05:00
|
|
|
//viewAnnotationManager = v.viewAnnotationManager
|
2022-02-04 21:58:00 -05:00
|
|
|
zoomToNodes(map)
|
2020-04-11 13:20:30 -07:00
|
|
|
}
|
2020-03-30 10:26:16 -07:00
|
|
|
}
|
2020-03-11 14:45:49 -07:00
|
|
|
}
|
2022-02-16 10:22:59 -05:00
|
|
|
|
2022-02-20 22:59:29 -05:00
|
|
|
private fun downloadOfflineRegion(styleURI: String = "") {
|
|
|
|
|
val style = styleURI.ifEmpty {
|
|
|
|
|
mapView?.getMapboxMap()
|
|
|
|
|
?.getStyle()?.styleURI.toString()
|
|
|
|
|
}
|
2022-02-17 10:16:58 -05:00
|
|
|
// By default, users may download up to 250MB of data for offline use without incurring
|
|
|
|
|
// additional charges. This limit is subject to change during the beta.
|
|
|
|
|
|
|
|
|
|
// - - - - - - - -
|
|
|
|
|
|
|
|
|
|
// 1. Create style package with loadStylePack() call.
|
|
|
|
|
|
|
|
|
|
// A style pack (a Style offline package) contains the loaded style and its resources: loaded
|
|
|
|
|
// sources, fonts, sprites. Style packs are identified with their style URI.
|
|
|
|
|
|
|
|
|
|
// Style packs are stored in the disk cache database, but their resources are not subject to
|
|
|
|
|
// the data eviction algorithm and are not considered when calculating the disk cache size.
|
2022-02-17 17:43:06 -05:00
|
|
|
|
2022-02-17 10:16:58 -05:00
|
|
|
stylePackCancelable = offlineManager.loadStylePack(
|
2022-02-20 22:59:29 -05:00
|
|
|
style,
|
2022-02-17 10:16:58 -05:00
|
|
|
// Build Style pack load options
|
|
|
|
|
StylePackLoadOptions.Builder()
|
|
|
|
|
.glyphsRasterizationMode(GlyphsRasterizationMode.IDEOGRAPHS_RASTERIZED_LOCALLY)
|
|
|
|
|
.metadata(Value(STYLE_PACK_METADATA))
|
|
|
|
|
.build(),
|
|
|
|
|
{ progress ->
|
|
|
|
|
// Update the download progress to UI
|
2022-02-17 15:22:22 -05:00
|
|
|
updateStylePackDownloadProgress(
|
|
|
|
|
progress.completedResourceCount,
|
2022-02-22 21:46:13 -05:00
|
|
|
progress.requiredResourceCount
|
2022-02-17 15:22:22 -05:00
|
|
|
)
|
2022-02-17 10:16:58 -05:00
|
|
|
},
|
|
|
|
|
{ expected ->
|
|
|
|
|
if (expected.isValue) {
|
|
|
|
|
expected.value?.let { stylePack ->
|
|
|
|
|
// Style pack download finishes successfully
|
2022-02-17 15:22:22 -05:00
|
|
|
debug("StylePack downloaded: $stylePack")
|
|
|
|
|
if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) {
|
|
|
|
|
debug("Doing stuff")
|
|
|
|
|
binding.stylePackDownloadProgress.visibility = View.INVISIBLE
|
|
|
|
|
} else {
|
|
|
|
|
debug("Waiting for tile region download to be finished.")
|
|
|
|
|
}
|
2022-02-17 10:16:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
expected.error?.let {
|
|
|
|
|
// Handle error occurred during the style pack download.
|
2022-02-17 15:22:22 -05:00
|
|
|
debug("StylePackError: $it")
|
2022-02-17 10:16:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// - - - - - - - -
|
|
|
|
|
|
|
|
|
|
// 2. Create a tile region with tiles for the outdoors style
|
|
|
|
|
|
|
|
|
|
// A Tile Region represents an identifiable geographic tile region with metadata, consisting of
|
|
|
|
|
// a set of tiles packs that cover a given area (a polygon). Tile Regions allow caching tiles
|
|
|
|
|
// packs in an explicit way: By creating a Tile Region, developers can ensure that all tiles in
|
|
|
|
|
// that region will be downloaded and remain cached until explicitly deleted.
|
|
|
|
|
|
|
|
|
|
// Creating a Tile Region requires supplying a description of the area geometry, the tilesets
|
|
|
|
|
// and zoom ranges of the tiles within the region.
|
|
|
|
|
|
|
|
|
|
// The tileset descriptor encapsulates the tile-specific data, such as which tilesets, zoom ranges,
|
|
|
|
|
// pixel ratio etc. the cached tile packs should have. It is passed to the Tile Store along with
|
|
|
|
|
// the region area geometry to load a new Tile Region.
|
|
|
|
|
|
|
|
|
|
// The OfflineManager is responsible for creating tileset descriptors for the given style and zoom range.
|
2022-02-17 17:43:06 -05:00
|
|
|
|
2022-02-17 10:16:58 -05:00
|
|
|
val tilesetDescriptor = offlineManager.createTilesetDescriptor(
|
|
|
|
|
TilesetDescriptorOptions.Builder()
|
2022-02-20 22:59:29 -05:00
|
|
|
.styleURI(style)
|
2022-02-17 10:16:58 -05:00
|
|
|
.minZoom(0)
|
|
|
|
|
.maxZoom(16)
|
|
|
|
|
.build()
|
|
|
|
|
)
|
|
|
|
|
// Use the the default TileStore to load this region. You can create custom TileStores are are
|
|
|
|
|
// unique for a particular file path, i.e. there is only ever one TileStore per unique path.
|
|
|
|
|
|
|
|
|
|
// Note that the TileStore path must be the same with the TileStore used when initialise the MapView.
|
|
|
|
|
tilePackCancelable = tileStore.loadTileRegion(
|
2022-02-17 17:43:06 -05:00
|
|
|
TILE_REGION_ID, // Make this dynamic
|
2022-02-17 10:16:58 -05:00
|
|
|
TileRegionLoadOptions.Builder()
|
|
|
|
|
.geometry(point)
|
|
|
|
|
.descriptors(listOf(tilesetDescriptor))
|
|
|
|
|
.metadata(Value(TILE_REGION_METADATA))
|
|
|
|
|
.acceptExpired(true)
|
|
|
|
|
.networkRestriction(NetworkRestriction.NONE)
|
|
|
|
|
.build(),
|
|
|
|
|
{ progress ->
|
2022-02-17 15:22:22 -05:00
|
|
|
updateTileRegionDownloadProgress(
|
|
|
|
|
progress.completedResourceCount,
|
|
|
|
|
progress.requiredResourceCount,
|
|
|
|
|
)
|
2022-02-17 10:16:58 -05:00
|
|
|
}
|
|
|
|
|
) { expected ->
|
|
|
|
|
if (expected.isValue) {
|
|
|
|
|
// Tile pack download finishes successfully
|
|
|
|
|
expected.value?.let { region ->
|
2022-02-17 15:22:22 -05:00
|
|
|
debug("TileRegion downloaded: $region")
|
|
|
|
|
if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) {
|
|
|
|
|
debug("Finished tilepack download")
|
|
|
|
|
binding.stylePackDownloadProgress.visibility = View.INVISIBLE
|
|
|
|
|
} else {
|
|
|
|
|
debug("Waiting for style pack download to be finished.")
|
|
|
|
|
}
|
2022-02-17 10:16:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
expected.error?.let {
|
|
|
|
|
// Handle error occurred during the tile region download.
|
2022-02-17 15:22:22 -05:00
|
|
|
debug("TileRegionError: $it")
|
2022-02-17 10:16:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// prepareCancelButton()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// private fun addViewAnnotation(point: Point) {
|
|
|
|
|
// viewAnnotationManager?.addViewAnnotation(
|
|
|
|
|
// resId = R.layout.user_icon_menu,
|
|
|
|
|
// options = viewAnnotationOptions {
|
|
|
|
|
// geometry(point)
|
|
|
|
|
// }
|
|
|
|
|
// )
|
|
|
|
|
// }
|
|
|
|
|
|
2022-02-16 13:56:17 -05:00
|
|
|
/**
|
|
|
|
|
* OnLongClick of the map set a position marker.
|
2022-02-17 17:43:06 -05:00
|
|
|
* If a user long-clicks again, the position of the first marker will be updated
|
2022-02-16 13:56:17 -05:00
|
|
|
*/
|
2022-02-16 10:22:59 -05:00
|
|
|
private val longClick = OnMapLongClickListener {
|
|
|
|
|
val userDefinedPointImg =
|
2022-02-17 17:43:06 -05:00
|
|
|
ContextCompat.getDrawable(
|
|
|
|
|
requireActivity(),
|
|
|
|
|
R.drawable.baseline_location_on_white_24dp
|
|
|
|
|
)!!
|
2022-02-16 10:22:59 -05:00
|
|
|
.toBitmap()
|
2022-02-20 21:06:59 -05:00
|
|
|
pointLong = String.format("%.2f", it.longitude())
|
|
|
|
|
pointLat = String.format("%.2f", it.latitude())
|
2022-02-17 10:16:58 -05:00
|
|
|
point = Point.fromLngLat(it.longitude(), it.latitude())
|
2022-02-16 10:22:59 -05:00
|
|
|
|
|
|
|
|
mapView?.getMapboxMap()?.getStyle()?.let { style ->
|
2022-02-16 13:56:17 -05:00
|
|
|
userTouchPosition.geometry(point)
|
|
|
|
|
|
|
|
|
|
if (!style.styleLayerExists(userTouchLayerId)) {
|
2022-02-17 17:43:06 -05:00
|
|
|
style.addImage(userPointImageId, userDefinedPointImg)
|
2022-02-16 13:56:17 -05:00
|
|
|
style.addSource(userTouchPosition)
|
|
|
|
|
style.addLayer(userTouchLayer)
|
|
|
|
|
}
|
2022-02-16 10:22:59 -05:00
|
|
|
}
|
|
|
|
|
return@OnMapLongClickListener true
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-22 21:46:13 -05:00
|
|
|
/*
|
|
|
|
|
if (this::point.isInitialized) {
|
|
|
|
|
if (it.latitude() == point.latitude() && it.longitude() == point.longitude()) {
|
|
|
|
|
mapView?.getMapboxMap()?.getStyle()?.let { style ->
|
|
|
|
|
style.removeStyleImage(userPointImageId)
|
|
|
|
|
style.removeStyleSource(userTouchPositionId)
|
|
|
|
|
style.removeStyleLayer(userTouchLayerId)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
2022-02-17 15:22:22 -05:00
|
|
|
private fun updateStylePackDownloadProgress(
|
|
|
|
|
progress: Long,
|
|
|
|
|
max: Long,
|
|
|
|
|
) {
|
|
|
|
|
binding.stylePackDownloadProgress.visibility = View.VISIBLE
|
|
|
|
|
binding.stylePackDownloadProgress.max = max.toInt()
|
|
|
|
|
binding.stylePackDownloadProgress.progress = progress.toInt()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun updateTileRegionDownloadProgress(
|
|
|
|
|
progress: Long,
|
|
|
|
|
max: Long,
|
|
|
|
|
) {
|
|
|
|
|
binding.stylePackDownloadProgress.max = max.toInt()
|
|
|
|
|
binding.stylePackDownloadProgress.progress = progress.toInt()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private val click = OnMapClickListener {
|
2022-02-18 10:58:59 -05:00
|
|
|
//binding.fabStyleToggle.isVisible &&
|
|
|
|
|
if (binding.downloadRegion.isVisible) {
|
|
|
|
|
//binding.fabStyleToggle.visibility = View.INVISIBLE
|
2022-02-17 17:12:17 -05:00
|
|
|
binding.downloadRegion.visibility = View.INVISIBLE
|
2022-02-17 15:22:22 -05:00
|
|
|
} else {
|
2022-02-18 10:58:59 -05:00
|
|
|
//binding.fabStyleToggle.visibility = View.VISIBLE
|
2022-02-17 17:12:17 -05:00
|
|
|
binding.downloadRegion.visibility = View.VISIBLE
|
2022-02-17 15:22:22 -05:00
|
|
|
}
|
|
|
|
|
return@OnMapClickListener true
|
|
|
|
|
}
|
2022-02-17 10:16:58 -05:00
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
private const val ZOOM = 12.0
|
|
|
|
|
private const val TILE_REGION_ID = "myTileRegion"
|
|
|
|
|
private const val STYLE_PACK_METADATA = "my-outdoor-style-pack"
|
|
|
|
|
private const val TILE_REGION_METADATA = "my-outdoors-tile-region"
|
|
|
|
|
}
|
2022-02-18 10:58:59 -05:00
|
|
|
|
2022-02-20 21:06:59 -05:00
|
|
|
private fun downloadRegionDialogFragment() {
|
|
|
|
|
val mapDownloadView = layoutInflater.inflate(R.layout.dialog_map_download, null)
|
|
|
|
|
val uri = mapDownloadView.findViewById<EditText>(R.id.uri)
|
|
|
|
|
val downloadRegionDialogFragment = AlertDialog.Builder(context)
|
|
|
|
|
|
|
|
|
|
if (this::pointLat.isInitialized && this::pointLat.isInitialized) {
|
2022-02-22 21:46:13 -05:00
|
|
|
val latText = mapDownloadView.findViewById<TextView>(R.id.longitude)
|
2022-02-20 21:06:59 -05:00
|
|
|
"Lat: $pointLong".also {
|
2022-02-22 21:46:13 -05:00
|
|
|
latText.text = it
|
|
|
|
|
View.VISIBLE.also { latText.visibility = View.VISIBLE }
|
2022-02-20 21:06:59 -05:00
|
|
|
}
|
2022-02-22 21:46:13 -05:00
|
|
|
val longText = mapDownloadView.findViewById<TextView>(R.id.latitude)
|
2022-02-20 21:06:59 -05:00
|
|
|
"Long: $pointLat".also {
|
2022-02-22 21:46:13 -05:00
|
|
|
longText.text = it
|
|
|
|
|
View.VISIBLE.also { longText.visibility = View.VISIBLE }
|
2022-02-20 21:06:59 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
downloadRegionDialogFragment.setView(mapDownloadView)
|
2022-02-22 21:46:13 -05:00
|
|
|
.setTitle("Download Region")
|
2022-02-20 21:06:59 -05:00
|
|
|
.setMultiChoiceItems(
|
|
|
|
|
R.array.MapMenuCheckbox,
|
|
|
|
|
null,
|
|
|
|
|
) { _, _, isChecked ->
|
|
|
|
|
if (isChecked) {
|
|
|
|
|
if (!uri.isVisible) {
|
|
|
|
|
uri.visibility =
|
|
|
|
|
View.VISIBLE
|
2022-02-18 10:58:59 -05:00
|
|
|
}
|
2022-02-20 21:06:59 -05:00
|
|
|
} else {
|
|
|
|
|
if (uri.isVisible) {
|
|
|
|
|
uri.visibility =
|
|
|
|
|
View.GONE
|
2022-02-18 10:58:59 -05:00
|
|
|
}
|
2022-02-20 21:06:59 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.setPositiveButton(
|
|
|
|
|
"Save"
|
2022-02-22 21:46:13 -05:00
|
|
|
) { _, _ ->
|
|
|
|
|
|
2022-02-20 21:32:19 -05:00
|
|
|
if (uri.isVisible) {
|
2022-02-20 21:06:59 -05:00
|
|
|
// Save URI
|
|
|
|
|
userStyleURI = uri.text.toString()
|
|
|
|
|
uri.setText("") // clear text
|
|
|
|
|
}
|
2022-02-22 21:46:13 -05:00
|
|
|
if ((this::point.isInitialized) && userStyleURI.isNotEmpty()) {
|
|
|
|
|
//TODO Create new popup to handle download menu
|
|
|
|
|
//TODO Name region and then confirm
|
|
|
|
|
//TODO Save Button to start download and cancel to stop download
|
|
|
|
|
downloadOfflineRegion(userStyleURI)
|
|
|
|
|
} else if ((this::point.isInitialized) && userStyleURI.isEmpty()) {
|
2022-02-20 21:32:19 -05:00
|
|
|
downloadOfflineRegion()
|
|
|
|
|
} else {
|
|
|
|
|
// Tell user to select region
|
2022-02-22 21:46:13 -05:00
|
|
|
val text =
|
|
|
|
|
"You must select a region on the map, long press the region you want to download"
|
|
|
|
|
val duration = Toast.LENGTH_LONG
|
|
|
|
|
val toast = Toast.makeText(requireContext(), text, duration)
|
|
|
|
|
toast.show()
|
2022-02-20 21:32:19 -05:00
|
|
|
}
|
2022-02-20 21:06:59 -05:00
|
|
|
}
|
2022-02-22 21:46:13 -05:00
|
|
|
.setNeutralButton("View Regions") { dialog, _ ->
|
|
|
|
|
|
|
|
|
|
//OfflineSwitch.getInstance().isMapboxStackConnected = false
|
|
|
|
|
// mapView = MapView(requireContext()).also { mapview ->
|
|
|
|
|
// val mapboxMap = mapview.getMapboxMap()
|
|
|
|
|
// mapboxMap.setCamera(CameraOptions.Builder().zoom(ZOOM).center(point).build())
|
|
|
|
|
// debug(userStyleURI)
|
|
|
|
|
// mapboxMap.loadStyleUri(userStyleURI) {
|
|
|
|
|
// CircleAnnotationOptions()
|
|
|
|
|
// .withPoint(point)
|
|
|
|
|
// .withCircleColor(Color.RED)
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// mapView?.onStart()
|
|
|
|
|
// Open up Downloaded Region managers
|
|
|
|
|
}
|
2022-02-20 21:06:59 -05:00
|
|
|
.setNegativeButton(
|
|
|
|
|
R.string.cancel
|
|
|
|
|
) { dialog, _ ->
|
2022-02-22 21:46:13 -05:00
|
|
|
removeOfflineRegions() //TODO: Add to offline manager window
|
2022-02-20 21:06:59 -05:00
|
|
|
dialog.cancel()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
downloadRegionDialogFragment.create()
|
|
|
|
|
downloadRegionDialogFragment.show()
|
2022-02-18 10:58:59 -05:00
|
|
|
}
|
2020-03-11 14:45:49 -07:00
|
|
|
}
|
2020-04-07 11:27:51 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|