Android 14 - 사진/동영상 파일, 일부 접근 권한 소개

Android 14에서 사진/동영상 등, 미디어 파일에 대한 일부 액세스 권한이 추가되었습니다.

기존에 앱은 사용자에게 사진/동영상 파일의 전체 접근 권한을 요청했었는데요, 이번에 사용자는 앱에 일부 파일 접근 권한만 부여할 수 있도록 변경되었습니다.

1. Android 13 이하에서 미디어 파일 접근 권한

Android 12 이하에서 미디어 파일을 읽으려면 READ_EXTERNAL_STORAGE 권한이 필요합니다.

  • android.permission.READ_EXTERNAL_STORAGE

Android 13에서는 READ_EXTERNAL_STORAGE 권한은 더 이상 사용되지 않고, 아래 3개 권한으로 분리되었습니다.

  • android.permission.READ_MEDIA_IMAGES
  • android.permission.READ_MEDIA_VIDEOS
  • android.permission.READ_MEDIA_AUDIOS

2. Android 14 이상에서 미디어 파일 접근 권한

기존에 아래 두개 권한을 사용자에게 요청하면 모든 사진/동영상 파일을 읽을 수 있었습니다.

  • android.permission.READ_MEDIA_IMAGES
  • android.permission.READ_MEDIA_VIDEOS

Android 14에서도 이 부분은 바뀌지 않았는데요. 대신, 사용자가 앱에 모든 권한을 부여하지 않고, 일부 파일의 접근 권한만 부여할 수 있는 기능이 추가되었습니다.

아래 권한이 Android 14에서 추가된 새로운 권한이며, 사용자가 일부 파일 접근 권한만 앱에 부여하였을 때, 이 권한이 앱에 부여됩니다.

  • android.permission.READ_MEDIA_VISUAL_USER_SELECTED

3. 신규 기능 구현 방법

다음과 같이 OS에 따라서 아래와 같이 앱의 Manifest에 권한을 추가합니다.

  • READ_EXTERNAL_STORAGE는 Android 12L 이하에서만 사용되기 때문에 maxSdkVersion="32"라고 명시
<!-- Devices running Android 12L (API level 32) or lower  -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

<!-- Devices running Android 13 (API level 33) or higher -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<!-- To handle the reselection within the app on Android 14 (API level 34) -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />

아래와 같이 권한을 사용자에게 요청할 수 있습니다.

  • Android 13 이하에서는 미디어 권한(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO)만 요청
  • Android 14 이상에서 미디어 권한과 함께 READ_MEDIA_VISUAL_USER_SELECTED 권한도 요청
// Register ActivityResult handler
val requestPermissions = registerForActivityResult(RequestMultiplePermissions()) { results ->
    // Handle permission requests results
    // See the permission example in the Android platform samples: https://github.com/android/platform-samples
}

// Permission request logic
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
    requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED))
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO))
} else {
    requestPermissions.launch(arrayOf(READ_EXTERNAL_STORAGE))
}

만약 비디오 파일만 접근하려면 아래와 같이 READ_MEDIA_VIDEO 권한만 요청할 수 있습니다. 사진 파일만 접근하는 경우에는 READ_MEDIA_IMAGES 권한을 요청하시면 됩니다

// Allow the user to select only videos
requestPermissions.launch(arrayOf(READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED))

아래와 같이 앱은 사진/동영상 접근 권한을 갖고 있는지 체크할 수 있습니다.

  • READ_MEDIA_IMAGES, READ_MEDIA_VIDEO 권한을 갖고 있으면, 모든 사진/동영상 파일을 접근할 수 있음
  • READ_MEDIA_VISUAL_USER_SELECTED 권한만 갖고 있으면, 일부 사진/동영상 파일만 접근할 수 있음
