プロダクト ニュース

埋め込み型写真選択ツール

8 分読了

埋め込み型フォトピッカー:アプリ内で写真や動画を非公開でリクエストするための、よりシームレスな方法

photopicker.png

Android の写真選択機能の画期的な新機能で、アプリのユーザーエクスペリエンスを向上させましょう!新しい組み込み型フォトピッカーは、アプリのインターフェース内で、ユーザーが写真や動画をシームレスかつプライバシーに配慮した方法で選択できる機能を提供します。これで、アプリは写真選択機能で利用できるすべてのメリットを享受できるようになります。クラウドコンテンツへのアクセスも、アプリのユーザーエクスペリエンスに直接統合されます。

組み込み型を選ぶ理由?

多くのアプリが、ユーザーが写真や動画を選択する際に、高度に統合されたシームレスな体験を提供したいと考えていることを私たちは理解しています。組み込みの写真選択機能はまさにそれを実現するために設計されており、ユーザーはアプリを離れることなく、最近撮影した写真にすばやくアクセスできます。また、お気に入りのクラウドメディアプロバイダー(例:Google フォト)に保存されている写真ライブラリ全体を、お気に入り、アルバム、検索機能などを含めて閲覧することも可能です。これにより、ユーザーはアプリを切り替える必要がなくなり、目的の写真がローカルに保存されているかクラウドに保存されているかを気にする必要もなくなります。

シームレスな統合、強化されたプライバシー

組み込みの写真選択機能を使用すれば、ユーザーが実際に何かを選択するまで、アプリはユーザーの写真や動画にアクセスする必要はありません。これは、ユーザーのプライバシー保護の向上と、よりスムーズなユーザー体験を意味します。さらに、組み込みの写真選択機能を使用すると、ユーザーはクラウドベースのメディアライブラリ全体にアクセスできますが、標準の写真アクセス権限はローカルファイルのみに制限されています。

Google メッセージに埋め込まれた写真選択ツール

Google メッセージは、組み込みの写真選択機能の威力を示しています。彼らは以下のように統合しました。

  • 直感的な配置: 写真選択ツールはカメラボタンのすぐ下に配置されており、ユーザーは新しい写真を撮影するか、既存の写真を選択するかを明確に選択できます。
  • ダイナミックプレビュー: ユーザーが写真をタップするとすぐに大きなプレビューが表示され、選択内容を簡単に確認できます。写真の選択を解除するとプレビューが消え、すっきりとした見やすい画面が維持されます。
  • さらにコンテンツを表示するには展開してください: 初期表示は簡略化されており、最近の写真に簡単にアクセスできます。しかし、ユーザーは写真選択ツールを簡単に拡張して、Google フォトのクラウドコンテンツを含む、ライブラリ内のすべての写真や動画を閲覧して選択することができます。
  • ユーザーの選択を尊重: 埋め込みフォトピッカーは、ユーザーが選択した特定の写真またはビデオへのアクセスのみを許可するため、ユーザーは写真とビデオの権限の要求を完全に停止できます。これにより、ユーザーが写真や動画へのアクセスを限定的に許可する場合など、メッセージアプリがそのような状況を処理する必要がなくなる。
gif1.gif
gif2.gif

実装

Photo Picker Jetpack ライブラリを使用すると、組み込みの写真ピッカーを簡単に統合できます。  

Jetpack Compose

まず、Jetpack Photo Picker ライブラリを依存関係として追加します。

implementation("androidx.photopicker:photopicker-compose:1.0.0-alpha01")

EmbeddedPhotoPicker 構成関数は、組み込みの写真選択 UI をコンポーズ画面に直接組み込むためのメカニズムを提供します。このコンポーザブルは、埋め込み型の写真選択 UI をホストする SurfaceView を作成します。EmbeddedPhotoPicker サービスへの接続を管理し、ユーザー操作を処理し、選択されたメディア URI を呼び出し元のアプリケーションに伝達します。  

