본문 바로가기

Android(Kotlin) Study A부터 Z까지 등반

FileProvider 예제 [카메라로 찍은 사진 가져오기]

💡 PhotoActivity.kt, activity_photo.xml, file_paths.xml, Manifest.xml

사진찍고 사진을 저장 → 이미지뷰에 띄우기(Bitmap) 예제를 해보겠습니다.

Manifest에 내 사진을 접근해줄 Provider추가. application 내부

<provider 
	android:name="androidx.core.content.FileProvider" 
	android:authorities="com.mydomain.fileprovider"
	android:exported="false"
	android:grantUriPermissions="true"> 
		<meta-data 
			android:name="android.support.FILE_PROVIDER_PATHS" 
			android:resource="@xml/file_paths" />   //접근할 위치? 라고 생각하면 될듯하다.
</provider>
android.support.v4.content.FileProvider에서 불러오면 안된다. 
2020. 5월에 라이브러리 관리 중단

getBitmap()함수는 API29에서 더이상 지원하지 않는다. 버전별로 분기를 나누면 될듯하다.

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="my_images"
        path="Android/data/com.example.fileproviderexample/files/Pictures" />
</paths>

<!--
저장될 외부 이미지 파일 경로이다.-->

activity_photo.xml

PhotoActivity.kt

onCreate

  • 권한요청
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.M) {
    if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(
            Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
) {
        Log.d(TAG, "권한 설정 완료")
    } else {
        Log.d(TAG, "권한 설정 요청")
        ActivityCompat.requestPermissions(
            this@PhotoActivity,
arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE),
            1
        )
    }
}
  • 권한요청 request
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    Log.d(TAG, "onRequestPermissionsResult");
    if (grantResults[0] == PackageManager.PERMISSION_GRANTED&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
        Log.d(TAG, "Permission: " + permissions[0] + "was " + grantResults[0]); }
}
  • 사진찍기 버튼 클릭 이벤트
binding.photoBtn.setOnClickListener{
dispatchTakePictureIntent()
}
  • 해당 메소드 dispatchTakePictureIntent
private fun dispatchTakePictureIntent() {
    val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) //찍은 이미지의 섬네일을 가져올 수 있다.(찍은거 미리보기 창)
    if (takePictureIntent.resolveActivity(packageManager) != null) { //ACTION_IMAGE_CAPTURE 해줄 수 있는 액티비티가 내 기기 내에 존재하는지
        // 찾고 없으면 null을 반환 
				// resolveActivity를 호출할 때 패키지 매니저를 보내는 이유는 무엇일까?
        var photoFile: File? = null
        try {
            photoFile = createImageFile()    //toString해서 Log찍어보니 절대 경로가 다 나온다, 구현해놓은 함수
            Log.d(TAG, photoFile.toString())
        } catch (ex: IOException) {
        }
        if (photoFile != null) {
            val photoURI: Uri =
                FileProvider.getUriForFile(this,
                    "com.example.fileproviderexample.fileprovider", photoFile) //왜 this로 보내줄까?
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)  //Name은 딱히 안 중요한듯하다.(첫번쨰 인자가 Name) 우린 카메라 앱에서 사진이 담길 Uri를 보내줄거다
            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO)  //인텐트 보내자.
        }
    }
}

💡 패키지 매니저 역할 중 하나 : Software repository로부터 패키지를 찾고, 다운로드하고, 설치하고, 업데이트하는 역할

 

  • 이미지 파일 경로 생성 및 파일 객체 반환
@Throws(IOException::class)
private fun createImageFile(): File? {
    val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
    val imageFileName = "JPEG_" + timeStamp + "_"    //파일 이름이다. 타임 스탬프로 이름 안 겹치게 해주려는듯하다.
    val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_PICTURES) // 픽쳐 디렉토리 파일 객체를 참조받아온다.
    val image: File = File.createTempFile(imageFileName, ".jpg", storageDir) //파일 어떤 이름으로, 어떤 형식으로, 어디에 만들지 정해주는 아이이다.
    mCurrentPhotoPath = image.absolutePath//이 경로로 저장을 요청할 것이다. 받아온다면 이 경로로 접근하면 되겠지?
    return image.absoluteFile// 파일 객체로 반환한다. 그냥 image로도 된다. 둘 다 파일이긴하니깐
}
  • 인텐트 결과를 받아오자.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode==REQUEST_TAKE_PHOTO) {
        if (resultCode ==RESULT_OK) {
            val file:File= File(mCurrentPhotoPath)   //미리 저장해둔 경로로 파일을 받아오자.
            val bitmap:Bitmap   // 이미지 파일일테니깐 이미지로 변환해야겠지?
            if (Build.VERSION.SDK_INT>= 29) {   //getBitmap이 API 29이후 버전에다가 쓰면 안된다고 한다. 그래서 분기를 나누었다.
                val source: ImageDecoder.Source =
                    ImageDecoder.createSource(contentResolver, Uri.fromFile(file))
                Log.d(TAG, source.toString())
                try {
                    bitmap = ImageDecoder.decodeBitmap(source)   //이부분은 왜 try를 써야하는걸까?
                    if (bitmap != null) {
                        binding.imageView.setImageBitmap(bitmap)   //받아온 이미지 여기에 불러올거다.
                    }
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            } else {
                try { //이부분은 왜 try를 써야하는걸까?
                    bitmap = MediaStore.Images.Media.getBitmap(contentResolver, Uri.fromFile(file))
                    Log.d(TAG, bitmap.toString())   
                    if (bitmap != null) {
                        binding.imageView.setImageBitmap(bitmap)   //받아온 이미지 여기에 불러올거다.
                    }
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        } else {

            Log.d("sadsadasdasdasd", "null!!!")
        }
    } else {
        Log.d("sadasd", "null!!!")
    }
}

결과

 

ref: https://ebbnflow.tistory.com/177

Android 공식 문서 | 사진 촬영

Android 공식 문서 | 카메라 제어

Android 공식 문서 | FileProvider