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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ jobs:
fingerprint:infra:scanner
fingerprint:infra:simafis-wrapper
fingerprint:infra:simprints-bio-sdk
fingerprint:infra:nec-bio-sdk
fingerprint:infra:image-distortion-config
reportsId: fingerprint
testing-tools:
Expand Down
2 changes: 1 addition & 1 deletion build-logic/build_properties.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extra.apply {
* Dev version >= 2025.2.0 is required to support enrolment record updates and SimFace configuration
* Dev version >= 2025.3.0 is required to receive smaples and structured down sync configuration
*/
set("VERSION_NAME", "2025.4.0")
set("VERSION_NAME", "2026.1.0")

/**
* Build type. The version code describes which build type was used for the build.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,11 @@ internal class FaceCaptureViewModel @Inject constructor(
if (faceConfiguration?.imageSavingStrategy?.shouldSaveImage() == true) {
saveFaceDetections()
}

if (faceDetections.isEmpty()) {
Simber.i("No face captured", tag = FACE_CAPTURE)
recapture()
return@launch
}
val items = faceDetections.map { detection ->
BiometricTemplateCapture(
captureEventId = detection.id,
Expand All @@ -187,7 +191,6 @@ internal class FaceCaptureViewModel @Inject constructor(
}
val referenceId = UUID.randomUUID().toString()
eventReporter.addBiometricReferenceCreationEvents(referenceId, items.map { it.captureEventId })

val format = faceDetections
.firstOrNull()
?.face
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,14 @@ internal class ConfirmationFragment : Fragment(R.layout.fragment_confirmation) {
) {
super.onViewCreated(view, savedInstanceState)
applySystemBarInsets(view)

val detection = mainVm.getSampleDetection()
// Check if state was lost (Process Death)
if (detection == null) {
// it is safer to force a recapture in this case
Simber.i("Face detection state lost during confirmation, forcing recapture", tag = ORCHESTRATION)
mainVm.recapture()
return
}
Simber.i("ConfirmationFragment started", tag = ORCHESTRATION)
startTime = faceTimeHelper.now()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ class FaceCaptureViewModelTest {
}
}

@Test
fun `restart capture when flow finishes and no face detected`() {
viewModel.initFaceBioSdk(mockk(), ModalitySdkType.SIM_FACE)
viewModel.flowFinished()
assertThat(viewModel.recaptureEvent.getOrAwaitValue()).isNotNull()
}

@Test
fun `Recapture requests clears capture list`() {
viewModel.captureFinished(faceDetections)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ internal class ExternalCredentialScanOcrFragment : Fragment(R.layout.fragment_ex

override fun onResume() {
super.onResume()
val currentPermission = requireActivity().getCurrentPermissionStatus(CAMERA)
when (currentPermission) {
when (val currentPermission = requireActivity().getCurrentPermissionStatus(CAMERA)) {
PermissionStatus.Granted -> initializeFragment()
PermissionStatus.Denied -> {
// Permission dialog was already displayed, and user denied permissions. Showing rationale so to avoid constantly-appearing
Expand Down Expand Up @@ -292,7 +291,7 @@ internal class ExternalCredentialScanOcrFragment : Fragment(R.layout.fragment_ex

private fun startOcr() {
imageAnalysis.setAnalyzer(cameraExecutor) { videoFrame: ImageProxy ->
if (viewModel.isRunningOcrOnFrame) {
if (viewModel.isRunningOcrOnFrame.get()) {
videoFrame.close()
return@setAnalyzer
}
Expand Down Expand Up @@ -360,6 +359,7 @@ internal class ExternalCredentialScanOcrFragment : Fragment(R.layout.fragment_ex
if (::imageAnalysis.isInitialized) {
imageAnalysis.clearAnalyzer()
}
viewModel.ocrStopped()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.launch
import java.util.concurrent.atomic.AtomicBoolean

internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor(
@Assisted val ocrDocumentType: OcrDocumentType,
Expand All @@ -57,8 +58,8 @@ internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor(
}

private var detectedBlocks: List<DetectedOcrBlock> = emptyList()
var isRunningOcrOnFrame: Boolean = false
private set
val isRunningOcrOnFrame = AtomicBoolean(false)

val isOcrActive: Boolean
get() = detectedBlocks.isNotEmpty()
private var ocrState: ScanOcrState = ScanOcrState.EMPTY
Expand Down Expand Up @@ -107,6 +108,10 @@ internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor(
}
}

fun ocrStopped() {
isRunningOcrOnFrame.set(false)
}

fun runOcrOnFrame(
frame: Bitmap,
cropConfig: OcrCropConfig,
Expand All @@ -127,7 +132,7 @@ internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor(
)
}
} finally {
isRunningOcrOnFrame = false
isRunningOcrOnFrame.set(false)
}
}
}
Expand Down Expand Up @@ -179,7 +184,7 @@ internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor(
}

fun ocrOnFrameStarted() {
isRunningOcrOnFrame = true
isRunningOcrOnFrame.set(true)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ internal class ExternalCredentialScanOcrViewModelTest {
assertThat(state.scansRequired).isEqualTo(3)
}

@Test
fun `ocrStopped resets the flag for frame processing`() {
viewModel.ocrOnFrameStarted()
assertThat(viewModel.isRunningOcrOnFrame.get()).isTrue()

viewModel.ocrStopped()
assertThat(viewModel.isRunningOcrOnFrame.get()).isFalse()
}

@Test
fun `runOcrOnFrame updates detected blocks and state when successful`() = runTest {
val mockDetectedBlock = mockk<DetectedOcrBlock>()
Expand All @@ -122,7 +131,7 @@ internal class ExternalCredentialScanOcrViewModelTest {

val state = observer.value() as ScanOcrState.ScanningInProgress
assertThat(state.successfulCaptures).isEqualTo(1)
assertThat(viewModel.isRunningOcrOnFrame).isFalse()
assertThat(viewModel.isRunningOcrOnFrame.get()).isFalse()
assertThat(viewModel.isOcrActive).isTrue()
}

Expand Down
1 change: 0 additions & 1 deletion fingerprint/infra/bio-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ dependencies {
implementation(project(":infra:config-store"))
api(project(":fingerprint:infra:base-bio-sdk"))
implementation(project(":fingerprint:infra:simprints-bio-sdk"))
implementation(project(":fingerprint:infra:nec-bio-sdk"))
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.simprints.fingerprint.infra.biosdk

import com.simprints.fingerprint.infra.biosdkimpl.SimprintsSdk
import com.simprints.fingerprint.infra.necsdkimpl.NecSdk
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -15,9 +14,4 @@ internal abstract class FingerprintBioSdkModule {
@SimprintsSdk
@Singleton
abstract fun provideSimprintsBioSdkWrapper(impl: SimprintsBioSdkWrapper): BioSdkWrapper

@Binds
@NecSdk
@Singleton
abstract fun provideNecBioSdkWrapper(impl: NECBioSdkWrapper): BioSdkWrapper
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package com.simprints.fingerprint.infra.biosdk

import com.simprints.fingerprint.infra.biosdkimpl.SimprintsSdk
import com.simprints.fingerprint.infra.necsdkimpl.NecSdk
import com.simprints.infra.config.store.models.ModalitySdkType
import javax.inject.Inject

class ResolveBioSdkWrapperUseCase @Inject constructor(
@param:SimprintsSdk private val simprintsWrapper: BioSdkWrapper,
@param:NecSdk private val necWrapper: BioSdkWrapper,
) {
operator fun invoke(fingerprintSdk: ModalitySdkType): BioSdkWrapper = when (fingerprintSdk) {
ModalitySdkType.SECUGEN_SIM_MATCHER -> simprintsWrapper
ModalitySdkType.NEC -> necWrapper
ModalitySdkType.NEC -> error("NEC not supported since v2026.1.0")
else -> error("Unknown fingerprint configuration")
}
}
Loading
Loading