아래 3가지 방법으로 가져온 이미지를 이미지뷰에 보여주는 예제를 통해 사용법을 익혀보도록 하겠습니다
- 갤러리 이미지
- 카메라 촬영 이미지(thumbnail)
- 카메라 촬영 이미지 (full-size photo)
3가지 모두 Intent 요청 후 onActivityResult로 결과를 받는 형태 입니다
(registeractivityresult를 사용했을 경우라면 ActivityResultCallback)
# 1 : 갤러리 이미지
- 이미지를 선택해서 불러오는 기능
1)요청
아래 2가지 방법이 많이 쓰입니다
- pickImg_01 : Intent.ACTION_PICK 사용 / URI를 기반으로 이미지 선택 앱 호출
- pickImg_02 : Intent.ACTION_GET_CONTENT 사용 / mime type을 지원하는 앱 호출(IntentChooser 사용해서)
'Intent.ACTION_GET_CONTENT'를 사용해서 사진을 가져올 앱을 사용자로 하게끔 선택하는 pickImg_02() 방법을 권고
const val REQ_SELECT_IMG = 200 //request code
private fun pickImg_01() {
Intent(Intent.ACTION_PICK).apply {
data = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
startActivityForResult(this, REQ_SELECT_IMG)
}
}
private fun pickImg_02() {
Intent(Intent.ACTION_GET_CONTENT).apply {
type = "image/*"
startActivityForResult(
Intent.createChooser(this, "Get Album"),
REQ_SELECT_IMG
)
}
}
2)결과
onActivityResult로 반환되는 intent의 data에 선택한 이미지 URL이 담겨 오고.
불러온 이미지를 그대로 쓸 수도 있지만, 보통 Resize도 하고 Rotate정보에 따라 회전 시켜서 보여줍니다
- setImgUri() : Uri를 기반으로 원본 이미지 그대로 이미지뷰에 셋
- setAdjImgUri() : Resize + Rotate가 필요할 경우 사용
'setAdjImgUri '함수 내부 에서 보기 쉬우려고 InputStream을 여러번 열었지만 성능상 좋을 리 없으므로...
실제 적용할 땐 최소한으로 사용하도록 고려
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
REQ_SELECT_IMG -> {
val currentImageUri = intent?.data ?: return //이미지 URL
val needAdjust = true
if (needAdjust) {
setAdjImgUri(currentImageUri) //방법 2
} else {
setImgUri(currentImageUri) //방법 1
}
}
}
}
}
/**
* Set Img 1) Uri를 사용해 이미지 셋 (Full-Size)
*/
private fun setImgUri(imgUri: Uri) {
imgUri.let {
val bitmap: Bitmap
if (Build.VERSION.SDK_INT < 28) {
bitmap = MediaStore.Images.Media.getBitmap(
this.contentResolver,
imgUri
)
binding.ivImg.setImageBitmap(bitmap)
} else {
val source =
ImageDecoder.createSource(this.contentResolver, imgUri)
bitmap = ImageDecoder.decodeBitmap(source)
binding.ivImg.setImageBitmap(bitmap)
}
}
}
/**
* Set Img 2) Uri를 사용해 이미지 셋 (Resize + Rotate가 필요할 경우 사용)
*/
private fun setAdjImgUri(imgUri: Uri) {
//1)회전할 각도 구하기
var degrees = 0f
contentResolver.openInputStream(imgUri)?.use { inputStream ->
degrees = getExifDegrees(ExifInterface(inputStream))
}
//2)Resizing 할 BitmapOption 만들기
val bmOptions = BitmapFactory.Options().apply {
// Get the dimensions of the bitmap
inJustDecodeBounds = true
contentResolver.openInputStream(imgUri)?.use { inputStream ->
//get img dimension
BitmapFactory.decodeStream(inputStream, null, this)
}
// Determine how much to scale down the image
val targetW: Int = 1000 //in pixel
val targetH: Int = 1000 //in pixel
val scaleFactor: Int = Math.min(outWidth / targetW, outHeight / targetH)
// Decode the image file into a Bitmap sized to fill the View
inJustDecodeBounds = false
inSampleSize = scaleFactor
}
//3) Bitmap 생성 및 셋팅 (resized + rotated)
contentResolver.openInputStream(imgUri)?.use { inputStream ->
BitmapFactory.decodeStream(inputStream, null, bmOptions)?.also { bitmap ->
val matrix = Matrix()
matrix.preRotate(degrees, 0f, 0f)
binding.ivImg.setImageBitmap(
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, false)
)
}
}
}
/**
* 이미지 회전 데이터 반환
*/
private fun getExifDegrees(ei: ExifInterface): Float {
val orientation: Int =
ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
return when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> 90f
ExifInterface.ORIENTATION_ROTATE_180 -> 180f
ExifInterface.ORIENTATION_ROTATE_270 -> 270f
else -> 0f
}
}
# 2 : 카메라 촬영 이미지 (thumbnail)
- 촬영 이미지를 저장 하지 않고 얻어올 수 있는 방법
- 대신 이 이미지는 촬영 원본 사이즈가 아닌 Resize된 작은 이미지이므로 썸네일이나 아이콘으로 사용하기엔 좋지만 더 큰 이미지가 필요하다면 다른 방법이 필요 ->'#3: 카메라 촬영 이미지 (full-size photo)'
1)요청
const val REQ_IMG_CAPTURE = 300
private fun takePicture() {
val pictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
pictureIntent.resolveActivity(packageManager)?.also {
startActivityForResult(pictureIntent, REQ_IMG_CAPTURE)
}
}
2)결과
onActivityResult로 반환되는 intent의 extra에 썸네일 크기의 사진 데이터가 담겨 옴
Bitmap 변환 후 사용
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
REQ_IMG_CAPTURE -> {
val extras = intent?.extras
val bitmap = extras?.get("data") as Bitmap?
binding.ivImg.setImageBitmap(bitmap)
}
}
}
}
# 3 : 카메라 촬영 이미지 (full-size photo)
* 이 예제는 targetSdk 30이상 이고 'Scoped Storage'를 바탕으로 합니다.
* 이 예제에서는 Permission 획득/체크를 따로 하지 않습니다. 획득 된 것으로 가정합니다. 따로 구현 해주세옹.
- 촬영 이미지을 저장해야 함
- 촬영 이미지가 저장될 빈 File을 미리 만들어두고, 그 File의 URI를 Intent에 실어 카메라를 호출
- 촬영을 하게 되면 해당 File에 데이터가 써지고, URI를 통해 이미지를 읽어와 사용
이게 복잡합니다...
Android10(Q)에서 Scoped Storage가 적용되면서 공용 공간에 접근할 수 있는 API 들이 deprecated 됐고,
대신 MediaStore API 를 사용하도록 권장되고 있습니다.
더이상 File 절대경로를 통한 공용 공간 접근을 허용하지 않으며, MediaStore를 통해 접근 해야 한다는 뜻입니다.
Media Store란? - https://developer.android.com/reference/android/provider/MediaStore
- Media Provider 와 application 간의 contract
- 안드로이드 시스템에서 제공하는 기능이며
- Media Data들을 Indexing 해서 미디어 DB로 관리합니다
MediaStroe를 이용해 데이터 저장을 할 시 저장 권한은 필요하지 않습니다.(단, 'Downloads' 폴더를 제외하곤, 파일 타입에 따라 저장 할 수 있는 폴더가 정해져 있습니다)
저장이 아닌 읽을 때는 READ_EXTERNAL_STORAGE 권한이 필요합니다
우선 어느 저장소를 사용할 것인지 결정하시고 시작하는게 좋겠습니다
저장소 관련해서는 아래 디벨러퍼를 참고 하시기 바랍니다
[Developer - 데이터 및 파일 저장소 개요] https://developer.android.com/training/data-storage?hl=ko
- 공용 공간 선택 시 : Scoped Storage를 고려해야하며 Q 미만에서 'WRITE_EXTERNAL_STORAGE' 권한 필요
- 앱 전용 공간 선택 시 : Android 4.3(Api level 18) 까지는 'WRITE_EXTERNAL_STORAGE' 권한이 필요하면 4.4 부터는 권한이 필요 없음
전용 공간부터 확인 해보도록 하겠습니다(그나마 간단...)
#3-1 카메라 촬영 (앱 전용 공간)
- context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)를 통한 외부 저장소를 예제로 함
- 경로 = '/storage/emulated/0/Android/data/패키지명/files/Pictures/'
- 파일의 URI는 FileProvider를 통해 생성
1) 사전 준비
- 퍼미션 선언, FileProvider 선언
- FileProvider를 위한 filepaths.xml 생성
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.imageuploadtest">
<!-- 앱 전용 공간에 저장이 필요 할 경우 ex)'getExternalFilesDir()'-->
<!-- 4.3 버전 까지는 퍼미션 필요 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
...
<application
...
<!-- FileProvider 선언 -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>
filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--<file-path> = 내부, 앱 스토리지 / getFileDir -->
<!--<cache-path> = 내부, 앱 캐시 스토리지 / getCacheDir -->
<!--<external-files-path> = 외부, 앱 전용 스토리지 file / getExternalFilesDir 사용시-->
<!--<external-cache-path> = 외부, 앱 전용 스토리지 cache / getExternalCacheDir 사용시-->
<!--<external-path> = 외부, 공용 저장소 사용 시 / Environment.getExternalStorageDirectory-->
//사진은 'getExternalFilesDir' 아래 'Pictures' 폴더 아래 저장되어 있을 예정
<external-files-path name="cameraImg" path="Pictures/"/>
</paths>
2) 카메라 호출
- 저장소에 빈 파일을 생성
- FileProvider를 이용해 빈 파일의 Uri를 얻음
- 얻은 photoURI를 Intent의 'MediaStore.EXTRA_OUTPUT' 속성 value값으로 put
- Intent를 이용해 카메라 호출
- 얻은 photoURI는 촬영 후, 이미지 가져올 때 사용해야 하니.. 챙겨두기
lateinit var photoURI: Uri
private fun takePictureFullSize() {
photoURI = Uri.EMPTY
val fullSizePictureIntent = getPictureIntent_App_Specific(applicationContext)
fullSizePictureIntent.resolveActivity(packageManager)?.also {
startActivityForResult(fullSizePictureIntent, REQ_IMG_CAPTURE_FULL_SIZE)
}
}
/**
* 카메라 호출할 Intent 생성
*/
fun getPictureIntent_App_Specific(context: Context): Intent {
val fullSizeCaptureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
//1) File 생성 - 촬영 사진이 저장 될
//photoFile 경로 = /storage/emulated/0/Android/data/패키지명/files/Pictures/
val photoFile: File? = try {
createImageFile(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES))
} catch (ex: IOException) {
// Error occurred while creating the File
ex.printStackTrace()
null
}
photoFile?.also {
//2) 생성된 File로 부터 Uri 생성 (by FileProvider)
//URI 형식 EX) content://com.example.img.fileprovider/cameraImg/JPEG_20211124_202832_6573897384086993610.jpg
photoURI = FileProvider.getUriForFile(
context,
BuildConfig.APPLICATION_ID + ".fileprovider",
it
)
//3) 생성된 Uri를 Intent에 Put
fullSizeCaptureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
}
return fullSizeCaptureIntent
}
/**
* 빈 파일 생성
*/
@Throws(IOException::class)
private fun createImageFile(storageDir: File?): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
return File.createTempFile(
"JPEG_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
).apply {
Log.i("syTest", "Created File AbsolutePath : $absolutePath")
}
}
3)결과
setAdjImgUri()함수는 '#1갤러리 이미지' 예제에서의 함수와 동일
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
REQ_IMG_CAPTURE_FULL_SIZE -> {
setAdjImgUri(photoURI) // -> 챙겨 두었던 photoUrI를 사용합니다
}
}
}
}
#3-2 카메라 촬영 (공용 공간)
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 를 통한 외부 저장소를 예제로 함
- 경로 = '/storage/emulated/0/Pictures/' ( == sdcard/Pictures/)
- Q 이상일 경우 : 파일의 URI는 MediaStore API를 이용해 획득
- Q 미만일 경우 : 파일의 URI는 FileProvider를 통해 획득
- Q 미만일 경우 WRITE_EXTERNAL_STORAGE 권한 필요
- Q = Android 10 = API Level 29
1) 사전 준비 (Q 미만 동작을 위해)
- 퍼미션 선언, FileProvider 선언
- FileProvider를 위한 filepaths.xml 생성
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.imageuploadtest">
<!-- 공용 공간에 저장이 필요 할 경우 ex)'getExternalStoragePublicDirectory()'-->
<!-- 9.0 버전 까지는 퍼미션 필요 -->
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
...
<application>
...
<!-- FileProvider 선언 -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>
filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--<file-path> = 내부, 앱 스토리지 / getFileDir -->
<!--<cache-path> = 내부, 앱 캐시 스토리지 / getCacheDir -->
<!--<external-files-path> = 외부, 앱 전용 스토리지 file / getExternalFilesDir 사용시-->
<!--<external-cache-path> = 외부, 앱 전용 스토리지 cache / getExternalCacheDir 사용시-->
<!--<external-path> = 외부, 공용 저장소 사용 시 / Environment.getExternalStorageDirectory-->
//사진은 'Environment.getExternalStorageDirectory' 아래 'Pictures' 폴더 아래 저장되어 있을 예정
<external-path name="cameraImgShared" path="Pictures/"/>
</paths>
2) 카메라 호출
- Q 미만 : 저장소에 빈 파일을 생성+ FileProvider를 이용해 빈 파일의 Uri를 획득(위의, 앱 전용 공간 저장 예제와 파일 저장 위치만 다르고 동일합니다)
- Q 이상 : ContentResolver를 이용해 Uri 획득
- 얻은 URI를 Intent의 'MediaStore.EXTRA_OUTPUT' 속성 value값으로 put
- Intent를 이용해 카메라 호출
- 얻은 URI는 촬영 후, 이미지 가져올 때 사용해야 하니.. 챙겨두기
const val REQ_IMG_CAPTURE_FULL_SIZE_SHARED_UNDER_Q = 500 //공용공간 - Q미만, Need WRITE Permission
const val REQ_IMG_CAPTURE_FULL_SIZE_SHARED_Q_AND_OVER = 600//공용공간 Q이상
lateinit var photoSharedURI_UNDER_Q: Uri
lateinit var photoSharedURI_Q_N_OVER: Uri
private fun takePictureFullSize_Shared() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val fullSizePictureIntent =
getPictureIntent_Shared_Q_N_Over(applicationContext)
fullSizePictureIntent.resolveActivity(packageManager)?.also {
startActivityForResult(
fullSizePictureIntent,
REQ_IMG_CAPTURE_FULL_SIZE_SHARED_Q_AND_OVER
)
}
} else {
//TODO Permission Check 필요
val fullSizePictureIntent =
getPictureIntent_Shared_Under_Q(applicationContext)
fullSizePictureIntent.resolveActivity(packageManager)?.also {
startActivityForResult(
fullSizePictureIntent,
REQ_IMG_CAPTURE_FULL_SIZE_SHARED_UNDER_Q
)
}
}
}
@Throws(IOException::class)
private fun createImageFile(storageDir: File?): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
return File.createTempFile(
"JPEG_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
).apply {
Log.i("syTest", "Created File AbsolutePath : $absolutePath")
}
}
/**
* 공유 영역 저장
* Android Q 미만 일 경우 ( ~ Android 9.0)
* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
*/
fun getPictureIntent_Shared_Under_Q(context: Context): Intent {
photoSharedURI_UNDER_Q = Uri.EMPTY
val fullSizeCaptureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
//1) File 생성 - 촬영 사진이 저장 될
//photoFile 경로 = /storage/emulated/0/Pictures/
val photoFile: File? = try {
//getExternalStoragePublicDirectory => deprecated in 29(Q) = 28(Android 9)까지 사용 가능
createImageFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES))
} catch (ex: IOException) {
// Error occurred while creating the File
ex.printStackTrace()
null
}
photoFile?.also {
//2) 생성된 File로 부터 Uri 생성 (by FileProvider)
photoSharedURI_UNDER_Q = FileProvider.getUriForFile(
context,
BuildConfig.APPLICATION_ID + ".fileprovider",
it
)
//3) 생성된 Uri를 Intent에 Put
fullSizeCaptureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoSharedURI_UNDER_Q)
}
return fullSizeCaptureIntent
}
/**
* 공유 영역 저장
* Android Q 이상일 경우, API 29 ~ (Android 10.0 ~ )
* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
*/
@RequiresApi(Build.VERSION_CODES.Q)
fun getPictureIntent_Shared_Q_N_Over(context: Context): Intent {
photoSharedURI_Q_N_OVER = Uri.EMPTY
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val contentValues = ContentValues()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "UriForAndroidQ_${timeStamp}.jpg")
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
//'RELATIVE_PATH', RequiresApi Q
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH,Environment.DIRECTORY_PICTURES)
//URI 형식 EX) content://media/external/images/media/1006
photoSharedURI_Q_N_OVER = context.contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
) ?: Uri.EMPTY
val fullSizeCaptureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
fullSizeCaptureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoSharedURI_Q_N_OVER)
return fullSizeCaptureIntent
}
#덧붙이는 말
* 많은 기술 블로그에서 사진 촬영 시 카메라 권한이 필요하다고 하지만, 공식 문서엔 관련 내용을 찾을 수 없었고
테스트를 해봐도 카메라 권한 없이 잘 동작하는 것을 확인했습니다.
사진 촬영 관련해서 공식 문서에 잘 정리되어 있습니다.
[Developer - 사진 촬영] https://developer.android.com/training/camera/photobasics?hl=en
* 사진 촬영 시 MediaStore API를 이용하지 않는다면, 카메라 촬영 하지 않고 뒤로가기로 돌아오면 빈 파일이 저장소에 남아 있습니다. 삭제 코드가 추가로 필요합니다.
*'3-1 카메라 촬영(앱 전용 공간)'의 경우도 MediaStore API를 사용할 수 있을 것 같습니다(테스트 필요)
*발생했던 ISSUE - Android 11 / 'getPictureIntent_Shared_Q_N_Over'
MediaStore API 사용하고 Camera 호출/촬영 했을 때
촬영 이미지 저장이 안되면서 resultCode가 0 으로 반환되는 현상이 있었습니다(정상이라면 -1로 반환 되어야 합니다)
Error : java.lang.IllegalStateException: Only owner is able to interact with pending item
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.sec.android.app.camera, PID: 4602
java.lang.IllegalStateException: Only owner is able to interact with pending item content://media/external/images/media/1063
at android.os.Parcel.createExceptionOrNull(Parcel.java:2393)
at android.os.Parcel.createException(Parcel.java:2369)
at android.os.Parcel.readException(Parcel.java:2352)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:153)
at android.content.ContentProviderProxy.openAssetFile(ContentProviderNative.java:704)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1826)
at android.content.ContentResolver.openOutputStream(ContentResolver.java:1528)
at android.content.ContentResolver.openOutputStream(ContentResolver.java:1504)
at com.sec.android.app.camera.util.Util.writeImageDataToRequestedUri(Util.java:1748)
at com.sec.android.app.camera.AttachFragment.setImageResult(AttachFragment.java:374)
at com.sec.android.app.camera.AttachFragment.onOkay(AttachFragment.java:339)
at com.sec.android.app.camera.AttachFragment.lambda$onCreateView$1$AttachFragment(AttachFragment.java:104)
at com.sec.android.app.camera.-$$Lambda$AttachFragment$1HW1j9LtVq7oh4YzjMEcPRnc9Io.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:8160)
at android.widget.TextView.performClick(TextView.java:16222)
at android.view.View.performClickInternal(View.java:8137)
at android.view.View.access$3700(View.java:888)
at android.view.View$PerformClick.run(View.java:30236)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8633)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
원인은
ContetnValue에 Pending을 걸었기 때문...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.put(MediaStore.Images.Media.IS_PENDING, 1)
}
Pending을 풀기전까지 다른 앱에서의 파일 접근을 막으려고 쓰는 기능인데, Pending을 풀지도 않고 카메라 호출을 해버려서 발생한 에러로 보입니다
*참고 블로그
[안드로이드 - MediaStore에 미디어 파일 저장하는 방법]
https://codechacha.com/ko/android-mediastore-insert-media-files/
*예제 프로젝트는 아래 Git 주소에서 실행해 보실 수 있습니다 ( Utils -> SetImgGalleryCamera)
https://github.com/suyoung1613/Utils.git
내용에 문제 있다면 알려주시기 바랍니다~
좋은 하루 되세요!
'Android - 기능' 카테고리의 다른 글
[Android] 바(Bar) 프로그레스 애니메이션 (0) | 2022.03.14 |
---|---|
[Android] 원형(Circle) 프로그레스 애니메이션 (0) | 2022.02.25 |
[Android] startActivityForResult @deprecated (0) | 2021.11.17 |
[Android] OutLineTextView - 글자 외곽선 더하기 (1) | 2021.11.14 |
[Android] 이미지 공유/Text 공유 (1) | 2021.10.09 |