본문 바로가기
Android Application/기초 사용법

안드로이드 ML Kit - QR scan 구현

by sdchjjj 2023. 4. 1.
728x90

안녕하세요.

 

이번 포스팅에서는 ml kit과 androidx camera 라이브러리를 사용하여 QR scanner를 구현해 보겠습니다.

 

언어: 코틀린

sdk vsersion

  - compile: 33

  - min: 21

  - target: 33

 

dependency를 추가합니다.

build.gradle(:app)

dependencies {
    ...
    implementation 'com.google.mlkit:barcode-scanning:17.1.0'
    implementation "androidx.camera:camera-camera2:1.3.0-alpha05"
    implementation "androidx.camera:camera-lifecycle:1.3.0-alpha05"
    implementation "androidx.camera:camera-view:1.3.0-alpha05"
    ...
}

 

Manifest에 CAMERA 권한을 추가합니다.

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.laboratory">
    ...
    <uses-permission android:name="android.permission.CAMERA" />
    ...
</manifest>

 

QrScanActivity를 생성하여, 이곳에서 scan을 진행하겠습니다.

activity_qr_scan.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".QrScanActivity">

    <androidx.camera.view.PreviewView
        android:id="@+id/camera_preview"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:layout_marginTop="77dp"
        app:layout_constraintTop_toTopOf="parent"
        tools:background="@color/white" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

QrScanActivity.kt

class QrScanActivity : AppCompatActivity() {

    private var _binding: ActivityQrScanBinding? = null
    private val binding get() = _binding!!

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        _binding = ActivityQrScanBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}

 

 

ImageAnalysis.Analyzer를 상속받는 이미지 스캔 클래스와 이미지에서 정보를 따내는 함수를 작성합니다.

타입이 url일 경우 해당 url로 이동하도록 하겠습니다.

QrScanActivity.kt

inner class QrScanner : ImageAnalysis.Analyzer {

    @ExperimentalGetImage
    override fun analyze(image: ImageProxy) {
        val mediaImage = image.image
        if (mediaImage != null) {
            val inputImage =
                InputImage.fromMediaImage(mediaImage, image.imageInfo.rotationDegrees)
            val scanner = BarcodeScanning.getClient(
                BarcodeScannerOptions.Builder()
                    .setBarcodeFormats(
                        Barcode.FORMAT_QR_CODE,
                        Barcode.FORMAT_AZTEC
                    )
                    .build()
            )
            scanner.process(inputImage)
                .addOnSuccessListener { barcodes ->
                    scanCode(barcodes)
                }
            image.close()
        }
    }
}
private fun scanCode(barcodes: List<Barcode>) {
    for (barcode in barcodes) {
        // See API reference for complete list of supported types
        when (barcode.valueType) {
            Barcode.TYPE_URL -> {
                val url = barcode.url!!.url
                moveToQrContents(url)
            }
        }
    }
}
private fun moveToQrContents(contents: String?) {
    if (contents != null) {
        startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(contents)))
    }
}

setBarcodeFormats 부분에서 format 설정을 해줄 수 있으며, scanCode에서의 Type으로 barcode의 정보를 확인할 수 있습니다.

 

Format과 Type의 종류는 아래와 같습니다.

Barcode.class

public class Barcode {
    public static final int FORMAT_UNKNOWN = -1;
    public static final int FORMAT_ALL_FORMATS = 0;
    public static final int FORMAT_CODE_128 = 1;
    public static final int FORMAT_CODE_39 = 2;
    public static final int FORMAT_CODE_93 = 4;
    public static final int FORMAT_CODABAR = 8;
    public static final int FORMAT_DATA_MATRIX = 16;
    public static final int FORMAT_EAN_13 = 32;
    public static final int FORMAT_EAN_8 = 64;
    public static final int FORMAT_ITF = 128;
    public static final int FORMAT_QR_CODE = 256;
    public static final int FORMAT_UPC_A = 512;
    public static final int FORMAT_UPC_E = 1024;
    public static final int FORMAT_PDF417 = 2048;
    public static final int FORMAT_AZTEC = 4096;
    public static final int TYPE_UNKNOWN = 0;
    public static final int TYPE_CONTACT_INFO = 1;
    public static final int TYPE_EMAIL = 2;
    public static final int TYPE_ISBN = 3;
    public static final int TYPE_PHONE = 4;
    public static final int TYPE_PRODUCT = 5;
    public static final int TYPE_SMS = 6;
    public static final int TYPE_TEXT = 7;
    public static final int TYPE_URL = 8;
    public static final int TYPE_WIFI = 9;
    public static final int TYPE_GEO = 10;
    public static final int TYPE_CALENDAR_EVENT = 11;
    public static final int TYPE_DRIVER_LICENSE = 12;
}

 

권한을 확인하는 코드를 작성합니다.

QrScanActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    _binding = ActivityQrScanBinding.inflate(layoutInflater)
    setContentView(binding.root)
    checkCameraPermission()
}

private fun checkCameraPermission() {
    ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1)
}

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.CAMERA
        ) == PackageManager.PERMISSION_GRANTED
    ) {
        startCamera()
    }
}

 

카메라와 프리뷰 코드를 작성합니다.

QrScanActivity.kt

private fun startCamera() {
    ProcessCameraProvider.getInstance(this).addListener({
        try {
            val processCameraProvider =
                ProcessCameraProvider.getInstance(this).get() as ProcessCameraProvider
            bindPreview(processCameraProvider)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }, ContextCompat.getMainExecutor(this))
}

private fun bindPreview(processCameraProvider: ProcessCameraProvider) {
    processCameraProvider.unbindAll()
    processCameraProvider.bindToLifecycle(
        this,
        CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
            .build(),
        Preview.Builder().build().apply {
            setSurfaceProvider(binding.cameraPreview.surfaceProvider)
        },
        ImageCapture.Builder().build(),
        ImageAnalysis.Builder()
            .setTargetRotation(Surface.ROTATION_270)
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
            .build().apply {
                setAnalyzer(Executors.newSingleThreadExecutor()!!, QrScanner())
            }
    )
}

 

테스트는 구글 페이지로 이동하는 QR code로 진행하겠습니다(figure1 참조).

figure1. QR code to google.com

 

figure2. run

잘 이동하는 것을 확인할 수 있습니다.

 

이상 포스팅을 마치겠습니다.

 

감사합니다.

728x90

댓글