package <%= java_package %> import android.app.Activity import android.content.Intent import android.os.Bundle import android.util.Size import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageButton import androidx.annotation.OptIn import androidx.appcompat.app.AppCompatActivity import androidx.camera.core.* import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.view.PreviewView import androidx.core.content.ContextCompat import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage import java.util.concurrent.Executors /** * Full-screen barcode/QR scanner activity. * * Uses CameraX + ML Kit BarcodeScanning. * * Required build.gradle dependencies: * implementation 'androidx.camera:camera-camera2:1.3.0' * implementation 'androidx.camera:camera-lifecycle:1.3.0' * implementation 'androidx.camera:camera-view:1.3.0' * implementation 'com.google.mlkit:barcode-scanning:17.2.0' * * Required AndroidManifest.xml entry: * */ class MobScannerActivity : AppCompatActivity() { private val executor = Executors.newSingleThreadExecutor() private var scanHandled = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val container = FrameLayout(this) setContentView(container) val previewView = PreviewView(this).also { it.layoutParams = FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) container.addView(it) } // Cancel button val cancelBtn = ImageButton(this).also { it.setImageResource(android.R.drawable.ic_menu_close_clear_cancel) it.layoutParams = FrameLayout.LayoutParams(128, 128).apply { setMargins(32, 80, 0, 0) } it.setOnClickListener { setResult(Activity.RESULT_CANCELED); finish() } container.addView(it) } val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() val preview = Preview.Builder().build().also { it.setSurfaceProvider(previewView.surfaceProvider) } val imageAnalyzer = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() .also { analysis -> val scanner = BarcodeScanning.getClient() analysis.setAnalyzer(executor) { imageProxy -> @OptIn(ExperimentalGetImage::class) val mediaImage = imageProxy.image if (mediaImage != null && !scanHandled) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) scanner.process(image) .addOnSuccessListener { barcodes -> barcodes.firstOrNull()?.rawValue?.let { value -> if (!scanHandled) { scanHandled = true val type = when (barcodes.first().format) { Barcode.FORMAT_QR_CODE -> "qr" Barcode.FORMAT_EAN_13 -> "ean13" Barcode.FORMAT_EAN_8 -> "ean8" Barcode.FORMAT_CODE_128 -> "code128" Barcode.FORMAT_CODE_39 -> "code39" Barcode.FORMAT_PDF417 -> "pdf417" Barcode.FORMAT_AZTEC -> "aztec" Barcode.FORMAT_DATA_MATRIX -> "data_matrix" else -> "qr" } val result = Intent().apply { putExtra("scan_value", value) putExtra("scan_type", type) } setResult(Activity.RESULT_OK, result) finish() } } } .addOnCompleteListener { imageProxy.close() } } else { imageProxy.close() } } } try { cameraProvider.unbindAll() cameraProvider.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageAnalyzer) } catch (e: Exception) { setResult(Activity.RESULT_CANCELED); finish() } }, ContextCompat.getMainExecutor(this)) } override fun onDestroy() { super.onDestroy() executor.shutdown() } }