diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/CaptureStateResources.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/CaptureStateResources.kt index c995358d35..c4d3eb3dd4 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/CaptureStateResources.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/CaptureStateResources.kt @@ -10,12 +10,12 @@ import com.simprints.infra.uibase.annotations.ExcludedFromGeneratedTestCoverageR @StringRes internal fun CaptureState.buttonTextId(isAskingRescan: Boolean): Int = when (this) { is CaptureState.NotCollected -> R.string.fingerprint_capture_scan - is CaptureState.Scanning -> R.string.fingerprint_capture_cancel_button - is CaptureState.TransferringImage -> R.string.fingerprint_capture_please_wait + is CaptureState.ScanProcess.Scanning -> R.string.fingerprint_capture_cancel_button + is CaptureState.ScanProcess.TransferringImage -> R.string.fingerprint_capture_please_wait is CaptureState.Skipped, - is CaptureState.NotDetected -> R.string.fingerprint_capture_rescan + is CaptureState.ScanProcess.NotDetected -> R.string.fingerprint_capture_rescan - is CaptureState.Collected -> if (scanResult.isGoodScan()) { + is CaptureState.ScanProcess.Collected -> if (scanResult.isGoodScan()) { if (isAskingRescan) { R.string.fingerprint_capture_rescan_question } else { @@ -30,13 +30,13 @@ internal fun CaptureState.buttonTextId(isAskingRescan: Boolean): Int = when (thi @ColorRes internal fun CaptureState.buttonBackgroundColour(): Int = when (this) { is CaptureState.NotCollected -> R.color.simprints_grey - is CaptureState.Scanning, - is CaptureState.TransferringImage -> R.color.simprints_blue + is CaptureState.ScanProcess.Scanning, + is CaptureState.ScanProcess.TransferringImage -> R.color.simprints_blue is CaptureState.Skipped, - is CaptureState.NotDetected -> R.color.simprints_red + is CaptureState.ScanProcess.NotDetected -> R.color.simprints_red - is CaptureState.Collected -> if (scanResult.isGoodScan()) { + is CaptureState.ScanProcess.Collected -> if (scanResult.isGoodScan()) { R.color.simprints_green } else { R.color.simprints_red @@ -47,16 +47,16 @@ internal fun CaptureState.buttonBackgroundColour(): Int = when (this) { @StringRes internal fun CaptureState.resultTextId(): Int = when (this) { is CaptureState.NotCollected -> R.string.fingerprint_capture_empty - is CaptureState.Scanning -> R.string.fingerprint_capture_empty - is CaptureState.TransferringImage -> if (scanResult.isGoodScan()) { + is CaptureState.ScanProcess.Scanning -> R.string.fingerprint_capture_empty + is CaptureState.ScanProcess.TransferringImage -> if (scanResult.isGoodScan()) { R.string.fingerprint_capture_good_scan_message } else { R.string.fingerprint_capture_poor_scan_message } is CaptureState.Skipped -> R.string.fingerprint_capture_finger_skipped_message - is CaptureState.NotDetected -> R.string.fingerprint_capture_no_finger_detected_message - is CaptureState.Collected -> if (scanResult.isGoodScan()) { + is CaptureState.ScanProcess.NotDetected -> R.string.fingerprint_capture_no_finger_detected_message + is CaptureState.ScanProcess.Collected -> if (scanResult.isGoodScan()) { R.string.fingerprint_capture_good_scan_message } else { R.string.fingerprint_capture_poor_scan_message @@ -67,18 +67,18 @@ internal fun CaptureState.resultTextId(): Int = when (this) { @ColorRes internal fun CaptureState.resultTextColour(): Int = when (this) { is CaptureState.NotCollected, - is CaptureState.Scanning -> android.R.color.white + is CaptureState.ScanProcess.Scanning -> android.R.color.white - is CaptureState.TransferringImage -> if (scanResult.isGoodScan()) { + is CaptureState.ScanProcess.TransferringImage -> if (scanResult.isGoodScan()) { R.color.simprints_green } else { R.color.simprints_red } is CaptureState.Skipped, - is CaptureState.NotDetected -> R.color.simprints_red + is CaptureState.ScanProcess.NotDetected -> R.color.simprints_red - is CaptureState.Collected -> if (scanResult.isGoodScan()) { + is CaptureState.ScanProcess.Collected -> if (scanResult.isGoodScan()) { R.color.simprints_green } else { R.color.simprints_red diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/FingerStateResources.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/FingerStateResources.kt index faf8c25318..89c1b3f4ce 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/FingerStateResources.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/resources/FingerStateResources.kt @@ -18,14 +18,14 @@ internal fun FingerState.indicatorDrawableId(selected: Boolean): Int = @DrawableRes internal fun FingerState.indicatorSelectedDrawableId(): Int = when (this.currentCapture()) { is CaptureState.NotCollected, - is CaptureState.Scanning, - is CaptureState.TransferringImage -> R.drawable.blank_selected + is CaptureState.ScanProcess.Scanning, + is CaptureState.ScanProcess.TransferringImage -> R.drawable.blank_selected is CaptureState.Skipped, - is CaptureState.NotDetected -> R.drawable.alert_selected + is CaptureState.ScanProcess.NotDetected -> R.drawable.alert_selected - is CaptureState.Collected -> when { - captures.all { it is CaptureState.Collected && it.scanResult.isGoodScan() } -> R.drawable.ok_selected + is CaptureState.ScanProcess.Collected -> when { + captures.all { it is CaptureState.ScanProcess.Collected && it.scanResult.isGoodScan() } -> R.drawable.ok_selected captures.any { it is CaptureState.NotCollected } -> R.drawable.blank_selected else -> R.drawable.alert_selected } @@ -35,14 +35,14 @@ internal fun FingerState.indicatorSelectedDrawableId(): Int = when (this.current @DrawableRes internal fun FingerState.indicatorDeselectedDrawableId(): Int = when (this.currentCapture()) { is CaptureState.NotCollected, - is CaptureState.Scanning, - is CaptureState.TransferringImage -> R.drawable.blank_deselected + is CaptureState.ScanProcess.Scanning, + is CaptureState.ScanProcess.TransferringImage -> R.drawable.blank_deselected is CaptureState.Skipped, - is CaptureState.NotDetected -> R.drawable.alert_deselected + is CaptureState.ScanProcess.NotDetected -> R.drawable.alert_deselected - is CaptureState.Collected -> when { - captures.all { it is CaptureState.Collected && it.scanResult.isGoodScan() } -> R.drawable.ok_deselected + is CaptureState.ScanProcess.Collected -> when { + captures.all { it is CaptureState.ScanProcess.Collected && it.scanResult.isGoodScan() } -> R.drawable.ok_deselected captures.any { it is CaptureState.NotCollected } -> R.drawable.blank_deselected else -> R.drawable.alert_deselected } @@ -62,11 +62,11 @@ internal fun FingerState.captureNumberTextId(): Int = IDR.string.fingerprint_cap @StringRes internal fun FingerState.directionTextId(isLastFinger: Boolean): Int = when (val currentCapture = this.currentCapture()) { is CaptureState.NotCollected -> if (currentCaptureIndex == 0) IDR.string.fingerprint_capture_please_scan else IDR.string.fingerprint_capture_please_scan_again - is CaptureState.Scanning -> IDR.string.fingerprint_capture_scanning - is CaptureState.TransferringImage -> IDR.string.fingerprint_capture_transfering_data + is CaptureState.ScanProcess.Scanning -> IDR.string.fingerprint_capture_scanning + is CaptureState.ScanProcess.TransferringImage -> IDR.string.fingerprint_capture_transfering_data is CaptureState.Skipped -> IDR.string.fingerprint_capture_good_scan_direction - is CaptureState.NotDetected -> IDR.string.fingerprint_capture_poor_scan_direction - is CaptureState.Collected -> if (currentCapture.scanResult.isGoodScan()) { + is CaptureState.ScanProcess.NotDetected -> IDR.string.fingerprint_capture_poor_scan_direction + is CaptureState.ScanProcess.Collected -> if (currentCapture.scanResult.isGoodScan()) { if (isLastFinger || currentCaptureIndex + 1 < captures.size) IDR.string.fingerprint_capture_empty else IDR.string.fingerprint_capture_good_scan_direction } else { IDR.string.fingerprint_capture_poor_scan_direction diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureFragment.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureFragment.kt index fdb42df238..58273096e8 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureFragment.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureFragment.kt @@ -253,7 +253,7 @@ internal class FingerprintCaptureFragment : Fragment(R.layout.fragment_fingerpri val dialogItems = state.fingerStates.map { ConfirmFingerprintsDialog.Item( it.id, - it.captures.count { capture -> capture is CaptureState.Collected && capture.scanResult.isGoodScan() }, + it.captures.count { capture -> capture is CaptureState.ScanProcess.Collected && capture.scanResult.isGoodScan() }, it.captures.size ) } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModel.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModel.kt index 4b946975fd..2a3e665fe9 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModel.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModel.kt @@ -27,6 +27,7 @@ import com.simprints.fingerprint.capture.state.ScanResult import com.simprints.fingerprint.capture.usecase.AddCaptureEventsUseCase import com.simprints.fingerprint.capture.usecase.GetNextFingerToAddUseCase import com.simprints.fingerprint.capture.usecase.GetStartStateUseCase +import com.simprints.fingerprint.capture.usecase.IsNoFingerDetectedLimitReachedUseCase import com.simprints.fingerprint.capture.usecase.SaveImageUseCase import com.simprints.fingerprint.infra.basebiosdk.exceptions.BioSdkException import com.simprints.fingerprint.infra.biosdk.BioSdkWrapper @@ -70,6 +71,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( private val getNextFingerToAdd: GetNextFingerToAddUseCase, private val getStartState: GetStartStateUseCase, private val addCaptureEvents: AddCaptureEventsUseCase, + private val isNoFingerDetectedLimitReachedUseCase: IsNoFingerDetectedLimitReachedUseCase, @ExternalScope private val externalScope: CoroutineScope, ) : ViewModel() { @@ -208,14 +210,14 @@ internal class FingerprintCaptureViewModel @Inject constructor( } when (it.currentCaptureState()) { - is CaptureState.Scanning, - is CaptureState.TransferringImage, + is CaptureState.ScanProcess.Scanning, + is CaptureState.ScanProcess.TransferringImage, -> pauseLiveFeedback() CaptureState.NotCollected, CaptureState.Skipped, - is CaptureState.NotDetected, - is CaptureState.Collected, + is CaptureState.ScanProcess.NotDetected, + is CaptureState.ScanProcess.Collected, -> { if (it.isShowingConfirmDialog) stopLiveFeedback() else startLiveFeedback(scannerManager.scanner) @@ -273,11 +275,11 @@ internal class FingerprintCaptureViewModel @Inject constructor( * */ fun progressBarTimeout() = bioSdkWrapper.scanningTimeoutMs + - if (isImageTransferRequired()) bioSdkWrapper.imageTransferTimeoutMs else 0 + if (isImageTransferRequired()) bioSdkWrapper.imageTransferTimeoutMs else 0 private fun isImageTransferRequired(): Boolean = bioSdkConfiguration.vero2?.imageSavingStrategy?.isImageTransferRequired() ?: false && - scannerManager.scanner.isImageTransferSupported() + scannerManager.scanner.isImageTransferSupported() fun updateSelectedFinger(index: Int) { viewModelScope.launch { @@ -308,7 +310,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( fun handleScanButtonPressed() { val state = state val fingerState = this.state.currentCaptureState() - if (fingerState is CaptureState.Collected && fingerState.scanResult.isGoodScan() && state.isAskingRescan.not()) { + if (fingerState is CaptureState.ScanProcess.Collected && fingerState.scanResult.isGoodScan() && state.isAskingRescan.not()) { updateState { it.copy(isAskingRescan = true) } } else { updateState { it.copy(isAskingRescan = false) } @@ -319,16 +321,16 @@ internal class FingerprintCaptureViewModel @Inject constructor( } private fun isBusyForScanning(): Boolean = with(state) { - currentCaptureState() is CaptureState.TransferringImage || isShowingConfirmDialog || isShowingSplashScreen + currentCaptureState() is CaptureState.ScanProcess.TransferringImage || isShowingConfirmDialog || isShowingSplashScreen } private fun toggleScanning() { when (state.currentCaptureState()) { - is CaptureState.Scanning -> cancelScanning() - is CaptureState.TransferringImage -> { /* do nothing */ + is CaptureState.ScanProcess.Scanning -> cancelScanning() + is CaptureState.ScanProcess.TransferringImage -> { /* do nothing */ } - is CaptureState.NotCollected, is CaptureState.Skipped, is CaptureState.NotDetected, is CaptureState.Collected -> startScanning() + is CaptureState.NotCollected, is CaptureState.Skipped, is CaptureState.ScanProcess.NotDetected, is CaptureState.ScanProcess.Collected -> startScanning() } } @@ -351,7 +353,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( timeOutMs = bioSdkWrapper.scanningTimeoutMs.toInt(), qualityThreshold = qualityThreshold(), // is this is the last bad scan, we allow low quality extraction - allowLowQualityExtraction = tooManyBadScans(state.currentCaptureState(), plusBadScan = true) + allowLowQualityExtraction = isTooManyBadScans(state.currentCaptureState(), plusBadScan = true) ) handleCaptureSuccess(capturedFingerprint) @@ -384,7 +386,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( private fun shouldProceedToImageTransfer(quality: Int): Boolean { val isGoodScan = quality >= qualityThreshold() - val isTooManyBadScans = tooManyBadScans(state.currentCaptureState(), plusBadScan = true) + val isTooManyBadScans = isTooManyBadScans(state.currentCaptureState(), plusBadScan = true) val shouldUploadImage = when (bioSdkConfiguration.vero2?.imageSavingStrategy) { NEVER, null -> false @@ -438,7 +440,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( lastCaptureStartedAt, fingerState, qualityThreshold(), - tooManyBadScans(captureState, plusBadScan = false) + isTooManyBadScans(captureState, plusBadScan = false) ) } captureEventIds[CaptureId(fingerState.id, fingerState.currentCaptureIndex)] = payloadId @@ -447,7 +449,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( private fun saveCurrentImageIfEager() { if (bioSdkConfiguration.vero2?.imageSavingStrategy?.isEager() == true) { with(state.currentFingerState()) { - (currentCapture() as? CaptureState.Collected)?.let { capture -> + (currentCapture() as? CaptureState.ScanProcess.Collected)?.let { capture -> runBlocking { saveImageIfExists(CaptureId(id, currentCaptureIndex), capture) } @@ -474,7 +476,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( if (isScanningEndStateAchieved()) { Simber.tag(FINGER_CAPTURE.name).i("Confirm fingerprints dialog shown") updateState { it.copy(isShowingConfirmDialog = true) } - } else if (currentCaptureState().let { it is CaptureState.Collected && it.scanResult.isGoodScan() }) { + } else if (currentCaptureState().let { it is CaptureState.ScanProcess.Collected && it.scanResult.isGoodScan() }) { nudgeToNextFinger() } else { if (haveNotExceedMaximumNumberOfFingersToAutoAdd()) { @@ -535,6 +537,9 @@ internal class FingerprintCaptureViewModel @Inject constructor( _vibrate.send() updateCaptureState(CaptureState::toNotDetected) addCaptureAndBiometricEventsInSession() + if (isNoFingerDetectedLimitReachedUseCase(state.currentCaptureState(), bioSdkConfiguration)) { + handleCaptureFinished() + } } fun handleMissingFingerButtonPressed() { @@ -553,12 +558,16 @@ internal class FingerprintCaptureViewModel @Inject constructor( private fun CollectFingerprintsState.everyActiveFingerHasSatisfiedTerminalCondition(): Boolean = fingerStates.all { captureHasSatisfiedTerminalCondition(it.currentCapture()) } - private fun tooManyBadScans(fingerState: CaptureState, plusBadScan: Boolean): Boolean = + private fun isTooManyBadScans(fingerState: CaptureState, plusBadScan: Boolean): Boolean { + val isNumberOfBadScansReached = isNumberOfBadScansReached(fingerState, plusBadScan) + val isNumberOfNoFingerDetectedReached = + isNoFingerDetectedLimitReachedUseCase(fingerState, bioSdkConfiguration) + return isNumberOfBadScansReached || isNumberOfNoFingerDetectedReached + } + + private fun isNumberOfBadScansReached(fingerState: CaptureState, plusBadScan: Boolean) = when (fingerState) { - is CaptureState.Scanning -> fingerState.numberOfBadScans - is CaptureState.TransferringImage -> fingerState.numberOfBadScans - is CaptureState.NotDetected -> fingerState.numberOfBadScans - is CaptureState.Collected -> fingerState.numberOfBadScans + is CaptureState.ScanProcess -> fingerState.numberOfBadScans else -> 0 } >= numberOfBadScansRequiredToAutoAddNewFinger - if (plusBadScan) 1 else 0 @@ -568,7 +577,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( private fun CollectFingerprintsState.weHaveTheMinimumNumberOfGoodScans(): Boolean = fingerStates.filter { val currentCapture = it.currentCapture() - currentCapture is CaptureState.Collected && currentCapture.scanResult.isGoodScan() + currentCapture is CaptureState.ScanProcess.Collected && currentCapture.scanResult.isGoodScan() }.size >= min(targetNumberOfGoodScans, numberOfOriginalFingers()) private fun CollectFingerprintsState.weHaveTheMinimumNumberOfAnyQualityScans() = @@ -578,10 +587,14 @@ internal class FingerprintCaptureViewModel @Inject constructor( private fun numberOfOriginalFingers() = originalFingerprintsToCapture.toSet().size - private fun captureHasSatisfiedTerminalCondition(captureState: CaptureState) = - captureState is CaptureState.Collected && (tooManyBadScans( + private fun captureHasSatisfiedTerminalCondition(captureState: CaptureState): Boolean { + val isCollected = captureState is CaptureState.ScanProcess.Collected && (isTooManyBadScans( captureState, plusBadScan = false - ) || captureState.scanResult.isGoodScan()) || captureState is CaptureState.Skipped + ) || captureState.scanResult.isGoodScan()) + val isSkipped = captureState is CaptureState.Skipped + val isNotDetected = isNoFingerDetectedLimitReachedUseCase(captureState, bioSdkConfiguration) + return isCollected || isSkipped || isNotDetected + } private fun fingerHasSatisfiedTerminalCondition(fingerState: FingerState) = fingerState.captures.all { captureHasSatisfiedTerminalCondition(it) } @@ -589,7 +602,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( fun handleConfirmFingerprintsAndContinue() { val collectedFingers = state.fingerStates.flatMap { it.captures.mapIndexedNotNull { index, capture -> - if (capture is CaptureState.Collected) Pair( + if (capture is CaptureState.ScanProcess.Collected) Pair( CaptureId(it.id, index), capture ) else null @@ -607,7 +620,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( } } - private fun saveImages(collectedFingers: List>) { + private fun saveImages(collectedFingers: List>) { runBlocking { collectedFingers.map { (id, collectedFinger) -> saveImageIfExists(id, collectedFinger) @@ -615,7 +628,7 @@ internal class FingerprintCaptureViewModel @Inject constructor( } } - private fun proceedToFinish(collectedFingers: List>) { + private fun proceedToFinish(collectedFingers: List>) { val resultItems = collectedFingers.map { (captureId, collectedFinger) -> FingerprintCaptureResult.Item( identifier = captureId.finger, @@ -632,7 +645,10 @@ internal class FingerprintCaptureViewModel @Inject constructor( _finishWithFingerprints.send(FingerprintCaptureResult(resultItems)) } - private suspend fun saveImageIfExists(id: CaptureId, collectedFinger: CaptureState.Collected) { + private suspend fun saveImageIfExists( + id: CaptureId, + collectedFinger: CaptureState.ScanProcess.Collected + ) { val captureEventId = captureEventIds[id] val imageRef = saveImage( vero2Configuration = bioSdkConfiguration.vero2!!, @@ -708,7 +724,6 @@ internal class FingerprintCaptureViewModel @Inject constructor( const val targetNumberOfGoodScans = 2 const val maximumTotalNumberOfFingersForAutoAdding = 4 const val numberOfBadScansRequiredToAutoAddNewFinger = 3 - const val AUTO_SWIPE_DELAY: Long = 500 const val TRY_DIFFERENT_FINGER_SPLASH_DELAY: Long = 2000 diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/state/CaptureState.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/state/CaptureState.kt index 0a36e293ea..73398df63c 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/state/CaptureState.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/state/CaptureState.kt @@ -4,55 +4,104 @@ internal sealed class CaptureState { data object NotCollected : CaptureState() data object Skipped : CaptureState() - data class Scanning(val numberOfBadScans: Int = 0) : CaptureState() - data class TransferringImage(val scanResult: ScanResult, val numberOfBadScans: Int = 0) : CaptureState() - data class NotDetected(val numberOfBadScans: Int = 0) : CaptureState() - data class Collected(val scanResult: ScanResult, val numberOfBadScans: Int = 0) : CaptureState() + sealed class ScanProcess : CaptureState() { + abstract val numberOfBadScans: Int + abstract val numberOfNoFingerDetectedScans: Int - fun isCommunicating(): Boolean = this is Scanning || this is TransferringImage + data class Scanning( + override val numberOfBadScans: Int, + override val numberOfNoFingerDetectedScans: Int + ) : ScanProcess() + + data class TransferringImage( + override val numberOfBadScans: Int, + override val numberOfNoFingerDetectedScans: Int, + val scanResult: ScanResult, + ) : ScanProcess() + + data class NotDetected( + override val numberOfBadScans: Int, + override val numberOfNoFingerDetectedScans: Int + ) : ScanProcess() + + data class Collected( + override val numberOfBadScans: Int, + override val numberOfNoFingerDetectedScans: Int, + val scanResult: ScanResult + ) : ScanProcess() + + } + + fun isCommunicating(): Boolean = + this is ScanProcess.Scanning || this is ScanProcess.TransferringImage fun toNotCollected() = NotCollected fun toSkipped() = Skipped - fun toScanning(): Scanning = when (this) { - is Scanning -> Scanning(numberOfBadScans) - is TransferringImage -> Scanning(numberOfBadScans) - is NotDetected -> Scanning(numberOfBadScans) - is Collected -> Scanning(numberOfBadScans) - else -> Scanning() + fun toScanning(): ScanProcess.Scanning = when (this) { + is ScanProcess -> ScanProcess.Scanning( + numberOfBadScans = numberOfBadScans, + numberOfNoFingerDetectedScans = numberOfNoFingerDetectedScans + ) + + else -> ScanProcess.Scanning( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0 + ) } - fun toTransferringImage(scanResult: ScanResult): TransferringImage = when (this) { - is TransferringImage -> TransferringImage(scanResult, numberOfBadScans) - is Scanning -> TransferringImage(scanResult, numberOfBadScans) - is NotDetected -> TransferringImage(scanResult, numberOfBadScans) - is Collected -> TransferringImage(scanResult, numberOfBadScans) - else -> TransferringImage(scanResult) + fun toTransferringImage(scanResult: ScanResult): ScanProcess.TransferringImage = when (this) { + is ScanProcess -> ScanProcess.TransferringImage( + numberOfBadScans = numberOfBadScans, + numberOfNoFingerDetectedScans = numberOfNoFingerDetectedScans, + scanResult = scanResult + ) + + else -> ScanProcess.TransferringImage( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = scanResult + ) } - fun toNotDetected(): NotDetected = when (this) { - is NotDetected -> NotDetected(numberOfBadScans) - is Scanning -> NotDetected(numberOfBadScans) - is TransferringImage -> NotDetected(numberOfBadScans) - is Collected -> NotDetected(numberOfBadScans) - else -> NotDetected() + fun toNotDetected(): ScanProcess.NotDetected = when (this) { + is ScanProcess -> ScanProcess.NotDetected( + numberOfBadScans = numberOfBadScans, + numberOfNoFingerDetectedScans = numberOfNoFingerDetectedScans + 1 + ) + + else -> ScanProcess.NotDetected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0 + ) } - fun toCollected(scanResult: ScanResult): Collected = when (this) { - is Scanning -> Collected(scanResult, numberOfBadScans + incIfBadScan(scanResult)) - is TransferringImage -> Collected(scanResult, numberOfBadScans + incIfBadScan(scanResult)) - is NotDetected -> Collected(scanResult, numberOfBadScans + incIfBadScan(scanResult)) - is Collected -> Collected(scanResult, numberOfBadScans + incIfBadScan(scanResult)) - else -> Collected(scanResult, incIfBadScan(scanResult)) + fun toCollected(scanResult: ScanResult): ScanProcess.Collected = when (this) { + is ScanProcess -> ScanProcess.Collected( + numberOfBadScans = numberOfBadScans + incIfBadScan(scanResult), + numberOfNoFingerDetectedScans = numberOfNoFingerDetectedScans, + scanResult = scanResult + ) + + else -> ScanProcess.Collected( + numberOfBadScans = incIfBadScan(scanResult), + numberOfNoFingerDetectedScans = 0, + scanResult, + ) } private fun incIfBadScan(scanResult: ScanResult) = if (scanResult.isGoodScan()) 0 else 1 - fun toCollected(imageBytes: ByteArray): Collected = when (this) { - is TransferringImage -> toCollected(scanResult.copy(image = imageBytes)) - is Collected -> Collected(scanResult.copy(image = imageBytes), numberOfBadScans) + fun toCollected(imageBytes: ByteArray): ScanProcess.Collected = when (this) { + is ScanProcess.TransferringImage -> toCollected(scanResult.copy(image = imageBytes)) + is ScanProcess.Collected -> ScanProcess.Collected( + numberOfBadScans = numberOfBadScans, + numberOfNoFingerDetectedScans = numberOfNoFingerDetectedScans, + scanResult = scanResult.copy(image = imageBytes) + ) + else -> throw IllegalStateException("Illegal attempt to move to collected state without scan result") } } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCase.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCase.kt index 7563c2c5e9..6b92773ae5 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCase.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCase.kt @@ -40,7 +40,7 @@ internal class AddCaptureEventsUseCase @Inject constructor( ) val fingerprintCaptureBiometricsEvent = - if (captureState is CaptureState.Collected && (captureEvent.payload.result == Result.GOOD_SCAN || tooManyBadScans)) + if (captureState is CaptureState.ScanProcess.Collected && (captureEvent.payload.result == Result.GOOD_SCAN || tooManyBadScans)) FingerprintCaptureBiometricsEvent( createdAt = lastCaptureStartedAt, fingerprint = mapCaptureToBiometricFingerprint( @@ -61,8 +61,8 @@ internal class AddCaptureEventsUseCase @Inject constructor( private fun mapCaptureStateToResult(captureState: CaptureState) = when (captureState) { is CaptureState.Skipped -> Result.SKIPPED - is CaptureState.NotDetected -> Result.NO_FINGER_DETECTED - is CaptureState.Collected -> if (captureState.scanResult.isGoodScan()) { + is CaptureState.ScanProcess.NotDetected -> Result.NO_FINGER_DETECTED + is CaptureState.ScanProcess.Collected -> if (captureState.scanResult.isGoodScan()) { Result.GOOD_SCAN } else { Result.BAD_QUALITY @@ -73,7 +73,7 @@ internal class AddCaptureEventsUseCase @Inject constructor( private fun mapCaptureStateToFingerprint(captureState: CaptureState, fingerState: FingerState) = captureState - .let { it as? CaptureState.Collected } + .let { it as? CaptureState.ScanProcess.Collected } ?.scanResult ?.let { FingerprintCapturePayload.Fingerprint( diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/IsNoFingerDetectedLimitReachedUseCase.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/IsNoFingerDetectedLimitReachedUseCase.kt new file mode 100644 index 0000000000..fb446b01a8 --- /dev/null +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/IsNoFingerDetectedLimitReachedUseCase.kt @@ -0,0 +1,25 @@ +package com.simprints.fingerprint.capture.usecase + +import com.simprints.fingerprint.capture.state.CaptureState +import com.simprints.infra.config.store.models.FingerprintConfiguration +import javax.inject.Inject + +internal class IsNoFingerDetectedLimitReachedUseCase @Inject constructor() { + operator fun invoke( + fingerState: CaptureState, + sdkConfiguration: FingerprintConfiguration.FingerprintSdkConfiguration + ): Boolean = when (fingerState) { + is CaptureState.ScanProcess -> { + val noFingerDetectedThreshold = + sdkConfiguration.maxCaptureAttempts?.noFingerDetected?.takeIf { it > 1 } + ?: MAXIMUM_LIMIT_OF_NO_FINGER_DETECTED_SCANS + fingerState.numberOfNoFingerDetectedScans >= noFingerDetectedThreshold + } + + else -> false + } + + companion object { + const val MAXIMUM_LIMIT_OF_NO_FINGER_DETECTED_SCANS = 40 // current maximum value in Vulcan + } +} diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/SaveImageUseCase.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/SaveImageUseCase.kt index ec4b2b0f0e..7fe343cc71 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/SaveImageUseCase.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/usecase/SaveImageUseCase.kt @@ -22,7 +22,7 @@ internal class SaveImageUseCase @Inject constructor( vero2Configuration: Vero2Configuration, finger: IFingerIdentifier, captureEventId: String?, - collectedFinger: CaptureState.Collected, + collectedFinger: CaptureState.ScanProcess.Collected, ) = if (collectedFinger.scanResult.image != null && captureEventId != null) { saveImage( imageBytes = collectedFinger.scanResult.image, diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerFragment.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerFragment.kt index b362daacc4..16291bf240 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerFragment.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/views/fingerviewpager/FingerFragment.kt @@ -141,12 +141,12 @@ internal class FingerFragment : Fragment(R.layout.fragment_finger) { ) } - is CaptureState.Scanning -> startTimeoutBar() - is CaptureState.TransferringImage -> { + is CaptureState.ScanProcess.Scanning -> startTimeoutBar() + is CaptureState.ScanProcess.TransferringImage -> { //Do nothing } - is CaptureState.NotDetected -> { + is CaptureState.ScanProcess.NotDetected -> { handleCancelled() progressBar.progressDrawable = ContextCompat.getDrawable( requireContext(), @@ -154,7 +154,7 @@ internal class FingerFragment : Fragment(R.layout.fragment_finger) { ) } - is CaptureState.Collected -> if (fingerState.scanResult.isGoodScan()) { + is CaptureState.ScanProcess.Collected -> if (fingerState.scanResult.isGoodScan()) { handleCancelled() progressBar.progressDrawable = ContextCompat.getDrawable( requireContext(), diff --git a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModelTest.kt b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModelTest.kt index e69efa9b4b..740e7a6585 100644 --- a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModelTest.kt +++ b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureViewModelTest.kt @@ -19,6 +19,7 @@ import com.simprints.fingerprint.capture.state.ScanResult import com.simprints.fingerprint.capture.usecase.AddCaptureEventsUseCase import com.simprints.fingerprint.capture.usecase.GetNextFingerToAddUseCase import com.simprints.fingerprint.capture.usecase.GetStartStateUseCase +import com.simprints.fingerprint.capture.usecase.IsNoFingerDetectedLimitReachedUseCase import com.simprints.fingerprint.capture.usecase.SaveImageUseCase import com.simprints.fingerprint.infra.basebiosdk.exceptions.BioSdkException import com.simprints.fingerprint.infra.biosdk.BioSdkWrapper @@ -98,9 +99,13 @@ class FingerprintCaptureViewModelTest { @MockK private lateinit var addCaptureEventsUseCase: AddCaptureEventsUseCase + @MockK + private lateinit var isNoFingerDetectedLimitReachedUseCase: IsNoFingerDetectedLimitReachedUseCase + private val getStartStateUseCase = GetStartStateUseCase() private val getNextFingerToAddUseCase = GetNextFingerToAddUseCase() + private lateinit var vm: FingerprintCaptureViewModel @Before @@ -118,6 +123,7 @@ class FingerprintCaptureViewModelTest { } coEvery { addCaptureEventsUseCase.invoke(any(), any(), any(), any()) } returns "payloadId" + coEvery { isNoFingerDetectedLimitReachedUseCase.invoke(any(), any()) } returns false every { scanner.isLiveFeedbackAvailable() } returns false every { scanner.isImageTransferSupported() } returns true @@ -130,15 +136,16 @@ class FingerprintCaptureViewModelTest { every { bioSdkWrapper.imageTransferTimeoutMs } returns 1000 vm = FingerprintCaptureViewModel( - scannerManager, - configManager, - timeHelper, - resolveBioSdkWrapperUseCase, - saveImageUseCase, - getNextFingerToAddUseCase, - getStartStateUseCase, - addCaptureEventsUseCase, - CoroutineScope(testCoroutineRule.testCoroutineDispatcher), + scannerManager = scannerManager, + configManager = configManager, + timeHelper = timeHelper, + resolveBioSdkWrapperUseCase = resolveBioSdkWrapperUseCase, + saveImage = saveImageUseCase, + getNextFingerToAdd = getNextFingerToAddUseCase, + getStartState = getStartStateUseCase, + addCaptureEvents = addCaptureEventsUseCase, + isNoFingerDetectedLimitReachedUseCase = isNoFingerDetectedLimitReachedUseCase, + externalScope = CoroutineScope(testCoroutineRule.testCoroutineDispatcher), ) } @@ -183,7 +190,7 @@ class FingerprintCaptureViewModelTest { vm.handleOnViewCreated(TWO_FINGERS_IDS, SECUGEN_SIM_MATCHER) vm.handleScanButtonPressed() - assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo(CaptureState.Scanning()) + assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo(CaptureState.ScanProcess.Scanning(0, 0)) } @Test @@ -214,8 +221,10 @@ class FingerprintCaptureViewModelTest { vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.TransferringImage( - ScanResult( + CaptureState.ScanProcess.TransferringImage( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 ) ) @@ -233,8 +242,10 @@ class FingerprintCaptureViewModelTest { vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.Collected( - ScanResult( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 ) ) @@ -259,8 +270,10 @@ class FingerprintCaptureViewModelTest { vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.Collected( - ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60) + CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60) ) ) vm.vibrate.assertEventReceived() @@ -281,10 +294,12 @@ class FingerprintCaptureViewModelTest { vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.Collected( - ScanResult( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 1, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 - ), 1 + ) ) ) vm.vibrate.assertEventReceived() @@ -302,10 +317,12 @@ class FingerprintCaptureViewModelTest { vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.Collected( - ScanResult( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 1, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 - ), 1 + ) ) ) vm.vibrate.assertEventReceived() @@ -322,7 +339,12 @@ class FingerprintCaptureViewModelTest { vm.handleOnViewCreated(TWO_FINGERS_IDS, SECUGEN_SIM_MATCHER) vm.handleScanButtonPressed() - assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo(CaptureState.NotDetected()) + assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( + CaptureState.ScanProcess.NotDetected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 1, + ) + ) vm.vibrate.assertEventReceived() coVerify { addCaptureEventsUseCase.invoke(any(), any(), any(), any()) } } @@ -368,26 +390,32 @@ class FingerprintCaptureViewModelTest { vm.handleOnViewCreated(TWO_FINGERS_IDS, SECUGEN_SIM_MATCHER) vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.Collected( - ScanResult( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 1, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 - ), 1 + ) ) ) vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.Collected( - ScanResult( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 2, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 - ), 2 + ) ) ) vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.Collected( - ScanResult( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 3, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60 - ), 3 + ) ) ) @@ -423,12 +451,13 @@ class FingerprintCaptureViewModelTest { fingerStates = FOUR_FINGERS_IDS.map { FingerState( it, listOf( - CaptureState.Collected( - ScanResult(BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), - numberOfBadScans = 3 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 3, + numberOfNoFingerDetectedScans = 0, + ScanResult(BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), + ) ) ) - ) }, currentFingerIndex = 3, isAskingRescan = false, @@ -472,13 +501,15 @@ class FingerprintCaptureViewModelTest { fingerStates = TWO_FINGERS_IDS.map { FingerState( it, listOf( - CaptureState.Collected( - ScanResult( - GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( + GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60 + ) ) ) ) - ) }, currentFingerIndex = 1, isAskingRescan = false, @@ -522,13 +553,15 @@ class FingerprintCaptureViewModelTest { fingerStates = TWO_FINGERS_IDS.map { FingerState( it, listOf( - CaptureState.Collected( - ScanResult( - GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( + GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 + ) ) ) ) - ) }, currentFingerIndex = 1, isAskingRescan = false, @@ -574,8 +607,10 @@ class FingerprintCaptureViewModelTest { vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.isAskingRescan).isFalse() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.Collected( - ScanResult( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( DIFFERENT_GOOD_QUALITY, DIFFERENT_TEMPLATE, TEMPLATE_FORMAT, null, 60 ) ) @@ -701,9 +736,10 @@ class FingerprintCaptureViewModelTest { FingerState( FOUR_FINGERS_IDS[0], listOf( - CaptureState.Collected( - ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), - numberOfBadScans = 2 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 2, + numberOfNoFingerDetectedScans = 1, + scanResult = ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), ) ) ), @@ -711,18 +747,20 @@ class FingerprintCaptureViewModelTest { FingerState( FOUR_FINGERS_IDS[2], listOf( - CaptureState.Collected( - ScanResult(BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), - numberOfBadScans = 3 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 3, + numberOfNoFingerDetectedScans = 1, + scanResult = ScanResult(BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), ) ) ), FingerState( FOUR_FINGERS_IDS[3], listOf( - CaptureState.Collected( - ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), - numberOfBadScans = 0 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 1, + scanResult = ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), ) ) ) @@ -809,9 +847,10 @@ class FingerprintCaptureViewModelTest { FingerState( FOUR_FINGERS_IDS[0], listOf( - CaptureState.Collected( - ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), - numberOfBadScans = 2 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 2, + numberOfNoFingerDetectedScans = 1, + scanResult = ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), ) ) ), @@ -819,18 +858,20 @@ class FingerprintCaptureViewModelTest { FingerState( FOUR_FINGERS_IDS[2], listOf( - CaptureState.Collected( - ScanResult(BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), - numberOfBadScans = 3 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 3, + numberOfNoFingerDetectedScans = 1, + scanResult = ScanResult(BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), ) ) ), FingerState( FOUR_FINGERS_IDS[3], listOf( - CaptureState.Collected( - ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), - numberOfBadScans = 0 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 1, + scanResult = ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), ) ) ) @@ -906,27 +947,30 @@ class FingerprintCaptureViewModelTest { FingerState( FOUR_FINGERS_IDS[0], listOf( - CaptureState.Collected( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 3, + numberOfNoFingerDetectedScans = 0, ScanResult(BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60), - numberOfBadScans = 3 ) ) ), FingerState( FOUR_FINGERS_IDS[1], listOf( - CaptureState.Collected( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 1, + numberOfNoFingerDetectedScans = 0, ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), - numberOfBadScans = 1 ) ) ), FingerState( FOUR_FINGERS_IDS[2], listOf( - CaptureState.Collected( + CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60), - numberOfBadScans = 0 ) ) ) @@ -967,7 +1011,8 @@ class FingerprintCaptureViewModelTest { vm.handleOnViewCreated(TWO_FINGERS_IDS, SECUGEN_SIM_MATCHER) vm.handleScanButtonPressed() - assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo(CaptureState.Scanning()) + assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo(CaptureState.ScanProcess.Scanning(numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0,)) vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo(CaptureState.NotCollected) } @@ -983,15 +1028,23 @@ class FingerprintCaptureViewModelTest { vm.handleOnViewCreated(TWO_FINGERS_IDS, SECUGEN_SIM_MATCHER) vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.TransferringImage( - ScanResult( - GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 + CaptureState.ScanProcess.TransferringImage( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( + qualityScore = GOOD_QUALITY, + template = TEMPLATE, + templateFormat = TEMPLATE_FORMAT, + image = null, + qualityThreshold = 60 ) ) ) vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.TransferringImage( + CaptureState.ScanProcess.TransferringImage( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, ScanResult( GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 ) @@ -1040,7 +1093,7 @@ class FingerprintCaptureViewModelTest { vm.handleOnViewCreated(TWO_FINGERS_IDS, SECUGEN_SIM_MATCHER) vm.handleScanButtonPressed() - assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo(CaptureState.Scanning()) + assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo(CaptureState.ScanProcess.Scanning(0, 0)) vm.handleOnBackPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo(CaptureState.NotCollected) } @@ -1067,7 +1120,9 @@ class FingerprintCaptureViewModelTest { vm.handleOnViewCreated(TWO_FINGERS_IDS, SECUGEN_SIM_MATCHER) vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.TransferringImage( + CaptureState.ScanProcess.TransferringImage( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, ScanResult( GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 ) @@ -1088,9 +1143,15 @@ class FingerprintCaptureViewModelTest { vm.handleScanButtonPressed() vm.handleOnBackPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.Collected( - ScanResult( - GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60 + CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( + qualityScore = GOOD_QUALITY, + template = TEMPLATE, + templateFormat = TEMPLATE_FORMAT, + image = null, + qualityThreshold = 60 ) ) ) @@ -1307,7 +1368,10 @@ class FingerprintCaptureViewModelTest { vm.handleScanButtonPressed() assertThat(vm.stateLiveData.value?.currentCaptureState()).isEqualTo( - CaptureState.NotDetected(3) + /* expected = */ CaptureState.ScanProcess.NotDetected( + numberOfBadScans = 3, + numberOfNoFingerDetectedScans = 1 + ) ) coVerify(exactly = 4) { addCaptureEventsUseCase.invoke(any(), any(), any(), any()) } diff --git a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCaseTest.kt b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCaseTest.kt index 136b1d3116..2c39d4d6e0 100644 --- a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCaseTest.kt +++ b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/AddCaptureEventsUseCaseTest.kt @@ -58,8 +58,10 @@ internal class AddCaptureEventsUseCaseTest { useCase.invoke( Timestamp(1L), FingerState( - IFingerIdentifier.LEFT_THUMB, listOf(CaptureState.Collected( - ScanResult(0, byteArrayOf(), "", null, 10))) + IFingerIdentifier.LEFT_THUMB, listOf(CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult(0, byteArrayOf(), "", null, 10))) ), 10, false @@ -74,8 +76,10 @@ internal class AddCaptureEventsUseCaseTest { useCase.invoke( Timestamp(1L), FingerState( - IFingerIdentifier.LEFT_THUMB, listOf(CaptureState.Collected( - ScanResult(100, byteArrayOf(), "", null, 10))) + IFingerIdentifier.LEFT_THUMB, listOf(CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult(100, byteArrayOf(), "", null, 10))) ), 10, false @@ -93,8 +97,10 @@ internal class AddCaptureEventsUseCaseTest { useCase.invoke( Timestamp(1L), FingerState( - IFingerIdentifier.LEFT_THUMB, listOf(CaptureState.Collected( - ScanResult(0, byteArrayOf(), "", null, 10))) + IFingerIdentifier.LEFT_THUMB, listOf(CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult(0, byteArrayOf(), "", null, 10))) ), 10, true diff --git a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/IsNoFingerDetectedLimitReachedUseCaseTest.kt b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/IsNoFingerDetectedLimitReachedUseCaseTest.kt new file mode 100644 index 0000000000..6c1ad5d42a --- /dev/null +++ b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/IsNoFingerDetectedLimitReachedUseCaseTest.kt @@ -0,0 +1,87 @@ +package com.simprints.fingerprint.capture.usecase + +import com.google.common.truth.Truth.assertThat +import com.simprints.fingerprint.capture.state.CaptureState +import com.simprints.infra.config.store.models.FingerprintConfiguration.FingerprintSdkConfiguration +import com.simprints.infra.config.store.models.MaxCaptureAttempts +import io.mockk.every +import io.mockk.mockk +import org.junit.Test + + +internal class IsNoFingerDetectedLimitReachedUseCaseTest { + private val isNoFingerDetectedLimitReachedUseCase = IsNoFingerDetectedLimitReachedUseCase() + + @Test + fun `when capture state is not ScanProcess, then returns false`() { + val fingerStateNotCollected = mockk() + val fingerStateSkipped = mockk() + val sdkConfiguration = mockk() + + listOf(fingerStateNotCollected, fingerStateSkipped).forEach { fingerState -> + assertThat( + isNoFingerDetectedLimitReachedUseCase( + fingerState = fingerState, + sdkConfiguration = sdkConfiguration + ) + ).isFalse() + } + } + + @Test + fun `when max capture attempts is null, then inclusive MAXIMUM_LIMIT_OF_NO_FINGER_DETECTED_SCANS is used`() { + val fingerState = mockk { + every { numberOfNoFingerDetectedScans } returns IsNoFingerDetectedLimitReachedUseCase.MAXIMUM_LIMIT_OF_NO_FINGER_DETECTED_SCANS + } + val sdkConfiguration = mockk { + every { maxCaptureAttempts } returns null + } + assertThat( + isNoFingerDetectedLimitReachedUseCase( + fingerState = fingerState, + sdkConfiguration = sdkConfiguration + ) + ).isTrue() + } + + @Test + fun `when number of no finger detected scans is greater than no finger detected threshold, then true is returned`() { + val noFingerDetectedThreshold = 3 + val noFingerDetectedScans = noFingerDetectedThreshold + val fingerState = mockk { + every { numberOfNoFingerDetectedScans } returns noFingerDetectedScans + } + val sdkConfiguration = mockk { + every { maxCaptureAttempts } returns mockk { + every { noFingerDetected } returns noFingerDetectedThreshold + } + } + assertThat( + isNoFingerDetectedLimitReachedUseCase( + fingerState = fingerState, + sdkConfiguration = sdkConfiguration + ) + ).isTrue() + } + + @Test + fun `when threshold number of no finger detected scans is lower than 2, then MAXIMUM_LIMIT_OF_NO_FINGER_DETECTED_SCANS is used`() { + val noFingerDetectedThreshold = + IsNoFingerDetectedLimitReachedUseCase.MAXIMUM_LIMIT_OF_NO_FINGER_DETECTED_SCANS + val noFingerDetectedScans = noFingerDetectedThreshold - 1 + val fingerState = mockk { + every { numberOfNoFingerDetectedScans } returns noFingerDetectedScans + } + val sdkConfiguration = mockk { + every { maxCaptureAttempts } returns mockk { + every { noFingerDetected } returns 1 + } + } + assertThat( + isNoFingerDetectedLimitReachedUseCase( + fingerState = fingerState, + sdkConfiguration = sdkConfiguration + ) + ).isFalse() + } +} diff --git a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/SaveImageUseCaseTest.kt b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/SaveImageUseCaseTest.kt index c02a4c7360..b188afece5 100644 --- a/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/SaveImageUseCaseTest.kt +++ b/fingerprint/capture/src/test/java/com/simprints/fingerprint/capture/usecase/SaveImageUseCaseTest.kt @@ -132,14 +132,15 @@ class SaveImageUseCaseTest { coVerify { imageRepo.storeImageSecurely(any(), "projectId", any(), any()) } } - private fun createCollectedStub(image: ByteArray?) = CaptureState.Collected( - ScanResult( + private fun createCollectedStub(image: ByteArray?) = CaptureState.ScanProcess.Collected( + numberOfBadScans = 0, + numberOfNoFingerDetectedScans = 0, + scanResult = ScanResult( 0, byteArrayOf(), "format", image, 10, ), - 0 ) } diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/ConfigLocalDataSourceImpl.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/ConfigLocalDataSourceImpl.kt index cd3ff29918..47a8f03ef1 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/ConfigLocalDataSourceImpl.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/ConfigLocalDataSourceImpl.kt @@ -155,6 +155,7 @@ internal class ConfigLocalDataSourceImpl @Inject constructor( comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(60), vero2 = null, + maxCaptureAttempts = null ), nec = null, ), diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/migrations/models/OldProjectConfig.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/migrations/models/OldProjectConfig.kt index a0c5934030..48a42c5046 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/migrations/models/OldProjectConfig.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/migrations/models/OldProjectConfig.kt @@ -141,6 +141,7 @@ internal data class OldProjectConfig( ?: FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(fingerprintQualityThreshold.toInt()), vero2 = vero2Configuration(), + maxCaptureAttempts = null ), nec = null, ) diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/FingerprintConfiguration.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/FingerprintConfiguration.kt index 3d613ac4ec..35632c4e45 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/FingerprintConfiguration.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/local/models/FingerprintConfiguration.kt @@ -1,8 +1,10 @@ package com.simprints.infra.config.store.local.models import com.simprints.infra.config.store.exceptions.InvalidProtobufEnumException +import com.simprints.infra.config.store.local.models.ProtoFingerprintConfiguration.ProtoMaxCaptureAttempts import com.simprints.infra.config.store.models.AgeGroup import com.simprints.infra.config.store.models.FingerprintConfiguration +import com.simprints.infra.config.store.models.MaxCaptureAttempts internal fun FingerprintConfiguration.toProto(): ProtoFingerprintConfiguration = ProtoFingerprintConfiguration.newBuilder() @@ -25,6 +27,7 @@ internal fun FingerprintConfiguration.FingerprintSdkConfiguration.toProto() = if (vero1 != null) it.vero1 = vero1.toProto() if (vero2 != null) it.vero2 = vero2.toProto() if (verificationMatchThreshold != null) it.verificationMatchThreshold = verificationMatchThreshold + if (maxCaptureAttempts != null) it.maxCaptureAttempts = maxCaptureAttempts.toProto() }.build() @@ -43,6 +46,8 @@ internal fun FingerprintConfiguration.FingerComparisonStrategy.toProto() = when FingerprintConfiguration.FingerComparisonStrategy.CROSS_FINGER_USING_MEAN_OF_MAX -> ProtoFingerprintConfiguration.FingerComparisonStrategy.CROSS_FINGER_USING_MEAN_OF_MAX } +internal fun MaxCaptureAttempts.toProto() = ProtoMaxCaptureAttempts.newBuilder().setNoFingerDetected(noFingerDetected).build() + internal fun ProtoFingerprintConfiguration.toDomain() = // if has nec or sim matcher then it's a new config @@ -62,6 +67,7 @@ internal fun ProtoFingerprintConfiguration.toDomainOld() = FingerprintConfigurat comparisonStrategyForVerification = comparisonStrategyForVerification.toDomain(), vero1 = vero1.toDomain(), vero2 = vero2.toDomain(), + maxCaptureAttempts = null ), nec = null, displayHandIcons = displayHandIcons, @@ -92,8 +98,12 @@ internal fun ProtoFingerprintConfiguration.ProtoFingerprintSdkConfiguration.toDo vero2 = if (hasVero2()) vero2.toDomain() else null, allowedAgeRange = if (hasAllowedAgeRange()) allowedAgeRange.toDomain() else AgeGroup(0, null), verificationMatchThreshold = if (hasVerificationMatchThreshold()) verificationMatchThreshold else null, + maxCaptureAttempts = maxCaptureAttempts.toDomain() ) +internal fun ProtoFingerprintConfiguration.ProtoMaxCaptureAttempts.toDomain() = MaxCaptureAttempts( + noFingerDetected = noFingerDetected +) internal fun ProtoFingerprintConfiguration.VeroGeneration.toDomain() = when (this) { ProtoFingerprintConfiguration.VeroGeneration.VERO_1 -> FingerprintConfiguration.VeroGeneration.VERO_1 diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/models/FingerprintConfiguration.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/FingerprintConfiguration.kt index db9a36e91d..71b22c942b 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/models/FingerprintConfiguration.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/FingerprintConfiguration.kt @@ -16,6 +16,10 @@ data class FingerprintConfiguration( val vero2: Vero2Configuration? = null, val allowedAgeRange: AgeGroup = AgeGroup(0, null), val verificationMatchThreshold: Float? = null, + /** + * Allowed amount of 'No Finger Detected' scans before proceeding further + */ + val maxCaptureAttempts: MaxCaptureAttempts? ) enum class VeroGeneration { diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/models/MaxCaptureAttempts.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/MaxCaptureAttempts.kt new file mode 100644 index 0000000000..535aa5b08e --- /dev/null +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/models/MaxCaptureAttempts.kt @@ -0,0 +1,5 @@ +package com.simprints.infra.config.store.models + +data class MaxCaptureAttempts( + val noFingerDetected: Int +) \ No newline at end of file diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfiguration.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfiguration.kt index 4c355a8ea0..4ac69abf95 100644 --- a/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfiguration.kt +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfiguration.kt @@ -31,6 +31,7 @@ internal data class ApiFingerprintConfiguration( val vero2: ApiVero2Configuration? = null, val allowedAgeRange: ApiAllowedAgeRange? = null, val verificationMatchThreshold: Float? = null, + val maxCaptureAttempts: ApiMaxCaptureAttempts? = null ) { fun toDomain() = FingerprintConfiguration.FingerprintSdkConfiguration( fingersToCapture = fingersToCapture.map { it.toDomain() }, @@ -40,6 +41,7 @@ internal data class ApiFingerprintConfiguration( vero2 = vero2?.toDomain(), allowedAgeRange = allowedAgeRange?.toDomain() ?: AgeGroup(0, null), verificationMatchThreshold = verificationMatchThreshold, + maxCaptureAttempts = maxCaptureAttempts?.toDomain() ) } diff --git a/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiMaxCaptureAttempts.kt b/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiMaxCaptureAttempts.kt new file mode 100644 index 0000000000..eb34f9b578 --- /dev/null +++ b/infra/config-store/src/main/java/com/simprints/infra/config/store/remote/models/ApiMaxCaptureAttempts.kt @@ -0,0 +1,13 @@ +package com.simprints.infra.config.store.remote.models + +import androidx.annotation.Keep +import com.simprints.infra.config.store.models.MaxCaptureAttempts + +@Keep +internal data class ApiMaxCaptureAttempts( + val noFingerDetected: Int +) { + fun toDomain(): MaxCaptureAttempts = MaxCaptureAttempts( + noFingerDetected = noFingerDetected + ) +} diff --git a/infra/config-store/src/main/proto/project_config.proto b/infra/config-store/src/main/proto/project_config.proto index b239d1bebb..74d0727ebb 100644 --- a/infra/config-store/src/main/proto/project_config.proto +++ b/infra/config-store/src/main/proto/project_config.proto @@ -96,6 +96,11 @@ message ProtoFingerprintConfiguration { optional ProtoVero1Configuration vero_1 = 5; optional ProtoAllowedAgeRange allowed_age_range = 6; optional float verification_match_threshold = 7; + ProtoMaxCaptureAttempts max_capture_attempts = 8; + } + + message ProtoMaxCaptureAttempts { + int32 no_finger_detected = 1; } } diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/models/FingerprintConfigurationTest.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/models/FingerprintConfigurationTest.kt index a905024d50..84d83df7aa 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/models/FingerprintConfigurationTest.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/models/FingerprintConfigurationTest.kt @@ -18,6 +18,7 @@ class FingerprintConfigurationTest { comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(60), vero2 = null, + maxCaptureAttempts = MaxCaptureAttempts(noFingerDetected = 17) ), nec = null, ) @@ -39,6 +40,7 @@ class FingerprintConfigurationTest { comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(60), vero2 = null, + maxCaptureAttempts = MaxCaptureAttempts(noFingerDetected = 17) ), ) Truth.assertThat(fingerprintConfiguration.getSdkConfiguration(FingerprintConfiguration.BioSdk.NEC)) diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfigurationTest.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfigurationTest.kt index 480f749c81..d6148c0a6d 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfigurationTest.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/remote/models/ApiFingerprintConfigurationTest.kt @@ -4,9 +4,11 @@ import com.google.common.truth.Truth.assertThat import com.simprints.infra.config.store.models.AgeGroup import com.simprints.infra.config.store.models.Finger import com.simprints.infra.config.store.models.FingerprintConfiguration +import com.simprints.infra.config.store.models.MaxCaptureAttempts import com.simprints.infra.config.store.models.Vero1Configuration import com.simprints.infra.config.store.testtools.apiDecisionPolicy import com.simprints.infra.config.store.testtools.apiFingerprintConfiguration +import com.simprints.infra.config.store.testtools.apiMaxCaptureAttempts import com.simprints.infra.config.store.testtools.decisionPolicy import com.simprints.infra.config.store.testtools.fingerprintConfiguration import org.junit.Test @@ -40,12 +42,13 @@ class ApiFingerprintConfigurationTest { listOf(ApiFingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER), true, ApiFingerprintConfiguration.ApiFingerprintSdkConfiguration( - listOf(ApiFingerprintConfiguration.Finger.LEFT_3RD_FINGER), - apiDecisionPolicy, - ApiFingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, - null, - apiFingerprintConfiguration.secugenSimMatcher?.vero2, - apiFingerprintConfiguration.secugenSimMatcher?.allowedAgeRange!!, + fingersToCapture = listOf(ApiFingerprintConfiguration.Finger.LEFT_3RD_FINGER), + decisionPolicy = apiDecisionPolicy, + comparisonStrategyForVerification = ApiFingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, + vero1 = null, + vero2 = apiFingerprintConfiguration.secugenSimMatcher?.vero2, + allowedAgeRange = apiFingerprintConfiguration.secugenSimMatcher?.allowedAgeRange!!, + maxCaptureAttempts = apiMaxCaptureAttempts ), null, ) @@ -54,12 +57,14 @@ class ApiFingerprintConfigurationTest { listOf(FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER), true, FingerprintConfiguration.FingerprintSdkConfiguration( - listOf(Finger.LEFT_3RD_FINGER), - decisionPolicy, - FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, - null, - fingerprintConfiguration.secugenSimMatcher?.vero2, - fingerprintConfiguration.secugenSimMatcher?.allowedAgeRange!!, + fingersToCapture = listOf(Finger.LEFT_3RD_FINGER), + decisionPolicy = decisionPolicy, + comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, + vero1 = null, + vero2 = fingerprintConfiguration.secugenSimMatcher?.vero2, + allowedAgeRange = fingerprintConfiguration.secugenSimMatcher?.allowedAgeRange!!, + verificationMatchThreshold = null, + maxCaptureAttempts = MaxCaptureAttempts(noFingerDetected = 17) ), null, ) @@ -75,12 +80,13 @@ class ApiFingerprintConfigurationTest { true, null, ApiFingerprintConfiguration.ApiFingerprintSdkConfiguration( - listOf(ApiFingerprintConfiguration.Finger.LEFT_3RD_FINGER), - apiDecisionPolicy, - ApiFingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, - ApiVero1Configuration(10), - null, - apiFingerprintConfiguration.secugenSimMatcher?.allowedAgeRange!! + fingersToCapture = listOf(ApiFingerprintConfiguration.Finger.LEFT_3RD_FINGER), + decisionPolicy = apiDecisionPolicy, + comparisonStrategyForVerification = ApiFingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, + vero1 = ApiVero1Configuration(10), + vero2 = null, + allowedAgeRange = apiFingerprintConfiguration.secugenSimMatcher?.allowedAgeRange!!, + maxCaptureAttempts = apiMaxCaptureAttempts ), ) @@ -90,12 +96,14 @@ class ApiFingerprintConfigurationTest { true, null, FingerprintConfiguration.FingerprintSdkConfiguration( - listOf(Finger.LEFT_3RD_FINGER), - decisionPolicy, - FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, - Vero1Configuration(10), - null, - fingerprintConfiguration.secugenSimMatcher?.allowedAgeRange!! + fingersToCapture = listOf(Finger.LEFT_3RD_FINGER), + decisionPolicy = decisionPolicy, + comparisonStrategyForVerification = FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, + vero1 = Vero1Configuration(10), + vero2 = null, + allowedAgeRange = fingerprintConfiguration.secugenSimMatcher?.allowedAgeRange!!, + verificationMatchThreshold = null, + maxCaptureAttempts = MaxCaptureAttempts(noFingerDetected = 17) ), ) diff --git a/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt b/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt index 2358d504f2..1c9ffb3edb 100644 --- a/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt +++ b/infra/config-store/src/test/java/com/simprints/infra/config/store/testtools/Models.kt @@ -9,6 +9,7 @@ import com.simprints.infra.config.store.local.models.ProtoDownSynchronizationCon import com.simprints.infra.config.store.local.models.ProtoFaceConfiguration import com.simprints.infra.config.store.local.models.ProtoFinger import com.simprints.infra.config.store.local.models.ProtoFingerprintConfiguration +import com.simprints.infra.config.store.local.models.ProtoFingerprintConfiguration.ProtoMaxCaptureAttempts import com.simprints.infra.config.store.local.models.ProtoGeneralConfiguration import com.simprints.infra.config.store.local.models.ProtoIdentificationConfiguration import com.simprints.infra.config.store.local.models.ProtoProject @@ -105,6 +106,7 @@ internal val protoConsentConfiguration = ProtoConsentConfiguration.newBuilder() .build() internal val apiAllowedAgeRange = ApiAllowedAgeRange(2, 10) +internal val apiMaxCaptureAttempts = ApiMaxCaptureAttempts(noFingerDetected = 17) internal val allowedAgeRange = AgeGroup(2, 10) internal val protoAllowedAgeRange = ProtoAllowedAgeRange.newBuilder().setStartInclusive(2).setEndExclusive(10).build() @@ -186,13 +188,14 @@ internal val apiFingerprintConfiguration = ApiFingerprintConfiguration( allowedSDKs = listOf(ApiFingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER), displayHandIcons = true, secugenSimMatcher = ApiFingerprintConfiguration.ApiFingerprintSdkConfiguration( - listOf(ApiFingerprintConfiguration.Finger.LEFT_3RD_FINGER), - apiDecisionPolicy, - ApiFingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, - ApiVero1Configuration(10), - apiVero2Configuration, - apiAllowedAgeRange, - 42.0f, + fingersToCapture = listOf(ApiFingerprintConfiguration.Finger.LEFT_3RD_FINGER), + decisionPolicy = apiDecisionPolicy, + comparisonStrategyForVerification = ApiFingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, + vero1 = ApiVero1Configuration(10), + vero2 = apiVero2Configuration, + allowedAgeRange = apiAllowedAgeRange, + verificationMatchThreshold = 42.0f, + maxCaptureAttempts = apiMaxCaptureAttempts ), nec = null, ) @@ -209,6 +212,7 @@ internal val fingerprintConfiguration = FingerprintConfiguration( vero2 = vero2Configuration, allowedAgeRange = allowedAgeRange, verificationMatchThreshold = 42.0f, + maxCaptureAttempts = MaxCaptureAttempts(noFingerDetected = 17) ), nec = null, ) @@ -226,6 +230,7 @@ internal val protoFingerprintConfiguration = ProtoFingerprintConfiguration.newBu .setVero2(protoVero2Configuration) .setAllowedAgeRange(protoAllowedAgeRange) .setVerificationMatchThreshold(42.0f) + .setMaxCaptureAttempts(ProtoMaxCaptureAttempts.newBuilder().setNoFingerDetected(17)) .build() ) .build() diff --git a/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt b/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt index e0934123da..7ff0cc1756 100644 --- a/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt +++ b/infra/sync/src/test/java/com/simprints/infra/sync/config/testtools/Models.kt @@ -1,6 +1,7 @@ package com.simprints.infra.sync.config.testtools import com.simprints.core.domain.tokenization.asTokenizableEncrypted +import com.simprints.infra.config.store.models.AgeGroup import com.simprints.infra.config.store.models.ConsentConfiguration import com.simprints.infra.config.store.models.DecisionPolicy import com.simprints.infra.config.store.models.DownSynchronizationConfiguration @@ -9,6 +10,7 @@ import com.simprints.infra.config.store.models.Finger import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.store.models.GeneralConfiguration import com.simprints.infra.config.store.models.IdentificationConfiguration +import com.simprints.infra.config.store.models.MaxCaptureAttempts import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.store.models.ProjectConfiguration import com.simprints.infra.config.store.models.ProjectState @@ -58,6 +60,10 @@ internal val fingerprintConfiguration = FingerprintConfiguration( FingerprintConfiguration.FingerComparisonStrategy.SAME_FINGER, vero1 = Vero1Configuration(10), vero2 = vero2Configuration, + allowedAgeRange = AgeGroup(0, null), + verificationMatchThreshold = 42.0f, + maxCaptureAttempts = MaxCaptureAttempts(noFingerDetected = 17) + ), nec = null, )