Wiadomości o usłudze

Bardziej niezawodny sposób dodawania i edytowania kontaktów na Androidzie

Czas czytania: 6 min
Wyświetl profil Roxanny Aliabadi Walker
Roxanna Aliabadi Walker Menedżer produktu, Android

Co się zmienia

Aplikacje kierowane na nadchodzącą wersję Androida nie będą już mogły bezpośrednio zapisywać kontaktów z uprawnieniem WRITE_CONTACTS. Zamiast tego możesz użyć standardowych intencji kontaktów (ACTION_INSERT, ACTION_EDIT i ACTION_INSERT_OR_EDIT). Pełną implementację referencyjną znajdziesz w przewodniku dla deweloperów na temat modyfikowania kontaktów za pomocą intencji. 

Na urządzeniach z nadchodzącą wersją Androida te 3 intencje dotyczące kontaktów będą działać spójnie i nie będą wymagać żadnych uprawnień. Jeśli Twoja aplikacja jest kierowana na nadchodzącą wersję Androida lub nowszą i próbuje bezpośrednio zapisywać kontakty, te zapisy zostaną odrzucone i spowodują błąd SecurityException lub odmowę uprawnień. W przypadku niewielkiej liczby aplikacji, które wymagają szerokiego dostępu do tworzenia i modyfikowania kontaktów, w Androidzie 17 QPR2 (IV kwartał 2026 r.) pojawi się nowe specjalne uprawnienie dostępu do aplikacji: MANAGE_CONTACTS. Google Play publikuje dziś zaktualizowane wytyczne dotyczące dostępu do kontaktów, w tym MANAGE_CONTACTS.

Dlaczego to robimy

Obecnie, jeśli aplikacja musi zapisać choćby jeden kontakt, możesz poprosić o uprawnienie WRITE_CONTACTS. To uprawnienie zapewnia jednak szeroki dostęp, umożliwiając aplikacji wprowadzanie w tle zmian w kontaktach na całym urządzeniu, nawet bez interakcji użytkownika. Większość aplikacji nie potrzebuje tak szerokich uprawnień.

Chociaż istnieją alternatywne rozwiązania, otrzymaliśmy od Was informacje, że powinny być one bardziej ustandaryzowane w całym ekosystemie. Wcześniej intencje kontaktów działały inaczej na różnych urządzeniach. Niektóre edytory odrzuciły przekazane przez Ciebie dane, np. zdjęcie kontaktu. Na niektórych urządzeniach przycisk Wstecz zapisywał kontakt bez wiedzy użytkownika lub opcja „Utwórz nowy kontakt” była trudna do znalezienia. Dlatego poproszenie o zgodę było racjonalnym wyborem. Rozwiązujemy wszystkie te problemy w nadchodzącej wersji Androida i wymagamy, aby urządzenia z tą wersją spełniały te same standardy.

Co użyć zamiast tego

Dodawanie i edytowanie kontaktu: używanie intencji kontaktu

Gdy używasz tych intencji – ACTION_INSERT, ACTION_EDIT i ACTION_INSERT_OR_EDIT – system wykonuje najtrudniejsze zadania, prezentując przejrzysty,  ustandaryzowany edytor kontaktów. Otwiera szybki formularz w formie półarkusza, w którym można dodawać nowe wpisy, lub umożliwia użytkownikowi łatwe wyszukiwanie i dodawanie informacji do istniejącego kontaktu. Użytkownicy mogą bez problemu rozwinąć ten widok do pełnoekranowego edytora, aby podać więcej szczegółów, a aplikacja nie musi mieć bezpośrednich uprawnień do zapisu. Poniżej znajdziesz wstępne makiety UX: 

Figure01_01_Quick_Insert_Half_Sheet_Prefilled.png
Zrzut ekranu 2 – Tytuł: Makieta pełnego arkusza tworzenia nowego kontaktu: zrzut ekranu wstępnego interfejsu użytkownika Androida przedstawiający pełnoekranowy edytor kontaktów z selektorem awatara oraz polami imienia i nazwiska, numeru telefonu i adresu e-mail. Projekt jest oznaczony jako „UX not Final” (To nie jest ostateczna wersja wrażeń użytkownika).