if (
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
    (
        ContextCompat.checkSelfPermission(context, READ_MEDIA_IMAGES) == PERMISSION_GRANTED ||
        ContextCompat.checkSelfPermission(context, READ_MEDIA_VIDEO) == PERMISSION_GRANTED
    )
) {
    // Full access on Android 13 (API level 33) or higher
} else if (
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
    ContextCompat.checkSelfPermission(context, READ_MEDIA_VISUAL_USER_SELECTED) == PERMISSION_GRANTED
) {
    // Partial access on Android 14 (API level 34) or higher
}  else if (ContextCompat.checkSelfPermission(context, READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
    // Full access up to Android 12 (API level 32)
} else {
    // Access denied
}

4. 미디어 파일 Query

아래 예제는 MediaStore를 통해 사진/동영상 파일을 가져오는 예제입니다.

갖고 있는 권한에 따라서 아래와 같은 차이점이 있습니다.

  • 전체 접근 권한을 갖고 있으면, 디바이스의 모든 사진/동영상 파일 정보가 리턴
  • 일부 접근 권한만 갖고 있으면, 사용자가 허락한 사진/동영상 파일 정보들만 리턴
data class Media(
    val uri: Uri,
    val name: String,
    val size: Long,
    val mimeType: String,
)

// Run the querying logic in a coroutine outside of the main thread to keep the app responsive.
// Keep in mind that this code snippet is querying only images of the shared storage.
suspend fun getImages(contentResolver: ContentResolver): List<Media> = withContext(Dispatchers.IO) {
    val projection = arrayOf(
        Images.Media._ID,
        Images.Media.DISPLAY_NAME,
        Images.Media.SIZE,
        Images.Media.MIME_TYPE,
    )

    val collectionUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Query all the device storage volumes instead of the primary only
        Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
    } else {
        Images.Media.EXTERNAL_CONTENT_URI
    }

    val images = mutableListOf<Media>()

    contentResolver.query(
        collectionUri,
        projection,
        null,
        null,
        "${Images.Media.DATE_ADDED} DESC"
    )?.use { cursor ->
        val idColumn = cursor.getColumnIndexOrThrow(Images.Media._ID)
        val displayNameColumn = cursor.getColumnIndexOrThrow(Images.Media.DISPLAY_NAME)
        val sizeColumn = cursor.getColumnIndexOrThrow(Images.Media.SIZE)
        val mimeTypeColumn = cursor.getColumnIndexOrThrow(Images.Media.MIME_TYPE)

        while (cursor.moveToNext()) {
            val uri = ContentUris.withAppendedId(collectionUri, cursor.getLong(idColumn))
            val name = cursor.getString(displayNameColumn)
            val size = cursor.getLong(sizeColumn)
            val mimeType = cursor.getString(mimeTypeColumn)

            val image = Media(uri, name, size, mimeType)
            images.add(image)
        }
    }

    return@withContext images
}

5. 디바이스 업그레이드 시 동작

Android 13 디바이스에서 Android 14로 업그레이드 될 때, 이미 설치된 앱은 아래와 같이 동작합니다.

  • 앱이 READ_MEDIA_IMAGES, READ_MEDIA_VIDEO 권한을 갖고 있으면, Android 14에서도 이 권한은 유지되고 모든 사진/동영상 파일에 접근할 수 있음

Android 12 디바이스에서 Android 14로 업그레이드 될 때, 이미 설치된 앱은 아래와 같이 동작합니다.

  • 앱이 READ_EXTERNAL_STORAGE 또는 WRITE_EXTERNAL_STORAGE 권한을 갖고 있으면, Android 14에서 READ_MEDIA_IMAGES, READ_MEDIA_VIDEO 권한이 앱에 자동으로 부여되며 모든 사진/동영상 파일에 접근할 수 있음

6. 일부 파일 접근 권한의 유효기간

사용자가 앱에 1개의 파일 접근 권한을 부여했습니다. 앱은 이 파일에 접근할 수 있는데, 언제까지 접근할 수 있을까요? READ_MEDIA_VISUAL_USER_SELECTED 권한만 갖고 있다면 영원히 파일에 접근할 수 있을까요?

Android developers 문서를 보면, 접근 권한은 만료될 수 있기 때문에(사용자가 권한 회수 등), 더 이상 접근할 수 없는 경우 다시 사용자에게 요청할 수 있도록 앱을 구현해야 한다고 합니다.

따라서, 미디어 파일에 대한 URI를 저장했다가 다시 읽으면 안되고, 파일을 읽기 전에 Media API로 query를 하여 현재 접근 가능한지 확인 후 파일을 읽도록 구현이 되어야 합니다.

Loading script...

Related Posts

codechachaCopyright ©2019 codechacha