package com.barcodescanner import android.Manifest import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.util.AttributeSet import android.util.Log import android.view.Choreographer import android.view.View import android.view.ViewGroup import android.widget.TextView import com.google.zxing.MultiFormatReader import com.google.zxing.Result import me.dm7.barcodescanner.core.IViewFinder import me.dm7.barcodescanner.zxing.ZXingScannerView class BarcodeScannerView : ZXingScannerView, ZXingScannerView.ResultHandler { constructor(context: Context?) : super(context) constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) private val callBacks = mutableListOf() private var flashEnabled: Boolean = false private var autoFocusEnabled: Boolean = false private var isCameraStarted: Boolean = false private var viewFinder: BarcodeViewFinder? = null private var frameWidth: Int? = null private var frameHeight: Int? = null init { setResultHandler(this) // relate to react native issue https://github.com/facebook/react-native/issues/17968 setupLayoutHack() setIsBorderCornerRounded(true) setBorderCornerRadius(10) } override fun createViewFinderView(context: Context): IViewFinder { if (viewFinder == null) { viewFinder = BarcodeViewFinder(context) } return viewFinder!! } fun setFrameWidth(width: Int) { frameWidth = width viewFinder?.layoutParams?.run { viewFinder?.layoutParams = ViewGroup.LayoutParams(width, frameHeight ?: this.height) } viewFinder?.setFrameWidth(width) } fun setFrameHeight(height: Int) { frameHeight = height viewFinder?.layoutParams?.run { viewFinder?.layoutParams = ViewGroup.LayoutParams(frameWidth ?: this.width, height) } viewFinder?.setFrameHeight(height) } private fun setupLayoutHack() { Choreographer.getInstance().postFrameCallback(object : Choreographer.FrameCallback { override fun doFrame(frameTimeNanos: Long) { manuallyLayoutChildren() viewTreeObserver.dispatchOnGlobalLayout() Choreographer.getInstance().postFrameCallback(this) } }) } fun manuallyLayoutChildren() { for (i in 0 until childCount) { val child = getChildAt(i) child.measure( MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY) ) child.layout(0, 0, child.measuredWidth, child.measuredHeight) } } fun setFlashModeEnabled(enabled: Boolean) { flashEnabled = enabled if (isCameraStarted) { flash = enabled } } fun setAutoFocusEnabled(enabled: Boolean) { autoFocusEnabled = enabled if (isCameraStarted) { setAutoFocus(enabled) } } fun setOnResultListener(callBack: (String) -> Unit) { if (!callBacks.contains(callBack)) { callBacks.add(callBack) } } override fun handleResult(rawResult: Result?) { val scanResult = rawResult?.text if (scanResult != null) { callBacks.onEach { it.invoke(scanResult) } } resumeCameraPreview(this) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() callBacks.clear() isCameraStarted = false } fun startCameraPreview() { if (hasCameraPermission() && !isCameraStarted) { setResultHandler(this) isCameraStarted = true startCamera() flash = flashEnabled setAutoFocus(autoFocusEnabled) } } override fun stopCamera() { super.stopCamera() isCameraStarted = false } private fun hasCameraPermission(): Boolean = context.isPermissionAlreadyPermit(Manifest.permission.CAMERA) }