Ilustracja 1. Tworzenie nowego kontaktu  *UX nie jest ostateczny*

Figure02_01_Add_To_Existing_0_Search_State_Half_Sheet.png
Zrzut ekranu 2 – tytuł: makieta pełnoekranowa „Dodaj informacje do istniejącego kontaktu”: zrzut ekranu przedstawiający przykładowy interfejs użytkownika Androida z pełnoekranowym edytorem kontaktów z selektorem awatara oraz polami imienia i nazwiska, numeru telefonu i adresu e-mail. Projekt pełnej strony zawiera elementy z informacjami o polach dodatkowych, które są ujawniane stopniowo. Projekt jest oznaczony jako „To nie jest ostateczna wersja wrażeń użytkownika”.

Rysunek 2. Dodawanie informacji do istniejącego kontaktu *UX nie jest ostateczny*


Uwaga: różni producenci urządzeń mogą modyfikować interfejs użytkownika, aby dopasować go do stylu swojego systemu operacyjnego, ale działanie pozostanie takie samo.


Możesz jeszcze bardziej usprawnić proces tworzenia, przekazując wraz z intencją opcjonalne ładunki danych, czyli dodatki:

  • EXTRA_ACCOUNT sugeruje, na którym koncie zapisać kontakt, co jest przydatne, jeśli aplikacja synchronizuje kontakty. Ta funkcja była już dostępna, ale jej obsługa różniła się w zależności od urządzenia. Począwszy od nadchodzącej wersji Androida, obsługa będzie ustandaryzowana na wszystkich urządzeniach.
  • EXTRA_PHOTO_URI przekazuje zdjęcie, aby wstępnie wypełnić zdjęcie kontaktu. Edytor obsługuje uprawnienia do odczytu identyfikatora URI. Jest to nowa funkcja w nadchodzącej wersji Androida, która będzie obsługiwana w całym ekosystemie.

Przed: zapisywanie bezpośrednio za pomocą WRITE_CONTACTS (gdy aplikacja będzie kierowana na nadchodzącą wersję Androida, spowoduje to błąd SecurityException lub odmowę uprawnień).

 

// Requires android.permission.WRITE_CONTACTS
fun saveContactDirectly(context: Context, name: String, phone: String, account: Account) {
    val ops = arrayListOf(
        ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type)
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, account.name)
            .build(),
        ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
            .withValue(
                ContactsContract.Data.MIMETYPE,
                ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
            )
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
            .build(),
        ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
            .withValue(
                ContactsContract.Data.MIMETYPE,
                ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
            )
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .build()
    )
    context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
}

 

 

Po: otwarcie edytora kontaktów (nie są wymagane uprawnienia)

 

fun saveContactWithEditor(
    context: Context,
    name: String,
    phone: String,
    account: Account,
    photoUri: Uri
) {
    val intent = Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI).apply {
        putExtra(ContactsContract.Intents.Insert.NAME, name)
        putExtra(ContactsContract.Intents.Insert.PHONE, phone)
        // Suggest which account the contact is saved to
        putExtra(ContactsContract.Intents.Insert.EXTRA_ACCOUNT, account)
        // New in the upcoming Android release: pre-fill the contact photo
        putExtra(ContactsContract.Intents.Insert.EXTRA_PHOTO_URI, photoUri)
        clipData = ClipData.newRawUri("", photoUri)
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    context.startActivity(intent)
}

 

Przed: bezpośrednie aktualizowanie istniejącego kontaktu

 

// Requires android.permission.WRITE_CONTACTS
fun addPhoneDirectly(context: Context, rawContactId: Long, phone: String) {
    val values = ContentValues().apply {
        put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
        put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
        put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
    }
    context.contentResolver.insert(ContactsContract.Data.CONTENT_URI, values)
}

