diff --git a/README.md b/README.md index 8b32d59..6344c3f 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ Reads MRZ field for React Native (Both iOS and Android) > **_NOTE:_** IOS Version only supports TD3 format Passport MRZ (doesn't support id cards), and only supports back camera. -> **_NOTE:_** Android Version only reads document number, expiry date, birth date, and fills rest of the fields empty. - ## Installation 1. **Install the Plugin**: @@ -31,14 +29,14 @@ Reads MRZ field for React Native (Both iOS and Android) Add the following code in your app to request camera permission at runtime: ```ts - import { Platform} from 'react-native'; + import { Platform } from 'react-native'; import * as Permissions from 'react-native-permissions'; async function requestCameraPermission() { try { const granted = await Permissions.request( (() => { - switch(Platform.OS) { + switch (Platform.OS) { case 'ios': return Permissions.PERMISSIONS.IOS.CAMERA; case 'android': @@ -55,7 +53,7 @@ Reads MRZ field for React Native (Both iOS and Android) buttonPositive: 'OK', } ); - switch(granted) { + switch (granted) { case Permissions.RESULTS.GRANTED: case Permissions.RESULTS.LIMITED: console.log('You can use the camera'); @@ -78,13 +76,14 @@ import MrzReader, { CameraSelector, DocType } from 'react-native-mrz-reader'; // ... { - console.log(mrz) + console.log(mrz); }} -/> +/>; ``` ## Example @@ -107,4 +106,4 @@ MIT --- -Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob) \ No newline at end of file +Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob) diff --git a/android/build.gradle b/android/build.gradle index 3b201c4..33b71bc 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -101,7 +101,7 @@ dependencies { implementation "androidx.camera:camera-view:1.3.3" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' - implementation 'cz.adaptech:tesseract4android:4.1.1' + implementation 'cz.adaptech.tesseract4android:tesseract4android-openmp:4.9.0' implementation 'org.jmrtd:jmrtd:0.7.18' } diff --git a/android/src/main/java/com/mrzreader/MrzReaderViewManager.kt b/android/src/main/java/com/mrzreader/MrzReaderViewManager.kt index 0c25d80..d34a665 100644 --- a/android/src/main/java/com/mrzreader/MrzReaderViewManager.kt +++ b/android/src/main/java/com/mrzreader/MrzReaderViewManager.kt @@ -3,6 +3,8 @@ package com.mrzreader import com.facebook.react.uimanager.SimpleViewManager import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp +import com.mrzreader.types.PreviewViewType +import com.mrzreader.types.ResizeMode import com.mrzreader.view.CameraPreviewView class MrzReaderViewManager : SimpleViewManager() { @@ -21,4 +23,24 @@ class MrzReaderViewManager : SimpleViewManager() { fun setDocType(view: CameraPreviewView, cardType: String) { view.setDocType(cardType) } + + @ReactProp(name = "resizeMode") + fun setResizeMode(view: CameraPreviewView, resizeMode: String?) { + if (resizeMode != null) { + val newMode = ResizeMode.fromValue(resizeMode) + view.resizeMode = newMode + } else { + view.resizeMode = ResizeMode.COVER + } + } + + @ReactProp(name = "androidPreviewViewType") + fun setAndroidPreviewViewType(view: CameraPreviewView, previewViewType: String?) { + if (previewViewType != null) { + val newType = PreviewViewType.fromValue(previewViewType) + view.androidPreviewViewType = newType + } else { + view.androidPreviewViewType = PreviewViewType.SURFACE_VIEW + } + } } diff --git a/android/src/main/java/com/mrzreader/dto/Coordinates.kt b/android/src/main/java/com/mrzreader/dto/Coordinates.kt deleted file mode 100644 index 7c11d65..0000000 --- a/android/src/main/java/com/mrzreader/dto/Coordinates.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.mrzreader.dto - -data class Coordinates( - val topLeft: Point, - val topRight: Point, - val bottomRight: Point, - val bottomLeft: Point -) diff --git a/android/src/main/java/com/mrzreader/dto/Line.kt b/android/src/main/java/com/mrzreader/dto/Line.kt deleted file mode 100644 index 4f732c0..0000000 --- a/android/src/main/java/com/mrzreader/dto/Line.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.mrzreader.dto - -data class Line(val p1: Point, val p2: Point) diff --git a/android/src/main/java/com/mrzreader/dto/Point.kt b/android/src/main/java/com/mrzreader/dto/Point.kt deleted file mode 100644 index 91f820e..0000000 --- a/android/src/main/java/com/mrzreader/dto/Point.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.mrzreader.dto - -data class Point(val x: Float, val y: Float) diff --git a/android/src/main/java/com/mrzreader/extensions/ViewGroup.kt b/android/src/main/java/com/mrzreader/extensions/ViewGroup.kt new file mode 100644 index 0000000..1dde903 --- /dev/null +++ b/android/src/main/java/com/mrzreader/extensions/ViewGroup.kt @@ -0,0 +1,19 @@ +package com.mrzreader.extensions + +import android.view.View +import android.view.ViewGroup + +// React does not trigger onLayout events for dynamically added views (`addView`). This fixes that. +// https://github.com/facebook/react-native/issues/17968#issuecomment-633308615 +fun ViewGroup.installHierarchyFitter() { + setOnHierarchyChangeListener(object : ViewGroup.OnHierarchyChangeListener { + override fun onChildViewRemoved(parent: View?, child: View?) = Unit + override fun onChildViewAdded(parent: View?, child: View?) { + parent?.measure( + View.MeasureSpec.makeMeasureSpec(measuredWidth, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(measuredHeight, View.MeasureSpec.EXACTLY) + ) + parent?.layout(0, 0, parent.measuredWidth, parent.measuredHeight) + } + }) +} diff --git a/android/src/main/java/com/mrzreader/types/PreviewViewType.kt b/android/src/main/java/com/mrzreader/types/PreviewViewType.kt new file mode 100644 index 0000000..4fedbbd --- /dev/null +++ b/android/src/main/java/com/mrzreader/types/PreviewViewType.kt @@ -0,0 +1,23 @@ +package com.mrzreader.types + +import androidx.camera.view.PreviewView + +enum class PreviewViewType(val value: String) { + SURFACE_VIEW("surface-view"), + TEXTURE_VIEW("texture-view"); + + fun toPreviewImplementationMode(): PreviewView.ImplementationMode = + when (this) { + SURFACE_VIEW -> PreviewView.ImplementationMode.PERFORMANCE + TEXTURE_VIEW -> PreviewView.ImplementationMode.COMPATIBLE + } + + companion object { + fun fromValue(value: String): PreviewViewType = + when (value) { + SURFACE_VIEW.value -> SURFACE_VIEW + TEXTURE_VIEW.value -> TEXTURE_VIEW + else -> throw IllegalArgumentException("Unknown PreviewViewType value: $value") + } + } +} diff --git a/android/src/main/java/com/mrzreader/types/ResizeMode.kt b/android/src/main/java/com/mrzreader/types/ResizeMode.kt new file mode 100644 index 0000000..19b9194 --- /dev/null +++ b/android/src/main/java/com/mrzreader/types/ResizeMode.kt @@ -0,0 +1,23 @@ +package com.mrzreader.types + +import androidx.camera.view.PreviewView + +enum class ResizeMode(val value: String) { + COVER("cover"), + CONTAIN("contain"); + + fun toScaleType(): PreviewView.ScaleType = + when (this) { + COVER -> PreviewView.ScaleType.FILL_CENTER + CONTAIN -> PreviewView.ScaleType.FIT_CENTER + } + + companion object { + fun fromValue(value: String): ResizeMode = + when (value) { + COVER.value -> COVER + CONTAIN.value -> CONTAIN + else -> throw IllegalArgumentException("Unknown ResizeMode value: $value") + } + } +} diff --git a/android/src/main/java/com/mrzreader/utils/OcrUtil.kt b/android/src/main/java/com/mrzreader/utils/OcrUtil.kt index f5ce235..486773e 100644 --- a/android/src/main/java/com/mrzreader/utils/OcrUtil.kt +++ b/android/src/main/java/com/mrzreader/utils/OcrUtil.kt @@ -14,7 +14,6 @@ import com.googlecode.leptonica.android.Rotate import com.googlecode.leptonica.android.Skew import com.googlecode.leptonica.android.WriteFile import com.mrzreader.R -import net.sf.scuba.data.Gender import org.jmrtd.lds.icao.MRZInfo import java.io.File import java.io.FileOutputStream @@ -24,6 +23,11 @@ import java.util.regex.Pattern class OcrUtil { var imageTextReader: ImageTextReader? = null + private data class MrzFilterResult( + val mrzInfo: MRZInfo, + val mrzRaw: String + ) + private fun preProcessBitmap(tBitmap: Bitmap): Bitmap? { var bitmap = tBitmap bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true) @@ -68,12 +72,10 @@ class OcrUtil { try { val bitmapProcess = preProcessBitmap(bitmap) val res = imageTextReader?.getTextFromBitmap(bitmapProcess) - val cleanText = - Html.fromHtml(res).toString().trim().replace(" ", "") - - val mrzInfo = filterScannedText(cleanText, docType) + val cleanText = Html.fromHtml(res).toString().trim().replace(" ", "") - return mrzInfo + val result = filterScannedText(cleanText, docType) + return result?.mrzInfo } catch (e: Exception) { Log.e("OcrUtil", "Error converting image to text: $e") } @@ -81,7 +83,7 @@ class OcrUtil { return null } - private fun filterScannedText(element: String, docType: DocType): MRZInfo? { + private fun filterScannedText(element: String, docType: DocType): MrzFilterResult? { try { var scannedTextBuffer = "" scannedTextBuffer += element.replace(" ", "") @@ -131,7 +133,7 @@ class OcrUtil { val strClearValue = scannedTextBuffer.substring(intFirstPosition) val intClearValueLen = strClearValue.length - if (intClearValueLen == 85) { + if (intClearValueLen >= 88) { scannedTextBuffer = strClearValue } } @@ -141,48 +143,42 @@ class OcrUtil { val matcherIDCardTD1Line1 = patternIDCardTD1Line1.matcher(scannedTextBuffer) val patternIDCardTD1Line2 = Pattern.compile(ID_CARD_TD_1_LINE_2_REGEX) val matcherIDCardTD1Line2 = patternIDCardTD1Line2.matcher(scannedTextBuffer) + val patternIDCardTD1Line3 = Pattern.compile(ID_CARD_TD_1_LINE_3_REGEX) + val matcherIDCardTD1Line3 = patternIDCardTD1Line3.matcher(scannedTextBuffer) + + if (matcherIDCardTD1Line1.find() && matcherIDCardTD1Line2.find() && matcherIDCardTD1Line3.find()) { + // Prefer exact TD1 length (90) starting from the detected type marker (I< / 1< / 1K / IK) + val startIdx = scannedTextBuffer.indexOf(typeIdCard).takeIf { it >= 0 } ?: return null + val candidate = scannedTextBuffer.substring(startIdx) + if (candidate.length < 90) return null + + val mrzRaw = candidate.substring(0, 90) + val line1 = mrzRaw.substring(0, 30) + val line2 = mrzRaw.substring(30, 60) + // val line3 = mrzRaw.substring(60, 90) // kept for completeness + + // Minimal validation (same as before) + var documentNumber = line1.substring(5, 14) + if (documentNumber.substring(3, 4) == "0") { + documentNumber = documentNumber.substring(0, 3) + "O" + documentNumber.substring(4, 9) + } + val dateOfBirthDay = line2.substring(0, 6) + val expiryDate = line2.substring(8, 14) + val chkdocumentNumber = line1.substring(14, 15)[0] + val chkdateOfBirthDay = line2.substring(6, 7)[0] + val chkexpiryDate = line2.substring(14, 15)[0] - if (matcherIDCardTD1Line1.find() && matcherIDCardTD1Line2.find()) { - - var line1 = matcherIDCardTD1Line1.group(0)?.toString() ?: "" - val line2 = matcherIDCardTD1Line2.group(0)?.toString() ?: "" - var mrzInfo: MRZInfo? = null - if (scannedTextBuffer.indexOf(typeIdCard) >= 0 && line1.length >= 14 && line2.length >= 14) { - - line1 = scannedTextBuffer.substring( - scannedTextBuffer.indexOf( - typeIdCard - ) - ) - - if (line1.length >= 14) { - var documentNumber = line1.substring(5, 14) - if (documentNumber.substring(3, 4) == "0") { - documentNumber = - documentNumber.substring(0, 3) + "O" + documentNumber.substring(4, 9) - } - // documentNumber = documentNumber.replace("O", "0"); - val dateOfBirthDay = line2.substring(0, 6) - val expiryDate = line2.substring(8, 14) - mrzInfo = buildTempMrz(documentNumber, dateOfBirthDay, expiryDate, docType) - val chkdocumentNumber = line1.substring(14, 15)[0] - val chkdateOfBirthDay = line2.substring(6, 7)[0] - val chkexpiryDate = line2.substring(14, 15)[0] - val valdocumentNumber = MRZInfo.checkDigit(documentNumber) - val valdateOfBirthDay = MRZInfo.checkDigit(dateOfBirthDay) - val valexpiryDate = MRZInfo.checkDigit(expiryDate) - val comparedocumentNumber = chkdocumentNumber.compareTo(valdocumentNumber) - val comparedateOfBirthDay = chkdateOfBirthDay.compareTo(valdateOfBirthDay) - val compareexpiryDate = chkexpiryDate.compareTo(valexpiryDate) - - if (comparedocumentNumber != 0) mrzInfo = null - if (comparedateOfBirthDay != 0) mrzInfo = null - if (compareexpiryDate != 0) mrzInfo = null - } - } + val valdocumentNumber = MRZInfo.checkDigit(documentNumber) + val valdateOfBirthDay = MRZInfo.checkDigit(dateOfBirthDay) + val valexpiryDate = MRZInfo.checkDigit(expiryDate) + + if (chkdocumentNumber.compareTo(valdocumentNumber) != 0) return null + if (chkdateOfBirthDay.compareTo(valdateOfBirthDay) != 0) return null + if (chkexpiryDate.compareTo(valexpiryDate) != 0) return null - return mrzInfo + val mrzInfo = MRZInfo(mrzRaw) + return MrzFilterResult(mrzInfo = mrzInfo, mrzRaw = mrzRaw) } } else if (docType === DocType.PASSPORT) { val patternPassportTD3Line1 = Pattern.compile(PASSPORT_TD_3_LINE_1_REGEX) @@ -200,8 +196,6 @@ class OcrUtil { val dateOfBirthDay = line2.substring(13, 19) val expiryDate = line2.substring(21, 27) - var mrzInfo: MRZInfo? = null - //----------------------------- val chkdocumentNumber = line2.substring(9, 10)[0] @@ -215,27 +209,27 @@ class OcrUtil { val compareexpiryDate = chkexpiryDate.compareTo(valexpiryDate) var isValidMrz = true if (comparedocumentNumber != 0) { - mrzInfo = null isValidMrz = false } if (comparedateOfBirthDay != 0) { - mrzInfo = null isValidMrz = false } if (compareexpiryDate != 0) { - mrzInfo = null isValidMrz = false } //--------------------- if (isValidMrz) { - val mrzInfoTemp: MRZInfo? = - buildTempMrz(documentNumber, dateOfBirthDay, expiryDate, docType) - if (mrzInfoTemp != null) { - mrzInfo = mrzInfoTemp - } + val line1 = matcherPassportTD3Line1.group(0) + val line2 = matcherPassportTD3Line2.group(0) + + if (line1 == null || line2 == null) return null + + val mrzRaw = line1 + line2 + val info = MRZInfo(mrzRaw) + return MrzFilterResult(mrzInfo = info, mrzRaw = mrzRaw) } - return mrzInfo + return null } } @@ -244,80 +238,13 @@ class OcrUtil { return null } - @Synchronized - private fun buildTempMrz( - documentNumber: String, - dateOfBirth: String, - expiryDate: String, - docType: DocType - ): MRZInfo? { - var mrzInfo: MRZInfo? = null - try { - if (docType === DocType.ID_CARD) { - mrzInfo = MRZInfo( - "P", - "NNN", - "", - "", - documentNumber, - "NNN", - dateOfBirth, - Gender.UNSPECIFIED, - expiryDate, - "" - ) - } - if (docType === DocType.PASSPORT) { - mrzInfo = MRZInfo( - "P", - "NNN", - "", - "", - documentNumber, - "NNN", - dateOfBirth, - Gender.UNSPECIFIED, - expiryDate, - "" - ) - } - if (docType === DocType.FRONT) { - mrzInfo = MRZInfo( - "P", - "NNN", - "", - "", - documentNumber, - "NNN", - dateOfBirth, - Gender.UNSPECIFIED, - expiryDate, - "" - ) - } - if (docType === DocType.OLD_ID) { - mrzInfo = MRZInfo( - "P", - "NNN", - "", - "", - documentNumber, - "NNN", - dateOfBirth, - Gender.UNSPECIFIED, - expiryDate, - "" - ) - } - } catch (_: Exception) { - } - return mrzInfo - } - companion object { - private const val ID_CARD_TD_1_LINE_1_REGEX = "([A|C|I][A-Z0-9<]{1})([A-Z]{3})([A-Z0-9<]{31})" + // TD1 (3 lines x 30 chars) + private const val ID_CARD_TD_1_LINE_1_REGEX = + "([A|C|I][A-Z0-9<]{1})([A-Z]{3})([A-Z0-9<]{25})" // 30 total private const val ID_CARD_TD_1_LINE_2_REGEX = - "([0-9]{6})([0-9]{1})([M|F|X|<]{1})([0-9]{6})([0-9]{1})([A-Z]{3})([A-Z0-9<]{11})([0-9]{1})" + "([0-9]{6})([0-9]{1})([M|F|X|<]{1})([0-9]{6})([0-9]{1})([A-Z]{3})([A-Z0-9<]{11})([0-9]{1})" // 30 total + private const val ID_CARD_TD_1_LINE_3_REGEX = "([A-Z0-9<]{30})" private const val PASSPORT_TD_3_LINE_1_REGEX = "(P[A-Z0-9<]{1})([A-Z]{3})([A-Z0-9<]{39})" private const val PASSPORT_TD_3_LINE_2_REGEX = "([A-Z0-9<]{9})([0-9]{1})([A-Z]{3})([0-9]{6})([0-9]{1})([M|F|X|<]{1})([0-9]{6})([0-9]{1})([A-Z0-9<]{14})([0-9]{1})([0-9]{1})" @@ -328,6 +255,6 @@ class OcrUtil { enum class DocType { - PASSPORT, ID_CARD, OTHER, FRONT, OLD_ID, OLD_DRIVER, NEW_DRIVER + PASSPORT, ID_CARD, } } diff --git a/android/src/main/java/com/mrzreader/view/CameraPreviewView.kt b/android/src/main/java/com/mrzreader/view/CameraPreviewView.kt index 8e20be8..4584d52 100644 --- a/android/src/main/java/com/mrzreader/view/CameraPreviewView.kt +++ b/android/src/main/java/com/mrzreader/view/CameraPreviewView.kt @@ -1,43 +1,45 @@ package com.mrzreader.view -import android.graphics.SurfaceTexture import android.util.AttributeSet -import android.view.Surface -import android.view.TextureView +import android.view.Gravity +import android.widget.FrameLayout import androidx.camera.core.AspectRatio import androidx.camera.core.Camera import androidx.camera.core.CameraSelector -import androidx.camera.core.DisplayOrientedMeteringPointFactory -import androidx.camera.core.FocusMeteringAction import androidx.camera.core.ImageAnalysis import androidx.camera.core.Preview -import androidx.camera.core.SurfaceRequest import androidx.camera.core.resolutionselector.AspectRatioStrategy import androidx.camera.core.resolutionselector.ResolutionSelector import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.camera.view.PreviewView import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner import com.facebook.react.bridge.ReactContext import com.facebook.react.modules.core.DeviceEventManagerModule import com.mrzreader.analyzer.MrzReaderAnalyzer +import com.mrzreader.extensions.installHierarchyFitter +import com.mrzreader.types.PreviewViewType +import com.mrzreader.types.ResizeMode import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.util.concurrent.ExecutorService import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit import kotlin.math.abs import kotlin.math.min class CameraPreviewView @JvmOverloads constructor( private val reactContext: ReactContext, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : TextureView(reactContext, attrs, defStyleAttr), TextureView.SurfaceTextureListener { +) : FrameLayout(reactContext, attrs, defStyleAttr) { companion object { private const val RATIO_4_3_VALUE = 4.0 / 3.0 private const val RATIO_16_9_VALUE = 16.0 / 9.0 } + private var previewView: PreviewView? = null + private val mainCoroutineScope = CoroutineScope(Dispatchers.Main) + private val mrzReaderAnalyzer = MrzReaderAnalyzer(reactContext) private val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor() private lateinit var surfaceProvider: Preview.SurfaceProvider @@ -48,11 +50,20 @@ class CameraPreviewView @JvmOverloads constructor( private var camera: Camera? = null - private var lastTouchX: Float = 0f - private var lastTouchY: Float = 0f + var androidPreviewViewType: PreviewViewType = PreviewViewType.SURFACE_VIEW + set(value) { + field = value + updatePreview() + } + var resizeMode: ResizeMode = ResizeMode.COVER + set(value) { + field = value + updatePreview() + } init { - surfaceTextureListener = this + this.installHierarchyFitter() + updatePreview() mrzReaderAnalyzer.mrzInfo.observeForever { val mrz = it.toString().replace(Regex("\\s+"), "") @@ -61,18 +72,34 @@ class CameraPreviewView @JvmOverloads constructor( sendMessageToReactNative(mrz) } } + } - setOnTouchListener { _, event -> - if (event.action == android.view.MotionEvent.ACTION_DOWN) { - lastTouchX = event.x - lastTouchY = event.y - performClick() - - true - } else false + private fun updatePreview() { + mainCoroutineScope.launch { + if (previewView == null) { + previewView = createPreviewView() + addView(previewView) + } + previewView?.let { + // Update implementation type from React + it.implementationMode = androidPreviewViewType.toPreviewImplementationMode() + // Update scale type from React + it.scaleType = resizeMode.toScaleType() + } + startCamera() } } + private fun createPreviewView(): PreviewView = + PreviewView(context).also { + it.installHierarchyFitter() + it.layoutParams = LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT, + Gravity.CENTER + ) + } + fun setDocType(docType: String) { mrzReaderAnalyzer.setDocType(docType) } @@ -88,28 +115,6 @@ class CameraPreviewView @JvmOverloads constructor( } } - private fun focusOnPoint(x: Float, y: Float): Boolean { - camera?.let { - val factory = DisplayOrientedMeteringPointFactory( - display, - it.cameraInfo, - this.width / 2f, - this.height / 2f - ) - - val point = factory.createPoint(x, y) - - val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF) - .setAutoCancelDuration(5, TimeUnit.SECONDS).build() - - it.cameraControl.startFocusAndMetering(action) - - return true - } ?: run { - return false - } - } - private fun sendMessageToReactNative(message: String) { reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) .emit("onMRZRead", message) @@ -124,33 +129,6 @@ class CameraPreviewView @JvmOverloads constructor( return AspectRatio.RATIO_16_9 } - override fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture, width: Int, height: Int) { - val surface = Surface(surfaceTexture) - surfaceProvider = Preview.SurfaceProvider { request: SurfaceRequest -> - val resolution = request.resolution - surfaceTexture.setDefaultBufferSize(resolution.width, resolution.height) - request.provideSurface(surface, cameraExecutor) { - // Handle surface completion - } - } - startCamera() - } - - override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) { - } - - override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean { - return true - } - - override fun onSurfaceTextureUpdated(surface: SurfaceTexture) { - } - - override fun performClick(): Boolean { - super.performClick() - - return focusOnPoint(lastTouchX, lastTouchY) - } override fun onDetachedFromWindow() { super.onDetachedFromWindow() @@ -166,7 +144,7 @@ class CameraPreviewView @JvmOverloads constructor( ResolutionSelector.Builder().setAspectRatioStrategy( AspectRatioStrategy( aspectRatio(width, height), - AspectRatioStrategy.FALLBACK_RULE_NONE + AspectRatioStrategy.FALLBACK_RULE_AUTO ) ).build() @@ -177,7 +155,7 @@ class CameraPreviewView @JvmOverloads constructor( .setResolutionSelector(resolutionSelector) .build() .also { - it.setSurfaceProvider(this.surfaceProvider) + it.setSurfaceProvider(this.previewView?.surfaceProvider) } val imageAnalysis = ImageAnalysis.Builder() diff --git a/example/src/App.tsx b/example/src/App.tsx index c9487cb..044c21b 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -4,6 +4,7 @@ import { StyleSheet, View, Platform } from 'react-native'; import MrzReaderView, { CameraSelector, DocType, + ResizeMode, } from 'react-native-mrz-reader'; import * as Permissions from 'react-native-permissions'; @@ -42,6 +43,7 @@ export default function App() { cameraSelector={CameraSelector.Back} docType={DocType.Passport} style={styles.box} + resizeMode={ResizeMode.CONTAIN} onMRZRead={(mrz) => { console.log(mrz); }} diff --git a/src/index.tsx b/src/index.tsx index e9c1924..369d4ac 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -23,9 +23,20 @@ export enum DocType { Passport = 'PASSPORT', } +export enum PreviewViewType { + SURFACE_VIEW = 'surface-view', + TEXTURE_VIEW = 'texture-view', +} + +export enum ResizeMode { + COVER = 'cover', + CONTAIN = 'contain', +} export type MrzReaderProps = { onMRZRead: (mrz: string) => void; cameraSelector?: CameraSelector; + androidPreviewViewType?: PreviewViewType; + resizeMode?: ResizeMode; docType: DocType; style: ViewStyle; };