Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
infra:recent-user-activity
infra:config-store
infra:config-sync
infra:credential-store
infra:sync
reportsId: infra1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class ClientApiViewModel @Inject internal constructor(
actionIdentifier = action.actionIdentifier,
sessionId = currentSessionId,
confirmed = confirmResponse.identificationOutcome,
externalCredential = confirmResponse.externalCredential,
),
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.core.os.bundleOf
import com.simprints.core.DeviceID
import com.simprints.core.PackageVersionName
import com.simprints.core.domain.response.AppErrorReason
import com.simprints.infra.events.event.domain.models.scope.Device
import com.simprints.infra.orchestration.data.ActionResponse
import com.simprints.libsimprints.Constants
import com.simprints.libsimprints.contracts.VersionsList
Expand All @@ -15,6 +14,7 @@ import com.simprints.libsimprints.contracts.data.Identification
import com.simprints.libsimprints.contracts.data.Identification.Companion.toJson
import com.simprints.libsimprints.contracts.data.RefusalForm
import com.simprints.libsimprints.contracts.data.Verification
import org.json.JSONObject
import javax.inject.Inject
import com.simprints.libsimprints.Identification as LegacyIdentification
import com.simprints.libsimprints.RefusalForm as LegacyRefusalForm
Expand Down Expand Up @@ -66,12 +66,26 @@ internal class LibSimprintsResponseMapper @Inject constructor(
}
}

is ActionResponse.ConfirmActionResponse -> bundleOf(
Constants.SIMPRINTS_SESSION_ID to response.sessionId,
Constants.SIMPRINTS_DEVICE_ID to deviceId,
Constants.SIMPRINTS_APP_VERSION_NAME to appVersionName,
Constants.SIMPRINTS_BIOMETRICS_COMPLETE_CHECK to true,
)
is ActionResponse.ConfirmActionResponse -> {
bundleOf(
Constants.SIMPRINTS_SESSION_ID to response.sessionId,
Constants.SIMPRINTS_DEVICE_ID to deviceId,
Constants.SIMPRINTS_APP_VERSION_NAME to appVersionName,
Constants.SIMPRINTS_BIOMETRICS_COMPLETE_CHECK to true,
HAS_CREDENTIAL to (response.externalCredential != null),
).also { bundle ->
val credentialJson = response.externalCredential?.let {
JSONObject()
.also {
it.put(SCANNED_CREDENTIAL_VALUE, response.externalCredential?.value)
it.put(SCANNED_CREDENTIAL_TYPE, response.externalCredential?.type)
}.toString()
}
if (credentialJson != null) {
bundle.putString(SCANNED_CREDENTIAL, credentialJson)
}
}
}

