Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
Expand All @@ -51,11 +53,13 @@ import org.groundplatform.android.system.PermissionsManager
import org.groundplatform.android.system.SettingsManager
import org.groundplatform.android.ui.components.MapFloatingActionButtonType
import org.groundplatform.android.ui.map.CameraUpdateRequest
import org.groundplatform.android.ui.map.Feature
import org.groundplatform.android.ui.map.NewCameraPositionViaBounds
import org.groundplatform.android.ui.map.NewCameraPositionViaCoordinates
import org.groundplatform.android.ui.map.NewCameraPositionViaCoordinatesAndZoomLevel
import org.groundplatform.android.ui.map.gms.GmsExt.toBounds
import org.groundplatform.android.ui.map.gms.toCoordinates
import org.groundplatform.android.ui.util.getDefaultColor
import org.groundplatform.domain.model.Survey
import org.groundplatform.domain.model.geometry.Coordinates
import org.groundplatform.domain.model.imagery.TileSource
Expand All @@ -67,6 +71,7 @@ import org.groundplatform.domain.repository.OfflineAreaRepositoryInterface
import org.groundplatform.domain.repository.SurveyRepositoryInterface
import timber.log.Timber

@OptIn(ExperimentalCoroutinesApi::class)
open class BaseMapViewModel
@Inject
constructor(
Expand Down Expand Up @@ -129,6 +134,33 @@ constructor(
}
.asLiveData()

/**
* Read-only LOI features for the active survey. Lazily initialized to avoid unnecessary database
* queries when not rendered.
*/
val existingLoiFeatures: Flow<Set<Feature>> by lazy {
surveyRepository.activeSurveyFlow
.flatMapLatest { survey ->
if (survey == null) flowOf(emptySet())
else locationOfInterestRepository.getValidLois(survey)
}
.map { lois ->
lois
.map {
Feature(
id = it.id,
type = Feature.Type.LOCATION_OF_INTEREST,
geometry = it.geometry,
style = Feature.Style(it.job.getDefaultColor()),
clusterable = false,
)
}
.toSet()
}
.onStart { emit(setOf()) }
.distinctUntilChanged()
}

/** Returns whether the user has granted fine location permission. */
fun hasLocationPermission() =
permissionsManager.isGranted(Manifest.permission.ACCESS_FINE_LOCATION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package org.groundplatform.android.ui.datacollection.tasks.point
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import org.groundplatform.android.ui.datacollection.tasks.AbstractTaskMapFragment
import org.groundplatform.android.ui.datacollection.tasks.launchWhenTaskVisible
import org.groundplatform.android.ui.map.Feature
Expand Down Expand Up @@ -50,7 +51,12 @@ class DropPinTaskMapFragment @Inject constructor() :
taskViewModel.updateCameraPosition(position)
}

override fun renderFeatures(): Flow<Set<Feature>> = taskViewModel.features
override fun renderFeatures(): Flow<Set<Feature>> =
combine(getMapViewModel().existingLoiFeatures, taskViewModel.features) {
loiFeatures,
pinFeatures ->
loiFeatures + pinFeatures
}

override fun setDefaultViewPort() {
val feature = taskViewModel.features.value?.firstOrNull() ?: return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import android.view.View
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.groundplatform.android.ui.datacollection.tasks.AbstractTaskMapFragment
Expand Down Expand Up @@ -66,8 +67,10 @@ class DrawAreaTaskMapFragment @Inject constructor() :
}

override fun renderFeatures(): Flow<Set<Feature>> =
taskViewModel.draftArea.map { feature: Feature? ->
if (feature == null) setOf() else setOf(feature)
combine(getMapViewModel().existingLoiFeatures, taskViewModel.draftArea) {
loiFeatures,
draftArea: Feature? ->
loiFeatures + setOfNotNull(draftArea)
}

override fun onMapCameraMoved(position: CameraPosition) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,26 @@ import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status
import com.google.android.gms.location.LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.groundplatform.android.BaseHiltTest
import org.groundplatform.android.FakeData.AREA_OF_INTEREST
import org.groundplatform.android.FakeData.JOB
import org.groundplatform.android.FakeData.LOCATION_OF_INTEREST
import org.groundplatform.android.FakeData.SURVEY
import org.groundplatform.android.system.FINE_LOCATION_UPDATES_REQUEST
import org.groundplatform.android.system.LocationManager
import org.groundplatform.android.system.PermissionsManager
import org.groundplatform.android.system.SettingsManager
import org.groundplatform.android.ui.components.MapFloatingActionButtonType
import org.groundplatform.android.ui.map.Feature
import org.groundplatform.android.ui.util.getDefaultColor
import org.groundplatform.domain.repository.LocationOfInterestRepositoryInterface
import org.groundplatform.domain.repository.MapStateRepositoryInterface
import org.groundplatform.domain.repository.OfflineAreaRepositoryInterface
Expand Down Expand Up @@ -160,6 +169,38 @@ class BaseMapViewModelTest : BaseHiltTest() {
verify(locationManager).disableLocationUpdates()
}

@Test
fun `Should show existing features on the map`() =
runWithTestDispatcher {
setupMocks()
val areaOfInterest = AREA_OF_INTEREST.copy(id = "loi id 2")
whenever(surveyRepository.activeSurveyFlow).thenReturn(MutableStateFlow(SURVEY))
whenever(locationOfInterestRepository.getValidLois(SURVEY))
.thenReturn(flowOf(setOf(LOCATION_OF_INTEREST, areaOfInterest)))

val features = viewModel.existingLoiFeatures.first { it.isNotEmpty() }

assertThat(features)
.containsExactly(
Feature(
id = LOCATION_OF_INTEREST.id,
type = Feature.Type.LOCATION_OF_INTEREST,
geometry = LOCATION_OF_INTEREST.geometry,
style = Feature.Style(JOB.getDefaultColor()),
clusterable = false,
selected = false,
),
Feature(
id = areaOfInterest.id,
type = Feature.Type.LOCATION_OF_INTEREST,
geometry = areaOfInterest.geometry,
style = Feature.Style(JOB.getDefaultColor()),
clusterable = false,
selected = false,
),
)
}

private fun setupMocks(
isLocationLocked: Boolean = false,
hasLocationPermissions: Boolean = true,
Expand Down
Loading