Skip to content

Commit

Permalink
Merge pull request #228 from myofficework000/barcode_scanner
Browse files Browse the repository at this point in the history
added barcode scanner implementation
  • Loading branch information
myofficework000 authored Jan 4, 2024
2 parents 291ab17 + 9d8e0ff commit fd122ab
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 2 deletions.
4 changes: 3 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
androidTestImplementation platform('androidx.compose:compose-bom:2023.03.00')
androidTestImplementation "io.mockk:mockk-android:1.13.4"
androidTestImplementation "io.mockk:mockk-agent:1.13.4"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"

Expand Down Expand Up @@ -173,7 +175,7 @@ dependencies {
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-view:${camerax_version}"
implementation 'com.google.mlkit:barcode-scanning:17.1.0'
implementation 'com.google.mlkit:barcode-scanning:17.2.0'

// Compose Constraint Layout
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.example.jetpack_compose_all_in_one.demos.barcode_scanner

import android.util.Size
import android.view.ViewGroup
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
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

enum class ScanMode {
QR_CODE,
BARCODE,
BOTH
}
@Composable
@androidx.annotation.OptIn(androidx.camera.core.ExperimentalGetImage::class)
@androidx.camera.core.ExperimentalGetImage
fun CameraPreview(
modifier: Modifier,
onBarcodeDetected: (Barcode) -> Unit,
scanMode: ScanMode = ScanMode.BOTH
) {
val context = LocalContext.current
val lifecycleOwner = LocalContext.current as LifecycleOwner
val cameraExecutor = Executors.newSingleThreadExecutor()
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()
}
val barcodeScanner = BarcodeScanning.getClient(options)

AndroidView(
modifier = modifier,
factory = { context ->
val previewView = PreviewView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}

val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}

val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(1280, 720))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.also {
it.setAnalyzer(cameraExecutor, { imageProxy ->
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
barcodeScanner.process(image)
.addOnSuccessListener { barcodes ->
for (barcode in barcodes) {
onBarcodeDetected(barcode)
}
}
.addOnCompleteListener {
imageProxy.close()
}
}
})
}
try {
cameraProvider.unbindAll()
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
@@ -0,0 +1,57 @@
package com.example.jetpack_compose_all_in_one.demos.barcode_scanner

import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import android.Manifest
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
@androidx.annotation.OptIn(androidx.camera.core.ExperimentalGetImage::class)
fun ScannerApp() {
var scanMode by remember { mutableStateOf(ScanMode.BOTH) }
var lastScannedCode by remember { mutableStateOf("") }
var hasCameraPermission by remember { mutableStateOf(false) }

val permissionResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { hasCameraPermission = it }
)

LaunchedEffect(key1 = true) {
permissionResult.launch(Manifest.permission.CAMERA)
}

Column {
Row {
Text(text = "QR Code")
RadioButton(selected = scanMode == ScanMode.QR_CODE, onClick = { scanMode = ScanMode.QR_CODE })
Text(text = "Barcode")
RadioButton(selected = scanMode == ScanMode.BARCODE, onClick = { scanMode = ScanMode.BARCODE })
Text(text = "Both")
RadioButton(selected = scanMode == ScanMode.BOTH, onClick = { scanMode = ScanMode.BOTH })
}
if (hasCameraPermission) {
CameraPreview(
modifier = Modifier.weight(1f),
onBarcodeDetected = { barcode ->
lastScannedCode = barcode.rawValue ?: "No barcode data"
},
scanMode = scanMode)
Text(text = "Last Scanned Code: $lastScannedCode")
} else {
Text(text = "Camera permission not granted")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.example.jetpack_compose_all_in_one.application_components.broadcastre
import com.example.jetpack_compose_all_in_one.application_components.content_provider.ContentProviderScreen
import com.example.jetpack_compose_all_in_one.application_components.content_provider.demo_contacts.ContactList
import com.example.jetpack_compose_all_in_one.application_components.content_provider.demo_images.ShowImages
import com.example.jetpack_compose_all_in_one.demos.barcode_scanner.ScannerApp
import com.example.jetpack_compose_all_in_one.demos.currency_converter.presentation.view.CurrencyFromToScreen
import com.example.jetpack_compose_all_in_one.demos.github_api.presentation.view.GithubUserListScreen
import com.example.jetpack_compose_all_in_one.demos.history_of_day.HistoryOfTheDayUI
Expand Down Expand Up @@ -314,6 +315,9 @@ fun MainContainerOfApp(
composable(NavDes.quiz.route()){
QuizScreen()
}
composable(NavDes.barcodeScanner.route()){
ScannerApp()
}

composable(NavDes.ChatDemoUI.route()) {
val vm = hiltViewModel<ChatViewModel>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,4 +298,7 @@ object NavConstants {

const val GRAPHQL_DEMO = "GraphQL Demo 1"
const val GRAPHQL_DEMO_ABOUT = "GraphQL Demo (Countries)"

const val BarcodeScannerUI = "BarcodeScannerUI"
const val BarcodeScannerUI_ABOUT = "BarcodeScanner"
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import com.example.jetpack_compose_all_in_one.utils.navigation.NavConstants.BOUN
import com.example.jetpack_compose_all_in_one.utils.navigation.NavConstants.BOUND_SERVICE_ABOUT
import com.example.jetpack_compose_all_in_one.utils.navigation.NavConstants.BROADCAST_RECEIVERS
import com.example.jetpack_compose_all_in_one.utils.navigation.NavConstants.BROADCAST_RECEIVERS_ABOUT
import com.example.jetpack_compose_all_in_one.utils.navigation.NavConstants.BarcodeScannerUI
import com.example.jetpack_compose_all_in_one.utils.navigation.NavConstants.BarcodeScannerUI_ABOUT
import com.example.jetpack_compose_all_in_one.utils.navigation.NavConstants.CHATGPT_DEMO
import com.example.jetpack_compose_all_in_one.utils.navigation.NavConstants.CHATGPT_DEMO_ABOUT
import com.example.jetpack_compose_all_in_one.utils.navigation.NavConstants.CHAT_DEMO_UI
Expand Down Expand Up @@ -439,6 +441,7 @@ sealed class NavDes(val data: INavigationDrawerItem, val customAppBarStringId: I
object githubUserList : NavDes(NavigationDrawerData(GithubUserListUI, GithubUserListUI_About))

object youTube : NavDes(NavigationDrawerData(YoutubeUI, YoutubeUI_ABOUT))
object barcodeScanner: NavDes(NavigationDrawerData(BarcodeScannerUI, BarcodeScannerUI_ABOUT))
object news : NavDes(NavigationDrawerData(NewsUI, NewsUI_ABOUT))

object polls : NavDes(NavigationDrawerData(PollUI, PollUI_About))
Expand Down Expand Up @@ -557,7 +560,8 @@ sealed class NavDes(val data: INavigationDrawerItem, val customAppBarStringId: I
news,
polls,
quiz,
NewsApiHeadlineSwipe
NewsApiHeadlineSwipe,
barcodeScanner
), DEMO_UI
)
)
Expand Down

0 comments on commit fd122ab

Please sign in to comment.