diff --git a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentFragment.kt b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentFragment.kt index 0ed60f93c2..297d4871d6 100644 --- a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentFragment.kt +++ b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentFragment.kt @@ -13,6 +13,7 @@ import androidx.navigation.fragment.navArgs import com.google.android.material.tabs.TabLayout import com.simprints.feature.consent.R import com.simprints.feature.consent.databinding.FragmentConsentBinding +import com.simprints.feature.consent.screens.consent.helpers.ConsentTextHelperFactory import com.simprints.feature.exitform.ExitFormContract import com.simprints.feature.exitform.ExitFormResult import com.simprints.feature.exitform.toArgs @@ -22,6 +23,7 @@ import com.simprints.infra.uibase.navigation.handleResult import com.simprints.infra.uibase.navigation.navigateSafely import com.simprints.infra.uibase.viewbinding.viewBinding import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import com.simprints.infra.resources.R as IDR @AndroidEntryPoint @@ -31,8 +33,12 @@ internal class ConsentFragment : Fragment(R.layout.fragment_consent) { private val binding by viewBinding(FragmentConsentBinding::bind) private val viewModel by viewModels() + @Inject + internal lateinit var textHelperFactory: ConsentTextHelperFactory + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + viewModel.init(textHelperFactory) binding.consentPrivacyNotice.paintFlags = binding.consentPrivacyNotice.paintFlags or Paint.UNDERLINE_TEXT_FLAG @@ -113,7 +119,7 @@ internal class ConsentFragment : Fragment(R.layout.fragment_consent) { private fun TabLayout.addParentalConsentTab( generalConsentText: String, - parentalConsentText: String + parentalConsentText: String, ) { addTab(newTab().setText(IDR.string.consent_parental_title), PARENTAL_CONSENT_TAB) addOnTabSelectedListener(OnTabSelectedListener { tab -> @@ -134,6 +140,7 @@ internal class ConsentFragment : Fragment(R.layout.fragment_consent) { } companion object { + private const val GENERAL_CONSENT_TAB = 0 private const val PARENTAL_CONSENT_TAB = 1 } diff --git a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentViewModel.kt b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentViewModel.kt index b144499038..8068854ab3 100644 --- a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentViewModel.kt +++ b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentViewModel.kt @@ -10,6 +10,7 @@ import com.simprints.core.livedata.send import com.simprints.core.tools.time.TimeHelper import com.simprints.feature.consent.ConsentResult import com.simprints.feature.consent.ConsentType +import com.simprints.feature.consent.screens.consent.helpers.ConsentTextHelperFactory import com.simprints.feature.consent.screens.consent.helpers.GeneralConsentTextHelper import com.simprints.feature.consent.screens.consent.helpers.ParentalConsentTextHelper import com.simprints.feature.exitform.ExitFormConfigurationBuilder @@ -33,13 +34,14 @@ internal class ConsentViewModel @Inject constructor( private val timeHelper: TimeHelper, private val configManager: ConfigManager, private val eventRepository: SessionEventRepository, - private val generalConsentTextHelper: GeneralConsentTextHelper, - private val parentalConsentTextHelper: ParentalConsentTextHelper, @ExternalScope private val externalScope: CoroutineScope, ) : ViewModel() { private val startConsentEventTime = timeHelper.now() + private lateinit var generalConsentTextHelper: GeneralConsentTextHelper + private lateinit var parentalConsentTextHelper: ParentalConsentTextHelper + val viewState: LiveData get() = _viewState private val _viewState = MutableLiveData(ConsentViewState()) @@ -53,6 +55,11 @@ internal class ConsentViewModel @Inject constructor( get() = _returnConsentResult private val _returnConsentResult = MutableLiveData>() + fun init(textHelperFactory: ConsentTextHelperFactory) { + generalConsentTextHelper = textHelperFactory.createGeneral() + parentalConsentTextHelper = textHelperFactory.createParental() + } + fun loadConfiguration(consentType: ConsentType) { viewModelScope.launch { val projectConfig = configManager.getProjectConfiguration() @@ -89,7 +96,7 @@ internal class ConsentViewModel @Inject constructor( private fun mapConfigToViewState( projectConfig: ProjectConfiguration, consentType: ConsentType, - selectedTabIndex: Int + selectedTabIndex: Int, ): ConsentViewState { val allowParentalConsent = projectConfig.consent.allowParentalConsent @@ -108,7 +115,7 @@ internal class ConsentViewModel @Inject constructor( private fun saveConsentEvent( currentConsentTab: ConsentTab, - result: ConsentEvent.ConsentPayload.Result + result: ConsentEvent.ConsentPayload.Result, ) = externalScope.launch { eventRepository.addOrUpdateEvent(ConsentEvent( startConsentEventTime, diff --git a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/ConsentTextHelperFactory.kt b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/ConsentTextHelperFactory.kt new file mode 100644 index 0000000000..99e53f72ad --- /dev/null +++ b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/ConsentTextHelperFactory.kt @@ -0,0 +1,17 @@ +package com.simprints.feature.consent.screens.consent.helpers + +import android.content.Context +import dagger.hilt.android.qualifiers.ActivityContext +import javax.inject.Inject + +/** + * Factory must be injected into the hilt entry point to receive the correct + * activity context which in turn will be passed to the text helpers. + */ +internal class ConsentTextHelperFactory @Inject constructor( + @ActivityContext private val context: Context, +) { + + fun createGeneral() = GeneralConsentTextHelper(context) + fun createParental() = ParentalConsentTextHelper(context) +} diff --git a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/GeneralConsentTextHelper.kt b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/GeneralConsentTextHelper.kt index 68b0d0818a..fafb9499c4 100644 --- a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/GeneralConsentTextHelper.kt +++ b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/GeneralConsentTextHelper.kt @@ -5,11 +5,9 @@ import com.simprints.feature.consent.ConsentType import com.simprints.infra.config.store.models.ConsentConfiguration import com.simprints.infra.config.store.models.GeneralConfiguration import com.simprints.infra.resources.R -import dagger.hilt.android.qualifiers.ApplicationContext -import javax.inject.Inject -internal class GeneralConsentTextHelper @Inject constructor( - @ApplicationContext val context: Context, +internal class GeneralConsentTextHelper( + private val context: Context, ) { // TODO All the `getString(id).format(arg,arg)` calls should be `getString(id,arg,arg)` one strings are fixed @@ -116,3 +114,4 @@ internal class GeneralConsentTextHelper @Inject constructor( GeneralConfiguration.Modality.FINGERPRINT -> context.getString(R.string.consent_biometrics_access_fingerprint) } } + diff --git a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/ParentalConsentTextHelper.kt b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/ParentalConsentTextHelper.kt index 25fa4ce0b4..ed28e646e2 100644 --- a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/ParentalConsentTextHelper.kt +++ b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/helpers/ParentalConsentTextHelper.kt @@ -5,11 +5,15 @@ import com.simprints.feature.consent.ConsentType import com.simprints.infra.config.store.models.ConsentConfiguration import com.simprints.infra.config.store.models.GeneralConfiguration.Modality import com.simprints.infra.resources.R +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import dagger.hilt.android.qualifiers.ActivityContext import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject -internal class ParentalConsentTextHelper @Inject constructor( - @ApplicationContext val context: Context +internal class ParentalConsentTextHelper( + private val context: Context ) { // TODO All the `getString(id).format(arg,arg)` calls should be `getString(id,arg,arg)` one strings are fixed diff --git a/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/ConsentViewModelTest.kt b/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/ConsentViewModelTest.kt index cdb8e659b1..fd757b1b6e 100644 --- a/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/ConsentViewModelTest.kt +++ b/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/ConsentViewModelTest.kt @@ -6,6 +6,7 @@ import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.time.Timestamp import com.simprints.feature.consent.ConsentResult import com.simprints.feature.consent.ConsentType +import com.simprints.feature.consent.screens.consent.helpers.ConsentTextHelperFactory import com.simprints.feature.consent.screens.consent.helpers.GeneralConsentTextHelper import com.simprints.feature.consent.screens.consent.helpers.ParentalConsentTextHelper import com.simprints.feature.exitform.ExitFormResult @@ -43,6 +44,9 @@ class ConsentViewModelTest { @MockK private lateinit var timeHelper: TimeHelper + @MockK + private lateinit var consentTextHelperFactory: ConsentTextHelperFactory + @MockK private lateinit var generalConsentTextHelper: GeneralConsentTextHelper @@ -69,6 +73,9 @@ class ConsentViewModelTest { coEvery { configManager.getProjectConfiguration() } returns projectConfig every { projectConfig.consent } returns mockk() + every { consentTextHelperFactory.createGeneral() } returns generalConsentTextHelper + every { consentTextHelperFactory.createParental() } returns parentalConsentTextHelper + every { timeHelper.now() } returns TIMESTAMP every { generalConsentTextHelper.assembleText(any(), any(), any()) } returns GENERAL_CONSENT every { parentalConsentTextHelper.assembleText(any(), any(), any()) } returns PARENTAL_CONSENT @@ -77,10 +84,9 @@ class ConsentViewModelTest { timeHelper, configManager, eventRepository, - generalConsentTextHelper, - parentalConsentTextHelper, CoroutineScope(testCoroutineRule.testCoroutineDispatcher) ) + vm.init(consentTextHelperFactory) } @Test @@ -210,6 +216,7 @@ class ConsentViewModelTest { } companion object { + private val TIMESTAMP = Timestamp(1L) private const val GENERAL_CONSENT = "General consent" private const val PARENTAL_CONSENT = "Parental consent" diff --git a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/SettingsViewModel.kt b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/SettingsViewModel.kt index 4f4774977f..80bb3425eb 100644 --- a/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/SettingsViewModel.kt +++ b/feature/dashboard/src/main/java/com/simprints/feature/dashboard/settings/SettingsViewModel.kt @@ -36,7 +36,7 @@ internal class SettingsViewModel @Inject constructor( fun updateLanguagePreference(language: String) { viewModelScope.launch { - configManager.updateDeviceConfiguration { it.apply { it.language = language } } + configManager.updateDeviceConfiguration { config -> config.also { it.language = language } } _languagePreference.postValue(language) Simber.tag(LoggingConstants.CrashReportTag.SETTINGS.name).i("Language set to $language") } diff --git a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/AgeGroupDisplayModel.kt b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/AgeGroupDisplayModel.kt index a13aab523b..114a56b47a 100644 --- a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/AgeGroupDisplayModel.kt +++ b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/AgeGroupDisplayModel.kt @@ -2,4 +2,7 @@ package com.simprints.feature.selectagegroup.screen import com.simprints.infra.config.store.models.AgeGroup -internal data class AgeGroupDisplayModel(val displayString: String, val range: AgeGroup) +internal data class AgeGroupDisplayModel( + val displayString: String, + val range: AgeGroup, +) diff --git a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/BuildAgeGroupsDescriptionUseCase.kt b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/BuildAgeGroupsDescriptionUseCase.kt index 3dc7218102..a82528819a 100644 --- a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/BuildAgeGroupsDescriptionUseCase.kt +++ b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/BuildAgeGroupsDescriptionUseCase.kt @@ -4,13 +4,12 @@ import android.content.Context import com.simprints.infra.config.store.ConfigRepository import com.simprints.infra.config.store.models.AgeGroup import com.simprints.infra.config.store.models.sortedUniqueAgeGroups -import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import com.simprints.infra.resources.R as IDR -internal class BuildAgeGroupsDescriptionUseCase @Inject constructor( +internal class BuildAgeGroupsDescriptionUseCase( private val configurationRepo: ConfigRepository, - @ApplicationContext private val context: Context, + private val context: Context, ) { /** diff --git a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/BuildAgeGroupsDescriptionUseCaseFactory.kt b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/BuildAgeGroupsDescriptionUseCaseFactory.kt new file mode 100644 index 0000000000..5f2c011718 --- /dev/null +++ b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/BuildAgeGroupsDescriptionUseCaseFactory.kt @@ -0,0 +1,13 @@ +package com.simprints.feature.selectagegroup.screen + +import android.content.Context +import com.simprints.infra.config.store.ConfigRepository +import dagger.hilt.android.qualifiers.ActivityContext +import javax.inject.Inject + +internal class BuildAgeGroupsDescriptionUseCaseFactory @Inject constructor( + private val configurationRepo: ConfigRepository, + @ActivityContext private val context: Context, +) { + fun create() = BuildAgeGroupsDescriptionUseCase(configurationRepo, context) +} diff --git a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupFragment.kt b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupFragment.kt index 0e57f7cd80..266ba29511 100644 --- a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupFragment.kt +++ b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupFragment.kt @@ -20,14 +20,20 @@ import com.simprints.infra.uibase.navigation.handleResult import com.simprints.infra.uibase.navigation.navigateSafely import com.simprints.infra.uibase.viewbinding.viewBinding import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject @AndroidEntryPoint internal class SelectSubjectAgeGroupFragment : Fragment(R.layout.fragment_age_group_selection) { private val viewModel: SelectSubjectAgeGroupViewModel by viewModels() private val binding by viewBinding(FragmentAgeGroupSelectionBinding::bind) + + @Inject + internal lateinit var descriptionUseCaseFactory: BuildAgeGroupsDescriptionUseCaseFactory + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + viewModel.init(descriptionUseCaseFactory) viewModel.ageGroups.observe(viewLifecycleOwner) { ageGroupsList -> fillRecyclerView(ageGroupsList) diff --git a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupViewModel.kt b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupViewModel.kt index cad850726c..415af7a83a 100644 --- a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupViewModel.kt +++ b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupViewModel.kt @@ -29,11 +29,12 @@ import javax.inject.Inject internal class SelectSubjectAgeGroupViewModel @Inject constructor( private val timeHelper: TimeHelper, private val eventRepository: SessionEventRepository, - private val buildAgeGroups: BuildAgeGroupsDescriptionUseCase, private val configurationRepo: ConfigRepository, @ExternalScope private val externalScope: CoroutineScope, ) : ViewModel() { + private lateinit var buildAgeGroupDescription: BuildAgeGroupsDescriptionUseCase + val finish: LiveData> get() = _finish private var _finish = MutableLiveData>() @@ -47,9 +48,13 @@ internal class SelectSubjectAgeGroupViewModel @Inject constructor( private val _showExitForm = MutableLiveData>() + fun init(factory: BuildAgeGroupsDescriptionUseCaseFactory) { + buildAgeGroupDescription = factory.create() + } + fun start() = viewModelScope.launch { startTime = timeHelper.now() - val ageGroups = buildAgeGroups() + val ageGroups = buildAgeGroupDescription() // notify the adapter _ageGroupsDisplayModel.value = ageGroups } diff --git a/feature/select-subject-age-group/src/test/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupViewModelTest.kt b/feature/select-subject-age-group/src/test/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupViewModelTest.kt index 572df68f1a..ef8e6cf228 100644 --- a/feature/select-subject-age-group/src/test/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupViewModelTest.kt +++ b/feature/select-subject-age-group/src/test/java/com/simprints/feature/selectagegroup/screen/SelectSubjectAgeGroupViewModelTest.kt @@ -12,6 +12,7 @@ import com.simprints.infra.resources.R import com.simprints.testtools.common.coroutines.TestCoroutineRule import io.mockk.MockKAnnotations import io.mockk.coEvery +import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.RelaxedMockK import kotlinx.coroutines.CoroutineScope @@ -33,6 +34,9 @@ class SelectSubjectAgeGroupViewModelTest { @MockK private lateinit var buildAgeGroups: BuildAgeGroupsDescriptionUseCase + @MockK + private lateinit var buildAgeGroupsDescriptionUseCaseFactory: BuildAgeGroupsDescriptionUseCaseFactory + @RelaxedMockK private lateinit var configurationRepo: ConfigRepository @@ -52,13 +56,15 @@ class SelectSubjectAgeGroupViewModelTest { MockKAnnotations.init(this) coEvery { buildAgeGroups() } returns ageGroupViewModels + every { buildAgeGroupsDescriptionUseCaseFactory.create() } returns buildAgeGroups + viewModel = SelectSubjectAgeGroupViewModel( timeHelper, eventRepository, - buildAgeGroups, configurationRepo, CoroutineScope(testCoroutineRule.testCoroutineDispatcher) ) + viewModel.init(buildAgeGroupsDescriptionUseCaseFactory) } @Test @@ -92,6 +98,7 @@ class SelectSubjectAgeGroupViewModelTest { Truth.assertThat(result.titleRes).isEqualTo(R.string.exit_form_title_fingerprinting) Truth.assertThat(result.backButtonRes).isEqualTo(R.string.exit_form_continue_fingerprints_button) } + @Test fun `test onBackPressed face modality`() = runTest { coEvery { configurationRepo.getProjectConfiguration().general.modalities } returns listOf( @@ -102,8 +109,9 @@ class SelectSubjectAgeGroupViewModelTest { // Assert that the titleRes and backButtonRes are equal to the face modality Truth.assertThat(result.titleRes).isEqualTo(R.string.exit_form_title_face) - Truth.assertThat( result.backButtonRes).isEqualTo(R.string.exit_form_continue_face_button) + Truth.assertThat(result.backButtonRes).isEqualTo(R.string.exit_form_continue_face_button) } + @Test fun `test onBackPressed multiple modalities`() = runTest { coEvery { configurationRepo.getProjectConfiguration().general.modalities } returns listOf( diff --git a/infra/core/src/main/java/com/simprints/core/tools/utils/LanguageHelper.kt b/infra/core/src/main/java/com/simprints/core/tools/utils/LanguageHelper.kt index ccd3991a6a..67e935a1e0 100644 --- a/infra/core/src/main/java/com/simprints/core/tools/utils/LanguageHelper.kt +++ b/infra/core/src/main/java/com/simprints/core/tools/utils/LanguageHelper.kt @@ -3,6 +3,7 @@ package com.simprints.core.tools.utils import android.content.Context import android.content.SharedPreferences import android.content.res.Configuration +import androidx.core.content.edit import com.simprints.core.ExcludedFromGeneratedTestCoverageReports import java.util.* @@ -16,11 +17,9 @@ object LanguageHelper { lateinit var prefs: SharedPreferences var language: String - get() { - return prefs.getString(SHARED_PREFS_LANGUAGE_KEY, SHARED_PREFS_LANGUAGE_DEFAULT)!! - } + get() = prefs.getString(SHARED_PREFS_LANGUAGE_KEY, SHARED_PREFS_LANGUAGE_DEFAULT)!! set(value) { - prefs.edit().putString(SHARED_PREFS_LANGUAGE_KEY, value).apply() + prefs.edit(commit = true) { putString(SHARED_PREFS_LANGUAGE_KEY, value) } } fun init(ctx: Context) {