Po: umożliwienie użytkownikowi wybrania kontaktu do zaktualizowania lub utworzenia nowego

fun addPhoneWithEditor(context: Context, phone: String) {
    val intent = Intent(Intent.ACTION_INSERT_OR_EDIT).apply {
        type = ContactsContract.Contacts.CONTENT_ITEM_TYPE
        putExtra(ContactsContract.Intents.Insert.PHONE, phone)
    }
    context.startActivity(intent)
}

Jeśli wiesz już, który kontakt chcesz zaktualizować, użyj metody ACTION_EDIT z identyfikatorem URI tego kontaktu:

fun editKnownContact(context: Context, contactUri: Uri, phone: String) {
    val intent = Intent(Intent.ACTION_EDIT, contactUri).apply {
        putExtra(ContactsContract.Intents.Insert.PHONE, phone)
    }
    context.startActivity(intent)
}

 

Wybieranie kontaktu: używanie selektora kontaktów

Jeśli chcesz tylko odczytywać kontakty wybrane przez użytkownika, użyj selektora kontaktów, który jest dostępny od Androida 17. Nie wymaga żadnych uprawnień.

Aplikacje do obsługi połączeń i komunikatory: korzystanie z katalogu osób

Niektóre aplikacje dodają do kontaktów działania „Zadzwoń przez…” lub „Wyślij wiadomość przez…”, aby użytkownicy mogli rozpocząć połączenie lub czat z poziomu aplikacji Telefon lub Kontakty. Jeśli Twoja aplikacja robi to obecnie przez zapisywanie dodatkowych wpisów w kontaktach użytkownika, przejdź na Katalog osób, który pojawi się w Androidzie QPR2 (IV kwartał 2026 r.). Jest to nowy katalog systemowy, w którym aplikacja publikuje informacje o tym, jak użytkownicy mogą się ze sobą kontaktować, bez edytowania kontaktów użytkownika.

Przykłady migracji aplikacji

Aby zobaczyć, jak te alternatywy działają w praktyce, zapoznaj się z kilkoma przykładami, jak różne typy aplikacji mogą podchodzić do dostępu do kontaktów:

Przykładowy typ aplikacji (niewyłączny).Zalecana ścieżka migracji
Aplikacje społecznościowe, do płatności P2P i wspólnych przejazdówJeśli aplikacja umożliwia użytkownikom znajdowanie znajomych, dzielenie rachunku lub udostępnianie stanu podróży, nie potrzebujesz dostępu do kontaktów. Używaj selektora kontaktów do jednorazowego wybierania kontaktów i uruchamiaj ACTION_INSERT z EXTRA_PHOTO_URI, gdy użytkownik kliknie „Zapisz w kontaktach” w profilu.
Skanery wizytówek i aplikacje do nawiązywania kontaktów podczas wydarzeńJeśli aplikacja skanuje identyfikator lub kod QR i zapisuje nowy kontakt albo dodaje służbowy numer telefonu do wpisu dotyczącego współpracownika, użyj ACTION_INSERT lub ACTION_INSERT_OR_EDIT. Ustandaryzowany edytor w formie połowy arkusza wstępnie wypełnia przeanalizowane pola (imię i nazwisko, firma, telefon, adres e-mail i zdjęcie), dzięki czemu użytkownik może je zweryfikować i zapisać jednym kliknięciem bez przyznawania uprawnień do działania w tle.
Aplikacje do połączeń VoIP i obsługi wiadomościZamiast prosić WRITE_CONTACTS o wstrzykiwanie niestandardowych wierszy „Zadzwoń do…” lub „Wyślij wiadomość do…” do książki adresowej użytkownika, opublikuj metadane dotyczące osiągalności w katalogu osób i użyj ACTION_INSERT, gdy użytkownicy dodają nowy kontakt z wątku czatu.
Aplikacje CRM i aplikacje do synchronizacji z chmurą dla firmJeśli aplikacja przeprowadza ciągłą, dwukierunkową synchronizację kontaktów w tle z kontem w chmurze lub kontem firmowym, gdzie wyświetlanie użytkownikowi interfejsu intencji dla każdego rekordu nie jest możliwe, aplikacja kwalifikuje się do zadeklarowania uprawnienia MANAGE_CONTACTS .

