diff options
Diffstat (limited to 'androidApp')
10 files changed, 299 insertions, 87 deletions
diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 1f8b0ed..5347780 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..bceb113 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 @@ -22,6 +22,7 @@ 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.map.MapWrapperFragment import mx.trackermap.TrackerMap.android.shared.MarkerTransformations import mx.trackermap.TrackerMap.client.models.EventInformation import mx.trackermap.TrackerMap.client.models.Geofence @@ -43,7 +44,7 @@ class UnitReportsFragment : Fragment() { private val binding get() = _binding!! private val unitReportsViewModel: UnitReportsViewModel by viewModel() - private lateinit var reportsMapFragment: MapFragment + private lateinit var reportsMapFragment: MapWrapperFragment override fun onCreateView( inflater: LayoutInflater, @@ -87,7 +88,7 @@ class UnitReportsFragment : Fragment() { private fun initializeMap() { Log.d("UnitReportsFragment", "initializeMap()") - reportsMapFragment = MapFragment() + reportsMapFragment = MapWrapperFragment() childFragmentManager.commit { replace(R.id.reportsMapContainer, reportsMapFragment) } @@ -127,8 +128,8 @@ class UnitReportsFragment : Fragment() { when (report) { is ReportController.Report.PositionsReport -> { - display(report.positions) - display(unitReportsViewModel.geofences.value!!) + reportsMapFragment.display(report.positions, isReport = true, center = true) + reportsMapFragment.display(unitReportsViewModel.geofences.value!!) showMap(true) } is ReportController.Report.EventsReport -> { @@ -136,8 +137,8 @@ class UnitReportsFragment : Fragment() { showMap(false) } is ReportController.Report.StopsReport -> { - display(report.stops) - display(unitReportsViewModel.geofences.value!!) + reportsMapFragment.display(report.stops) + reportsMapFragment.display(unitReportsViewModel.geofences.value!!) showMap(true) } is ReportController.Report.XlsxReport -> { @@ -167,7 +168,7 @@ class UnitReportsFragment : Fragment() { } unitReportsViewModel.geofences.observe(viewLifecycleOwner) { geofences -> - display(geofences) + reportsMapFragment.display(geofences) } } @@ -199,25 +200,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,36 +266,6 @@ 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 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..5304f8e 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 @@ -98,6 +98,11 @@ class MapFragment : GlobeMapFragment() { mapControl.setPositionGeo(longitude, latitude, 0.4) } + override fun onStop() { + super.onStop() + hasStarted = false + } + override fun mapDidStopMoving( mapControl: MapController?, corners: Array<out Point3d>?, @@ -341,19 +346,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 +381,12 @@ 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) } } } 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..51b0118 --- /dev/null +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapWrapperFragment.kt @@ -0,0 +1,161 @@ +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.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.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() + } + + 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) + } + } + + private fun removeMap() { + Log.d("UnitReportsFragment", "removeMap()") + childFragmentManager.commit { + remove(mapFragment) + } + } + + private fun setupEvents() { + binding.zoomInButton.setOnClickListener { + if (mapFragment.hasStarted) { + mapFragment.zoomIn() + } else { + mapFragment.setupCallbacks.add { + mapFragment.zoomIn() + } + } + } + binding.zoomOutButton.setOnClickListener { + if (mapFragment.hasStarted) { + mapFragment.zoomOut() + } else { + mapFragment.setupCallbacks.add { + mapFragment.zoomOut() + } + } + } + } + + fun setMarkerCallback(callback: MarkerCallback) { + mapFragment.markerCallback = callback + } + + fun setOverlayPaddingTop(top: Int) { + val overlay by lazy { binding.overlay } + overlay.setPadding(0, top, 0, 0) + } + + fun focusOn( + latitude: Double, + longitude: Double, + height: Double = 0.00001, + animated: Boolean = true + ) { + 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("UnitReportsFragment", "Displaying positions: $positions") + + mapFragment.display( + positions.mapNotNull(MarkerTransformations::positionToMarker).toTypedArray(), + isReport = isReport, + center = center + ) + } else { + mapFragment.setupCallbacks.clear() + mapFragment.setupCallbacks.add { + display(positions, isReport, center) + } + } + } + + fun display(stops: Array<Stop>) { + if (mapFragment.hasStarted) { + Log.d("UnitReportsFragment", "Displaying stops: $stops") + + mapFragment.display( + stops.mapNotNull(MarkerTransformations::stopToMarker).toTypedArray(), + isReport = true + ) + } else { + mapFragment.setupCallbacks.add { + display(stops) + } + } + } + + fun display(geofences: Array<Geofence>) { + Log.d("UnitReportsFragment", "Geofences: $geofences") + if (mapFragment.hasStarted) { + Log.d("UnitReportsFragment", "Displaying geofences: $geofences") + mapFragment.displayGeofences(geofences) + } else { + mapFragment.setupCallbacks.add { + display(geofences) + } + } + } + + fun updateLayer(layer: MapLayer.Type) { + if (mapFragment.hasStarted) { + mapFragment.updateLayer(layer) + } else { + mapFragment.setupCallbacks.add { + updateLayer(layer) + } + } + } +}
\ 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..b67bca8 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 @@ -14,7 +14,6 @@ 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 @@ -30,7 +29,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,8 +62,9 @@ class UnitMapFragment : Fragment() { private fun initializeMap() { shouldCenter = true - unitsMapFragment = childFragmentManager.findFragmentById(R.id.unitsMap) as MapFragment - unitsMapFragment.markerCallback = unitsViewModel::selectUnitWith + mapFragment = childFragmentManager.findFragmentById(R.id.unitsMap) as MapWrapperFragment + mapFragment.setMarkerCallback(unitsViewModel::selectUnitWith) + mapFragment.setOverlayPaddingTop(resources.getDimensionPixelSize(R.dimen.nav_height)) val layer = MapLayer.defaultLayer binding.attributionText.text = HtmlCompat.fromHtml(layer.attribution, 0) @@ -75,8 +75,8 @@ class UnitMapFragment : Fragment() { unitsViewModel.units.observe(viewLifecycleOwner) { units -> Log.d("UnitMapFragment", "Available units: $units") - unitsMapFragment.display( - units.mapNotNull(MarkerTransformations::unitToMarker).toTypedArray(), + mapFragment.display( + units.mapNotNull { it.position }.toTypedArray(), isReport = false, center = shouldCenter ) @@ -107,22 +107,22 @@ 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/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..479c855 --- /dev/null +++ b/androidApp/src/main/res/layout/fragment_map_wrapper.xml @@ -0,0 +1,65 @@ +<?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" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + +</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..9eb92de 100644 --- a/androidApp/src/main/res/layout/unit_map_fragment.xml +++ b/androidApp/src/main/res/layout/unit_map_fragment.xml @@ -14,7 +14,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" - android:name="mx.trackermap.TrackerMap.android.map.MapFragment" + android:name="mx.trackermap.TrackerMap.android.map.MapWrapperFragment" tools:visibility="visible"/> <androidx.cardview.widget.CardView |