Android エクスペリエンスでは、プライバシーとユーザーによる管理が引き続き重視されています。写真選択ツールによってメディアの共有が安全かつ簡単に実装できるようになったのと同様に、連絡先の選択にも同レベルのプライバシー、シンプルさ、優れたユーザー エクスペリエンスがもたらされます。
連絡先のプライバシーに関する新しい標準
これまで、特定のユーザーの連絡先へのアクセスを必要とするアプリケーションは、広範な READ_CONTACTS 権限に依存していました。この方法は機能しますが、アプリに必要以上のデータへのアクセスを許可してしまうことがよくありました。Android 17 で導入された新しい Android 連絡先選択ツールは、連絡先選択のための標準化された安全な検索可能なインターフェースを提供することで、この状況を変えます。
この機能により、ユーザーはアプリにアクセスを許可する連絡先を具体的に選択できます。これは、データの透明性と権限のフットプリントの最小化に対する Android の取り組みに沿ったものです。
仕組み
デベロッパーは、Intent.ACTION_PICK_CONTACTS インテントを使用して連絡先選択ツールを統合できます。この更新された API には、次のような強力な機能がいくつか用意されています。
- 詳細なデータ リクエスト: アプリは、連絡先レコード全体を受け取るのではなく、電話番号やメールアドレスなど、必要なフィールドを正確に指定できます。
- 複数選択のサポート: 選択ツールは、単一の連絡先と複数の連絡先の両方の選択をサポートしているため、デベロッパーはグループ招待などの機能をより柔軟に実装できます。
- 選択制限: デベロッパーは、ユーザーが一度に選択できる連絡先の数にカスタム制限を設定できます。
- 一時的なアクセス: 選択すると、システムはリクエストされたデータへの一時的な読み取りアクセスを提供するセッション URI を返します。これにより、アクセスが必要以上に長く続くことはありません。
- 他のプロファイルへのアクセス: この新しいインテントを使用すると、インターフェースで、仕事用プロファイル、クローン プロファイル、プライベート スペースなど、他のユーザー プロファイルからコンテンツを選択できます。
- パフォーマンスの最適化: 連絡先選択ツールは、結果をまとめてクエリできる単一の URI を返します。これにより、個々の連絡先 URI を個別にクエリする必要がなくなります。これは
ACTION_PICKで必要でした。この効率化により、単一のBinderトランザクションを利用することで、システム オーバーヘッドがさらに削減されます。
下位互換性と実装
Android 17 以降を搭載したデバイスの場合、連絡先データ型を指定する従来の ACTION_PICK インテントは、より安全な新しいインターフェースに自動的にアップグレードされます。ただし、複数選択などの高度な機能を最大限に活用するには、デベロッパーは実装コードを更新し、ContentResolver を使用して返されたセッション URI をクエリすることをおすすめします。
連絡先選択ツールを統合する連絡先選択ツールを統合するには、デベロッパーは ACTION_PICK_CONTACTS インテントを使用します。以下は、選択ツールを起動して、メールや電話番号などの特定のデータ フィールドをリクエストする方法を示すコード例です。
// State to hold the list of selected contacts var contacts by remember { mutableStateOf<List>(emptyList()) } // Launcher for the Contact Picker intent val pickContact = rememberLauncherForActivityResult(StartActivityForResult()) { if (it.resultCode == Activity.RESULT_OK) { val resultUri = it.data?.data ?: return@rememberLauncherForActivityResult // Process the result URI in a background thread coroutine.launch { contacts = processContactPickerResultUri(resultUri, context) } } } // Define the specific contact data fields you need val requestedFields = arrayListOf( Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE, ) // Set up the intent for the Contact Picker val pickContactIntent = Intent(ACTION_PICK_CONTACTS).apply { putExtra(EXTRA_PICK_CONTACTS_SELECTION_LIMIT, 5) putStringArrayListExtra( EXTRA_PICK_CONTACTS_REQUESTED_DATA_FIELDS, requestedFields ) putExtra(EXTRA_PICK_CONTACTS_MATCH_ALL_DATA_FIELDS, false) } // Launch the picker pickContact.launch(pickContactIntent)
ユーザーが選択を行うと、アプリは返されたセッション URI をクエリしてリクエストされた連絡先情報を抽出することで、結果を処理します。
// Data class representing a parsed Contact with selected details data class Contact(val id: String, val name: String, val email: String?, val phone: String?) // Helper function to query the content resolver with the URI returned by the Contact Picker. // Parses the cursor to extract contact details such as name, email, and phone number private suspend fun processContactPickerResultUri( sessionUri: Uri, context: Context ): List<Contact> = withContext(Dispatchers.IO) { // Define the columns we want to retrieve from the ContactPicker ContentProvider val projection = arrayOf( ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY, ContactsContract.Data.MIMETYPE, // Type of data (e.g., email or phone) ContactsContract.Data.DATA1, // The actual data (Phone number / Email string) ) val results = mutableListOf<Contact>() // Note: The Contact Picker Session Uri doesn't support custom selection & selectionArgs. context.contentResolver.query(sessionUri, projection, null, null, null)?.use { cursor -> // Get the column indices for our requested projection val contactIdIdx = cursor.getColumnIndex(ContactsContract.Contacts._ID) val mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE) val nameIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY) val data1Idx = cursor.getColumnIndex(ContactsContract.Data.DATA1) while (cursor.moveToNext()) { val contactId = cursor.getString(contactIdIdx) val mimeType = cursor.getString(mimeTypeIdx) val name = cursor.getString(nameIdx) ?: "" val data1 = cursor.getString(data1Idx) ?: "" // Determine if the current row represents an email or a phone number val email = if (mimeType == Email.CONTENT_ITEM_TYPE) data1 else null val phone = if (mimeType == Phone.CONTENT_ITEM_TYPE) data1 else null // Add the parsed contact to our results list results.add(Contact(contactId, name, email, phone)) } } return@withContext results }
詳細なドキュメントはこちらをご覧ください。
デベロッパー向けのベスト プラクティス
最高のユーザー エクスペリエンスを提供し、高いセキュリティ基準を維持するため、次のことをおすすめします。
- データの最小化: アプリに必要な特定のデータ フィールド(メールなど)のみをリクエストします。
- 即時永続化: セッション URI のアクセスは一時的なため、選択したデータをすぐに永続化します。
続きを読む
-
2026 年 1 月 27 日2026 年 1 月 27 日
プロダクト ニュース
埋め込み型の写真選択ツール: アプリ内で写真や動画を非公開でリクエストする、よりシームレスな方法。
Roxanna Aliabadi Walker, Yacine Rezgui • 所要時間: 8 分
-
プロダクト ニュース
モバイル エコシステムは常に進化しており、新たな機会と新たな脅威の両方をもたらしています。このような変化を通じて、Android と Google Play は、数十億のユーザーが安心してアプリを利用できるようにし、デベロッパーのイノベーションが発展できるようにすることに取り組んでいます。
Vijaya Kaza • 所要時間: 3 分
-
2026 年 4 月 22 日2026 年 4 月 22 日
プロダクト ニュース
Jetpack Compose 2026 年 4 月リリースは安定版です。このリリースには、Compose コアモジュール バージョン 1.11(BOM マッピングの完全版を参照)、共有要素デバッグツール、トラックパッド イベントなどが含まれています。
Meghan Mehta • 所要時間: 5 分
最新情報の入手
Android 開発に関する最新の分析情報を毎週メールでお届けします。