package com.dynamsoft.reactnativelib.cvr import android.util.Log import com.dynamsoft.core.basic_structures.CompletionListener import com.dynamsoft.core.basic_structures.EnumCapturedResultItemType import com.dynamsoft.core.basic_structures.ImageData import com.dynamsoft.cvr.* import com.dynamsoft.dbr.DecodedBarcodesResult import com.dynamsoft.dcp.ParsedResult import com.dynamsoft.ddn.DeskewedImageResultItem import com.dynamsoft.ddn.EnhancedImageResultItem import com.dynamsoft.ddn.ProcessedDocumentResult import com.dynamsoft.dlr.RecognizedTextLinesResult import com.dynamsoft.reactnativelib.basicutils.toWritableMap import com.dynamsoft.reactnativelib.core.ImageSourceAdapterModuleImpl import com.dynamsoft.reactnativelib.basicutils.updateFromReadableMap import com.facebook.react.bridge.* import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking private const val onCapturedResultReceived_Event = "onCapturedResultReceived" private const val onDecodedBarcodesReceived_Event = "onDecodedBarcodesReceived" private const val onProcessedDocumentResultReceived_Event = "onProcessedDocumentResultReceived" private const val onRecognizedTextLinesReceived_Event = "onRecognizedTextLinesReceived" private const val onParsedResultsReceived_Event = "onParsedResultsReceived" private const val TAG = "CvrModule" val filterMap = hashMapOf() class CaptureVisionRouterModuleImpl(private val reactContext: ReactApplicationContext) { companion object { const val NAME = "DynamsoftCaptureVisionRouterModule" } private var ifStopCapturing = false private var currentDeskewedImages: Array? = null private var currentEnhancedImages: Array? = null private var cvr: CaptureVisionRouter? = null private val emitter by lazy { reactContext.getJSModule(RCTDeviceEventEmitter::class.java) } private var continueCRR = false private var crrRemoved = false private val crrMap by lazy { mapOf( onCapturedResultReceived_Event to object : CapturedResultReceiver { override fun onCapturedResultReceived(result: CapturedResult) { currentDeskewedImages = result.items.filter { it.type == EnumCapturedResultItemType.CRIT_DESKEWED_IMAGE } .map { (it as DeskewedImageResultItem).imageData }.toTypedArray() currentEnhancedImages = result.items.filter { it.type == EnumCapturedResultItemType.CRIT_ENHANCED_IMAGE } .map { (it as EnhancedImageResultItem).imageData }.toTypedArray() emitResultAndWaitForResponse(onCapturedResultReceived_Event, result.toWritableMap()) } }, onDecodedBarcodesReceived_Event to object : CapturedResultReceiver { override fun onDecodedBarcodesReceived(result: DecodedBarcodesResult) { emitResultAndWaitForResponse(onDecodedBarcodesReceived_Event, result.toWritableMap()) } }, onProcessedDocumentResultReceived_Event to object : CapturedResultReceiver { override fun onProcessedDocumentResultReceived(result: ProcessedDocumentResult) { currentDeskewedImages = result.deskewedImageResultItems.map { (it as DeskewedImageResultItem).imageData }.toTypedArray() currentEnhancedImages = result.enhancedImageResultItems.map { (it as EnhancedImageResultItem).imageData }.toTypedArray() emitResultAndWaitForResponse(onProcessedDocumentResultReceived_Event, result.toWritableMap()) } }, onRecognizedTextLinesReceived_Event to object : CapturedResultReceiver { override fun onRecognizedTextLinesReceived(result: RecognizedTextLinesResult) { emitResultAndWaitForResponse(onRecognizedTextLinesReceived_Event, result.toWritableMap()) } }, onParsedResultsReceived_Event to object : CapturedResultReceiver { override fun onParsedResultsReceived(result: ParsedResult) { emitResultAndWaitForResponse(onParsedResultsReceived_Event, result.toWritableMap()) } } ) } private fun emitResultAndWaitForResponse(eventKey: String, result: WritableMap) { continueCRR = false emitter.emit(eventKey, result) while (!continueCRR && !crrRemoved && !ifStopCapturing) { //waiting for js callback end runBlocking { delay(5) } } } fun getName() = NAME @ReactMethod(isBlockingSynchronousMethod = true) fun createInstance(): Boolean { if(cvr == null) cvr = CaptureVisionRouter() return true } @ReactMethod(isBlockingSynchronousMethod = true) fun destroyInstance(): Boolean { cvr?.removeAllResultReceivers() ifStopCapturing = true cvr?.stopCapturing() try { cvr?.input = null } catch (_: Exception) {} cvr = null return true } @ReactMethod fun continueCRR() { continueCRR = true } @ReactMethod fun addListener(type: String?) { crrRemoved = false crrMap[type]?.let { cvr?.addResultReceiver(it) } } @ReactMethod fun removeListeners(count: Int) { //keep } @ReactMethod fun removeListener(type: String?) { crrMap[type]?.let { cvr?.removeResultReceiver(it) } } @ReactMethod fun removeAllResultListeners() { crrRemoved = true crrMap.forEach { cvr?.removeResultReceiver(it.value) } } @ReactMethod fun addFilter(filterId: String?) { cvr?.addResultFilter(filterMap[filterId]) } @ReactMethod fun removeFilter(filterId: String?) { cvr?.removeResultFilter(filterMap[filterId]) } @ReactMethod fun startCapturing(template: String, promise: Promise) { ifStopCapturing = false cvr?.startCapturing(template, object : CompletionListener { override fun onSuccess() { Log.i(TAG, "onSuccess: startCapturing($template)") promise.resolve(null) } override fun onFailure(errorCode: Int, errorString: String?) { Log.e(TAG, "onFailure: startCapturing($template). ErrorCode: $errorCode. ErrorString: $errorString") promise.reject(errorCode.toString(), errorString) } }) } @ReactMethod fun stopCapturing(promise: Promise) { ifStopCapturing = true cvr?.stopCapturing() promise.resolve(null) } @ReactMethod fun setInput(inputId: String) { ImageSourceAdapterModuleImpl.mapISA[inputId]?.apply { cvr?.input = this } } @ReactMethod fun resetSettings(promise: Promise) { try { cvr?.resetSettings() promise.resolve(true) } catch (e: CaptureVisionRouterException) { promise.reject(e.errorCode.toString(), e.message) } } @ReactMethod fun getSimplifiedSettings(template: String, promise: Promise) { try { promise.resolve(cvr?.getSimplifiedSettings(template)?.toWritableMap()) } catch (e: CaptureVisionRouterException) { promise.reject(e.errorCode.toString(), e.message) } } @ReactMethod fun updateSettings(settings: ReadableMap, template: String, promise: Promise) { try { cvr?.getSimplifiedSettings(template)?.apply { updateFromReadableMap(settings) cvr?.updateSettings(template, this) } promise.resolve(null) } catch (e: CaptureVisionRouterException) { promise.reject(e.errorCode.toString(), e.message) } } @ReactMethod fun initSettings(content: String, promise: Promise) { try { cvr?.initSettings(content) promise.resolve(null) } catch (e: CaptureVisionRouterException) { promise.reject(e.errorCode.toString(), e.message) } } @ReactMethod fun initSettingsFromFile(path: String, promise: Promise) { try { cvr?.initSettingsFromFile(path) promise.resolve(null) } catch (e: CaptureVisionRouterException) { promise.reject(e.errorCode.toString(), e.message) } } @ReactMethod fun outputSettingsToFile(template: String, path: String, includeDefaultValues: Boolean, promise: Promise) { try { cvr?.outputSettingsToFile(template, path, includeDefaultValues) promise.resolve(null) } catch (e: CaptureVisionRouterException) { promise.reject(e.errorCode.toString(), e.message) } } @ReactMethod fun outputSettings(template: String, includeDefaultValues: Boolean, promise: Promise) { try { promise.resolve(cvr?.outputSettings(template, includeDefaultValues)) } catch (e: CaptureVisionRouterException) { promise.reject(e.errorCode.toString(), e.message) } } @ReactMethod fun switchCapturingTemplate(template: String, promise: Promise) { try { cvr?.switchCapturingTemplate(template) promise.resolve(null) } catch (e: CaptureVisionRouterException) { promise.reject(e.errorCode.toString(), e.message) } } @ReactMethod fun clearDLModelBuffers() { CaptureVisionRouter.clearDLModelBuffers() } @ReactMethod fun setGlobalIntraOpNumThreads(intraOpNumThreads: Int) { CaptureVisionRouter.setGlobalIntraOpNumThreads(intraOpNumThreads) } @ReactMethod(isBlockingSynchronousMethod = true) fun install(): Boolean { reactContext.javaScriptContextHolder?.apply { nativeInstall(this.get()) } return true } //@Native private fun captureImageDataCalledInJSI(imageData: ImageData, template: String): CapturedResult { return cvr?.capture(imageData, template) ?: CapturedResult() } //@Native private fun captureFileCalledInJSI(filePath: String, template: String): CapturedResult { return cvr?.capture(filePath, template) ?: CapturedResult() } //@Native private fun captureFileBytesCalledInJSI(fileBytes: ByteArray, template: String): CapturedResult { return cvr?.capture(fileBytes, template) ?: CapturedResult() } //@Native private fun getOriginalImageCalledInJSI(imageHashId: String): ImageData { var originalImage = cvr?.intermediateResultManager?.getOriginalImage(imageHashId) if (originalImage == null) { originalImage = ImageData() originalImage.bytes = ByteArray(0) } return originalImage } private external fun nativeInstall(jsiPtr: Long) }