Merge pull request #349 from aarongrider/android-barcode
Fix Android barcode scanning
This commit is contained in:
commit
2e68fcb79f
@ -9,6 +9,7 @@ import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.hardware.SensorManager
|
||||
import android.media.AudioManager
|
||||
import android.media.MediaActionSound
|
||||
@ -17,6 +18,7 @@ import android.util.Log
|
||||
import android.view.*
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.camera.core.*
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
@ -32,6 +34,7 @@ import com.facebook.react.bridge.WritableMap
|
||||
import com.facebook.react.common.ReactConstants.TAG
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter
|
||||
import com.rncamerakit.barcode.BarcodeFrame
|
||||
import java.io.File
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
@ -39,24 +42,34 @@ import java.util.concurrent.Executors
|
||||
@SuppressLint("ViewConstructor") // Extra constructors unused. Not using visual layout tools
|
||||
class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObserver {
|
||||
private val currentContext: ThemedReactContext = context
|
||||
|
||||
private var camera: Camera? = null
|
||||
private var preview: Preview? = null
|
||||
private var imageCapture: ImageCapture? = null
|
||||
private var qrCodeAnalyzer: ImageAnalysis? = null
|
||||
private var cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||
private var camera: Camera? = null
|
||||
private var orientationListener: OrientationEventListener? = null
|
||||
private var viewFinder: PreviewView = PreviewView(context)
|
||||
private var barcodeFrame: BarcodeFrame? = null
|
||||
private var cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
|
||||
private var scanBarcode: Boolean = false
|
||||
private var lensType = CameraSelector.LENS_FACING_BACK
|
||||
private var autoFocus = "on"
|
||||
private var zoomMode = "on"
|
||||
private var cameraProvider: ProcessCameraProvider? = null
|
||||
private var outputPath: String? = null
|
||||
private var shutterAnimationDuration: Int = 50
|
||||
private var effectLayer = View(context)
|
||||
private var counter = 0
|
||||
|
||||
// Camera Props
|
||||
private var lensType = CameraSelector.LENS_FACING_BACK
|
||||
private var autoFocus = "on"
|
||||
private var zoomMode = "on"
|
||||
|
||||
// Barcode Props
|
||||
private var scanBarcode: Boolean = false
|
||||
private var showFrame = false
|
||||
private var frameRect: Rect? = null
|
||||
private var frameColor = Color.GREEN
|
||||
private var laserColor = Color.RED
|
||||
|
||||
private fun getActivity() : Activity {
|
||||
return currentContext.currentActivity!!
|
||||
}
|
||||
@ -194,15 +207,17 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
.build()
|
||||
|
||||
val analyzer = QRCodeAnalyzer { barcodes ->
|
||||
if (barcodes.isNotEmpty()) {
|
||||
onBarcodeRead(barcodes)
|
||||
}
|
||||
}
|
||||
qrCodeAnalyzer!!.setAnalyzer(cameraExecutor, analyzer)
|
||||
|
||||
val useCases = mutableListOf(preview, imageCapture)
|
||||
if (scanBarcode) useCases.add(qrCodeAnalyzer!!)
|
||||
|
||||
if (scanBarcode) {
|
||||
val analyzer = QRCodeAnalyzer { barcodes ->
|
||||
if (barcodes.isNotEmpty()) {
|
||||
onBarcodeRead(barcodes)
|
||||
}
|
||||
}
|
||||
qrCodeAnalyzer!!.setAnalyzer(cameraExecutor, analyzer)
|
||||
useCases.add(qrCodeAnalyzer!!)
|
||||
}
|
||||
|
||||
try {
|
||||
// Unbind use cases before rebinding
|
||||
@ -317,10 +332,10 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
|
||||
|
||||
private fun onBarcodeRead(barcodes: List<String>) {
|
||||
val event: WritableMap = Arguments.createMap()
|
||||
event.putArray("barcodes", Arguments.makeNativeArray(barcodes))
|
||||
event.putString("codeStringValue", barcodes.first())
|
||||
currentContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(
|
||||
id,
|
||||
"onBarcodeRead",
|
||||
"onReadCode",
|
||||
event
|
||||
)
|
||||
}
|
||||
@ -409,6 +424,41 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs
|
||||
outputPath = path
|
||||
}
|
||||
|
||||
fun setShowFrame(enabled: Boolean) {
|
||||
if (enabled) {
|
||||
barcodeFrame = BarcodeFrame(context)
|
||||
val actualPreviewWidth = resources.displayMetrics.widthPixels
|
||||
val actualPreviewHeight = resources.displayMetrics.heightPixels
|
||||
val height: Int = convertDeviceHeightToSupportedAspectRatio(actualPreviewWidth, actualPreviewHeight)
|
||||
barcodeFrame!!.setFrameColor(frameColor)
|
||||
barcodeFrame!!.setLaserColor(laserColor)
|
||||
(barcodeFrame as View).layout(0, 0, actualPreviewWidth, height)
|
||||
addView(barcodeFrame)
|
||||
} else if (barcodeFrame != null) {
|
||||
removeView(barcodeFrame)
|
||||
barcodeFrame = null
|
||||
}
|
||||
}
|
||||
|
||||
fun setLaserColor(@ColorInt color: Int) {
|
||||
laserColor = color
|
||||
if (barcodeFrame != null) {
|
||||
barcodeFrame!!.setLaserColor(laserColor)
|
||||
}
|
||||
}
|
||||
|
||||
fun setFrameColor(@ColorInt color: Int) {
|
||||
frameColor = color
|
||||
if (barcodeFrame != null) {
|
||||
barcodeFrame!!.setFrameColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertDeviceHeightToSupportedAspectRatio(actualWidth: Int, actualHeight: Int): Int {
|
||||
val maxScreenRatio = 16 / 9f
|
||||
return (if (actualHeight / actualWidth > maxScreenRatio) actualWidth * maxScreenRatio else actualHeight).toInt()
|
||||
}
|
||||
|
||||
private fun hasPermissions(): Boolean {
|
||||
val requiredPermissions = arrayOf(Manifest.permission.CAMERA)
|
||||
if (requiredPermissions.all {
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
package com.rncamerakit
|
||||
|
||||
import android.graphics.Color
|
||||
import android.util.Log
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.camera.view.CameraView
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import com.facebook.react.bridge.ReadableType
|
||||
import com.facebook.react.common.MapBuilder
|
||||
@ -9,6 +12,7 @@ import com.facebook.react.uimanager.SimpleViewManager
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.facebook.react.uimanager.annotations.ReactProp
|
||||
|
||||
|
||||
class CKCameraManager : SimpleViewManager<CKCamera>() {
|
||||
|
||||
override fun getName() : String {
|
||||
@ -42,7 +46,7 @@ class CKCameraManager : SimpleViewManager<CKCamera>() {
|
||||
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
|
||||
return MapBuilder.of(
|
||||
"onOrientationChange", MapBuilder.of("registrationName", "onOrientationChange"),
|
||||
"onBarcodeRead", MapBuilder.of("registrationName", "onBarcodeRead"),
|
||||
"onReadCode", MapBuilder.of("registrationName", "onReadCode"),
|
||||
"onPictureTaken", MapBuilder.of("registrationName", "onPictureTaken")
|
||||
)
|
||||
}
|
||||
@ -72,6 +76,21 @@ class CKCameraManager : SimpleViewManager<CKCamera>() {
|
||||
view.setScanBarcode(enabled)
|
||||
}
|
||||
|
||||
@ReactProp(name = "showFrame")
|
||||
fun setShowFrame(view: CKCamera, enabled: Boolean) {
|
||||
view.setShowFrame(enabled)
|
||||
}
|
||||
|
||||
@ReactProp(name = "laserColor", defaultInt = Color.RED)
|
||||
fun setLaserColor(view: CKCamera, @ColorInt color: Int) {
|
||||
view.setLaserColor(color)
|
||||
}
|
||||
|
||||
@ReactProp(name = "frameColor", defaultInt = Color.GREEN)
|
||||
fun setFrameColor(view: CKCamera, @ColorInt color: Int) {
|
||||
view.setFrameColor(color)
|
||||
}
|
||||
|
||||
@ReactProp(name = "outputPath")
|
||||
fun setOutputPath(view: CKCamera, path: String) {
|
||||
view.setOutputPath(path)
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
package com.rncamerakit.barcode
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.view.View
|
||||
import androidx.annotation.ColorInt
|
||||
|
||||
import com.rncamerakit.R
|
||||
|
||||
class BarcodeFrame(context: Context) : View(context) {
|
||||
|
||||
private var dimPaint: Paint = Paint()
|
||||
private var framePaint: Paint = Paint()
|
||||
private var borderPaint: Paint = Paint()
|
||||
private var laserPaint: Paint = Paint()
|
||||
var frameRect: Rect = Rect()
|
||||
|
||||
private var frameWidth = 0
|
||||
private var frameHeight = 0
|
||||
private var borderMargin = 0
|
||||
private var previousFrameTime = System.currentTimeMillis()
|
||||
private var laserY = 0
|
||||
|
||||
private fun init(context: Context) {
|
||||
framePaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
|
||||
dimPaint.style = Paint.Style.FILL
|
||||
dimPaint.color = context.resources.getColor(R.color.bg_dark)
|
||||
borderPaint = Paint()
|
||||
borderPaint.style = Paint.Style.STROKE
|
||||
borderPaint.strokeWidth = STROKE_WIDTH.toFloat()
|
||||
laserPaint.style = Paint.Style.STROKE
|
||||
laserPaint.strokeWidth = STROKE_WIDTH.toFloat()
|
||||
borderMargin = context.resources.getDimensionPixelSize(R.dimen.border_length)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
frameWidth = measuredWidth
|
||||
frameHeight = measuredHeight
|
||||
val marginWidth = width / WIDTH_SCALE
|
||||
val marginHeight = (height / HEIGHT_SCALE).toInt()
|
||||
frameRect.left = marginWidth
|
||||
frameRect.right = width - marginWidth
|
||||
frameRect.top = marginHeight
|
||||
frameRect.bottom = height - marginHeight
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
val timeElapsed = System.currentTimeMillis() - previousFrameTime
|
||||
super.onDraw(canvas)
|
||||
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), dimPaint)
|
||||
canvas.drawRect(frameRect, framePaint)
|
||||
drawBorder(canvas)
|
||||
drawLaser(canvas, timeElapsed)
|
||||
previousFrameTime = System.currentTimeMillis()
|
||||
this.invalidate(frameRect)
|
||||
}
|
||||
|
||||
private fun drawBorder(canvas: Canvas) {
|
||||
canvas.drawLine(frameRect.left.toFloat(), frameRect.top.toFloat(), frameRect.left.toFloat(), (frameRect.top + borderMargin).toFloat(), borderPaint)
|
||||
canvas.drawLine(frameRect.left.toFloat(), frameRect.top.toFloat(), (frameRect.left + borderMargin).toFloat(), frameRect.top.toFloat(), borderPaint)
|
||||
canvas.drawLine(frameRect.left.toFloat(), frameRect.bottom.toFloat(), frameRect.left.toFloat(), (frameRect.bottom - borderMargin).toFloat(), borderPaint)
|
||||
canvas.drawLine(frameRect.left.toFloat(), frameRect.bottom.toFloat(), (frameRect.left + borderMargin).toFloat(), frameRect.bottom.toFloat(), borderPaint)
|
||||
canvas.drawLine(frameRect.right.toFloat(), frameRect.top.toFloat(), (frameRect.right - borderMargin).toFloat(), frameRect.top.toFloat(), borderPaint)
|
||||
canvas.drawLine(frameRect.right.toFloat(), frameRect.top.toFloat(), frameRect.right.toFloat(), (frameRect.top + borderMargin).toFloat(), borderPaint)
|
||||
canvas.drawLine(frameRect.right.toFloat(), frameRect.bottom.toFloat(), frameRect.right.toFloat(), (frameRect.bottom - borderMargin).toFloat(), borderPaint)
|
||||
canvas.drawLine(frameRect.right.toFloat(), frameRect.bottom.toFloat(), (frameRect.right - borderMargin).toFloat(), frameRect.bottom.toFloat(), borderPaint)
|
||||
}
|
||||
|
||||
private fun drawLaser(canvas: Canvas, timeElapsed: Long) {
|
||||
if (laserY > frameRect.bottom || laserY < frameRect.top) laserY = frameRect.top
|
||||
canvas.drawLine((frameRect.left + STROKE_WIDTH).toFloat(), laserY.toFloat(), (frameRect.right - STROKE_WIDTH).toFloat(), laserY.toFloat(), laserPaint)
|
||||
laserY += (timeElapsed / ANIMATION_SPEED).toInt()
|
||||
}
|
||||
|
||||
fun setFrameColor(@ColorInt borderColor: Int) {
|
||||
borderPaint.color = borderColor
|
||||
}
|
||||
|
||||
fun setLaserColor(@ColorInt laserColor: Int) {
|
||||
laserPaint.color = laserColor
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val STROKE_WIDTH = 5
|
||||
private const val ANIMATION_SPEED = 8
|
||||
private const val WIDTH_SCALE = 7
|
||||
private const val HEIGHT_SCALE = 2.75
|
||||
}
|
||||
|
||||
init {
|
||||
init(context)
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ export default class BarcodeScreenExample extends Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
example: undefined,
|
||||
value: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@ -23,8 +24,8 @@ export default class BarcodeScreenExample extends Component {
|
||||
|
||||
render() {
|
||||
if (this.state.example) {
|
||||
const CameraScreen = this.state.example;
|
||||
return <CameraScreen />;
|
||||
const Screen = this.state.example;
|
||||
return <Screen value={this.state.value} />;
|
||||
}
|
||||
return (
|
||||
<CameraScreen
|
||||
@ -35,14 +36,13 @@ export default class BarcodeScreenExample extends Component {
|
||||
off: require('../images/flashOff.png'),
|
||||
auto: require('../images/flashAuto.png'),
|
||||
}}
|
||||
showFrame
|
||||
scanBarcode
|
||||
laserColor={'blue'}
|
||||
surfaceColor={'black'}
|
||||
showFrame
|
||||
laserColor={'yellow'}
|
||||
frameColor={'yellow'}
|
||||
surfaceColor={'black'}
|
||||
onReadCode={(event) => {
|
||||
console.log(event);
|
||||
this.setState({ example: CheckingScreen });
|
||||
this.setState({ example: CheckingScreen, value: event.nativeEvent.codeStringValue });
|
||||
}}
|
||||
hideControls
|
||||
// offsetForScannerFrame = {10}
|
||||
|
||||
@ -13,10 +13,12 @@ export default class CheckingScreen extends Component {
|
||||
render() {
|
||||
if (this.state.example) {
|
||||
const CheckingScreen = this.state.example;
|
||||
return <CheckingScreen />;
|
||||
const value = this.state.value;
|
||||
return <CheckingScreen value={value} />;
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.valueText}>{this.props.value}</Text>
|
||||
<TouchableOpacity onPress={() => this.setState({ example: BarcodeScreen })}>
|
||||
<Text style={styles.buttonText}>Back button</Text>
|
||||
</TouchableOpacity>
|
||||
@ -32,6 +34,10 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
valueText: {
|
||||
marginBottom: 20,
|
||||
fontSize: 40,
|
||||
},
|
||||
buttonText: {
|
||||
color: 'blue',
|
||||
marginBottom: 20,
|
||||
|
||||
@ -25,7 +25,7 @@ function Camera(props, ref) {
|
||||
_.update(transformedProps, 'laserColor', (c) => processColor(c));
|
||||
_.update(transformedProps, 'surfaceColor', (c) => processColor(c));
|
||||
|
||||
return <NativeCamera flashMode={props.flashMode} ref={nativeRef} {...props} />;
|
||||
return <NativeCamera flashMode={props.flashMode} ref={nativeRef} {...transformedProps} />;
|
||||
}
|
||||
|
||||
const { PORTRAIT, PORTRAIT_UPSIDE_DOWN, LANDSCAPE_LEFT, LANDSCAPE_RIGHT } = RNCameraKitModule.getConstants();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user