안드로이드/SDK version

[Android] 카메라 촬영 Android10이상과 그 이하

코딩하는후운 2024. 3. 18. 13:25
반응형

안드로이드 카메라 촬영 Android10과 그 이하

1)Android 10 이상은 외부저장소 경로를 가져올수가 없다.
그래서 MediaStore를 이용하여 Media저장을 해야한다. 권한은 카메라만 있어도 저장됨.

2)Android 10 미만은 외부저장소를 사용 할 수 있으니 외부저장소에 생성하여 저장.
권한은 카메라, 외부저장소(Read, Write)필요.
-문제: 처음에 1)방법으로 해보았는데 Andrid9(갤럭시8)에서 사진촬영 후 uri에 값이 null임, intent.data에 uri들어있음
사진 촬영된 것도 조그만한 이미지로 잡혀서 실제 사용을 못할 정도(섬네일 사진을 가져오는듯)
-찾은방법: 2)방법을 사용하면서 FileProvider를 이용해 파일공유를 해주어 원본을 가져오는 법

2-1) res/xml 폴더에 path 정의

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="storage/emulated" path="."/>
</paths>

2-2) AndroidManifest에 FileProvider 정의
<!-- Android 10미만 카메라 사진촬영 후 원본 파일 가져오는 공유 Provider -->
<provider
    android:authorities="com.smadian.ainanny.provider"
    android:name="androidx.core.content.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true"
    tools:replace="android:authorities">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        tools:replace="android:resource"
        android:resource="@xml/filepaths"/>
</provider>



===여기서 tools:replace="android:authorities" 와 tools:replace="android:resource" 는
내 프로젝트에서 TedPermission라이브러리를 쓰고있는데 build시 provider가 중복으로 되어있어서 추가해줌.



//팝업창 띄우기

AlertDialog.Builder(requireContext())
.setItems(R.array.photoSelect){ _, which ->
    when(which){
        0->{
            //촬영하기
            cameraShoot()
        }
        1->{
            //사진첩에서 가져오기
            Intent().apply {
                type = "image/*"
                action = Intent.ACTION_GET_CONTENT
                startActivityForResult(this, Constants.REQUEST_PHOTOBOOK_IMPORT)
            }
        }
        2->{
            //기본 이미지
            babyRegistMethodVM.babyImageUrl.value = ""
        }
    }
}
.show()




/**
 * 사진 촬영 하기
 */

private fun cameraShoot(){

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        //Android 10 이상
        PermissionUtil.requestTedPermission(Manifest.permission.CAMERA)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { permissionResult ->
                if (permissionResult.isGranted) {
                    val values = ContentValues().apply {
                        val fileName = "${Constants.FILE_PHOTO_NAME}${SystemClock.currentThreadTimeMillis()}.jpg"
                        put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
                        put(MediaStore.Images.Media.MIME_TYPE, "image/jpg")
                        //추가 경로를 설정
                        put(MediaStore.Images.Media.RELATIVE_PATH, Constants.FILE_SAVE_FOLDER)

                        //IS_PENDING 속성을 1로 해주는 것은 파일을 write할 때 까지 다른 곳에서 사용 못하게 하는 것입니다.
                        //파일을 모두 write 할때 이 속성을 0으로 update 해주어야 합니다.
                        //현 포스팅은 카메라 예제로 외부에서 수정할 수 있어야 하므로 0으로 설정하거나 따로 처리 하지 않습니다.

                        //1로 되어 있으면 카메라로 찍은 이미지가 저장되지 않습니다.
                        //0으로 되어 있으면 카메라로 찍은 이미지가 저장 됩니다.
                        put(MediaStore.Images.Media.IS_PENDING, 0)
                    }

                    //권한 없이도 진행 가능 합니다.
                    //경로 -> content://media/external/images/media/84
                    babyRegistMethodVM.contentUri = activity?.contentResolver?.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

                    Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
                        putExtra(MediaStore.EXTRA_OUTPUT, babyRegistMethodVM.contentUri)
                        startActivityForResult(this, Constants.REQUEST_PHOTO_SHOOT)
                    }
                }
            }
    }else{
        //10 미만 , 내부 저장소 권한을 줘야 파일 저장됨.
        PermissionUtil.requestTedPermission(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { permissionResult ->
                if (permissionResult.isGranted) {
                    try {
                        val dirPath: String = Environment.getExternalStorageDirectory().absolutePath.toString() + "/" + Constants.FILE_SAVE_FOLDER
                        val dir = File(dirPath)
                        if (!dir.exists()) {
                            dir.mkdir()
                        }
                        val filePathData = File.createTempFile(Constants.FILE_PHOTO_NAME, ".jpg", dir)
                        if (!filePathData.exists()) {
                            filePathData.createNewFile()
                        }
                        babyRegistMethodVM.contentUri = FileProvider.getUriForFile(requireContext(), requireContext().packageName+".provider", filePathData)
                        Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
                            putExtra(MediaStore.EXTRA_OUTPUT, babyRegistMethodVM.contentUri)
                            startActivityForResult(this, Constants.REQUEST_PHOTO_SHOOT)
                        }
                    } catch (e: java.lang.Exception) {
                        e.printStackTrace()
                    }
                }
            }
    }
}






//결과 값 받아서 처리

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    when(requestCode){
        Constants.REQUEST_PHOTO_SHOOT->{
            if(resultCode != Activity.RESULT_OK){
                //만약 카메라를 사용해 사진을 찍지 않고 뒤로 가게 되면 생성한 uri를 제거해 주어야 합니다.
                //그렇게 하지 않으면 검은 화면의 빈 파일이 갤러리에 존재하게 됩니다.
                babyRegistMethodVM.contentUri?.let {
                    activity?.contentResolver?.delete(it, null, null)
                    babyRegistMethodVM.contentUri = null
                    return
                }
            }

            babyRegistMethodVM.contentUri?.let {
                startCropImageActivity(it)
            }
        }
        Constants.REQUEST_PHOTOBOOK_IMPORT->{
            //사진앨범
            if(resultCode != Activity.RESULT_OK) return

            data?.let {
                it.data?.let {uri->
                    startCropImageActivity(uri)
                }
            }

        }
        CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE->{
            //Crop한후에 데이터

            val result = CropImage.getActivityResult(data)
            if(resultCode == Activity.RESULT_OK){
                babyRegistMethodVM.contentUri = null
                babyRegistMethodVM.babyImageUrl.value = result.uri.toString()

                LogUtil.e(tag, "=========${result.uri}")
            }else if(resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE){
                babyRegistMethodVM.contentUri = null
                LogUtil.e(tag, result.error)
            }
        }
    }
}
반응형