diff options
author | Iván Ávalos <avalos@disroot.org> | 2022-01-16 04:16:53 -0600 |
---|---|---|
committer | Iván Ávalos <avalos@disroot.org> | 2022-01-16 04:16:53 -0600 |
commit | 4a796e47bede0cbe5512868001c22b6889e9f72e (patch) | |
tree | 48aa1dfed3dcae88f0453c1c9c4788d04173f9e0 | |
parent | 51d42034f00cc640ff94c06333e2354c8e440a3f (diff) | |
parent | a2ba612c515a53cb6e7f283858a518d60188651b (diff) | |
download | etbsa-trackermap-mobile-4a796e47bede0cbe5512868001c22b6889e9f72e.tar.gz etbsa-trackermap-mobile-4a796e47bede0cbe5512868001c22b6889e9f72e.tar.bz2 etbsa-trackermap-mobile-4a796e47bede0cbe5512868001c22b6889e9f72e.zip |
Merge branch 'main' of https://git.sr.ht/~avalos/trackermap-mobile
14 files changed, 190 insertions, 49 deletions
diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 6fe79ad..bec9466 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -39,7 +39,7 @@ val googleImplementation by configurations dependencies { implementation(project(":shared")) - implementation("com.google.android.material:material:1.4.0") + implementation("com.google.android.material:material:1.5.0") implementation("androidx.appcompat:appcompat:1.4.0") implementation("androidx.constraintlayout:constraintlayout:2.1.2") implementation("com.squareup.okhttp3:okhttp:4.9.1") @@ -56,6 +56,7 @@ dependencies { implementation("com.github.zerobranch:SwipeLayout:1.3.1") implementation("com.github.addisonElliott:SegmentedButton:3.1.9") implementation("mil.nga.sf:sf-wkt:1.0.1") + implementation("com.soywiz.korlibs.krypto:krypto:2.4.12") implementation(group = "", name = "WhirlyGlobeMaply", ext = "aar") googleImplementation(platform("com.google.firebase:firebase-bom:29.0.3")) 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 60c8a47..a8478fd 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 @@ -11,12 +11,15 @@ import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.drawable.toBitmap import com.mousebird.maply.* +import com.soywiz.krypto.md5 import mil.nga.sf.Polygon import mil.nga.sf.util.SFException import mil.nga.sf.wkt.GeometryReader import java.io.File import mx.trackermap.TrackerMap.android.R import mx.trackermap.TrackerMap.client.models.Geofence +import mx.trackermap.TrackerMap.client.models.MapLayer +import mx.trackermap.TrackerMap.utils.MapCalculus import mx.trackermap.TrackerMap.utils.MarkerType typealias MarkerCallback = (Int?) -> Unit @@ -56,17 +59,14 @@ class MapFragment : GlobeMapFragment() { override fun controlHasStarted() { Log.d("MapFragment", "controlHasStarted") - val cacheDirName = "stamen_watercolor6" - val cacheDir = File(activity!!.cacheDir, cacheDirName) - cacheDir.mkdir() - if (tileInfo == null) { - tileInfo = RemoteTileInfoNew( - getString(R.string.maps_streets_tile_url), - 0, - 21 - ) - (tileInfo as RemoteTileInfoNew).cacheDir = cacheDir + // Load default map layer + val layer = resources.getStringArray(R.array.maps_streets_tile) + val tmpInfo = RemoteTileInfoNew(layer[0], layer[1].toInt(), layer[2].toInt()) + tileInfo = tileInfoSetCacheDir(layer[0], tmpInfo) + tileInfo?.let { + setZoomLimits(it.minZoom, it.maxZoom) + } } val params = SamplingParams() @@ -83,7 +83,16 @@ class MapFragment : GlobeMapFragment() { val latitude = 23.191 val longitude = -100.36 - focusOn(latitude, longitude, zoom = 0.4, animated = false) + focusOn(latitude, longitude, height = 0.4, animated = false) + } + + override fun mapDidStopMoving( + mapControl: MapController?, + corners: Array<out Point3d>?, + userMotion: Boolean + ) { + super.mapDidStopMoving(mapControl, corners, userMotion) + Log.d("MapFragment", "Height: ${mapControl?.height}") } override fun userDidSelect( @@ -93,7 +102,6 @@ class MapFragment : GlobeMapFragment() { screenLoc: Point2d? ) { super.userDidSelect(mapControl, selObjs, loc, screenLoc) - selObjs?.forEach { selectedObject -> if (selectedObject.selObj is ScreenMarker) { val screenMarker = selectedObject.selObj as ScreenMarker @@ -228,7 +236,8 @@ class MapFragment : GlobeMapFragment() { mbr.expandByFraction(0.1) mapControl.addPostSurfaceRunnable { - val zoom = mapControl.findHeightToViewBounds(mbr, mbr.middle()) + val zoom = mapControl.zoomLimitMax.coerceAtLeast( + mapControl.findHeightToViewBounds(mbr, mbr.middle())) mapControl.setPositionGeo(mbr.middle(), zoom) } } @@ -308,19 +317,57 @@ class MapFragment : GlobeMapFragment() { )) } - fun focusOn(latitude: Double, longitude: Double, zoom: Double = 0.0000144, animated: Boolean = true) { + fun focusOn( + latitude: Double, + longitude: Double, + height: Double? = 0.00001, + animated: Boolean = true + ) { val lat = latitude * Math.PI / 180 val lon = longitude * Math.PI / 180 + // Ensure height is equal or higher than bottom limit + val z = mapControl.zoomLimitMin.coerceAtLeast(height ?: 0.0) if (animated) { - mapControl.animatePositionGeo(lon, lat, zoom, 0.2) + mapControl.animatePositionGeo(lon, lat, z, 0.2) } else { - mapControl.setPositionGeo(lon, lat, zoom) + mapControl.setPositionGeo(lon, lat, z) } } - fun updateTileInfo(tileInfo: TileInfoNew) { - this.tileInfo = tileInfo - loader?.changeTileInfo(tileInfo) + private fun tileInfoSetCacheDir(url: String, tileInfo: TileInfoNew): TileInfoNew? { + return context?.let { + val cacheDirName = url.toByteArray(Charsets.UTF_8).md5().hex + val cacheDirMap = File(it.cacheDir, cacheDirName) + cacheDirMap.mkdir() + Log.d("MapFragment", "Cache dir for $url = ${cacheDirMap.absolutePath}") + + (tileInfo as? RemoteTileInfoNew)?.cacheDir = cacheDirMap + tileInfo + } + } + + fun updateTile(layer: MapLayer) { + val tileInfo = RemoteTileInfoNew(layer.url, layer.minZoom, layer.maxZoom) + this.tileInfo = tileInfoSetCacheDir(layer.url, tileInfo) + this.tileInfo?.let { + loader?.changeTileInfo(it) + setZoomLimits(tileInfo.minZoom, tileInfo.maxZoom) + } + } + + private fun setZoomLimits(minZoom: Int, maxZoom: Int) { + mapControl?.let { + it.setZoomLimits( + it.heightForMapScale( + MapCalculus.zoomLevelToScale(maxZoom) + ?: MapCalculus.zoomLevelToScale(21)!! + ), + it.heightForMapScale( + MapCalculus.zoomLevelToScale(minZoom) + ?: MapCalculus.zoomLevelToScale(1)!! + ) + ) + } } private fun getIcon(markerType: MarkerType): Bitmap { 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 09f41a8..6f6596f 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,6 +6,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.text.HtmlCompat import kotlinx.coroutines.DelicateCoroutinesApi import mx.trackermap.TrackerMap.android.R import mx.trackermap.TrackerMap.android.databinding.UnitMapFragmentBinding @@ -53,6 +54,9 @@ class UnitMapFragment(private val unitsViewModel: UnitsViewModel) : UnitFragment private fun initializeMap() { unitsMapFragment = childFragmentManager.findFragmentById(R.id.unitsMap) as MapFragment unitsMapFragment.markerCallback = unitsViewModel::selectUnitWith + + val layer = resources.getStringArray(R.array.maps_streets_tile) + binding.attributionText.text = HtmlCompat.fromHtml(layer[3], 0) } private fun setupObservers() { @@ -94,9 +98,10 @@ class UnitMapFragment(private val unitsViewModel: UnitsViewModel) : UnitFragment } } - unitsViewModel.mapTileInfo.observe(viewLifecycleOwner) { tileInfo -> + unitsViewModel.mapLayer.observe(viewLifecycleOwner) { layer -> Log.d("UnitMapFragment", "Loading layer!") - unitsMapFragment.updateTileInfo(tileInfo) + unitsMapFragment.updateTile(layer) + binding.attributionText.text = HtmlCompat.fromHtml(layer.attribution, 0) } unitsViewModel.geofences.observe(viewLifecycleOwner) { geofences -> @@ -108,7 +113,7 @@ class UnitMapFragment(private val unitsViewModel: UnitsViewModel) : UnitFragment Log.d("UnitMapFragment", "removeObservers()") unitsViewModel.units.removeObservers(viewLifecycleOwner) unitsViewModel.selectedUnit.removeObservers(viewLifecycleOwner) - unitsViewModel.mapTileInfo.removeObservers(viewLifecycleOwner) + unitsViewModel.mapLayer.removeObservers(viewLifecycleOwner) unitsViewModel.geofences.removeObservers(viewLifecycleOwner) } 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 bf5f1d8..1bbcb80 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 @@ -17,6 +17,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 org.koin.androidx.viewmodel.ext.android.viewModel @DelicateCoroutinesApi @@ -90,13 +91,16 @@ class UnitsActivity : AppCompatActivity() { val popOver = PopupMenu(this, view) popOver.menuInflater.inflate(R.menu.map_layers, popOver.menu) popOver.setOnMenuItemClickListener { item -> - unitsViewModel.setMapLayer( + val layer = resources.getStringArray( when (item.itemId) { - R.id.layerStreets -> getString(R.string.maps_streets_tile_url) - R.id.layerSatellite -> getString(R.string.maps_satellite_url) - else -> getString(R.string.maps_streets_tile_url) + R.id.layerStreets -> R.array.maps_streets_tile + R.id.layerSatellite -> R.array.maps_satellite_tile + else -> R.array.maps_streets_tile } ) + unitsViewModel.setMapLayer(MapLayer( + layer[0], layer[1].toInt(), layer[2].toInt(), layer[3] + )) true } popOver.show() diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt index 08d35a2..d3060f0 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt @@ -2,12 +2,11 @@ package mx.trackermap.TrackerMap.android.units import android.util.Log import androidx.lifecycle.* -import com.mousebird.maply.RemoteTileInfoNew -import com.mousebird.maply.TileInfoNew import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import mx.trackermap.TrackerMap.client.models.Geofence +import mx.trackermap.TrackerMap.client.models.MapLayer import mx.trackermap.TrackerMap.client.models.UnitInformation import mx.trackermap.TrackerMap.controllers.GeofencesController import mx.trackermap.TrackerMap.controllers.UnitsController @@ -30,14 +29,14 @@ class UnitsViewModel( private var _unitsDisplayMode = MutableLiveData(UnitsDisplayMode.MAP) private var _units = MutableLiveData<List<UnitInformation>>() private var _selectedUnit = MutableLiveData<UnitInformation?>() - private var _mapTileInfo = MutableLiveData<TileInfoNew>() + private var _mapLayer = MutableLiveData<MapLayer>() private var _geofences = MutableLiveData<Map<Int, Geofence>>() val searchQuery: LiveData<String> get() = _searchQuery val unitsDisplayMode: LiveData<UnitsDisplayMode> get() = _unitsDisplayMode val units: LiveData<List<UnitInformation>> get() = _units val selectedUnit: LiveData<UnitInformation?> get() = _selectedUnit - val mapTileInfo: LiveData<TileInfoNew> get() = _mapTileInfo + val mapLayer: LiveData<MapLayer> get() = _mapLayer val geofences: LiveData<Map<Int, Geofence>> get() = _geofences init { @@ -86,9 +85,8 @@ class UnitsViewModel( _unitsDisplayMode.postValue(displayMode) } - fun setMapLayer(url: String, minZoom: Int = 0, maxZoom: Int = 21) { - val tileInfo = RemoteTileInfoNew(url, minZoom, maxZoom) - _mapTileInfo.postValue(tileInfo) + fun setMapLayer(layer: MapLayer) { + _mapLayer.postValue(layer) } fun toggleDisplayMode() { diff --git a/androidApp/src/main/res/layout/unit_map_fragment.xml b/androidApp/src/main/res/layout/unit_map_fragment.xml index 486f1ce..8395013 100644 --- a/androidApp/src/main/res/layout/unit_map_fragment.xml +++ b/androidApp/src/main/res/layout/unit_map_fragment.xml @@ -14,7 +14,8 @@ 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.MapFragment" + tools:visibility="invisible"/> <androidx.cardview.widget.CardView android:id="@+id/mapUnitCard" @@ -121,4 +122,24 @@ </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/androidApp/src/main/res/values-es-rMX/map_layers.xml b/androidApp/src/main/res/values-es-rMX/map_layers.xml new file mode 100644 index 0000000..eff37d9 --- /dev/null +++ b/androidApp/src/main/res/values-es-rMX/map_layers.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="layer_streets">Calles</string> + <string name="layer_satellite">Satélite</string> +</resources>
\ No newline at end of file diff --git a/androidApp/src/main/res/values-es-rMX/strings.xml b/androidApp/src/main/res/values-es-rMX/strings.xml index 47e4716..0806870 100644 --- a/androidApp/src/main/res/values-es-rMX/strings.xml +++ b/androidApp/src/main/res/values-es-rMX/strings.xml @@ -19,9 +19,6 @@ <string name="switch_layer">Cambiar capa del mapa</string> <string name="open_profile">Ver información de cuenta</string> - <string name="layer_streets">Calles</string> - <string name="layer_satellite">Satélite</string> - <string name="menu_account">Cuenta</string> <string name="menu_about">Acerca de</string> <string name="menu_logout">Cerrar sesión</string> diff --git a/androidApp/src/main/res/values/colors.xml b/androidApp/src/main/res/values/colors.xml index bb4ab14..23fe6fb 100644 --- a/androidApp/src/main/res/values/colors.xml +++ b/androidApp/src/main/res/values/colors.xml @@ -12,6 +12,8 @@ <color name="colorGeofenceLabel">#1976D2</color> <color name="colorGeofenceLabelOutline">#FFFFFF</color> <color name="colorReport">#388E3C</color> + <color name="colorAttributionText">#66000000</color> + <color name="colorAttributionBackground">#4DFFFFFF</color> <color name="colorOnline">#388E3C</color> <color name="colorOffline">#D32F2F</color> diff --git a/androidApp/src/main/res/values/dimen.xml b/androidApp/src/main/res/values/dimen.xml index 271a12b..92d5242 100644 --- a/androidApp/src/main/res/values/dimen.xml +++ b/androidApp/src/main/res/values/dimen.xml @@ -33,6 +33,7 @@ <dimen name="geofence_label_text_size">11sp</dimen> <dimen name="geofence_label_width">4dp</dimen> <dimen name="report_label_width">10dp</dimen> + <dimen name="attribution_text_size">11sp</dimen> <!-- User Information --> <dimen name="fields_spacing">8dp</dimen> diff --git a/androidApp/src/main/res/values/map_layers.xml b/androidApp/src/main/res/values/map_layers.xml new file mode 100644 index 0000000..bffceb9 --- /dev/null +++ b/androidApp/src/main/res/values/map_layers.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="layer_streets">Streets</string> + <string name="layer_satellite">Satellite</string> + + <!-- Tile URLs --> + <!-- [0] = tile server URL --> + <!-- [1] = min zoom --> + <!-- [2] = max zoom --> + <!-- [3] = attribution text --> + + <string-array name="maps_streets_tile" translatable="false"> + <item>https://a.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png</item> + <item>0</item> + <item>21</item> + <item>© OpenStreetMap France | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors</item> + </string-array> + + <string-array name="maps_satellite_tile" translatable="false"> + <item>https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}</item> + <item>0</item> + <item>20</item> + <item>Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community</item> + </string-array> + +</resources>
\ No newline at end of file diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml index 1caf502..1cc1f10 100644 --- a/androidApp/src/main/res/values/strings.xml +++ b/androidApp/src/main/res/values/strings.xml @@ -10,13 +10,6 @@ https://www.google.com/maps/place/%1$f,%2$f?z=19 </string> - <string name="maps_streets_tile_url" translatable="false"> - https://mt0.google.com/vt/lyrs=m&hl=en&x={x}&y={y}&z={z}&s=Ga - </string> - <string name="maps_satellite_url" translatable="false"> - https://mt0.google.com/vt/lyrs=y&hl=en&x={x}&y={y}&z={z}&s=Ga - </string> - <string name="notification_channel_id" translatable="false">default</string> <string name="notification_channel" translatable="false">Default</string> @@ -39,10 +32,6 @@ <string name="switch_layer">Switch map layer</string> <string name="open_profile">View account info</string> - <string name="layer_streets">Streets</string> - <string name="layer_satellite">Satellite</string> - <string name="layer_hybrid">Hybrid</string> - <string name="menu_account">Account</string> <string name="menu_about">About</string> <string name="menu_logout">Logout</string> diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/MapLayer.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/MapLayer.kt new file mode 100644 index 0000000..64381d9 --- /dev/null +++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/MapLayer.kt @@ -0,0 +1,8 @@ +package mx.trackermap.TrackerMap.client.models + +data class MapLayer( + val url: String, + val minZoom: Int, + val maxZoom: Int, + val attribution: String +)
\ No newline at end of file diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/MapCalculus.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/MapCalculus.kt new file mode 100644 index 0000000..118c117 --- /dev/null +++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/MapCalculus.kt @@ -0,0 +1,37 @@ +package mx.trackermap.TrackerMap.utils + +class MapCalculus { + companion object { + /** + * WhirlyGlobe library uses height rather than zoom levels, but it supports converting + * Mapnik denominator scales to height, so we can first convert zoom levels to Mapnik + * denominator scales, and then convert them to height using WhirlyGlobe. + * Source: https://github.com/openstreetmap/mapnik-stylesheets/blob/master/zoom-to-scale.txt + */ + fun zoomLevelToScale(zoom: Int): Double? = + when (zoom) { + 1 -> 279541132.014 + 2 -> 139770566.007 + 3 -> 69885283.0036 + 4 -> 34942641.5018 + 5 -> 17471320.7509 + 6 -> 8735660.37545 + 7 -> 4367830.18772 + 8 -> 2183915.09386 + 9 -> 1091957.54693 + 10 -> 545978.773466 + 11 -> 272989.386733 + 12 -> 136494.693366 + 13 -> 68247.3466832 + 14 -> 34123.6733416 + 15 -> 17061.8366708 + 16 -> 8530.9183354 + 17 -> 4265.4591677 + 18 -> 2132.72958385 + 19 -> 1066.36479193 + 20 -> 533.182395965 + 21 -> 266.5911979825 + else -> null + } + } +}
\ No newline at end of file |