diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfo.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfo.kt index 551b691e8e..22e7765840 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfo.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfo.kt @@ -4,6 +4,7 @@ data class SyncInfo( val isLoggedIn: Boolean = true, val isConfigurationLoadingProgressBarVisible: Boolean = false, val isLoginPromptSectionVisible: Boolean = false, + val isImageSyncSectionVisible: Boolean = false, val syncInfoSectionRecords: SyncInfoSectionRecords = SyncInfoSectionRecords(), val syncInfoSectionImages: SyncInfoSectionImages = SyncInfoSectionImages(), val syncInfoSectionModules: SyncInfoSectionModules = SyncInfoSectionModules(), diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoFragment.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoFragment.kt index 2f0794ab6b..bb5c777d72 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoFragment.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/SyncInfoFragment.kt @@ -12,6 +12,7 @@ import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.core.view.isGone import androidx.core.view.isInvisible +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -195,7 +196,7 @@ internal class SyncInfoFragment : Fragment(R.layout.fragment_sync_info) { renderRecordsSection(syncInfo.syncInfoSectionRecords, config) // Images section - binding.layoutImagesSync.isGone = !config.isSyncInfoImageSyncVisible + binding.layoutImagesSync.isVisible = config.isSyncInfoImageSyncVisible && syncInfo.isImageSyncSectionVisible renderImagesSection(syncInfo.syncInfoSectionImages) // Modules section diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCase.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCase.kt index d3b21473b1..e94566b55b 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCase.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCase.kt @@ -25,6 +25,7 @@ import com.simprints.infra.config.store.models.TokenKeyType import com.simprints.infra.config.store.models.canSyncDataToSimprints import com.simprints.infra.config.store.models.isCommCareEventDownSyncAllowed import com.simprints.infra.config.store.models.isModuleSelectionAvailable +import com.simprints.infra.config.store.models.isSampleUploadEnabledInProject import com.simprints.infra.config.store.models.isSimprintsEventDownSyncAllowed import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager @@ -320,12 +321,13 @@ internal class ObserveSyncInfoUseCase @Inject constructor( ) val syncInfo = SyncInfo( - isLoggedIn, + isLoggedIn = isLoggedIn, isConfigurationLoadingProgressBarVisible = isRefreshing, isLoginPromptSectionVisible = isReLoginRequired && !isPreLogoutUpSync, - syncInfoSectionRecords, - syncInfoSectionImages, - syncInfoSectionModules, + isImageSyncSectionVisible = projectConfig.isSampleUploadEnabledInProject(), + syncInfoSectionRecords = syncInfoSectionRecords, + syncInfoSectionImages = syncInfoSectionImages, + syncInfoSectionModules = syncInfoSectionModules, ) return@combine9 syncInfo }.onRecordSyncComplete { diff --git a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCaseTest.kt b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCaseTest.kt index b85298f6c7..93edd48e36 100644 --- a/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCaseTest.kt +++ b/feature/dashboard/src/test/java/com/simprints/feature/dashboard/settings/syncinfo/usecase/ObserveSyncInfoUseCaseTest.kt @@ -23,6 +23,7 @@ import com.simprints.infra.config.store.models.UpSynchronizationConfiguration import com.simprints.infra.config.store.models.canSyncDataToSimprints import com.simprints.infra.config.store.models.isCommCareEventDownSyncAllowed import com.simprints.infra.config.store.models.isModuleSelectionAvailable +import com.simprints.infra.config.store.models.isSampleUploadEnabledInProject import com.simprints.infra.config.store.models.isSimprintsEventDownSyncAllowed import com.simprints.infra.config.store.tokenization.TokenizationProcessor import com.simprints.infra.config.sync.ConfigManager @@ -180,6 +181,7 @@ class ObserveSyncInfoUseCaseTest { every { any().isModuleSelectionAvailable() } returns false every { any().isSimprintsEventDownSyncAllowed() } returns true every { any().isCommCareEventDownSyncAllowed() } returns false + every { any().isSampleUploadEnabledInProject() } returns true every { commCarePermissionChecker.hasCommCarePermissions() } returns true } diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/models/ProjectConfiguration.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/ProjectConfiguration.kt index de9f8f56f2..cf85f651e7 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/models/ProjectConfiguration.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/ProjectConfiguration.kt @@ -37,14 +37,30 @@ fun ProjectConfiguration.canSyncBiometricDataToSimprints(): Boolean = fun ProjectConfiguration.canSyncAnalyticsDataToSimprints(): Boolean = synchronization.up.simprints.kind == UpSynchronizationConfiguration.UpSynchronizationKind.ONLY_ANALYTICS -fun ProjectConfiguration.isSimprintsEventDownSyncAllowed(): Boolean = - synchronization.down.simprints != null && +fun ProjectConfiguration.isSimprintsEventDownSyncAllowed(): Boolean = synchronization.down.simprints != null && synchronization.down.simprints.frequency != Frequency.ONLY_PERIODICALLY_UP_SYNC fun ProjectConfiguration.isCommCareEventDownSyncAllowed(): Boolean = synchronization.down.commCare != null fun ProjectConfiguration.imagesUploadRequiresUnmeteredConnection(): Boolean = synchronization.up.simprints.imagesRequireUnmeteredConnection +fun ProjectConfiguration.isSampleUploadEnabledInProject(): Boolean = listOfNotNull( + face?.rankOne?.imageSavingStrategy?.let { it != FaceConfiguration.ImageSavingStrategy.NEVER }, + face?.simFace?.imageSavingStrategy?.let { it != FaceConfiguration.ImageSavingStrategy.NEVER }, + fingerprint + ?.nec + ?.vero2 + ?.imageSavingStrategy + ?.let { it != Vero2Configuration.ImageSavingStrategy.NEVER }, + fingerprint + ?.secugenSimMatcher + ?.vero2 + ?.imageSavingStrategy + ?.let { it != Vero2Configuration.ImageSavingStrategy.NEVER }, +).let { explicitStrategies -> + explicitStrategies.isNotEmpty() && explicitStrategies.any { it } +} + fun ProjectConfiguration.allowedAgeRanges(): List = listOfNotNull( face?.rankOne?.allowedAgeRange, fingerprint?.secugenSimMatcher?.allowedAgeRange, diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/models/ProjectConfigurationTest.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/models/ProjectConfigurationTest.kt index 6eaaddc567..3c17b34cf7 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/models/ProjectConfigurationTest.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/models/ProjectConfigurationTest.kt @@ -1,6 +1,6 @@ package com.simprints.infra.config.store.models -import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.* import com.simprints.infra.config.store.models.UpSynchronizationConfiguration.CoSyncUpSynchronizationConfiguration import com.simprints.infra.config.store.models.UpSynchronizationConfiguration.SimprintsUpSynchronizationConfiguration import com.simprints.infra.config.store.models.UpSynchronizationConfiguration.UpSynchronizationKind.ALL @@ -10,10 +10,12 @@ import com.simprints.infra.config.store.models.UpSynchronizationConfiguration.Up import com.simprints.infra.config.store.testtools.faceConfiguration import com.simprints.infra.config.store.testtools.faceSdkConfiguration import com.simprints.infra.config.store.testtools.fingerprintConfiguration +import com.simprints.infra.config.store.testtools.fingerprintSdkConfiguration import com.simprints.infra.config.store.testtools.projectConfiguration import com.simprints.infra.config.store.testtools.simprintsDownSyncConfigurationConfiguration import com.simprints.infra.config.store.testtools.simprintsUpSyncConfigurationConfiguration import com.simprints.infra.config.store.testtools.synchronizationConfiguration +import com.simprints.infra.config.store.testtools.vero2Configuration import org.junit.Test class ProjectConfigurationTest { @@ -232,7 +234,7 @@ class ProjectConfigurationTest { val config = projectConfiguration.copy( synchronization = synchronizationConfiguration.copy( down = synchronizationConfiguration.down.copy( - simprints = null + simprints = null, ), ), ) @@ -245,7 +247,7 @@ class ProjectConfigurationTest { val config = projectConfiguration.copy( synchronization = synchronizationConfiguration.copy( down = synchronizationConfiguration.down.copy( - commCare = DownSynchronizationConfiguration.CommCareDownSynchronizationConfiguration + commCare = DownSynchronizationConfiguration.CommCareDownSynchronizationConfiguration, ), ), ) @@ -258,7 +260,7 @@ class ProjectConfigurationTest { val config = projectConfiguration.copy( synchronization = synchronizationConfiguration.copy( down = synchronizationConfiguration.down.copy( - commCare = null + commCare = null, ), ), ) @@ -284,6 +286,74 @@ class ProjectConfigurationTest { } } + @Test + fun `isSampleUploadEnabledInProject should return correct based on available saving strategy`() { + data class TestData( + val rankOneStrategy: FaceConfiguration.ImageSavingStrategy? = null, + val simFaceStrategy: FaceConfiguration.ImageSavingStrategy? = null, + val secugenStrategy: Vero2Configuration.ImageSavingStrategy? = null, + val necStragery: Vero2Configuration.ImageSavingStrategy? = null, + val result: Boolean, + ) + listOf( + TestData( + rankOneStrategy = FaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN, + simFaceStrategy = FaceConfiguration.ImageSavingStrategy.NEVER, + result = true, + ), + TestData( + necStragery = Vero2Configuration.ImageSavingStrategy.NEVER, + secugenStrategy = Vero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN, + result = true, + ), + TestData( + simFaceStrategy = FaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN, + secugenStrategy = Vero2Configuration.ImageSavingStrategy.NEVER, + result = true, + ), + TestData( + simFaceStrategy = FaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN, + secugenStrategy = Vero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN, + result = true, + ), + TestData( + necStragery = Vero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN, + result = true, + ), + TestData( + rankOneStrategy = FaceConfiguration.ImageSavingStrategy.NEVER, + simFaceStrategy = FaceConfiguration.ImageSavingStrategy.NEVER, + necStragery = Vero2Configuration.ImageSavingStrategy.NEVER, + secugenStrategy = Vero2Configuration.ImageSavingStrategy.NEVER, + result = false, + ), + TestData( + rankOneStrategy = FaceConfiguration.ImageSavingStrategy.NEVER, + necStragery = Vero2Configuration.ImageSavingStrategy.NEVER, + result = false, + ), + TestData(result = false), + ).forEach { (rankOne, simFace, secugen, nec, result) -> + assertThat( + projectConfiguration + .copy( + face = faceConfiguration.copy( + rankOne = rankOne?.let { faceSdkConfiguration.copy(imageSavingStrategy = it) }, + simFace = simFace?.let { faceSdkConfiguration.copy(imageSavingStrategy = it) }, + ), + fingerprint = fingerprintConfiguration.copy( + secugenSimMatcher = secugen?.let { + fingerprintSdkConfiguration.copy(vero2 = vero2Configuration.copy(imageSavingStrategy = it)) + }, + nec = nec?.let { + fingerprintSdkConfiguration.copy(vero2 = vero2Configuration.copy(imageSavingStrategy = it)) + }, + ), + ).isSampleUploadEnabledInProject(), + ).isEqualTo(result) + } + } + @Test fun `allowedAgeRanges returns all non-null age ranges`() { val faceAgeRange = AgeGroup(10, 20) diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt index af4bdeada6..7817c2930f 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt @@ -261,20 +261,22 @@ internal val apiFingerprintConfiguration = ApiFingerprintConfiguration( nec = null, ) +internal val fingerprintSdkConfiguration = FingerprintConfiguration.FingerprintSdkConfiguration( + fingersToCapture = listOf(Finger.LEFT_3RD_FINGER), + decisionPolicy = decisionPolicy, + comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, + vero1 = Vero1Configuration(qualityThreshold = 10), + vero2 = vero2Configuration, + allowedAgeRange = allowedAgeRange, + verificationMatchThreshold = 42.0f, + maxCaptureAttempts = MaxCaptureAttempts(noFingerDetected = 17), +) + internal val fingerprintConfiguration = FingerprintConfiguration( allowedScanners = listOf(FingerprintConfiguration.VeroGeneration.VERO_2), allowedSDKs = listOf(FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER), displayHandIcons = true, - secugenSimMatcher = FingerprintConfiguration.FingerprintSdkConfiguration( - fingersToCapture = listOf(Finger.LEFT_3RD_FINGER), - decisionPolicy = decisionPolicy, - comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, - vero1 = Vero1Configuration(qualityThreshold = 10), - vero2 = vero2Configuration, - allowedAgeRange = allowedAgeRange, - verificationMatchThreshold = 42.0f, - maxCaptureAttempts = MaxCaptureAttempts(noFingerDetected = 17), - ), + secugenSimMatcher = fingerprintSdkConfiguration, nec = null, )