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 @@ -45,6 +45,7 @@ import com.simprints.infra.uibase.view.fadeOut
import com.simprints.infra.uibase.viewbinding.viewBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
Expand Down Expand Up @@ -85,6 +86,7 @@ internal class ExternalCredentialScanOcrFragment : Fragment(R.layout.fragment_ex
private var checkAnimator: ViewPropertyAnimator? = null
private var isAnimatingCompletion: Boolean = false
private var pendingFinishAction: (() -> Unit)? = null
private var ocrPreProcessingJob: Job? = null

@Inject
lateinit var viewModelFactory: ExternalCredentialScanOcrViewModel.Factory
Expand Down Expand Up @@ -132,11 +134,11 @@ internal class ExternalCredentialScanOcrFragment : Fragment(R.layout.fragment_ex
}
}

override fun onDestroy() {
override fun onDestroyView() {
stopOcr()
stopCamera()
clearAnimations()
super.onDestroy()
super.onDestroyView()
}

private fun clearAnimations() {
Expand Down Expand Up @@ -299,48 +301,46 @@ internal class ExternalCredentialScanOcrFragment : Fragment(R.layout.fragment_ex
// Running OCR as often as we can while camera feedback is displayed to the user
viewModel.ocrOnFrameStarted()
if (viewModel.ocrConfig.useHighRes) {
captureHighResImageForOcr()
videoFrame.close()
captureHighResImageForOcr { highResImage ->
preProcessImageAndRunOcr(highResImage)
}
} else {
captureFrameFromVideoStreamForOcr(videoFrame)
preProcessImageAndRunOcr(videoFrame)
}
}
}

private fun captureFrameFromVideoStreamForOcr(imageProxy: ImageProxy) {
lifecycleScope.launch(bgDispatcher) {
private fun preProcessImageAndRunOcr(imageProxy: ImageProxy) {
ocrPreProcessingJob?.cancel()
ocrPreProcessingJob = lifecycleScope.launch(bgDispatcher) {
try {
val (bitmap, imageInfo) = imageProxy.toBitmap() to imageProxy.imageInfo
val cropConfig: OcrCropConfig = buildOcrCropConfigUseCase(
rotationDegrees = imageInfo.rotationDegrees,
cameraPreview = binding.preview,
documentScannerArea = binding.documentScannerArea,
)
viewModel.runOcrOnFrame(frame = bitmap, cropConfig)
if (ocrPreProcessingJob?.isActive == true) {
val cropConfig: OcrCropConfig = buildOcrCropConfigUseCase(
rotationDegrees = imageInfo.rotationDegrees,
cameraPreview = binding.preview,
documentScannerArea = binding.documentScannerArea,
)
viewModel.runOcrOnFrame(frame = bitmap, cropConfig)
} else {
Simber.i(
"Unable to run OCR preprocessing, coroutine context is cancelled",
tag = MULTI_FACTOR_ID,
)
}
} finally {
imageProxy.close()
}
}
}

private fun captureHighResImageForOcr() {
private fun captureHighResImageForOcr(onImageCaptured: (ImageProxy) -> Unit) {
imageCapture.takePicture(
cameraExecutor,
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(imageProxy: ImageProxy) {
lifecycleScope.launch(bgDispatcher) {
try {
val (bitmap, imageInfo) = imageProxy.toBitmap() to imageProxy.imageInfo
val cropConfig: OcrCropConfig = buildOcrCropConfigUseCase(
rotationDegrees = imageInfo.rotationDegrees,
cameraPreview = binding.preview,
documentScannerArea = binding.documentScannerArea,
)
viewModel.runOcrOnFrame(frame = bitmap, cropConfig)
} finally {
imageProxy.close()
}
}
onImageCaptured(imageProxy)
}

override fun onError(e: ImageCaptureException) {
Expand All @@ -357,6 +357,7 @@ internal class ExternalCredentialScanOcrFragment : Fragment(R.layout.fragment_ex
}

private fun stopOcr() {
ocrPreProcessingJob?.cancel()
if (::imageAnalysis.isInitialized) {
imageAnalysis.clearAnalyzer()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import com.simprints.feature.externalcredential.screens.scanocr.usecase.Normaliz
import com.simprints.feature.externalcredential.screens.scanocr.usecase.ZoomOntoCredentialUseCase
import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential
import com.simprints.infra.authstore.AuthStore
import com.simprints.infra.config.store.models.ExperimentalProjectConfiguration
import com.simprints.infra.config.store.models.TokenKeyType
import com.simprints.infra.config.store.models.experimental
import com.simprints.infra.config.store.tokenization.TokenizationProcessor
Expand Down Expand Up @@ -64,7 +63,7 @@ internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor(
private set
val isOcrActive: Boolean
get() = detectedBlocks.isNotEmpty()
private var state: ScanOcrState = ScanOcrState.EMPTY
private var ocrState: ScanOcrState = ScanOcrState.EMPTY
set(value) {
field = value
_stateLiveData.postValue(value)
Expand All @@ -91,7 +90,7 @@ internal class ExternalCredentialScanOcrViewModel @AssistedInject constructor(
}

private fun updateState(state: (ScanOcrState) -> ScanOcrState) {
this.state = state(this.state)
this.ocrState = state(this.ocrState)
}

fun getDocumentTypeRes(): Int = when (ocrDocumentType) {
Expand Down