is ActionResponse.VerifyActionResponse -> bundleOf(
Constants.SIMPRINTS_SESSION_ID to response.sessionId,
Expand Down Expand Up @@ -182,5 +196,9 @@ internal class LibSimprintsResponseMapper @Inject constructor(

companion object {
internal const val RESULT_CODE_OVERRIDE = "result_code_override"
internal const val HAS_CREDENTIAL = "hasCredential"
internal const val SCANNED_CREDENTIAL = "scannedCredential"
internal const val SCANNED_CREDENTIAL_VALUE = "value"
internal const val SCANNED_CREDENTIAL_TYPE = "type"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ internal class ClientApiViewModelTest {
fun `handleConfirmResponse saves correct events`() = runTest {
viewModel.handleConfirmResponse(
mockRequest(),
mockk { every { identificationOutcome } returns true },
mockk {
every { identificationOutcome } returns true
every { externalCredential } returns mockk()
},
)

coVerify {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,6 @@ class ActionToIntentMapperTest {
actionIdentifier = ConfirmIdentityActionFactory.getIdentifier().copy(packageName = packageName),
sessionId = "sessionId",
confirmed = true,
externalCredential = null,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class CommCareResponseMapperTest {
actionIdentifier = ConfirmIdentityActionFactory.getIdentifier(),
sessionId = "sessionId",
confirmed = true,
externalCredential = null,
),
).getBundle(CommCareConstants.COMMCARE_BUNDLE_KEY) ?: bundleOf()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@ package com.simprints.feature.clientapi.mappers.response

import androidx.test.ext.junit.runners.*
import com.google.common.truth.Truth.*
import com.simprints.core.domain.externalcredential.ExternalCredentialType
import com.simprints.core.domain.response.AppErrorReason
import com.simprints.core.domain.response.AppMatchConfidence
import com.simprints.core.domain.tokenization.asTokenizableEncrypted
import com.simprints.feature.clientapi.mappers.request.requestFactories.ConfirmIdentityActionFactory
import com.simprints.feature.clientapi.mappers.request.requestFactories.EnrolActionFactory
import com.simprints.feature.clientapi.mappers.request.requestFactories.EnrolLastBiometricsActionFactory
import com.simprints.feature.clientapi.mappers.request.requestFactories.IdentifyRequestActionFactory
import com.simprints.feature.clientapi.mappers.request.requestFactories.VerifyActionFactory
import com.simprints.feature.clientapi.mappers.response.LibSimprintsResponseMapper.Companion.HAS_CREDENTIAL
import com.simprints.feature.clientapi.mappers.response.LibSimprintsResponseMapper.Companion.SCANNED_CREDENTIAL
import com.simprints.feature.clientapi.mappers.response.LibSimprintsResponseMapper.Companion.SCANNED_CREDENTIAL_TYPE
import com.simprints.feature.clientapi.mappers.response.LibSimprintsResponseMapper.Companion.SCANNED_CREDENTIAL_VALUE
import com.simprints.infra.orchestration.data.ActionResponse
import com.simprints.infra.orchestration.data.responses.AppMatchResult
import com.simprints.libsimprints.Constants
import com.simprints.libsimprints.contracts.VersionsList
import io.mockk.*
import org.junit.Test
import org.junit.runner.RunWith
import com.simprints.libsimprints.Identification as LegacyIdentification
Expand Down Expand Up @@ -126,18 +133,27 @@ class LibSimprintsResponseMapperTest {

@Test
fun `correctly maps confirm response`() {
val expectedValue = "expectedValue".asTokenizableEncrypted()
val expectedType = ExternalCredentialType.NHISCard
val expectedJson = "{\"$SCANNED_CREDENTIAL_VALUE\":\"$expectedValue\",\"$SCANNED_CREDENTIAL_TYPE\":\"$expectedType\"}"
val extras = mapper(
ActionResponse.ConfirmActionResponse(
actionIdentifier = ConfirmIdentityActionFactory.getIdentifier(),
sessionId = "sessionId",
confirmed = true,
externalCredential = mockk {
every { value } returns expectedValue
every { type } returns expectedType
},
),
)

assertThat(extras.getString(Constants.SIMPRINTS_SESSION_ID)).isEqualTo("sessionId")
assertThat(extras.getString(Constants.SIMPRINTS_DEVICE_ID)).isEqualTo("deviceId")
assertThat(extras.getString(Constants.SIMPRINTS_APP_VERSION_NAME)).isEqualTo("appVersionName")
assertThat(extras.getBoolean(Constants.SIMPRINTS_BIOMETRICS_COMPLETE_CHECK)).isTrue()
assertThat(extras.getBoolean(HAS_CREDENTIAL)).isTrue()
assertThat(extras.getString(SCANNED_CREDENTIAL)).isEqualTo(expectedJson)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class OdkResponseMapperTest {
actionIdentifier = ConfirmIdentityActionFactory.getIdentifier(),
sessionId = "sessionId",
confirmed = true,
externalCredential = null,
),
)

Expand Down
1 change: 1 addition & 0 deletions feature/external-credential/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {
implementation(project(":infra:enrolment-records:repository"))
implementation(project(":infra:auth-store"))
implementation(project(":infra:matching"))
implementation(project(":infra:credential-store"))
implementation(libs.androidX.cameraX.view)
implementation(libs.mlkit.text.recognition)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.res.Resources
import androidx.annotation.PluralsRes
import androidx.annotation.StringRes
import com.simprints.core.domain.externalcredential.ExternalCredentialType
import com.simprints.infra.resources.R as IDR

fun Resources.getQuantityCredentialString(
@PluralsRes id: Int,
Expand All @@ -23,3 +24,18 @@ fun Resources.getQuantityCredentialString(
getString(documentTypeRes),
)
}

fun Resources.getCredentialFieldTitle(type: ExternalCredentialType): String = when (type) {
ExternalCredentialType.NHISCard -> IDR.string.mfid_nhis_card_credential_field
ExternalCredentialType.GhanaIdCard -> IDR.string.mfid_ghana_id_credential_field
ExternalCredentialType.QRCode -> IDR.string.mfid_qr_credential_field
}.run(::getString)

fun Resources.getCredentialTypeString(type: ExternalCredentialType?): String = getCredentialTypeRes(type).run(::getString)

fun Resources.getCredentialTypeRes(type: ExternalCredentialType?): Int = when (type) {
ExternalCredentialType.NHISCard -> IDR.string.mfid_type_nhis_card
ExternalCredentialType.GhanaIdCard -> IDR.string.mfid_type_ghana_id_card
ExternalCredentialType.QRCode -> IDR.string.mfid_type_qr_code
null -> IDR.string.mfid_type_any_document
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ data class BoundingBox(
val bottom: Int,
) : Serializable

internal fun Rect.toBoundingBox(): BoundingBox = BoundingBox(left, top, right, bottom)
fun Rect.toBoundingBox(): BoundingBox = BoundingBox(left, top, right, bottom)

internal fun BoundingBox.toRect(): Rect = Rect(left, top, right, bottom)
fun BoundingBox.toRect(): Rect = Rect(left, top, right, bottom)
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,6 @@ internal class ExternalCredentialViewModel @Inject internal constructor() : View
}
}

fun mapTypeToStringResource(type: ExternalCredentialType?) = when (type) {
ExternalCredentialType.NHISCard -> IDR.string.mfid_type_nhis_card
ExternalCredentialType.GhanaIdCard -> IDR.string.mfid_type_ghana_id_card
ExternalCredentialType.QRCode -> IDR.string.mfid_type_qr_code
null -> IDR.string.mfid_type_any_document
}

fun mapTypeToCredentialFieldResource(type: ExternalCredentialType) = when (type) {
ExternalCredentialType.NHISCard -> IDR.string.mfid_nhis_card_credential_field
ExternalCredentialType.GhanaIdCard -> IDR.string.mfid_ghana_id_credential_field
ExternalCredentialType.QRCode -> IDR.string.mfid_qr_credential_field
}

fun finish(result: ExternalCredentialSearchResult) {
_finishEvent.send(result)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import com.simprints.core.tools.extentions.getCurrentPermissionStatus
import com.simprints.core.tools.extentions.permissionFromResult
import com.simprints.feature.externalcredential.R
import com.simprints.feature.externalcredential.databinding.FragmentExternalCredentialScanOcrBinding
import com.simprints.feature.externalcredential.ext.fadeIn
import com.simprints.feature.externalcredential.ext.fadeOut
import com.simprints.feature.externalcredential.screens.controller.ExternalCredentialViewModel
import com.simprints.feature.externalcredential.screens.scanocr.model.OcrCropConfig
import com.simprints.feature.externalcredential.screens.scanocr.usecase.BuildOcrCropConfigUseCase
Expand All @@ -39,6 +37,8 @@ import com.simprints.infra.logging.LoggingConstants.CrashReportTag.MULTI_FACTOR_
import com.simprints.infra.logging.Simber
import com.simprints.infra.uibase.navigation.navigateSafely
import com.simprints.infra.uibase.view.applySystemBarInsets
import com.simprints.infra.uibase.view.fadeIn
import com.simprints.infra.uibase.view.fadeOut
import com.simprints.infra.uibase.viewbinding.viewBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineDispatcher
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ import com.simprints.feature.externalcredential.screens.scanocr.usecase.CropDocu
import com.simprints.feature.externalcredential.screens.scanocr.usecase.GetCredentialCoordinatesUseCase
import com.simprints.feature.externalcredential.screens.scanocr.usecase.KeepOnlyBestDetectedBlockUseCase
import com.simprints.feature.externalcredential.screens.scanocr.usecase.NormalizeBitmapToPreviewUseCase
import com.simprints.feature.externalcredential.screens.scanocr.usecase.SaveScannedImageUseCase
import com.simprints.feature.externalcredential.screens.scanocr.usecase.SaveScannedImageUseCase.ScanImageType.ZoomedInCredential
import com.simprints.feature.externalcredential.screens.scanocr.usecase.ZoomOntoCredentialUseCase
import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential
import com.simprints.infra.authstore.AuthStore
import com.simprints.infra.config.store.models.TokenKeyType
import com.simprints.infra.config.store.tokenization.TokenizationProcessor
import com.simprints.infra.config.sync.ConfigManager
import com.simprints.infra.credential.store.CredentialImageRepository
import com.simprints.infra.credential.store.model.CredentialScanImageType.ZoomedInCredential
import com.simprints.infra.logging.LoggingConstants.CrashReportTag.MULTI_FACTOR_ID
import com.simprints.infra.logging.Simber
import com.simprints.infra.resources.R
Expand All @@ -41,7 +41,7 @@ internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor(
private val cropDocumentFromPreviewUseCase: CropDocumentFromPreviewUseCase,
private val getCredentialCoordinatesUseCase: GetCredentialCoordinatesUseCase,
private val keepOnlyBestDetectedBlockUseCase: KeepOnlyBestDetectedBlockUseCase,
private val saveScannedImageUseCase: SaveScannedImageUseCase,
private val credentialImageRepository: CredentialImageRepository,
private val zoomOntoCredentialUseCase: ZoomOntoCredentialUseCase,
private val tokenizationProcessor: TokenizationProcessor,
private val authStore: AuthStore,
Expand Down Expand Up @@ -138,10 +138,9 @@ internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor(
}
}

private fun buildZoomedImagePath(detectedBlock: DetectedOcrBlock): String? = try {
saveScannedImageUseCase(
private suspend fun buildZoomedImagePath(detectedBlock: DetectedOcrBlock): String? = try {
credentialImageRepository.saveCredentialScan(
bitmap = zoomOntoCredentialUseCase(detectedBlock.imagePath, detectedBlock.blockBoundingBox),
documentType = detectedBlock.documentType,
imageType = ZoomedInCredential,
)
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import com.simprints.core.ExcludedFromGeneratedTestCoverageReports
import com.simprints.feature.externalcredential.model.toBoundingBox
import com.simprints.feature.externalcredential.screens.scanocr.model.DetectedOcrBlock
import com.simprints.feature.externalcredential.screens.scanocr.model.OcrDocumentType
import com.simprints.feature.externalcredential.screens.scanocr.usecase.SaveScannedImageUseCase.ScanImageType.FullDocument
import com.simprints.infra.credential.store.CredentialImageRepository
import com.simprints.infra.credential.store.model.CredentialScanImageType.FullDocument
import com.simprints.infra.logging.LoggingConstants.CrashReportTag.MULTI_FACTOR_ID
import com.simprints.infra.logging.Simber
import javax.inject.Inject
Expand All @@ -20,7 +21,7 @@ import javax.inject.Singleton
internal class GetCredentialCoordinatesUseCase @Inject constructor(
private val ghanaNhisCardOcrSelectorUseCase: GhanaNhisCardOcrSelectorUseCase,
private val ghanaIdCardOcrSelectorUseCase: GhanaIdCardOcrSelectorUseCase,
private val saveScannedImageUseCase: SaveScannedImageUseCase,
private val credentialImageRepository: CredentialImageRepository,
) {
private val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)

Expand Down Expand Up @@ -61,16 +62,16 @@ internal class GetCredentialCoordinatesUseCase @Inject constructor(
if (isValid) {
val blockBoundingRect = textBlock.boundingBox ?: return@firstNotNullOfOrNull null
val lineBoundingRect = textLine.boundingBox ?: return@firstNotNullOfOrNull null
val savedImagePath = saveScannedImageUseCase(bitmap, documentType, imageType = FullDocument)
DetectedOcrBlock(
val savedImagePath = credentialImageRepository.saveCredentialScan(bitmap, imageType = FullDocument)
return@firstNotNullOfOrNull DetectedOcrBlock(
imagePath = savedImagePath,
documentType = documentType,
blockBoundingBox = blockBoundingRect.toBoundingBox(),
lineBoundingBox = lineBoundingRect.toBoundingBox(),
readoutValue = lineReadout,
)
} else {
null
return@firstNotNullOfOrNull null
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package com.simprints.feature.externalcredential.screens.scanocr.usecase

import com.simprints.feature.externalcredential.screens.scanocr.model.DetectedOcrBlock
import com.simprints.feature.externalcredential.screens.scanocr.model.OcrDocumentType
import com.simprints.infra.credential.store.CredentialImageRepository
import javax.inject.Inject

internal class KeepOnlyBestDetectedBlockUseCase @Inject constructor(
private val getExternalCredentialBasedOnConfidenceUseCase: GetExternalCredentialBasedOnConfidenceUseCase,
private val findBestTextBlockForCredentialUseCase: FindBestTextBlockForCredentialUseCase,
private val deleteScannedImageUseCase: DeleteScannedImageUseCase,
private val credentialImageRepository: CredentialImageRepository,
) {
suspend operator fun invoke(
allDetectedBlock: List<DetectedOcrBlock>,
Expand All @@ -24,7 +25,7 @@ internal class KeepOnlyBestDetectedBlockUseCase @Inject constructor(
allDetectedBlock
.map(DetectedOcrBlock::imagePath)
.filterNot { imagePath -> imagePath == detectedBlock.imagePath }
.onEach { imagePath -> deleteScannedImageUseCase(imagePath) }
.onEach { imagePath -> credentialImageRepository.deleteByPath(imagePath) }
return detectedBlock
}
}
Loading