写真選択ツール

デバイス上のメディア ファイルとともに写真選択ツール ダイアログが表示されます。アプリと共有する写真を選択します。
図 1.写真選択ツールは、写真をアプリと共有するための直感的な UI です。

写真選択ツールは、メディア ライブラリが日付が新しい順に表示される、閲覧可能なインターフェースです。プライバシーに関するおすすめの方法についての Codelab で説明されているように、写真選択ツールでは、メディア ライブラリ全体ではなく、選択した画像と動画にのみアクセスを許可するという安全な方法が取られています。

デバイスで対象のクラウド メディア プロバイダを登録しているユーザーは、リモートに保存されている写真や動画から選択することもできます。詳しくは、クラウド メディア プロバイダについての説明をご覧ください。

このツールは自動的に更新されるため、コードを変更しなくても、ユーザーは時間の経過にともないアプリの拡張された機能を利用できるようになります。

Jetpack Activity コントラクトを使用する

写真選択ツールの統合を容易にするには、androidx.activity ライブラリのバージョン 1.7.0 以降を組み込みます。

次のアクティビティ結果コントラクトを使用して、写真選択ツールを起動します。

デバイスで写真選択ツールを使用できない場合、ライブラリは代わりに ACTION_OPEN_DOCUMENT インテントのアクションを自動的に呼び出します。このインテントは、Android 4.4(API レベル 19)以降を搭載しているデバイスでサポートされています。特定のデバイスで写真選択ツールが使用可能かどうかを確認するには、isPhotoPickerAvailable() を呼び出します。

メディア アイテムを 1 つ選択する

1 つのメディア アイテムを選択するには、次のコード スニペットに示すように、PickVisualMedia アクティビティ結果コントラクトを使用します。

View

// 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)))

View

// 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)))

複数のメディア ファイルを選択する

複数のメディア アイテムを選択するには、次のコード スニペットに示すように、選択可能なメディア ファイルの最大数を設定します。

View

// 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))

View

// 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)

Java

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

コード変換で HDR 動画を処理する

Android 13(API 33)では、ハイ ダイナミック レンジ(HDR)動画をキャプチャする機能が導入されました。HDR はより豊かな視覚体験を提供しますが、古いアプリの中には、これらの新しいフォーマットを処理できないものがあり、再生中の不自然な色再現(顔が緑色になるなど)などの問題が発生する可能性があります。この互換性の問題に対処するため、写真選択ツールには、リクエスト元のアプリに提供する前に HDR 動画を 標準ダイナミック レンジ(SDR)形式に自動的に変換できるトランスコード機能が用意されています。

写真選択ツールのトランスコードの主な目的は、HDR を明示的にサポートしていないアプリでも、幅広いアプリで一貫した視覚的に正確なメディア エクスペリエンスを実現することです。写真選択ツールでは、HDR 動画を SDR にコード変換することで、アプリの互換性を高め、シームレスなユーザー エクスペリエンスを提供することを目的としています。

写真選択ツールのトランスコードの仕組み

写真選択ツールの HDR コード変換はデフォルトでは有効になっていません。この機能を有効にするには、写真選択ツールを起動するときに、アプリでメディア形式の処理機能を明示的に宣言する必要があります。

アプリは、写真選択ツールにメディア処理機能を提供します。これは、AndroidX Activity ライブラリを使用して写真選択ツールを起動するときに、PickVisualMediaRequest.BuildermediaCapabilities を追加することで行われます。これを容易にするため、PickVisualMediaRequest.Builder に新しい API setMediaCapabilitiesForTranscoding(capabilities: MediaCapabilities?) が追加されました。

HDR のトランコード動作は、MediaCapabilities クラスを使用して制御できます。アプリがサポートする HDR タイプを正確に指定する MediaCapabilities オブジェクトを指定します(例: TYPE_HLG10TYPE_HDR10TYPE_HDR10_PLUSTYPE_DOLBY_VISION)。

トランコーディングを完全に無効にするには、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 版/安定版リリースを使用していることを確認します。