개인 정보 보호와 사용자 관리 기능은 Android 환경의 핵심으로 유지됩니다. 사진 선택 도구가 미디어 공유를 안전하고 쉽게 구현할 수 있도록 지원하는 것처럼 이제 연락처 선택에도 동일한 수준의 개인 정보 보호, 단순성, 뛰어난 사용자 환경을 제공합니다.
연락처 개인 정보 보호를 위한 새로운 표준
이전에는 특정 사용자의 연락처에 액세스해야 하는 애플리케이션이 광범위한 READ_CONTACTS 권한을 사용했습니다. 이 접근 방식은 작동하지만 앱에 필요한 것보다 더 많은 데이터를 부여하는 경우가 많았습니다. Android 17에서 도입된 새로운 Android 연락처 선택 도구는 연락처 선택을 위한 표준화되고 안전하며 검색 가능한 인터페이스를 제공하여 이러한 역학 관계를 바꿉니다.
이 기능을 통해 사용자는 선택한 특정 연락처에만 앱 액세스 권한을 부여할 수 있으며, 이는 데이터 투명성과 최소화된 권한 사용을 위한 Android의 약속과 일치합니다.
작동 방식
개발자는 Intent.ACTION_PICK_CONTACTS 인텐트를 사용하여 연락처 선택 도구를 통합할 수 있습니다. 이 업데이트된 API는 다음과 같은 강력한 기능을 제공합니다.
- 세부 데이터 요청: 앱은 전체 연락처 레코드를 수신하는 대신 전화번호나 이메일 주소와 같이 필요한 필드를 정확하게 지정할 수 있습니다.
- 다중 선택 지원: 선택 도구는 단일 및 다중 연락처 선택을 모두 지원하므로 개발자는 그룹 초대와 같은 기능을 더 유연하게 사용할 수 있습니다.
- 선택 제한: 개발자는 사용자가 한 번에 선택할 수 있는 연락처 수에 맞춤 제한을 설정할 수 있습니다.
- 임시 액세스: 선택하면 시스템에서 요청된 데이터에 대한 임시 읽기 액세스를 제공하는 세션 URI를 반환하여 액세스가 필요 이상으로 지속되지 않도록 합니다.
- 다른 프로필에 대한 액세스: 이 새로운 인텐트를 사용하면 인터페이스에서 사용자가 직장 프로필, 클론 프로필, 비공개 스페이스와 같은 다른 사용자 프로필의 콘텐츠를 선택할 수 있습니다.
- 최적화된 성능: 연락처 선택기는
ACTION_PICK에서 요구하는 대로 개별 연락처 URI를 별도로 쿼리할 필요 없이 집합적 결과 쿼리를 허용하는 단일 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 Emulator를 사용하면 멀티 디바이스 상호작용을 그 어느 때보다 쉽게 테스트할 수 있습니다.
Steven Jenkins • 전문 길이: 2분
-
제품 소식
모든 개발자의 AI 워크플로와 요구사항은 고유하며, AI가 개발에 어떤 도움을 줄지 선택할 수 있는 것이 중요합니다. 1월에는 Android 스튜디오에서 AI 기능을 구동하는 데 로컬 또는 원격 AI 모델을 선택할 수 있는 기능이 도입되었습니다.
Matthew Warner • 전문 길이: 2분
소식 받아 보기
Android 개발 관련 최신 정보를 이메일로 받아 보세요.