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
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ internal class FaceCaptureViewModel @Inject constructor(
fun flowFinished() {
viewModelScope.launch {
val projectConfiguration = configRepository.getProjectConfiguration()
if (projectConfiguration.face?.imageSavingStrategy == FaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN) {
if (projectConfiguration.face?.imageSavingStrategy?.shouldSaveImage() == true) {
saveFaceDetections()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class FaceCaptureViewModelTest {

@Test
fun `Save face detections should be called when image saving strategy set to ONLY_GOO_SCAN`() {
coEvery { configRepository.getProjectConfiguration().face?.imageSavingStrategy } returns ImageSavingStrategy.ONLY_GOOD_SCAN
coEvery { configRepository.getProjectConfiguration().face?.imageSavingStrategy } returns ImageSavingStrategy.ONLY_USED_IN_REFERENCE

viewModel.captureFinished(faceDetections)
viewModel.flowFinished()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.simprints.infra.config.store.models.Vero2Configuration.ImageSavingStr

internal fun ImageSavingStrategy.deduceFileExtension(): String = when (this) {
ImageSavingStrategy.NEVER -> ""
ImageSavingStrategy.ONLY_USED_IN_REFERENCE,
ImageSavingStrategy.ONLY_GOOD_SCAN,
ImageSavingStrategy.EAGER -> "wsq"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ import com.simprints.fingerprint.infra.scanner.exceptions.safe.ScannerOperationI
import com.simprints.fingerprint.infra.scanner.wrapper.ScannerWrapper
import com.simprints.infra.config.store.ConfigRepository
import com.simprints.infra.config.store.models.FingerprintConfiguration
import com.simprints.infra.config.store.models.Vero2Configuration.ImageSavingStrategy.EAGER
import com.simprints.infra.config.store.models.Vero2Configuration.ImageSavingStrategy.NEVER
import com.simprints.infra.config.store.models.Vero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN
import com.simprints.infra.config.store.models.Vero2Configuration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
import com.simprints.infra.images.model.Path
import com.simprints.infra.images.model.SecuredImageRef
import com.simprints.infra.logging.LoggingConstants.CrashReportTag.FINGER_CAPTURE
Expand Down Expand Up @@ -176,13 +180,14 @@ internal class FingerprintCaptureViewModel @Inject constructor(

private suspend fun initBioSdk() {
try {
bioSdkWrapper= resolveBioSdkWrapperUseCase()
bioSdkWrapper = resolveBioSdkWrapperUseCase()
bioSdkWrapper.initialize()
} catch (e: BioSdkException.BioSdkInitializationException) {
Simber.e(e)
_invalidLicense.send()
}
}

private fun launchReconnect() {
if (!state.isShowingConnectionScreen) {
updateState {
Expand All @@ -201,12 +206,14 @@ internal class FingerprintCaptureViewModel @Inject constructor(

when (it.currentCaptureState()) {
is CaptureState.Scanning,
is CaptureState.TransferringImage -> pauseLiveFeedback()
is CaptureState.TransferringImage,
-> pauseLiveFeedback()

CaptureState.NotCollected,
CaptureState.Skipped,
is CaptureState.NotDetected,
is CaptureState.Collected -> {
is CaptureState.Collected,
-> {
if (it.isShowingConfirmDialog) stopLiveFeedback()
else startLiveFeedback(scannerManager.scanner)
}
Expand Down Expand Up @@ -265,7 +272,7 @@ internal class FingerprintCaptureViewModel @Inject constructor(
bioSdkWrapper.scanningTimeoutMs +
if (isImageTransferRequired()) bioSdkWrapper.imageTransferTimeoutMs else 0

private fun isImageTransferRequired(): Boolean =
private fun isImageTransferRequired(): Boolean =
bioSdkConfiguration.vero2?.imageSavingStrategy?.isImageTransferRequired() ?: false &&
scannerManager.scanner.isImageTransferSupported()

Expand Down Expand Up @@ -372,10 +379,18 @@ internal class FingerprintCaptureViewModel @Inject constructor(
}
}

private fun shouldProceedToImageTransfer(quality: Int) = isImageTransferRequired() &&
(quality >= qualityThreshold() ||
tooManyBadScans(state.currentCaptureState(), plusBadScan = true) ||
bioSdkConfiguration.vero2?.imageSavingStrategy?.isEager() ?: false)
private fun shouldProceedToImageTransfer(quality: Int): Boolean {
val isGoodScan = quality >= qualityThreshold()
val isTooManyBadScans = tooManyBadScans(state.currentCaptureState(), plusBadScan = true)

val shouldUploadImage = when (bioSdkConfiguration.vero2?.imageSavingStrategy) {
NEVER, null -> false
EAGER -> true
ONLY_GOOD_SCAN -> isGoodScan
ONLY_USED_IN_REFERENCE -> isGoodScan || isTooManyBadScans
}
return isImageTransferRequired() && shouldUploadImage
}

private fun proceedToImageTransfer() {
imageTransferTask?.cancel()
Expand Down Expand Up @@ -504,6 +519,7 @@ internal class FingerprintCaptureViewModel @Inject constructor(
Simber.e(e)
handleNoFingerDetected()
}

else -> {
updateCaptureState { toNotCollected() }
Simber.e(e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.simprints.fingerprint.capture.extensions
import com.google.common.truth.Truth.assertThat
import com.simprints.infra.config.store.models.Vero2Configuration.ImageSavingStrategy.EAGER
import com.simprints.infra.config.store.models.Vero2Configuration.ImageSavingStrategy.NEVER
import com.simprints.infra.config.store.models.Vero2Configuration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
import com.simprints.infra.config.store.models.Vero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN
import org.junit.Test

Expand All @@ -13,6 +14,7 @@ internal class SaveFingerprintImagesStrategyTest {
mapOf(
EAGER to "wsq",
ONLY_GOOD_SCAN to "wsq",
ONLY_USED_IN_REFERENCE to "wsq",
NEVER to "",
).map { (actual, expected) -> assertThat(actual.deduceFileExtension()).isEqualTo(expected) }
}
Expand All @@ -22,6 +24,7 @@ internal class SaveFingerprintImagesStrategyTest {
mapOf(
EAGER to true,
ONLY_GOOD_SCAN to true,
ONLY_USED_IN_REFERENCE to true,
NEVER to false,
).map { (actual, expected) -> assertThat(actual.isImageTransferRequired()).isEqualTo(expected) }
}
Expand All @@ -31,6 +34,7 @@ internal class SaveFingerprintImagesStrategyTest {
mapOf(
EAGER to true,
ONLY_GOOD_SCAN to false,
ONLY_USED_IN_REFERENCE to false,
NEVER to false,
).map { (actual, expected) -> assertThat(actual.isEager()).isEqualTo(expected) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ class FingerprintCaptureViewModelTest {
GOOD_SCAN
)
acquireImageResponses(OK)
withImageTransfer(isEager = true)
withImageTransfer(strategy = ImageSavingStrategy.EAGER)

vm.handleOnViewCreated(TWO_FINGERS_IDS)

Expand Down Expand Up @@ -862,6 +862,101 @@ class FingerprintCaptureViewModelTest {
}
}


@Test
@ExperimentalTime
fun receivesMixOfScanResults_withGoodScansOnlyTransfer_updatesStateCorrectlyAndReturnsFingersCorrectly() = runTest {
mockScannerSetUiIdle()
setupCaptureFingerprintResponses(
BAD_SCAN,
BAD_SCAN,
BAD_SCAN,

BAD_SCAN,
GOOD_SCAN,

GOOD_SCAN,
)
acquireImageResponses(OK)
withImageTransfer(strategy = ImageSavingStrategy.ONLY_GOOD_SCAN)

vm.handleOnViewCreated(TWO_FINGERS_IDS)

// Finger 1
vm.handleScanButtonPressed()
vm.handleScanButtonPressed()
vm.handleScanButtonPressed()
// vm.handleScanButtonPressed()
advanceTimeBy(TIME_SKIP_MS)

// Finger 3
vm.handleScanButtonPressed()
vm.handleScanButtonPressed()
advanceTimeBy(TIME_SKIP_MS)

// Finger 4
// vm.handleScanButtonPressed()
vm.handleScanButtonPressed()
advanceTimeBy(TIME_SKIP_MS)

assertThat(vm.stateLiveData.value).isEqualTo(
CollectFingerprintsState(
fingerStates = listOf(
FingerState(
FOUR_FINGERS_IDS[0],
listOf(
CaptureState.Collected(
ScanResult(BAD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, null, 60),
numberOfBadScans = 3
)
)
),
FingerState(
FOUR_FINGERS_IDS[1],
listOf(
CaptureState.Collected(
ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60),
numberOfBadScans = 1
)
)
),
FingerState(
FOUR_FINGERS_IDS[2],
listOf(
CaptureState.Collected(
ScanResult(GOOD_QUALITY, TEMPLATE, TEMPLATE_FORMAT, IMAGE, 60),
numberOfBadScans = 0
)
)
)
),
currentFingerIndex = 2,
isAskingRescan = false,
isShowingConfirmDialog = true,
isShowingSplashScreen = false,
isShowingConnectionScreen = false,
)
)
coVerify(exactly = 6) { addCaptureEventsUseCase.invoke(any(), any(), any(), any()) }

vm.handleConfirmFingerprintsAndContinue()
// Save image is called even if scanResult.image == null
coVerify(exactly = 3) { saveImageUseCase.invoke(any(), any(), any()) }

vm.finishWithFingerprints.assertEventReceivedWithContentAssertions { actualFingerprints ->
assertThat(actualFingerprints?.results).hasSize(3)
assertThat(actualFingerprints?.results?.map { it.identifier }).containsExactly(
IFingerIdentifier.LEFT_THUMB,
IFingerIdentifier.LEFT_INDEX_FINGER,
IFingerIdentifier.RIGHT_THUMB
)
actualFingerprints?.results?.forEach {
assertThat(it.sample?.template).isEqualTo(TEMPLATE)
assertThat(it.sample?.imageRef).isNotNull()
}
}
}

@Test
@ExperimentalTime
fun pressingCancel_duringScan_cancelsProperly() = runTest {
Expand Down Expand Up @@ -1232,8 +1327,8 @@ class FingerprintCaptureViewModelTest {
every { vero2Configuration.imageSavingStrategy } returns ImageSavingStrategy.NEVER
}

private fun withImageTransfer(isEager: Boolean = false) {
every { vero2Configuration.imageSavingStrategy } returns if (isEager) ImageSavingStrategy.EAGER else ImageSavingStrategy.ONLY_GOOD_SCAN
private fun withImageTransfer(strategy: ImageSavingStrategy = ImageSavingStrategy.ONLY_USED_IN_REFERENCE) {
every { vero2Configuration.imageSavingStrategy } returns strategy
coEvery { saveImageUseCase.invoke(any(), any(), any()) } returns mockk {
every { relativePath } returns Path(emptyArray())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ internal data class OldProjectConfig(
?: DEFAULT_FACE_FRAMES_TO_CAPTURE,
qualityThreshold = faceQualityThreshold.toInt(),
imageSavingStrategy = if (saveFaceImages.toBoolean()) {
FaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN
FaceConfiguration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
} else {
FaceConfiguration.ImageSavingStrategy.NEVER
},
Expand Down Expand Up @@ -139,7 +139,7 @@ internal data class OldProjectConfig(
displayLiveFeedback = fingerprintLiveFeedbackOn.toBoolean(),
imageSavingStrategy = when (saveFingerprintImagesStrategy) {
"NEVER" -> Vero2Configuration.ImageSavingStrategy.NEVER
"WSQ_15" -> Vero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN
"WSQ_15" -> Vero2Configuration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
"WSQ_15_EAGER" -> Vero2Configuration.ImageSavingStrategy.EAGER
else -> Vero2Configuration.ImageSavingStrategy.NEVER
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ internal fun FaceConfiguration.toProto(): ProtoFaceConfiguration =
internal fun FaceConfiguration.ImageSavingStrategy.toProto(): ProtoFaceConfiguration.ImageSavingStrategy =
when (this) {
FaceConfiguration.ImageSavingStrategy.NEVER -> ProtoFaceConfiguration.ImageSavingStrategy.NEVER
FaceConfiguration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE -> ProtoFaceConfiguration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
FaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN -> ProtoFaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN
}

Expand All @@ -28,6 +29,7 @@ internal fun ProtoFaceConfiguration.toDomain(): FaceConfiguration =
internal fun ProtoFaceConfiguration.ImageSavingStrategy.toDomain(): FaceConfiguration.ImageSavingStrategy =
when (this) {
ProtoFaceConfiguration.ImageSavingStrategy.NEVER -> FaceConfiguration.ImageSavingStrategy.NEVER
ProtoFaceConfiguration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE -> FaceConfiguration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
ProtoFaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN -> FaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN
ProtoFaceConfiguration.ImageSavingStrategy.UNRECOGNIZED -> throw InvalidProtobufEnumException(
"invalid image saving strategy $name"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal fun Vero2Configuration.ImageSavingStrategy.toProto(): ProtoVero2Configu
when (this) {
Vero2Configuration.ImageSavingStrategy.NEVER -> ProtoVero2Configuration.ImageSavingStrategy.NEVER
Vero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN -> ProtoVero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN
Vero2Configuration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE -> ProtoVero2Configuration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
Vero2Configuration.ImageSavingStrategy.EAGER -> ProtoVero2Configuration.ImageSavingStrategy.EAGER
}

Expand Down Expand Up @@ -47,6 +48,7 @@ internal fun ProtoVero2Configuration.ImageSavingStrategy.toDomain(): Vero2Config
when (this) {
ProtoVero2Configuration.ImageSavingStrategy.NEVER -> Vero2Configuration.ImageSavingStrategy.NEVER
ProtoVero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN -> Vero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN
ProtoVero2Configuration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE -> Vero2Configuration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
ProtoVero2Configuration.ImageSavingStrategy.EAGER -> Vero2Configuration.ImageSavingStrategy.EAGER
ProtoVero2Configuration.ImageSavingStrategy.UNRECOGNIZED -> throw InvalidProtobufEnumException(
"invalid ImageSavingStrategy $name"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ data class FaceConfiguration(

enum class ImageSavingStrategy {
NEVER,
ONLY_USED_IN_REFERENCE,
ONLY_GOOD_SCAN;

fun shouldSaveImage() = this != NEVER
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ data class Vero2Configuration(
enum class ImageSavingStrategy {
NEVER,
ONLY_GOOD_SCAN,
ONLY_USED_IN_REFERENCE,
EAGER;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ internal data class ApiFaceConfiguration(
@Keep
enum class ImageSavingStrategy {
NEVER,
ONLY_USED_IN_REFERENCE,
ONLY_GOOD_SCAN;

fun toDomain(): FaceConfiguration.ImageSavingStrategy =
when (this) {
NEVER -> FaceConfiguration.ImageSavingStrategy.NEVER
ONLY_USED_IN_REFERENCE -> FaceConfiguration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
ONLY_GOOD_SCAN -> FaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ internal data class ApiVero2Configuration(
enum class ImageSavingStrategy {
NEVER,
ONLY_GOOD_SCAN,
ONLY_USED_IN_REFERENCE,
EAGER;

fun toDomain(): Vero2Configuration.ImageSavingStrategy =
when (this) {
NEVER -> Vero2Configuration.ImageSavingStrategy.NEVER
ONLY_USED_IN_REFERENCE -> Vero2Configuration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE
ONLY_GOOD_SCAN -> Vero2Configuration.ImageSavingStrategy.ONLY_GOOD_SCAN
EAGER -> Vero2Configuration.ImageSavingStrategy.EAGER
}
Expand Down
2 changes: 2 additions & 0 deletions infra/config-store/src/main/proto/project_config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ message ProtoFaceConfiguration {
enum ImageSavingStrategy {
NEVER = 0;
ONLY_GOOD_SCAN = 1;
ONLY_USED_IN_REFERENCE = 2;
}
}

Expand Down Expand Up @@ -93,6 +94,7 @@ message ProtoVero2Configuration {
NEVER = 0;
ONLY_GOOD_SCAN = 1;
EAGER = 2;
ONLY_USED_IN_REFERENCE = 3;
}

enum CaptureStrategy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ class ProjectConfigSharedPrefsMigrationTest {
private val PROTO_FACE_CONFIGURATION = ProtoFaceConfiguration.newBuilder()
.setNbOfImagesToCapture(2)
.setQualityThreshold(-1)
.setImageSavingStrategy(ProtoFaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN)
.setImageSavingStrategy(ProtoFaceConfiguration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE)
.setDecisionPolicy(
ProtoDecisionPolicy.newBuilder().setLow(1).setMedium(20).setHigh(100).build()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class FaceConfigurationTest {
fun `should map correctly the ImageSavingStrategy enums`() {
val mapping = mapOf(
ProtoFaceConfiguration.ImageSavingStrategy.NEVER to FaceConfiguration.ImageSavingStrategy.NEVER,
ProtoFaceConfiguration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE to FaceConfiguration.ImageSavingStrategy.ONLY_USED_IN_REFERENCE,
ProtoFaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN to FaceConfiguration.ImageSavingStrategy.ONLY_GOOD_SCAN,
)

Expand Down
Loading