사진 선택 도구

기기의 미디어 파일과 함께 표시되는 사진 선택 도구 대화상자 앱에 공유할 사진 선택
그림 1. 앱에 사진을 공유할 수 있는 직관적인 UI를 제공하는 사진 선택 도구

사진 선택 도구는 탐색 가능한 인터페이스를 제공하여 사용자에게 날짜별로 정렬된(최신 항목부터 오래된 항목 순서로) 미디어 라이브러리를 표시합니다. 개인 정보 보호 권장사항 Codelab에서 볼 수 있듯이 사진 선택 도구는 사용자가 앱에 미디어 라이브러리 전체가 아닌 선택한 이미지 및 동영상에 대해서만 액세스 권한을 부여하도록 내장된 안전한 방법을 제공합니다.

기기에 적격한 클라우드 미디어 제공업체가 있는 사용자는 원격으로 저장된 사진과 동영상에서 선택할 수도 있습니다. 클라우드 미디어 제공업체 자세히 알아보기

이 도구는 자동으로 업데이트되므로 코드를 변경할 필요 없이 시간이 지남에 따라 앱 사용자에게 확장된 기능을 제공합니다.

Jetpack 활동 계약 사용

사진 선택 도구 통합을 간소화하려면 androidx.activity 라이브러리 버전 1.7.0 이상을 포함해야 합니다.

다음 활동 결과 계약을 사용하여 사진 선택 도구를 실행합니다.

기기에서 사진 선택 도구를 사용할 수 없는 경우 라이브러리는 자동으로 ACTION_OPEN_DOCUMENT 인텐트 작업을 호출합니다. 이 인텐트는 Android 4.4(API 수준 19) 이상을 실행하는 기기에서 지원됩니다. isPhotoPickerAvailable()을 호출하여 특정 기기에서 사진 선택 도구를 사용할 수 있는지 확인할 수 있습니다.

단일 미디어 항목 선택

단일 미디어 항목을 선택하려면 다음 코드 스니펫에 표시된 것과 같이 PickVisualMedia 활동 결과 계약을 사용하세요.

// Registers a photo picker activity launcher in single-select mode.
val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
    // Callback is invoked after the user selects a media item or closes the
    // photo picker.
    if (uri != null) {
        Log.d("PhotoPicker", "Selected URI: $uri")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// Include only one of the following calls to launch(), depending on the types
// of media that you want to let the user choose from.

// Launch the photo picker and let the user choose images and videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

// Launch the photo picker and let the user choose only images.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

// Launch the photo picker and let the user choose only videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.VideoOnly))

// Launch the photo picker and let the user choose only images/videos of a
// specific MIME type, such as GIFs.
val mimeType = "image/gif"
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.SingleMimeType(mimeType)))

// Registers a photo picker activity launcher in single-select mode.
ActivityResultLauncher<PickVisualMediaRequest> pickMedia =
        registerForActivityResult(new PickVisualMedia(), uri -> {
    // Callback is invoked after the user selects a media item or closes the
    // photo picker.
    if (uri != null) {
        Log.d("PhotoPicker", "Selected URI: " + uri);
    } else {
        Log.d("PhotoPicker", "No media selected");
    }
});

// Include only one of the following calls to launch(), depending on the types
// of media that you want to let the user choose from.

// Launch the photo picker and let the user choose images and videos.
pickMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.ImageAndVideo.INSTANCE)
        .build());

// Launch the photo picker and let the user choose only images.
pickMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.ImageOnly.INSTANCE)
        .build());

// Launch the photo picker and let the user choose only videos.
pickMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.VideoOnly.INSTANCE)
        .build());

// Launch the photo picker and let the user choose only images/videos of a
// specific MIME type, such as GIFs.
String mimeType = "image/gif";
pickMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(new PickVisualMedia.SingleMimeType(mimeType))
        .build());

Compose

// Registers a photo picker activity launcher in single-select mode.
val pickMedia = rememberLauncherForActivityResult(PickVisualMedia()) { uri ->
    // Callback is invoked after the user selects a media item or closes the
    // photo picker.
    if (uri != null) {
        Log.d("PhotoPicker", "Selected URI: $uri")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// Include only one of the following calls to launch(), depending on the types
// of media that you want to let the user choose from.

// Launch the photo picker and let the user choose images and videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

// Launch the photo picker and let the user choose only images.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

// Launch the photo picker and let the user choose only videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.VideoOnly))

// Launch the photo picker and let the user choose only images/videos of a
// specific MIME type, such as GIFs.
val mimeType = "image/gif"
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.SingleMimeType(mimeType)))

여러 미디어 항목 선택