Zmiany do sprawdzenia

  • Niestandardowe typy danych nie są obsługiwane w edytorze. Jeśli przekażesz wiersze Insert.DATA z typami MIME innymi niż standardowe typy ContactsContract.CommonDataKinds, te typy MIME zostaną zignorowane przez edytor bez powiadomienia.
  • SHOW_OR_CREATE_CONTACT nie jest obsługiwany przez edytor systemowy. Aplikacje do obsługi kontaktów, które są zainstalowane na urządzeniu, nadal mogą obsługiwać te dane. Aby zapewnić spójne działanie na różnych urządzeniach, użyj ACTION_INSERT_OR_EDIT.
  • Domyślnie zapisywanie kończy się niepowodzeniem. Jeśli Twoja aplikacja będzie kierowana na nadchodzącą wersję Androida, każdy bezpośredni zapis bez MANAGE_CONTACTS spowoduje błąd SecurityException or permission denial. Sprawdź, czy tak jest.

Czy potrzebuję MANAGE_CONTACTS?

Raczej nie. Jeśli aplikacja zapisuje lub edytuje kontakty na prośbę użytkownika, powyższe intencje obejmują te działania bez konieczności uzyskiwania uprawnień i składania deklaracji w Konsoli Play.

MANAGE_CONTACTS jest przeznaczone dla aplikacji, których głównym celem jest zarządzanie kontaktami, gdy intencje nie wystarczają. Przykład:

Twoja aplikacja…Potrzebujesz MANAGE_CONTACTS?
Zapisuje kontakt z czatu, wizytówki lub zamówienia.Nie,użyj ACTION_INSERT
Dodaje numer lub adres e-mail do istniejącego kontaktu.Nie,użyj ACTION_INSERT_OR_EDIT lub ACTION_EDIT
Umożliwia użytkownikom wybieranie kontaktów, które chcą zaprosić, którym chcą coś udostępnić lub zapłacić.Nie,użyj selektora kontaktów.
Dodaje do kontaktów działania związane z połączeniami lub wiadomościami.Nie,użyj katalogu osób
ma własny ekran edycji kontaktów z logo marki.Nie,sam niestandardowy interfejs nie wystarczy.
jest specjalną aplikacją do zarządzania kontaktami lub zastępującą dialer;Prawdopodobnie tak
jest aplikacją do identyfikacji rozmówcy i ochrony przed spamem, która masowo aktualizuje wpisy w książce adresowej;Prawdopodobnie tak
Synchronizuje kontakty z kontem firmowym lub kontem w chmurze w tle.Prawdopodobnie tak
wykonuje zbiorcze tworzenie i przywracanie kopii zapasowej kontaktów lub przenoszenie danych z jednego telefonu na drugi.Prawdopodobnie tak
ustawiać niestandardowe dzwonki dla poszczególnych kontaktów;Prawdopodobnie tak

Jak to działa:

  • MANAGE_CONTACTS to specjalne uprawnienie dostępu do aplikacji. Nie jest to okno środowiska wykonawczego. Użytkownicy mogą ją włączyć, wybierając Ustawienia > Specjalny dostęp do aplikacji > Zarządzaj kontaktami.
// Opens Settings > Apps > Special app access > Manage contacts
val intent = Intent(Settings.ACTION_MANAGE_CONTACTS, Uri.fromParts("package", packageName, null))
context.startActivity(intent)
  • Obejmuje odczytywanie, dodawanie, edytowanie i usuwanie kontaktów, kont oraz danych z katalogu osób. Nie musisz też prosić o READ_CONTACTS.
  • Aby opublikować aplikację w Google Play z uprawnieniem MANAGE_CONTACTS, prześlij w Konsoli Play deklarację wyjaśniającą, dlaczego intencje nie wystarczają do działania głównej funkcjonalności aplikacji.

