aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIván Ávalos <avalos@disroot.org>2022-01-17 23:46:13 -0600
committerIván Ávalos <avalos@disroot.org>2022-01-17 23:46:13 -0600
commit675eb2739105e53a8f5f0f602b3c2b2206d28623 (patch)
tree183b82c471e611d6de0390270e690e238265b149
parent9ff880b37f3e44aa3edbd402c8c01aed5467e26a (diff)
parent9325b6e0de41e8446bb6ce66915fb0c9fe0de4f3 (diff)
downloadetbsa-trackermap-mobile-675eb2739105e53a8f5f0f602b3c2b2206d28623.tar.gz
etbsa-trackermap-mobile-675eb2739105e53a8f5f0f602b3c2b2206d28623.tar.bz2
etbsa-trackermap-mobile-675eb2739105e53a8f5f0f602b3c2b2206d28623.zip
Merge branch 'main' of https://git.sr.ht/~avalos/trackermap-mobile
 Conflicts:  androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt
-rw-r--r--androidApp/build.gradle.kts2
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt89
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt59
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapWrapperFragment.kt234
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/UnitMapFragment.kt34
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/shared/Utils.kt26
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt16
-rw-r--r--androidApp/src/main/res/drawable/icon_zoom_in.xml10
-rw-r--r--androidApp/src/main/res/drawable/icon_zoom_out.xml10
-rw-r--r--androidApp/src/main/res/layout/fragment_map_wrapper.xml92
-rw-r--r--androidApp/src/main/res/layout/unit_details_reports.xml1
-rw-r--r--androidApp/src/main/res/layout/unit_map_fragment.xml23
-rw-r--r--build.gradle.kts2
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt3
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/MapCalculus.kt33
15 files changed, 484 insertions, 150 deletions
diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts
index 3947727..5e196ce 100644
--- a/androidApp/build.gradle.kts
+++ b/androidApp/build.gradle.kts
@@ -2,6 +2,7 @@ plugins {
id("com.android.application")
id("kotlinx-serialization")
kotlin("android")
+ id("kotlin-android")
}
android {
@@ -62,6 +63,7 @@ dependencies {
implementation("mil.nga.sf:sf-wkt:1.0.1")
implementation("com.soywiz.korlibs.krypto:krypto:2.4.12")
implementation(group = "", name = "WhirlyGlobeMaply", ext = "aar")
+ implementation("androidx.legacy:legacy-support-v4:1.0.0")
googleImplementation(platform("com.google.firebase:firebase-bom:29.0.3"))
googleImplementation("com.google.firebase:firebase-messaging")
diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt
index e1a24ec..51d2f6d 100644
--- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt
+++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt
@@ -21,12 +21,8 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import mx.trackermap.TrackerMap.android.R
import mx.trackermap.TrackerMap.android.databinding.UnitDetailsReportsBinding
import mx.trackermap.TrackerMap.android.details.UnitDetailsAdapter
-import mx.trackermap.TrackerMap.android.map.MapFragment
-import mx.trackermap.TrackerMap.android.shared.MarkerTransformations
+import mx.trackermap.TrackerMap.android.map.MapWrapperFragment
import mx.trackermap.TrackerMap.client.models.EventInformation
-import mx.trackermap.TrackerMap.client.models.Geofence
-import mx.trackermap.TrackerMap.client.models.Position
-import mx.trackermap.TrackerMap.client.models.Stop
import mx.trackermap.TrackerMap.controllers.ReportController
import mx.trackermap.TrackerMap.utils.Formatter
import mx.trackermap.TrackerMap.utils.ReportDates
@@ -43,7 +39,7 @@ class UnitReportsFragment : Fragment() {
private val binding get() = _binding!!
private val unitReportsViewModel: UnitReportsViewModel by viewModel()
- private lateinit var reportsMapFragment: MapFragment
+ private lateinit var mapFragment: MapWrapperFragment
override fun onCreateView(
inflater: LayoutInflater,
@@ -60,12 +56,12 @@ class UnitReportsFragment : Fragment() {
unitReportsViewModel.setDeviceId(
arguments?.getInt(UnitDetailsAdapter.DEVICE_ID_ARG) ?: 0)
setupEvents()
+ initializeMap()
}
override fun onResume() {
super.onResume()
- initializeMap()
setupObservers()
}
@@ -73,7 +69,6 @@ class UnitReportsFragment : Fragment() {
super.onPause()
removeObservers()
- removeMap()
if (unitReportsViewModel.report.value == null) {
unitReportsViewModel.fetchReport()
@@ -87,16 +82,11 @@ class UnitReportsFragment : Fragment() {
private fun initializeMap() {
Log.d("UnitReportsFragment", "initializeMap()")
- reportsMapFragment = MapFragment()
- childFragmentManager.commit {
- replace(R.id.reportsMapContainer, reportsMapFragment)
- }
- }
-
- private fun removeMap() {
- Log.d("UnitReportsFragment", "removeMap()")
+ mapFragment = MapWrapperFragment.newInstance(
+ showLayerToggle = true
+ )
childFragmentManager.commit {
- remove(reportsMapFragment)
+ replace(R.id.reportsMapContainer, mapFragment)
}
}
@@ -127,8 +117,8 @@ class UnitReportsFragment : Fragment() {
when (report) {
is ReportController.Report.PositionsReport -> {
- display(report.positions)
- display(unitReportsViewModel.geofences.value!!)
+ mapFragment.display(unitReportsViewModel.geofences.value!!)
+ mapFragment.display(report.positions, isReport = true, center = true)
showMap(true)
}
is ReportController.Report.EventsReport -> {
@@ -136,8 +126,8 @@ class UnitReportsFragment : Fragment() {
showMap(false)
}
is ReportController.Report.StopsReport -> {
- display(report.stops)
- display(unitReportsViewModel.geofences.value!!)
+ mapFragment.display(unitReportsViewModel.geofences.value!!)
+ mapFragment.display(report.stops)
showMap(true)
}
is ReportController.Report.XlsxReport -> {
@@ -167,7 +157,7 @@ class UnitReportsFragment : Fragment() {
}
unitReportsViewModel.geofences.observe(viewLifecycleOwner) { geofences ->
- display(geofences)
+ mapFragment.display(geofences)
}
}
@@ -199,25 +189,6 @@ class UnitReportsFragment : Fragment() {
popOver.show()
}
- private fun display(positions: Array<Position>) {
- if (reportsMapFragment.hasStarted) {
- Log.d("UnitReportsFragment", "Displaying positions: $positions")
-
- binding.eventsScroll.visibility = View.GONE
- binding.reportsMapContainer.visibility = View.VISIBLE
-
- reportsMapFragment.display(
- positions.mapNotNull(MarkerTransformations::positionToMarker).toTypedArray(),
- isReport = true
- )
- } else {
- reportsMapFragment.setupCallbacks.clear()
- reportsMapFragment.setupCallbacks.add {
- display(positions)
- }
- }
- }
-
private fun display(events: Array<EventInformation>) {
Log.d("UnitReportsFragment", "Displaying events: $events")
@@ -284,43 +255,13 @@ class UnitReportsFragment : Fragment() {
}
}
- private fun display(stops: Array<Stop>) {
- if (reportsMapFragment.hasStarted) {
- Log.d("UnitReportsFragment", "Displaying stops: $stops")
-
- binding.eventsScroll.visibility = View.GONE
- binding.reportsMapContainer.visibility = View.VISIBLE
-
- reportsMapFragment.display(
- stops.mapNotNull(MarkerTransformations::stopToMarker).toTypedArray(),
- isReport = true
- )
- } else {
- reportsMapFragment.setupCallbacks.add {
- display(stops)
- }
- }
- }
-
- private fun display(geofences: Array<Geofence>) {
- Log.d("UnitReportsFragment", "Geofences: $geofences")
- if (reportsMapFragment.hasStarted) {
- Log.d("UnitReportsFragment", "Displaying geofences: $geofences")
- reportsMapFragment.displayGeofences(geofences)
- } else {
- reportsMapFragment.setupCallbacks.add {
- display(geofences)
- }
- }
- }
-
private fun loading() {
binding.reportLoading.root.visibility = View.VISIBLE
binding.eventsScroll.visibility = View.GONE
binding.reportsMapContainer.visibility = View.GONE
childFragmentManager.commit {
- hide(reportsMapFragment)
+ hide(mapFragment)
}
}
@@ -332,9 +273,9 @@ class UnitReportsFragment : Fragment() {
childFragmentManager.commit {
if (shouldShowMap) {
- show(reportsMapFragment)
+ show(mapFragment)
} else {
- hide(reportsMapFragment)
+ hide(mapFragment)
}
}
}
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())
}
}
diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/shared/Utils.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/shared/Utils.kt
new file mode 100644
index 0000000..56a9167
--- /dev/null
+++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/shared/Utils.kt
@@ -0,0 +1,26 @@
+package mx.trackermap.TrackerMap.android.shared
+
+import android.content.Context
+import android.view.View
+import androidx.appcompat.widget.PopupMenu
+import mx.trackermap.TrackerMap.android.R
+import mx.trackermap.TrackerMap.client.models.MapLayer
+
+class Utils {
+ companion object {
+ fun showLayersPopUp(context: Context, view: View, callback: (layer: MapLayer.Type) -> Unit) {
+ val popOver = PopupMenu(context, view)
+ popOver.menuInflater.inflate(R.menu.map_layers, popOver.menu)
+ popOver.setOnMenuItemClickListener { item ->
+ val layer = when (item.itemId) {
+ R.id.layerStreets -> MapLayer.Type.STREETS
+ R.id.layerSatellite -> MapLayer.Type.SATELLITE
+ else -> MapLayer.Type.STREETS
+ }
+ callback(layer)
+ true
+ }
+ popOver.show()
+ }
+ }
+} \ No newline at end of file
diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt
index 7d42588..9fcef7f 100644
--- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt
+++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt
@@ -19,7 +19,7 @@ import mx.trackermap.TrackerMap.android.databinding.UnitsActivityBinding
import mx.trackermap.TrackerMap.android.devices.DevicesFragment
import mx.trackermap.TrackerMap.android.map.UnitMapFragment
import mx.trackermap.TrackerMap.android.session.UserInformationActivity
-import mx.trackermap.TrackerMap.client.models.MapLayer
+import mx.trackermap.TrackerMap.android.shared.Utils
import org.koin.androidx.viewmodel.ext.android.viewModel
import kotlin.time.ExperimentalTime
@@ -94,19 +94,9 @@ class UnitsActivity : AppCompatActivity() {
}
private fun showLayersPopUp(view: View) {
- val popOver = PopupMenu(this, view)
- popOver.menuInflater.inflate(R.menu.map_layers, popOver.menu)
- popOver.setOnMenuItemClickListener { item ->
- val layer = when (item.itemId) {
- R.id.layerStreets -> MapLayer.Type.STREETS
- R.id.layerGmapsStreets -> MapLayer.Type.GMAPS_STREETS
- R.id.layerGmapsSatellite -> MapLayer.Type.GMAPS_SATELLITE
- else -> MapLayer.Type.STREETS
- }
- unitsViewModel.setMapLayerType(layer)
- true
+ Utils.showLayersPopUp(this, view) {
+ unitsViewModel.setMapLayerType(it)
}
- popOver.show()
}
private fun unfocusSearch(clearText: Boolean = false) {
diff --git a/androidApp/src/main/res/drawable/icon_zoom_in.xml b/androidApp/src/main/res/drawable/icon_zoom_in.xml
new file mode 100644
index 0000000..eb23254
--- /dev/null
+++ b/androidApp/src/main/res/drawable/icon_zoom_in.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/androidApp/src/main/res/drawable/icon_zoom_out.xml b/androidApp/src/main/res/drawable/icon_zoom_out.xml
new file mode 100644
index 0000000..791a2f8
--- /dev/null
+++ b/androidApp/src/main/res/drawable/icon_zoom_out.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,13H5v-2h14v2z"/>
+</vector>
diff --git a/androidApp/src/main/res/layout/fragment_map_wrapper.xml b/androidApp/src/main/res/layout/fragment_map_wrapper.xml
new file mode 100644
index 0000000..949cc10
--- /dev/null
+++ b/androidApp/src/main/res/layout/fragment_map_wrapper.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <androidx.fragment.app.FragmentContainerView
+ android:id="@+id/mapContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:visibility="visible"/>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/zoomInButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/fab_margin"
+ android:layout_marginEnd="@dimen/fab_margin"
+ android:backgroundTint="@android:color/white"
+ android:contentDescription="@string/switch_layer"
+ android:src="@drawable/icon_zoom_in"
+ app:borderWidth="0dp"
+ app:elevation="@dimen/fab_elevation"
+ app:fabSize="mini"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/zoomOutButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/fab_margin"
+ android:backgroundTint="@android:color/white"
+ android:contentDescription="@string/switch_layer"
+ android:src="@drawable/icon_zoom_out"
+ app:borderWidth="0dp"
+ app:elevation="@dimen/fab_elevation"
+ app:fabSize="mini"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/zoomInButton" />
+
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/mapLayerToggle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/fab_margin"
+ android:backgroundTint="@android:color/white"
+ android:contentDescription="@string/switch_layer"
+ android:src="@drawable/icon_layers"
+ app:borderWidth="0dp"
+ app:elevation="@dimen/fab_elevation"
+ app:fabSize="mini"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/zoomOutButton"
+ android:visibility="gone" />
+
+ <!-- Attribution -->
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:background="@color/colorAttributionBackground"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent">
+
+ <TextView
+ android:id="@+id/attributionText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:text="Copyright (C) OpenStreetMap"
+ android:textSize="@dimen/attribution_text_size"
+ android:textColor="@color/colorAttributionText"
+ android:layout_margin="6dp" />
+
+ </FrameLayout>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+ <include
+ android:id="@+id/mapLoading"
+ layout="@layout/loading_indicator"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/androidApp/src/main/res/layout/unit_details_reports.xml b/androidApp/src/main/res/layout/unit_details_reports.xml
index 7210e1f..f53ce51 100644
--- a/androidApp/src/main/res/layout/unit_details_reports.xml
+++ b/androidApp/src/main/res/layout/unit_details_reports.xml
@@ -7,7 +7,6 @@
<androidx.fragment.app.FragmentContainerView
android:id="@+id/reportsMapContainer"
- android:name="mx.trackermap.TrackerMap.android.map.MapFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/margin"
diff --git a/androidApp/src/main/res/layout/unit_map_fragment.xml b/androidApp/src/main/res/layout/unit_map_fragment.xml
index a86a7e9..f168662 100644
--- a/androidApp/src/main/res/layout/unit_map_fragment.xml
+++ b/androidApp/src/main/res/layout/unit_map_fragment.xml
@@ -6,7 +6,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
- <fragment
+ <androidx.fragment.app.FragmentContainerView
android:id="@+id/unitsMap"
android:layout_width="0dp"
android:layout_height="0dp"
@@ -14,7 +14,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
- android:name="mx.trackermap.TrackerMap.android.map.MapFragment"
tools:visibility="visible"/>
<androidx.cardview.widget.CardView
@@ -143,24 +142,4 @@
</androidx.cardview.widget.CardView>
- <!-- Attribution -->
- <FrameLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:background="@color/colorAttributionBackground"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="parent">
-
- <TextView
- android:id="@+id/attributionText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- tools:text="Copyright (C) OpenStreetMap"
- android:textSize="@dimen/attribution_text_size"
- android:textColor="@color/colorAttributionText"
- android:layout_margin="6dp" />
-
- </FrameLayout>
-
</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 3d353e2..21ecdbc 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -6,7 +6,7 @@ buildscript {
maven("https://jitpack.io")
}
dependencies {
- classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0")
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
classpath("org.jetbrains.kotlin:kotlin-serialization:1.6.0")
classpath("com.android.tools.build:gradle:7.0.4")
classpath("com.google.gms:google-services:4.3.10")
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt
index 4bda0ae..7d01282 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt
@@ -40,6 +40,9 @@ open class ApiClient(val baseUrl: String) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
+ engine {
+ requestTimeout = 10_000
+ }
}
val defaultHeaders: Map<String, String> =
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/MapCalculus.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/MapCalculus.kt
index d4d69fa..1a0d27b 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/MapCalculus.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/MapCalculus.kt
@@ -1,5 +1,7 @@
package mx.trackermap.TrackerMap.utils
+import kotlin.math.roundToLong
+
class MapCalculus {
companion object {
/**
@@ -36,5 +38,36 @@ class MapCalculus {
24 -> 33.3238997478125
else -> null
}
+
+ fun scaleToZoomLevel(scale: Double): Int {
+ fun z(d: Int) = zoomLevelToScale(d)!!
+ return when {
+ scale > z(1) -> 1
+ scale in z(2)..z(1) -> 2
+ scale in z(3)..z(2) -> 3
+ scale in z(4)..z(3) -> 4
+ scale in z(5)..z(4) -> 5
+ scale in z(6)..z(5) -> 6
+ scale in z(7)..z(6) -> 7
+ scale in z(8)..z(7) -> 8
+ scale in z(9)..z(8) -> 9
+ scale in z(10)..z(9) -> 10
+ scale in z(11)..z(10) -> 11
+ scale in z(12)..z(11) -> 12
+ scale in z(13)..z(12) -> 13
+ scale in z(14)..z(13) -> 14
+ scale in z(15)..z(14) -> 15
+ scale in z(16)..z(15) -> 16
+ scale in z(17)..z(16) -> 17
+ scale in z(18)..z(17) -> 18
+ scale in z(19)..z(18) -> 19
+ scale in z(20)..z(19) -> 20
+ scale in z(21)..z(20) -> 21
+ scale in z(22)..z(21) -> 22
+ scale in z(23)..z(22) -> 23
+ scale in z(24)..z(23) -> 24
+ else -> 24
+ }
+ }
}
} \ No newline at end of file