Skip to content

Commit

Permalink
Fix the bug for barcode scanner example
Browse files Browse the repository at this point in the history
  • Loading branch information
myofficework000 committed Mar 18, 2024
1 parent e7b181b commit 351c25c
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
Expand All @@ -27,7 +28,7 @@ import com.example.jetpack_compose_all_in_one.utils.LogicPager
@Composable
private fun ContentProviderContent() {
// Remembering the current page using mutableStateOf
val currentPage = rememberSaveable { mutableStateOf(0) }
val currentPage = rememberSaveable { mutableIntStateOf(0) }

// LogicPager composable for handling page navigation
LogicPager(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,24 @@ import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import java.util.concurrent.Executors

/**
* ScanMode is an enum class representing different scan modes supported by the camera preview.
* It includes options for scanning QR codes, barcodes, or both.
*/
enum class ScanMode {
QR_CODE,
BARCODE,
BOTH
}

/**
* CameraPreview is a composable function used to display a camera preview for scanning QR codes and barcodes.
* It leverages the CameraX API for camera management and the ML Kit Barcode Scanning API for barcode detection.
*
* @param modifier The modifier for the camera preview.
* @param onBarcodeDetected Callback function to handle the detected barcode.
* @param scanMode The scan mode to specify the type of codes to be scanned (default: ScanMode.BOTH).
*/
@Composable
@androidx.annotation.OptIn(androidx.camera.core.ExperimentalGetImage::class)
@androidx.camera.core.ExperimentalGetImage
Expand All @@ -32,68 +45,97 @@ fun CameraPreview(
onBarcodeDetected: (Barcode) -> Unit,
scanMode: ScanMode = ScanMode.BOTH
) {
// Retrieve the current context and lifecycle owner
val context = LocalContext.current
val lifecycleOwner = LocalContext.current as LifecycleOwner

// Executor for camera operations
val cameraExecutor = Executors.newSingleThreadExecutor()

// Configure barcode scanner options based on the specified scan mode
val options = when (scanMode) {
ScanMode.QR_CODE -> BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
.build()

ScanMode.BARCODE -> BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_CODE_128, Barcode.FORMAT_CODE_39)
.build()

ScanMode.BOTH -> BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
.build()
}

// Initialize the barcode scanner client
val barcodeScanner = BarcodeScanning.getClient(options)

// Create an AndroidView to integrate CameraX preview
AndroidView(
modifier = modifier,
factory = { context ->
// Create a PreviewView to display the camera preview
val previewView = PreviewView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}

// Obtain the camera provider
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

// Configure the camera preview
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}

// Configure image analysis for barcode detection
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(1280, 720))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.also {
it.setAnalyzer(cameraExecutor, { imageProxy ->
it.setAnalyzer(cameraExecutor) { imageProxy ->
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
val image = InputImage.fromMediaImage(
mediaImage,
imageProxy.imageInfo.rotationDegrees
)
// Process the image for barcode detection
barcodeScanner.process(image)
.addOnSuccessListener { barcodes ->
// Invoke callback function for each detected barcode
for (barcode in barcodes) {
onBarcodeDetected(barcode)
}
}
.addOnCompleteListener {
// Close the image proxy after processing
imageProxy.close()
}
}
})
}
}

// Bind the camera preview and image analysis to the camera lifecycle
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageAnalysis)
cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
imageAnalysis
)
} catch (e: Exception) {
e.printStackTrace()
}
}, ContextCompat.getMainExecutor(context))

previewView
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,32 @@ import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

/**
* ScannerApp is a composable function that represents the main screen of the scanner application.
* This function integrates camera preview for scanning QR codes and barcodes and displays the last scanned code.
*/
@Composable
@androidx.annotation.OptIn(androidx.camera.core.ExperimentalGetImage::class)
fun ScannerApp() {
// State variables to track scan mode, last scanned code, and camera permission status
var scanMode by remember { mutableStateOf(ScanMode.BOTH) }
var lastScannedCode by remember { mutableStateOf("") }
var hasCameraPermission by remember { mutableStateOf(false) }

// Request camera permission and update state based on the result
val permissionResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { hasCameraPermission = it }
)

// Request camera permission when the component is launched
LaunchedEffect(key1 = true) {
permissionResult.launch(Manifest.permission.CAMERA)
}

// Column layout to organize UI components vertically
Column {
// Row to display scan mode options (QR code, barcode, both)
Row {
Text(text = "QR Code")
RadioButton(selected = scanMode == ScanMode.QR_CODE, onClick = { scanMode = ScanMode.QR_CODE })
Expand All @@ -42,16 +51,21 @@ fun ScannerApp() {
Text(text = "Both")
RadioButton(selected = scanMode == ScanMode.BOTH, onClick = { scanMode = ScanMode.BOTH })
}
// Check camera permission status and display camera preview or permission message accordingly
if (hasCameraPermission) {
// Display camera preview for scanning
CameraPreview(
modifier = Modifier.weight(1f),
onBarcodeDetected = { barcode ->
lastScannedCode = barcode.rawValue ?: "No barcode data"
},
// Update last scanned code when a barcode is detected
lastScannedCode = barcode.rawValue ?: "No barcode data"
},
scanMode = scanMode)
// Display the last scanned code
Text(text = "Last Scanned Code: $lastScannedCode")
} else {
// Display message when camera permission is not granted
Text(text = "Camera permission not granted")
}
}
}
}

0 comments on commit 351c25c

Please sign in to comment.