여러 미디어 항목을 선택하려면 다음 코드 스니펫에 표시된 것과 같이 선택 가능한 최대 미디어 파일 수를 설정하세요.

// Registers a photo picker activity launcher in multi-select mode.
// In this example, the app lets the user select up to 5 media files.
val pickMultipleMedia =
        registerForActivityResult(PickMultipleVisualMedia(5)) { uris ->
    // Callback is invoked after the user selects media items or closes the
    // photo picker.
    if (uris.isNotEmpty()) {
        Log.d("PhotoPicker", "Number of items selected: ${uris.size}")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// For this example, launch the photo picker and let the user choose images
// and videos. If you want the user to select a specific type of media file,
// use the overloaded versions of launch(), as shown in the section about how
// to select a single media item.
pickMultipleMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

// Registers a photo picker activity launcher in multi-select mode.
// In this example, the app lets the user select up to 5 media files.
ActivityResultLauncher<PickVisualMediaRequest> pickMultipleMedia =
        registerForActivityResult(new PickMultipleVisualMedia(5), uris -> {
    // Callback is invoked after the user selects media items or closes the
    // photo picker.
    if (!uris.isEmpty()) {
        Log.d("PhotoPicker", "Number of items selected: " + uris.size());
    } else {
        Log.d("PhotoPicker", "No media selected");
    }
});

// For this example, launch the photo picker and let the user choose images
// and videos. If you want the user to select a specific type of media file,
// use the overloaded versions of launch(), as shown in the section about how
// to select a single media item.
pickMultipleMedia.launch(new PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.ImageAndVideo.INSTANCE)
        .build());

Compose

// Registers a photo picker activity launcher in multi-select mode.
// In this example, the app lets the user select up to 5 media files.
val pickMultipleMedia =
        rememberLauncherForActivityResult(PickMultipleVisualMedia(5)) { uris ->
    // Callback is invoked after the user selects media items or closes the
    // photo picker.
    if (uris.isNotEmpty()) {
        Log.d("PhotoPicker", "Number of items selected: ${uris.size}")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// For this example, launch the photo picker and let the user choose images
// and videos. If you want the user to select a specific type of media file,
// use the overloaded versions of launch(), as shown in the section about how
// to select a single media item.
pickMultipleMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

플랫폼은 개발자가 사용자에게 사진 선택 도구에서 선택하도록 요청할 수 있는 최대 파일 수를 제한합니다. 이 제한에 액세스하려면 getPickImagesMaxLimit()를 호출하세요. 사진 선택 도구가 지원되지 않는 기기에서는 이 제한이 무시됩니다.

대상 기기

사진 선택 도구는 다음 기준을 충족하는 기기에서 사용할 수 있습니다.

Android 4.4(API 수준 19)~Android 10(API 수준 29)을 실행하는 이전 기기와 Google Play 서비스를 지원하는 Android 11 또는 12를 실행하는 Android Go 기기에는 사진 선택 도구의 백포팅 버전을 설치하면 됩니다. Google Play 서비스를 통해, 백포팅된 사진 선택 도구 모듈의 자동 설치를 사용 설정하려면 앱의 매니페스트 파일에서 <application> 태그에 다음 항목을 추가하세요.

<!-- Trigger Google Play services to install the backported photo picker module. -->
<service android:name="com.google.android.gms.metadata.ModuleDependencies"
         android:enabled="false"
         android:exported="false"
         tools:ignore="MissingClass">
    <intent-filter>
        <action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
    </intent-filter>
    <meta-data android:name="photopicker_activity:0:required" android:value="" />
</service>

미디어 파일 액세스 유지

기본적으로 시스템은 기기가 다시 시작되거나 앱이 중지될 때까지 미디어 파일 액세스 권한을 앱에 부여합니다. 앱이 백그라운드에서 대용량 파일을 업로드하는 것과 같이 장기 실행 작업을 실행하는 경우 이러한 액세스를 더 오래 유지해야 할 수 있습니다. takePersistableUriPermission() 메서드를 호출하면 됩니다.

Kotlin

val flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flag)

자바

int flag = Intent.FLAG_GRANT_READ_URI_PERMISSION;
context.contentResolver.takePersistableUriPermission(uri, flag);

트랜스코딩으로 HDR 동영상 처리

Android 13 (API 33)에서는 HDR(High Dynamic Range) 동영상을 캡처하는 기능을 도입했습니다. HDR은 더 풍부한 시각적 환경을 제공하지만 일부 이전 앱은 이러한 최신 형식을 처리할 수 없어 재생 중에 얼굴이 녹색으로 표시되는 등 부자연스러운 색상 재현과 같은 문제가 발생할 수 있습니다. 이 호환성 격차를 해결하기 위해 사진 선택 도구는 요청하는 앱에 제공하기 전에 HDR 동영상을 Standard Dynamic Range (SDR) 형식으로 자동 변환할 수 있는 트랜스코딩 기능을 제공합니다.

사진 선택 도구 트랜스코딩의 주요 목표는 아직 명시적인 HDR 지원이 없는 애플리케이션을 포함하여 더 광범위한 애플리케이션에서 일관되고 시각적으로 정확한 미디어 환경을 보장하는 것입니다. 사진 선택 도구는 HDR 동영상을 SDR로 트랜스코딩하여 앱 호환성을 개선하고 원활한 사용자 환경을 제공하는 것을 목표로 합니다.

사진 선택 도구의 트랜스코딩 작동 방식

사진 선택 도구 HDR 트랜스코딩은 기본적으로 사용 설정되지 않습니다. 이 기능을 사용 설정하려면 앱이 사진 선택 도구를 실행할 때 미디어 형식 처리 기능을 명시적으로 선언해야 합니다.

앱이 사진 선택 도구에 미디어 처리 기능을 제공합니다. 이는 AndroidX Activity 라이브러리를 사용하여 사진 선택 도구를 실행할 때 PickVisualMediaRequest.BuildermediaCapabilities를 추가하여 실행됩니다. 이를 용이하게 하기 위해 PickVisualMediaRequest.Builder에 새 API setMediaCapabilitiesForTranscoding(capabilities: MediaCapabilities?)가 추가되었습니다.

MediaCapabilities 클래스를 사용하여 HDR 트랜스코딩 동작을 제어할 수 있습니다. 앱에서 지원하는 HDR 유형을 정확하게 지정하는 MediaCapabilities 객체를 제공합니다 (예: TYPE_HLG10, TYPE_HDR10, TYPE_HDR10_PLUS, TYPE_DOLBY_VISION)를 사용하려고 할 때 Tegra2 기반 기기 및 기타 기기가 비정상 종료되었습니다.

트랜스코딩을 완전히 사용 중지하려면 MediaCapabilities 대신 null를 전달합니다. 제공된 기능에 명시적으로 나열되지 않은 HDR 유형은 지원되지 않는 것으로 간주됩니다. 이 API는 Android 13 (API 수준 33) 및 이후 버전에서 지원되며 @RequiresApi(Build.VERSION_CODES.TIRAMISU)로 주석 처리됩니다.

import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
import androidx.annotation.RequiresApi
import android.os.Build
import android.util.Log
import android.provider.MediaStore

// Registers a photo picker activity launcher.
val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
    // Callback invoked after media selected or picker activity closed.
    if (uri != null) {
        Log.d("photo picker", "Selected URI: $uri")
    } else {
        Log.d("photo picker", "No media selected")
    }
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun launchPhotoPickerWithTranscodingSupport() {
    val mediaCapabilities = MediaCapabilities.Builder()
        .addSupportedHdrType(MediaCapabilities.HdrType.TYPE_HLG10)
        .build()

    // Launch the photo picker and let the user choose only videos with
    // transcoding enabled.
    pickMedia.launch(PickVisualMediaRequest.Builder()
        .setMediaType(PickVisualMedia.VideoOnly)
        .setMediaCapabilitiesForTranscoding(mediaCapabilities)
        .build())
}

사진 선택 도구의 트랜스코딩은 앱의 미디어 기능과 선택한 동영상을 모두 기반으로 합니다. 트랜스코딩이 실행되면 트랜스코딩된 동영상의 URI가 반환됩니다.

HDR 트랜스코딩 관련 중요 고려사항

  • 성능 및 저장용량: 트랜스코딩에는 처리 시간이 소요되며 새 파일이 생성되어 저장용량이 사용됩니다.
  • 동영상 길이 제한: 사용자 경험과 저장용량 제약 조건 간의 균형을 맞추기 위해 동영상 길이에 1분의 제한이 있습니다.
  • 캐시된 파일 관리: 과도한 스토리지 사용을 방지하기 위해 유휴 유지관리 중에 캐시된 트랜스코딩된 파일이 주기적으로 삭제됩니다.
  • 기기 사용 가능 여부: 사진 선택 도구 트랜스코딩은 Android 13(API 수준 33) 이상에서 지원됩니다.
  • AndroidX 활동 통합: 필요한 setMediaCapabilitiesForTranscoding API가 포함되어 있으므로 AndroidX Activity 라이브러리의 버전 1.11.0-alpha01 이상 알파/베타/RC/안정화 버전을 사용해야 합니다.