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 8f8606c54c..0affc90d14 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 @@ -82,20 +82,23 @@ internal class ConsentViewModel @Inject constructor( selectedTabIndex: Int, ): ConsentViewState { val allowParentalConsent = projectConfig.consent.allowParentalConsent + val isMultiFactorIdEnabled = projectConfig.multifactorId?.allowedExternalCredentials?.isNotEmpty() ?: false return ConsentViewState( showLogo = projectConfig.consent.displaySimprintsLogo, showParentalConsent = allowParentalConsent, consentTextBuilder = GeneralConsentTextHelper( - projectConfig.consent, - projectConfig.general.modalities, - consentType, + config = projectConfig.consent, + modalities = projectConfig.general.modalities, + consentType = consentType, + isMultiFactorIdEnabled = isMultiFactorIdEnabled, ), parentalTextBuilder = if (allowParentalConsent) { ParentalConsentTextHelper( - projectConfig.consent, - projectConfig.general.modalities, - consentType, + config = projectConfig.consent, + modalities = projectConfig.general.modalities, + consentType = consentType, + isMultiFactorIdEnabled = isMultiFactorIdEnabled, ) } else { null 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 6fcb535638..c8e234d80e 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 @@ -10,17 +10,21 @@ internal data class GeneralConsentTextHelper( private val config: ConsentConfiguration, private val modalities: List, private val consentType: ConsentType, + private val isMultiFactorIdEnabled: Boolean, ) { // TODO All the `getString(id).format(arg,arg)` calls should be `getString(id,arg,arg)` one strings are fixed - // First argument in consent text should always be program name, second is modality specific access/use case text fun assembleText(context: Context) = StringBuilder() .apply { val modalityUseCase = getModalitySpecificUseCaseText(context, modalities) - val modalityAccess = getModalitySpecificAccessText(context, modalities) + val modalityAccess = + getModalitySpecificAccessText(context, modalities) + getMultiFactorIdAccessText(context, isMultiFactorIdEnabled) - filterAppRequestForConsent(context, consentType, config, modalityUseCase) - filterForDataSharingOptions(context, config, modalityUseCase, modalityAccess) + val requestModalityUseCase = modalityUseCase + getMultiFactorIdUseCaseText(context, isMultiFactorIdEnabled) + val dataSharingModalityUseCase = modalityUseCase + getMultiFactorIdSharingText(context, isMultiFactorIdEnabled) + + filterAppRequestForConsent(context, consentType, config, requestModalityUseCase) + filterForDataSharingOptions(context, config, dataSharingModalityUseCase, modalityAccess) }.toString() private fun StringBuilder.filterAppRequestForConsent( @@ -134,4 +138,42 @@ internal data class GeneralConsentTextHelper( Modality.FACE -> context.getString(R.string.consent_biometrics_access_face) Modality.FINGERPRINT -> context.getString(R.string.consent_biometrics_access_fingerprint) } + + private fun getMultiFactorIdUseCaseText( + context: Context, + isMultiFactorIdEnabled: Boolean, + ): String = if (isMultiFactorIdEnabled) { + listOf( + ",", + context.getString(R.string.consent_biometric_concat_modalities), + context.getString(R.string.consent_credentials_general), + ).joinToString(separator = " ") + } else { + "" + } + + private fun getMultiFactorIdAccessText( + context: Context, + isMultiFactorIdEnabled: Boolean, + ): String = if (isMultiFactorIdEnabled) { + listOf( + ",", + context.getString(R.string.consent_credentials_access), + ).joinToString(separator = " ") + } else { + "" + } + + private fun getMultiFactorIdSharingText( + context: Context, + isMultiFactorIdEnabled: Boolean, + ): String = if (isMultiFactorIdEnabled) { + listOf( + ",", + context.getString(R.string.consent_biometric_concat_modalities), + context.getString(R.string.consent_credentials_your_id), + ).joinToString(separator = " ") + } else { + "" + } } 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 bf73ec7eb4..66ae44a2f7 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 @@ -10,17 +10,20 @@ internal data class ParentalConsentTextHelper( private val config: ConsentConfiguration, private val modalities: List, private val consentType: ConsentType, + private val isMultiFactorIdEnabled: Boolean, ) { // TODO All the `getString(id).format(arg,arg)` calls should be `getString(id,arg,arg)` one strings are fixed - // First argument in consent text should always be program name, second is modality specific access/use case text fun assembleText(context: Context): String = StringBuilder() .apply { val modalityUseCase = getModalitySpecificUseCaseText(context, modalities) - val modalityAccess = getModalitySpecificAccessText(context, modalities) + val modalityAccess = + getModalitySpecificAccessText(context, modalities) + getMultiFactorIdAccessText(context, isMultiFactorIdEnabled) - filterAppRequestForParentalConsent(context, consentType, config, modalityUseCase) - extractDataSharingOptions(context, config, modalityUseCase, modalityAccess) + val requestModalityUseCase = modalityUseCase + getMultiFactorIdUseCaseText(context, isMultiFactorIdEnabled) + val dataSharingModalityUseCase = modalityUseCase + getMultiFactorIdSharingText(context, isMultiFactorIdEnabled) + filterAppRequestForParentalConsent(context, consentType, config, requestModalityUseCase) + extractDataSharingOptions(context, config, dataSharingModalityUseCase, modalityAccess) }.toString() private fun StringBuilder.filterAppRequestForParentalConsent( @@ -136,4 +139,42 @@ internal data class ParentalConsentTextHelper( Modality.FINGERPRINT -> context.getString(R.string.consent_biometrics_access_fingerprint) else -> "" } + + private fun getMultiFactorIdAccessText( + context: Context, + isMultiFactorIdEnabled: Boolean, + ): String = if (isMultiFactorIdEnabled) { + listOf( + ",", + context.getString(R.string.consent_credentials_parental_access), + ).joinToString(separator = " ") + } else { + "" + } + + private fun getMultiFactorIdUseCaseText( + context: Context, + isMultiFactorIdEnabled: Boolean, + ): String = if (isMultiFactorIdEnabled) { + listOf( + ",", + context.getString(R.string.consent_biometric_concat_modalities), + context.getString(R.string.consent_credentials_parental_general), + ).joinToString(separator = " ") + } else { + "" + } + + private fun getMultiFactorIdSharingText( + context: Context, + isMultiFactorIdEnabled: Boolean, + ): String = if (isMultiFactorIdEnabled) { + listOf( + ",", + context.getString(R.string.consent_biometric_concat_modalities), + context.getString(R.string.consent_credentials_parental_your_id), + ).joinToString(separator = " ") + } else { + "" + } } diff --git a/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/helpers/GeneralConsentTextHelperTest.kt b/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/helpers/GeneralConsentTextHelperTest.kt index 985d9c4087..cda8dba7b1 100644 --- a/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/helpers/GeneralConsentTextHelperTest.kt +++ b/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/helpers/GeneralConsentTextHelperTest.kt @@ -40,6 +40,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.ENROL, + false, ).assembleText(context) val expectedString = context @@ -63,6 +64,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT, GeneralConfiguration.Modality.FACE), ConsentType.ENROL, + false, ).assembleText(context) val expectedString = context @@ -86,6 +88,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FACE), ConsentType.ENROL, + false, ).assembleText(context) val expectedString = context @@ -109,6 +112,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT, GeneralConfiguration.Modality.FACE), ConsentType.ENROL, + false, ).assembleText(context) val expectedString = context @@ -132,6 +136,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.VERIFY, + false, ).assembleText(context) val expectedString = context @@ -155,6 +160,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -178,6 +184,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT, GeneralConfiguration.Modality.FACE), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -201,6 +208,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -225,6 +233,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FACE), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -249,6 +258,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT, GeneralConfiguration.Modality.FACE), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -273,6 +283,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -296,6 +307,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -318,6 +330,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -340,6 +353,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -362,6 +376,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -384,6 +399,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -407,6 +423,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -429,6 +446,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) assertThat(generalConsentText).doesNotContainMatch("\\.\\w") @@ -448,6 +466,7 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) assertThat(generalConsentText).doesNotContainMatch("^\\s.*") @@ -467,11 +486,84 @@ class GeneralConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) assertThat(generalConsentText).doesNotContain(" ") } + @Test + fun `should add multi-factor ID use case text when isMultiFactorIdEnabled is true`() { + val generalConsentText = GeneralConsentTextHelper( + configWithPrompt( + ConsentConfiguration.ConsentPromptConfiguration( + enrolmentVariant = ConsentConfiguration.ConsentEnrolmentVariant.STANDARD, + dataSharedWithPartner = false, + dataUsedForRAndD = false, + privacyRights = false, + confirmation = false, + ), + ), + listOf(GeneralConfiguration.Modality.FINGERPRINT), + ConsentType.ENROL, + isMultiFactorIdEnabled = true, + ).assembleText(context) + + val expectedText = String.format( + ", %s %s", + context.getString(R.string.consent_biometric_concat_modalities), + context.getString(R.string.consent_credentials_general), + ) + + assertThat(generalConsentText).contains(expectedText) + } + + @Test + fun `should add multi-factor ID sharing text when isMultiFactorIdEnabled is true`() { + val generalConsentText = GeneralConsentTextHelper( + configWithPrompt( + ConsentConfiguration.ConsentPromptConfiguration( + enrolmentVariant = ConsentConfiguration.ConsentEnrolmentVariant.STANDARD, + dataSharedWithPartner = true, + dataUsedForRAndD = false, + privacyRights = false, + confirmation = false, + ), + ), + listOf(GeneralConfiguration.Modality.FINGERPRINT), + ConsentType.IDENTIFY, + isMultiFactorIdEnabled = true, + ).assembleText(context) + + // Just check for the key identifying phrase from the credentials text + assertThat(generalConsentText).contains("national ID document or other external credentials") + } + + @Test + fun `should add multi-factor ID access text when isMultiFactorIdEnabled is true`() { + val generalConsentText = GeneralConsentTextHelper( + configWithPrompt( + ConsentConfiguration.ConsentPromptConfiguration( + enrolmentVariant = ConsentConfiguration.ConsentEnrolmentVariant.STANDARD, + dataSharedWithPartner = false, + dataUsedForRAndD = false, + privacyRights = false, + confirmation = false, + ), + ), + listOf(GeneralConfiguration.Modality.FINGERPRINT), + ConsentType.IDENTIFY, + isMultiFactorIdEnabled = true, + ).assembleText(context) + + val expectedText = String.format( + ", %s", + context.getString(R.string.consent_credentials_access), + ) + + assertThat(generalConsentText).contains(expectedText) + } + private fun configWithPrompt(prompt: ConsentConfiguration.ConsentPromptConfiguration) = ConsentConfiguration( programName = PROGRAM_NAME, organizationName = ORGANIZATION_NAME, diff --git a/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/helpers/ParentalConsentTextHelperTest.kt b/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/helpers/ParentalConsentTextHelperTest.kt index 7fe9694c79..542eae823a 100644 --- a/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/helpers/ParentalConsentTextHelperTest.kt +++ b/feature/consent/src/test/java/com/simprints/feature/consent/screens/consent/helpers/ParentalConsentTextHelperTest.kt @@ -40,6 +40,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.ENROL, + false, ).assembleText(context) val expectedString = context @@ -63,6 +64,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT, GeneralConfiguration.Modality.FACE), ConsentType.ENROL, + false, ).assembleText(context) val expectedString = context @@ -86,6 +88,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FACE), ConsentType.ENROL, + false, ).assembleText(context) val expectedString = context @@ -109,6 +112,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT, GeneralConfiguration.Modality.FACE), ConsentType.ENROL, + false, ).assembleText(context) val expectedString = context @@ -132,6 +136,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.VERIFY, + false, ).assembleText(context) val expectedString = context @@ -155,6 +160,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -178,6 +184,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT, GeneralConfiguration.Modality.FACE), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -201,6 +208,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -225,6 +233,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FACE), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -249,6 +258,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT, GeneralConfiguration.Modality.FACE), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -273,6 +283,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -296,6 +307,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -318,6 +330,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -340,6 +353,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -362,6 +376,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -384,6 +399,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -407,6 +423,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) val expectedString = context @@ -429,6 +446,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) assertThat(parentalConsentText).doesNotContainMatch("\\.\\w") @@ -448,6 +466,7 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) assertThat(parentalConsentText).doesNotContainMatch("^\\s.*") @@ -467,11 +486,72 @@ class ParentalConsentTextHelperTest { ), listOf(GeneralConfiguration.Modality.FINGERPRINT), ConsentType.IDENTIFY, + false, ).assembleText(context) assertThat(parentalConsentText).doesNotContain(" ") } + @Test + fun `should add multi-factor ID use case text when isMultiFactorIdEnabled is true`() { + val parentalConsentText = ParentalConsentTextHelper( + configWithPrompt( + ConsentConfiguration.ConsentPromptConfiguration( + enrolmentVariant = ConsentConfiguration.ConsentEnrolmentVariant.STANDARD, + dataSharedWithPartner = false, + dataUsedForRAndD = false, + privacyRights = false, + confirmation = false, + ), + ), + listOf(GeneralConfiguration.Modality.FINGERPRINT), + ConsentType.ENROL, + isMultiFactorIdEnabled = true, + ).assembleText(context) + + assertThat(parentalConsentText).contains(context.getString(R.string.consent_credentials_parental_general)) + } + + @Test + fun `should add multi-factor ID access text when isMultiFactorIdEnabled is true`() { + val parentalConsentText = ParentalConsentTextHelper( + configWithPrompt( + ConsentConfiguration.ConsentPromptConfiguration( + enrolmentVariant = ConsentConfiguration.ConsentEnrolmentVariant.STANDARD, + dataSharedWithPartner = false, + dataUsedForRAndD = false, + privacyRights = false, + confirmation = false, + ), + ), + listOf(GeneralConfiguration.Modality.FINGERPRINT), + ConsentType.IDENTIFY, + isMultiFactorIdEnabled = true, + ).assembleText(context) + + assertThat(parentalConsentText).contains(context.getString(R.string.consent_credentials_parental_access)) + } + + @Test + fun `should add multi-factor ID sharing text when isMultiFactorIdEnabled is true`() { + val parentalConsentText = ParentalConsentTextHelper( + configWithPrompt( + ConsentConfiguration.ConsentPromptConfiguration( + enrolmentVariant = ConsentConfiguration.ConsentEnrolmentVariant.STANDARD, + dataSharedWithPartner = true, + dataUsedForRAndD = false, + privacyRights = false, + confirmation = false, + ), + ), + listOf(GeneralConfiguration.Modality.FINGERPRINT), + ConsentType.IDENTIFY, + isMultiFactorIdEnabled = true, + ).assembleText(context) + + assertThat(parentalConsentText).contains("child's national ID document or other external credentials") + } + private fun configWithPrompt(prompt: ConsentConfiguration.ConsentPromptConfiguration) = ConsentConfiguration( programName = PROGRAM_NAME, organizationName = ORGANIZATION_NAME, diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ExternalCredentialSearchResult.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ExternalCredentialSearchResult.kt index 8942e3b815..d2584c3ed4 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ExternalCredentialSearchResult.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/ExternalCredentialSearchResult.kt @@ -19,7 +19,7 @@ import com.simprints.feature.externalcredential.screens.search.model.ScannedCred @ExcludedFromGeneratedTestCoverageReports("Data class") data class ExternalCredentialSearchResult( val flowType: FlowType, - val scannedCredential: ScannedCredential, + val scannedCredential: ScannedCredential?, val matchResults: List, ) : StepResult { val goodMatches = matchResults.filter(CredentialMatch::isVerificationSuccessful) diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/controller/ExternalCredentialViewModel.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/controller/ExternalCredentialViewModel.kt index 751dd4b5db..d7f60e8678 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/controller/ExternalCredentialViewModel.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/controller/ExternalCredentialViewModel.kt @@ -3,17 +3,22 @@ package com.simprints.feature.externalcredential.screens.controller import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.simprints.core.domain.externalcredential.ExternalCredentialType import com.simprints.core.livedata.LiveDataEventWithContent import com.simprints.core.livedata.send import com.simprints.feature.externalcredential.ExternalCredentialSearchResult import com.simprints.feature.externalcredential.model.ExternalCredentialParams +import com.simprints.infra.config.sync.ConfigManager import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject -import com.simprints.infra.resources.R as IDR +import kotlin.collections.orEmpty @HiltViewModel -internal class ExternalCredentialViewModel @Inject internal constructor() : ViewModel() { +internal class ExternalCredentialViewModel @Inject internal constructor( + private val configManager: ConfigManager, +) : ViewModel() { private var isInitialized = false lateinit var params: ExternalCredentialParams private set @@ -28,6 +33,18 @@ internal class ExternalCredentialViewModel @Inject internal constructor() : View private val _stateLiveData = MutableLiveData(ExternalCredentialState.EMPTY) val stateLiveData: LiveData = _stateLiveData + val externalCredentialTypes: LiveData> + get() = _externalCredentialTypes + private val _externalCredentialTypes = MutableLiveData>() + + init { + viewModelScope.launch { + val config = configManager.getProjectConfiguration() + val allowedExternalCredentials = config.multifactorId?.allowedExternalCredentials.orEmpty() + _externalCredentialTypes.postValue(allowedExternalCredentials) + } + } + private fun updateState(state: (ExternalCredentialState) -> ExternalCredentialState) { this.state = state(this.state) } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectFragment.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectFragment.kt index 54dce8c37a..6ab288a498 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectFragment.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectFragment.kt @@ -7,7 +7,6 @@ import android.widget.Button import android.widget.TextView import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.bottomsheet.BottomSheetBehavior @@ -32,7 +31,6 @@ import com.simprints.infra.resources.R as IDR @AndroidEntryPoint internal class ExternalCredentialSelectFragment : Fragment(R.layout.fragment_external_credential_select) { private val mainViewModel: ExternalCredentialViewModel by activityViewModels() - private val viewModel by viewModels() private val binding by viewBinding(FragmentExternalCredentialSelectBinding::bind) private var dialog: Dialog? = null @@ -46,7 +44,6 @@ internal class ExternalCredentialSelectFragment : Fragment(R.layout.fragment_ext Simber.i("ExternalCredentialSelectFragment started", tag = ORCHESTRATION) observeChanges() - viewModel.loadExternalCredentials() } override fun onDestroy() { @@ -85,7 +82,7 @@ internal class ExternalCredentialSelectFragment : Fragment(R.layout.fragment_ext } private fun observeChanges() { - viewModel.externalCredentialTypes.observe(viewLifecycleOwner) { externalCredentialTypes -> + mainViewModel.externalCredentialTypes.observe(viewLifecycleOwner) { externalCredentialTypes -> updateSelectedCredentialType(null) fillRecyclerView(externalCredentialTypes) initViews(externalCredentialTypes) diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectViewModel.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectViewModel.kt deleted file mode 100644 index e55d9da033..0000000000 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/select/ExternalCredentialSelectViewModel.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.simprints.feature.externalcredential.screens.select - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.simprints.core.domain.externalcredential.ExternalCredentialType -import com.simprints.infra.config.sync.ConfigManager -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -internal class ExternalCredentialSelectViewModel @Inject internal constructor( - private val configManager: ConfigManager, -) : ViewModel() { - val externalCredentialTypes: LiveData> - get() = _externalCredentialTypes - private val _externalCredentialTypes = MutableLiveData>() - - fun loadExternalCredentials() { - viewModelScope.launch { - val config = configManager.getProjectConfiguration() - val allowedExternalCredentials = config.multifactorId?.allowedExternalCredentials.orEmpty() - _externalCredentialTypes.postValue(allowedExternalCredentials) - } - } -} diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/skip/ExternalCredentialSkipFragment.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/skip/ExternalCredentialSkipFragment.kt index d6f73c5f31..edec286386 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/skip/ExternalCredentialSkipFragment.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/skip/ExternalCredentialSkipFragment.kt @@ -1,8 +1,96 @@ package com.simprints.feature.externalcredential.screens.skip +import android.os.Bundle +import android.view.View +import androidx.core.view.isVisible +import androidx.core.widget.addTextChangedListener import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.findNavController +import com.simprints.core.domain.externalcredential.ExternalCredentialType +import com.simprints.feature.externalcredential.ExternalCredentialSearchResult import com.simprints.feature.externalcredential.R +import com.simprints.feature.externalcredential.databinding.FragmentExternalCredentialSkipBinding +import com.simprints.feature.externalcredential.ext.getCredentialTypeString +import com.simprints.feature.externalcredential.screens.controller.ExternalCredentialViewModel +import com.simprints.infra.uibase.viewbinding.viewBinding import dagger.hilt.android.AndroidEntryPoint +import com.simprints.infra.resources.R as IDR @AndroidEntryPoint -class ExternalCredentialSkipFragment : Fragment(R.layout.fragment_external_credential_skip) +class ExternalCredentialSkipFragment : Fragment(R.layout.fragment_external_credential_skip) { + private val binding by viewBinding(FragmentExternalCredentialSkipBinding::bind) + private val mainViewModel: ExternalCredentialViewModel by activityViewModels() + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + super.onViewCreated(view, savedInstanceState) + + initObservers() + } + + private fun initObservers() { + mainViewModel.externalCredentialTypes.observe(viewLifecycleOwner) { credentialTypes -> + initViews(credentialTypes) + initListeners() + } + } + + private fun initViews(credentialTypes: List) = with(binding) { + val dynamicTextReasonItemMap = + mapOf( + title to IDR.string.mfid_skip_title, + skipReasonDoesNotHaveDocument to IDR.string.mfid_skip_reason_does_not_have, + skipReasonDidNotBring to IDR.string.mfid_skip_reason_did_not_bring, + skipReasonIncorrect to IDR.string.mfid_skip_reason_incorrect, + skipReasonDoesNotWantToProvide to IDR.string.mfid_skip_reason_does_not_want_to_provide, + skipReasonDamaged to IDR.string.mfid_skip_reason_damaged, + skipReasonUnableToScan to IDR.string.mfid_skip_reason_unable_to_scan, + ) + dynamicTextReasonItemMap.forEach { entry -> + val textView = entry.key + val stringRes = entry.value + val credentialText = when (credentialTypes.size) { + 1 -> resources.getCredentialTypeString(credentialTypes.first()) + else -> getString(IDR.string.mfid_type_any_document) + } + textView.text = getString(stringRes, credentialText) + } + } + + private fun initListeners() = with(binding) { + skipCredentialScanRadioGroup.setOnCheckedChangeListener { _, checkedId -> + reasonTextInputLayout.isVisible = checkedId == R.id.skipReasonOther + + val isSkipButtonEnabled = when (checkedId) { + R.id.skipReasonOther -> { + reasonTextInput.text.toString().isNotEmpty() + } + + else -> true + } + buttonSkip.isEnabled = isSkipButtonEnabled + } + reasonTextInput.addTextChangedListener( + afterTextChanged = { + buttonSkip.isEnabled = it.toString().isNotEmpty() + }, + ) + buttonGoBack.setOnClickListener { + findNavController().popBackStack() + } + + // [MS-1166] We should log skip reasons in analytics. + buttonSkip.setOnClickListener { + mainViewModel.finish( + ExternalCredentialSearchResult( + flowType = mainViewModel.params.flowType, + scannedCredential = null, + matchResults = emptyList(), + ), + ) + } + } +} diff --git a/feature/external-credential/src/main/res/layout/fragment_external_credential_skip.xml b/feature/external-credential/src/main/res/layout/fragment_external_credential_skip.xml index 1860c12b36..25d8233497 100644 --- a/feature/external-credential/src/main/res/layout/fragment_external_credential_skip.xml +++ b/feature/external-credential/src/main/res/layout/fragment_external_credential_skip.xml @@ -1,12 +1,156 @@ - + android:layout_height="match_parent" + android:background="@color/simprints_blue"> + + + android:padding="@dimen/padding_default" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="Why did you skip the document scan?" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +