Android Study

CameraX 최신 버전

85chong 2022. 1. 4. 19:36
728x90
반응형
SMALL
package (본인 패키지)

import android.annotation.SuppressLint
import android.graphics.*
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Bundle
import android.util.*
import android.util.Base64
import android.view.View
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import (본인패키지).BuildConfig
import (본인패키지).base.BaseActivity
import (본인패키지).base.BaseTAG
import (본인패키지).base.BaseYLOG
import (본인패키지).data.CameraSupportPictureData
import (본인패키지).databinding.ActivityCameraReBinding
import (본인패키지).di.DIManager
import java.io.*
import java.nio.ByteBuffer
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.collections.ArrayList


class CameraActivityRenew:BaseActivity() {

    val TAG = "CameraActivityRe"

    private lateinit var viewBinding: ActivityCameraReBinding
    private lateinit var mCameraExecutor:ExecutorService
    private var mImageCapture: ImageCapture? = null
    var mSupportCameraPictureData = CameraSupportPictureData()
    var mPreview:Preview? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewBinding = ActivityCameraReBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)
        mPreview = Preview.Builder().build()
        //현재 Device에서 target가능한 카메라 size list 구해서 가장 근접한 size로 셋팅
        mSupportCameraPictureData = finalFixSize(getSupportScreenSize())

        //카메라 open
        startCamera()

        //카메라 닫기 Button Listener
        viewBinding.btnClose.setOnClickListener {
            finish()
        }

        //촬영 Button Listener
        viewBinding.buttonCapture.setOnClickListener {
            Log.d(TAG, "mSupportCameraPictureData")
            viewBinding.buttonCapture.isEnabled = false//촬영순간 Disable
            captureCameraPreview()
        }
        //카메라 스레드 인스턴스 생성
        mCameraExecutor = Executors.newSingleThreadExecutor()
    }

    override fun onBackPressed() {
        super.onBackPressed()
        finish()
    }

    @SuppressLint("UnsafeExperimentalUsageError", "RestrictedApi")
    fun startCamera(){
        try{
            val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
            cameraProviderFuture.addListener(Runnable {
                //BackCamera create builder
                val cameraSelector = CameraSelector.Builder()
                    .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                    .build()
                mPreview?.setSurfaceProvider(viewBinding.previewView.surfaceProvider)
                //uses
                mImageCapture = ImageCapture.Builder()
                    .setTargetResolution(Size(mSupportCameraPictureData.width, mSupportCameraPictureData.height))
                    .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                    .build()

                val useCaseGroup = UseCaseGroup.Builder()
                    .addUseCase(mPreview!!)
                    .addUseCase(mImageCapture!!)
                    .build()
                try{
                    val cameraProvider = cameraProviderFuture.get()
                    cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, useCaseGroup)
                }catch (e:Exception){
                    BaseYLOG.showLog("e",BaseTAG.TAG_EXCEPTION_MSG,"CameratActivityRe()/startCamera/cameraProvider e : ","${e.message}")
                }
            }, ContextCompat.getMainExecutor(this))
        }catch (e:Exception){
            BaseYLOG.showLog("e",BaseTAG.TAG_EXCEPTION_MSG,"CameratActivityRe()/startCamera e : ","${e.message}")
        }
    }

    //촬영하기
    fun captureCameraPreview(){
        val imageCapture = mImageCapture?:return
        imageCapture.takePicture(mCameraExecutor, object : ImageCapture.OnImageCapturedCallback() {
            override fun onCaptureSuccess(image: ImageProxy) {
                try{
                    val bitmap: Bitmap = imageProxyToBitmap(image)
                    var rotateBitmap = bitmap.customRotate(getTheDegrees(image))
                    setPreviewImage()//Preview Image
                    val byteArr = bitmapCompressToByteArray(rotateBitmap)
                    //Crop captured Image
                    if(operatorToCropImage(byteArr)==null)return
                    var cropImageArr = operatorToCropImage(byteArr)
                    //Array of CropImage convert to Base64String
                    val final_result = byteArrayToBase64String(cropImageArr)
/*
                    if(BuildConfig.DEBUG){
                        writeTxTfile(final_result)
                    }
*/
                    MainWebviewActivity.compWebViewImageString = final_result
                    setResult(RESULT_OK)
                    finish()
                }catch (e:Exception){
                    BaseYLOG.showLog("e",BaseTAG.TAG_EXCEPTION_MSG,"CameratActivityRe()/captureCameraPreview e : ","${e.message}")
                }
                super.onCaptureSuccess(image)
            }
            override fun onError(exception: ImageCaptureException) {
                BaseYLOG.showLog("e",BaseTAG.TAG_EXCEPTION_MSG,"CameratActivityRe()/onError exception : ","${exception.message}")
                super.onError(exception)
            }
        })
    }

    //(촬영된 이미지 형태)imageProxy -> Bitmap
    private fun imageProxyToBitmap(image: ImageProxy):Bitmap{
        val planProxy = image.planes[0]
        val buffer:ByteBuffer = planProxy.buffer
        val bytes = ByteArray(buffer.remaining())
        buffer.get(bytes)
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
    }

    //Bitmap rotate
    fun Bitmap.customRotate(degrees: Float) = Bitmap.createBitmap(this,0,0,width,height,Matrix().apply {
            postRotate(degrees)
        },true
    )

    //촬영후 미리보기 image
    fun setPreviewImage(){
        try{
            runOnUiThread {
                viewBinding.screenShotImage.visibility = View.VISIBLE
                viewBinding.previewView.visibility = View.GONE
                viewBinding.screenShotImage.setImageBitmap(viewBinding.previewView.bitmap)
            }
        }catch (e:Exception){
            BaseYLOG.showLog("e",BaseTAG.TAG_EXCEPTION_MSG,"CameratActivityRe()/captureCameraPreview e : ","${e.message}")
        }
    }

    //Device 기본 설정된 회전각에 맞춰 각변경
    fun getTheDegrees(image:ImageProxy):Float{
        var degrees = 0f
        try{
            val getDeviceDefaultRotationDegrees = image.imageInfo.rotationDegrees
            when(getDeviceDefaultRotationDegrees){
                90 ->{degrees = 90f}
                180 ->{degrees = 180f}
                270 ->{degrees = 270f}
            }
        }catch (e:Exception){
            BaseYLOG.showLog("e",BaseTAG.TAG_EXCEPTION_MSG,"CameratActivityRe()/getDeviceDefaultRotationDegrees e : ","${e.message}")
        }
        return degrees
    }

    //byteArr -> 공백 문자 제거 -> Base64String
    fun byteArrayToBase64String(byteArray: ByteArray):String{
        //ByteArray -> Base64String 변환
        val byteArrString_result = Base64.encodeToString(byteArray, Base64.DEFAULT)
        //앞뒤 공백 제거
        var trim_from_get3000Data = byteArrString_result.trim()
        //개행문자 제거
        var final_Result = trim_from_get3000Data.replace(
            "\\r\\n|\\r|\\n|\\n\\r".toRegex(),
            ""
        )
        return final_Result
    }

    //byteArr -> Bitmap
    fun byteArrToBitmap(byteArray: ByteArray):Bitmap{
        try{
            return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
        }catch (e: Exception){
            return null!!
        }
    }

    //bitmap -> 화질 변환 압축 -> ByteArray
    fun bitmapCompressToByteArray(bitmap: Bitmap):ByteArray{
        //0 ~ 100
        var quallity_level = 10
        var stream = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.PNG, quallity_level, stream)
        var bitmapData = stream.toByteArray()
        return bitmapData
    }

    //현재 Device 에서 지원가능한 ScreenSize List
    fun getSupportScreenSize():ArrayList<CameraSupportPictureData>{
        var cameraSupportPreviewDataArr = arrayListOf<CameraSupportPictureData>()
        try{
            val cameraManager:CameraManager = getSystemService(CAMERA_SERVICE) as CameraManager
            for(cameraId in cameraManager.cameraIdList){
                var cameraCharacteristeics = cameraManager.getCameraCharacteristics(cameraId)
                if(cameraCharacteristeics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT){
                    continue
                }
                var info = cameraCharacteristeics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
                for(getSize in info!!.getOutputSizes(ImageFormat.JPEG)){
                    //기본 가로모드 기준에서 추출된 값이어서 , 세로모드로 사용을 위해 값을 전환시켜서 넣어줌
                    var diagonal = DIManager.commonUtil.getDiagonalSize(getSize.width,getSize.height)
                    cameraSupportPreviewDataArr.add(CameraSupportPictureData(diagonal = diagonal,width = getSize.height,height = getSize.width))
                }
                return cameraSupportPreviewDataArr
                break
            }
        }catch (e: Exception){
            BaseYLOG.showLog("e", BaseTAG.TAG_EXCEPTION_MSG,"CameraActivityRe / getSupportScreenSize / e : ","${e.message}")
        }
        return cameraSupportPreviewDataArr
    }

    //byteArr -> Crop Image at Center -> BytepArray
    fun operatorToCropImage(byteArray: ByteArray):ByteArray{
        //Target Rectangle W/H
        var targetViewWidth = viewBinding.ivCenterTargetRectangle.width
        var targetViewHeight = viewBinding.ivCenterTargetRectangle.height

        //Target Rectangle W/H
        var targetView_X = ((MainWebviewActivity.compRealsizeWidth/2) -(targetViewWidth/2))
        var targetView_Y = ((MainWebviewActivity.compRealsizeHeight/2) - (targetViewHeight/2))

        //ByteArray -> Bitmap -> Captured rotate Bitmap Image W/H
        var rotateBitmap = byteArrToBitmap(byteArray)
        var bitmapW = rotateBitmap.width
        var bitmapH = rotateBitmap.height

        var cropImage:Bitmap?=null
        try{
            var modify_final_X = 0
            var modify_final_Y = (bitmapH * targetView_Y)/MainWebviewActivity.compRealsizeHeight
            var modify_final_width = 0
            var modify_fianl_height = (bitmapH * targetViewHeight)/MainWebviewActivity.compRealsizeHeight
            //more tight crop Image #1
            if(mSupportCameraPictureData.width > MainWebviewActivity.compRealsizeWidth){
                modify_final_width = ((bitmapW * targetViewWidth)/MainWebviewActivity.compRealsizeWidth)-(mSupportCameraPictureData.width-MainWebviewActivity.compRealsizeWidth)
            }else if(mSupportCameraPictureData.width < MainWebviewActivity.compRealsizeWidth){
                modify_final_width = ((bitmapW * targetViewWidth)/MainWebviewActivity.compRealsizeWidth)-(MainWebviewActivity.compRealsizeWidth-mSupportCameraPictureData.width)
            }else{
                modify_final_width = ((bitmapW * targetViewWidth)/MainWebviewActivity.compRealsizeWidth)
            }
            //more tight crop Image #2
            if(mSupportCameraPictureData.width != MainWebviewActivity.compRealsizeWidth){
                modify_final_X = (bitmapW * targetView_X)/MainWebviewActivity.compRealsizeWidth+((mSupportCameraPictureData.width-MainWebviewActivity.compRealsizeWidth)/2)
            }else{
                modify_final_X = (bitmapW * targetView_X)/MainWebviewActivity.compRealsizeWidth
            }
            cropImage = Bitmap.createBitmap(rotateBitmap,modify_final_X,modify_final_Y,modify_final_width,modify_fianl_height)
            //Bitmap -> ByteArray 변환
            return bitmapCompressToByteArray(cropImage)
        }catch (e:Exception){
            BaseYLOG.showLog("e", BaseTAG.TAG_EXCEPTION_MSG,"CameraActivityRe / operatorToCropImage / e : ","${e.message}")
            return null!!
        }
    }

    //최종 Device Size 지정
    fun finalFixSize(cameraSupportPreviewDataArr:ArrayList<CameraSupportPictureData>):CameraSupportPictureData{
        var resultArr = ArrayList<Int>()
        //Device Size == surfaceView size
        for(supportSizeData in cameraSupportPreviewDataArr){
            //기본, 가로 default 라서 가로,세로 바꿔서 체크 해야함
            if(supportSizeData.height == MainWebviewActivity.compRealsizeWidth
                && supportSizeData.width== MainWebviewActivity.compRealsizeHeight){
                return supportSizeData
            }
        }
        //Device Size != surfaceView size(제일 근접한 사이즈로 셋팅)
        for(supportSize in cameraSupportPreviewDataArr){
            if(supportSize.diagonal < MainWebviewActivity.compRealsizeDiagonal){
                resultArr.add(MainWebviewActivity.compRealsizeDiagonal-supportSize.diagonal)
            }else{
                resultArr.add(supportSize.diagonal- MainWebviewActivity.compRealsizeDiagonal)
            }
        }
        var index = resultArr.indexOf(Collections.min(resultArr))
        return  cameraSupportPreviewDataArr.get(index)
    }

    override fun onDestroy() {
        super.onDestroy()
        mCameraExecutor.shutdown()
    }

    //========== TEST ========== TEST ========== TEST ========== TEST ========== TEST ========== TEST ========== TEST ========== TEST
    //권한설정 주고 실행 해야함
    //Text 파일 입력후 파일 생성
/*
    fun writeTxTfile(strValue: String){
        var filename = "yckBase64StringValue.txt"
        var file = File(getExternalFilesDir(null), filename)
        try {
            var fos = FileOutputStream(file)
            var writer = BufferedWriter(OutputStreamWriter(fos))
            writer.write(strValue)
            writer.flush()
            writer.close()
        }catch (e: Exception){
            Log.e(TAG, "e : ${e.message}")
            return
        }
        Log.e(TAG, "complete")
    }
*/


}//class end