From 56c1480acb59b303804d9f717153b96b44acd841 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Wed, 16 Feb 2022 08:32:50 -0500 Subject: [PATCH 01/51] added example files for offline maps for testing --- .../com/geeksville/mesh/ui/OfflineActivity.kt | 425 ++++++++++++++++++ app/src/main/res/layout/activity_offline.xml | 92 ++++ .../main/res/layout/item_gesture_alert.xml | 14 + 3 files changed, 531 insertions(+) create mode 100644 app/src/main/java/com/geeksville/mesh/ui/OfflineActivity.kt create mode 100644 app/src/main/res/layout/activity_offline.xml create mode 100644 app/src/main/res/layout/item_gesture_alert.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/OfflineActivity.kt b/app/src/main/java/com/geeksville/mesh/ui/OfflineActivity.kt new file mode 100644 index 000000000..738fe75e1 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/OfflineActivity.kt @@ -0,0 +1,425 @@ +package com.geeksville.mesh.ui + +import android.annotation.SuppressLint +import android.graphics.Color +import android.os.Bundle +import android.os.Handler +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.annotation.NonNull +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.geeksville.mesh.R +import com.geeksville.mesh.databinding.ActivityOfflineBinding +import com.mapbox.bindgen.Value +import com.mapbox.common.* +import com.mapbox.geojson.Point +import com.mapbox.maps.* +import com.mapbox.maps.plugin.annotation.annotations +import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions +import com.mapbox.maps.plugin.annotation.generated.createCircleAnnotationManager +import java.util.* + +/** + * Example app that shows how to use OfflineManager and TileStore to + * download regions for offline use. + * + * 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. + */ +class OfflineActivity : AppCompatActivity() { + 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)) + ) + } + } + private val resourceOptions: ResourceOptions by lazy { + ResourceOptions.Builder().applyDefaultParams(this).tileStore(tileStore).build() + } + private val offlineManager: OfflineManager by lazy { + OfflineManager(resourceOptions) + } + private val offlineLogsAdapter: OfflineLogsAdapter by lazy { + OfflineLogsAdapter() + } + private var mapView: MapView? = null + private lateinit var handler: Handler + private lateinit var binding: ActivityOfflineBinding + private var stylePackCancelable: Cancelable? = null + private var tilePackCancelable: Cancelable? = null + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityOfflineBinding.inflate(layoutInflater) + setContentView(binding.root) + handler = Handler() + + // Initialize a logger that writes into the recycler view + binding.recycler.layoutManager = LinearLayoutManager(this) + binding.recycler.adapter = offlineLogsAdapter + + prepareDownloadButton() + } + + private fun prepareDownloadButton() { + updateButton("DOWNLOAD") { + downloadOfflineRegion() + } + } + + private fun prepareCancelButton() { + updateButton("CANCEL DOWNLOAD") { + stylePackCancelable?.cancel() + tilePackCancelable?.cancel() + prepareDownloadButton() + } + } + + private fun prepareViewMapButton() { + // Disable network stack, so that the map can only load from downloaded region. + OfflineSwitch.getInstance().isMapboxStackConnected = false + logInfoMessage("Mapbox network stack disabled.") + handler.post { + updateButton("VIEW MAP") { + // create a Mapbox MapView + + // Note that the MapView must be initialised with the same TileStore that is used to create + // the tile regions. (i.e. the tileStorePath must be consistent). + + // If user did not assign the tile store path specifically during the tile region download + // and the map initialisation period, the default tile store path will be used and + // no extra action is needed. + mapView = MapView(this@OfflineActivity).also { mapview -> + val mapboxMap = mapview.getMapboxMap() + mapboxMap.setCamera(CameraOptions.Builder().zoom(ZOOM).center(TOKYO).build()) + mapboxMap.loadStyleUri(Style.OUTDOORS) { + // Add a circle annotation to the offline geometry. + mapview.annotations.createCircleAnnotationManager().create( + CircleAnnotationOptions() + .withPoint(TOKYO) + .withCircleColor(Color.RED) + ) + } + } + binding.container.addView(mapView) + mapView?.onStart() + prepareShowDownloadedRegionButton() + } + } + } + + private fun prepareShowDownloadedRegionButton() { + updateButton("SHOW DOWNLOADED REGIONS") { + showDownloadedRegions() + prepareRemoveOfflineRegionButton() + } + } + + private fun prepareRemoveOfflineRegionButton() { + updateButton("REMOVE DOWNLOADED REGIONS") { + removeOfflineRegions() + showDownloadedRegions() + binding.container.removeAllViews() + + // Re-enable the Mapbox network stack, so that the new offline region download can succeed. + OfflineSwitch.getInstance().isMapboxStackConnected = true + logInfoMessage("Mapbox network stack enabled.") + + prepareDownloadButton() + } + } + + private fun updateButton(text: String, listener: View.OnClickListener) { + binding.button.text = text + binding.button.setOnClickListener(listener) + } + + 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. + stylePackCancelable = offlineManager.loadStylePack( + Style.OUTDOORS, + // 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 + updateStylePackDownloadProgress( + progress.completedResourceCount, + progress.requiredResourceCount, + "StylePackLoadProgress: $progress" + ) + }, + { expected -> + if (expected.isValue) { + expected.value?.let { stylePack -> + // Style pack download finishes successfully + logSuccessMessage("StylePack downloaded: $stylePack") + if (binding.tilePackDownloadProgress.progress == binding.tilePackDownloadProgress.max) { + prepareViewMapButton() + } else { + logInfoMessage("Waiting for tile region download to be finished.") + } + } + } + expected.error?.let { + // Handle error occurred during the style pack download. + logErrorMessage("StylePackError: $it") + } + } + ) + + // - - - - - - - - + + // 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. + val tilesetDescriptor = offlineManager.createTilesetDescriptor( + TilesetDescriptorOptions.Builder() + .styleURI(Style.OUTDOORS) + .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( + TILE_REGION_ID, + TileRegionLoadOptions.Builder() + .geometry(TOKYO) + .descriptors(listOf(tilesetDescriptor)) + .metadata(Value(TILE_REGION_METADATA)) + .acceptExpired(true) + .networkRestriction(NetworkRestriction.NONE) + .build(), + { progress -> + updateTileRegionDownloadProgress( + progress.completedResourceCount, + progress.requiredResourceCount, + "TileRegionLoadProgress: $progress" + ) + } + ) { expected -> + if (expected.isValue) { + // Tile pack download finishes successfully + expected.value?.let { region -> + logSuccessMessage("TileRegion downloaded: $region") + if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { + prepareViewMapButton() + } else { + logInfoMessage("Waiting for style pack download to be finished.") + } + } + } + expected.error?.let { + // Handle error occurred during the tile region download. + logErrorMessage("TileRegionError: $it") + } + } + prepareCancelButton() + } + + private fun showDownloadedRegions() { + // Get a list of tile regions that are currently available. + tileStore.getAllTileRegions { expected -> + if (expected.isValue) { + expected.value?.let { tileRegionList -> + logInfoMessage("Existing tile regions: $tileRegionList") + } + } + expected.error?.let { tileRegionError -> + logErrorMessage("TileRegionError: $tileRegionError") + } + } + // Get a list of style packs that are currently available. + offlineManager.getAllStylePacks { expected -> + if (expected.isValue) { + expected.value?.let { stylePackList -> + logInfoMessage("Existing style packs: $stylePackList") + } + } + expected.error?.let { stylePackError -> + logErrorMessage("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. + offlineManager.removeStylePack(Style.OUTDOORS) + + MapboxMap.clearData(resourceOptions) { + it.error?.let { error -> + logErrorMessage(error) + } + } + + // Reset progressbar. + updateStylePackDownloadProgress(0, 0) + updateTileRegionDownloadProgress(0, 0) + } + + private fun updateStylePackDownloadProgress(progress: Long, max: Long, message: String? = null) { + binding.stylePackDownloadProgress.max = max.toInt() + binding.stylePackDownloadProgress.progress = progress.toInt() + message?.let { + offlineLogsAdapter.addLog(OfflineLog.StylePackProgress(it)) + } + } + + private fun updateTileRegionDownloadProgress(progress: Long, max: Long, message: String? = null) { + binding.tilePackDownloadProgress.max = max.toInt() + binding.tilePackDownloadProgress.progress = progress.toInt() + message?.let { + offlineLogsAdapter.addLog(OfflineLog.TilePackProgress(it)) + } + } + + private fun logInfoMessage(message: String) { + offlineLogsAdapter.addLog(OfflineLog.Info(message)) + } + + private fun logErrorMessage(message: String) { + offlineLogsAdapter.addLog(OfflineLog.Error(message)) + } + + private fun logSuccessMessage(message: String) { + offlineLogsAdapter.addLog(OfflineLog.Success(message)) + } + + override fun onStart() { + super.onStart() + mapView?.onStart() + } + + override fun onStop() { + super.onStop() + mapView?.onStop() + } + + override fun onDestroy() { + super.onDestroy() + // Cancel the current downloading jobs + stylePackCancelable?.cancel() + tilePackCancelable?.cancel() + // Remove downloaded style packs and tile regions. + removeOfflineRegions() + // Bring back the network connectivity when exiting the OfflineActivity. + OfflineSwitch.getInstance().isMapboxStackConnected = true + mapView?.onDestroy() + } + + private class OfflineLogsAdapter : RecyclerView.Adapter() { + private var isUpdating: Boolean = false + private val updateHandler = Handler() + private val logs = ArrayList() + + @SuppressLint("NotifyDataSetChanged") + private val updateRunnable = Runnable { + notifyDataSetChanged() + isUpdating = false + } + + class ViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) { + internal var alertMessageTv: TextView = view.findViewById(R.id.alert_message) + } + + @NonNull + override fun onCreateViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder { + val view = + LayoutInflater.from(parent.context).inflate(R.layout.item_gesture_alert, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(@NonNull holder: ViewHolder, position: Int) { + val alert = logs[position] + holder.alertMessageTv.text = alert.message + holder.alertMessageTv.setTextColor( + ContextCompat.getColor(holder.alertMessageTv.context, alert.color) + ) + } + + override fun getItemCount(): Int { + return logs.size + } + + fun addLog(alert: OfflineLog) { + when (alert) { + is OfflineLog.Error -> Log.e(TAG, alert.message) + else -> Log.d(TAG, alert.message) + } + logs.add(0, alert) + if (!isUpdating) { + isUpdating = true + updateHandler.postDelayed(updateRunnable, 250) + } + } + } + + 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) + } + + companion object { + private const val TAG = "OfflineActivity" + private const val ZOOM = 12.0 + private val TOKYO: Point = Point.fromLngLat(139.769305, 35.682027) + 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" + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_offline.xml b/app/src/main/res/layout/activity_offline.xml new file mode 100644 index 000000000..b225e8e78 --- /dev/null +++ b/app/src/main/res/layout/activity_offline.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_gesture_alert.xml b/app/src/main/res/layout/item_gesture_alert.xml new file mode 100644 index 000000000..797354b11 --- /dev/null +++ b/app/src/main/res/layout/item_gesture_alert.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file From 955d1de0c82a27294e89c8f8b48bd169cbdcedcb Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 16 Feb 2022 10:22:59 -0500 Subject: [PATCH 02/51] Added long click listener to add new "point" to map for offline region downloading !WIP --- .../com/geeksville/mesh/ui/MapFragment.kt | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 2d7993474..62d535747 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -1,5 +1,7 @@ package com.geeksville.mesh.ui +import android.app.AlertDialog +import android.content.Intent import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater @@ -31,6 +33,7 @@ import com.mapbox.maps.extension.style.sources.addSource import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions import com.mapbox.maps.plugin.animation.flyTo +import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures import dagger.hilt.android.AndroidEntryPoint @@ -45,6 +48,9 @@ class MapFragment : ScreenFragment("Map"), Logging { private val labelLayerId = "label-layer" private val markerImageId = "my-marker-image" + + private val userTouchPositionId = "user-touch-position" + private val userTouchLayerId = "user-touch-layer" private var nodePositions = GeoJsonSource(GeoJsonSource.Builder(nodeSourceId)) private val nodeLayer = SymbolLayer(nodeLayerId, nodeSourceId) @@ -52,6 +58,11 @@ class MapFragment : ScreenFragment("Map"), Logging { .iconAnchor(IconAnchor.BOTTOM) .iconAllowOverlap(true) + private val userTouchLayer = SymbolLayer(userTouchLayerId, userTouchPositionId) + .iconImage(markerImageId) + .iconAnchor(IconAnchor.BOTTOM) + .iconAllowOverlap(true) + private val labelLayer = SymbolLayer(labelLayerId, nodeSourceId) .textField(Expression.get("name")) .textSize(12.0) @@ -179,6 +190,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } v.gestures.rotateEnabled = false + v.gestures.addOnMapLongClickListener(this.longClick) // Provide initial positions model.nodeDB.nodes.value?.let { nodes -> @@ -195,6 +207,33 @@ class MapFragment : ScreenFragment("Map"), Logging { } } } + + //TODO Create list of touch positions that can be updated on new press + private val longClick = OnMapLongClickListener { + val userDefinedPointImg = + ContextCompat.getDrawable(requireActivity(), R.drawable.ic_twotone_person_24)!! + .toBitmap() + val point = Point.fromLngLat(it.longitude(), it.latitude()) + val userTouchPosition = GeoJsonSource(GeoJsonSource.Builder(userTouchPositionId)) + userTouchPosition.geometry(point) + + mapView?.getMapboxMap()?.getStyle()?.let { style -> + style.addImage("userImage", userDefinedPointImg) + style.addSource(userTouchPosition) + style.addLayer(userTouchLayer) + + } + return@OnMapLongClickListener true + } + + 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) + } } From ffe66ec81c4aa40d66db1f8ef187abea1b728f86 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 16 Feb 2022 13:56:17 -0500 Subject: [PATCH 03/51] Fixed issue with adding multiple layers instead of moving touch position --- .../com/geeksville/mesh/ui/MapFragment.kt | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 62d535747..6c4bc4496 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -53,6 +53,9 @@ class MapFragment : ScreenFragment("Map"), Logging { private val userTouchLayerId = "user-touch-layer" private var nodePositions = GeoJsonSource(GeoJsonSource.Builder(nodeSourceId)) + private val userTouchPosition = GeoJsonSource(GeoJsonSource.Builder(userTouchPositionId)) + + private val nodeLayer = SymbolLayer(nodeLayerId, nodeSourceId) .iconImage(markerImageId) .iconAnchor(IconAnchor.BOTTOM) @@ -208,19 +211,23 @@ class MapFragment : ScreenFragment("Map"), Logging { } } - //TODO Create list of touch positions that can be updated on new press + /** + * OnLongClick of the map set a position marker. + */ private val longClick = OnMapLongClickListener { val userDefinedPointImg = ContextCompat.getDrawable(requireActivity(), R.drawable.ic_twotone_person_24)!! .toBitmap() val point = Point.fromLngLat(it.longitude(), it.latitude()) - val userTouchPosition = GeoJsonSource(GeoJsonSource.Builder(userTouchPositionId)) - userTouchPosition.geometry(point) mapView?.getMapboxMap()?.getStyle()?.let { style -> - style.addImage("userImage", userDefinedPointImg) - style.addSource(userTouchPosition) - style.addLayer(userTouchLayer) + userTouchPosition.geometry(point) + + if (!style.styleLayerExists(userTouchLayerId)) { + style.addImage("userImage", userDefinedPointImg) + style.addSource(userTouchPosition) + style.addLayer(userTouchLayer) + } } return@OnMapLongClickListener true From 2730641d92915b26f8c5932ed05efa8210395cc6 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Thu, 17 Feb 2022 10:16:58 -0500 Subject: [PATCH 04/51] Doing some rough testing --- .../com/geeksville/mesh/ui/MapFragment.kt | 257 +++++++++++++++++- 1 file changed, 255 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 6c4bc4496..6ab2443bf 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -4,6 +4,7 @@ import android.app.AlertDialog import android.content.Intent import android.graphics.Color import android.os.Bundle +import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -16,10 +17,14 @@ import com.geeksville.android.GeeksvilleApplication import com.geeksville.android.Logging import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.R +import com.geeksville.mesh.databinding.ActivityOfflineBinding import com.geeksville.mesh.model.UIViewModel import com.geeksville.util.formatAgo +import com.mapbox.bindgen.Value +import com.mapbox.common.* import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection +import com.mapbox.geojson.Geometry import com.mapbox.geojson.Point import com.mapbox.maps.* import com.mapbox.maps.dsl.cameraOptions @@ -33,14 +38,41 @@ import com.mapbox.maps.extension.style.sources.addSource import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions import com.mapbox.maps.plugin.animation.flyTo +import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures +import com.mapbox.maps.viewannotation.ViewAnnotationManager +import com.mapbox.maps.viewannotation.viewAnnotationOptions import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MapFragment : ScreenFragment("Map"), Logging { + + 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)) + ) + } + } + + 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 + private lateinit var binding: ActivityOfflineBinding + + private lateinit var point: Geometry + private val model: UIViewModel by activityViewModels() private val nodeSourceId = "node-positions" @@ -48,11 +80,16 @@ class MapFragment : ScreenFragment("Map"), Logging { private val labelLayerId = "label-layer" private val markerImageId = "my-marker-image" + private var stylePackCancelable: Cancelable? = null + private var tilePackCancelable: Cancelable? = null + private val userTouchPositionId = "user-touch-position" private val userTouchLayerId = "user-touch-layer" private var nodePositions = GeoJsonSource(GeoJsonSource.Builder(nodeSourceId)) + private lateinit var viewAnnotationManager: ViewAnnotationManager + private val userTouchPosition = GeoJsonSource(GeoJsonSource.Builder(userTouchPositionId)) @@ -157,6 +194,84 @@ class MapFragment : ScreenFragment("Map"), Logging { var mapView: MapView? = null + + private fun prepareRemoveOfflineRegionButton() { + downloadButton("REMOVE DOWNLOADED REGIONS") { + removeOfflineRegions() + showDownloadedRegions() + binding.container.removeAllViews() + + // Re-enable the Mapbox network stack, so that the new offline region download can succeed. + OfflineSwitch.getInstance().isMapboxStackConnected = true + debug("Mapbox network stack enabled.") + + prepareDownloadButton() + } + } + + private fun prepareDownloadButton() { + downloadButton("DOWNLOAD") { + downloadOfflineRegion() + } + } + + + 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. + offlineManager.removeStylePack(Style.OUTDOORS) + + MapboxMap.clearData(resourceOptions) { + it.error?.let { error -> + debug(error) + } + } + } + + + private fun downloadButton(text: String, listener: View.OnClickListener) { + binding.button.text = text + binding.button.setOnClickListener(listener) + } + /** * Mapbox native code can crash painfully if you ever call a mapbox view function while the view is not actively being show */ @@ -194,6 +309,7 @@ class MapFragment : ScreenFragment("Map"), Logging { v.gestures.rotateEnabled = false v.gestures.addOnMapLongClickListener(this.longClick) + // v.gestures.addOnMapClickListener(this.click) // Provide initial positions model.nodeDB.nodes.value?.let { nodes -> @@ -206,11 +322,135 @@ class MapFragment : ScreenFragment("Map"), Logging { if (isViewVisible) onNodesChanged(map, nodes.values) }) + //viewAnnotationManager = v.viewAnnotationManager zoomToNodes(map) } } } + + 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. + stylePackCancelable = offlineManager.loadStylePack( + Style.OUTDOORS, + // 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 +// updateStylePackDownloadProgress( +// progress.completedResourceCount, +// progress.requiredResourceCount, +// "StylePackLoadProgress: $progress" +// ) + }, + { expected -> + if (expected.isValue) { + expected.value?.let { stylePack -> + // Style pack download finishes successfully + // logSuccessMessage("StylePack downloaded: $stylePack") + // if (binding.tilePackDownloadProgress.progress == binding.tilePackDownloadProgress.max) { + // prepareViewMapButton() + // } else { + // logInfoMessage("Waiting for tile region download to be finished.") + // } + } + } + expected.error?.let { + // Handle error occurred during the style pack download. + // logErrorMessage("StylePackError: $it") + } + } + ) + + // - - - - - - - - + + // 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. + val tilesetDescriptor = offlineManager.createTilesetDescriptor( + TilesetDescriptorOptions.Builder() + .styleURI(Style.OUTDOORS) + .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( + TILE_REGION_ID, + TileRegionLoadOptions.Builder() + .geometry(point) + .descriptors(listOf(tilesetDescriptor)) + .metadata(Value(TILE_REGION_METADATA)) + .acceptExpired(true) + .networkRestriction(NetworkRestriction.NONE) + .build(), + { progress -> + //updateTileRegionDownloadProgress( + // progress.completedResourceCount, + // progress.requiredResourceCount, + // "TileRegionLoadProgress: $progress" + // ) + } + ) { expected -> + if (expected.isValue) { + // Tile pack download finishes successfully + expected.value?.let { region -> + // logSuccessMessage("TileRegion downloaded: $region") + if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { + // prepareViewMapButton() + } else { + // logInfoMessage("Waiting for style pack download to be finished.") + } + } + } + expected.error?.let { + // Handle error occurred during the tile region download. + // logErrorMessage("TileRegionError: $it") + } + } + // prepareCancelButton() + } + + +// private fun addViewAnnotation(point: Point) { +// viewAnnotationManager?.addViewAnnotation( +// resId = R.layout.user_icon_menu, +// options = viewAnnotationOptions { +// geometry(point) +// } +// ) +// } + /** * OnLongClick of the map set a position marker. */ @@ -218,7 +458,7 @@ class MapFragment : ScreenFragment("Map"), Logging { val userDefinedPointImg = ContextCompat.getDrawable(requireActivity(), R.drawable.ic_twotone_person_24)!! .toBitmap() - val point = Point.fromLngLat(it.longitude(), it.latitude()) + point = Point.fromLngLat(it.longitude(), it.latitude()) mapView?.getMapboxMap()?.getStyle()?.let { style -> userTouchPosition.geometry(point) @@ -228,11 +468,16 @@ class MapFragment : ScreenFragment("Map"), Logging { style.addSource(userTouchPosition) style.addLayer(userTouchLayer) } - } return@OnMapLongClickListener true } +// private val click = OnMapClickListener { +// val point = Point.fromLngLat(it.longitude(), it.latitude()) +// addViewAnnotation(point) +// return@OnMapClickListener true +// } + 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) @@ -241,6 +486,14 @@ class MapFragment : ScreenFragment("Map"), Logging { class StylePackProgress(message: String) : OfflineLog(message, android.R.color.holo_orange_dark) } + + companion object { + private const val TAG = "OfflineActivity" + 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" + } } From d80302e274fffa7688c58d4e3bfa3749d3d6d073 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Thu, 17 Feb 2022 10:38:33 -0500 Subject: [PATCH 05/51] Added updated mapview.xml --- app/src/main/res/layout/map_view.xml | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml index 132cb4ee9..71cd660ec 100644 --- a/app/src/main/res/layout/map_view.xml +++ b/app/src/main/res/layout/map_view.xml @@ -1,8 +1,24 @@ - - \ No newline at end of file + android:layout_height="match_parent"> + + + + + + + + + + + + + \ No newline at end of file From 1d0bc127988c086b25fe80f578b9d5dabd556571 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Thu, 17 Feb 2022 10:38:46 -0500 Subject: [PATCH 06/51] Added temp file --- app/src/main/res/layout/user_icon_menu.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 app/src/main/res/layout/user_icon_menu.xml diff --git a/app/src/main/res/layout/user_icon_menu.xml b/app/src/main/res/layout/user_icon_menu.xml new file mode 100644 index 000000000..88d8f5ec1 --- /dev/null +++ b/app/src/main/res/layout/user_icon_menu.xml @@ -0,0 +1,19 @@ + + + + + + From 23c748ddc29736487be55fc0845171940833c420 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 17 Feb 2022 11:41:05 -0500 Subject: [PATCH 07/51] Making some changes. There is a TODO list I am working through --- .../com/geeksville/mesh/ui/MapFragment.kt | 66 +++++++------------ app/src/main/res/layout/map_view.xml | 20 +++--- 2 files changed, 33 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 6ab2443bf..d002a8fd2 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -18,6 +18,8 @@ import com.geeksville.android.Logging import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.R import com.geeksville.mesh.databinding.ActivityOfflineBinding +import com.geeksville.mesh.databinding.MapNotAllowedBinding +import com.geeksville.mesh.databinding.MapViewBinding import com.geeksville.mesh.model.UIViewModel import com.geeksville.util.formatAgo import com.mapbox.bindgen.Value @@ -28,6 +30,7 @@ import com.mapbox.geojson.Geometry import com.mapbox.geojson.Point import com.mapbox.maps.* import com.mapbox.maps.dsl.cameraOptions +import com.mapbox.maps.extension.style.expressions.dsl.generated.id import com.mapbox.maps.extension.style.expressions.generated.Expression import com.mapbox.maps.extension.style.layers.addLayer import com.mapbox.maps.extension.style.layers.generated.SymbolLayer @@ -69,7 +72,9 @@ class MapFragment : ScreenFragment("Map"), Logging { } private lateinit var handler: Handler - private lateinit var binding: ActivityOfflineBinding + private lateinit var binding: MapViewBinding + private lateinit var mapNotAllowedBinding: MapNotAllowedBinding + private lateinit var viewAnnotationManager: ViewAnnotationManager private lateinit var point: Geometry @@ -88,7 +93,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private val userTouchLayerId = "user-touch-layer" private var nodePositions = GeoJsonSource(GeoJsonSource.Builder(nodeSourceId)) - private lateinit var viewAnnotationManager: ViewAnnotationManager private val userTouchPosition = GeoJsonSource(GeoJsonSource.Builder(userTouchPositionId)) @@ -179,42 +183,21 @@ class MapFragment : ScreenFragment("Map"), Logging { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { // We can't allow mapbox if user doesn't want analytics - val id = - if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) { - // Mapbox Access token - R.layout.map_view - } else { - R.layout.map_not_allowed - } - - return inflater.inflate(id, container, false) + 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 + } } var mapView: MapView? = null - private fun prepareRemoveOfflineRegionButton() { - downloadButton("REMOVE DOWNLOADED REGIONS") { - removeOfflineRegions() - showDownloadedRegions() - binding.container.removeAllViews() - - // Re-enable the Mapbox network stack, so that the new offline region download can succeed. - OfflineSwitch.getInstance().isMapboxStackConnected = true - debug("Mapbox network stack enabled.") - - prepareDownloadButton() - } - } - - private fun prepareDownloadButton() { - downloadButton("DOWNLOAD") { - downloadOfflineRegion() - } - } - private fun showDownloadedRegions() { // Get a list of tile regions that are currently available. @@ -266,12 +249,6 @@ class MapFragment : ScreenFragment("Map"), Logging { } } - - private fun downloadButton(text: String, listener: View.OnClickListener) { - binding.button.text = text - binding.button.setOnClickListener(listener) - } - /** * Mapbox native code can crash painfully if you ever call a mapbox view function while the view is not actively being show */ @@ -281,6 +258,9 @@ class MapFragment : ScreenFragment("Map"), Logging { override fun onViewCreated(viewIn: View, savedInstanceState: Bundle?) { super.onViewCreated(viewIn, savedInstanceState) + binding.fabStyleToggle.setOnClickListener { + downloadOfflineRegion() + } // We might not have a real mapview if running with analytics if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) { val vIn = viewIn.findViewById(R.id.mapView) @@ -426,11 +406,11 @@ class MapFragment : ScreenFragment("Map"), Logging { // Tile pack download finishes successfully expected.value?.let { region -> // logSuccessMessage("TileRegion downloaded: $region") - if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { - // prepareViewMapButton() - } else { - // logInfoMessage("Waiting for style pack download to be finished.") - } + // if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { + // prepareViewMapButton() + // } else { + // logInfoMessage("Waiting for style pack download to be finished.") + // } } } expected.error?.let { diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml index 71cd660ec..9178f0b8b 100644 --- a/app/src/main/res/layout/map_view.xml +++ b/app/src/main/res/layout/map_view.xml @@ -5,20 +5,20 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - - - - + \ No newline at end of file From 52daa14658b360b750e37d02174e9e72a5f758d7 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 17 Feb 2022 15:10:10 -0500 Subject: [PATCH 08/51] removed example files --- .../com/geeksville/mesh/ui/OfflineActivity.kt | 425 ------------------ app/src/main/res/layout/activity_offline.xml | 92 ---- 2 files changed, 517 deletions(-) delete mode 100644 app/src/main/java/com/geeksville/mesh/ui/OfflineActivity.kt delete mode 100644 app/src/main/res/layout/activity_offline.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/OfflineActivity.kt b/app/src/main/java/com/geeksville/mesh/ui/OfflineActivity.kt deleted file mode 100644 index 738fe75e1..000000000 --- a/app/src/main/java/com/geeksville/mesh/ui/OfflineActivity.kt +++ /dev/null @@ -1,425 +0,0 @@ -package com.geeksville.mesh.ui - -import android.annotation.SuppressLint -import android.graphics.Color -import android.os.Bundle -import android.os.Handler -import android.util.Log -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.annotation.NonNull -import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.ContextCompat -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.geeksville.mesh.R -import com.geeksville.mesh.databinding.ActivityOfflineBinding -import com.mapbox.bindgen.Value -import com.mapbox.common.* -import com.mapbox.geojson.Point -import com.mapbox.maps.* -import com.mapbox.maps.plugin.annotation.annotations -import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions -import com.mapbox.maps.plugin.annotation.generated.createCircleAnnotationManager -import java.util.* - -/** - * Example app that shows how to use OfflineManager and TileStore to - * download regions for offline use. - * - * 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. - */ -class OfflineActivity : AppCompatActivity() { - 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)) - ) - } - } - private val resourceOptions: ResourceOptions by lazy { - ResourceOptions.Builder().applyDefaultParams(this).tileStore(tileStore).build() - } - private val offlineManager: OfflineManager by lazy { - OfflineManager(resourceOptions) - } - private val offlineLogsAdapter: OfflineLogsAdapter by lazy { - OfflineLogsAdapter() - } - private var mapView: MapView? = null - private lateinit var handler: Handler - private lateinit var binding: ActivityOfflineBinding - private var stylePackCancelable: Cancelable? = null - private var tilePackCancelable: Cancelable? = null - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityOfflineBinding.inflate(layoutInflater) - setContentView(binding.root) - handler = Handler() - - // Initialize a logger that writes into the recycler view - binding.recycler.layoutManager = LinearLayoutManager(this) - binding.recycler.adapter = offlineLogsAdapter - - prepareDownloadButton() - } - - private fun prepareDownloadButton() { - updateButton("DOWNLOAD") { - downloadOfflineRegion() - } - } - - private fun prepareCancelButton() { - updateButton("CANCEL DOWNLOAD") { - stylePackCancelable?.cancel() - tilePackCancelable?.cancel() - prepareDownloadButton() - } - } - - private fun prepareViewMapButton() { - // Disable network stack, so that the map can only load from downloaded region. - OfflineSwitch.getInstance().isMapboxStackConnected = false - logInfoMessage("Mapbox network stack disabled.") - handler.post { - updateButton("VIEW MAP") { - // create a Mapbox MapView - - // Note that the MapView must be initialised with the same TileStore that is used to create - // the tile regions. (i.e. the tileStorePath must be consistent). - - // If user did not assign the tile store path specifically during the tile region download - // and the map initialisation period, the default tile store path will be used and - // no extra action is needed. - mapView = MapView(this@OfflineActivity).also { mapview -> - val mapboxMap = mapview.getMapboxMap() - mapboxMap.setCamera(CameraOptions.Builder().zoom(ZOOM).center(TOKYO).build()) - mapboxMap.loadStyleUri(Style.OUTDOORS) { - // Add a circle annotation to the offline geometry. - mapview.annotations.createCircleAnnotationManager().create( - CircleAnnotationOptions() - .withPoint(TOKYO) - .withCircleColor(Color.RED) - ) - } - } - binding.container.addView(mapView) - mapView?.onStart() - prepareShowDownloadedRegionButton() - } - } - } - - private fun prepareShowDownloadedRegionButton() { - updateButton("SHOW DOWNLOADED REGIONS") { - showDownloadedRegions() - prepareRemoveOfflineRegionButton() - } - } - - private fun prepareRemoveOfflineRegionButton() { - updateButton("REMOVE DOWNLOADED REGIONS") { - removeOfflineRegions() - showDownloadedRegions() - binding.container.removeAllViews() - - // Re-enable the Mapbox network stack, so that the new offline region download can succeed. - OfflineSwitch.getInstance().isMapboxStackConnected = true - logInfoMessage("Mapbox network stack enabled.") - - prepareDownloadButton() - } - } - - private fun updateButton(text: String, listener: View.OnClickListener) { - binding.button.text = text - binding.button.setOnClickListener(listener) - } - - 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. - stylePackCancelable = offlineManager.loadStylePack( - Style.OUTDOORS, - // 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 - updateStylePackDownloadProgress( - progress.completedResourceCount, - progress.requiredResourceCount, - "StylePackLoadProgress: $progress" - ) - }, - { expected -> - if (expected.isValue) { - expected.value?.let { stylePack -> - // Style pack download finishes successfully - logSuccessMessage("StylePack downloaded: $stylePack") - if (binding.tilePackDownloadProgress.progress == binding.tilePackDownloadProgress.max) { - prepareViewMapButton() - } else { - logInfoMessage("Waiting for tile region download to be finished.") - } - } - } - expected.error?.let { - // Handle error occurred during the style pack download. - logErrorMessage("StylePackError: $it") - } - } - ) - - // - - - - - - - - - - // 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. - val tilesetDescriptor = offlineManager.createTilesetDescriptor( - TilesetDescriptorOptions.Builder() - .styleURI(Style.OUTDOORS) - .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( - TILE_REGION_ID, - TileRegionLoadOptions.Builder() - .geometry(TOKYO) - .descriptors(listOf(tilesetDescriptor)) - .metadata(Value(TILE_REGION_METADATA)) - .acceptExpired(true) - .networkRestriction(NetworkRestriction.NONE) - .build(), - { progress -> - updateTileRegionDownloadProgress( - progress.completedResourceCount, - progress.requiredResourceCount, - "TileRegionLoadProgress: $progress" - ) - } - ) { expected -> - if (expected.isValue) { - // Tile pack download finishes successfully - expected.value?.let { region -> - logSuccessMessage("TileRegion downloaded: $region") - if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { - prepareViewMapButton() - } else { - logInfoMessage("Waiting for style pack download to be finished.") - } - } - } - expected.error?.let { - // Handle error occurred during the tile region download. - logErrorMessage("TileRegionError: $it") - } - } - prepareCancelButton() - } - - private fun showDownloadedRegions() { - // Get a list of tile regions that are currently available. - tileStore.getAllTileRegions { expected -> - if (expected.isValue) { - expected.value?.let { tileRegionList -> - logInfoMessage("Existing tile regions: $tileRegionList") - } - } - expected.error?.let { tileRegionError -> - logErrorMessage("TileRegionError: $tileRegionError") - } - } - // Get a list of style packs that are currently available. - offlineManager.getAllStylePacks { expected -> - if (expected.isValue) { - expected.value?.let { stylePackList -> - logInfoMessage("Existing style packs: $stylePackList") - } - } - expected.error?.let { stylePackError -> - logErrorMessage("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. - offlineManager.removeStylePack(Style.OUTDOORS) - - MapboxMap.clearData(resourceOptions) { - it.error?.let { error -> - logErrorMessage(error) - } - } - - // Reset progressbar. - updateStylePackDownloadProgress(0, 0) - updateTileRegionDownloadProgress(0, 0) - } - - private fun updateStylePackDownloadProgress(progress: Long, max: Long, message: String? = null) { - binding.stylePackDownloadProgress.max = max.toInt() - binding.stylePackDownloadProgress.progress = progress.toInt() - message?.let { - offlineLogsAdapter.addLog(OfflineLog.StylePackProgress(it)) - } - } - - private fun updateTileRegionDownloadProgress(progress: Long, max: Long, message: String? = null) { - binding.tilePackDownloadProgress.max = max.toInt() - binding.tilePackDownloadProgress.progress = progress.toInt() - message?.let { - offlineLogsAdapter.addLog(OfflineLog.TilePackProgress(it)) - } - } - - private fun logInfoMessage(message: String) { - offlineLogsAdapter.addLog(OfflineLog.Info(message)) - } - - private fun logErrorMessage(message: String) { - offlineLogsAdapter.addLog(OfflineLog.Error(message)) - } - - private fun logSuccessMessage(message: String) { - offlineLogsAdapter.addLog(OfflineLog.Success(message)) - } - - override fun onStart() { - super.onStart() - mapView?.onStart() - } - - override fun onStop() { - super.onStop() - mapView?.onStop() - } - - override fun onDestroy() { - super.onDestroy() - // Cancel the current downloading jobs - stylePackCancelable?.cancel() - tilePackCancelable?.cancel() - // Remove downloaded style packs and tile regions. - removeOfflineRegions() - // Bring back the network connectivity when exiting the OfflineActivity. - OfflineSwitch.getInstance().isMapboxStackConnected = true - mapView?.onDestroy() - } - - private class OfflineLogsAdapter : RecyclerView.Adapter() { - private var isUpdating: Boolean = false - private val updateHandler = Handler() - private val logs = ArrayList() - - @SuppressLint("NotifyDataSetChanged") - private val updateRunnable = Runnable { - notifyDataSetChanged() - isUpdating = false - } - - class ViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) { - internal var alertMessageTv: TextView = view.findViewById(R.id.alert_message) - } - - @NonNull - override fun onCreateViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder { - val view = - LayoutInflater.from(parent.context).inflate(R.layout.item_gesture_alert, parent, false) - return ViewHolder(view) - } - - override fun onBindViewHolder(@NonNull holder: ViewHolder, position: Int) { - val alert = logs[position] - holder.alertMessageTv.text = alert.message - holder.alertMessageTv.setTextColor( - ContextCompat.getColor(holder.alertMessageTv.context, alert.color) - ) - } - - override fun getItemCount(): Int { - return logs.size - } - - fun addLog(alert: OfflineLog) { - when (alert) { - is OfflineLog.Error -> Log.e(TAG, alert.message) - else -> Log.d(TAG, alert.message) - } - logs.add(0, alert) - if (!isUpdating) { - isUpdating = true - updateHandler.postDelayed(updateRunnable, 250) - } - } - } - - 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) - } - - companion object { - private const val TAG = "OfflineActivity" - private const val ZOOM = 12.0 - private val TOKYO: Point = Point.fromLngLat(139.769305, 35.682027) - 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" - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_offline.xml b/app/src/main/res/layout/activity_offline.xml deleted file mode 100644 index b225e8e78..000000000 --- a/app/src/main/res/layout/activity_offline.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 34a3900936011aad23d49ad0463c898b9b31940a Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 17 Feb 2022 15:22:22 -0500 Subject: [PATCH 09/51] Making slow progress --- .../com/geeksville/mesh/ui/MapFragment.kt | 88 ++++++++++++------- .../drawable/baseline_layers_white_24dp.xml | 6 ++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 61 insertions(+), 34 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_layers_white_24dp.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index d002a8fd2..b123f2ac6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -1,7 +1,5 @@ package com.geeksville.mesh.ui -import android.app.AlertDialog -import android.content.Intent import android.graphics.Color import android.os.Bundle import android.os.Handler @@ -17,7 +15,6 @@ import com.geeksville.android.GeeksvilleApplication import com.geeksville.android.Logging import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.R -import com.geeksville.mesh.databinding.ActivityOfflineBinding import com.geeksville.mesh.databinding.MapNotAllowedBinding import com.geeksville.mesh.databinding.MapViewBinding import com.geeksville.mesh.model.UIViewModel @@ -30,7 +27,6 @@ import com.mapbox.geojson.Geometry import com.mapbox.geojson.Point import com.mapbox.maps.* import com.mapbox.maps.dsl.cameraOptions -import com.mapbox.maps.extension.style.expressions.dsl.generated.id import com.mapbox.maps.extension.style.expressions.generated.Expression import com.mapbox.maps.extension.style.layers.addLayer import com.mapbox.maps.extension.style.layers.generated.SymbolLayer @@ -289,7 +285,7 @@ class MapFragment : ScreenFragment("Map"), Logging { v.gestures.rotateEnabled = false v.gestures.addOnMapLongClickListener(this.longClick) - // v.gestures.addOnMapClickListener(this.click) + v.gestures.addOnMapClickListener(this.click) // Provide initial positions model.nodeDB.nodes.value?.let { nodes -> @@ -331,27 +327,28 @@ class MapFragment : ScreenFragment("Map"), Logging { .build(), { progress -> // Update the download progress to UI -// updateStylePackDownloadProgress( -// progress.completedResourceCount, -// progress.requiredResourceCount, -// "StylePackLoadProgress: $progress" -// ) + updateStylePackDownloadProgress( + progress.completedResourceCount, + progress.requiredResourceCount, + "StylePackLoadProgress: $progress" + ) }, { expected -> if (expected.isValue) { expected.value?.let { stylePack -> // Style pack download finishes successfully - // logSuccessMessage("StylePack downloaded: $stylePack") - // if (binding.tilePackDownloadProgress.progress == binding.tilePackDownloadProgress.max) { - // prepareViewMapButton() - // } else { - // logInfoMessage("Waiting for tile region download to be finished.") - // } + 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.") + } } } expected.error?.let { // Handle error occurred during the style pack download. - // logErrorMessage("StylePackError: $it") + debug("StylePackError: $it") } } ) @@ -395,27 +392,28 @@ class MapFragment : ScreenFragment("Map"), Logging { .networkRestriction(NetworkRestriction.NONE) .build(), { progress -> - //updateTileRegionDownloadProgress( - // progress.completedResourceCount, - // progress.requiredResourceCount, - // "TileRegionLoadProgress: $progress" - // ) + updateTileRegionDownloadProgress( + progress.completedResourceCount, + progress.requiredResourceCount, + "TileRegionLoadProgress: $progress" + ) } ) { expected -> if (expected.isValue) { // Tile pack download finishes successfully expected.value?.let { region -> - // logSuccessMessage("TileRegion downloaded: $region") - // if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { - // prepareViewMapButton() - // } else { - // logInfoMessage("Waiting for style pack download to be finished.") - // } + 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.") + } } } expected.error?.let { // Handle error occurred during the tile region download. - // logErrorMessage("TileRegionError: $it") + debug("TileRegionError: $it") } } // prepareCancelButton() @@ -452,11 +450,33 @@ class MapFragment : ScreenFragment("Map"), Logging { return@OnMapLongClickListener true } -// private val click = OnMapClickListener { -// val point = Point.fromLngLat(it.longitude(), it.latitude()) -// addViewAnnotation(point) -// return@OnMapClickListener true -// } + 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() + } + + private val click = OnMapClickListener { + if (binding.fabStyleToggle.isVisible) { + binding.fabStyleToggle.visibility = View.INVISIBLE + } else { + binding.fabStyleToggle.visibility = View.VISIBLE + } + return@OnMapClickListener true + } private sealed class OfflineLog(val message: String, val color: Int) { class Info(message: String) : OfflineLog(message, android.R.color.black) diff --git a/app/src/main/res/drawable/baseline_layers_white_24dp.xml b/app/src/main/res/drawable/baseline_layers_white_24dp.xml new file mode 100644 index 000000000..02fd48926 --- /dev/null +++ b/app/src/main/res/drawable/baseline_layers_white_24dp.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f4345293e..75d730716 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -124,4 +124,5 @@ Delete Delete All Messages Long Range / Slow + Style Selection \ No newline at end of file From dac84c2093c86ebd5a19e8449bbbc133a4034719 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 17 Feb 2022 15:22:46 -0500 Subject: [PATCH 10/51] Forgot to add map_view.xml --- app/src/main/res/layout/map_view.xml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml index 9178f0b8b..1a58a9abc 100644 --- a/app/src/main/res/layout/map_view.xml +++ b/app/src/main/res/layout/map_view.xml @@ -5,20 +5,42 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + + + + + + + tools:backgroundTint="@color/buttonColor" + android:contentDescription="@string/style_selection" /> + \ No newline at end of file From 1ee500b14da353410e93bbe9331d7c4054aa1dd3 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 17 Feb 2022 16:12:12 -0500 Subject: [PATCH 11/51] Added TODO list for design --- .../main/java/com/geeksville/mesh/ui/MapFragment.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index b123f2ac6..5635d20bc 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -60,6 +60,12 @@ class MapFragment : ScreenFragment("Map"), Logging { } } + //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 + private val resourceOptions: ResourceOptions by lazy { ResourceOptions.Builder().applyDefaultParams(requireContext()).tileStore(tileStore).build() } @@ -255,7 +261,9 @@ class MapFragment : ScreenFragment("Map"), Logging { super.onViewCreated(viewIn, savedInstanceState) binding.fabStyleToggle.setOnClickListener { - downloadOfflineRegion() + + //TODO: Setup Style menu for satellite view, street view, & outdoor view + // downloadOfflineRegion() } // We might not have a real mapview if running with analytics if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) { @@ -488,7 +496,6 @@ class MapFragment : ScreenFragment("Map"), Logging { } companion object { - private const val TAG = "OfflineActivity" private const val ZOOM = 12.0 private const val TILE_REGION_ID = "myTileRegion" private const val STYLE_PACK_METADATA = "my-outdoor-style-pack" From 068f2ba8b2a752f3ff6b2db66928aeba669354d1 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Thu, 17 Feb 2022 17:12:17 -0500 Subject: [PATCH 12/51] Added "download button" with icon and updated onMapTouch --- .../com/geeksville/mesh/ui/MapFragment.kt | 8 ++- .../drawable/baseline_download_white_24dp.xml | 6 +++ app/src/main/res/layout/map_view.xml | 49 ++++++++++++------- 3 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_download_white_24dp.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 5635d20bc..d368081c9 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -200,7 +200,6 @@ class MapFragment : ScreenFragment("Map"), Logging { var mapView: MapView? = null - private fun showDownloadedRegions() { // Get a list of tile regions that are currently available. tileStore.getAllTileRegions { expected -> @@ -265,6 +264,9 @@ class MapFragment : ScreenFragment("Map"), Logging { //TODO: Setup Style menu for satellite view, street view, & outdoor view // downloadOfflineRegion() } + binding.downloadRegion.setOnClickListener { + downloadOfflineRegion() + } // We might not have a real mapview if running with analytics if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) { val vIn = viewIn.findViewById(R.id.mapView) @@ -478,10 +480,12 @@ class MapFragment : ScreenFragment("Map"), Logging { } private val click = OnMapClickListener { - if (binding.fabStyleToggle.isVisible) { + if (binding.fabStyleToggle.isVisible && binding.downloadRegion.isVisible) { binding.fabStyleToggle.visibility = View.INVISIBLE + binding.downloadRegion.visibility = View.INVISIBLE } else { binding.fabStyleToggle.visibility = View.VISIBLE + binding.downloadRegion.visibility = View.VISIBLE } return@OnMapClickListener true } diff --git a/app/src/main/res/drawable/baseline_download_white_24dp.xml b/app/src/main/res/drawable/baseline_download_white_24dp.xml new file mode 100644 index 000000000..41ed7875b --- /dev/null +++ b/app/src/main/res/drawable/baseline_download_white_24dp.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml index 1a58a9abc..b1ff6a129 100644 --- a/app/src/main/res/layout/map_view.xml +++ b/app/src/main/res/layout/map_view.xml @@ -5,12 +5,11 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:mapbox_cameraZoom="0" /> + android:progressTint="@color/colorPrimaryDark" + android:visibility="gone" /> + + + + + + + - - - \ No newline at end of file From dcad0f3ad98deb65aaae3ee34a94183c073969a2 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Thu, 17 Feb 2022 17:43:06 -0500 Subject: [PATCH 13/51] updated marker image & testing some features --- .../com/geeksville/mesh/ui/MapFragment.kt | 25 +++++++++++++------ .../baseline_location_on_white_24dp.xml | 6 +++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_location_on_white_24dp.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index d368081c9..182f5f424 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -77,6 +77,7 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var binding: MapViewBinding private lateinit var mapNotAllowedBinding: MapNotAllowedBinding private lateinit var viewAnnotationManager: ViewAnnotationManager + private lateinit var mapStyleURI: String private lateinit var point: Geometry @@ -86,6 +87,7 @@ class MapFragment : ScreenFragment("Map"), Logging { private val nodeLayerId = "node-layer" private val labelLayerId = "label-layer" private val markerImageId = "my-marker-image" + private val userPointImageId = "user-image"; private var stylePackCancelable: Cancelable? = null private var tilePackCancelable: Cancelable? = null @@ -105,7 +107,7 @@ class MapFragment : ScreenFragment("Map"), Logging { .iconAllowOverlap(true) private val userTouchLayer = SymbolLayer(userTouchLayerId, userTouchPositionId) - .iconImage(markerImageId) + .iconImage(userPointImageId) .iconAnchor(IconAnchor.BOTTOM) .iconAllowOverlap(true) @@ -241,7 +243,7 @@ class MapFragment : ScreenFragment("Map"), Logging { // 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. - offlineManager.removeStylePack(Style.OUTDOORS) + offlineManager.removeStylePack(mapStyleURI) MapboxMap.clearData(resourceOptions) { it.error?.let { error -> @@ -262,7 +264,6 @@ class MapFragment : ScreenFragment("Map"), Logging { binding.fabStyleToggle.setOnClickListener { //TODO: Setup Style menu for satellite view, street view, & outdoor view - // downloadOfflineRegion() } binding.downloadRegion.setOnClickListener { downloadOfflineRegion() @@ -290,6 +291,7 @@ class MapFragment : ScreenFragment("Map"), Logging { it.addImage(markerImageId, markerIcon) it.addLayer(nodeLayer) it.addLayer(labelLayer) + this.mapStyleURI = map.getStyle()?.styleURI.toString() } } @@ -328,8 +330,9 @@ class MapFragment : ScreenFragment("Map"), Logging { // 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. + stylePackCancelable = offlineManager.loadStylePack( - Style.OUTDOORS, + mapStyleURI, // Build Style pack load options StylePackLoadOptions.Builder() .glyphsRasterizationMode(GlyphsRasterizationMode.IDEOGRAPHS_RASTERIZED_LOCALLY) @@ -380,9 +383,10 @@ class MapFragment : ScreenFragment("Map"), Logging { // 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. + val tilesetDescriptor = offlineManager.createTilesetDescriptor( TilesetDescriptorOptions.Builder() - .styleURI(Style.OUTDOORS) + .styleURI(mapStyleURI) .minZoom(0) .maxZoom(16) .build() @@ -393,7 +397,7 @@ class MapFragment : ScreenFragment("Map"), Logging { // Note that the TileStore path must be the same with the TileStore used when initialise the MapView. tilePackCancelable = tileStore.loadTileRegion( - TILE_REGION_ID, + TILE_REGION_ID, // Make this dynamic TileRegionLoadOptions.Builder() .geometry(point) .descriptors(listOf(tilesetDescriptor)) @@ -441,10 +445,14 @@ class MapFragment : ScreenFragment("Map"), Logging { /** * OnLongClick of the map set a position marker. + * If a user long-clicks again, the position of the first marker will be updated */ private val longClick = OnMapLongClickListener { val userDefinedPointImg = - ContextCompat.getDrawable(requireActivity(), R.drawable.ic_twotone_person_24)!! + ContextCompat.getDrawable( + requireActivity(), + R.drawable.baseline_location_on_white_24dp + )!! .toBitmap() point = Point.fromLngLat(it.longitude(), it.latitude()) @@ -452,7 +460,7 @@ class MapFragment : ScreenFragment("Map"), Logging { userTouchPosition.geometry(point) if (!style.styleLayerExists(userTouchLayerId)) { - style.addImage("userImage", userDefinedPointImg) + style.addImage(userPointImageId, userDefinedPointImg) style.addSource(userTouchPosition) style.addLayer(userTouchLayer) } @@ -479,6 +487,7 @@ class MapFragment : ScreenFragment("Map"), Logging { binding.stylePackDownloadProgress.progress = progress.toInt() } + // TODO: Make this dynamic private val click = OnMapClickListener { if (binding.fabStyleToggle.isVisible && binding.downloadRegion.isVisible) { binding.fabStyleToggle.visibility = View.INVISIBLE diff --git a/app/src/main/res/drawable/baseline_location_on_white_24dp.xml b/app/src/main/res/drawable/baseline_location_on_white_24dp.xml new file mode 100644 index 000000000..8b4720d58 --- /dev/null +++ b/app/src/main/res/drawable/baseline_location_on_white_24dp.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 75d730716..ef69dcd22 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -125,4 +125,5 @@ Delete All Messages Long Range / Slow Style Selection + Download Region \ No newline at end of file From a579ce76fc30a540e65de7b9be700f26d3e35204 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Fri, 18 Feb 2022 10:58:59 -0500 Subject: [PATCH 14/51] Added dialog for download. (investigating other options) --- .../com/geeksville/mesh/ui/MapFragment.kt | 91 +++++++++++++++---- .../main/res/layout/dialog_map_download.xml | 44 +++++++++ app/src/main/res/layout/map_view.xml | 21 +++-- 3 files changed, 128 insertions(+), 28 deletions(-) create mode 100644 app/src/main/res/layout/dialog_map_download.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 182f5f424..0f454ab4b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -1,14 +1,21 @@ package com.geeksville.mesh.ui +import android.app.AlertDialog +import android.app.Dialog +import android.content.DialogInterface import android.graphics.Color import android.os.Bundle import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.CheckBox +import android.widget.EditText +import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible +import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import com.geeksville.android.GeeksvilleApplication @@ -33,6 +40,7 @@ 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 +import com.mapbox.maps.extension.style.layers.properties.generated.Visibility import com.mapbox.maps.extension.style.sources.addSource import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions @@ -78,6 +86,7 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var mapNotAllowedBinding: MapNotAllowedBinding private lateinit var viewAnnotationManager: ViewAnnotationManager private lateinit var mapStyleURI: String + private lateinit var userStyleURI: String private lateinit var point: Geometry @@ -261,12 +270,21 @@ class MapFragment : ScreenFragment("Map"), Logging { override fun onViewCreated(viewIn: View, savedInstanceState: Bundle?) { super.onViewCreated(viewIn, savedInstanceState) - binding.fabStyleToggle.setOnClickListener { - - //TODO: Setup Style menu for satellite view, street view, & outdoor view - } +// binding.fabStyleToggle.setOnClickListener { +// +// //TODO: Setup Style menu for satellite view, street view, & outdoor view +// } binding.downloadRegion.setOnClickListener { - downloadOfflineRegion() + // Display menu for download region + + // Add option to pull custom published style with .mbtile (This will require a URI from mapbox) + val downloadMenuFragment = DownloadRegionDialogFragment() + downloadMenuFragment.show(this.parentFragmentManager, "Download Region") + // Populate Coordinates on menu for downloadable region with specs + // Show save button -> once save is clicked add option to name region and then confirm + // Convert Save Button to cancel once download starts + // Show region + //downloadOfflineRegion() } // We might not have a real mapview if running with analytics if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) { @@ -316,7 +334,6 @@ class MapFragment : ScreenFragment("Map"), Logging { } } - 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. @@ -489,31 +506,69 @@ class MapFragment : ScreenFragment("Map"), Logging { // TODO: Make this dynamic private val click = OnMapClickListener { - if (binding.fabStyleToggle.isVisible && binding.downloadRegion.isVisible) { - binding.fabStyleToggle.visibility = View.INVISIBLE + //binding.fabStyleToggle.isVisible && + if (binding.downloadRegion.isVisible) { + //binding.fabStyleToggle.visibility = View.INVISIBLE binding.downloadRegion.visibility = View.INVISIBLE } else { - binding.fabStyleToggle.visibility = View.VISIBLE + //binding.fabStyleToggle.visibility = View.VISIBLE binding.downloadRegion.visibility = View.VISIBLE } return@OnMapClickListener true } - 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) - } - 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" } + + + //TODO: Setup checkbox to add URI + //TODO: Investigate different UI elements (Rather than dialog) + class DownloadRegionDialogFragment : DialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return activity?.let { + + val mapDownloadView = layoutInflater.inflate(R.layout.dialog_map_download, null) + + /* if (mapDownloadView.isActivated) { + val checkBox = mapDownloadView.findViewById(R.id.checkbox) + if (checkBox.isChecked) { + mapDownloadView.findViewById(R.id.uri).visibility = + View.VISIBLE + } else { + mapDownloadView.findViewById(R.id.uri).visibility = + View.GONE + } + }*/ + // Use the Builder class for convenient dialog construction + val builder = AlertDialog.Builder(it) + builder.setView(mapDownloadView) + .setMessage("Download Region") + .setPositiveButton( + "Save" + ) { dialog, _ -> + val editText = mapDownloadView.findViewById(R.id.uri) + if (editText.text != null) { + // Save URI + MapFragment().userStyleURI = editText.text.toString() + editText.setText("")// Clear Text + } + } + .setNegativeButton( + R.string.cancel + ) { dialog, _ -> + dialog.cancel() + // User cancelled the dialog + } + // Create the AlertDialog object and return it + builder.create() + } ?: throw IllegalStateException("Activity cannot be null") + } + } } diff --git a/app/src/main/res/layout/dialog_map_download.xml b/app/src/main/res/layout/dialog_map_download.xml new file mode 100644 index 000000000..6f9b9b36e --- /dev/null +++ b/app/src/main/res/layout/dialog_map_download.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml index b1ff6a129..7a3ba9f60 100644 --- a/app/src/main/res/layout/map_view.xml +++ b/app/src/main/res/layout/map_view.xml @@ -36,21 +36,21 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"> - + + + + + + + + + + \ No newline at end of file From 483360c1bdc0dd339800f24d24f7860ddf00399a Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Sun, 20 Feb 2022 19:11:03 -0500 Subject: [PATCH 15/51] Updated dialog for URI input --- .../com/geeksville/mesh/ui/MapFragment.kt | 45 ++++++++++--------- .../main/res/layout/dialog_map_download.xml | 30 +++++-------- app/src/main/res/values/arrays.xml | 6 +++ 3 files changed, 41 insertions(+), 40 deletions(-) create mode 100644 app/src/main/res/values/arrays.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 0f454ab4b..bd4d599dc 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -85,7 +85,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var binding: MapViewBinding private lateinit var mapNotAllowedBinding: MapNotAllowedBinding private lateinit var viewAnnotationManager: ViewAnnotationManager - private lateinit var mapStyleURI: String private lateinit var userStyleURI: String private lateinit var point: Geometry @@ -252,7 +251,7 @@ class MapFragment : ScreenFragment("Map"), Logging { // 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. - offlineManager.removeStylePack(mapStyleURI) + offlineManager.removeStylePack(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) MapboxMap.clearData(resourceOptions) { it.error?.let { error -> @@ -309,7 +308,6 @@ class MapFragment : ScreenFragment("Map"), Logging { it.addImage(markerImageId, markerIcon) it.addLayer(nodeLayer) it.addLayer(labelLayer) - this.mapStyleURI = map.getStyle()?.styleURI.toString() } } @@ -349,7 +347,7 @@ class MapFragment : ScreenFragment("Map"), Logging { // the data eviction algorithm and are not considered when calculating the disk cache size. stylePackCancelable = offlineManager.loadStylePack( - mapStyleURI, + mapView?.getMapboxMap()?.getStyle()?.styleURI.toString(), // Build Style pack load options StylePackLoadOptions.Builder() .glyphsRasterizationMode(GlyphsRasterizationMode.IDEOGRAPHS_RASTERIZED_LOCALLY) @@ -403,7 +401,7 @@ class MapFragment : ScreenFragment("Map"), Logging { val tilesetDescriptor = offlineManager.createTilesetDescriptor( TilesetDescriptorOptions.Builder() - .styleURI(mapStyleURI) + .styleURI(mapView?.getMapboxMap()?.getStyle()?.styleURI!!) .minZoom(0) .maxZoom(16) .build() @@ -524,8 +522,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private const val TILE_REGION_METADATA = "my-outdoors-tile-region" } - - //TODO: Setup checkbox to add URI //TODO: Investigate different UI elements (Rather than dialog) class DownloadRegionDialogFragment : DialogFragment() { @@ -533,29 +529,34 @@ class MapFragment : ScreenFragment("Map"), Logging { return activity?.let { val mapDownloadView = layoutInflater.inflate(R.layout.dialog_map_download, null) + val uri = mapDownloadView.findViewById(R.id.uri) - /* if (mapDownloadView.isActivated) { - val checkBox = mapDownloadView.findViewById(R.id.checkbox) - if (checkBox.isChecked) { - mapDownloadView.findViewById(R.id.uri).visibility = - View.VISIBLE - } else { - mapDownloadView.findViewById(R.id.uri).visibility = - View.GONE - } - }*/ // Use the Builder class for convenient dialog construction val builder = AlertDialog.Builder(it) builder.setView(mapDownloadView) - .setMessage("Download Region") + .setMultiChoiceItems( + R.array.MapMenuCheckbox, + null, + ) { _, _, isChecked -> + if (isChecked) { + if (!uri.isVisible) { + uri.visibility = + View.VISIBLE + } + } else { + if (uri.isVisible) { + uri.visibility = + View.GONE + } + } + } .setPositiveButton( "Save" ) { dialog, _ -> - val editText = mapDownloadView.findViewById(R.id.uri) - if (editText.text != null) { + if (uri.text != null) { // Save URI - MapFragment().userStyleURI = editText.text.toString() - editText.setText("")// Clear Text + MapFragment().userStyleURI = uri.text.toString() + uri.setText("")// Clear Text } } .setNegativeButton( diff --git a/app/src/main/res/layout/dialog_map_download.xml b/app/src/main/res/layout/dialog_map_download.xml index 6f9b9b36e..c337ce30a 100644 --- a/app/src/main/res/layout/dialog_map_download.xml +++ b/app/src/main/res/layout/dialog_map_download.xml @@ -3,24 +3,6 @@ android:layout_height="wrap_content" android:orientation="vertical"> - - - - - - - - - + + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 000000000..c0035db1a --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,6 @@ + + + + Use Custom URI? + + \ No newline at end of file From b1075d3a9397e534601a5146e070390ef8f1beb5 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Sun, 20 Feb 2022 19:55:42 -0500 Subject: [PATCH 16/51] Removed unused imports --- .../main/java/com/geeksville/mesh/ui/MapFragment.kt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index bd4d599dc..768e0f21a 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -2,16 +2,13 @@ package com.geeksville.mesh.ui import android.app.AlertDialog import android.app.Dialog -import android.content.DialogInterface import android.graphics.Color import android.os.Bundle import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.CheckBox import android.widget.EditText -import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible @@ -40,7 +37,6 @@ 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 -import com.mapbox.maps.extension.style.layers.properties.generated.Visibility import com.mapbox.maps.extension.style.sources.addSource import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions @@ -49,7 +45,6 @@ import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.maps.viewannotation.ViewAnnotationManager -import com.mapbox.maps.viewannotation.viewAnnotationOptions import dagger.hilt.android.AndroidEntryPoint @@ -522,7 +517,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private const val TILE_REGION_METADATA = "my-outdoors-tile-region" } - //TODO: Investigate different UI elements (Rather than dialog) class DownloadRegionDialogFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { @@ -552,7 +546,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } .setPositiveButton( "Save" - ) { dialog, _ -> + ) { _, _ -> if (uri.text != null) { // Save URI MapFragment().userStyleURI = uri.text.toString() @@ -562,8 +556,7 @@ class MapFragment : ScreenFragment("Map"), Logging { .setNegativeButton( R.string.cancel ) { dialog, _ -> - dialog.cancel() - // User cancelled the dialog + dialog.cancel() // User cancelled the dialog } // Create the AlertDialog object and return it builder.create() From c2107e5c937aef777d733faeea73a8594e9d4011 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Sun, 20 Feb 2022 21:06:59 -0500 Subject: [PATCH 17/51] updated menu --- .../com/geeksville/mesh/ui/MapFragment.kt | 101 ++++++++++-------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 768e0f21a..54c4a6d72 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -2,6 +2,7 @@ package com.geeksville.mesh.ui import android.app.AlertDialog import android.app.Dialog +import android.content.Context import android.graphics.Color import android.os.Bundle import android.os.Handler @@ -9,6 +10,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.EditText +import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible @@ -46,6 +48,7 @@ import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.maps.viewannotation.ViewAnnotationManager import dagger.hilt.android.AndroidEntryPoint +import java.lang.ClassCastException @AndroidEntryPoint @@ -81,6 +84,8 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var mapNotAllowedBinding: MapNotAllowedBinding private lateinit var viewAnnotationManager: ViewAnnotationManager private lateinit var userStyleURI: String + private lateinit var pointLat: String + private lateinit var pointLong: String private lateinit var point: Geometry @@ -272,8 +277,8 @@ class MapFragment : ScreenFragment("Map"), Logging { // Display menu for download region // Add option to pull custom published style with .mbtile (This will require a URI from mapbox) - val downloadMenuFragment = DownloadRegionDialogFragment() - downloadMenuFragment.show(this.parentFragmentManager, "Download Region") + this.downloadRegionDialogFragment() + // Populate Coordinates on menu for downloadable region with specs // Show save button -> once save is clicked add option to name region and then confirm // Convert Save Button to cancel once download starts @@ -464,6 +469,8 @@ class MapFragment : ScreenFragment("Map"), Logging { R.drawable.baseline_location_on_white_24dp )!! .toBitmap() + pointLong = String.format("%.2f", it.longitude()) + pointLat = String.format("%.2f", it.latitude()) point = Point.fromLngLat(it.longitude(), it.latitude()) mapView?.getMapboxMap()?.getStyle()?.let { style -> @@ -517,54 +524,56 @@ class MapFragment : ScreenFragment("Map"), Logging { private const val TILE_REGION_METADATA = "my-outdoors-tile-region" } - class DownloadRegionDialogFragment : DialogFragment() { + private fun downloadRegionDialogFragment() { + val mapDownloadView = layoutInflater.inflate(R.layout.dialog_map_download, null) + val uri = mapDownloadView.findViewById(R.id.uri) + val downloadRegionDialogFragment = AlertDialog.Builder(context) - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return activity?.let { - - val mapDownloadView = layoutInflater.inflate(R.layout.dialog_map_download, null) - val uri = mapDownloadView.findViewById(R.id.uri) - - // Use the Builder class for convenient dialog construction - val builder = AlertDialog.Builder(it) - builder.setView(mapDownloadView) - .setMultiChoiceItems( - R.array.MapMenuCheckbox, - null, - ) { _, _, isChecked -> - if (isChecked) { - if (!uri.isVisible) { - uri.visibility = - View.VISIBLE - } - } else { - if (uri.isVisible) { - uri.visibility = - View.GONE - } - } - } - .setPositiveButton( - "Save" - ) { _, _ -> - if (uri.text != null) { - // Save URI - MapFragment().userStyleURI = uri.text.toString() - uri.setText("")// Clear Text - } - } - .setNegativeButton( - R.string.cancel - ) { dialog, _ -> - dialog.cancel() // User cancelled the dialog - } - // Create the AlertDialog object and return it - builder.create() - } ?: throw IllegalStateException("Activity cannot be null") + if (this::pointLat.isInitialized && this::pointLat.isInitialized) { + "Lat: $pointLong".also { + mapDownloadView.findViewById(R.id.longitude).text = it + } + "Long: $pointLat".also { + mapDownloadView.findViewById(R.id.latitude).text = it + } } + + downloadRegionDialogFragment.setView(mapDownloadView) + .setMultiChoiceItems( + R.array.MapMenuCheckbox, + null, + ) { _, _, isChecked -> + if (isChecked) { + if (!uri.isVisible) { + uri.visibility = + View.VISIBLE + } + } else { + if (uri.isVisible) { + uri.visibility = + View.GONE + } + } + } + .setPositiveButton( + "Save" + ) { _, _ -> + if (uri.text != null) { + // Save URI + userStyleURI = uri.text.toString() + uri.setText("") // clear text + } + } + .setNegativeButton( + R.string.cancel + ) { dialog, _ -> + dialog.cancel() + } + + downloadRegionDialogFragment.create() + downloadRegionDialogFragment.show() } } - From 4196a5afd84fbd18f487c02c7321323702041ce9 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Sun, 20 Feb 2022 21:32:19 -0500 Subject: [PATCH 18/51] Updated some logic --- .../com/geeksville/mesh/ui/MapFragment.kt | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 54c4a6d72..513831b55 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -278,12 +278,7 @@ class MapFragment : ScreenFragment("Map"), Logging { // Add option to pull custom published style with .mbtile (This will require a URI from mapbox) this.downloadRegionDialogFragment() - // Populate Coordinates on menu for downloadable region with specs - // Show save button -> once save is clicked add option to name region and then confirm - // Convert Save Button to cancel once download starts - // Show region - //downloadOfflineRegion() } // We might not have a real mapview if running with analytics if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) { @@ -557,16 +552,26 @@ class MapFragment : ScreenFragment("Map"), Logging { } .setPositiveButton( "Save" - ) { _, _ -> - if (uri.text != null) { + ) { dialog, _ -> + if (uri.isVisible) { // Save URI userStyleURI = uri.text.toString() uri.setText("") // clear text } + if ((this::pointLat.isInitialized && this::pointLong.isInitialized) || this::userStyleURI.isInitialized) { + // Create new popup to handle download menu + // Name region and then confirm + // Save Button to start download and cancel + downloadOfflineRegion() + } else { + dialog.cancel() + // Tell user to select region + } } .setNegativeButton( R.string.cancel ) { dialog, _ -> + removeOfflineRegions() dialog.cancel() } From 7c4aaecb7745b3fa09fb52a7e6284831e588303e Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Sun, 20 Feb 2022 22:59:29 -0500 Subject: [PATCH 19/51] Removed need for lateinit variable --- .../java/com/geeksville/mesh/ui/MapFragment.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 513831b55..a4c7abff1 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -83,7 +83,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var binding: MapViewBinding private lateinit var mapNotAllowedBinding: MapNotAllowedBinding private lateinit var viewAnnotationManager: ViewAnnotationManager - private lateinit var userStyleURI: String private lateinit var pointLat: String private lateinit var pointLong: String @@ -327,7 +326,11 @@ class MapFragment : ScreenFragment("Map"), Logging { } } - private fun downloadOfflineRegion() { + private fun downloadOfflineRegion(styleURI: String = "") { + val style = styleURI.ifEmpty { + mapView?.getMapboxMap() + ?.getStyle()?.styleURI.toString() + } // 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. @@ -342,7 +345,7 @@ class MapFragment : ScreenFragment("Map"), Logging { // the data eviction algorithm and are not considered when calculating the disk cache size. stylePackCancelable = offlineManager.loadStylePack( - mapView?.getMapboxMap()?.getStyle()?.styleURI.toString(), + style, // Build Style pack load options StylePackLoadOptions.Builder() .glyphsRasterizationMode(GlyphsRasterizationMode.IDEOGRAPHS_RASTERIZED_LOCALLY) @@ -396,7 +399,7 @@ class MapFragment : ScreenFragment("Map"), Logging { val tilesetDescriptor = offlineManager.createTilesetDescriptor( TilesetDescriptorOptions.Builder() - .styleURI(mapView?.getMapboxMap()?.getStyle()?.styleURI!!) + .styleURI(style) .minZoom(0) .maxZoom(16) .build() @@ -553,12 +556,13 @@ class MapFragment : ScreenFragment("Map"), Logging { .setPositiveButton( "Save" ) { dialog, _ -> + var userStyleURI: String = "" if (uri.isVisible) { // Save URI userStyleURI = uri.text.toString() uri.setText("") // clear text } - if ((this::pointLat.isInitialized && this::pointLong.isInitialized) || this::userStyleURI.isInitialized) { + if ((this::pointLat.isInitialized && this::pointLong.isInitialized) || userStyleURI.isNotEmpty()) { // Create new popup to handle download menu // Name region and then confirm // Save Button to start download and cancel From d74c122f23e35d6bf82cfd1016027471cffdf0d4 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Tue, 22 Feb 2022 21:46:13 -0500 Subject: [PATCH 20/51] Testing toast and downloading --- .../com/geeksville/mesh/ui/MapFragment.kt | 94 ++++++++++++------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index a4c7abff1..f7a728f45 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -1,8 +1,6 @@ package com.geeksville.mesh.ui import android.app.AlertDialog -import android.app.Dialog -import android.content.Context import android.graphics.Color import android.os.Bundle import android.os.Handler @@ -11,10 +9,10 @@ import android.view.View import android.view.ViewGroup import android.widget.EditText import android.widget.TextView +import android.widget.Toast import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible -import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import com.geeksville.android.GeeksvilleApplication @@ -27,10 +25,7 @@ import com.geeksville.mesh.model.UIViewModel import com.geeksville.util.formatAgo import com.mapbox.bindgen.Value import com.mapbox.common.* -import com.mapbox.geojson.Feature -import com.mapbox.geojson.FeatureCollection -import com.mapbox.geojson.Geometry -import com.mapbox.geojson.Point +import com.mapbox.geojson.* import com.mapbox.maps.* import com.mapbox.maps.dsl.cameraOptions import com.mapbox.maps.extension.style.expressions.generated.Expression @@ -43,12 +38,12 @@ import com.mapbox.maps.extension.style.sources.addSource import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions import com.mapbox.maps.plugin.animation.flyTo +import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.maps.viewannotation.ViewAnnotationManager import dagger.hilt.android.AndroidEntryPoint -import java.lang.ClassCastException @AndroidEntryPoint @@ -69,7 +64,6 @@ class MapFragment : ScreenFragment("Map"), Logging { //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 private val resourceOptions: ResourceOptions by lazy { @@ -85,8 +79,10 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var viewAnnotationManager: ViewAnnotationManager private lateinit var pointLat: String private lateinit var pointLong: String + private lateinit var userStyleURI: String - private lateinit var point: Geometry + + private lateinit var point: Point private val model: UIViewModel by activityViewModels() @@ -94,7 +90,7 @@ class MapFragment : ScreenFragment("Map"), Logging { private val nodeLayerId = "node-layer" private val labelLayerId = "label-layer" private val markerImageId = "my-marker-image" - private val userPointImageId = "user-image"; + private val userPointImageId = "user-image" private var stylePackCancelable: Cancelable? = null private var tilePackCancelable: Cancelable? = null @@ -128,7 +124,7 @@ class MapFragment : ScreenFragment("Map"), Logging { .textAllowOverlap(true) - private fun onNodesChanged(map: MapboxMap, nodes: Collection) { + private fun onNodesChanged(nodes: Collection) { val nodesWithPosition = nodes.filter { it.validPosition != null } /** @@ -274,10 +270,7 @@ class MapFragment : ScreenFragment("Map"), Logging { // } binding.downloadRegion.setOnClickListener { // Display menu for download region - - // Add option to pull custom published style with .mbtile (This will require a URI from mapbox) this.downloadRegionDialogFragment() - // Populate Coordinates on menu for downloadable region with specs } // We might not have a real mapview if running with analytics if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) { @@ -311,14 +304,14 @@ class MapFragment : ScreenFragment("Map"), Logging { // Provide initial positions model.nodeDB.nodes.value?.let { nodes -> - onNodesChanged(map, nodes.values) + onNodesChanged(nodes.values) } } // Any times nodes change update our map model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { nodes -> if (isViewVisible) - onNodesChanged(map, nodes.values) + onNodesChanged(nodes.values) }) //viewAnnotationManager = v.viewAnnotationManager zoomToNodes(map) @@ -355,8 +348,7 @@ class MapFragment : ScreenFragment("Map"), Logging { // Update the download progress to UI updateStylePackDownloadProgress( progress.completedResourceCount, - progress.requiredResourceCount, - "StylePackLoadProgress: $progress" + progress.requiredResourceCount ) }, { expected -> @@ -404,7 +396,6 @@ class MapFragment : ScreenFragment("Map"), Logging { .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. @@ -422,7 +413,6 @@ class MapFragment : ScreenFragment("Map"), Logging { updateTileRegionDownloadProgress( progress.completedResourceCount, progress.requiredResourceCount, - "TileRegionLoadProgress: $progress" ) } ) { expected -> @@ -483,10 +473,21 @@ class MapFragment : ScreenFragment("Map"), Logging { return@OnMapLongClickListener true } + /* + 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) + } + } + } + */ + private fun updateStylePackDownloadProgress( progress: Long, max: Long, - message: String? = null ) { binding.stylePackDownloadProgress.visibility = View.VISIBLE binding.stylePackDownloadProgress.max = max.toInt() @@ -496,13 +497,11 @@ class MapFragment : ScreenFragment("Map"), Logging { private fun updateTileRegionDownloadProgress( progress: Long, max: Long, - message: String? = null ) { binding.stylePackDownloadProgress.max = max.toInt() binding.stylePackDownloadProgress.progress = progress.toInt() } - // TODO: Make this dynamic private val click = OnMapClickListener { //binding.fabStyleToggle.isVisible && if (binding.downloadRegion.isVisible) { @@ -528,15 +527,20 @@ class MapFragment : ScreenFragment("Map"), Logging { val downloadRegionDialogFragment = AlertDialog.Builder(context) if (this::pointLat.isInitialized && this::pointLat.isInitialized) { + val latText = mapDownloadView.findViewById(R.id.longitude) "Lat: $pointLong".also { - mapDownloadView.findViewById(R.id.longitude).text = it + latText.text = it + View.VISIBLE.also { latText.visibility = View.VISIBLE } } + val longText = mapDownloadView.findViewById(R.id.latitude) "Long: $pointLat".also { - mapDownloadView.findViewById(R.id.latitude).text = it + longText.text = it + View.VISIBLE.also { longText.visibility = View.VISIBLE } } } downloadRegionDialogFragment.setView(mapDownloadView) + .setTitle("Download Region") .setMultiChoiceItems( R.array.MapMenuCheckbox, null, @@ -555,27 +559,49 @@ class MapFragment : ScreenFragment("Map"), Logging { } .setPositiveButton( "Save" - ) { dialog, _ -> - var userStyleURI: String = "" + ) { _, _ -> + if (uri.isVisible) { // Save URI userStyleURI = uri.text.toString() uri.setText("") // clear text } - if ((this::pointLat.isInitialized && this::pointLong.isInitialized) || userStyleURI.isNotEmpty()) { - // Create new popup to handle download menu - // Name region and then confirm - // Save Button to start download and cancel + 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()) { downloadOfflineRegion() } else { - dialog.cancel() // Tell user to select region + 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() } } + .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 + } .setNegativeButton( R.string.cancel ) { dialog, _ -> - removeOfflineRegions() + removeOfflineRegions() //TODO: Add to offline manager window dialog.cancel() } From cac9c9be7cdfacadbe1825fe8f58c89ef35705e8 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Tue, 22 Feb 2022 22:05:26 -0500 Subject: [PATCH 21/51] Able to view mbtile style with test and move map to it --- .../com/geeksville/mesh/ui/MapFragment.kt | 54 +++++++++++++------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index f7a728f45..81e11ab71 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -13,6 +13,7 @@ import android.widget.Toast import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible +import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import com.geeksville.android.GeeksvilleApplication @@ -44,6 +45,8 @@ import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.maps.viewannotation.ViewAnnotationManager import dagger.hilt.android.AndroidEntryPoint +import kotlin.math.cos +import kotlin.math.sin @AndroidEntryPoint @@ -81,7 +84,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var pointLong: String private lateinit var userStyleURI: String - private lateinit var point: Point private val model: UIViewModel by activityViewModels() @@ -398,12 +400,23 @@ class MapFragment : ScreenFragment("Map"), Logging { ) // 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. + val right = calculateCoordinate(0.0, point.latitude(), point.longitude()) + val top = calculateCoordinate(90.0, point.latitude(), point.longitude()) + val left = calculateCoordinate(180.0, point.latitude(), point.longitude()) + val bottom = calculateCoordinate(270.0, point.latitude(), point.longitude()) + + val pointList = listOf(right, top, left, bottom) + + // val ploygonCoordList = listOf(pointList) + // Polygon.fromLngLats(ploygonCoordList) + val squareRegion = LineString.fromLngLats(pointList) + // Note that the TileStore path must be the same with the TileStore used when initialise the MapView. tilePackCancelable = tileStore.loadTileRegion( TILE_REGION_ID, // Make this dynamic TileRegionLoadOptions.Builder() - .geometry(point) + .geometry(squareRegion) .descriptors(listOf(tilesetDescriptor)) .metadata(Value(TILE_REGION_METADATA)) .acceptExpired(true) @@ -463,7 +476,6 @@ class MapFragment : ScreenFragment("Map"), Logging { mapView?.getMapboxMap()?.getStyle()?.let { style -> userTouchPosition.geometry(point) - if (!style.styleLayerExists(userTouchLayerId)) { style.addImage(userPointImageId, userDefinedPointImg) style.addSource(userTouchPosition) @@ -473,6 +485,15 @@ class MapFragment : ScreenFragment("Map"), Logging { return@OnMapLongClickListener true } + private fun calculateCoordinate(degrees: Double, lat: Double, long: Double): Point { + val deg = Math.toRadians(degrees) + val distancesInMeters = 4023.36 // 2.5 miles + + val newLong = long + (180 / Math.PI) * (distancesInMeters / 6378137) / cos(long) * cos(deg) + val newLat = lat + (180 / Math.PI) * (distancesInMeters / 6378137) / sin(lat) * sin(deg); + return Point.fromLngLat(newLong, newLat) + } + /* if (this::point.isInitialized) { if (it.latitude() == point.latitude() && it.longitude() == point.longitude()) { @@ -515,7 +536,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } companion object { - private const val ZOOM = 12.0 + private const val ZOOM = 9.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" @@ -585,17 +606,20 @@ class MapFragment : ScreenFragment("Map"), Logging { .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() + mapView?.getMapboxMap().also { + it?.flyTo( + CameraOptions.Builder() + .zoom(ZOOM) + .center(point) + .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) + debug(userStyleURI) + it?.loadStyleUri(userStyleURI) { + CircleAnnotationOptions() + .withPoint(point) + .withCircleColor(Color.RED) + } + + } // Open up Downloaded Region managers } .setNegativeButton( From cdbd762e27a622de080528acea3f592a714a55e0 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 23 Feb 2022 09:26:56 -0500 Subject: [PATCH 22/51] Hide elements until value exists, added comments --- app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt | 5 +++++ app/src/main/res/layout/dialog_map_download.xml | 2 ++ 2 files changed, 7 insertions(+) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 81e11ab71..957592309 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -400,6 +400,11 @@ class MapFragment : ScreenFragment("Map"), Logging { ) // 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. + + /* + Calculate region from user specified position. + 2.5 miles N,S,E,W from user center point. + */ val right = calculateCoordinate(0.0, point.latitude(), point.longitude()) val top = calculateCoordinate(90.0, point.latitude(), point.longitude()) val left = calculateCoordinate(180.0, point.latitude(), point.longitude()) diff --git a/app/src/main/res/layout/dialog_map_download.xml b/app/src/main/res/layout/dialog_map_download.xml index c337ce30a..1feed4287 100644 --- a/app/src/main/res/layout/dialog_map_download.xml +++ b/app/src/main/res/layout/dialog_map_download.xml @@ -13,6 +13,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:padding="8dp" + android:visibility="gone" android:text="Lat:" /> From 48b6bd1ebffd5a78609da89d5ff0e3a84991ef29 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 23 Feb 2022 13:54:48 -0500 Subject: [PATCH 23/51] Draw region around center point. (Need to investigate formulas that take the shape of the earth + lat/long not being perfect square into account) --- .../com/geeksville/mesh/ui/MapFragment.kt | 62 ++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 957592309..5cdabb51e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -13,7 +13,6 @@ import android.widget.Toast import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible -import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import com.geeksville.android.GeeksvilleApplication @@ -32,11 +31,11 @@ import com.mapbox.maps.dsl.cameraOptions import com.mapbox.maps.extension.style.expressions.generated.Expression import com.mapbox.maps.extension.style.layers.addLayer 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 +import com.mapbox.maps.extension.style.layers.generated.lineLayer +import com.mapbox.maps.extension.style.layers.properties.generated.* import com.mapbox.maps.extension.style.sources.addSource import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource +import com.mapbox.maps.extension.style.sources.generated.geoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions import com.mapbox.maps.plugin.animation.flyTo import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions @@ -479,12 +478,59 @@ class MapFragment : ScreenFragment("Map"), Logging { pointLat = String.format("%.2f", it.latitude()) point = Point.fromLngLat(it.longitude(), it.latitude()) + + /* + Calculate region from user specified position. + 2.5 miles N,S,E,W from user center point. + */ + val topFromCenter = calculateCoordinate(90.0, point.latitude(), point.longitude()) + val bottomFromCenter = calculateCoordinate(270.0, point.latitude(), point.longitude()) + + val topLeft = + calculateCoordinate(180.0, topFromCenter.latitude(), topFromCenter.longitude()) + val topRight = calculateCoordinate(0.0, topFromCenter.latitude(), topFromCenter.longitude()) + val bottomLeft = + calculateCoordinate(180.0, bottomFromCenter.latitude(), bottomFromCenter.longitude()) + val bottomRight = + calculateCoordinate(0.0, bottomFromCenter.latitude(), bottomFromCenter.longitude()) + + val pointList = listOf( + topRight, + topLeft, + bottomLeft, + bottomRight, + topRight + ) + + // val ploygonCoordList = listOf(pointList) + // Polygon.fromLngLats(ploygonCoordList) + val squareRegion = LineString.fromLngLats(pointList) + val geoJsonSource = geoJsonSource("BOUNDING_BOX_ID") { + geometry(squareRegion) + } + val lineLayer = lineLayer("lineLayer", "BOUNDING_BOX_ID") { + lineCap(LineCap.ROUND) + lineJoin(LineJoin.MITER) + lineOpacity(0.7) + lineWidth(1.5) + lineColor("#888") + } + + mapView?.getMapboxMap()?.getStyle()?.let { style -> userTouchPosition.geometry(point) + geoJsonSource.geometry(squareRegion) if (!style.styleLayerExists(userTouchLayerId)) { style.addImage(userPointImageId, userDefinedPointImg) style.addSource(userTouchPosition) + style.addSource(geoJsonSource) + style.addLayer(lineLayer) style.addLayer(userTouchLayer) + } else { + style.removeStyleLayer("lineLayer") + style.removeStyleSource("BOUNDING_BOX_ID") + style.addSource(geoJsonSource) + style.addLayer(lineLayer) } } return@OnMapLongClickListener true @@ -492,10 +538,12 @@ class MapFragment : ScreenFragment("Map"), Logging { private fun calculateCoordinate(degrees: Double, lat: Double, long: Double): Point { val deg = Math.toRadians(degrees) - val distancesInMeters = 4023.36 // 2.5 miles + val distancesInMeters = 8046.72 // 5 miles - val newLong = long + (180 / Math.PI) * (distancesInMeters / 6378137) / cos(long) * cos(deg) - val newLat = lat + (180 / Math.PI) * (distancesInMeters / 6378137) / sin(lat) * sin(deg); + val newLong = + long + (180 / Math.PI) * (distancesInMeters / 6378137) / cos(lat) * cos(deg) //cos(long) * cos(deg) + val newLat = + lat + (180 / Math.PI) * (distancesInMeters / 6378137) * sin(deg) // sin(lat) * sin(deg) return Point.fromLngLat(newLong, newLat) } From 8dfcbf5f388bfe06a3ca987409efe8d163b52dbd Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 23 Feb 2022 16:13:11 -0500 Subject: [PATCH 24/51] Testing some things out --- .../java/com/geeksville/mesh/ui/MapFragment.kt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 5cdabb51e..507e2e750 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -485,21 +485,31 @@ class MapFragment : ScreenFragment("Map"), Logging { */ val topFromCenter = calculateCoordinate(90.0, point.latitude(), point.longitude()) val bottomFromCenter = calculateCoordinate(270.0, point.latitude(), point.longitude()) + val rightFromCenter = calculateCoordinate(360.0, point.latitude(), point.longitude()) + val leftFromCenter = calculateCoordinate(180.0, point.latitude(), point.longitude()) val topLeft = calculateCoordinate(180.0, topFromCenter.latitude(), topFromCenter.longitude()) - val topRight = calculateCoordinate(0.0, topFromCenter.latitude(), topFromCenter.longitude()) + val topRight = + calculateCoordinate(360.0, topFromCenter.latitude(), topFromCenter.longitude()) val bottomLeft = calculateCoordinate(180.0, bottomFromCenter.latitude(), bottomFromCenter.longitude()) val bottomRight = calculateCoordinate(0.0, bottomFromCenter.latitude(), bottomFromCenter.longitude()) - val pointList = listOf( - topRight, + /* + topRight, topLeft, bottomLeft, bottomRight, topRight + */ + val pointList = listOf( + rightFromCenter, + topFromCenter, + leftFromCenter, + bottomFromCenter, + rightFromCenter ) // val ploygonCoordList = listOf(pointList) @@ -538,7 +548,7 @@ class MapFragment : ScreenFragment("Map"), Logging { private fun calculateCoordinate(degrees: Double, lat: Double, long: Double): Point { val deg = Math.toRadians(degrees) - val distancesInMeters = 8046.72 // 5 miles + val distancesInMeters = 8046.72 / 2 // 2.5 miles val newLong = long + (180 / Math.PI) * (distancesInMeters / 6378137) / cos(lat) * cos(deg) //cos(long) * cos(deg) From 27ab5c1a26891fae7c0579710ab34653030dfa9f Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Wed, 23 Feb 2022 19:47:30 -0500 Subject: [PATCH 25/51] Testing with a circle region. Still not sure why the shape morphs --- .../com/geeksville/mesh/ui/MapFragment.kt | 113 +++++++++--------- 1 file changed, 55 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 507e2e750..c4361e9be 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -38,7 +38,9 @@ import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource import com.mapbox.maps.extension.style.sources.generated.geoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions import com.mapbox.maps.plugin.animation.flyTo +import com.mapbox.maps.plugin.annotation.annotations import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions +import com.mapbox.maps.plugin.annotation.generated.createCircleAnnotationManager import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures @@ -92,10 +94,14 @@ class MapFragment : ScreenFragment("Map"), Logging { private val labelLayerId = "label-layer" private val markerImageId = "my-marker-image" private val userPointImageId = "user-image" + private val boundingBoxId = "BOUNDING_BOX_ID" + private val lineLayerId = "lineLayer" private var stylePackCancelable: Cancelable? = null private var tilePackCancelable: Cancelable? = null + private lateinit var circleRegion: LineString + private val userTouchPositionId = "user-touch-position" private val userTouchLayerId = "user-touch-layer" @@ -420,7 +426,7 @@ class MapFragment : ScreenFragment("Map"), Logging { tilePackCancelable = tileStore.loadTileRegion( TILE_REGION_ID, // Make this dynamic TileRegionLoadOptions.Builder() - .geometry(squareRegion) + .geometry(circleRegion) .descriptors(listOf(tilesetDescriptor)) .metadata(Value(TILE_REGION_METADATA)) .acceptExpired(true) @@ -463,6 +469,16 @@ class MapFragment : ScreenFragment("Map"), Logging { // ) // } + + private fun circleCoordinateGenerator(): List { + val circleCoordinates = mutableListOf() + for (i in 0..360) { + val point = calculateCoordinate(i.toDouble(), point.latitude(), point.longitude()) + circleCoordinates.add(point) + } + return circleCoordinates + } + /** * OnLongClick of the map set a position marker. * If a user long-clicks again, the position of the first marker will be updated @@ -478,47 +494,11 @@ class MapFragment : ScreenFragment("Map"), Logging { pointLat = String.format("%.2f", it.latitude()) point = Point.fromLngLat(it.longitude(), it.latitude()) - - /* - Calculate region from user specified position. - 2.5 miles N,S,E,W from user center point. - */ - val topFromCenter = calculateCoordinate(90.0, point.latitude(), point.longitude()) - val bottomFromCenter = calculateCoordinate(270.0, point.latitude(), point.longitude()) - val rightFromCenter = calculateCoordinate(360.0, point.latitude(), point.longitude()) - val leftFromCenter = calculateCoordinate(180.0, point.latitude(), point.longitude()) - - val topLeft = - calculateCoordinate(180.0, topFromCenter.latitude(), topFromCenter.longitude()) - val topRight = - calculateCoordinate(360.0, topFromCenter.latitude(), topFromCenter.longitude()) - val bottomLeft = - calculateCoordinate(180.0, bottomFromCenter.latitude(), bottomFromCenter.longitude()) - val bottomRight = - calculateCoordinate(0.0, bottomFromCenter.latitude(), bottomFromCenter.longitude()) - - /* - topRight, - topLeft, - bottomLeft, - bottomRight, - topRight - */ - val pointList = listOf( - rightFromCenter, - topFromCenter, - leftFromCenter, - bottomFromCenter, - rightFromCenter - ) - - // val ploygonCoordList = listOf(pointList) - // Polygon.fromLngLats(ploygonCoordList) - val squareRegion = LineString.fromLngLats(pointList) - val geoJsonSource = geoJsonSource("BOUNDING_BOX_ID") { - geometry(squareRegion) + circleRegion = LineString.fromLngLats(circleCoordinateGenerator()) + val geoJsonSource = geoJsonSource(boundingBoxId) { + geometry(circleRegion) } - val lineLayer = lineLayer("lineLayer", "BOUNDING_BOX_ID") { + val lineLayer = lineLayer(lineLayerId, boundingBoxId) { lineCap(LineCap.ROUND) lineJoin(LineJoin.MITER) lineOpacity(0.7) @@ -529,7 +509,6 @@ class MapFragment : ScreenFragment("Map"), Logging { mapView?.getMapboxMap()?.getStyle()?.let { style -> userTouchPosition.geometry(point) - geoJsonSource.geometry(squareRegion) if (!style.styleLayerExists(userTouchLayerId)) { style.addImage(userPointImageId, userDefinedPointImg) style.addSource(userTouchPosition) @@ -537,8 +516,8 @@ class MapFragment : ScreenFragment("Map"), Logging { style.addLayer(lineLayer) style.addLayer(userTouchLayer) } else { - style.removeStyleLayer("lineLayer") - style.removeStyleSource("BOUNDING_BOX_ID") + style.removeStyleLayer(lineLayerId) + style.removeStyleSource(boundingBoxId) style.addSource(geoJsonSource) style.addLayer(lineLayer) } @@ -548,13 +527,28 @@ class MapFragment : ScreenFragment("Map"), Logging { private fun calculateCoordinate(degrees: Double, lat: Double, long: Double): Point { val deg = Math.toRadians(degrees) - val distancesInMeters = 8046.72 / 2 // 2.5 miles + val distancesInMeters = 8046.72 // 2.5 miles + val radiusOfEarthInMeters = 6378137 + if (degrees in 0.1..89.9) { - val newLong = - long + (180 / Math.PI) * (distancesInMeters / 6378137) / cos(lat) * cos(deg) //cos(long) * cos(deg) - val newLat = - lat + (180 / Math.PI) * (distancesInMeters / 6378137) * sin(deg) // sin(lat) * sin(deg) - return Point.fromLngLat(newLong, newLat) + } + if (degrees in 90.1..179.9) { + + } + if (degrees in 180.1..2.0) { + + } + if (degrees in 270.0..360.0) { + + } + + val x = + long + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) / cos(lat) * cos( + deg + ) + val y = + lat + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * sin(deg) // sin(lat) * sin(deg) + return Point.fromLngLat(x, y) } /* @@ -650,12 +644,12 @@ class MapFragment : ScreenFragment("Map"), Logging { userStyleURI = uri.text.toString() uri.setText("") // clear text } - if ((this::point.isInitialized) && userStyleURI.isNotEmpty()) { + if ((this::point.isInitialized) && this::userStyleURI.isInitialized) { //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()) { + } else if ((this::point.isInitialized) && !this::userStyleURI.isInitialized) { downloadOfflineRegion() } else { // Tell user to select region @@ -675,16 +669,19 @@ class MapFragment : ScreenFragment("Map"), Logging { .zoom(ZOOM) .center(point) .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) - debug(userStyleURI) - it?.loadStyleUri(userStyleURI) { - CircleAnnotationOptions() - .withPoint(point) - .withCircleColor(Color.RED) - } + //debug(userStyleURI) + it?.loadStyleUri(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) { + } } // Open up Downloaded Region managers + mapView?.annotations?.createCircleAnnotationManager()?.create( + CircleAnnotationOptions() + .withPoint(point) + .withCircleColor(Color.RED) + ) } + .setNegativeButton( R.string.cancel ) { dialog, _ -> From ff751680bbe0e3e7737f1fe8506d8607c041410a Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Wed, 23 Feb 2022 20:22:04 -0500 Subject: [PATCH 26/51] I was never good at trig :'( --- .../com/geeksville/mesh/ui/MapFragment.kt | 68 ++++++------------- 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index c4361e9be..75b6de300 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -100,7 +100,7 @@ class MapFragment : ScreenFragment("Map"), Logging { private var stylePackCancelable: Cancelable? = null private var tilePackCancelable: Cancelable? = null - private lateinit var circleRegion: LineString + private lateinit var squareRegion: Geometry private val userTouchPositionId = "user-touch-position" @@ -406,27 +406,11 @@ class MapFragment : ScreenFragment("Map"), Logging { // 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. - /* - Calculate region from user specified position. - 2.5 miles N,S,E,W from user center point. - */ - val right = calculateCoordinate(0.0, point.latitude(), point.longitude()) - val top = calculateCoordinate(90.0, point.latitude(), point.longitude()) - val left = calculateCoordinate(180.0, point.latitude(), point.longitude()) - val bottom = calculateCoordinate(270.0, point.latitude(), point.longitude()) - - val pointList = listOf(right, top, left, bottom) - - // val ploygonCoordList = listOf(pointList) - // Polygon.fromLngLats(ploygonCoordList) - val squareRegion = LineString.fromLngLats(pointList) - - // Note that the TileStore path must be the same with the TileStore used when initialise the MapView. tilePackCancelable = tileStore.loadTileRegion( TILE_REGION_ID, // Make this dynamic TileRegionLoadOptions.Builder() - .geometry(circleRegion) + .geometry(squareRegion) .descriptors(listOf(tilesetDescriptor)) .metadata(Value(TILE_REGION_METADATA)) .acceptExpired(true) @@ -469,16 +453,6 @@ class MapFragment : ScreenFragment("Map"), Logging { // ) // } - - private fun circleCoordinateGenerator(): List { - val circleCoordinates = mutableListOf() - for (i in 0..360) { - val point = calculateCoordinate(i.toDouble(), point.latitude(), point.longitude()) - circleCoordinates.add(point) - } - return circleCoordinates - } - /** * OnLongClick of the map set a position marker. * If a user long-clicks again, the position of the first marker will be updated @@ -494,9 +468,24 @@ class MapFragment : ScreenFragment("Map"), Logging { pointLat = String.format("%.2f", it.latitude()) point = Point.fromLngLat(it.longitude(), it.latitude()) - circleRegion = LineString.fromLngLats(circleCoordinateGenerator()) + + /* + Calculate region from user specified position. + 10 miles NE,NW,SE,SW from user center point. + 100 Sq Mile Region + */ + val topRight = calculateCoordinate(45.0, point.latitude(), point.longitude()) + val topLeft = calculateCoordinate(135.0, point.latitude(), point.longitude()) + val bottomLeft = calculateCoordinate(225.0, point.latitude(), point.longitude()) + val bottomRight = calculateCoordinate(315.0, point.latitude(), point.longitude()) + + val pointList = listOf(topRight, topLeft, bottomLeft, bottomRight, topRight) + + val squareLineString = LineString.fromLngLats(pointList) + + squareRegion = squareLineString//LineString.fromLngLats(circleCoordinateGenerator()) val geoJsonSource = geoJsonSource(boundingBoxId) { - geometry(circleRegion) + geometry(squareRegion) } val lineLayer = lineLayer(lineLayerId, boundingBoxId) { lineCap(LineCap.ROUND) @@ -527,27 +516,14 @@ class MapFragment : ScreenFragment("Map"), Logging { private fun calculateCoordinate(degrees: Double, lat: Double, long: Double): Point { val deg = Math.toRadians(degrees) - val distancesInMeters = 8046.72 // 2.5 miles + val distancesInMeters = 16093.44// 10 miles val radiusOfEarthInMeters = 6378137 - if (degrees in 0.1..89.9) { - - } - if (degrees in 90.1..179.9) { - - } - if (degrees in 180.1..2.0) { - - } - if (degrees in 270.0..360.0) { - - } - val x = - long + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) / cos(lat) * cos( + long + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * cos( deg ) val y = - lat + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * sin(deg) // sin(lat) * sin(deg) + lat + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * sin(deg) return Point.fromLngLat(x, y) } From 4b1516da292d30794cf7adb761623923bcc0721a Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 24 Feb 2022 08:44:32 -0500 Subject: [PATCH 27/51] fixed lat,lon text on dialog window --- .../com/geeksville/mesh/ui/MapFragment.kt | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 75b6de300..bd34ad6c4 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -464,8 +464,8 @@ class MapFragment : ScreenFragment("Map"), Logging { R.drawable.baseline_location_on_white_24dp )!! .toBitmap() - pointLong = String.format("%.2f", it.longitude()) - pointLat = String.format("%.2f", it.latitude()) + pointLong = String.format("%.6f", it.longitude()) + pointLat = String.format("%.6f", it.latitude()) point = Point.fromLngLat(it.longitude(), it.latitude()) @@ -516,7 +516,7 @@ class MapFragment : ScreenFragment("Map"), Logging { private fun calculateCoordinate(degrees: Double, lat: Double, long: Double): Point { val deg = Math.toRadians(degrees) - val distancesInMeters = 16093.44// 10 miles + val distancesInMeters = 1609.344 * 10 // 1609.344 is 1 mile in meters -> multiplier will be user specified up to a max of 10 val radiusOfEarthInMeters = 6378137 val x = long + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * cos( @@ -527,18 +527,6 @@ class MapFragment : ScreenFragment("Map"), Logging { return Point.fromLngLat(x, y) } - /* - 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) - } - } - } - */ - private fun updateStylePackDownloadProgress( progress: Long, max: Long, @@ -582,12 +570,12 @@ class MapFragment : ScreenFragment("Map"), Logging { if (this::pointLat.isInitialized && this::pointLat.isInitialized) { val latText = mapDownloadView.findViewById(R.id.longitude) - "Lat: $pointLong".also { + "Lon: $pointLong".also { latText.text = it View.VISIBLE.also { latText.visibility = View.VISIBLE } } val longText = mapDownloadView.findViewById(R.id.latitude) - "Long: $pointLat".also { + "Lat: $pointLat".also { longText.text = it View.VISIBLE.also { longText.visibility = View.VISIBLE } } @@ -646,9 +634,7 @@ class MapFragment : ScreenFragment("Map"), Logging { .center(point) .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) //debug(userStyleURI) - it?.loadStyleUri(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) { - - } + it?.loadStyleUri(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) } // Open up Downloaded Region managers mapView?.annotations?.createCircleAnnotationManager()?.create( From da99cee937b664c79739b7655539d07230dda81c Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 24 Feb 2022 08:51:27 -0500 Subject: [PATCH 28/51] Remove layers on download cancel --- .../com/geeksville/mesh/ui/MapFragment.kt | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index bd34ad6c4..7663f1eb7 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -443,16 +443,6 @@ class MapFragment : ScreenFragment("Map"), Logging { // prepareCancelButton() } - -// private fun addViewAnnotation(point: Point) { -// viewAnnotationManager?.addViewAnnotation( -// resId = R.layout.user_icon_menu, -// options = viewAnnotationOptions { -// geometry(point) -// } -// ) -// } - /** * OnLongClick of the map set a position marker. * If a user long-clicks again, the position of the first marker will be updated @@ -516,7 +506,8 @@ class MapFragment : ScreenFragment("Map"), Logging { private fun calculateCoordinate(degrees: Double, lat: Double, long: Double): Point { val deg = Math.toRadians(degrees) - val distancesInMeters = 1609.344 * 10 // 1609.344 is 1 mile in meters -> multiplier will be user specified up to a max of 10 + val distancesInMeters = + 1609.344 * 10 // 1609.344 is 1 mile in meters -> multiplier will be user specified up to a max of 10 val radiusOfEarthInMeters = 6378137 val x = long + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * cos( @@ -647,6 +638,13 @@ class MapFragment : ScreenFragment("Map"), Logging { .setNegativeButton( R.string.cancel ) { dialog, _ -> + mapView?.getMapboxMap()?.getStyle { style -> + style.removeStyleLayer(lineLayerId) + style.removeStyleSource(boundingBoxId) + style.removeStyleLayer(userTouchLayerId) + style.removeStyleSource(userTouchPositionId) + style.removeStyleImage(userPointImageId) + } removeOfflineRegions() //TODO: Add to offline manager window dialog.cancel() } From b07eb344d810ea1a22291a7bd5ab7e7ba8dc88f5 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 24 Feb 2022 17:53:40 -0500 Subject: [PATCH 29/51] Added doc comment --- .../java/com/geeksville/mesh/ui/MapFragment.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 7663f1eb7..4bdfb9590 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -65,7 +65,6 @@ class MapFragment : ScreenFragment("Map"), Logging { } } - //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 @@ -80,7 +79,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var handler: Handler private lateinit var binding: MapViewBinding private lateinit var mapNotAllowedBinding: MapNotAllowedBinding - private lateinit var viewAnnotationManager: ViewAnnotationManager private lateinit var pointLat: String private lateinit var pointLong: String private lateinit var userStyleURI: String @@ -504,10 +502,17 @@ class MapFragment : ScreenFragment("Map"), Logging { return@OnMapLongClickListener true } + /** + * Find's coordinates (Lat,Lon) a specified distance from given (lat,lon) using degrees to determine direction + * @param degrees Angle + * @param lat latitude position + * @param long longitude position + * @return Point + */ private fun calculateCoordinate(degrees: Double, lat: Double, long: Double): Point { val deg = Math.toRadians(degrees) val distancesInMeters = - 1609.344 * 10 // 1609.344 is 1 mile in meters -> multiplier will be user specified up to a max of 10 + 1609.344 * 5 // 1609.344 is 1 mile in meters -> multiplier will be user specified up to a max of 10 val radiusOfEarthInMeters = 6378137 val x = long + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * cos( @@ -616,15 +621,12 @@ class MapFragment : ScreenFragment("Map"), Logging { } } .setNeutralButton("View Regions") { dialog, _ -> - - //OfflineSwitch.getInstance().isMapboxStackConnected = false mapView?.getMapboxMap().also { it?.flyTo( CameraOptions.Builder() .zoom(ZOOM) .center(point) .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) - //debug(userStyleURI) it?.loadStyleUri(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) } // Open up Downloaded Region managers @@ -634,7 +636,6 @@ class MapFragment : ScreenFragment("Map"), Logging { .withCircleColor(Color.RED) ) } - .setNegativeButton( R.string.cancel ) { dialog, _ -> From 82e43cdf37147c008f8b3b1642badc19232d3a84 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Fri, 25 Feb 2022 22:39:39 -0500 Subject: [PATCH 30/51] minor changes --- .../com/geeksville/mesh/ui/MapFragment.kt | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 75b6de300..99d7a78bb 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -481,9 +481,8 @@ class MapFragment : ScreenFragment("Map"), Logging { val pointList = listOf(topRight, topLeft, bottomLeft, bottomRight, topRight) - val squareLineString = LineString.fromLngLats(pointList) + squareRegion = LineString.fromLngLats(pointList) - squareRegion = squareLineString//LineString.fromLngLats(circleCoordinateGenerator()) val geoJsonSource = geoJsonSource(boundingBoxId) { geometry(squareRegion) } @@ -495,7 +494,6 @@ class MapFragment : ScreenFragment("Map"), Logging { lineColor("#888") } - mapView?.getMapboxMap()?.getStyle()?.let { style -> userTouchPosition.geometry(point) if (!style.styleLayerExists(userTouchLayerId)) { @@ -527,18 +525,6 @@ class MapFragment : ScreenFragment("Map"), Logging { return Point.fromLngLat(x, y) } - /* - 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) - } - } - } - */ - private fun updateStylePackDownloadProgress( progress: Long, max: Long, @@ -614,7 +600,6 @@ class MapFragment : ScreenFragment("Map"), Logging { .setPositiveButton( "Save" ) { _, _ -> - if (uri.isVisible) { // Save URI userStyleURI = uri.text.toString() @@ -630,7 +615,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } else { // Tell user to select region val text = - "You must select a region on the map, long press the region you want to download" + "You must select a region on the map, long press to set the region you want to download" val duration = Toast.LENGTH_LONG val toast = Toast.makeText(requireContext(), text, duration) toast.show() From e333bb08e7dbc8d479c4f81e4575f96b78264ef5 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Mon, 28 Feb 2022 07:52:26 -0500 Subject: [PATCH 31/51] Updates from master --- app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index ac8353331..2a5b585e8 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -65,6 +65,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } } + //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 From b490d40d2e49c92ce34d7d5921bf5cf6951488b4 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Tue, 1 Mar 2022 09:28:37 -0500 Subject: [PATCH 32/51] Making progress. Still need to build the manager & clean up code --- .../com/geeksville/mesh/ui/MapFragment.kt | 123 ++++++++++++------ .../main/res/layout/adapter_region_layout.xml | 35 +++++ .../main/res/layout/dialog_map_download.xml | 27 +--- app/src/main/res/layout/dialog_map_name.xml | 18 +++ .../res/layout/offline_region_manager.xml | 14 ++ 5 files changed, 151 insertions(+), 66 deletions(-) create mode 100644 app/src/main/res/layout/adapter_region_layout.xml create mode 100644 app/src/main/res/layout/dialog_map_name.xml create mode 100644 app/src/main/res/layout/offline_region_manager.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 2a5b585e8..e0be2b5b4 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -7,18 +7,18 @@ import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.EditText -import android.widget.TextView -import android.widget.Toast +import android.widget.* import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer +import androidx.recyclerview.widget.RecyclerView import com.geeksville.android.GeeksvilleApplication import com.geeksville.android.Logging import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.R +import com.geeksville.mesh.databinding.AdapterRegionLayoutBinding import com.geeksville.mesh.databinding.MapNotAllowedBinding import com.geeksville.mesh.databinding.MapViewBinding import com.geeksville.mesh.model.UIViewModel @@ -44,7 +44,6 @@ import com.mapbox.maps.plugin.annotation.generated.createCircleAnnotationManager import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures -import com.mapbox.maps.viewannotation.ViewAnnotationManager import dagger.hilt.android.AndroidEntryPoint import kotlin.math.cos import kotlin.math.sin @@ -80,8 +79,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var handler: Handler private lateinit var binding: MapViewBinding private lateinit var mapNotAllowedBinding: MapNotAllowedBinding - private lateinit var pointLat: String - private lateinit var pointLong: String private lateinit var userStyleURI: String private lateinit var point: Point @@ -101,7 +98,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var squareRegion: Geometry - private val userTouchPositionId = "user-touch-position" private val userTouchLayerId = "user-touch-layer" private var nodePositions = GeoJsonSource(GeoJsonSource.Builder(nodeSourceId)) @@ -453,15 +449,13 @@ class MapFragment : ScreenFragment("Map"), Logging { R.drawable.baseline_location_on_white_24dp )!! .toBitmap() - pointLong = String.format("%.6f", it.longitude()) - pointLat = String.format("%.6f", it.latitude()) point = Point.fromLngLat(it.longitude(), it.latitude()) /* Calculate region from user specified position. - 10 miles NE,NW,SE,SW from user center point. - 100 Sq Mile Region + 5 miles NE,NW,SE,SW from user center point. + 25 Sq Mile Region */ val topRight = calculateCoordinate(45.0, point.latitude(), point.longitude()) val topLeft = calculateCoordinate(135.0, point.latitude(), point.longitude()) @@ -563,18 +557,6 @@ class MapFragment : ScreenFragment("Map"), Logging { val uri = mapDownloadView.findViewById(R.id.uri) val downloadRegionDialogFragment = AlertDialog.Builder(context) - if (this::pointLat.isInitialized && this::pointLat.isInitialized) { - val latText = mapDownloadView.findViewById(R.id.longitude) - "Lon: $pointLong".also { - latText.text = it - View.VISIBLE.also { latText.visibility = View.VISIBLE } - } - val longText = mapDownloadView.findViewById(R.id.latitude) - "Lat: $pointLat".also { - longText.text = it - View.VISIBLE.also { longText.visibility = View.VISIBLE } - } - } downloadRegionDialogFragment.setView(mapDownloadView) .setTitle("Download Region") @@ -603,12 +585,7 @@ class MapFragment : ScreenFragment("Map"), Logging { uri.setText("") // clear text } if ((this::point.isInitialized) && this::userStyleURI.isInitialized) { - //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) && !this::userStyleURI.isInitialized) { - downloadOfflineRegion() + saveDialog(userStyleURI) } else { // Tell user to select region val text = @@ -619,20 +596,22 @@ class MapFragment : ScreenFragment("Map"), Logging { } } .setNeutralButton("View Regions") { dialog, _ -> - mapView?.getMapboxMap().also { - it?.flyTo( - CameraOptions.Builder() - .zoom(ZOOM) - .center(point) - .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) - it?.loadStyleUri(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) - } + val regions = layoutInflater.inflate(R.layout.adapter_region_layout, null) + val regionFragment = AlertDialog.Builder(context) + regionFragment.setView(regions) + regionFragment.create() + regionFragment.show() + + // Open up Downloaded Region managers - mapView?.annotations?.createCircleAnnotationManager()?.create( - CircleAnnotationOptions() - .withPoint(point) - .withCircleColor(Color.RED) - ) +// mapView?.getMapboxMap().also { +// it?.flyTo( +// CameraOptions.Builder() +// .zoom(ZOOM) +// .center(point) +// .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) +// it?.loadStyleUri(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) +// } } .setNegativeButton( R.string.cancel @@ -651,6 +630,66 @@ class MapFragment : ScreenFragment("Map"), Logging { downloadRegionDialogFragment.create() downloadRegionDialogFragment.show() } + + class ViewHolder(itemView: AdapterRegionLayoutBinding) : + RecyclerView.ViewHolder(itemView.root) { + val offlineRegion: ImageView = itemView.offlineRegion + val regionName: TextView = itemView.regionName + } + + private fun saveDialog(styleURI: String = "") { + + val nameRegionDialog = AlertDialog.Builder(context) + nameRegionDialog.setView(R.layout.dialog_map_name) + nameRegionDialog.setTitle("Name") + nameRegionDialog.setNegativeButton("Cancel") { dialog, _ -> + dialog.cancel() + } + nameRegionDialog.setPositiveButton("Save") { _, _ -> + downloadOfflineRegion(styleURI) + } + nameRegionDialog.create() + nameRegionDialog.show() + } + + private val offlineRegionAdapter = object : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val inflater = LayoutInflater.from(requireContext()) + + val regionViewBinding = AdapterRegionLayoutBinding.inflate(inflater, parent, false) + + return ViewHolder(regionViewBinding) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + var regionList = mutableListOf() + val region = regionList[position] + val name = "" + tileStore.getAllTileRegions { expected -> + if (expected.isValue) { + expected.value?.let { tileRegionList -> + regionList = tileRegionList + } + } + } + } + + override fun getItemCount(): Int { + var count = 0 + tileStore.getAllTileRegions { expected -> + if (expected.isValue) { + expected.value?.let { tileRegionList -> + count = tileRegionList.size + } + } + expected.error?.let { tileRegionError -> + debug("TileRegionError: $tileRegionError") + } + } + return count + } + + } } diff --git a/app/src/main/res/layout/adapter_region_layout.xml b/app/src/main/res/layout/adapter_region_layout.xml new file mode 100644 index 000000000..71fef8efb --- /dev/null +++ b/app/src/main/res/layout/adapter_region_layout.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_map_download.xml b/app/src/main/res/layout/dialog_map_download.xml index 1feed4287..7c205e88b 100644 --- a/app/src/main/res/layout/dialog_map_download.xml +++ b/app/src/main/res/layout/dialog_map_download.xml @@ -1,30 +1,9 @@ - - - - - - - + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/offline_region_manager.xml b/app/src/main/res/layout/offline_region_manager.xml new file mode 100644 index 000000000..397e1a8c6 --- /dev/null +++ b/app/src/main/res/layout/offline_region_manager.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file From 50c762d5c8a3c5a234a0aa2e93e945e97e562859 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 2 Mar 2022 08:15:39 -0500 Subject: [PATCH 33/51] removed unused imports --- .../com/geeksville/mesh/ui/MapFragment.kt | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index e0be2b5b4..b4de6da2f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -38,9 +38,6 @@ import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource import com.mapbox.maps.extension.style.sources.generated.geoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions import com.mapbox.maps.plugin.animation.flyTo -import com.mapbox.maps.plugin.annotation.annotations -import com.mapbox.maps.plugin.annotation.generated.CircleAnnotationOptions -import com.mapbox.maps.plugin.annotation.generated.createCircleAnnotationManager import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures @@ -96,6 +93,7 @@ class MapFragment : ScreenFragment("Map"), Logging { private var stylePackCancelable: Cancelable? = null private var tilePackCancelable: Cancelable? = null + private lateinit var regionName: String private lateinit var squareRegion: Geometry private val userTouchPositionId = "user-touch-position" @@ -586,6 +584,8 @@ class MapFragment : ScreenFragment("Map"), Logging { } if ((this::point.isInitialized) && this::userStyleURI.isInitialized) { saveDialog(userStyleURI) + } else if (this::point.isInitialized && !this::userStyleURI.isInitialized) { + saveDialog() } else { // Tell user to select region val text = @@ -638,18 +638,27 @@ class MapFragment : ScreenFragment("Map"), Logging { } private fun saveDialog(styleURI: String = "") { - + val regionDialogNameView = layoutInflater.inflate(R.layout.dialog_map_name, null) + val userInput = regionDialogNameView.findViewById(R.id.mapName) val nameRegionDialog = AlertDialog.Builder(context) - nameRegionDialog.setView(R.layout.dialog_map_name) - nameRegionDialog.setTitle("Name") + nameRegionDialog.setView(regionDialogNameView) + + nameRegionDialog.setTitle("Set Region Name") nameRegionDialog.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel() } nameRegionDialog.setPositiveButton("Save") { _, _ -> - downloadOfflineRegion(styleURI) + if (userInput.text.isEmpty()) { + // Error out dialog + } else { + this.regionName = userInput.text.toString() + userInput.setText("") + downloadOfflineRegion(styleURI) + } } nameRegionDialog.create() nameRegionDialog.show() + } private val offlineRegionAdapter = object : RecyclerView.Adapter() { From 07dd8a826fcf3523af0983e8cdfefadca9f641a9 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 2 Mar 2022 08:16:07 -0500 Subject: [PATCH 34/51] extracted resource to strings.xml --- app/src/main/res/layout/dialog_map_name.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/dialog_map_name.xml b/app/src/main/res/layout/dialog_map_name.xml index cf63fa138..03a497065 100644 --- a/app/src/main/res/layout/dialog_map_name.xml +++ b/app/src/main/res/layout/dialog_map_name.xml @@ -12,7 +12,7 @@ android:layout_marginTop="16dp" android:layout_marginRight="4dp" android:layout_marginBottom="4dp" - android:hint="Name" + android:hint="@string/name" android:inputType="text" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ef69dcd22..d5dbe5905 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -126,4 +126,5 @@ Long Range / Slow Style Selection Download Region + Name \ No newline at end of file From ee68c98e98f893ff19be43b81a467fa84792a106 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 2 Mar 2022 09:04:31 -0500 Subject: [PATCH 35/51] added logic to validate map naming window --- .../com/geeksville/mesh/ui/MapFragment.kt | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index b4de6da2f..280048c66 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -1,6 +1,7 @@ package com.geeksville.mesh.ui import android.app.AlertDialog +import android.content.DialogInterface import android.graphics.Color import android.os.Bundle import android.os.Handler @@ -647,18 +648,27 @@ class MapFragment : ScreenFragment("Map"), Logging { nameRegionDialog.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel() } - nameRegionDialog.setPositiveButton("Save") { _, _ -> - if (userInput.text.isEmpty()) { - // Error out dialog - } else { - this.regionName = userInput.text.toString() - userInput.setText("") - downloadOfflineRegion(styleURI) - } - } - nameRegionDialog.create() - nameRegionDialog.show() + nameRegionDialog.setPositiveButton( + "Save", null + ) + val dialog = nameRegionDialog.create() + dialog.show() + dialog.getButton(AlertDialog.BUTTON_POSITIVE) + .setOnClickListener { + if (userInput.text.isEmpty()) { + val text = + "You must enter a name" + val duration = Toast.LENGTH_LONG + val toast = Toast.makeText(requireContext(), text, duration) + toast.show() + } else { + this.regionName = userInput.text.toString() + userInput.setText("") + dialog.dismiss() + downloadOfflineRegion(styleURI) + } + } } private val offlineRegionAdapter = object : RecyclerView.Adapter() { From c622b72a539e2e4bbd27732732d392a094830f60 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 2 Mar 2022 15:29:14 -0500 Subject: [PATCH 36/51] Fixed some logic paths with the menu --- .../com/geeksville/mesh/ui/MapFragment.kt | 89 ++++++++++++------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 280048c66..be5fa7295 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -77,9 +77,9 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var handler: Handler private lateinit var binding: MapViewBinding private lateinit var mapNotAllowedBinding: MapNotAllowedBinding - private lateinit var userStyleURI: String + private var userStyleURI: String? = null - private lateinit var point: Point + private var point: Point? = null private val model: UIViewModel by activityViewModels() @@ -346,7 +346,7 @@ class MapFragment : ScreenFragment("Map"), Logging { .metadata(Value(STYLE_PACK_METADATA)) .build(), { progress -> - // Update the download progress to UI + //TODO: Update the download progress to UI updateStylePackDownloadProgress( progress.completedResourceCount, progress.requiredResourceCount @@ -434,7 +434,6 @@ class MapFragment : ScreenFragment("Map"), Logging { debug("TileRegionError: $it") } } - // prepareCancelButton() } /** @@ -456,10 +455,10 @@ class MapFragment : ScreenFragment("Map"), Logging { 5 miles NE,NW,SE,SW from user center point. 25 Sq Mile Region */ - val topRight = calculateCoordinate(45.0, point.latitude(), point.longitude()) - val topLeft = calculateCoordinate(135.0, point.latitude(), point.longitude()) - val bottomLeft = calculateCoordinate(225.0, point.latitude(), point.longitude()) - val bottomRight = calculateCoordinate(315.0, point.latitude(), point.longitude()) + val topRight = calculateCoordinate(45.0, point?.latitude()!!, point?.longitude()!!) + val topLeft = calculateCoordinate(135.0, point?.latitude()!!, point?.longitude()!!) + val bottomLeft = calculateCoordinate(225.0, point?.latitude()!!, point?.longitude()!!) + val bottomRight = calculateCoordinate(315.0, point?.latitude()!!, point?.longitude()!!) val pointList = listOf(topRight, topLeft, bottomLeft, bottomRight, topRight) @@ -477,7 +476,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } mapView?.getMapboxMap()?.getStyle()?.let { style -> - userTouchPosition.geometry(point) + userTouchPosition.geometry(point!!) if (!style.styleLayerExists(userTouchLayerId)) { style.addImage(userPointImageId, userDefinedPointImg) style.addSource(userTouchPosition) @@ -576,27 +575,9 @@ class MapFragment : ScreenFragment("Map"), Logging { } } .setPositiveButton( - "Save" - ) { _, _ -> - if (uri.isVisible) { - // Save URI - userStyleURI = uri.text.toString() - uri.setText("") // clear text - } - if ((this::point.isInitialized) && this::userStyleURI.isInitialized) { - saveDialog(userStyleURI) - } else if (this::point.isInitialized && !this::userStyleURI.isInitialized) { - saveDialog() - } else { - // Tell user to select region - val text = - "You must select a region on the map, long press to set the region you want to download" - val duration = Toast.LENGTH_LONG - val toast = Toast.makeText(requireContext(), text, duration) - toast.show() - } - } - .setNeutralButton("View Regions") { dialog, _ -> + "Save", null + ) + .setNeutralButton("View Regions") { _, _ -> val regions = layoutInflater.inflate(R.layout.adapter_region_layout, null) val regionFragment = AlertDialog.Builder(context) regionFragment.setView(regions) @@ -618,6 +599,8 @@ class MapFragment : ScreenFragment("Map"), Logging { R.string.cancel ) { dialog, _ -> mapView?.getMapboxMap()?.getStyle { style -> + point = null + userStyleURI = null style.removeStyleLayer(lineLayerId) style.removeStyleSource(boundingBoxId) style.removeStyleLayer(userTouchLayerId) @@ -628,8 +611,48 @@ class MapFragment : ScreenFragment("Map"), Logging { dialog.cancel() } - downloadRegionDialogFragment.create() - downloadRegionDialogFragment.show() + val dialog = downloadRegionDialogFragment.create() + dialog.show() + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + if (uri.isVisible) { + if (uri.text.isNotEmpty()) { + // Save URI + userStyleURI = uri.text.toString() + uri.setText("") // clear text + } + } + if ((this.point != null) && (this.userStyleURI != null)) { + if (this.userStyleURI!!.isNotEmpty()) { + saveDialog(userStyleURI!!) + dialog.dismiss() + } else { + Toast.makeText( + requireContext(), + "Style URI cannot be empty", + Toast.LENGTH_SHORT + ).show() + } + } else if (this.point != null) { + if (this.userStyleURI == null && uri.isVisible) { + Toast.makeText( + requireContext(), + "Style URI cannot be empty", + Toast.LENGTH_SHORT + ).show() + } else { + saveDialog() + dialog.dismiss() + } + } else { + dialog.dismiss() + // Tell user to select region + Toast.makeText( + requireContext(), + "You must select a region on the map, long press to set the region you want to download", + Toast.LENGTH_LONG + ).show() + } + } } class ViewHolder(itemView: AdapterRegionLayoutBinding) : @@ -659,7 +682,7 @@ class MapFragment : ScreenFragment("Map"), Logging { if (userInput.text.isEmpty()) { val text = "You must enter a name" - val duration = Toast.LENGTH_LONG + val duration = Toast.LENGTH_SHORT val toast = Toast.makeText(requireContext(), text, duration) toast.show() } else { From cee1b0e9f3179b049b13047168f4d73350c9755d Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Wed, 2 Mar 2022 18:46:50 -0500 Subject: [PATCH 37/51] Testing map with custom style pack --- .../com/geeksville/mesh/ui/MapFragment.kt | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index be5fa7295..222a02681 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -578,22 +578,25 @@ class MapFragment : ScreenFragment("Map"), Logging { "Save", null ) .setNeutralButton("View Regions") { _, _ -> - val regions = layoutInflater.inflate(R.layout.adapter_region_layout, null) - val regionFragment = AlertDialog.Builder(context) - regionFragment.setView(regions) - regionFragment.create() - regionFragment.show() - +// val regions = layoutInflater.inflate(R.layout.adapter_region_layout, null) +// val regionFragment = AlertDialog.Builder(context) +// regionFragment.setView(regions) +// regionFragment.create() +// regionFragment.show() // Open up Downloaded Region managers -// mapView?.getMapboxMap().also { -// it?.flyTo( -// CameraOptions.Builder() -// .zoom(ZOOM) -// .center(point) -// .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) -// it?.loadStyleUri(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) -// } + mapView?.getMapboxMap().also { + it?.flyTo( + CameraOptions.Builder() + .zoom(ZOOM) + .center(point) + .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) + if (userStyleURI != null) { + it?.loadStyleUri(userStyleURI.toString()) + } else { + it?.loadStyleUri(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) + } + } } .setNegativeButton( R.string.cancel From 217806f93e4ac5bbeb35d491e18e34711cfd7efc Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Sun, 6 Mar 2022 19:52:49 -0500 Subject: [PATCH 38/51] Simplifying offline flow --- .../com/geeksville/mesh/ui/MapFragment.kt | 92 ++++--------------- 1 file changed, 19 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 222a02681..91bf4424f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -301,7 +301,6 @@ class MapFragment : ScreenFragment("Map"), Logging { v.gestures.rotateEnabled = false v.gestures.addOnMapLongClickListener(this.longClick) - v.gestures.addOnMapClickListener(this.click) // Provide initial positions model.nodeDB.nodes.value?.let { nodes -> @@ -475,6 +474,10 @@ class MapFragment : ScreenFragment("Map"), Logging { lineColor("#888") } + if (point != null) { + binding.downloadRegion.visibility = View.VISIBLE + } + mapView?.getMapboxMap()?.getStyle()?.let { style -> userTouchPosition.geometry(point!!) if (!style.styleLayerExists(userTouchLayerId)) { @@ -490,6 +493,13 @@ class MapFragment : ScreenFragment("Map"), Logging { style.addLayer(lineLayer) } } + mapView?.getMapboxMap().also { mapboxMap -> + mapboxMap?.flyTo( + CameraOptions.Builder() + .zoom(ZOOM) + .center(point) + .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) + } return@OnMapLongClickListener true } @@ -531,20 +541,8 @@ class MapFragment : ScreenFragment("Map"), Logging { binding.stylePackDownloadProgress.progress = progress.toInt() } - private val click = OnMapClickListener { - //binding.fabStyleToggle.isVisible && - if (binding.downloadRegion.isVisible) { - //binding.fabStyleToggle.visibility = View.INVISIBLE - binding.downloadRegion.visibility = View.INVISIBLE - } else { - //binding.fabStyleToggle.visibility = View.VISIBLE - binding.downloadRegion.visibility = View.VISIBLE - } - return@OnMapClickListener true - } - companion object { - private const val ZOOM = 9.0 + private const val ZOOM = 11.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" @@ -610,6 +608,8 @@ class MapFragment : ScreenFragment("Map"), Logging { style.removeStyleSource(userTouchPositionId) style.removeStyleImage(userPointImageId) } + binding.downloadRegion.visibility = View.INVISIBLE + removeOfflineRegions() //TODO: Add to offline manager window dialog.cancel() } @@ -624,35 +624,14 @@ class MapFragment : ScreenFragment("Map"), Logging { uri.setText("") // clear text } } - if ((this.point != null) && (this.userStyleURI != null)) { - if (this.userStyleURI!!.isNotEmpty()) { - saveDialog(userStyleURI!!) - dialog.dismiss() - } else { - Toast.makeText( - requireContext(), - "Style URI cannot be empty", - Toast.LENGTH_SHORT - ).show() - } - } else if (this.point != null) { - if (this.userStyleURI == null && uri.isVisible) { - Toast.makeText( - requireContext(), - "Style URI cannot be empty", - Toast.LENGTH_SHORT - ).show() - } else { - saveDialog() - dialog.dismiss() - } - } else { + if (uri.isVisible && (this.userStyleURI != null)) { + downloadOfflineRegion(userStyleURI!!) dialog.dismiss() - // Tell user to select region + } else { Toast.makeText( requireContext(), - "You must select a region on the map, long press to set the region you want to download", - Toast.LENGTH_LONG + "Style URI cannot be empty", + Toast.LENGTH_SHORT ).show() } } @@ -664,39 +643,6 @@ class MapFragment : ScreenFragment("Map"), Logging { val regionName: TextView = itemView.regionName } - private fun saveDialog(styleURI: String = "") { - val regionDialogNameView = layoutInflater.inflate(R.layout.dialog_map_name, null) - val userInput = regionDialogNameView.findViewById(R.id.mapName) - val nameRegionDialog = AlertDialog.Builder(context) - nameRegionDialog.setView(regionDialogNameView) - - nameRegionDialog.setTitle("Set Region Name") - nameRegionDialog.setNegativeButton("Cancel") { dialog, _ -> - dialog.cancel() - } - nameRegionDialog.setPositiveButton( - "Save", null - ) - - val dialog = nameRegionDialog.create() - dialog.show() - dialog.getButton(AlertDialog.BUTTON_POSITIVE) - .setOnClickListener { - if (userInput.text.isEmpty()) { - val text = - "You must enter a name" - val duration = Toast.LENGTH_SHORT - val toast = Toast.makeText(requireContext(), text, duration) - toast.show() - } else { - this.regionName = userInput.text.toString() - userInput.setText("") - dialog.dismiss() - downloadOfflineRegion(styleURI) - } - } - } - private val offlineRegionAdapter = object : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(requireContext()) From 40b0cbad061f3a682aa804d1b8345e0561cc5ebc Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Sun, 6 Mar 2022 20:00:34 -0500 Subject: [PATCH 39/51] removed unused ui layout --- app/src/main/res/layout/dialog_map_name.xml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 app/src/main/res/layout/dialog_map_name.xml diff --git a/app/src/main/res/layout/dialog_map_name.xml b/app/src/main/res/layout/dialog_map_name.xml deleted file mode 100644 index 03a497065..000000000 --- a/app/src/main/res/layout/dialog_map_name.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - \ No newline at end of file From cc295d0fb374f5087249d489282e729294a3cf44 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Mon, 7 Mar 2022 08:10:06 -0500 Subject: [PATCH 40/51] Simplified some logic --- .../com/geeksville/mesh/ui/MapFragment.kt | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 91bf4424f..4dba12fe2 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -622,17 +622,19 @@ class MapFragment : ScreenFragment("Map"), Logging { // Save URI userStyleURI = uri.text.toString() uri.setText("") // clear text + + downloadOfflineRegion(userStyleURI!!) + dialog.dismiss() + } else { + Toast.makeText( + requireContext(), + "Style URI cannot be empty", + Toast.LENGTH_SHORT + ).show() } - } - if (uri.isVisible && (this.userStyleURI != null)) { - downloadOfflineRegion(userStyleURI!!) - dialog.dismiss() } else { - Toast.makeText( - requireContext(), - "Style URI cannot be empty", - Toast.LENGTH_SHORT - ).show() + downloadOfflineRegion() + dialog.dismiss() } } } From 3fde01588c629d948b2416ef2052981cb175372a Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Mon, 7 Mar 2022 09:44:29 -0500 Subject: [PATCH 41/51] Working on "view region" flow --- app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt | 4 +++- app/src/main/res/layout/map_view.xml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 4dba12fe2..f925615c4 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -592,7 +592,9 @@ class MapFragment : ScreenFragment("Map"), Logging { if (userStyleURI != null) { it?.loadStyleUri(userStyleURI.toString()) } else { - it?.loadStyleUri(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) + it?.getStyle().also { style -> + style?.removeStyleImage(userPointImageId) + } } } } diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml index 7a3ba9f60..0f4498c2b 100644 --- a/app/src/main/res/layout/map_view.xml +++ b/app/src/main/res/layout/map_view.xml @@ -53,6 +53,7 @@ android:layout_margin="16dp" android:backgroundTint="@color/buttonColor" android:contentDescription="@string/download_region" + android:visibility="invisible" android:src="@drawable/baseline_download_white_24dp" tools:background="@color/buttonColor" /> From 71724a6fd22d2b648d9873247c9ffa6092e07fbd Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Tue, 8 Mar 2022 13:49:22 -0500 Subject: [PATCH 42/51] Cleaning up some code --- .../java/com/geeksville/mesh/ui/MapFragment.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index f925615c4..e18943566 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -1,7 +1,6 @@ package com.geeksville.mesh.ui import android.app.AlertDialog -import android.content.DialogInterface import android.graphics.Color import android.os.Bundle import android.os.Handler @@ -39,7 +38,6 @@ import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource import com.mapbox.maps.extension.style.sources.generated.geoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions import com.mapbox.maps.plugin.animation.flyTo -import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures import dagger.hilt.android.AndroidEntryPoint @@ -248,7 +246,6 @@ class MapFragment : ScreenFragment("Map"), Logging { // 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. offlineManager.removeStylePack(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) - MapboxMap.clearData(resourceOptions) { it.error?.let { error -> debug(error) @@ -454,10 +451,12 @@ class MapFragment : ScreenFragment("Map"), Logging { 5 miles NE,NW,SE,SW from user center point. 25 Sq Mile Region */ + //____________________________________________________________________________________________ val topRight = calculateCoordinate(45.0, point?.latitude()!!, point?.longitude()!!) val topLeft = calculateCoordinate(135.0, point?.latitude()!!, point?.longitude()!!) val bottomLeft = calculateCoordinate(225.0, point?.latitude()!!, point?.longitude()!!) val bottomRight = calculateCoordinate(315.0, point?.latitude()!!, point?.longitude()!!) + //____________________________________________________________________________________________ val pointList = listOf(topRight, topLeft, bottomLeft, bottomRight, topRight) @@ -505,18 +504,18 @@ class MapFragment : ScreenFragment("Map"), Logging { /** * Find's coordinates (Lat,Lon) a specified distance from given (lat,lon) using degrees to determine direction - * @param degrees Angle - * @param lat latitude position - * @param long longitude position + * @param degrees degree of desired position from current position. (center point is 0,0 and desired point, top right corner, is 45 degrees from 0,0) + * @param lat latitude position (current position lat) + * @param lon longitude position (current position lon) * @return Point */ - private fun calculateCoordinate(degrees: Double, lat: Double, long: Double): Point { + private fun calculateCoordinate(degrees: Double, lat: Double, lon: Double): Point { val deg = Math.toRadians(degrees) val distancesInMeters = 1609.344 * 5 // 1609.344 is 1 mile in meters -> multiplier will be user specified up to a max of 10 val radiusOfEarthInMeters = 6378137 val x = - long + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * cos( + lon + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * cos( deg ) val y = @@ -591,6 +590,9 @@ class MapFragment : ScreenFragment("Map"), Logging { .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) if (userStyleURI != null) { it?.loadStyleUri(userStyleURI.toString()) + it?.getStyle().also { style -> + //TODO: Add box for downloaded region + } } else { it?.getStyle().also { style -> style?.removeStyleImage(userPointImageId) From 2f945545e5140d3811be0a09fe6195747b31d6f5 Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Tue, 8 Mar 2022 13:58:21 -0500 Subject: [PATCH 43/51] syncing changes --- .../java/com/geeksville/mesh/ui/MapFragment.kt | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 91bf4424f..bceec03f4 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -1,8 +1,6 @@ package com.geeksville.mesh.ui -import android.app.AlertDialog -import android.content.DialogInterface -import android.graphics.Color +import android.app.AlertDialog import android.graphics.Color import android.os.Bundle import android.os.Handler import android.view.LayoutInflater @@ -39,7 +37,6 @@ import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource import com.mapbox.maps.extension.style.sources.generated.geoJsonSource import com.mapbox.maps.plugin.animation.MapAnimationOptions import com.mapbox.maps.plugin.animation.flyTo -import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.OnMapLongClickListener import com.mapbox.maps.plugin.gestures.gestures import dagger.hilt.android.AndroidEntryPoint @@ -62,9 +59,6 @@ class MapFragment : ScreenFragment("Map"), Logging { } } - - //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 private val resourceOptions: ResourceOptions by lazy { @@ -74,7 +68,6 @@ class MapFragment : ScreenFragment("Map"), Logging { OfflineManager(resourceOptions) } - private lateinit var handler: Handler private lateinit var binding: MapViewBinding private lateinit var mapNotAllowedBinding: MapNotAllowedBinding private var userStyleURI: String? = null @@ -88,13 +81,12 @@ class MapFragment : ScreenFragment("Map"), Logging { private val labelLayerId = "label-layer" private val markerImageId = "my-marker-image" private val userPointImageId = "user-image" - private val boundingBoxId = "BOUNDING_BOX_ID" - private val lineLayerId = "lineLayer" + private val boundingBoxId = "bounding-box-id" + private val lineLayerId = "line-layer-id" private var stylePackCancelable: Cancelable? = null private var tilePackCancelable: Cancelable? = null - private lateinit var regionName: String private lateinit var squareRegion: Geometry private val userTouchPositionId = "user-touch-position" @@ -113,7 +105,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private val userTouchLayer = SymbolLayer(userTouchLayerId, userTouchPositionId) .iconImage(userPointImageId) .iconAnchor(IconAnchor.BOTTOM) - .iconAllowOverlap(true) private val labelLayer = SymbolLayer(labelLayerId, nodeSourceId) .textField(Expression.get("name")) @@ -575,7 +566,7 @@ class MapFragment : ScreenFragment("Map"), Logging { .setPositiveButton( "Save", null ) - .setNeutralButton("View Regions") { _, _ -> + .setNeutralButton("View Region") { _, _ -> // val regions = layoutInflater.inflate(R.layout.adapter_region_layout, null) // val regionFragment = AlertDialog.Builder(context) // regionFragment.setView(regions) From 0cb30171c1c98deb55ba4f9581c952eae4777083 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 9 Mar 2022 10:12:53 -0500 Subject: [PATCH 44/51] Trying to keep layers persistent across multiple styles --- .../com/geeksville/mesh/ui/MapFragment.kt | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index d0e136e09..37ff8e131 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -29,6 +29,8 @@ import com.mapbox.maps.* import com.mapbox.maps.dsl.cameraOptions import com.mapbox.maps.extension.style.expressions.generated.Expression import com.mapbox.maps.extension.style.layers.addLayer +import com.mapbox.maps.extension.style.layers.addPersistentLayer +import com.mapbox.maps.extension.style.layers.generated.LineLayer import com.mapbox.maps.extension.style.layers.generated.SymbolLayer import com.mapbox.maps.extension.style.layers.generated.lineLayer import com.mapbox.maps.extension.style.layers.properties.generated.* @@ -72,6 +74,9 @@ class MapFragment : ScreenFragment("Map"), Logging { private lateinit var mapNotAllowedBinding: MapNotAllowedBinding private var userStyleURI: String? = null + private lateinit var geoJsonSource: GeoJsonSource + private lateinit var lineLayer: LineLayer + private var point: Point? = null private val model: UIViewModel by activityViewModels() @@ -284,8 +289,8 @@ class MapFragment : ScreenFragment("Map"), Logging { if (it.isStyleLoaded) { it.addSource(nodePositions) it.addImage(markerImageId, markerIcon) - it.addLayer(nodeLayer) - it.addLayer(labelLayer) + it.addPersistentLayer(nodeLayer) + it.addPersistentLayer(labelLayer) } } @@ -455,10 +460,10 @@ class MapFragment : ScreenFragment("Map"), Logging { squareRegion = LineString.fromLngLats(pointList) - val geoJsonSource = geoJsonSource(boundingBoxId) { + geoJsonSource = geoJsonSource(boundingBoxId) { geometry(squareRegion) } - val lineLayer = lineLayer(lineLayerId, boundingBoxId) { + lineLayer = lineLayer(lineLayerId, boundingBoxId) { lineCap(LineCap.ROUND) lineJoin(LineJoin.MITER) lineOpacity(0.7) @@ -476,7 +481,7 @@ class MapFragment : ScreenFragment("Map"), Logging { style.addImage(userPointImageId, userDefinedPointImg) style.addSource(userTouchPosition) style.addSource(geoJsonSource) - style.addLayer(lineLayer) + style.addPersistentLayer(lineLayer) style.addLayer(userTouchLayer) } else { style.removeStyleLayer(lineLayerId) @@ -568,13 +573,6 @@ class MapFragment : ScreenFragment("Map"), Logging { "Save", null ) .setNeutralButton("View Region") { _, _ -> -// val regions = layoutInflater.inflate(R.layout.adapter_region_layout, null) -// val regionFragment = AlertDialog.Builder(context) -// regionFragment.setView(regions) -// regionFragment.create() -// regionFragment.show() - - // Open up Downloaded Region managers mapView?.getMapboxMap().also { it?.flyTo( CameraOptions.Builder() @@ -583,9 +581,6 @@ class MapFragment : ScreenFragment("Map"), Logging { .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) if (userStyleURI != null) { it?.loadStyleUri(userStyleURI.toString()) - it?.getStyle().also { style -> - //TODO: Add box for downloaded region - } } else { it?.getStyle().also { style -> style?.removeStyleImage(userPointImageId) From 0df9f8f5876ab01598f77adf5d09ce3868d63eab Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 9 Mar 2022 10:24:03 -0500 Subject: [PATCH 45/51] Removed un-used UI layout & related code. --- .../com/geeksville/mesh/ui/MapFragment.kt | 47 ------------------- .../main/res/layout/adapter_region_layout.xml | 35 -------------- 2 files changed, 82 deletions(-) delete mode 100644 app/src/main/res/layout/adapter_region_layout.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 37ff8e131..16ae39d3b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -12,12 +12,10 @@ import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer -import androidx.recyclerview.widget.RecyclerView import com.geeksville.android.GeeksvilleApplication import com.geeksville.android.Logging import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.R -import com.geeksville.mesh.databinding.AdapterRegionLayoutBinding import com.geeksville.mesh.databinding.MapNotAllowedBinding import com.geeksville.mesh.databinding.MapViewBinding import com.geeksville.mesh.model.UIViewModel @@ -630,51 +628,6 @@ class MapFragment : ScreenFragment("Map"), Logging { } } } - - class ViewHolder(itemView: AdapterRegionLayoutBinding) : - RecyclerView.ViewHolder(itemView.root) { - val offlineRegion: ImageView = itemView.offlineRegion - val regionName: TextView = itemView.regionName - } - - private val offlineRegionAdapter = object : RecyclerView.Adapter() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val inflater = LayoutInflater.from(requireContext()) - - val regionViewBinding = AdapterRegionLayoutBinding.inflate(inflater, parent, false) - - return ViewHolder(regionViewBinding) - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - var regionList = mutableListOf() - val region = regionList[position] - val name = "" - tileStore.getAllTileRegions { expected -> - if (expected.isValue) { - expected.value?.let { tileRegionList -> - regionList = tileRegionList - } - } - } - } - - override fun getItemCount(): Int { - var count = 0 - tileStore.getAllTileRegions { expected -> - if (expected.isValue) { - expected.value?.let { tileRegionList -> - count = tileRegionList.size - } - } - expected.error?.let { tileRegionError -> - debug("TileRegionError: $tileRegionError") - } - } - return count - } - - } } diff --git a/app/src/main/res/layout/adapter_region_layout.xml b/app/src/main/res/layout/adapter_region_layout.xml deleted file mode 100644 index 71fef8efb..000000000 --- a/app/src/main/res/layout/adapter_region_layout.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file From a291f785834c936760336959d7726fcf8f0ae41b Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Wed, 9 Mar 2022 10:24:31 -0500 Subject: [PATCH 46/51] Made FAB invisible until region is created for downloading --- app/src/main/res/layout/map_view.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml index 7a3ba9f60..1067f0772 100644 --- a/app/src/main/res/layout/map_view.xml +++ b/app/src/main/res/layout/map_view.xml @@ -43,7 +43,7 @@ - + From 58a20dc326382b69a7e182894e2e866c8e04be21 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 10 Mar 2022 08:17:00 -0500 Subject: [PATCH 47/51] Updating UI flow --- .../com/geeksville/mesh/ui/MapFragment.kt | 274 +++++++++--------- 1 file changed, 143 insertions(+), 131 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 16ae39d3b..845d32991 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -96,6 +96,8 @@ class MapFragment : ScreenFragment("Map"), Logging { private val userTouchLayerId = "user-touch-layer" private var nodePositions = GeoJsonSource(GeoJsonSource.Builder(nodeSourceId)) + private var tileRegionDownloadSuccess = false + private var stylePackDownloadSuccess = false private val userTouchPosition = GeoJsonSource(GeoJsonSource.Builder(userTouchPositionId)) @@ -200,32 +202,6 @@ class MapFragment : ScreenFragment("Map"), Logging { var mapView: MapView? = null - 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 @@ -241,7 +217,12 @@ class MapFragment : ScreenFragment("Map"), Logging { // 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. - offlineManager.removeStylePack(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) + + if (userStyleURI != null) { + offlineManager.removeStylePack(userStyleURI!!) + } else { + offlineManager.removeStylePack(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) + } MapboxMap.clearData(resourceOptions) { it.error?.let { error -> debug(error) @@ -313,118 +294,140 @@ class MapFragment : ScreenFragment("Map"), Logging { } private fun downloadOfflineRegion(styleURI: String = "") { + val style = styleURI.ifEmpty { mapView?.getMapboxMap() ?.getStyle()?.styleURI.toString() } - // 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. - // - - - - - - - - + if (OfflineSwitch.getInstance().isMapboxStackConnected) { + // 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. + // 1. Create style package with loadStylePack() call. - // 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. + // 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. - stylePackCancelable = offlineManager.loadStylePack( - style, - // Build Style pack load options - StylePackLoadOptions.Builder() - .glyphsRasterizationMode(GlyphsRasterizationMode.IDEOGRAPHS_RASTERIZED_LOCALLY) - .metadata(Value(STYLE_PACK_METADATA)) - .build(), - { progress -> - //TODO: Update the download progress to UI - updateStylePackDownloadProgress( - progress.completedResourceCount, - progress.requiredResourceCount - ) - }, - { expected -> + // 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. + + stylePackCancelable = offlineManager.loadStylePack( + style, + // Build Style pack load options + StylePackLoadOptions.Builder() + .glyphsRasterizationMode(GlyphsRasterizationMode.IDEOGRAPHS_RASTERIZED_LOCALLY) + .metadata(Value(STYLE_PACK_METADATA)) + .build(), + { progress -> + //TODO: Update the download progress to UI + updateStylePackDownloadProgress( + progress.completedResourceCount, + progress.requiredResourceCount + ) + }, + { expected -> + if (expected.isValue) { + expected.value?.let { stylePack -> + // Style pack download finishes successfully + debug("StylePack downloaded: $stylePack") + if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { + debug("Doing stuff") + binding.stylePackDownloadProgress.visibility = View.INVISIBLE + stylePackDownloadSuccess = true + } else { + debug("Waiting for tile region download to be finished.") + } + } + } + expected.error?.let { + Toast.makeText( + requireContext(), + "Unable to download style pack", + Toast.LENGTH_SHORT + ).show() + // Handle error occurred during the style pack download. + debug("StylePackError: $it") + } + } + ) + + // - - - - - - - - + + // 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. + + val tilesetDescriptor = offlineManager.createTilesetDescriptor( + TilesetDescriptorOptions.Builder() + .styleURI(style) + .minZoom(0) + .maxZoom(10) + .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( + TILE_REGION_ID, // Make this dynamic + TileRegionLoadOptions.Builder() + .geometry(squareRegion) + .descriptors(listOf(tilesetDescriptor)) + .metadata(Value(TILE_REGION_METADATA)) + .acceptExpired(true) + .networkRestriction(NetworkRestriction.NONE) + .build(), + { progress -> + updateTileRegionDownloadProgress( + progress.completedResourceCount, + progress.requiredResourceCount, + ) + } + ) { expected -> if (expected.isValue) { - expected.value?.let { stylePack -> - // Style pack download finishes successfully - debug("StylePack downloaded: $stylePack") + // Tile pack download finishes successfully + expected.value?.let { region -> + debug("TileRegion downloaded: $region") if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { - debug("Doing stuff") + debug("Finished tilepack download") binding.stylePackDownloadProgress.visibility = View.INVISIBLE + tileRegionDownloadSuccess = true } else { - debug("Waiting for tile region download to be finished.") + debug("Waiting for style pack download to be finished.") } } } expected.error?.let { - // Handle error occurred during the style pack download. - debug("StylePackError: $it") + Toast.makeText( + requireContext(), + "Unable to download TileRegion", + Toast.LENGTH_SHORT + ).show() + // Handle error occurred during the tile region download. + debug("TileRegionError: $it") } } - ) - - // - - - - - - - - - - // 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. - - val tilesetDescriptor = offlineManager.createTilesetDescriptor( - TilesetDescriptorOptions.Builder() - .styleURI(style) - .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( - TILE_REGION_ID, // Make this dynamic - TileRegionLoadOptions.Builder() - .geometry(squareRegion) - .descriptors(listOf(tilesetDescriptor)) - .metadata(Value(TILE_REGION_METADATA)) - .acceptExpired(true) - .networkRestriction(NetworkRestriction.NONE) - .build(), - { progress -> - updateTileRegionDownloadProgress( - progress.completedResourceCount, - progress.requiredResourceCount, - ) - } - ) { expected -> - if (expected.isValue) { - // Tile pack download finishes successfully - expected.value?.let { region -> - 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.") - } - } - } - expected.error?.let { - // Handle error occurred during the tile region download. - debug("TileRegionError: $it") - } + } else { + Toast.makeText( + requireContext(), + "You are not connected to the internet, you cannot download an offline map", + Toast.LENGTH_LONG + ).show() } } @@ -571,19 +574,28 @@ class MapFragment : ScreenFragment("Map"), Logging { "Save", null ) .setNeutralButton("View Region") { _, _ -> - mapView?.getMapboxMap().also { - it?.flyTo( - CameraOptions.Builder() - .zoom(ZOOM) - .center(point) - .build(), MapAnimationOptions.mapAnimationOptions { duration(1000) }) - if (userStyleURI != null) { - it?.loadStyleUri(userStyleURI.toString()) - } else { - it?.getStyle().also { style -> - style?.removeStyleImage(userPointImageId) + if (tileRegionDownloadSuccess && stylePackDownloadSuccess) { + mapView?.getMapboxMap().also { + it?.flyTo( + CameraOptions.Builder() + .zoom(ZOOM) + .center(point) + .build(), + MapAnimationOptions.mapAnimationOptions { duration(1000) }) + if (userStyleURI != null) { + it?.loadStyleUri(userStyleURI.toString()) + } else { + it?.getStyle().also { style -> + style?.removeStyleImage(userPointImageId) + } } } + } else { + Toast.makeText( + requireContext(), + "No downloaded region available", + Toast.LENGTH_SHORT + ).show() } } .setNegativeButton( From e58e12ad5e3272c1b989b2ddaf4a24e742825601 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 10 Mar 2022 08:52:23 -0500 Subject: [PATCH 48/51] Added hard-coded strings to translations --- .../java/com/geeksville/mesh/ui/MapFragment.kt | 16 ++++++++-------- app/src/main/res/values/strings.xml | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 845d32991..3808aeb95 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -345,7 +345,7 @@ class MapFragment : ScreenFragment("Map"), Logging { expected.error?.let { Toast.makeText( requireContext(), - "Unable to download style pack", + R.string.stylepack_download_error_alert, Toast.LENGTH_SHORT ).show() // Handle error occurred during the style pack download. @@ -415,7 +415,7 @@ class MapFragment : ScreenFragment("Map"), Logging { expected.error?.let { Toast.makeText( requireContext(), - "Unable to download TileRegion", + R.string.tileregion_error_alert, Toast.LENGTH_SHORT ).show() // Handle error occurred during the tile region download. @@ -425,7 +425,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } else { Toast.makeText( requireContext(), - "You are not connected to the internet, you cannot download an offline map", + R.string.download_region_connection_alert, Toast.LENGTH_LONG ).show() } @@ -553,7 +553,7 @@ class MapFragment : ScreenFragment("Map"), Logging { downloadRegionDialogFragment.setView(mapDownloadView) - .setTitle("Download Region") + .setTitle(R.string.download_region_dialog_title) .setMultiChoiceItems( R.array.MapMenuCheckbox, null, @@ -571,9 +571,9 @@ class MapFragment : ScreenFragment("Map"), Logging { } } .setPositiveButton( - "Save", null + R.string.save_btn, null ) - .setNeutralButton("View Region") { _, _ -> + .setNeutralButton(R.string.view_region_btn) { _, _ -> if (tileRegionDownloadSuccess && stylePackDownloadSuccess) { mapView?.getMapboxMap().also { it?.flyTo( @@ -593,7 +593,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } else { Toast.makeText( requireContext(), - "No downloaded region available", + R.string.no_download_region_alert, Toast.LENGTH_SHORT ).show() } @@ -630,7 +630,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } else { Toast.makeText( requireContext(), - "Style URI cannot be empty", + R.string.style_uri_empty_alert, Toast.LENGTH_SHORT ).show() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 893939168..3802ae091 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -130,4 +130,12 @@ Style Selection Download Region Name + View Region + Save + No downloaded region available + Style URI cannot be empty + Download Region + You are not connected to the internet, you cannot download an offline map + Unable to download TileRegion + Unable to download style pack \ No newline at end of file From 262808e9527e8da6e3cc58c64fbff609ffe2c034 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Thu, 10 Mar 2022 09:38:56 -0500 Subject: [PATCH 49/51] Looking into why boolean is not being set --- .../com/geeksville/mesh/ui/MapFragment.kt | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 3808aeb95..d94edf482 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -98,7 +98,6 @@ class MapFragment : ScreenFragment("Map"), Logging { private var tileRegionDownloadSuccess = false private var stylePackDownloadSuccess = false - private val userTouchPosition = GeoJsonSource(GeoJsonSource.Builder(userTouchPositionId)) @@ -574,29 +573,28 @@ class MapFragment : ScreenFragment("Map"), Logging { R.string.save_btn, null ) .setNeutralButton(R.string.view_region_btn) { _, _ -> - if (tileRegionDownloadSuccess && stylePackDownloadSuccess) { - mapView?.getMapboxMap().also { - it?.flyTo( - CameraOptions.Builder() - .zoom(ZOOM) - .center(point) - .build(), - MapAnimationOptions.mapAnimationOptions { duration(1000) }) - if (userStyleURI != null) { - it?.loadStyleUri(userStyleURI.toString()) - } else { - it?.getStyle().also { style -> - style?.removeStyleImage(userPointImageId) - } + mapView?.getMapboxMap().also { + it?.flyTo( + CameraOptions.Builder() + .zoom(ZOOM) + .center(point) + .build(), + MapAnimationOptions.mapAnimationOptions { duration(1000) }) + if (userStyleURI != null) { + it?.loadStyleUri(userStyleURI.toString()) + } else { + it?.getStyle().also { style -> + style?.removeStyleImage(userPointImageId) } } - } else { - Toast.makeText( - requireContext(), - R.string.no_download_region_alert, - Toast.LENGTH_SHORT - ).show() } +// } else { +// Toast.makeText( +// requireContext(), +// R.string.no_download_region_alert, +// Toast.LENGTH_SHORT +// ).show() +// } } .setNegativeButton( R.string.cancel From 0ebcf185a875bc698946edb4a21a758e7cc4fed0 Mon Sep 17 00:00:00 2001 From: PWRxPSYCHO Date: Fri, 11 Mar 2022 10:08:21 -0500 Subject: [PATCH 50/51] More tweaks --- .../com/geeksville/mesh/ui/MapFragment.kt | 76 +++++++++++-------- app/src/main/res/layout/map_view.xml | 64 ++++++++++++---- 2 files changed, 93 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index d94edf482..db768d69e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -219,14 +219,18 @@ class MapFragment : ScreenFragment("Map"), Logging { if (userStyleURI != null) { offlineManager.removeStylePack(userStyleURI!!) + mapView?.getMapboxMap()?.loadStyleUri(Style.OUTDOORS) } else { offlineManager.removeStylePack(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString()) + mapView?.getMapboxMap()?.loadStyleUri(Style.OUTDOORS) } MapboxMap.clearData(resourceOptions) { it.error?.let { error -> debug(error) } } + updateStylePackDownloadProgress(0, 0) + updateTileRegionDownloadProgress(0, 0) } /** @@ -300,6 +304,7 @@ class MapFragment : ScreenFragment("Map"), Logging { } if (OfflineSwitch.getInstance().isMapboxStackConnected) { + // 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. @@ -313,6 +318,8 @@ class MapFragment : ScreenFragment("Map"), Logging { // 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. + binding.stylePackDownloadProgress.visibility = View.VISIBLE + binding.stylePackText.visibility = View.VISIBLE stylePackCancelable = offlineManager.loadStylePack( style, // Build Style pack load options @@ -321,10 +328,9 @@ class MapFragment : ScreenFragment("Map"), Logging { .metadata(Value(STYLE_PACK_METADATA)) .build(), { progress -> - //TODO: Update the download progress to UI updateStylePackDownloadProgress( progress.completedResourceCount, - progress.requiredResourceCount + progress.requiredResourceCount, ) }, { expected -> @@ -333,9 +339,11 @@ class MapFragment : ScreenFragment("Map"), Logging { // Style pack download finishes successfully debug("StylePack downloaded: $stylePack") if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { - debug("Doing stuff") + debug("Style pack download complete") + binding.stylePackText.visibility = View.INVISIBLE binding.stylePackDownloadProgress.visibility = View.INVISIBLE stylePackDownloadSuccess = true + } else { debug("Waiting for tile region download to be finished.") } @@ -382,6 +390,8 @@ class MapFragment : ScreenFragment("Map"), Logging { // 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. + binding.tilePackText.visibility = View.VISIBLE + binding.tilePackDownloadProgress.visibility = View.VISIBLE tilePackCancelable = tileStore.loadTileRegion( TILE_REGION_ID, // Make this dynamic TileRegionLoadOptions.Builder() @@ -402,10 +412,12 @@ class MapFragment : ScreenFragment("Map"), Logging { // Tile pack download finishes successfully expected.value?.let { region -> debug("TileRegion downloaded: $region") - if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) { + if (binding.tilePackDownloadProgress.progress == binding.tilePackDownloadProgress.max) { debug("Finished tilepack download") - binding.stylePackDownloadProgress.visibility = View.INVISIBLE + binding.tilePackDownloadProgress.visibility = View.INVISIBLE + binding.tilePackText.visibility = View.INVISIBLE tileRegionDownloadSuccess = true + } else { debug("Waiting for style pack download to be finished.") } @@ -510,7 +522,7 @@ class MapFragment : ScreenFragment("Map"), Logging { private fun calculateCoordinate(degrees: Double, lat: Double, lon: Double): Point { val deg = Math.toRadians(degrees) val distancesInMeters = - 1609.344 * 5 // 1609.344 is 1 mile in meters -> multiplier will be user specified up to a max of 10 + 1609.344 * 2.5 // 1609.344 is 1 mile in meters -> multiplier will be user specified up to a max of 10 val radiusOfEarthInMeters = 6378137 val x = lon + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * cos( @@ -525,7 +537,6 @@ class MapFragment : ScreenFragment("Map"), Logging { progress: Long, max: Long, ) { - binding.stylePackDownloadProgress.visibility = View.VISIBLE binding.stylePackDownloadProgress.max = max.toInt() binding.stylePackDownloadProgress.progress = progress.toInt() } @@ -534,15 +545,15 @@ class MapFragment : ScreenFragment("Map"), Logging { progress: Long, max: Long, ) { - binding.stylePackDownloadProgress.max = max.toInt() - binding.stylePackDownloadProgress.progress = progress.toInt() + binding.tilePackDownloadProgress.max = max.toInt() + binding.tilePackDownloadProgress.progress = progress.toInt() } companion object { - private const val ZOOM = 11.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" + private const val ZOOM = 12.5 + private const val TILE_REGION_ID = "tile-region" + private const val STYLE_PACK_METADATA = "outdoor-style-pack" + private const val TILE_REGION_METADATA = "outdoor-tile-region" } private fun downloadRegionDialogFragment() { @@ -573,28 +584,29 @@ class MapFragment : ScreenFragment("Map"), Logging { R.string.save_btn, null ) .setNeutralButton(R.string.view_region_btn) { _, _ -> - mapView?.getMapboxMap().also { - it?.flyTo( - CameraOptions.Builder() - .zoom(ZOOM) - .center(point) - .build(), - MapAnimationOptions.mapAnimationOptions { duration(1000) }) - if (userStyleURI != null) { - it?.loadStyleUri(userStyleURI.toString()) - } else { - it?.getStyle().also { style -> - style?.removeStyleImage(userPointImageId) + if (tileRegionDownloadSuccess && stylePackDownloadSuccess) { + mapView?.getMapboxMap().also { + it?.flyTo( + CameraOptions.Builder() + .zoom(ZOOM) + .center(point) + .build(), + MapAnimationOptions.mapAnimationOptions { duration(1000) }) + if (userStyleURI != null) { + it?.loadStyleUri(userStyleURI.toString()) + } else { + it?.getStyle().also { style -> + style?.removeStyleImage(userPointImageId) + } } } + } else { + Toast.makeText( + requireContext(), + R.string.no_download_region_alert, + Toast.LENGTH_SHORT + ).show() } -// } else { -// Toast.makeText( -// requireContext(), -// R.string.no_download_region_alert, -// Toast.LENGTH_SHORT -// ).show() -// } } .setNegativeButton( R.string.cancel diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml index 1067f0772..7b1c190c3 100644 --- a/app/src/main/res/layout/map_view.xml +++ b/app/src/main/res/layout/map_view.xml @@ -13,20 +13,55 @@ + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:gravity="center" + android:orientation="vertical" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="@+id/mapView"> + + + android:progressTint="@color/colorPrimary" + android:visibility="invisible" /> + + + + - - - - - - - - - + + + + + + + + + - \ No newline at end of file From 448fe99cf57f59c2c21a61fd9eb2c4e181e1853b Mon Sep 17 00:00:00 2001 From: Jackson Rosenthal Date: Sat, 12 Mar 2022 17:09:20 -0500 Subject: [PATCH 51/51] setup "dev option" for offline maps --- .../com/geeksville/mesh/ui/MapFragment.kt | 31 ++++++++++--------- app/src/main/res/values/strings.xml | 3 +- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index db768d69e..2381276e0 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -59,7 +59,14 @@ class MapFragment : ScreenFragment("Map"), Logging { } } - //TODO: Update download animation + /** + * DEVELOPER OPTION TO ENABLE OFFLINE MAPS + * Set this variable to true to enable offline maps + */ + //___________________________________________________ + private val offlineEnabled = false + //___________________________________________________ + private val resourceOptions: ResourceOptions by lazy { ResourceOptions.Builder().applyDefaultParams(requireContext()).tileStore(tileStore).build() @@ -277,14 +284,15 @@ class MapFragment : ScreenFragment("Map"), Logging { } v.gestures.rotateEnabled = false - v.gestures.addOnMapLongClickListener(this.longClick) + if (offlineEnabled) { + v.gestures.addOnMapLongClickListener(this.longClick) + } // Provide initial positions model.nodeDB.nodes.value?.let { nodes -> onNodesChanged(nodes.values) } } - // Any times nodes change update our map model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { nodes -> if (isViewVisible) @@ -343,19 +351,16 @@ class MapFragment : ScreenFragment("Map"), Logging { binding.stylePackText.visibility = View.INVISIBLE binding.stylePackDownloadProgress.visibility = View.INVISIBLE stylePackDownloadSuccess = true - } else { debug("Waiting for tile region download to be finished.") } } } expected.error?.let { - Toast.makeText( - requireContext(), - R.string.stylepack_download_error_alert, - Toast.LENGTH_SHORT - ).show() + stylePackDownloadSuccess = false // Handle error occurred during the style pack download. + binding.stylePackText.visibility = View.INVISIBLE + binding.stylePackDownloadProgress.visibility = View.INVISIBLE debug("StylePackError: $it") } } @@ -424,12 +429,10 @@ class MapFragment : ScreenFragment("Map"), Logging { } } expected.error?.let { - Toast.makeText( - requireContext(), - R.string.tileregion_error_alert, - Toast.LENGTH_SHORT - ).show() + tileRegionDownloadSuccess = false // Handle error occurred during the tile region download. + binding.tilePackDownloadProgress.visibility = View.INVISIBLE + binding.tilePackText.visibility = View.INVISIBLE debug("TileRegionError: $it") } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3802ae091..e0e11c9d8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -136,6 +136,5 @@ Style URI cannot be empty Download Region You are not connected to the internet, you cannot download an offline map - Unable to download TileRegion - Unable to download style pack + Unable to download style pack \ No newline at end of file