Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,7 @@ internal class IntentToActionMapper @Inject constructor(
return when (actionIdentifier.packageName) {
OdkConstants.PACKAGE_NAME -> mapOdkAction(actionIdentifier, extras, project)
CommCareConstants.PACKAGE_NAME -> mapCommCareAction(actionIdentifier, extras, project)
LibSimprintsConstants.PACKAGE_NAME -> {
mapLibSimprintsAction(actionIdentifier, extras, project)
}

LibSimprintsConstants.PACKAGE_NAME -> mapLibSimprintsAction(actionIdentifier, extras, project)
else -> throw InvalidRequestException(
"Unsupported package name", ClientApiError.INVALID_STATE_FOR_INTENT_ACTION
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ internal class EnrolRequestBuilder(
actionIdentifier = actionIdentifier,
projectId = extractor.getProjectId(),
userId = extractor.getUserId().asTokenizableRaw(),
moduleId = extractor.getModuleId().asTokenizableRaw(),
biometricDataSource = extractor.getBiometricDataSource(),
subjectAge = extractor.getSubjectAge(),
metadata = extractor.getMetadata(),
moduleId = extractor.getModuleId().asTokenizableRaw(),
unknownExtras = extractor.getUnknownExtras()
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ internal class IdentifyRequestBuilder(
userId = extractor.getUserId().asTokenizableRaw(),
moduleId = extractor.getModuleId().asTokenizableRaw(),
biometricDataSource = extractor.getBiometricDataSource(),
subjectAge = extractor.getSubjectAge(),
metadata = extractor.getMetadata(),
unknownExtras = extractor.getUnknownExtras()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ internal class VerifyRequestBuilder(
userId = extractor.getUserId().asTokenizableRaw(),
moduleId = extractor.getModuleId().asTokenizableRaw(),
biometricDataSource = extractor.getBiometricDataSource(),
subjectAge = extractor.getSubjectAge(),
metadata = extractor.getMetadata(),
verifyGuid = extractor.getVerifyGuid(),
unknownExtras = extractor.getUnknownExtras()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.simprints.feature.clientapi.mappers.request.extractors

import android.content.Intent
import com.simprints.core.tools.json.JsonHelper
import com.simprints.feature.clientapi.extensions.extractString
import com.simprints.libsimprints.Constants

Expand All @@ -13,7 +14,7 @@ internal abstract class ActionRequestExtractor(private val extras: Map<String, A
Constants.SIMPRINTS_PROJECT_ID,
Constants.SIMPRINTS_USER_ID,
Constants.SIMPRINTS_MODULE_ID,
Constants.SIMPRINTS_METADATA
Constants.SIMPRINTS_METADATA,
)

open fun getProjectId(): String = extras.extractString(Constants.SIMPRINTS_PROJECT_ID)
Expand All @@ -26,6 +27,11 @@ internal abstract class ActionRequestExtractor(private val extras: Map<String, A

open fun getMetadata(): String = extras.extractString(Constants.SIMPRINTS_METADATA)

open fun getSubjectAge(): Int? {
val parsedMetadata = JsonHelper.fromJson<Map<String, Any>>(getMetadata())
return parsedMetadata[Constants.SIMPRINTS_SUBJECT_AGE] as? Int
}

protected open fun Intent.extractString(key: String): String = this.getStringExtra(key) ?: ""

open fun getUnknownExtras(): Map<String, Any?> = extras.filter { it.key.isNotBlank() && !expectedKeys.contains(it.key) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.simprints.matcher

import com.simprints.core.domain.common.FlowType
import com.simprints.infra.config.store.models.FingerprintConfiguration
import com.simprints.infra.enrolment.records.store.domain.models.BiometricDataSource
import com.simprints.infra.enrolment.records.store.domain.models.SubjectQuery
import com.simprints.matcher.screen.MatchFragmentArgs
Expand All @@ -12,12 +13,14 @@ object MatchContract {
fun getArgs(
fingerprintSamples: List<MatchParams.FingerprintSample> = emptyList(),
faceSamples: List<MatchParams.FaceSample> = emptyList(),
fingerprintSDK: FingerprintConfiguration.BioSdk? = null,
flowType: FlowType,
subjectQuery: SubjectQuery,
biometricDataSource: BiometricDataSource,
) = MatchFragmentArgs(MatchParams(
faceSamples,
fingerprintSamples,
fingerprintSDK,
flowType,
subjectQuery,
biometricDataSource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.simprints.core.domain.common.FlowType
import com.simprints.infra.enrolment.records.store.domain.models.SubjectQuery
import com.simprints.infra.uibase.annotations.ExcludedFromGeneratedTestCoverageReports
import com.simprints.core.domain.fingerprint.IFingerIdentifier
import com.simprints.infra.config.store.models.FingerprintConfiguration
import com.simprints.infra.enrolment.records.store.domain.models.BiometricDataSource
import kotlinx.parcelize.Parcelize

Expand All @@ -14,6 +15,7 @@ import kotlinx.parcelize.Parcelize
data class MatchParams(
val probeFaceSamples: List<FaceSample> = emptyList(),
val probeFingerprintSamples: List<FingerprintSample> = emptyList(),
val fingerprintSDK: FingerprintConfiguration.BioSdk? = null,
val flowType: FlowType,
val queryForCandidates: SubjectQuery,
val biometricDataSource: BiometricDataSource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ internal class MatchViewModel @Inject constructor(
else -> fingerprintMatcher
}

val (sortedResults, totalCandidates) = matcherUseCase(
val matcherResult = matcherUseCase(
params,
onLoadingCandidates = { tag ->
Simber.tag(tag).i("Loading candidates")
Expand All @@ -61,19 +61,19 @@ internal class MatchViewModel @Inject constructor(
startTime,
endTime,
params,
totalCandidates,
matcherUseCase.matcherName(),
sortedResults
matcherResult.totalCandidates,
matcherResult.matcherName,
matcherResult.matchResultItems
)

setMatchState(totalCandidates, sortedResults)
setMatchState(matcherResult.totalCandidates, matcherResult.matchResultItems)

// wait a bit for the user to see the results
delay(matchingEndWaitTimeInMillis)

_matchResponse.send(when {
isFaceMatch -> FaceMatchResult(sortedResults)
else -> FingerprintMatchResult(sortedResults)
isFaceMatch -> FaceMatchResult(matcherResult.matchResultItems)
else -> FingerprintMatchResult(matcherResult.matchResultItems)
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import com.simprints.infra.facebiosdk.matching.FaceSample
import com.simprints.infra.logging.LoggingConstants
import com.simprints.matcher.FaceMatchResult
import com.simprints.matcher.MatchParams
import com.simprints.matcher.MatchResultItem
import com.simprints.matcher.usecases.MatcherUseCase.MatcherResult
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
Expand All @@ -25,23 +25,22 @@ internal class FaceMatcherUseCase @Inject constructor(
) : MatcherUseCase {

override val crashReportTag = LoggingConstants.CrashReportTag.FACE_MATCHING.name
override suspend fun matcherName ()= faceMatcher.matcherName

override suspend operator fun invoke(
matchParams: MatchParams,
onLoadingCandidates: (tag: String) -> Unit,
): Pair<List<MatchResultItem>, Int> = coroutineScope {
): MatcherResult = coroutineScope {
if (matchParams.probeFaceSamples.isEmpty()) {
return@coroutineScope Pair(emptyList(), 0)
return@coroutineScope MatcherResult(emptyList(), 0, faceMatcher.matcherName)
}
val samples = mapSamples(matchParams.probeFaceSamples)
val totalCandidates = enrolmentRecordRepository.count(matchParams.queryForCandidates, dataSource = matchParams.biometricDataSource)
if (totalCandidates == 0) {
return@coroutineScope Pair(emptyList(), 0)
return@coroutineScope MatcherResult(emptyList(), 0, faceMatcher.matcherName)
}

onLoadingCandidates(crashReportTag)
createRanges(totalCandidates)
val resultItems = createRanges(totalCandidates)
.map { range ->
async(dispatcher) {
val batchCandidates = getCandidates(matchParams.queryForCandidates, range, dataSource = matchParams.biometricDataSource)
Expand All @@ -50,7 +49,8 @@ internal class FaceMatcherUseCase @Inject constructor(
}
.awaitAll()
.reduce { acc, subSet -> acc.addAll(subSet) }
.toList() to totalCandidates
.toList()
MatcherResult(resultItems, totalCandidates, faceMatcher.matcherName)
}

private fun mapSamples(probes: List<MatchParams.FaceSample>) = probes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.simprints.core.domain.fingerprint.IFingerIdentifier
import com.simprints.fingerprint.infra.basebiosdk.matching.domain.FingerIdentifier
import com.simprints.fingerprint.infra.basebiosdk.matching.domain.Fingerprint
import com.simprints.fingerprint.infra.basebiosdk.matching.domain.FingerprintIdentity
import com.simprints.fingerprint.infra.biosdk.BioSdkWrapper
import com.simprints.fingerprint.infra.biosdk.ResolveBioSdkWrapperUseCase
import com.simprints.infra.config.store.models.FingerprintConfiguration.FingerComparisonStrategy.CROSS_FINGER_USING_MEAN_OF_MAX
import com.simprints.infra.config.sync.ConfigManager
Expand All @@ -15,7 +16,7 @@ import com.simprints.infra.enrolment.records.store.domain.models.SubjectQuery
import com.simprints.infra.logging.LoggingConstants
import com.simprints.matcher.FingerprintMatchResult
import com.simprints.matcher.MatchParams
import com.simprints.matcher.MatchResultItem
import com.simprints.matcher.usecases.MatcherUseCase.MatcherResult
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
Expand All @@ -31,40 +32,42 @@ internal class FingerprintMatcherUseCase @Inject constructor(
) : MatcherUseCase {

override val crashReportTag = LoggingConstants.CrashReportTag.MATCHING.name
override suspend fun matcherName() = resolveBioSdkWrapper().matcherName

override suspend operator fun invoke(
matchParams: MatchParams,
onLoadingCandidates: (tag: String) -> Unit,
): Pair<List<MatchResultItem>, Int> = coroutineScope {
): MatcherResult = coroutineScope {
val bioSdkWrapper = resolveBioSdkWrapper(matchParams.fingerprintSDK!!)

if (matchParams.probeFingerprintSamples.isEmpty()) {
return@coroutineScope Pair(emptyList(), 0)
return@coroutineScope MatcherResult(emptyList(), 0, bioSdkWrapper.matcherName)
}
val samples = mapSamples(matchParams.probeFingerprintSamples)
// Only candidates with supported template format are considered
val queryWithSupportedFormat =
matchParams.queryForCandidates.copy(
fingerprintSampleFormat = resolveBioSdkWrapper().supportedTemplateFormat
fingerprintSampleFormat = bioSdkWrapper.supportedTemplateFormat
)
val totalCandidates = enrolmentRecordRepository.count(queryWithSupportedFormat, dataSource = matchParams.biometricDataSource)
if (totalCandidates == 0) {
return@coroutineScope Pair(emptyList(), 0)
return@coroutineScope MatcherResult(emptyList(), 0, bioSdkWrapper.matcherName)
}

onLoadingCandidates(crashReportTag)
createRanges(totalCandidates)
val resultItems = createRanges(totalCandidates)
.map { range ->
async(dispatcher) {
val batchCandidates = getCandidates(queryWithSupportedFormat, range, matchParams.biometricDataSource)
match(samples, batchCandidates, matchParams.flowType)
match(samples, batchCandidates, matchParams.flowType, bioSdkWrapper)
.fold(MatchResultSet<FingerprintMatchResult.Item>()) { acc, item ->
acc.add(FingerprintMatchResult.Item(item.id, item.score))
}
}
}
.awaitAll()
.reduce { acc, subSet -> acc.addAll(subSet) }
.toList() to totalCandidates
.toList()
MatcherResult(resultItems, totalCandidates, bioSdkWrapper.matcherName)
}

private fun mapSamples(probes: List<MatchParams.FingerprintSample>) = probes
Expand Down Expand Up @@ -93,7 +96,8 @@ internal class FingerprintMatcherUseCase @Inject constructor(
probes: List<Fingerprint>,
candidates: List<FingerprintIdentity>,
flowType: FlowType,
) = resolveBioSdkWrapper().match(
bioSdkWrapper: BioSdkWrapper,
) = bioSdkWrapper.match(
FingerprintIdentity("", probes),
candidates,
isCrossFingerMatchingEnabled(flowType),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ import com.simprints.matcher.MatchResultItem
internal interface MatcherUseCase {

val crashReportTag: String
suspend fun matcherName(): String

/**
* Returns a list of [MatchResultItem]s sorted by confidence score in descending order
* and the total number of candidates that were considered.
* Returns a MatcherResult which contains a list of [MatchResultItem]s sorted by confidence score in descending order,
* the total number of candidates that were considered and the name of the matcher that was used
*/
suspend operator fun invoke(
matchParams: MatchParams,
onLoadingCandidates: (tag: String) -> Unit = {},
): Pair<List<MatchResultItem>, Int>
): MatcherResult

data class MatcherResult(
val matchResultItems: List<MatchResultItem>,
val totalCandidates: Int,
val matcherName: String,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.simprints.feature.orchestrator.usecases.UpdateDailyActivityUseCase
import com.simprints.feature.orchestrator.usecases.response.AppResponseBuilderUseCase
import com.simprints.feature.orchestrator.usecases.steps.BuildStepsUseCase
import com.simprints.feature.setup.LocationStore
import com.simprints.fingerprint.capture.FingerprintCaptureParams
import com.simprints.fingerprint.capture.FingerprintCaptureResult
import com.simprints.infra.config.store.models.GeneralConfiguration
import com.simprints.infra.config.sync.ConfigManager
Expand Down Expand Up @@ -169,7 +170,13 @@ internal class OrchestratorViewModel @Inject constructor(
}
}
if (currentStep.id == StepId.FINGERPRINT_CAPTURE && result is FingerprintCaptureResult) {
val matchingStep = steps.firstOrNull { it.id == StepId.FINGERPRINT_MATCHER }
val captureParams = currentStep.payload.getParcelable<FingerprintCaptureParams>("params")
// Find the matching step for the same fingerprint SDK as there may be multiple match steps
val matchingStep = steps.firstOrNull { step ->
(step.id == StepId.FINGERPRINT_MATCHER)
&& (step.payload.getParcelable<MatchStepStubPayload>(MatchStepStubPayload.STUB_KEY)?.fingerprintSDK
== captureParams?.fingerprintSDK)
}

if (matchingStep != null) {
val fingerprintSamples = result.results.mapNotNull { it.sample }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.simprints.feature.orchestrator.steps
import android.os.Parcelable
import androidx.core.os.bundleOf
import com.simprints.core.domain.common.FlowType
import com.simprints.infra.config.store.models.FingerprintConfiguration
import com.simprints.infra.enrolment.records.store.domain.models.BiometricDataSource
import com.simprints.matcher.MatchContract
import com.simprints.matcher.MatchParams
Expand All @@ -20,6 +21,7 @@ internal data class MatchStepStubPayload(
val flowType: FlowType,
val subjectQuery: SubjectQuery,
val biometricDataSource: BiometricDataSource,
val fingerprintSDK: FingerprintConfiguration.BioSdk?,
) : Parcelable {

fun toFaceStepArgs(samples: List<MatchParams.FaceSample>) = MatchContract.getArgs(
Expand All @@ -31,6 +33,7 @@ internal data class MatchStepStubPayload(

fun toFingerprintStepArgs(samples: List<MatchParams.FingerprintSample>) = MatchContract.getArgs(
fingerprintSamples = samples,
fingerprintSDK = fingerprintSDK,
flowType = flowType,
subjectQuery = subjectQuery,
biometricDataSource = biometricDataSource,
Expand All @@ -43,6 +46,7 @@ internal data class MatchStepStubPayload(
flowType: FlowType,
subjectQuery: SubjectQuery,
biometricDataSource: BiometricDataSource,
) = bundleOf(STUB_KEY to MatchStepStubPayload(flowType, subjectQuery, biometricDataSource))
fingerprintSDK: FingerprintConfiguration.BioSdk? = null,
) = bundleOf(STUB_KEY to MatchStepStubPayload(flowType, subjectQuery, biometricDataSource, fingerprintSDK))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.simprints.fingerprint.capture
import com.simprints.core.domain.common.FlowType
import com.simprints.fingerprint.capture.screen.FingerprintCaptureFragmentArgs
import com.simprints.core.domain.fingerprint.IFingerIdentifier
import com.simprints.infra.config.store.models.FingerprintConfiguration

object FingerprintCaptureContract {

Expand All @@ -11,6 +12,7 @@ object FingerprintCaptureContract {
fun getArgs(
flowType: FlowType,
fingers: List<IFingerIdentifier>,
) = FingerprintCaptureFragmentArgs(FingerprintCaptureParams(flowType, fingers)).toBundle()
fingerprintSDK: FingerprintConfiguration.BioSdk,
) = FingerprintCaptureFragmentArgs(FingerprintCaptureParams(flowType, fingers, fingerprintSDK)).toBundle()

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import android.os.Parcelable
import androidx.annotation.Keep
import com.simprints.core.domain.common.FlowType
import com.simprints.core.domain.fingerprint.IFingerIdentifier
import com.simprints.infra.config.store.models.FingerprintConfiguration
import kotlinx.parcelize.Parcelize

@Keep
@Parcelize
data class FingerprintCaptureParams(
val flowType: FlowType,
val fingerprintsToCapture: List<IFingerIdentifier>
val fingerprintsToCapture: List<IFingerIdentifier>,
val fingerprintSDK: FingerprintConfiguration.BioSdk,
) : Parcelable
Loading