コスギデンサン >> 情報系メモ >> Android

Camera2でバーコードを読んでみる 2018/9
Target Version : 27

•とにかくなんでもいいからCamera2でバーコードを読みたい。
•基本的に「Camera2でプレビューしてみる」と同じ。

build.gradle
dependencies {
    ... 
        implementation 'com.google.android.gms:play-services:11.0.4+'
        }

MainActivity.kt
package jp.kd2.simplebarcode1

import android.support.v7.app.AppCompatActivity
import android.os.Bundle

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.widget.Toast;

import com.google.android.gms.vision.Frame
import com.google.android.gms.vision.barcode.Barcode
import com.google.android.gms.vision.barcode.BarcodeDetector

class MainActivity : AppCompatActivity() {

    // ? -> Nullable Type
    private var mTextureView: TextureView? = null
    private val TAG = MainActivity::class.java.simpleName
    private var detector : BarcodeDetector? = null    // Activity初期化

    // 立ち上げ時と、タテ→ヨコ、ヨコ→タテの変換時にも呼ばれる。
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
            mTextureView = findViewById(R.id.preview)
            detector = BarcodeDetector.Builder(applicationContext)
                .setBarcodeFormats(Barcode.DATA_MATRIX or Barcode.QR_CODE or Barcode.ISBN or Barcode.CODE_128).build()
    }

    // 描画時
    override fun onResume() {
        super.onResume()
        // TextureViewが利用可能になったらカメラを開く
        if (mTextureView!!.isAvailable) {
            // call now
            val width = mTextureView!!.width
            val height = mTextureView!!.height
            openCamera(width, height)
        } else {
            // 動画等を描画する際にコールバックする関数を登録する。
            mTextureView!!.surfaceTextureListener = mSurfaceTextureListener
        }
    }

    // 停止時
    public override fun onPause() {
        super.onPause()
        closeCamera()
    }

    /******************** カメラ制御 ********************/
    private var mCameraDevice: CameraDevice? = null
    private var mSize: Size? = null    // カメラの状態によるコールバックメソッド
    private val mStateCallback = object : CameraDevice.StateCallback() {
        // カメラに接続された時
        override fun onOpened(cameraDevice: CameraDevice) {
            mCameraDevice = cameraDevice
            createCameraPreviewSession()
        }
        // カメラが切断させたとき
        override fun onDisconnected(cameraDevice: CameraDevice) {
            mCameraDevice = null
        }
        // エラー
        override fun onError(cameraDevice: CameraDevice, i: Int) {
            mCameraDevice = null
        }
    }

    // カメラに接続する。
    private fun openCamera(width: Int, height: Int) {
        // パーミッションのチェック
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            // パーミッションを求める。
            requestCameraPermission()
            return
        }
        val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
        try {
            val cameraId = cameraManager.cameraIdList[0]
            val characteristics = cameraManager.getCameraCharacteristics(cameraId)
            val map = characteristics.get(
                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
            mSize = map!!.getOutputSizes(SurfaceTexture::class.java)[0]
            cameraManager.openCamera(cameraId, mStateCallback, null)
        } catch (t: Throwable) {
            Log.d(TAG, t.message)
        }
    }

    // カメラを切断する。
    private fun closeCamera() {
        try {
            if (null != mCameraDevice)
                mCameraDevice!!.close()
        } catch (t: Throwable) {
            Log.d(TAG, t.message)
        }
    }

    /******************** プレビュー制御 ********************/
    private var mCaptureRequestbuilder: CaptureRequest.Builder? = null
    private var gWidth : Int = 0
    private var gHeight : Int = 0    // 描画領域のイベントリスナ

    private val mSurfaceTextureListener = object : TextureView.SurfaceTextureListener {
        override fun onSurfaceTextureAvailable(texture: SurfaceTexture, width: Int, height: Int) {
            openCamera(width, height)
            gWidth = width
            gHeight = height
        }
        override fun onSurfaceTextureSizeChanged(texture: SurfaceTexture, width: Int, height: Int) {
            Log.d("DEBUG", "onSurfaceTextureSizeChanged")
        }
        override fun onSurfaceTextureDestroyed(texture: SurfaceTexture): Boolean {
            return true
        }
        override fun onSurfaceTextureUpdated(texture: SurfaceTexture) {
            Log.d("DEBUG", "onSurfaceTextureUpdated")
            try {
                val frame = Frame.Builder().setBitmap(mTextureView!!.getBitmap(gWidth, gHeight)).build()
                val barcodes = detector!!.detect(frame)
                if (barcodes.size() > 0) {
                    val thisCode = barcodes.valueAt(0)
                    Log.d("code", thisCode.rawValue)
                    System.out.println(thisCode.rawValue)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    // 描画領域のコールバックメソッド
    private val mCaptureStateCallback = object : CameraCaptureSession.StateCallback() {
        override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
            try {
                cameraCaptureSession.setRepeatingRequest(mCaptureRequestbuilder!!.build(), null, null)
            } catch (t: Throwable) {
                Log.d(TAG, t.message)
            }
        }        override fun onConfigureFailed(cameraCaptureSession: CameraCaptureSession) {}
    }

    // プレビューセッションの開始
    private fun createCameraPreviewSession() {
        val texture = mTextureView!!.surfaceTexture
        texture.setDefaultBufferSize(mSize!!.width, mSize!!.height)
        val surface = Surface(texture)
        try {
            mCaptureRequestbuilder = mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
            mCaptureRequestbuilder!!.addTarget(surface)
            mCameraDevice!!.createCaptureSession(listOf(surface), mCaptureStateCallback, null)
        } catch (t: Throwable) {
            Log.d(TAG, t.message)
        }
    }

    /******************** パーミッション制御 ********************/
    private val REQUEST_CAMERA_PERMISSION = 1
    // ユーザにパーミッションの許可を求める。
    private fun requestCameraPermission() {
        requestPermissions(arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
    }
    // パーミッションの結果を取得後のコールバックメソッド
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array,
                                            grantResults: IntArray) {
        if (requestCode == REQUEST_CAMERA_PERMISSION) {
            if (grantResults.size != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                // パーミッションが許可されなかった場合。
                this.finish()
            }
        } else {
            // 許可ダイアログの承認結果を受け取る。
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        }
    }
}