From c1c09f65a02359c4f4d933f595234c532c610811 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 11 Feb 2026 11:59:51 +0200 Subject: [PATCH 1/7] =?UTF-8?q?Cherry=20picking=20latest=20linter=20rules?= =?UTF-8?q?=CB=86=20from=20the=20main=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.editorconfig b/.editorconfig index 9446997321..d740210478 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,9 +19,22 @@ ktlint_standard_annotation = disabled # If enabled, this rules forces any multiline assignment to the new line regardless of length ktlint_standard_multiline-expression-wrapping = disabled +# If enabled, this rules forces braces to all branches if any single one has them +ktlint_standard_when-entry-bracing = disabled + +# If enabled, this rule forces blank line between when branches if there is a single multiline branch +ktlint_standard_blank-line-between-when-conditions = disabled +ij_kotlin_line_break_after_multiline_when_entry = false + # Default allows the expression body to be a single call to a wrapper # function (e.g. `runTest{}`) without a line break ktlint_function_signature_body_expression_wrapping = default # In tests star imports are fine since there could be lots helper functions ij_kotlin_packages_to_use_import_on_demand=androidx.test.**,io.mockk.**,com.google.common.truth.** + +# Backticked function names are only used in tests and those names can be very long +ktlint_ignore_back_ticked_identifier=true + +# Unused import deletion is disabled by default due to a issue with +ktlint_standard_no-unused-imports = enabled From 0cf5f67fe5db0cd0ab20a45f9a8e6ba704e8a13b Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 12 Feb 2026 14:54:49 +0200 Subject: [PATCH 2/7] [MS-1340] Adding validation for the credential fields edit text --- .../ExternalCredentialSearchFragment.kt | 34 ++++++++++++++----- .../ExternalCredentialSearchViewModel.kt | 26 ++++++++++++++ .../search/model/SearchCredentialState.kt | 3 ++ .../src/main/res/color/ic_edit_color.xml | 5 +++ .../src/main/res/drawable/ic_done.xml | 2 +- .../ExternalCredentialSearchViewModelTest.kt | 10 ++++++ 6 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 feature/external-credential/src/main/res/color/ic_edit_color.xml diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt index 9df99a4d9b..443bc2411c 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt @@ -7,6 +7,7 @@ import android.view.View import android.view.inputmethod.InputMethodManager import androidx.core.content.ContextCompat import androidx.core.view.isVisible +import androidx.core.widget.addTextChangedListener import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels @@ -58,7 +59,6 @@ internal class ExternalCredentialSearchFragment : Fragment(R.layout.fragment_ext @Inject lateinit var zoomOntoCredentialUseCase: ZoomOntoCredentialUseCase - private var isEditingCredential: Boolean = false override fun onViewCreated( view: View, @@ -92,7 +92,9 @@ internal class ExternalCredentialSearchFragment : Fragment(R.layout.fragment_ext val credentialType = state.scannedCredential.credentialType val credentialField = resources.getCredentialFieldTitle(credentialType) val currentEditTextValue = credentialEditText.text.toString() + val isEditingCredential = state.isEditingCredential renderImage(state.scannedCredential) + renderCredentialEdit(state) credential.takeIf { currentEditTextValue.isEmpty() }?.let { credentialEditText.setText(it) // Setting only once at the start } @@ -105,15 +107,20 @@ internal class ExternalCredentialSearchFragment : Fragment(R.layout.fragment_ext confirmCredentialCheckbox.isChecked = state.isConfirmed iconEditCredential.setOnClickListener { - viewModel.updateConfirmation(isConfirmed = false) - toggleCredentialEdit() - if (!isEditingCredential) { - viewModel.confirmCredentialUpdate(credentialEditText.text.toString().asTokenizableRaw()) + if (isEditingCredential) { + viewModel.confirmCredentialUpdate(updatedCredential = credentialEditText.text.toString().asTokenizableRaw()) } + viewModel.updateIsEditingCredential(isEditing = !isEditingCredential) } confirmCredentialCheckbox.setOnCheckedChangeListener { _, checkedId -> viewModel.updateConfirmation(isConfirmed = checkedId) } + + credentialEditText.addTextChangedListener( + afterTextChanged = { _ -> + renderEditIcon(isEditingCredential) + }, + ) } private fun renderSearchProgress( @@ -196,7 +203,7 @@ internal class ExternalCredentialSearchFragment : Fragment(R.layout.fragment_ext val isSearching = state.searchState != SearchState.Searching buttonRecapture.isVisible = isSearching buttonConfirm.isVisible = isSearching - buttonConfirm.isEnabled = state.isConfirmed + buttonConfirm.isEnabled = state.isConfirmed && !state.isEditingCredential viewModel.getButtonTextResource(state.searchState, state.flowType)?.run(buttonConfirm::setText) buttonConfirm.setOnClickListener { viewModel.finish(state) @@ -224,8 +231,9 @@ internal class ExternalCredentialSearchFragment : Fragment(R.layout.fragment_ext } } - private fun toggleCredentialEdit() = with(binding) { - isEditingCredential = !isEditingCredential + private fun renderCredentialEdit(state: SearchCredentialState) = with(binding) { + val isEditingCredential = state.isEditingCredential + renderEditIcon(isEditingCredential) val iconRes = if (isEditingCredential) { R.drawable.ic_done } else { @@ -248,4 +256,14 @@ internal class ExternalCredentialSearchFragment : Fragment(R.layout.fragment_ext private fun hideKeyboard() { requireActivity().hideKeyboard() } + + private fun renderEditIcon(isEditingCredential: Boolean) = with(binding) { + val isEditIconEnabled = if (isEditingCredential) { + viewModel.isCredentialFormatValid(credentialEditText.text?.toString()) + } else { + true + } + iconEditCredential.alpha = if (isEditIconEnabled) 1.0f else 0.5f + iconEditCredential.isEnabled = isEditIconEnabled + } } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt index e0c2289fc2..77a51b1a31 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt @@ -13,6 +13,8 @@ import com.simprints.core.livedata.send import com.simprints.core.tools.time.TimeHelper import com.simprints.feature.externalcredential.ExternalCredentialSearchResult import com.simprints.feature.externalcredential.model.ExternalCredentialParams +import com.simprints.feature.externalcredential.screens.scanocr.usecase.GhanaIdCardOcrSelectorUseCase +import com.simprints.feature.externalcredential.screens.scanocr.usecase.GhanaNhisCardOcrSelectorUseCase import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential import com.simprints.feature.externalcredential.screens.search.model.SearchCredentialState import com.simprints.feature.externalcredential.screens.search.model.SearchState @@ -41,6 +43,8 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( private val tokenizationProcessor: TokenizationProcessor, private val enrolmentRecordRepository: EnrolmentRecordRepository, private val eventsTracker: ExternalCredentialEventTrackerUseCase, + private val ghanaIdCardOcrSelectorUseCase: GhanaIdCardOcrSelectorUseCase, + private val ghanaNhisCardOcrSelectorUseCase: GhanaNhisCardOcrSelectorUseCase, ) : ViewModel() { @AssistedFactory interface Factory { @@ -79,6 +83,10 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( updateState { it.copy(isConfirmed = isConfirmed) } } + fun updateIsEditingCredential(isEditing: Boolean) { + updateState { it.copy(isEditingCredential = isEditing) } + } + fun confirmCredentialUpdate(updatedCredential: TokenizableString.Raw) { viewModelScope.launch { val project = configManager.getProject(authStore.signedInProjectId) @@ -202,4 +210,22 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( ) } } + + fun isCredentialFormatValid(credential: String?): Boolean { + if (credential == null) return false + return when (scannedCredential.credentialType) { + ExternalCredentialType.NHISCard -> { + // 8 digits + ghanaNhisCardOcrSelectorUseCase(credential) + } + ExternalCredentialType.GhanaIdCard -> { + // Ghana ID card number pattern is "GHA-12345789-0" + ghanaIdCardOcrSelectorUseCase(credential) + } + ExternalCredentialType.QRCode -> { + // No QR code validation as of 2025.4.1 + true + } + } + } } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/SearchCredentialState.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/SearchCredentialState.kt index 4493365467..fee2e741da 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/SearchCredentialState.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/model/SearchCredentialState.kt @@ -5,6 +5,7 @@ import com.simprints.core.ExcludedFromGeneratedTestCoverageReports import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.feature.externalcredential.model.CredentialMatch +import kotlin.Boolean @Keep @ExcludedFromGeneratedTestCoverageReports("Data struct") @@ -14,6 +15,7 @@ internal data class SearchCredentialState( val flowType: FlowType, val searchState: SearchState, val isConfirmed: Boolean, + val isEditingCredential: Boolean, ) { companion object { fun buildInitial( @@ -25,6 +27,7 @@ internal data class SearchCredentialState( flowType = flowType, searchState = SearchState.Searching, isConfirmed = false, + isEditingCredential = false, ) } } diff --git a/feature/external-credential/src/main/res/color/ic_edit_color.xml b/feature/external-credential/src/main/res/color/ic_edit_color.xml new file mode 100644 index 0000000000..982d0cb05b --- /dev/null +++ b/feature/external-credential/src/main/res/color/ic_edit_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/feature/external-credential/src/main/res/drawable/ic_done.xml b/feature/external-credential/src/main/res/drawable/ic_done.xml index c7ea9bc53a..6b7bc1618e 100644 --- a/feature/external-credential/src/main/res/drawable/ic_done.xml +++ b/feature/external-credential/src/main/res/drawable/ic_done.xml @@ -4,6 +4,6 @@ android:viewportWidth="960" android:viewportHeight="960"> diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt index e72cf4d9a9..00fa9a2874 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt @@ -12,6 +12,8 @@ import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.time.Timestamp import com.simprints.feature.externalcredential.model.CredentialMatch import com.simprints.feature.externalcredential.model.ExternalCredentialParams +import com.simprints.feature.externalcredential.screens.scanocr.usecase.GhanaIdCardOcrSelectorUseCase +import com.simprints.feature.externalcredential.screens.scanocr.usecase.GhanaNhisCardOcrSelectorUseCase import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential import com.simprints.feature.externalcredential.screens.search.model.SearchCredentialState import com.simprints.feature.externalcredential.screens.search.model.SearchState @@ -79,6 +81,12 @@ internal class ExternalCredentialSearchViewModelTest { @MockK lateinit var eventsTracker: ExternalCredentialEventTrackerUseCase + @MockK + lateinit var ghanaIdCardOcrSelectorUseCase: GhanaIdCardOcrSelectorUseCase + + @MockK + lateinit var ghanaNhisCardOcrSelectorUseCase: GhanaNhisCardOcrSelectorUseCase + private lateinit var viewModel: ExternalCredentialSearchViewModel private val projectId = "projectId" @@ -105,6 +113,8 @@ internal class ExternalCredentialSearchViewModelTest { tokenizationProcessor = tokenizationProcessor, enrolmentRecordRepository = enrolmentRecordRepository, eventsTracker = eventsTracker, + ghanaIdCardOcrSelectorUseCase = ghanaIdCardOcrSelectorUseCase, + ghanaNhisCardOcrSelectorUseCase = ghanaNhisCardOcrSelectorUseCase, ) @Test From 14b61ac0907075e62cb9b353d6d8eeab27e5b850 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 12 Feb 2026 15:37:56 +0200 Subject: [PATCH 3/7] [MS-1340] Updating tests --- .../ExternalCredentialSearchViewModelTest.kt | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt index 00fa9a2874..d0db231fbd 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt @@ -313,4 +313,54 @@ internal class ExternalCredentialSearchViewModelTest { assertThat(viewModel.stateLiveData.value?.displayedCredential).isEqualTo(decryptedCredential) coVerify { tokenizationProcessor.decrypt(encryptedCredential, TokenKeyType.ExternalCredential, project) } } + + @Test + fun `isCredentialFormatValid validates NHIS card format`() = runTest { + val validNhisCard = "12345678" + val invalidNhisCard = "invalid" + + every { mockScannedCredential.credentialType } returns ExternalCredentialType.NHISCard + every { ghanaNhisCardOcrSelectorUseCase(validNhisCard) } returns true + every { ghanaNhisCardOcrSelectorUseCase(invalidNhisCard) } returns false + + viewModel = createViewModel() + + assertThat(viewModel.isCredentialFormatValid(validNhisCard)).isTrue() + assertThat(viewModel.isCredentialFormatValid(invalidNhisCard)).isFalse() + assertThat(viewModel.isCredentialFormatValid(null)).isFalse() + verify { ghanaNhisCardOcrSelectorUseCase(validNhisCard) } + verify { ghanaNhisCardOcrSelectorUseCase(invalidNhisCard) } + } + + @Test + fun `isCredentialFormatValid validates Ghana ID card format`() = runTest { + val validGhanaIdCard = "GHA-12345789-0" + val invalidGhanaIdCard = "invalid" + + every { mockScannedCredential.credentialType } returns ExternalCredentialType.GhanaIdCard + every { ghanaIdCardOcrSelectorUseCase(validGhanaIdCard) } returns true + every { ghanaIdCardOcrSelectorUseCase(invalidGhanaIdCard) } returns false + + viewModel = createViewModel() + + assertThat(viewModel.isCredentialFormatValid(validGhanaIdCard)).isTrue() + assertThat(viewModel.isCredentialFormatValid(invalidGhanaIdCard)).isFalse() + assertThat(viewModel.isCredentialFormatValid(null)).isFalse() + verify { ghanaIdCardOcrSelectorUseCase(validGhanaIdCard) } + verify { ghanaIdCardOcrSelectorUseCase(invalidGhanaIdCard) } + } + + @Test + fun `isCredentialFormatValid always returns true for QR code`() = runTest { + val anyValue = "any_value" + val emptyValue = "" + + every { mockScannedCredential.credentialType } returns ExternalCredentialType.QRCode + + viewModel = createViewModel() + + assertThat(viewModel.isCredentialFormatValid(anyValue)).isTrue() + assertThat(viewModel.isCredentialFormatValid(emptyValue)).isTrue() + assertThat(viewModel.isCredentialFormatValid(null)).isFalse() + } } From 2bfbc80bb56ba0805756b71944e513c4dd4d7813 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 12 Feb 2026 15:43:28 +0200 Subject: [PATCH 4/7] [MS-1340] Removing existing text watch when rendering updated state --- .../screens/search/ExternalCredentialSearchFragment.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt index 443bc2411c..78c29cc57e 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt @@ -3,6 +3,7 @@ package com.simprints.feature.externalcredential.screens.search import android.content.Context import android.graphics.BitmapFactory import android.os.Bundle +import android.text.TextWatcher import android.view.View import android.view.inputmethod.InputMethodManager import androidx.core.content.ContextCompat @@ -59,6 +60,7 @@ internal class ExternalCredentialSearchFragment : Fragment(R.layout.fragment_ext @Inject lateinit var zoomOntoCredentialUseCase: ZoomOntoCredentialUseCase + private var credentialTextWatcher: TextWatcher? = null override fun onViewCreated( view: View, @@ -116,7 +118,8 @@ internal class ExternalCredentialSearchFragment : Fragment(R.layout.fragment_ext viewModel.updateConfirmation(isConfirmed = checkedId) } - credentialEditText.addTextChangedListener( + credentialTextWatcher?.let(credentialEditText::removeTextChangedListener) + credentialTextWatcher = credentialEditText.addTextChangedListener( afterTextChanged = { _ -> renderEditIcon(isEditingCredential) }, From c9286b71ccd2dae9f5bc7aa6206f1e83874ada51 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 12 Feb 2026 15:44:54 +0200 Subject: [PATCH 5/7] [MS-1340] Fixing code comments --- .../screens/scanocr/usecase/GhanaIdCardOcrSelectorUseCase.kt | 2 +- .../screens/search/ExternalCredentialSearchViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/GhanaIdCardOcrSelectorUseCase.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/GhanaIdCardOcrSelectorUseCase.kt index b5672f17fa..5c6b74f3d2 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/GhanaIdCardOcrSelectorUseCase.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/scanocr/usecase/GhanaIdCardOcrSelectorUseCase.kt @@ -6,7 +6,7 @@ internal class GhanaIdCardOcrSelectorUseCase @Inject constructor() { operator fun invoke(readoutValue: String): Boolean = GHANA_ID_PATTERN.matches(readoutValue) companion object { - // Ghana ID card number pattern is "GHA-12345789-0" + // Ghana ID card number pattern is "GHA-123456789-0" private val GHANA_ID_PATTERN = Regex("^GHA-\\d{9}-\\d$") } } diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt index 77a51b1a31..6b5d99debb 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt @@ -219,7 +219,7 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( ghanaNhisCardOcrSelectorUseCase(credential) } ExternalCredentialType.GhanaIdCard -> { - // Ghana ID card number pattern is "GHA-12345789-0" + // Ghana ID card number pattern is "GHA-123456789-0" ghanaIdCardOcrSelectorUseCase(credential) } ExternalCredentialType.QRCode -> { From 0b08e7771b63de48f71ac754dcec6840ae6af363 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 17 Feb 2026 10:53:20 +0200 Subject: [PATCH 6/7] [MS-1340] Confirmation checkbox is disabled if credential is being edited --- .../screens/search/ExternalCredentialSearchFragment.kt | 3 ++- .../src/main/res/color/checkbox_color.xml | 5 +++++ .../main/res/layout/fragment_external_credential_search.xml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 feature/external-credential/src/main/res/color/checkbox_color.xml diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt index 78c29cc57e..3aa685e6d2 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchFragment.kt @@ -106,7 +106,8 @@ internal class ExternalCredentialSearchFragment : Fragment(R.layout.fragment_ext credentialValue.text = currentEditTextValue confirmCredentialCheckbox.isVisible = state.searchState != SearchState.Searching confirmCredentialCheckbox.text = getString(IDR.string.mfid_confirmation_checkbox_text, credentialField) - confirmCredentialCheckbox.isChecked = state.isConfirmed + confirmCredentialCheckbox.isChecked = state.isConfirmed && !state.isEditingCredential + confirmCredentialCheckbox.isEnabled = !state.isEditingCredential iconEditCredential.setOnClickListener { if (isEditingCredential) { diff --git a/feature/external-credential/src/main/res/color/checkbox_color.xml b/feature/external-credential/src/main/res/color/checkbox_color.xml new file mode 100644 index 0000000000..2f81cf52fc --- /dev/null +++ b/feature/external-credential/src/main/res/color/checkbox_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/feature/external-credential/src/main/res/layout/fragment_external_credential_search.xml b/feature/external-credential/src/main/res/layout/fragment_external_credential_search.xml index 96b349c4a5..3675099657 100644 --- a/feature/external-credential/src/main/res/layout/fragment_external_credential_search.xml +++ b/feature/external-credential/src/main/res/layout/fragment_external_credential_search.xml @@ -109,7 +109,7 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="10dp" android:layout_marginBottom="@dimen/margin_large" - android:buttonTint="@color/simprints_blue" + android:buttonTint="@color/checkbox_color" android:checked="false" android:paddingStart="@dimen/margin_default" android:visibility="gone" From acada9a5fd05dff33b56c8602f94560c27ebb05d Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 17 Feb 2026 10:55:56 +0200 Subject: [PATCH 7/7] [MS-1340] Renaming credential validation use cases for better readability --- .../ExternalCredentialSearchViewModel.kt | 8 +++---- .../ExternalCredentialSearchViewModelTest.kt | 24 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt index 6b5d99debb..31a781fc88 100644 --- a/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt +++ b/feature/external-credential/src/main/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModel.kt @@ -43,8 +43,8 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( private val tokenizationProcessor: TokenizationProcessor, private val enrolmentRecordRepository: EnrolmentRecordRepository, private val eventsTracker: ExternalCredentialEventTrackerUseCase, - private val ghanaIdCardOcrSelectorUseCase: GhanaIdCardOcrSelectorUseCase, - private val ghanaNhisCardOcrSelectorUseCase: GhanaNhisCardOcrSelectorUseCase, + private val ghanaIdValidationUseCase: GhanaIdCardOcrSelectorUseCase, + private val ghanaNhisCardValidationUseCase: GhanaNhisCardOcrSelectorUseCase, ) : ViewModel() { @AssistedFactory interface Factory { @@ -216,11 +216,11 @@ internal class ExternalCredentialSearchViewModel @AssistedInject constructor( return when (scannedCredential.credentialType) { ExternalCredentialType.NHISCard -> { // 8 digits - ghanaNhisCardOcrSelectorUseCase(credential) + ghanaNhisCardValidationUseCase(credential) } ExternalCredentialType.GhanaIdCard -> { // Ghana ID card number pattern is "GHA-123456789-0" - ghanaIdCardOcrSelectorUseCase(credential) + ghanaIdValidationUseCase(credential) } ExternalCredentialType.QRCode -> { // No QR code validation as of 2025.4.1 diff --git a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt index d0db231fbd..737fd5e00f 100644 --- a/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt +++ b/feature/external-credential/src/test/java/com/simprints/feature/externalcredential/screens/search/ExternalCredentialSearchViewModelTest.kt @@ -82,10 +82,10 @@ internal class ExternalCredentialSearchViewModelTest { lateinit var eventsTracker: ExternalCredentialEventTrackerUseCase @MockK - lateinit var ghanaIdCardOcrSelectorUseCase: GhanaIdCardOcrSelectorUseCase + lateinit var ghanaIdValidationUseCase: GhanaIdCardOcrSelectorUseCase @MockK - lateinit var ghanaNhisCardOcrSelectorUseCase: GhanaNhisCardOcrSelectorUseCase + lateinit var ghanaNhisCardValidationUseCase: GhanaNhisCardOcrSelectorUseCase private lateinit var viewModel: ExternalCredentialSearchViewModel @@ -113,8 +113,8 @@ internal class ExternalCredentialSearchViewModelTest { tokenizationProcessor = tokenizationProcessor, enrolmentRecordRepository = enrolmentRecordRepository, eventsTracker = eventsTracker, - ghanaIdCardOcrSelectorUseCase = ghanaIdCardOcrSelectorUseCase, - ghanaNhisCardOcrSelectorUseCase = ghanaNhisCardOcrSelectorUseCase, + ghanaIdValidationUseCase = ghanaIdValidationUseCase, + ghanaNhisCardValidationUseCase = ghanaNhisCardValidationUseCase, ) @Test @@ -320,16 +320,16 @@ internal class ExternalCredentialSearchViewModelTest { val invalidNhisCard = "invalid" every { mockScannedCredential.credentialType } returns ExternalCredentialType.NHISCard - every { ghanaNhisCardOcrSelectorUseCase(validNhisCard) } returns true - every { ghanaNhisCardOcrSelectorUseCase(invalidNhisCard) } returns false + every { ghanaNhisCardValidationUseCase(validNhisCard) } returns true + every { ghanaNhisCardValidationUseCase(invalidNhisCard) } returns false viewModel = createViewModel() assertThat(viewModel.isCredentialFormatValid(validNhisCard)).isTrue() assertThat(viewModel.isCredentialFormatValid(invalidNhisCard)).isFalse() assertThat(viewModel.isCredentialFormatValid(null)).isFalse() - verify { ghanaNhisCardOcrSelectorUseCase(validNhisCard) } - verify { ghanaNhisCardOcrSelectorUseCase(invalidNhisCard) } + verify { ghanaNhisCardValidationUseCase(validNhisCard) } + verify { ghanaNhisCardValidationUseCase(invalidNhisCard) } } @Test @@ -338,16 +338,16 @@ internal class ExternalCredentialSearchViewModelTest { val invalidGhanaIdCard = "invalid" every { mockScannedCredential.credentialType } returns ExternalCredentialType.GhanaIdCard - every { ghanaIdCardOcrSelectorUseCase(validGhanaIdCard) } returns true - every { ghanaIdCardOcrSelectorUseCase(invalidGhanaIdCard) } returns false + every { ghanaIdValidationUseCase(validGhanaIdCard) } returns true + every { ghanaIdValidationUseCase(invalidGhanaIdCard) } returns false viewModel = createViewModel() assertThat(viewModel.isCredentialFormatValid(validGhanaIdCard)).isTrue() assertThat(viewModel.isCredentialFormatValid(invalidGhanaIdCard)).isFalse() assertThat(viewModel.isCredentialFormatValid(null)).isFalse() - verify { ghanaIdCardOcrSelectorUseCase(validGhanaIdCard) } - verify { ghanaIdCardOcrSelectorUseCase(invalidGhanaIdCard) } + verify { ghanaIdValidationUseCase(validGhanaIdCard) } + verify { ghanaIdValidationUseCase(invalidGhanaIdCard) } } @Test