2020-03-11 14:45:49 -07:00
package com.geeksville.mesh.ui
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
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.*
2020-03-30 12:47:01 -07:00
import com.mapbox.geojson.Feature
import com.mapbox.geojson.FeatureCollection
2022-02-17 10:16:58 -05:00
import com.mapbox.geojson.Geometry
2020-03-30 12:47:01 -07:00
import com.mapbox.geojson.Point
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-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
import com.mapbox.maps.viewannotation.viewAnnotationOptions
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: Add option to download mbtiles from existing tiles on MapBox (No mobile SDK supports mbtiles natively, they must be uploaded to MapBox studio first, and then they can be downloaded by specifying a URI)
//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-17 17:43:06 -05:00
private lateinit var mapStyleURI : String
2022-02-17 10:16:58 -05:00
private lateinit var point : Geometry
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-17 17:43:06 -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
2020-04-08 09:53:04 -07:00
private fun onNodesChanged ( map : MapboxMap , nodes : Collection < NodeInfo > ) {
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-17 17:43:06 -05:00
offlineManager . removeStylePack ( mapStyleURI )
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-17 11:41:05 -05:00
binding . fabStyleToggle . setOnClickListener {
2022-02-17 16:12:12 -05:00
//TODO: Setup Style menu for satellite view, street view, & outdoor view
2022-02-17 11:41:05 -05:00
}
2022-02-17 17:12:17 -05:00
binding . downloadRegion . setOnClickListener {
downloadOfflineRegion ( )
}
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-17 17:43:06 -05:00
this . mapStyleURI = map . getStyle ( ) ?. styleURI . toString ( )
2022-02-05 16:44:39 -05:00
}
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 ->
onNodesChanged ( map , nodes . values )
}
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 )
onNodesChanged ( map , nodes . values )
} )
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-17 10:16:58 -05:00
private fun downloadOfflineRegion ( ) {
// 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-17 17:43:06 -05:00
mapStyleURI ,
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 ,
progress . requiredResourceCount ,
" StylePackLoadProgress: $progress "
)
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-17 17:43:06 -05:00
. styleURI ( mapStyleURI )
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 ,
" TileRegionLoadProgress: $progress "
)
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-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-17 15:22:22 -05:00
private fun updateStylePackDownloadProgress (
progress : Long ,
max : Long ,
message : String ? = null
) {
binding . stylePackDownloadProgress . visibility = View . VISIBLE
binding . stylePackDownloadProgress . max = max . toInt ( )
binding . stylePackDownloadProgress . progress = progress . toInt ( )
}
private fun updateTileRegionDownloadProgress (
progress : Long ,
max : Long ,
message : String ? = null
) {
binding . stylePackDownloadProgress . max = max . toInt ( )
binding . stylePackDownloadProgress . progress = progress . toInt ( )
}
2022-02-17 17:43:06 -05:00
// TODO: Make this dynamic
2022-02-17 15:22:22 -05:00
private val click = OnMapClickListener {
2022-02-17 17:12:17 -05:00
if ( binding . fabStyleToggle . isVisible && binding . downloadRegion . isVisible ) {
2022-02-17 15:22:22 -05:00
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 {
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
2022-02-16 10:22:59 -05:00
private sealed class OfflineLog ( val message : String , val color : Int ) {
class Info ( message : String ) : OfflineLog ( message , android . R . color . black )
class Error ( message : String ) : OfflineLog ( message , android . R . color . holo _red _dark )
class Success ( message : String ) : OfflineLog ( message , android . R . color . holo _green _dark )
class TilePackProgress ( message : String ) : OfflineLog ( message , android . R . color . holo _purple )
class StylePackProgress ( message : String ) :
OfflineLog ( message , android . R . color . holo _orange _dark )
}
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 "
}
2020-03-11 14:45:49 -07:00
}
2020-04-07 11:27:51 -07:00