プライバシーとユーザーによる管理は、Android エクスペリエンスの中核にあり続けます。写真選択ツールによってメディアの共有が安全かつ簡単に実装できるようになったのと同様に、連絡先の選択にも同じレベルのプライバシー、シンプルさ、優れたユーザー エクスペリエンスがもたらされます。
連絡先のプライバシーに関する新しい基準
これまで、特定のユーザーの連絡先へのアクセスを必要とするアプリは、広範な READ_CONTACTS 権限に依存していました。このアプローチは機能しますが、アプリに必要以上のデータが提供されることがよくありました。Android 17 で導入された新しい Android 連絡先選択ツールは、連絡先の選択のための標準化された安全な検索可能なインターフェースを提供することで、この動的な状況を変えます。
この機能により、ユーザーはアプリにアクセスを許可する連絡先を特定して選択できるようになり、データの透明性と権限フットプリントの最小化に対する Android の取り組みに沿ったものとなります。
フィルタの仕組み
デベロッパーは、Intent.ACTION_PICK_CONTACTS インテントを使用して連絡先ピッカー(TBC)を統合できます。この更新された API には、次のような強力な機能がいくつかあります。
- 詳細なデータ リクエスト: アプリは、連絡先レコード全体ではなく、電話番号やメールアドレスなど、必要なフィールドを正確に指定できます。
- 複数選択のサポート: ピッカーは、単一の連絡先と複数の連絡先の両方の選択をサポートしているため、デベロッパーはグループへの招待などの機能でより柔軟に対応できます。
- 選択制限: デベロッパーは、ユーザーが一度に選択できる連絡先の数にカスタム制限を設定できます。
- 一時的なアクセス: 選択すると、システムはリクエストされたデータへの一時的な読み取りアクセスを提供するセッション URI を返します。これにより、アクセスが必要以上に長く継続されることはありません。
- 他のプロファイルへのアクセス: この新しいインテントを使用すると、インターフェースで、仕事用プロファイル、クローン プロファイル、プライベート スペースなどの他のユーザー プロファイルからコンテンツを選択できます。
-
パフォーマンスの最適化: 連絡先ピッカー(TBC)は、結果の一括クエリを可能にする単一の URI を返します。これにより、
ACTION_PICKで要求されるように個々の連絡先 URI を個別にクエリする必要がなくなります。この効率性により、単一の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 アクセスは一時的なため、選択したデータをすぐに永続化します。
続きを読む
-
プロダクト ニュース
埋め込み写真選択ツール: アプリ内で写真や動画を非公開でリクエストする、よりシームレスな方法。
Roxanna Aliabadi Walker, Yacine Rezgui • 所要時間: 8 分
-
プロダクト ニュース
Android Studio Panda 4 が安定版となり、本番環境で使用できる準備が整いました。このリリースでは、プランニング モードや次の編集の予測などが導入され、高品質の Android アプリをこれまで以上に簡単に作成できるようになりました。
Matt Dyor • 所要時間: 5 分
-
プロダクト ニュース
Android デベロッパーで、革新的な AI 機能をアプリに実装したいとお考えの場合は、最近リリースされた強力な新機能をご利用ください。
Thomas Ezan • 所要時間: 3 分
メールを受け取る
Android 開発に関する最新の分析情報を毎週メールでお届けします。