@Composable
fun EmbeddedPhotoPickerDemo() {
    // We keep track of the list of selected attachments
    var attachments by remember { mutableStateOf(emptyList<Uri>()) }

    val coroutineScope = rememberCoroutineScope()
    // We hide the bottom sheet by default but we show it when the user clicks on the button
    val scaffoldState = rememberBottomSheetScaffoldState(
        bottomSheetState = rememberStandardBottomSheetState(
            initialValue = SheetValue.Hidden,
            skipHiddenState = false
        )
    )

    // Customize the embedded photo picker
    val photoPickerInfo = EmbeddedPhotoPickerFeatureInfo
        .Builder()
        // Set limit the selection to 5 items
        .setMaxSelectionLimit(5)
        // Order the items selection (each item will have an index visible in the photo picker)
        .setOrderedSelection(true)
        // Set the accent color (red in this case, otherwise it follows the device's accent color)
        .setAccentColor(0xFF0000)
        .build()

    // The embedded photo picker state will be stored in this variable
    val photoPickerState = rememberEmbeddedPhotoPickerState(
        onSelectionComplete = {
            coroutineScope.launch {
                // Hide the bottom sheet once the user has clicked on the done button inside the picker
                scaffoldState.bottomSheetState.hide()
            }
        },
        onUriPermissionGranted = {
            // We update our list of attachments with the new Uris granted
            attachments += it
        },
        onUriPermissionRevoked = {
            // We update our list of attachments with the Uris revoked
            attachments -= it
        }
    )

       SideEffect {
        val isExpanded = scaffoldState.bottomSheetState.targetValue == SheetValue.Expanded

        // We show/hide the embedded photo picker to match the bottom sheet state
        photoPickerState.setCurrentExpanded(isExpanded)
    }

    BottomSheetScaffold(
        topBar = {
            TopAppBar(title = { Text("Embedded Photo Picker demo") })
        },
        scaffoldState = scaffoldState,
        sheetPeekHeight = if (scaffoldState.bottomSheetState.isVisible) 400.dp else 0.dp,
        sheetContent = {
            Column(Modifier.fillMaxWidth()) {
                // We render the embedded photo picker inside the bottom sheet
                EmbeddedPhotoPicker(
                    state = photoPickerState,
                    embeddedPhotoPickerFeatureInfo = photoPickerInfo
                )
            }
        }
    ) { innerPadding ->
        Column(Modifier.padding(innerPadding).fillMaxSize().padding(horizontal = 16.dp)) {
            Button(onClick = {
                coroutineScope.launch {
                    // We expand the bottom sheet, which will trigger the embedded picker to be shown
                    scaffoldState.bottomSheetState.partialExpand()
                }
            }) {
                Text("Open photo picker")
            }
            LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 64.dp)) {
                // We render the image using the Coil library
                itemsIndexed(attachments) { index, uri ->
                    AsyncImage(
                        model = uri,
                        contentDescription = "Image ${index + 1}",
                        contentScale = ContentScale.Crop,
                        modifier = Modifier.clickable {
                            coroutineScope.launch {
                                // When the user clicks on the media from the app's UI, we deselect it
                                // from the embedded photo picker by calling the method deselectUri
                                photoPickerState.deselectUri(uri)
                            }
                        }
                    )
                }
            }
        }
    }
}

ビュー

まず、Jetpack Photo Picker ライブラリを依存関係として追加します。

implementation("androidx.photopicker:photopicker:1.0.0-alpha01")

埋め込み型の写真選択機能を追加するには、レイアウトファイルにエントリを追加する必要があります。  

<view class="androidx.photopicker.EmbeddedPhotoPickerView"
    android:id="@+id/photopicker"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

そして、アクティビティ/フラグメント内でそれを初期化してください。

// We keep track of the list of selected attachments
private val _attachments = MutableStateFlow(emptyList<Uri>())
val attachments = _attachments.asStateFlow()

private lateinit var picker: EmbeddedPhotoPickerView
private var openSession: EmbeddedPhotoPickerSession? = null

val pickerListener = object EmbeddedPhotoPickerStateChangeListener {
    override fun onSessionOpened (newSession: EmbeddedPhotoPickerSession) {
        openSession = newSession
    }

    override fun onSessionError (throwable: Throwable) {}

    override fun onUriPermissionGranted(uris: List<Uri>) {
        _attachments += uris
    }

    override fun onUriPermissionRevoked (uris: List<Uri>) {
        _attachments -= uris
    }

    override fun onSelectionComplete() {
        // Hide the embedded photo picker as the user is done with the photo/video selection
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_view)
    
    //
    // Add the embedded photo picker to a bottom sheet to allow the dragging to display the full photo library
    //

    picker = findViewById(R.id.photopicker)
    picker.addEmbeddedPhotoPickerStateChangeListener(pickerListener)
    picker.setEmbeddedPhotoPickerFeatureInfo(
        // Set a custom accent color
        EmbeddedPhotoPickerFeatureInfo.Builder().setAccentColor(0xFF0000).build()
    )
}

埋め込みピッカーを操作するには、EmbeddedPhotoPickerSession のさまざまなメソッドを呼び出すことができます。

// Notify the embedded picker of a configuration change
openSession.notifyConfigurationChanged(newConfig)

// Update the embedded picker to expand following a user interaction
openSession.notifyPhotoPickerExpanded(/* expanded: */ true)

// Resize the embedded picker
openSession.notifyResized(/* width: */ 512, /* height: */ 256)

// Show/hide the embedded picker (after a form has been submitted)
openSession.notifyVisibilityChanged(/* visible: */ false)

// Remove unselected media from the embedded picker after they have been
// unselected from the host app's UI
openSession.requestRevokeUriPermission(removedUris)

埋め込み写真選択ツールは、SDK 拡張機能 15 以降を搭載した Android 14(API レベル 34)以降を実行しているユーザーが利用できます。写真選択ツールを利用できるデバイスについて詳しくは、こちらをご覧ください

ユーザーのプライバシーとセキュリティを強化するため、システムは埋め込みの写真選択ツールを、描画やオーバーレイができないようにレンダリングします。この意図的な設計上の選択により、広告バナーを計画する場合と同様に、UX でフォト ピッカーの表示領域を個別の専用要素として考慮する必要があります。

フィードバックや提案がある場合は、問題トラッカーにチケットを送信してください。

作成者:
続きを読む