Skip to content
Merged
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 @@ -27,6 +27,7 @@ import org.groundplatform.domain.repository.SurveyRepositoryInterface
import org.groundplatform.domain.repository.UserRepositoryInterface
import org.groundplatform.domain.usecases.GetLoiReportUseCase
import org.groundplatform.domain.usecases.submission.SubmitDataUseCase
import org.groundplatform.domain.usecases.survey.GetDataSharingTermsUseCase
import org.groundplatform.domain.usecases.survey.GetSurveyListItemUseCase
import org.groundplatform.domain.usecases.survey.RemoveOfflineSurveyUseCase
import org.groundplatform.domain.usecases.survey.SyncSurveyUseCase
Expand Down Expand Up @@ -90,4 +91,8 @@ object UseCaseModule {
surveyRepository: SurveyRepositoryInterface,
mapStateRepository: MapStateRepositoryInterface,
) = RemoveOfflineSurveyUseCase(surveyRepository, mapStateRepository)

@Provides
fun providesGetDataSharingTermsUseCase(surveyRepository: SurveyRepositoryInterface) =
GetDataSharingTermsUseCase(surveyRepository)
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,11 @@ constructor(
override suspend fun unsubscribeFromSurveyUpdates(surveyId: String) {
remoteDataStore.unsubscribeFromSurveyUpdates(surveyId)
}

override fun getDataSharingConsent(surveyId: String): Boolean =
localValueStore.getDataSharingConsent(surveyId)

override fun setDataSharingConsent(surveyId: String, consent: Boolean) {
localValueStore.setDataSharingConsent(surveyId, consent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.groundplatform.android.R
import org.groundplatform.android.common.Constants.CLUSTERING_ZOOM_THRESHOLD
import org.groundplatform.android.data.local.LocalValueStore
import org.groundplatform.android.system.LocationManager
import org.groundplatform.android.system.PermissionsManager
import org.groundplatform.android.system.SettingsManager
Expand All @@ -55,7 +54,6 @@ import org.groundplatform.android.ui.home.mapcontainer.jobs.SelectedLoiSheetData
import org.groundplatform.android.ui.map.Feature
import org.groundplatform.android.ui.map.gms.GmsExt.area
import org.groundplatform.android.ui.util.getDefaultColor
import org.groundplatform.android.usecases.datasharingterms.GetDataSharingTermsUseCase
import org.groundplatform.domain.model.Survey
import org.groundplatform.domain.model.job.Job
import org.groundplatform.domain.model.locationofinterest.LocationOfInterest
Expand All @@ -66,6 +64,7 @@ import org.groundplatform.domain.repository.SubmissionRepositoryInterface
import org.groundplatform.domain.repository.SurveyRepositoryInterface
import org.groundplatform.domain.repository.UserRepositoryInterface
import org.groundplatform.domain.usecases.GetLoiReportUseCase
import org.groundplatform.domain.usecases.survey.GetDataSharingTermsUseCase
import org.groundplatform.feature.pdf.LoiReportExporter
import org.groundplatform.ui.components.loireport.LoiReportAction
import timber.log.Timber
Expand All @@ -85,7 +84,6 @@ internal constructor(
permissionsManager: PermissionsManager,
private val surveyRepository: SurveyRepositoryInterface,
private val userRepository: UserRepositoryInterface,
private val localValueStore: LocalValueStore,
private val locationOfInterestHelper: LocationOfInterestHelper,
private val getLoiReportUseCase: GetLoiReportUseCase,
private val loiReportExporter: LoiReportExporter,
Expand Down Expand Up @@ -300,9 +298,10 @@ internal constructor(
featureClicked.value = features.minByOrNull { it.geometry.area() }
}

/** Records user consent to the data sharing terms of the active survey. */
fun grantDataSharingConsent() {
val survey = requireNotNull(surveyRepository.activeSurvey)
localValueStore.setDataSharingConsent(survey.id, true)
surveyRepository.setDataSharingConsent(survey.id, true)
}

/**
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,10 @@ interface SurveyRepositoryInterface {
suspend fun subscribeToSurveyUpdates(surveyId: String)

suspend fun unsubscribeFromSurveyUpdates(surveyId: String)

/** Returns true if the user has agreed to data sharing terms for the given [surveyId]. */
fun getDataSharingConsent(surveyId: String): Boolean

/** Records the user's [consent] to data sharing terms for the given [surveyId]. */
fun setDataSharingConsent(surveyId: String, consent: Boolean)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.groundplatform.android.usecases.datasharingterms

import javax.inject.Inject
import org.groundplatform.android.data.local.LocalValueStore
package org.groundplatform.domain.usecases.survey

import org.groundplatform.domain.model.Survey
import org.groundplatform.domain.repository.SurveyRepositoryInterface

class GetDataSharingTermsUseCase
@Inject
constructor(
private val localValueStore: LocalValueStore,
private val surveyRepository: SurveyRepositoryInterface,
) {
/**
* Returns the data sharing terms for the currently active survey, if not already accepted.
*
* Returns [Result.success] with `null` if the survey has no terms or the user has already accepted
* them. Returns [Result.failure] with [InvalidCustomSharingTermsException] if custom terms text is
* blank, or [IllegalStateException] if no survey is currently active.
*/
class GetDataSharingTermsUseCase(private val surveyRepository: SurveyRepositoryInterface) {

/** Returns the data sharing terms for the currently active survey, if not already accepted. */
operator fun invoke(): Result<Survey.DataSharingTerms?> = runCatching {
val survey = surveyRepository.activeSurvey ?: error("No active survey")
val sharingTerms = survey.dataSharingTerms
if (sharingTerms == null || localValueStore.getDataSharingConsent(survey.id)) {
if (sharingTerms == null || surveyRepository.getDataSharingConsent(survey.id)) {
// User previously agreed to the terms or data sharing terms are missing.
return Result.success(null)
}
Expand All @@ -41,5 +41,6 @@ constructor(
return Result.success(sharingTerms)
}

/** Thrown when a survey defines custom data sharing terms with blank text. */
class InvalidCustomSharingTermsException : Exception()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.groundplatform.domain.usecases.survey

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlinx.coroutines.test.runTest
import org.groundplatform.domain.model.Survey
import org.groundplatform.testing.FakeDataGenerator
import org.groundplatform.testing.FakeSurveyRepository

class GetDataSharingTermsUseCaseTest {
private val surveyRepository = FakeSurveyRepository()
private val getDataSharingTermsUseCase = GetDataSharingTermsUseCase(surveyRepository)

private suspend fun activateSurvey(survey: Survey) {
surveyRepository.offlineSurveys = listOf(survey)
surveyRepository.activateSurvey(survey.id)
}

@Test
fun `Fails with exception if no survey active`() {
val result = getDataSharingTermsUseCase()

assertTrue(result.isFailure)
assertIs<IllegalStateException>(result.exceptionOrNull())
assertEquals("No active survey", result.exceptionOrNull()?.message)
}

@Test
fun `Fails with custom exception if custom data sharing terms are invalid`() = runTest {
val survey = FakeDataGenerator.newSurvey(dataSharingTerms = Survey.DataSharingTerms.Custom(""))
activateSurvey(survey)

val result = getDataSharingTermsUseCase()

assertTrue(result.isFailure)
assertIs<GetDataSharingTermsUseCase.InvalidCustomSharingTermsException>(
result.exceptionOrNull()
)
}

@Test
fun `Succeeds with null if data sharing terms is already accepted`() = runTest {
val survey = FakeDataGenerator.newSurvey()
activateSurvey(survey)
surveyRepository.setDataSharingConsent(survey.id, true)

val result = getDataSharingTermsUseCase()

assertTrue(result.isSuccess)
assertNull(result.getOrNull())
}

@Test
fun `Succeeds with null if data sharing terms is missing`() = runTest {
val survey = FakeDataGenerator.newSurvey().copy(dataSharingTerms = null)
activateSurvey(survey)

val result = getDataSharingTermsUseCase()

assertTrue(result.isSuccess)
assertNull(result.getOrNull())
}

@Test
fun `Succeeds with data sharing terms if not already accepted`() = runTest {
val survey = FakeDataGenerator.newSurvey()
activateSurvey(survey)

val result = getDataSharingTermsUseCase()

assertTrue(result.isSuccess)
assertEquals(survey.dataSharingTerms, result.getOrNull())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class FakeSurveyRepository : SurveyRepositoryInterface {

val onGetRemoteSurveyCall = FakeCall<String, Survey?> { id -> remoteSurveys.find { it.id == id } }

private val dataSharingConsentMap = mutableMapOf<String, Boolean>()

override suspend fun saveSurvey(survey: Survey) {
offlineSurveys = offlineSurveys + survey
}
Expand Down Expand Up @@ -75,4 +77,11 @@ class FakeSurveyRepository : SurveyRepositoryInterface {
override suspend fun unsubscribeFromSurveyUpdates(surveyId: String) {
subscribedSurveyIds.remove(surveyId)
}

override fun getDataSharingConsent(surveyId: String): Boolean =
dataSharingConsentMap[surveyId] ?: false

override fun setDataSharingConsent(surveyId: String, consent: Boolean) {
dataSharingConsentMap[surveyId] = consent
}
}
Loading