Etapy migracji

  • Znajdź zapisy kontaktów. Wyszukaj w kodzie ciągi WRITE_CONTACTS, ContactsContract.RawContacts.CONTENT_URI, ContactsContract.Data.CONTENT_URI i applyBatch(ContactsContract.AUTHORITY, …).
  • Zastąp każdy zapis wywołany przez użytkownika intencją: ACTION_INSERT w przypadku nowych kontaktów oraz ACTION_EDIT lub ACTION_INSERT_OR_EDIT w przypadku aktualizacji.
  • Używaj tylko standardowych typów danych w Insert.DATA. Przenieś działania związane z połączeniami lub wiadomościami do katalogu osób.
  • Zastanów się, czy naprawdę potrzebujesz MANAGE_CONTACTS, korzystając z tabeli powyżej. Jeśli tak, zaplanuj deklarację w Konsoli Play.
  • Usuń WRITE_CONTACTS z pliku manifestu, gdy nie będzie już potrzebny w żadnej ścieżce kodu.

Oś czasu

KiedySkutki
21 października 2026 r.Google Play ogłasza zasady dotyczące MANAGE_CONTACTS
2026 r. (Android 17, kwartalna wersja platformy 2)Uprawnienie MANAGE_CONTACTS staje się dostępne
2027 (nadchodząca wersja Androida)Uprawnienie WRITE_CONTACTS nie przyznaje już dostępu do zapisu aplikacjom kierowanym na nadchodzącą wersję Androida. Intencje dotyczące kontaktów są ustandaryzowane na różnych urządzeniach
2027 r., nie wcześniej niż nadchodząca wersja AndroidaKonsola Play prosi aplikacje, które używają uprawnienia MANAGE_CONTACTS, o przesłanie deklaracji
2027 r., nie wcześniej niż nadchodząca wersja AndroidaWymagana zgodność z zasadami Google Play

Najczęstsze pytania

  • Czy dotyczy to każdej aplikacji?
    • Wycofanie WRITE_CONTACTS dotyczy tylko aplikacji kierowanych na nadchodzącą wersję Androida i nowsze. Aplikacje kierowane na starsze wersje zachowają swoje obecne działanie, ale nowy, ustandaryzowany edytor kontaktów będzie dostępny we wszystkich aplikacjach działających w nowym systemie operacyjnym, niezależnie od docelowego pakietu SDK.
  • Czy moja aplikacja może usuwać kontakty za pomocą intencji?
    • No. There's no intent for deleting contacts. Użytkownicy usuwają kontakty w aplikacji Kontakty. Aplikacje z MANAGE_CONTACTS mogą usuwać kontakty bezpośrednio.
  • Mam już własny ekran edycji kontaktów. Czy mogę go zachować?
    • Tylko wtedy, gdy Twoja aplikacja kwalifikuje się do MANAGE_CONTACTS. Chęć zachowania niestandardowego projektu nie jest wystarczającym powodem.
  • Czy MANAGE_CONTACTS zastępuje READ_CONTACTS?
    • MANAGE_CONTACTS obejmuje dostęp do odczytu, więc nie musisz prosić o oba uprawnienia, ale wiele aplikacji kwalifikuje się do READ_CONTACTS, mimo że nie muszą w ogóle modyfikować kontaktów. Te aplikacje mogą nadal prosić o READ_CONTACTS.
  • Co z użytkownikami starszych wersji Androida?
    • Intencje kontaktów działają też w starszych wersjach i są obsługiwane przez aplikację Kontakty na urządzeniu. Większość aplikacji będzie działać prawidłowo na wszystkich urządzeniach korzystających z tych intencji. Jeśli w Twojej aplikacji występuje konkretny problem ze spójnością, który został rozwiązany tylko w najnowszych wersjach, możesz zachować uprawnienie WRITE_CONTACTS w pliku manifestu z odpowiednim zakresem maxSdkVersion.
Czytaj dalej