diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/eventMap/EventMapFragment.kt b/app/src/main/java/org/dhis2/usescases/programEventDetail/eventMap/EventMapFragment.kt index eca2a6ab5d..d53449d68a 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/eventMap/EventMapFragment.kt +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/eventMap/EventMapFragment.kt @@ -16,6 +16,7 @@ import org.dhis2.commons.locationprovider.LocationSettingLauncher import org.dhis2.databinding.FragmentProgramEventDetailMapBinding import org.dhis2.maps.carousel.CarouselAdapter import org.dhis2.maps.layer.MapLayerDialog +import org.dhis2.maps.location.MapLocationEngine import org.dhis2.usescases.general.FragmentGlobalAbstract import org.dhis2.usescases.programEventDetail.ProgramEventDetailActivity import org.dhis2.usescases.programEventDetail.ProgramEventDetailViewModel @@ -55,7 +56,7 @@ class EventMapFragment : programEventsViewModel.setProgress(true) binding = FragmentProgramEventDetailMapBinding.inflate(inflater, container, false) binding.apply { - eventMapManager = org.dhis2.maps.managers.EventMapManager(mapView) + eventMapManager = org.dhis2.maps.managers.EventMapManager(mapView, MapLocationEngine(requireContext())) eventMapManager?.let { fragmentLifeCycle.addObserver(it) } eventMapManager?.onCreate(savedInstanceState) eventMapManager?.featureType = presenter.programFeatureType() diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/mapView/SearchTEMap.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/mapView/SearchTEMap.kt index bd8ac8e108..c826d9927d 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/mapView/SearchTEMap.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/mapView/SearchTEMap.kt @@ -22,6 +22,7 @@ import org.dhis2.databinding.FragmentSearchMapBinding import org.dhis2.maps.ExternalMapNavigation import org.dhis2.maps.carousel.CarouselAdapter import org.dhis2.maps.layer.MapLayerDialog +import org.dhis2.maps.location.MapLocationEngine import org.dhis2.maps.managers.TeiMapManager import org.dhis2.maps.model.MapStyle import org.dhis2.usescases.general.FragmentGlobalAbstract @@ -117,7 +118,7 @@ class SearchTEMap : FragmentGlobalAbstract(), MapboxMap.OnMapClickListener { viewModel.setSearchScreen() } - teiMapManager = TeiMapManager(binding.mapView) + teiMapManager = TeiMapManager(binding.mapView, MapLocationEngine(requireContext())) teiMapManager?.let { lifecycle.addObserver(it) } teiMapManager?.onCreate(savedInstanceState) teiMapManager?.teiFeatureType = presenter.getTrackedEntityType(tEType).featureType() diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipFragment.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipFragment.kt index 6a1eb2f5d1..f6d650cfcb 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipFragment.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipFragment.kt @@ -26,6 +26,7 @@ import org.dhis2.maps.ExternalMapNavigation import org.dhis2.maps.carousel.CarouselAdapter import org.dhis2.maps.geometry.mapper.featurecollection.MapRelationshipsToFeatureCollection import org.dhis2.maps.layer.MapLayerDialog +import org.dhis2.maps.location.MapLocationEngine import org.dhis2.maps.managers.RelationshipMapManager import org.dhis2.maps.model.RelationshipUiComponentModel import org.dhis2.ui.ThemeManager @@ -99,7 +100,7 @@ class RelationshipFragment : FragmentGlobalAbstract(), RelationshipView, OnMapCl DataBindingUtil.inflate(inflater, R.layout.fragment_relationships, container, false) relationshipAdapter = RelationshipAdapter(presenter, colorUtils) binding.relationshipRecycler.adapter = relationshipAdapter - relationshipMapManager = RelationshipMapManager(binding.mapView) + relationshipMapManager = RelationshipMapManager(binding.mapView, MapLocationEngine(requireContext())) lifecycle.addObserver(relationshipMapManager) relationshipMapManager.onCreate(savedInstanceState) relationshipMapManager.onMapClickListener = this diff --git a/commons/src/main/java/org/dhis2/commons/featureconfig/model/Feature.kt b/commons/src/main/java/org/dhis2/commons/featureconfig/model/Feature.kt index 425e712269..7b5e976a50 100644 --- a/commons/src/main/java/org/dhis2/commons/featureconfig/model/Feature.kt +++ b/commons/src/main/java/org/dhis2/commons/featureconfig/model/Feature.kt @@ -1,3 +1,6 @@ package org.dhis2.commons.featureconfig.model -enum class Feature(val description: String) { AUTO_LOGOUT("automatic log out") } +enum class Feature(val description: String) { + AUTO_LOGOUT("automatic log out"), + RESPONSIVE_HOME("responsive home"), +} diff --git a/commons/src/main/java/org/dhis2/commons/featureconfig/model/FeatureState.kt b/commons/src/main/java/org/dhis2/commons/featureconfig/model/FeatureState.kt index 7272f4adc0..aa45ad465d 100644 --- a/commons/src/main/java/org/dhis2/commons/featureconfig/model/FeatureState.kt +++ b/commons/src/main/java/org/dhis2/commons/featureconfig/model/FeatureState.kt @@ -1,3 +1,12 @@ package org.dhis2.commons.featureconfig.model -data class FeatureState(val feature: Feature, val enable: Boolean = false) +data class FeatureState( + val feature: Feature, + val enable: Boolean = false, + val canBeEnabled: Boolean = true, + val extras: FeatureOptions? = null, +) + +sealed class FeatureOptions { + data class ResponsiveHome(val totalItems: Int?) : FeatureOptions() +} diff --git a/commons/src/main/java/org/dhis2/commons/locationprovider/LocationProviderImpl.kt b/commons/src/main/java/org/dhis2/commons/locationprovider/LocationProviderImpl.kt index 5323eec4e4..b9e532a2a9 100644 --- a/commons/src/main/java/org/dhis2/commons/locationprovider/LocationProviderImpl.kt +++ b/commons/src/main/java/org/dhis2/commons/locationprovider/LocationProviderImpl.kt @@ -5,32 +5,19 @@ import android.annotation.SuppressLint import android.content.Context import android.content.Context.LOCATION_SERVICE import android.content.pm.PackageManager -import android.location.Criteria import android.location.Location import android.location.LocationListener import android.location.LocationManager import android.os.Bundle import androidx.core.app.ActivityCompat -class LocationProviderImpl(val context: Context) : LocationProvider { +open class LocationProviderImpl(val context: Context) : LocationProvider { - private val locationManager: LocationManager by lazy { initLocationManager() } - private val locationCriteria: Criteria by lazy { initHighAccuracyCriteria() } - private val locationProvider: String? by lazy { initLocationProvider() } - - private fun initLocationManager(): LocationManager { - return context.getSystemService(LOCATION_SERVICE) as LocationManager - } - - private fun initLocationProvider(): String? { - return locationManager.getBestProvider(locationCriteria, false) + private val locationManager: LocationManager by lazy { + context.getSystemService(LOCATION_SERVICE) as LocationManager } - - private fun initHighAccuracyCriteria(): Criteria { - return Criteria().apply { - accuracy = Criteria.ACCURACY_FINE - speedAccuracy = Criteria.ACCURACY_HIGH - } + private val locationProvider: String? by lazy { + locationManager.getProviders(true).find { it == "fused" } } private var locationListener: LocationListener? = null @@ -61,10 +48,7 @@ class LocationProviderImpl(val context: Context) : LocationProvider { if (hasPermission()) { locationListener = object : LocationListener { override fun onLocationChanged(location: Location) { - location.let { - onNewLocation(it) - stopLocationUpdates() - } + onNewLocation(location) } override fun onProviderEnabled(provider: String) { @@ -82,11 +66,10 @@ class LocationProviderImpl(val context: Context) : LocationProvider { } locationManager.requestLocationUpdates( + LocationManager.GPS_PROVIDER, 1000, - 5f, - locationCriteria, - locationListener!!, - null, + 1f, + requireNotNull(locationListener), ) } } diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/location/AccuracyIndicator.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/location/AccuracyIndicator.kt index 2285f6d142..00b0945a4e 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/location/AccuracyIndicator.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/location/AccuracyIndicator.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -15,6 +16,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -50,8 +52,8 @@ fun AccuracyIndicator( accuracyIndicatorState: AccuracyIndicatorState = rememberAccuracyIndicatorState(timeLeft = LOCATION_TIME_LEFT), accuracyRange: AccuracyRange, minLocationPrecision: Int? = null, - ) { + val scope = rememberCoroutineScope() val density = LocalDensity.current LaunchedEffect(key1 = accuracyRange) { @@ -61,11 +63,15 @@ fun AccuracyIndicator( Layout( modifier = modifier, content = { - Box(modifier = Modifier.size(24.dp)) { - if (accuracyIndicatorState.timeLeft > 0) { + Box( + modifier = Modifier + .size(24.dp), + ) { + if (accuracyIndicatorState.shouldDisplayProgress(accuracyRange)) { ProgressIndicator( modifier = Modifier.fillMaxSize(), - type = ProgressIndicatorType.CIRCULAR, + progress = accuracyIndicatorState.accuracyProgress(), + type = ProgressIndicatorType.CIRCULAR_SMALL, ) } else { Icon( @@ -78,7 +84,9 @@ fun AccuracyIndicator( } Text( - modifier = Modifier.height(24.dp), + modifier = Modifier + .height(24.dp) + .wrapContentHeight(Alignment.CenterVertically), text = buildAccuracyText(accuracyRange), ) Tag( @@ -112,32 +120,44 @@ fun AccuracyIndicator( layout(constraints.maxWidth, totalHeight) { val noLocation = (accuracyIndicatorState.timeLeft == 0) and (accuracyRange is AccuracyRange.None) + + val displayMessage = accuracyIndicatorState.displayMessage(accuracyRange) + if (!noLocation) { placeProgressIndicator( placeables[0], constraints.maxWidth, + totalHeight, accuracyIndicatorState.progressPosition, ) } else { placeNoLocationMessage( placeables[3], constraints.maxWidth, + totalHeight, ) } if (accuracyIndicatorState.displayInfo(accuracyRange)) { + accuracyIndicatorState.updateVerticalOffset( + scope, + ((totalHeight - placeables[0].height) / 2) + .takeIf { !displayMessage } ?: 0, + ) + placeAccuracyRange( placeables[1], placeables[0].width, with(density) { 8.dp.toPx() }.toInt(), + accuracyIndicatorState.verticalOffset, ) placeAccuracyLabel( placeables[2], placeables[0].width, placeables[1].width, - with(density) { 2.dp.toPx() }.toInt(), + accuracyIndicatorState.verticalOffset + with(density) { 2.dp.toPx() }.toInt(), with(density) { 24.dp.toPx() }.toInt(), ) - if ((accuracyIndicatorState.timeLeft == 0) or (accuracyRange is AccuracyRange.Low) or (accuracyRange is AccuracyRange.Medium)) { + if (displayMessage) { placeMessage( placeables[3], placeables[0].width, @@ -213,18 +233,20 @@ private fun messageText( ) else -> - stringResource(id = R.string.accuracy_please_wait) + " $timeLeft" + stringResource(id = R.string.accuracy_please_wait) } private fun Placeable.PlacementScope.placeProgressIndicator( progressIndicatorPlaceable: Placeable, totalWidth: Int, + totalHeight: Int, progressPosition: Float, ) { val centerPosition = (totalWidth - progressIndicatorPlaceable.width) / 2 + val centerPositionY = (totalHeight - progressIndicatorPlaceable.height) / 2 progressIndicatorPlaceable.placeRelative( (progressPosition * centerPosition).toInt(), - 0, + centerPositionY, ) } @@ -232,8 +254,9 @@ private fun Placeable.PlacementScope.placeAccuracyRange( accuracyRangePlaceable: Placeable, indicatorWidth: Int, offset: Int, + verticalOffset: Int, ) { - accuracyRangePlaceable.placeRelative(indicatorWidth + offset, 0) + accuracyRangePlaceable.placeRelative(indicatorWidth + offset, verticalOffset) } private fun Placeable.PlacementScope.placeAccuracyLabel( @@ -265,11 +288,13 @@ private fun Placeable.PlacementScope.placeMessage( private fun Placeable.PlacementScope.placeNoLocationMessage( messagePlaceable: Placeable, totalWidth: Int, + totalHeight: Int, ) { val centerPosition = (totalWidth - messagePlaceable.width) / 2 + val centerPositionY = (totalHeight - messagePlaceable.height) / 2 messagePlaceable.placeRelative( centerPosition, - 0, + centerPositionY, ) } diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/location/AccuracyIndicatorState.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/location/AccuracyIndicatorState.kt index c4927a8cae..7b20213571 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/location/AccuracyIndicatorState.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/location/AccuracyIndicatorState.kt @@ -17,11 +17,15 @@ import org.dhis2.maps.model.AccuracyRange @Stable interface AccuracyIndicatorState { val progressPosition: Float + val verticalOffset: Int var timeLeft: Int fun updateAccuracy(scope: CoroutineScope, accuracyRange: AccuracyRange) fun displayInfo(accuracyRange: AccuracyRange): Boolean fun displayMessage(accuracyRange: AccuracyRange): Boolean + fun accuracyProgress(): Float + fun updateVerticalOffset(scope: CoroutineScope, verticalOffset: Int) + fun shouldDisplayProgress(accuracyRange: AccuracyRange): Boolean } @Stable @@ -29,15 +33,32 @@ class AccuracyIndicatorStateImpl(private val defaultTimeLeft: Int) : AccuracyInd override val progressPosition: Float get() = _progressX.value + override val verticalOffset: Int + get() = _verticalOffset.value.toInt() + override var timeLeft by mutableIntStateOf(defaultTimeLeft) private var _progressX = Animatable(0f) + private var _verticalOffset = Animatable(0f) + + private var _accuracyProgress = Animatable(0f) + private val animationSpec = tween( durationMillis = 300, easing = FastOutSlowInEasing, ) + override fun accuracyProgress(): Float = _accuracyProgress.value + + override fun updateVerticalOffset(scope: CoroutineScope, verticalOffset: Int) { + scope.launch { + _verticalOffset.animateTo( + targetValue = verticalOffset.toFloat(), + ) + } + } + override fun updateAccuracy(scope: CoroutineScope, accuracyRange: AccuracyRange) { scope.launch { _progressX.animateTo( @@ -54,6 +75,10 @@ class AccuracyIndicatorStateImpl(private val defaultTimeLeft: Int) : AccuracyInd while (timeLeft > 0) { delay(1000) timeLeft-- + + _accuracyProgress.animateTo( + targetValue = 1f - timeLeft / defaultTimeLeft.toFloat(), + ) } } } @@ -65,9 +90,14 @@ class AccuracyIndicatorStateImpl(private val defaultTimeLeft: Int) : AccuracyInd override fun displayMessage(accuracyRange: AccuracyRange): Boolean { val noLocationNoTimeLeft = (timeLeft == 0) and (accuracyRange is AccuracyRange.None) val locationRequiresMessage = - (accuracyRange is AccuracyRange.Low) or (accuracyRange is AccuracyRange.Medium) + (accuracyRange is AccuracyRange.Low) or (accuracyRange is AccuracyRange.Medium) or + (accuracyRange is AccuracyRange.Good) return noLocationNoTimeLeft or locationRequiresMessage } + + override fun shouldDisplayProgress(accuracyRange: AccuracyRange): Boolean { + return timeLeft > 0 && (accuracyRange !is AccuracyRange.VeryGood) + } } @Composable diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/location/MapLocationEngine.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/location/MapLocationEngine.kt new file mode 100644 index 0000000000..16d20486c3 --- /dev/null +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/location/MapLocationEngine.kt @@ -0,0 +1,65 @@ +package org.dhis2.maps.location + +import android.app.PendingIntent +import android.content.Context +import android.os.Looper +import com.mapbox.mapboxsdk.location.engine.LocationEngine +import com.mapbox.mapboxsdk.location.engine.LocationEngineCallback +import com.mapbox.mapboxsdk.location.engine.LocationEngineRequest +import com.mapbox.mapboxsdk.location.engine.LocationEngineResult +import org.dhis2.commons.locationprovider.LocationProviderImpl +import java.lang.Exception + +class MapLocationEngine(context: Context) : LocationProviderImpl(context), LocationEngine { + + override fun getLastLocation(callback: LocationEngineCallback) { + getLastKnownLocation( + onNewLocation = { + callback.onSuccess(LocationEngineResult.create(it)) + }, + onPermissionNeeded = { + callback.onFailure(Exception("Permission needed")) + }, + onLocationDisabled = { + callback.onFailure(Exception("Location disabled")) + }, + ) + } + + override fun requestLocationUpdates( + request: LocationEngineRequest, + callback: LocationEngineCallback, + looper: Looper?, + ) { + getLastKnownLocation( + onNewLocation = { + callback.onSuccess(LocationEngineResult.create(it)) + }, + onPermissionNeeded = { + callback.onFailure(Exception("Permission needed")) + }, + onLocationDisabled = { + callback.onFailure(Exception("Location disabled")) + }, + ) + } + + override fun requestLocationUpdates( + request: LocationEngineRequest, + pendingIntent: PendingIntent?, + ) { + getLastKnownLocation( + onNewLocation = {}, + onPermissionNeeded = {}, + onLocationDisabled = {}, + ) + } + + override fun removeLocationUpdates(callback: LocationEngineCallback) { + stopLocationUpdates() + } + + override fun removeLocationUpdates(pendingIntent: PendingIntent?) { + stopLocationUpdates() + } +} diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/DefaultMapManager.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/DefaultMapManager.kt index e0436c6df4..cf4ced1984 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/DefaultMapManager.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/DefaultMapManager.kt @@ -5,6 +5,7 @@ import androidx.core.content.ContextCompat import com.mapbox.geojson.BoundingBox import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection +import com.mapbox.mapboxsdk.location.engine.LocationEngine import com.mapbox.mapboxsdk.maps.MapView import com.mapbox.mapboxsdk.style.layers.FillLayer import com.mapbox.mapboxsdk.style.layers.PropertyFactory @@ -16,8 +17,9 @@ import org.hisp.dhis.android.core.common.FeatureType class DefaultMapManager( mapView: MapView, + locationEngine: LocationEngine, private val featureType: FeatureType, -) : MapManager(mapView) { +) : MapManager(mapView, locationEngine) { private var featureCollection: FeatureCollection? = null diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/EventMapManager.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/EventMapManager.kt index 0dc7635fa9..2d5e11704d 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/EventMapManager.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/EventMapManager.kt @@ -8,6 +8,7 @@ import com.mapbox.geojson.BoundingBox import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection import com.mapbox.mapboxsdk.geometry.LatLng +import com.mapbox.mapboxsdk.location.engine.LocationEngine import com.mapbox.mapboxsdk.maps.MapView import com.mapbox.mapboxsdk.style.sources.GeoJsonSource import org.dhis2.maps.R @@ -18,7 +19,10 @@ import org.dhis2.maps.geometry.mapper.featurecollection.MapTeisToFeatureCollecti import org.dhis2.maps.layer.LayerType import org.hisp.dhis.android.core.common.FeatureType -class EventMapManager(mapView: MapView) : MapManager(mapView) { +class EventMapManager( + mapView: MapView, + locationEngine: LocationEngine, +) : MapManager(mapView, locationEngine) { private var featureCollection: FeatureCollection? = null private var deFeatureCollection: Map = emptyMap() diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/MapManager.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/MapManager.kt index e582ff96f0..51e27f1eca 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/MapManager.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/MapManager.kt @@ -13,6 +13,7 @@ import com.mapbox.mapboxsdk.geometry.LatLng import com.mapbox.mapboxsdk.geometry.LatLngBounds import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions import com.mapbox.mapboxsdk.location.LocationComponentOptions +import com.mapbox.mapboxsdk.location.engine.LocationEngine import com.mapbox.mapboxsdk.location.permissions.PermissionsListener import com.mapbox.mapboxsdk.location.permissions.PermissionsManager import com.mapbox.mapboxsdk.maps.MapView @@ -32,7 +33,10 @@ import org.dhis2.maps.layer.basemaps.BaseMapManager import org.dhis2.maps.layer.basemaps.BaseMapStyle import org.dhis2.maps.layer.basemaps.BaseMapStyleBuilder.internalBaseMap -abstract class MapManager(val mapView: MapView) : LifecycleObserver { +abstract class MapManager( + val mapView: MapView, + val locationEngine: LocationEngine?, +) : LifecycleObserver { var map: MapboxMap? = null lateinit var mapLayerManager: MapLayerManager @@ -233,8 +237,14 @@ abstract class MapManager(val mapView: MapView) : LifecycleObserver { LocationComponentOptions.builder(mapView.context) .accuracyAnimationEnabled(true) .build(), - ) - .useDefaultLocationEngine(true) + ).apply { + if (this@MapManager.locationEngine != null) { + useDefaultLocationEngine(false) + locationEngine(this@MapManager.locationEngine) + } else { + useDefaultLocationEngine(true) + } + } .build(), ) isLocationComponentEnabled = true diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/RelationshipMapManager.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/RelationshipMapManager.kt index 701c966658..5b72d4bda1 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/RelationshipMapManager.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/RelationshipMapManager.kt @@ -4,6 +4,7 @@ import androidx.appcompat.content.res.AppCompatResources import com.mapbox.geojson.BoundingBox import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection +import com.mapbox.mapboxsdk.location.engine.LocationEngine import com.mapbox.mapboxsdk.maps.MapView import com.mapbox.mapboxsdk.style.sources.GeoJsonSource import com.mapbox.mapboxsdk.utils.BitmapUtils @@ -13,7 +14,10 @@ import org.dhis2.maps.geometry.mapper.featurecollection.MapRelationshipsToFeatur import org.dhis2.maps.geometry.mapper.featurecollection.MapTeisToFeatureCollection import org.dhis2.maps.layer.LayerType -class RelationshipMapManager(mapView: MapView) : MapManager(mapView) { +class RelationshipMapManager( + mapView: MapView, + locationEngine: LocationEngine, +) : MapManager(mapView, locationEngine) { companion object { const val RELATIONSHIP_ICON = "RELATIONSHIP_ICON" diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/TeiMapManager.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/TeiMapManager.kt index 1b1ce60836..e5bd386382 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/TeiMapManager.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/managers/TeiMapManager.kt @@ -14,6 +14,7 @@ import com.mapbox.geojson.BoundingBox import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection import com.mapbox.mapboxsdk.geometry.LatLng +import com.mapbox.mapboxsdk.location.engine.LocationEngine import com.mapbox.mapboxsdk.maps.MapView import com.mapbox.mapboxsdk.style.sources.GeoJsonSource import com.mapbox.mapboxsdk.utils.BitmapUtils @@ -30,9 +31,11 @@ import org.dhis2.maps.layer.LayerType import org.dhis2.maps.layer.MapLayerManager import org.dhis2.maps.model.MapStyle import org.hisp.dhis.android.core.common.FeatureType -import java.util.HashMap -class TeiMapManager(mapView: MapView) : MapManager(mapView) { +class TeiMapManager( + mapView: MapView, + locationEngine: LocationEngine, +) : MapManager(mapView, locationEngine) { private var fieldFeatureCollections: Map = emptyMap() private var teiFeatureCollections: HashMap? = null @@ -431,6 +434,7 @@ class TeiMapManager(mapView: MapView) : MapManager(mapView) { RELATIONSHIP_UID, features.first().getStringProperty(RELATIONSHIP_UID), ) + else -> features.first() } } diff --git a/dhis2_android_maps/src/main/java/org/dhis2/maps/views/MapSelectorActivity.kt b/dhis2_android_maps/src/main/java/org/dhis2/maps/views/MapSelectorActivity.kt index 2596ccedf2..5f5be27b00 100644 --- a/dhis2_android_maps/src/main/java/org/dhis2/maps/views/MapSelectorActivity.kt +++ b/dhis2_android_maps/src/main/java/org/dhis2/maps/views/MapSelectorActivity.kt @@ -7,7 +7,6 @@ import android.content.Intent import android.content.pm.PackageManager import android.location.LocationListener import android.os.Bundle -import android.os.Looper import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.annotation.RequiresPermission @@ -26,7 +25,6 @@ import androidx.lifecycle.asLiveData import androidx.recyclerview.widget.GridLayoutManager import com.mapbox.mapboxsdk.geometry.LatLng import com.mapbox.mapboxsdk.location.engine.LocationEngineDefault -import com.mapbox.mapboxsdk.location.engine.LocationEngineRequest import com.mapbox.mapboxsdk.location.engine.MapboxFusedLocationEngineImpl import com.mapbox.mapboxsdk.maps.MapboxMap import org.dhis2.commons.bindings.clipWithAllRoundedCorners @@ -40,9 +38,8 @@ import org.dhis2.maps.geometry.bound.GetBoundingBox import org.dhis2.maps.geometry.getLatLngPointList import org.dhis2.maps.geometry.polygon.PolygonAdapter import org.dhis2.maps.location.AccuracyIndicator -import org.dhis2.maps.location.LOCATION_FASTEST_INTERVAL -import org.dhis2.maps.location.LOCATION_INTERVAL import org.dhis2.maps.location.MapActivityLocationCallback +import org.dhis2.maps.location.MapLocationEngine import org.dhis2.maps.managers.DefaultMapManager import org.dhis2.maps.model.AccuracyRange import org.dhis2.ui.theme.Dhis2Theme @@ -119,7 +116,7 @@ class MapSelectorActivity : binding.mapView.clipWithAllRoundedCorners(8.dp) - mapManager = DefaultMapManager(binding.mapView, locationType) + mapManager = DefaultMapManager(binding.mapView, MapLocationEngine(this), locationType) mapboxLocationProvider = MapboxFusedLocationEngineImpl(this) mapSelectorViewModel.featureCollection.asLiveData().observe(this) { featureCollection -> mapManager.update( @@ -222,13 +219,10 @@ class MapSelectorActivity : @RequiresPermission(permission.ACCESS_FINE_LOCATION) private fun initLocationUpdates() { - mapboxLocationProvider.requestLocationUpdates( - LocationEngineRequest.Builder(LOCATION_INTERVAL) - .setFastestInterval(LOCATION_FASTEST_INTERVAL) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) - .build(), - locationListener, - Looper.myLooper(), + locationProvider.getLastKnownLocation( + { location -> locationListener.onLocationChanged(location) }, + {}, + {}, ) } diff --git a/dhis2_android_maps/src/main/res/layout/activity_map_selector.xml b/dhis2_android_maps/src/main/res/layout/activity_map_selector.xml index a30b06600f..4133dd1cce 100644 --- a/dhis2_android_maps/src/main/res/layout/activity_map_selector.xml +++ b/dhis2_android_maps/src/main/res/layout/activity_map_selector.xml @@ -90,6 +90,7 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" + android:layout_marginTop="16dp" app:layout_constraintBottom_toTopOf="@id/recycler"/>