Skip to content

Commit

Permalink
[ANDROAPP-5165] fix accuracy indicator comments (dhis2#3778)
Browse files Browse the repository at this point in the history
* fix accuracy indicator comments

Signed-off-by: Pablo <[email protected]>

* remove commented code

Signed-off-by: Pablo <[email protected]>

---------

Signed-off-by: Pablo <[email protected]>
  • Loading branch information
Balcan authored Aug 27, 2024
1 parent f86f5ee commit 021162c
Show file tree
Hide file tree
Showing 16 changed files with 200 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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"),
}
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -82,11 +66,10 @@ class LocationProviderImpl(val context: Context) : LocationProvider {
}

locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000,
5f,
locationCriteria,
locationListener!!,
null,
1f,
requireNotNull(locationListener),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ 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
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
Expand Down Expand Up @@ -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) {
Expand All @@ -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(
Expand All @@ -78,7 +84,9 @@ fun AccuracyIndicator(
}

Text(
modifier = Modifier.height(24.dp),
modifier = Modifier
.height(24.dp)
.wrapContentHeight(Alignment.CenterVertically),
text = buildAccuracyText(accuracyRange),
)
Tag(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -213,27 +233,30 @@ 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,
)
}

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(
Expand Down Expand Up @@ -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,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,48 @@ 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
class AccuracyIndicatorStateImpl(private val defaultTimeLeft: Int) : AccuracyIndicatorState {
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<Float>(
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(
Expand All @@ -54,6 +75,10 @@ class AccuracyIndicatorStateImpl(private val defaultTimeLeft: Int) : AccuracyInd
while (timeLeft > 0) {
delay(1000)
timeLeft--

_accuracyProgress.animateTo(
targetValue = 1f - timeLeft / defaultTimeLeft.toFloat(),
)
}
}
}
Expand All @@ -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
Expand Down
Loading

0 comments on commit 021162c

Please sign in to comment.