Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sarkiroka/177cb41ce560eacb8f4d650ce90e339f to your computer and use it in GitHub Desktop.
Save sarkiroka/177cb41ce560eacb8f4d650ce90e339f to your computer and use it in GitHub Desktop.
Android (Kotlin) Barcode Scanner with Preview Stealing: Zxing get preview and detect anything else than QR codes

This Android project demonstrates how to continuously capture the camera preview while also listening for barcode scans using the journeyapps/barcodescanner library. This is useful when you want to execute additional processing (like detecting custom patterns) on the camera preview.

Key Components:

build.gradle.kts:

dependencies {
...
    implementation("com.journeyapps:zxing-android-embedded:4.3.0")
...
}

layout.xml: Declares a DecoratedBarcodeView where the camera feed will be displayed.

...
    <com.journeyapps.barcodescanner.DecoratedBarcodeView
        android:id="@+id/barcodeView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
...

onCreate method: Initializes the camera and starts the periodic preview stealing function.

...
        barcodeView = findViewById(R.id.barcodeView)
        capture = CaptureManager(this, barcodeView)
        capture.initializeFromIntent(intent, savedInstanceState)
        capture.decode()
        periodicPreviewStealer()
        barcodeView.decodeContinuous(object : BarcodeCallback {
            override fun barcodeResult(result: BarcodeResult) {
                handleResult(result.result.text)
            }

            override fun possibleResultPoints(resultPoints: List<ResultPoint>) {}
        })
...

The preview / periodic stealer method: Uses a Handler to continuously request camera previews, process them, and run custom code (handleResult method) on the main thread if certain conditions are met (in this example, if foxesResult.found is true).

    private fun periodicPreviewStealer() {
        val handler = Handler(Looper.getMainLooper())
        val runnableCode = object : Runnable {
            private var hasImagePreview = false
            override fun run() {
                if (!isCameraPaused) { // class variable, set true when application onPause called for example
                    barcodeView.barcodeView.cameraInstance.requestPreview(object : PreviewCallback {
                        override fun onPreview(sourceData: SourceData) {
                            hasImagePreview = true
                            val bitmap = sourceData.bitmap
                            val width = bitmap.width
                            val height = bitmap.height
                            val size = width * height
                            val pixels = IntArray(size)
                            bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
                            val foxesResult = detectFoxes(width, height, pixels)
                            if (foxesResult.found) {
                                Handler(Looper.getMainLooper()).post {
                                    handleResult(foxesResult)
                                }
                            }
                            Handler(Looper.getMainLooper()).postDelayed({
                                periodicPreviewStealer()
                            }, 1000)
                        }

                        override fun onPreviewError(e: java.lang.Exception?) {
                            Log.e("periodicPreviewStealer", "Not yet implemented ${e?.message}")
                        }
                    })
                }
                if (!hasImagePreview) {
                    Handler(Looper.getMainLooper()).postDelayed({
                        if (!hasImagePreview) {
                            handler.postDelayed(this, 1)
                        }
                    }, 1000)
                }
            }
        }
        handler.post(runnableCode)
    }

Important Notes: The variable isCameraPaused should be managed based on your app's lifecycle. For instance, set it to true in onPause() and false in onResume() to ensure the camera preview stealing is paused and resumed appropriately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment