diff options
Diffstat (limited to 'androidApp/src/main/java/mx/trackermap/TrackerMap/android/map')
3 files changed, 288 insertions, 39 deletions
diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt index 1ba8fd6..87fb44a 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt @@ -27,7 +27,7 @@ import mx.trackermap.TrackerMap.utils.MarkerType typealias SetupCallback = () -> Unit typealias MarkerCallback = (Int?) -> Unit -class MapFragment : GlobeMapFragment() { +open class MapFragment : GlobeMapFragment() { private var loader: QuadImageLoader? = null @@ -68,8 +68,6 @@ class MapFragment : GlobeMapFragment() { override fun controlHasStarted() { Log.d("MapFragment", "controlHasStarted") - hasStarted = true - setupCallbacks.forEach { it() } if (tileInfo == null) { val layer = MapLayer.defaultLayer @@ -96,6 +94,14 @@ class MapFragment : GlobeMapFragment() { val latitude = 23.191 * Math.PI / 180 val longitude = -100.36 * Math.PI / 180 mapControl.setPositionGeo(longitude, latitude, 0.4) + + hasStarted = true + setupCallbacks.forEach { it() } + } + + override fun onStop() { + super.onStop() + hasStarted = false } override fun mapDidStopMoving( @@ -341,19 +347,29 @@ class MapFragment : GlobeMapFragment() { height: Double = 0.00001, animated: Boolean = true ) { - mapControl?.addPostSurfaceRunnable { - val lat = latitude * Math.PI / 180 - val lon = longitude * Math.PI / 180 - // Ensure height is equal or higher than bottom limit - val z = height.coerceAtLeast(mapControl.zoomLimitMin) - if (animated) { - mapControl.animatePositionGeo(lon, lat, z, 0.2) - } else { - mapControl.setPositionGeo(lon, lat, z) - } + val lat = latitude * Math.PI / 180 + val lon = longitude * Math.PI / 180 + // Ensure height is equal or higher than bottom limit + val z = height.coerceAtLeast(mapControl.zoomLimitMin) + if (animated) { + mapControl.animatePositionGeo(lon, lat, z, 0.2) + } else { + mapControl.setPositionGeo(lon, lat, z) } } + fun zoomIn() { + val pos = mapControl.positionGeo.toPoint2d().toDegrees() + val zoom = mapControl.currentMapScale() / 2 + focusOn(pos.y, pos.x, mapControl.heightForMapScale(zoom)) + } + + fun zoomOut() { + val pos = mapControl.positionGeo.toPoint2d().toDegrees() + val zoom = mapControl.currentMapScale() * 2 + focusOn(pos.y, pos.x, mapControl.heightForMapScale(zoom)) + } + private fun tileInfoSetCacheDir(url: String, tileInfo: TileInfoNew): TileInfoNew? { return context?.let { val cacheDirName = url.toByteArray(Charsets.UTF_8).md5().hex @@ -366,14 +382,15 @@ class MapFragment : GlobeMapFragment() { } fun updateLayer(layer: MapLayer.Type) { - mapControl?.addPostSurfaceRunnable { - MapLayer.layers[layer]?.let { - val tileInfo = RemoteTileInfoNew(it.url, it.minZoom, it.maxZoom) - this.tileInfo = tileInfoSetCacheDir(it.url, tileInfo) - this.tileInfo?.let { t -> - loader?.changeTileInfo(t) - setZoomLimits(tileInfo.minZoom, tileInfo.maxZoom) - } + MapLayer.layers[layer]?.let { + val tileInfo = RemoteTileInfoNew(it.url, it.minZoom, it.maxZoom) + this.tileInfo = tileInfoSetCacheDir(it.url, tileInfo) + this.tileInfo?.let { t -> + loader?.changeTileInfo(t) + setZoomLimits(tileInfo.minZoom, tileInfo.maxZoom) + + val pos = mapControl.positionGeo.toPoint2d().toDegrees() + focusOn(pos.y, pos.x, mapControl.positionGeo.z, animated = true) } } } diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapWrapperFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapWrapperFragment.kt new file mode 100644 index 0000000..01587ae --- /dev/null +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapWrapperFragment.kt @@ -0,0 +1,234 @@ +package mx.trackermap.TrackerMap.android.map + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.text.HtmlCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.commit +import mx.trackermap.TrackerMap.android.R +import mx.trackermap.TrackerMap.android.databinding.FragmentMapWrapperBinding +import mx.trackermap.TrackerMap.android.shared.MarkerTransformations +import mx.trackermap.TrackerMap.android.shared.Utils +import mx.trackermap.TrackerMap.client.models.* + +class MapWrapperFragment: Fragment() { + private var _binding: FragmentMapWrapperBinding? = null + private val binding get() = _binding!! + + private val mapFragment = MapFragment() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentMapWrapperBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupEvents() + setupViews() + } + + override fun onResume() { + super.onResume() + initializeMap() + } + + override fun onPause() { + super.onPause() + removeMap() + } + + override fun onDestroy() { + super.onDestroy() + _binding = null + } + + private fun initializeMap() { + Log.d("MapWrapperFragment", "initializeMap()") + childFragmentManager.commit { + replace(R.id.mapContainer, mapFragment) + } + + val layer = MapLayer.defaultLayer + binding.attributionText.text = HtmlCompat.fromHtml(layer.attribution, 0) + + if (mapFragment.hasStarted) { + binding.mapLoading.root.visibility = View.GONE + } else { + binding.mapLoading.root.visibility = View.VISIBLE + mapFragment.setupCallbacks.add { + binding.mapLoading.root.visibility = View.GONE + } + } + } + + private fun removeMap() { + Log.d("MapWrapperFragment", "removeMap()") + childFragmentManager.commit { + remove(mapFragment) + } + } + + private fun setupEvents() { + binding.zoomInButton.setOnClickListener { + if (mapFragment.hasStarted) { + mapFragment.zoomIn() + } else { + mapFragment.setupCallbacks.add { + binding.zoomInButton.performClick() + } + } + } + binding.zoomOutButton.setOnClickListener { + if (mapFragment.hasStarted) { + mapFragment.zoomOut() + } else { + mapFragment.setupCallbacks.add { + binding.zoomOutButton.performClick() + } + } + } + binding.mapLayerToggle.setOnClickListener { + context?.let { context -> + if (mapFragment.hasStarted) { + Utils.showLayersPopUp(context, it) { type -> + mapFragment.updateLayer(type) + MapLayer.layers[type]?.attribution?.let { attribution -> + binding.attributionText.text = + HtmlCompat.fromHtml(attribution, 0) + } + } + } else { + binding.mapLayerToggle.performClick() + } + } + } + } + + private fun setupViews() { + val overlayMarginTop = arguments?.getInt(OVERLAY_MARGIN_TOP_ARG) + val showLayerToggle = arguments?.getBoolean(SHOW_LAYER_TOGGLE_ARG) + + overlayMarginTop?.let { top -> + binding.overlay.setPadding(0, top, 0, 0) + } + showLayerToggle?.let { show -> + binding.mapLayerToggle.visibility = if (show) View.VISIBLE else View.GONE + } + } + + fun setMarkerCallback(callback: MarkerCallback) { + mapFragment.markerCallback = callback + } + + fun focusOn( + latitude: Double, + longitude: Double, + height: Double = 0.00001, + animated: Boolean = true + ) { + Log.d("MapWrapperFragment", "Focusing on $latitude, $longitude") + if (mapFragment.hasStarted) { + mapFragment.focusOn(latitude, longitude, height, animated) + } else { + mapFragment.setupCallbacks.add { + focusOn(latitude, longitude, height, animated) + } + } + } + + fun display(positions: Array<Position>, isReport: Boolean, center: Boolean) { + if (mapFragment.hasStarted) { + Log.d("MapWrapperFragment", "Displaying positions: $positions") + + mapFragment.display( + positions.mapNotNull(MarkerTransformations::positionToMarker).toTypedArray(), + isReport = isReport, + center = center + ) + } else { + mapFragment.setupCallbacks.add { + display(positions, isReport, center) + } + } + } + + fun display(units: Array<UnitInformation>, isReport: Boolean, center: Boolean) { + if (mapFragment.hasStarted) { + Log.d("MapWrapperFragment", "Displaying units: $units") + + mapFragment.display( + units.mapNotNull(MarkerTransformations::unitToMarker).toTypedArray(), + isReport = isReport, + center = center + ) + } else { + mapFragment.setupCallbacks.add { + display(units, isReport, center) + } + } + } + + fun display(stops: Array<Stop>) { + if (mapFragment.hasStarted) { + Log.d("MapWrapperFragment", "Displaying stops: $stops") + + mapFragment.display( + stops.mapNotNull(MarkerTransformations::stopToMarker).toTypedArray(), + isReport = true + ) + } else { + mapFragment.setupCallbacks.add { + display(stops) + } + } + } + + fun display(geofences: Array<Geofence>) { + if (mapFragment.hasStarted) { + Log.d("MapWrapperFragment", "Displaying geofences: $geofences") + mapFragment.displayGeofences(geofences) + } else { + mapFragment.setupCallbacks.add { + display(geofences) + } + } + } + + fun updateLayer(layer: MapLayer.Type) { + if (mapFragment.hasStarted) { + mapFragment.updateLayer(layer) + MapLayer.layers[layer]?.attribution?.let { attribution -> + binding.attributionText.text = + HtmlCompat.fromHtml(attribution, 0) + } + } else { + mapFragment.setupCallbacks.add { + updateLayer(layer) + } + } + } + + companion object { + const val SHOW_LAYER_TOGGLE_ARG = "show_layer_toggle" + const val OVERLAY_MARGIN_TOP_ARG = "overlay_margin_top" + + fun newInstance( + showLayerToggle: Boolean = false, + overlayMarginTop: Int = 0 + ): MapWrapperFragment { + val args = Bundle() + args.putBoolean(SHOW_LAYER_TOGGLE_ARG, showLayerToggle) + args.putInt(OVERLAY_MARGIN_TOP_ARG, overlayMarginTop) + val fragment = MapWrapperFragment() + fragment.arguments = args + return fragment + } + } +}
\ No newline at end of file diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/UnitMapFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/UnitMapFragment.kt index b6396c8..768c0f0 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/UnitMapFragment.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/UnitMapFragment.kt @@ -6,18 +6,16 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.text.HtmlCompat import androidx.fragment.app.Fragment +import androidx.fragment.app.commit import androidx.fragment.app.viewModels import kotlin.time.ExperimentalTime import kotlinx.coroutines.DelicateCoroutinesApi import mx.trackermap.TrackerMap.android.R import mx.trackermap.TrackerMap.android.databinding.UnitMapFragmentBinding import mx.trackermap.TrackerMap.android.details.DetailsActivity -import mx.trackermap.TrackerMap.android.shared.MarkerTransformations import mx.trackermap.TrackerMap.android.shared.UnitRenderData import mx.trackermap.TrackerMap.android.units.UnitsViewModel -import mx.trackermap.TrackerMap.client.models.MapLayer import mx.trackermap.TrackerMap.client.models.UnitInformation @DelicateCoroutinesApi @@ -30,7 +28,7 @@ class UnitMapFragment : Fragment() { private var _binding: UnitMapFragmentBinding? = null private val binding get() = _binding!! - private lateinit var unitsMapFragment: MapFragment + private lateinit var mapFragment: MapWrapperFragment private var shouldCenter = true @@ -63,11 +61,13 @@ class UnitMapFragment : Fragment() { private fun initializeMap() { shouldCenter = true - unitsMapFragment = childFragmentManager.findFragmentById(R.id.unitsMap) as MapFragment - unitsMapFragment.markerCallback = unitsViewModel::selectUnitWith - - val layer = MapLayer.defaultLayer - binding.attributionText.text = HtmlCompat.fromHtml(layer.attribution, 0) + mapFragment = MapWrapperFragment.newInstance( + overlayMarginTop = resources.getDimensionPixelSize(R.dimen.nav_height) + ) + childFragmentManager.commit { + replace(R.id.unitsMap, mapFragment) + } + mapFragment.setMarkerCallback(unitsViewModel::selectUnitWith) } private fun setupObservers() { @@ -75,10 +75,10 @@ class UnitMapFragment : Fragment() { unitsViewModel.units.observe(viewLifecycleOwner) { units -> Log.d("UnitMapFragment", "Available units: $units") - unitsMapFragment.display( - units.mapNotNull(MarkerTransformations::unitToMarker).toTypedArray(), + mapFragment.display( + units.toTypedArray(), isReport = false, - center = shouldCenter + center = shouldCenter && unitsViewModel.selectedUnit.value == null ) if (units.isNotEmpty()) { shouldCenter = false @@ -107,22 +107,20 @@ class UnitMapFragment : Fragment() { ), binding.mapUnitCard.context, unit, this::itemAction ) - unitsMapFragment.focusOn(unit.position!!.latitude!!, unit.position!!.longitude!!) + mapFragment.focusOn(unit.position!!.latitude!!, unit.position!!.longitude!!) } } unitsViewModel.mapLayerType.observe(viewLifecycleOwner) { type -> Log.d("UnitMapFragment", "Loading layer!") - unitsMapFragment.updateLayer(type) + mapFragment.updateLayer(type) unitsViewModel.selectedUnit.value?.let { - unitsMapFragment.focusOn(it.position!!.latitude!!, it.position!!.longitude!!) + mapFragment.focusOn(it.position!!.latitude!!, it.position!!.longitude!!) } - val layer = MapLayer.layers[type]!! - binding.attributionText.text = HtmlCompat.fromHtml(layer.attribution, 0) } unitsViewModel.geofences.observe(viewLifecycleOwner) { geofences -> - unitsMapFragment.displayGeofences(geofences.values.toTypedArray()) + mapFragment.display(geofences.values.toTypedArray()) } } |