埋め込み型写真選択ツール: アプリで写真や動画を非公開でリクエストするための、よりシームレスな方法
Android の写真選択ツールに、アプリのユーザー エクスペリエンスを向上させる新しい方法が追加されました。新しい埋め込み型の写真選択ツールを使用すると、ユーザーはアプリのインターフェース内で写真や動画をシームレスに選択できます。また、プライバシーにも配慮されています。これにより、アプリは写真選択ツールで利用可能なすべてのメリット(クラウド コンテンツへのアクセスなど)を、アプリのエクスペリエンスに直接統合できるようになります。
埋め込みの理由
多くのアプリでは、写真や動画を選択する際に、高度に統合されたシームレスなエクスペリエンスをユーザーに提供したいと考えていることと思います。埋め込み型の写真選択ツールは、まさにそれを実現するために設計されています。ユーザーはアプリを離れることなく、最近の写真にすばやくアクセスできます。また、お気に入り、アルバム、検索機能など、お好みのクラウド メディア プロバイダ(Google フォトなど)のライブラリ全体を探索することもできます。これにより、ユーザーはアプリを切り替える必要がなくなり、目的の写真がローカルに保存されているかクラウドに保存されているかを気にする必要もなくなります。
シームレスな統合、プライバシーの強化
埋め込みの写真選択ツールを使用すると、ユーザーが実際に何かを選択するまで、アプリはユーザーの写真や動画にアクセスする必要がありません。これにより、ユーザーのプライバシーが強化され、より効率的なエクスペリエンスが実現します。さらに、埋め込みの写真選択ツールを使用すると、ユーザーはクラウドベースのメディア ライブラリ全体にアクセスできます。一方、標準の写真権限はローカル ファイルのみに制限されています。
Google メッセージに埋め込まれた写真選択ツール
Google メッセージは、埋め込みの写真選択ツールの機能を活用しています。統合方法は次のとおりです。
- 直感的な配置: 写真選択ツールはカメラボタンのすぐ下に配置されているため、ユーザーは新しい写真を撮影するか、既存の写真を選択するかを明確に選択できます。
- 動的なプレビュー: ユーザーが写真をタップすると、すぐに大きなプレビューが表示されるため、選択した写真を簡単に確認できます。写真の選択を解除すると、プレビューが消え、すっきりとした画面が保たれます。
- 展開してさらにコンテンツを表示: 最初のビューは簡素化されており、最近の写真に簡単にアクセスできます。ただし、ユーザーは写真選択ツールを簡単に展開して、Google フォトのクラウド コンテンツを含むライブラリ内のすべての写真や動画を閲覧して選択できます。
- ユーザーの選択を尊重: 埋め込み型の写真選択ツールは、ユーザーが選択した特定の写真や動画へのアクセスのみを許可します。つまり、写真と動画の権限をリクエストする必要がなくなります。また、ユーザーが写真や動画へのアクセスを制限している状況をメッセージが処理する必要もなくなります。
実装
埋め込みの写真選択ツールは、写真選択ツール Jetpack ライブラリを使用すると簡単に統合できます。
Jetpack Compose
まず、Jetpack 写真選択ツール ライブラリを依存関係として追加します。
implementation("androidx.photopicker:photopicker-compose:1.0.0-alpha01")
EmbeddedPhotoPicker コンポーズ可能な関数は、埋め込みの写真選択ツール UI を Compose 画面に直接組み込むメカニズムを提供します。このコンポーズ可能な関数は、埋め込みの写真選択ツール 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 写真選択ツール ライブラリを依存関係として追加します。
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)
なお、埋め込み型の写真選択ツールは、Android 14(API レベル 34)以降と SDK 拡張機能 15 以降を搭載したデバイスで利用できます。詳しくは、写真選択ツールを利用できるデバイスをご覧ください。
ユーザーのプライバシーとセキュリティを強化するため、システムは描画やオーバーレイを防止する方法で埋め込みの写真選択ツールをレンダリングします。この意図的な設計上の選択は、広告バナーを計画する場合と同様に、UX で写真選択ツールの表示領域を個別の専用要素として考慮する必要があることを意味します。
フィードバックやご提案がある場合は、Issue Tracker にチケットを送信してください。
-
サービスに関する最新情報次期 Android リリースをターゲットとするアプリでは、WRITE_CONTACTS 権限を使用して連絡先に直接書き込むことができなくなります。
Roxanna Aliabadi Walker • 所要時間: 6 分 -
サービスに関する最新情報プライバシーとユーザー管理は、Android エクスペリエンスの中心にあります。写真選択ツールによってメディア共有が安全かつ簡単に実装できるようになったのと同様に、連絡先の選択でも、同じレベルのプライバシー、シンプルさ、優れたユーザー エクスペリエンスを実現します。
Roxanna Aliabadi Walker • 所要時間: 4 分 -
サービスに関する最新情報Unity は世界中のモバイルタイトルの半数以上を支えており、第一印象が重要です。ゲーム デベロッパーにとって、スムーズで信頼性の高いプレーヤー オンボーディング エクスペリエンスは、1 日目の維持率と長期的なエンゲージメントを促進するために不可欠です。
Michelle Martin • 所要時間: 1 分
Android 開発に関する最新の分析情報を毎週メールでお届けします。