dostawca kontaktów

Dostawca kontaktów to wydajny i elastyczny komponent Androida, który zarządza centralnym repozytorium danych o użytkownikach na urządzeniu. Dostawca kontaktów jest źródłem danych wyświetlanych w aplikacji Kontakty na urządzeniu. Możesz też uzyskać dostęp do jego danych w swojej aplikacji i przenosić dane między urządzeniem a usługami online. Dostawca obsługuje wiele źródeł danych i stara się zarządzać jak największą liczbą danych o każdej osobie, co powoduje, że jego organizacja jest skomplikowana. Z tego powodu interfejs API dostawcy obejmuje szeroki zestaw klas i interfejsów, które ułatwiają pobieranie i modyfikowanie danych.

W tym przewodniku znajdziesz informacje o tych kwestiach:

  • Podstawowa struktura dostawcy
  • Jak pobrać dane od dostawcy.
  • Jak zmodyfikować dane u dostawcy.
  • Jak napisać adapter synchronizacji do synchronizowania danych między serwerem a dostawcą kontaktów.

W tym przewodniku przyjęto założenie, że znasz podstawy dotyczące dostawców treści na Androida. Aby dowiedzieć się więcej o dostawcach treści na Androida, przeczytaj przewodnik Podstawy dotyczące dostawców treści.

Organizacja dostawcy kontaktów

Dostawca kontaktów to komponent dostawcy treści na Androida. Zawiera ona 3 rodzaje danych dotyczących osoby, z których każdy odpowiada tabeli oferowanej przez dostawcę, jak pokazano na rysunku 1:

Rysunek 1. Struktura tabeli dostawcy kontaktów

Te 3 tabele zwykle nazywane są nazwami klas umów. Klasy te definiują stałe wartości URI treści, nazwy kolumn i wartości kolumn używanych przez tabele:

Tabela ContactsContract.Contacts
Wiersze reprezentujące różne osoby na podstawie agregacji nieprzetworzonych wierszy kontaktów.
Tabela ContactsContract.RawContacts
Wiersze zawierające podsumowanie danych osoby, które są specyficzne dla typu i konta użytkownika.
Tabela ContactsContract.Data
Wiersze zawierające szczegóły kontaktu w postaci danych nieprzetworzonych, takich jak adresy e-mail lub numery telefonów.

Pozostałe tabele reprezentowane przez klasy kontraktu w ContactsContract to tabele pomocnicze, których dostawca kontaktów używa do zarządzania operacjami lub obsługi określonych funkcji w aplikacji kontaktów lub telefonii na urządzeniu.

Kontakty bazowe

Dane kontaktowe w postaci nieprzetworzonych danych reprezentują dane osoby pochodzące z jednego typu konta i jego nazwy. Dostawca danych kontaktowych zezwala na korzystanie z więcej niż 1 usługi online jako źródła danych o konkretnej osobie, dlatego zezwala na wiele nieprzetworzonych kontaktów dotyczących tej samej osoby. Wiele kontaktów w postaci nieprzetworzonych danych umożliwia też użytkownikowi połączenie danych osoby z więcej niż 1 konta tego samego typu.

Większość danych dotyczących nieprzetworzonego kontaktu nie jest przechowywana w tabeli ContactsContract.RawContacts. Zamiast tego jest przechowywany w co najmniej 1 wierszu tabeli ContactsContract.Data. Każdy wiersz danych zawiera kolumnę Data.RAW_CONTACT_ID, która zawiera wartość RawContacts._ID jego nadrzędnego wiersza ContactsContract.RawContacts.

Ważne kolumny kontaktów w postaci nieprzetworzonych danych

Ważne kolumny w tabeli ContactsContract.RawContacts są wymienione w tabeli 1. Po tabeli przeczytaj te uwagi:

Tabela 1. Ważne kolumny z danymi kontaktów

Nazwa kolumny Użyj Uwagi
ACCOUNT_NAME Nazwa konta danego typu, które jest źródłem tego nieprzetworzonego kontaktu. Na przykład nazwa konta Google to jeden z adresów Gmaila właściciela urządzenia. Więcej informacji znajdziesz w następnym wpisie dotyczącym ACCOUNT_TYPE. Format tej nazwy zależy od typu konta. Nie musi to być adres e-mail.
ACCOUNT_TYPE Typ konta, które jest źródłem tego nieprzetworzonego kontaktu. Na przykład typ konta Google to com.google. Zawsze sprawdzaj, czy Twój typ konta zawiera identyfikator domeny, która należy do Ciebie lub którą zarządzasz. Dzięki temu Twój rodzaj konta będzie unikalny. Typ konta, który udostępnia dane kontaktów, zwykle ma powiązany adapter synchronizacji, który synchronizuje się z dostawcą kontaktów.
DELETED Flaga „deleted” (usunięty) w przypadku nieprzetworzonego kontaktu. Ten parametr pozwala dostawcy kontaktów na utrzymywanie wiersza wewnętrznie, dopóki adaptery synchronizacji będą mogły usunąć go ze swoich serwerów, a następnie z repozytorium.

Uwagi

Oto ważne uwagi na temat tabeli ContactsContract.RawContacts:

  • Nazwa nieprzetworzonego kontaktu nie jest zapisana w kolumnie ContactsContract.RawContacts. Jest on przechowywany w tabeli ContactsContract.Data w wierszu ContactsContract.CommonDataKinds.StructuredName. Nieprzetworzony kontakt ma tylko 1 wiersz tego typu w tabeli ContactsContract.Data.
  • Uwaga: aby używać danych własnego konta w surowym wierszu kontaktu, musisz najpierw zarejestrować to konto w usłudze AccountManager. Aby to zrobić, poproś użytkowników o dodanie rodzaju konta i nazwy ich do listy kont. Jeśli tego nie zrobisz, dostawca kontaktów automatycznie usunie wiersz z kontaktem.

    Jeśli np. chcesz, aby aplikacja przechowywała dane kontaktów w usłudze internetowej z domeną com.example.dataservice, a konto użytkownika w tej usłudze to becky.sharp@dataservice.example.com, użytkownik musi najpierw dodać „typ” konta (com.example.dataservice) i „nazwa” konta (becky.smart@dataservice.example.com), zanim aplikacja będzie mogła dodawać nieprzetworzone wiersze kontaktów. Możesz wyjaśnić to wymaganie w dokumentacji lub poprosić użytkownika o dodanie typu i nazwy. Typy kont i ich nazwy są opisane bardziej szczegółowo w następnej sekcji.

Źródła nieprzetworzonych danych kontaktów

Aby zrozumieć, jak działają kontakty w postaci surowych danych, zastanów się nad użytkownikiem „Emilią Dickinson”, która ma na swoim urządzeniu zdefiniowane 3 konta użytkowników:

  • emily.dickinson@gmail.com
  • emilyd@gmail.com
  • Konto na Twitterze „belle_of_amherst”

Ten użytkownik ma włączoną opcję Synchronizuj kontakty na wszystkich 3 kontach w ustawieniach Konta.

Załóżmy, że Emily Dickinson otwiera okno przeglądarki, loguje się w Gmailu jako emily.dickinson@gmail.com, otwiera Kontakty i dodaje „Thomas Higginson”. Później loguje się w Gmailu jako emilyd@gmail.com i wysyła e-maila do „Thomasa Higginsona”, co automatycznie dodaje go do listy kontaktów. Obserwuje też na Twitterze konto „colonel_tom” (identyfikator Thomasa Higginsona).

W wyniku tej pracy dostawca kontaktów tworzy 3 dane kontaktowe:

  1. Nieprzetworzony kontakt użytkownika „Thomas Higginson” powiązany z adresem emily.dickinson@gmail.com. Typ konta użytkownika to Google.
  2. Drugi kontakt w formie tekstu zwykłego dla „Thomas Higginson”, powiązany z emilyd@gmail.com. Typ konta użytkownika to również Google. Na koncie Google jest drugi nieprzetworzony kontakt, mimo że jego nazwa jest identyczna z poprzednią, ponieważ ta osoba została dodana do innego konta użytkownika.
  3. Trzeci kontakt w formie tekstu niezaszyfrowanego dla „Thomas Higginson” powiązany z „belle_of_amherst”. Typ konta użytkownika to Twitter.

Dane

Jak już wspomnieliśmy, dane nieprzetworzonego kontaktu są przechowywane w wierszu ContactsContract.Data, który jest powiązany z wartością _ID nieprzetworzonego kontaktu. Dzięki temu pojedynczy kontakt nieprzetworzony może zawierać wiele wystąpień tego samego typu danych, np. adresów e-mail lub numerów telefonów. Jeśli na przykład adres e-mail „Thomas Higginson” dla adresu emilyd@gmail.com (nieprzetworzony wiersz kontaktu Thomasa Higginsona powiązany z kontem Google emilyd@gmail.com) ma domowy adres e-mail thigg@gmail.com, a służbowy adres e-mail thomas.higginson@gmail.com, dostawca kontaktów przechowuje te dwa wiersze adresów e-mail i łączy je z nieprzetworzonym kontaktem.

Zwróć uwagę, że jedna tabela zawiera różne typy danych. W tabeli ContactsContract.Data znajdziesz wiersze z danymi o nazwie wyświetlanej, numerze telefonu, adresie e-mail, adresie pocztowym, zdjęciu i witrynie. Aby ułatwić sobie zarządzanie, tabela ContactsContract.Data zawiera kolumny z nazwami opisowymi i inne z nazwami ogólnymi. Zawartość kolumny z nazwą opisową ma takie samo znaczenie niezależnie od typu danych w wierszu. Zawartość kolumny z nazwą ogólną ma różne znaczenie w zależności od typu danych.

Nazwa kolumny ma charakter opisowy.

Oto kilka przykładów opisowych nazw kolumn:

RAW_CONTACT_ID
Wartość kolumny _ID w kontakcie nieprzetworzonym dla tych danych.
MIMETYPE
Typ danych przechowywanych w tym wierszu wyrażony jako niestandardowy typ MIME. Dostawca kontaktów używa typów MIME zdefiniowanych w podklasach ContactsContract.CommonDataKinds. Te typy MIME są typu open source i mogą być używane przez dowolną aplikację lub adapter synchronizacji, który współpracuje z usługą dostawcy kontaktów.
IS_PRIMARY
Jeśli ten typ wiersza danych może wystąpić więcej niż raz w przypadku nieprzetworzonego kontaktu, kolumna IS_PRIMARY oznacza wiersz danych, który zawiera podstawowe dane dla tego typu. Jeśli na przykład użytkownik naciśnie długo numer telefonu kontaktu i wybierze Ustaw domyślny, kolumna IS_PRIMARY w wierszu ContactsContract.Data zawierającym ten numer będzie miała wartość różną od 0.

Ogólne nazwy kolumn

Dostępnych jest 15 ogólnych kolumn o nazwach DATA1DATA15 oraz 4 dodatkowe ogólne kolumny SYNC1SYNC4, których powinny używać tylko adaptery synchronizacji. Stałe nazwy kolumn działają zawsze, niezależnie od typu danych zawartych we wierszu.

Kolumna DATA1 jest zindeksowana. Dostawca kontaktów zawsze używa tej kolumny do danych, które według niego będą najczęściej docelowymi elementami zapytania. Na przykład w wierszu e-maila ta kolumna zawiera rzeczywisty adres e-mail.

Zgodnie z konwencją kolumna DATA15 jest zarezerwowana na przechowywanie danych typu Binary Large Object (BLOB), takich jak miniatury zdjęć.

Nazwy kolumn związane z konkretnymi typami

Aby ułatwić pracę z kolumnami w przypadku danego typu wiersza, usługa Contacts Provider udostępnia też nazwy kolumn zależne od typu, zdefiniowane w podklasach klasy ContactsContract.CommonDataKinds. Stała ta po prostu nadaje inną nazwę stałej tej samej nazwie kolumny, co ułatwia dostęp do danych w wierszu określonego typu.

Na przykład klasa ContactsContract.CommonDataKinds.Email definiuje stałe nazw kolumn dla wiersza ContactsContract.Data o typie MIME Email.CONTENT_ITEM_TYPE. Klasa zawiera stałą wartość ADDRESS w kolumnie adresu e-mail. Rzeczywista wartość parametru ADDRESS to „data1”, która jest taka sama jak ogólna nazwa kolumny.

Uwaga: nie dodawaj własnych danych niestandardowych do tabeli ContactsContract.Data, używając wiersza z jednym z wstępnie zdefiniowanych typów MIME dostawcy. Może to spowodować utratę danych lub nieprawidłowe działanie dostawcy. Nie należy na przykład dodawać wiersza z typem MIME Email.CONTENT_ITEM_TYPE, który zawiera nazwę użytkownika zamiast adresu e-mail w kolumnie DATA1. Jeśli w przypadku wiersza używasz własnego niestandardowego typu MIME, możesz dowolnie definiować nazwy kolumn dla tego typu i z nich korzystać.

Rysunek 2 pokazuje, jak kolumny opisowe i kolumny danych wyglądają w wierszu ContactsContract.Data oraz jak nazwy kolumn dla poszczególnych typów „nakładają się” na nazwy ogólne kolumn.

Jak nazwy kolumn dla poszczególnych typów są mapowane na ogólne nazwy kolumn

Rysunek 2. Nazwy kolumn typowe dla danego typu i ogólne nazwy kolumn.

Klasy nazw kolumn zależne od typu

Tabela 2 zawiera najczęściej używane klasy nazw kolumn o konkretnym typie:

Tabela 2. Klasy nazw kolumn zależne od typu

Klasa mapowania Typ danych Uwagi
ContactsContract.CommonDataKinds.StructuredName Dane nazwy nieprzetworzonego kontaktu powiązanego z tym wierszem danych. Nieprzetworzony kontakt ma tylko 1 z tych wierszy.
ContactsContract.CommonDataKinds.Photo Główne zdjęcie nieprzetworzonego kontaktu powiązanego z tym wierszem danych. Nieprzetworzony kontakt ma tylko jeden z tych wierszy.
ContactsContract.CommonDataKinds.Email Adres e-mail nieprzetworzonego kontaktu powiązanego z tym wierszem danych. Nieprzetworzony kontakt może mieć kilka adresów e-mail.
ContactsContract.CommonDataKinds.StructuredPostal Adres pocztowy nieprzetworzonego kontaktu powiązanego z tym wierszem danych. Kontakt w postaci surowych danych może mieć wiele adresów pocztowych.
ContactsContract.CommonDataKinds.GroupMembership Identyfikator, który łączy surowy kontakt z jedną z grup w dostawcy kontaktów. Grupy to opcjonalna funkcja rodzaju i nazwy konta. Więcej informacji o tych grupach znajdziesz w sekcji Grupy kontaktów.

kontakty,

Dostawca kontaktów łączy nieprzetworzone wiersze kontaktów ze wszystkich typów kont i nazw kont, tworząc kontakt. Ułatwia to wyświetlanie i modyfikowanie wszystkich danych zebranych przez użytkownika na temat danej osoby. Dostawca kontaktów zarządza tworzeniem nowych wierszy kontaktów oraz agregacją nieprzetworzonych kontaktów z istniejącym wierszem kontaktu. Ani aplikacje, ani adaptery synchronizacji nie mogą dodawać kontaktów, a niektóre kolumny w wierszu kontaktów są tylko do odczytu.

Uwaga: jeśli spróbujesz dodać kontakt do dostawcy kontaktów z użyciem parametru insert(), otrzymasz wyjątek UnsupportedOperationException. Jeśli spróbujesz zaktualizować kolumnę oznaczoną jako „tylko do odczytu”, aktualizacja zostanie zignorowana.

Dostawca kontaktów tworzy nowy kontakt w odpowiedzi na dodanie nowego nieprzetworzonego kontaktu, który nie pasuje do żadnych dotychczasowych kontaktów. Dostawca robi to również, gdy dane bieżącego kontaktu w postaci nieprzetworzonej ulegną zmianie w taki sposób, że nie będą już odpowiadać danym wcześniej do niego przypisanym. Jeśli aplikacja lub adapter synchronizacji utworzy nowy nieprzetworzony kontakt, który nie pasuje do istniejącego kontaktu, nowy nieprzetworzony kontakt zostanie zsumowany z dotychczasowym kontaktem.

Dostawca danych kontaktów łączy wiersz kontaktu z wierszami nieprzetworzonego kontaktu za pomocą kolumny _ID w tabeli Contacts. Kolumna CONTACT_ID tabeli nieprzetworzonych kontaktówContactsContract.RawContacts zawiera wartości _ID wiersza kontaktów powiązanego z każdym wierszem nieprzetworzonych kontaktów.

Tabela ContactsContract.Contacts zawiera też kolumnę LOOKUP_KEY, która jest trwałym linkiem do wiersza kontaktu. Dostawca kontaktów zarządza kontaktami automatycznie, dlatego może zmienić wartość pola _ID wiersza kontaktu w odpowiedzi na agregację lub synchronizację. Nawet jeśli tak się stanie, URI treściCONTENT_LOOKUP_URI połączony z LOOKUP_KEY kontaktu nadal będzie wskazywał wiersz kontaktu, więc możesz używaćLOOKUP_KEY do utrzymywania linków do „ulubionych” kontaktów itp. Ta kolumna ma własny format, który nie ma nic wspólnego z formatem kolumny _ID.

Rysunek 3 pokazuje relacje między 3 głównymi tabelami.

Główne tabele dostawcy kontaktów

Rysunek 3. Relacje między tabelami Kontakty, Nieprzetworzone kontakty i Szczegóły.

Uwaga: jeśli publikujesz aplikację w Sklepie Google Play lub jeśli Twoja aplikacja jest na urządzeniu z Androidem w wersji 10 (poziom interfejsu API 29) lub nowszej, pamiętaj, że ograniczony zestaw pól danych i metod dotyczących kontaktów jest przestarzały.

W przypadku wymienionych warunków system okresowo usuwa wszystkie wartości zapisane w tych polach danych:

Interfejsy API używane do ustawiania powyższych pól danych również są nieaktualne:

Ponadto te pola nie zwracają już często kontaktowanych osób. Pamiętaj, że niektóre z tych pól wpływają na ranking kontaktów tylko wtedy, gdy kontakty te należą do określonego rodzaju danych.

Jeśli Twoje aplikacje uzyskują dostęp do tych pól lub interfejsów API albo je aktualizują, użyj metod alternatywnych. Na przykład w pewnych przypadkach możesz korzystać z prywatnych dostawców treści lub innych danych przechowywanych w aplikacji lub systemach zaplecza.

Aby sprawdzić, czy ta zmiana nie ma wpływu na działanie aplikacji, możesz ręcznie wyczyścić te pola danych. Aby to zrobić, uruchom to polecenie ADB na urządzeniu z Androidem 4.1 (poziom interfejsu API 16) lub nowszym:

adb shell content delete \
--uri content://com.android.contacts/contacts/delete_usage

Dane z adapterów synchronizacji

Użytkownicy wpisują dane kontaktów bezpośrednio na urządzeniu, ale dane są też przesyłane do dostawcy kontaktów z usług internetowych za pomocą adapterów synchronizacji, które automatyzują przesyłanie danych między urządzeniem a usługami. Adaptery synchronizacji działają w tle pod kontrolą systemu i wywołują metody ContentResolver, aby zarządzać danymi.

W Androidzie usługa internetowa, z którą współpracuje adapter synchronizacji, jest identyfikowana na podstawie typu konta. Każdy adapter synchronizacji działa z jednym typem konta, ale może obsługiwać wiele nazw kont tego typu. Typy kont i ich nazwy są krótko opisane w sekcji Źródła nieprzetworzonych danych kontaktów. Definicje poniżej zawierają więcej szczegółów i omawiają, jak typ i nazwa konta wiążą się z adapterami i usługami synchronizacji.

Rodzaj konta
Identyfikuje usługę, w której użytkownik przechowuje dane. W większości przypadków użytkownik musi uwierzytelnić się w usłudze. Na przykład Kontakty Google to typ konta, który jest identyfikowany za pomocą kodu google.com. Ta wartość odpowiada typowi konta używanego przez AccountManager.
Nazwa konta
Identyfikuje konkretne konto lub login określonego rodzaju konta. Kontakty Google to te same konta co konta Google, które mają adres e-mail jako nazwę konta. Inne usługi mogą używać nazwy użytkownika składającej się z jednego słowa lub identyfikatora numerycznego.

Typy kont nie muszą być unikalne. Użytkownik może skonfigurować wiele kont Kontaktów Google i pobrać ich dane do dostawcy kontaktów. Może się to zdarzyć, jeśli użytkownik ma jeden zestaw kontaktów osobistych na nazwę konta osobistego, a drugi na konto służbowe. Nazwy kont są zazwyczaj unikalne. Razem określają one konkretny przepływ danych między dostawcą kontaktów a usługą zewnętrzną.

Jeśli chcesz przenieść dane usługi do dostawcy kontaktów, musisz napisać własny adapter synchronizacji. Szczegółowe informacje na ten temat znajdziesz w sekcji Adaptery synchronizacji dostawcy kontaktów.

Rysunek 4 przedstawia, jak usługa dostawcy kontaktów wpisuje się w przepływ danych o użytkownikach. W polu „Synchroniz adaptery” każdy adapter jest oznaczony typem konta.

Przepływ danych o osobach

Rysunek 4. Przepływ danych przez dostawcę kontaktów.

Wymagane uprawnienia

Aplikacje, które chcą korzystać z dostawcy kontaktów, muszą o te uprawnienia:

Dostęp z możliwością odczytu do co najmniej 1 tabeli
READ_CONTACTS, określony w AndroidManifest.xml za pomocą elementu <uses-permission> o wartości <uses-permission android:name="android.permission.READ_CONTACTS">.
Dostęp do zapisu w co najmniej 1 tabeli.
WRITE_CONTACTS, określony w AndroidManifest.xml za pomocą elementu <uses-permission> o wartości <uses-permission android:name="android.permission.WRITE_CONTACTS">.

Te uprawnienia nie obejmują danych profilowych użytkownika. Profil użytkownika i wymagane uprawnienia są omawiane w następującej sekcji: Profil użytkownika.

Pamiętaj, że dane kontaktowe użytkownika są danymi osobowymi i poufnymi. Użytkownicy obawiają się o swoją prywatność, dlatego nie chcą, aby aplikacje zbierały dane o nich lub ich kontaktach. Jeśli nie jest jasne, dlaczego potrzebujesz pozwolenia na dostęp do danych swoich kontaktów, może przyznać Twojej aplikacji niskie oceny lub po prostu odmówić jej instalacji.

Profil użytkownika

Tabela ContactsContract.Contacts zawiera 1 wiersz z danymi profilu użytkownika urządzenia. Te dane opisują user urządzenia, a nie kontakt użytkownika. Wiersz kontaktów profilu jest powiązany z wierszem kontaktów w postaci nieprzetworzonych danych w przypadku każdego systemu, który używa profilu. Każdy wiersz danych o nieprzetworzonych kontaktach może zawierać wiele wierszy danych. Stałe wartości służące do uzyskiwania dostępu do profilu użytkownika są dostępne w klasie ContactsContract.Profile.

Dostęp do profilu użytkownika wymaga specjalnych uprawnień. Oprócz uprawnieńREAD_CONTACTSWRITE_CONTACTS potrzebnych do odczytu i zapisu dostęp do profilu użytkownika wymaga uprawnień android.Manifest.permission#READ_PROFILE i android.Manifest.permission#WRITE_PROFILE odpowiednio do odczytu i zapisu.

Pamiętaj, że profil użytkownika należy traktować jako poufny. Uprawnienie android.Manifest.permission#READ_PROFILE umożliwia dostęp do danych użytkownika urządzenia umożliwiających identyfikację tożsamości. W opisie aplikacji poinformuj użytkownika, dlaczego potrzebujesz dostępu do jego profilu.

Aby pobrać wiersz kontaktu z profilem użytkownika, wykonaj wywołanie ContentResolver.query(). Ustaw identyfikator URI treści na CONTENT_URI i nie podawaj żadnych kryteriów wyboru. Możesz też użyć tego identyfikatora URI treści jako podstawowego identyfikatora URI do pobierania nieprzetworzonych kontaktów lub danych do profilu. Na przykład ten fragment umożliwia pobranie danych dla profilu:

Kotlin

// Sets the columns to retrieve for the user profile
projection = arrayOf(
        ContactsContract.Profile._ID,
        ContactsContract.Profile.DISPLAY_NAME_PRIMARY,
        ContactsContract.Profile.LOOKUP_KEY,
        ContactsContract.Profile.PHOTO_THUMBNAIL_URI
)

// Retrieves the profile from the Contacts Provider
profileCursor = contentResolver.query(
        ContactsContract.Profile.CONTENT_URI,
        projection,
        null,
        null,
        null
)

Java

// Sets the columns to retrieve for the user profile
projection = new String[]
    {
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMARY,
        Profile.LOOKUP_KEY,
        Profile.PHOTO_THUMBNAIL_URI
    };

// Retrieves the profile from the Contacts Provider
profileCursor =
        getContentResolver().query(
                Profile.CONTENT_URI,
                projection ,
                null,
                null,
                null);

Uwaga: jeśli pobierasz wiele wierszy kontaktów i chcesz sprawdzić, czy jeden z nich jest profilem użytkownika, przetestuj kolumnę IS_USER_PROFILE wiersza. Jeśli kontakt jest profilem użytkownika, ta kolumna ma wartość „1”.

Metadane dostawcy kontaktów

Dostawca kontaktów zarządza danymi, które śledzą stan danych kontaktów w repozytorium. Te metadane repozytorium są przechowywane w różnych miejscach, takich jak wiersze tabel Nieprzetworzone kontakty, dane i kontakty, tabela ContactsContract.Settings oraz ContactsContract.SyncState. W tabeli poniżej przedstawiono wpływ poszczególnych elementów metadanych:

Tabela 3. Metadane u dostawcy kontaktów

Tabela Kolumna Wartości Znaczenie
ContactsContract.RawContacts DIRTY „0” – nie zmieniono od ostatniej synchronizacji. Oznacza nieprzetworzone kontakty, które zostały zmienione na urządzeniu i muszą zostać zsynchronizowane z serwerem. Wartość jest ustawiana automatycznie przez dostawcę kontaktów, gdy aplikacje na Androida zaktualizują wiersz.

Adaptery synchronizacji, które modyfikują nieprzetworzone kontakty lub tabele danych, powinny zawsze dołączać ciąg CALLER_IS_SYNCADAPTER do używanego przez nie identyfikatora URI treści. Zapobiega to oznaczeniu wierszy przez dostawcę jako nieprawidłowych. W przeciwnym razie modyfikacje adaptera synchronizacji będą wyglądać jak modyfikacje lokalne i będą wysyłane na serwer, nawet jeśli to serwer był źródłem modyfikacji.

„1” – wartość zmieniona od ostatniej synchronizacji, należy zsynchronizować z serwerem.
ContactsContract.RawContacts VERSION Numer wersji danego wiersza. Dostawca kontaktów automatycznie zwiększa tę wartość po każdej zmianie wiersza lub powiązanych z nim danych.
ContactsContract.Data DATA_VERSION Numer wersji tego wiersza. Dostawca kontaktów automatycznie zwiększa tę wartość za każdym razem, gdy zmieni się wiersz danych.
ContactsContract.RawContacts SOURCE_ID Wartość ciągu znaków, która jednoznacznie identyfikuje ten nieprzetworzony kontakt z kontem, na którym został utworzony. Gdy adapter synchronizacji tworzy nowy nieprzetworzony kontakt, w tej kolumnie należy ustawić unikalny identyfikator tego kontaktu serwera. Gdy aplikacja na Androida tworzy nowy kontakt w postaci surowych danych, powinna pozostawić tę kolumnę pustą. To sygnalizuje adapterowi synchronizacji, że powinien utworzyć na serwerze nowy kontakt w postaci danych nieprzetworzonych i uzyskać wartość dla SOURCE_ID.

Identyfikator źródła musi być niepowtarzalny w przypadku każdego typu konta i powinien być stabilny podczas synchronizacji:

  • Unikalny: każdy nieprzetworzony kontakt na koncie musi mieć własny identyfikator źródła. Jeśli tego nie zrobisz, mogą wystąpić problemy z aplikacją Kontakty. Zwróć uwagę, że 2 nieprzetworzone kontakty na tym samym typie konta mogą mieć ten sam identyfikator źródła. Na przykład kontakt „Thomas Higginson” na koncie emily.dickinson@gmail.com może mieć ten sam identyfikator źródłowy co kontakt „Thomas Higginson” na koncie emilyd@gmail.com.
  • Stabilny: identyfikatory źródeł są trwałą częścią danych usługi online dotyczących kontaktu w postaci surowych danych. Jeśli na przykład użytkownik wyczyści pamięć podręczną aplikacji Kontakty w ustawieniach aplikacji i ponownie zsynchronizuje dane, przywrócone dane kontaktów w postaci nieprzetworzonej powinny mieć te same identyfikatory źródeł co wcześniej. Jeśli tego nie zrobisz, skróty przestaną działać.
ContactsContract.Groups GROUP_VISIBLE „0” – kontakty w tej grupie nie powinny być widoczne w interfejsie aplikacji na Androida. Ta kolumna zapewnia zgodność z serwerami, które umożliwiają użytkownikom ukrywanie kontaktów w określonych grupach.
„1” – kontakty z tej grupy mogą być widoczne w interfejsach aplikacji.
ContactsContract.Settings UNGROUPED_VISIBLE „0” – w przypadku tego konta i tego typu konta kontakty, które nie należą do grupy, są niewidoczne w interfejsie aplikacji na Androida. Domyślnie kontakty są niewidoczne, jeśli żaden z ich nie należy do grupy (przynależność do grupy jest wskazywana przez co najmniej 1 wiersz ContactsContract.CommonDataKinds.GroupMembership w tabeli ContactsContract.Data). Ustawienie tej flagi w wierszu tabeli ContactsContract.Settings dla danego typu konta i konta spowoduje, że kontakty bez grup będą widoczne. Jednym z zastosować tej flagi jest wyświetlanie kontaktów z serwerów, które nie używają grup.
„1” – w przypadku tego konta i jego typu kontakty, które nie należą do grupy, są widoczne dla interfejsów aplikacji.
ContactsContract.SyncState (wszystkie) Używaj tej tabeli do przechowywania metadanych adaptera synchronizacji. Dzięki tej tabeli możesz przechowywać stan synchronizacji i inne dane związane z synchronizacją na urządzeniu.

Dostęp do dostawcy kontaktów

W tej sekcji znajdziesz wytyczne dotyczące uzyskiwania dostępu do danych od dostawcy danych kontaktowych. Omówiono w niej te kwestie:

  • Zapytania dotyczące encji.
  • Modyfikacja zbiorcza.
  • wyszukiwanie i modyfikowanie za pomocą intencji;
  • integralność danych;

Więcej informacji o modyfikowaniu za pomocą adaptera synchronizacji znajdziesz w sekcji Adaptery synchronizacji dostawcy kontaktów.

Zapytania o encje

Tabele dostawcy kontaktów są uporządkowane hierarchicznie, dlatego często warto pobrać wiersz i wszystkie połączone z nim wiersze „podrzędne”. Aby na przykład wyświetlić wszystkie informacje o osobie, możesz pobrać wszystkie wiersze ContactsContract.RawContacts dla pojedynczego wiersza ContactsContract.Contacts lub wszystkie wiersze ContactsContract.CommonDataKinds.Email dla pojedynczego wiersza ContactsContract.RawContacts. Aby ułatwić to zadanie, usługa ContactsProvider udostępnia konstrukcje elementu, które działają jak złączenia bazy danych między tabelami.

Entia jest jak tabela utworzona z wybranych kolumn z tabeli nadrzędnej i jej tabeli podrzędnej. Gdy wysyłasz zapytanie dotyczące encji, podajesz projekcję i kryteria wyszukiwania na podstawie kolumn dostępnych w tej encji. W wyniku otrzymujesz tablicę Cursor zawierającą po 1 wierszu na każdy pobrany wiersz tabeli podrzędnej. Jeśli na przykład zapytasz o ContactsContract.Contacts.Entity dla nazwy kontaktu i wszystkich wierszy ContactsContract.CommonDataKinds.Email dla wszystkich kontaktów nieprzetworzonych dla tej nazwy, otrzymasz Cursor zawierający po jednym wierszu dla każdego wiersza ContactsContract.CommonDataKinds.Email.

Elementy upraszczają zapytania. Za pomocą encji możesz pobrać wszystkie dane kontaktów jednocześnie lub nieprzetworzony kontakt bez konieczności wcześniejszego wysyłania zapytania do tabeli nadrzędnej w celu uzyskania identyfikatora, a następnie wysyłania go do tabeli podrzędnej. Poza tym dostawca kontaktów przetwarza zapytanie względem encji w ramach jednej transakcji, co zapewnia wewnętrzną spójność pobieranych danych.

Uwaga: encja zwykle nie zawiera wszystkich kolumn tabeli nadrzędnej i podrzędnej. Jeśli spróbujesz użyć nazwy kolumny, której nie ma na liście stałych nazw kolumny dla danego elementu, otrzymasz Exception.

Poniższy fragment kodu pokazuje, jak pobrać wszystkie nieprzetworzone wiersze kontaktów dla danego kontaktu. Fragment kodu jest częścią większej aplikacji, która zawiera 2 czynności: „główna” i „szczegóły”. Główna aktywność wyświetla listę wierszy kontaktów. Gdy użytkownik wybierze jeden z nich, aktywność wysyła jego identyfikator do aktywności szczegółowej. Aktywność szczegółowa używa parametru ContactsContract.Contacts.Entity do wyświetlania wszystkich wierszy danych ze wszystkich nieprzetworzonych kontaktów powiązanych z wybranym kontaktem.

Ten fragment kodu pochodzi z aktywności „szczegóły”:

Kotlin

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY
    )

    // Initializes the loader identified by LOADER_ID.
    loaderManager.initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this        // The context of the activity
    )

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = SimpleCursorAdapter(
            this,                       // the context of the activity
            R.layout.detail_list_item,  // the view item containing the detail widgets
            mCursor,                    // the backing cursor
            fromColumns,               // the columns in the cursor that provide the data
            toViews,                   // the views in the view item that display the data
            0)                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.adapter = cursorAdapter
...
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    val projection: Array<String> = arrayOf(
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
    )

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    val sortOrder = "${ContactsContract.Contacts.Entity.RAW_CONTACT_ID} ASC"

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return CursorLoader(
            applicationContext, // The activity's context
            contactUri,        // The entity content URI for a single contact
            projection,         // The columns to retrieve
            null,               // Retrieve all the raw contacts and their data rows.
            null,               //
            sortOrder           // Sort by the raw contact ID.
    )
}

Java

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);

    // Initializes the loader identified by LOADER_ID.
    getLoaderManager().initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this);      // The context of the activity

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = new SimpleCursorAdapter(
            this,                        // the context of the activity
            R.layout.detail_list_item,   // the view item containing the detail widgets
            mCursor,                     // the backing cursor
            fromColumns,                // the columns in the cursor that provide the data
            toViews,                    // the views in the view item that display the data
            0);                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.setAdapter(cursorAdapter);
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    String[] projection =
        {
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
        };

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    String sortOrder =
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
            " ASC";

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return new CursorLoader(
            getApplicationContext(),  // The activity's context
            contactUri,              // The entity content URI for a single contact
            projection,               // The columns to retrieve
            null,                     // Retrieve all the raw contacts and their data rows.
            null,                     //
            sortOrder);               // Sort by the raw contact ID.
}

Po zakończeniu wczytywania LoaderManager wywołuje funkcję wywołania zwrotnego onLoadFinished(). Jednym z argumentów tej metody jest obiekt Cursor z wynikami zapytania. W swojej aplikacji możesz pobierać dane z tego pliku Cursor, aby wyświetlać je lub dalej z nimi pracować.

Modyfikacja zbiorcza

W miarę możliwości należy wstawiać, aktualizować i usuwać dane w usługodawcy kontaktów w „trybie zbiorczym”, tworząc tablicę ArrayList obiektów ContentProviderOperation i wywołując funkcję applyBatch(). Ponieważ dostawca kontaktów wykonuje wszystkie operacje w applyBatch() w ramach jednej transakcji, wprowadzone przez Ciebie zmiany nigdy nie opuszczą repozytorium kontaktów w stanie niezgodnym z innymi. Modyfikacja zbiorcza ułatwia też jednoczesne wstawianie nieprzetworzonego kontaktu i jego szczegółowych danych.

Uwaga: aby zmodyfikować pojedynczy kontakt w postaci danych nieprzetworzonych, rozważ wysłanie intencji do aplikacji kontaktów na urządzeniu zamiast modyfikować kontakt w aplikacji. Więcej informacji na ten temat znajdziesz w sekcji Pobieranie i modyfikowanie za pomocą intencji.

Punkty zysku

Modyfikacja zbiorcza zawierająca dużą liczbę operacji może blokować inne procesy, co może powodować ogólnie nieprzyjemne wrażenia użytkowników. Aby zorganizować wszystkie modyfikacje, które chcesz wprowadzić, na jak najmniejszej liczbie osobnych list, a zarazem zapobiec ich blokowaniu przez system, ustaw punkty plonu dla co najmniej 1 operacji. Punkt zysku to obiekt ContentProviderOperation, który ma wartość isYieldAllowed() ustawioną na true. Gdy usługa dostawcy kontaktów napotka punkt zwrotu, wstrzymuje swoją pracę, aby umożliwić działanie innym procesom, i zamyka bieżącą transakcję. Gdy dostawca ponownie się uruchomi, kontynuuje kolejną operację w ArrayList i rozpoczyna nową transakcję.

Punkty rentowności mogą powodować więcej niż 1 transakcję na każde połączenie z applyBatch(). Z tego powodu należy ustawić punkt wypłaty dla ostatniej operacji w przypadku zbioru powiązanych wierszy. Na przykład punkt zwrotu powinieneś ustawić dla ostatniej operacji w zbiorze, która dodaje wiersze z nieprzetworzonymi danymi o kontaktach i powiązane z nimi wiersze danych, lub dla ostatniej operacji w zbiorze wierszy powiązanych z jednym kontaktem.

Punkty uboju są również jednostką atomową. Wszystkie próby dostępu między 2 punktami rentowności będą albo skuteczne, albo nieudane jako pojedyncza jednostka. Jeśli nie ustawisz żadnych punktów zysku, najmniejsza operacja niepodzielna będzie obejmować całą porcję operacji. Jeśli używasz punktów zysku, zapobiegasz pogorszeniu wydajności systemu przez operacje, jednocześnie zapewniając, że podzbiór operacji jest niepodzielny.

Odwołania wsteczne modyfikacji

Podczas wstawiania nowego wiersza nieprzetworzonego kontaktu i powiązanych z nim wierszy danych jako zestawu obiektówContentProviderOperation musisz połączyć wiersze danych z wierszem nieprzetworzonego kontaktu, wstawiając wartość_ID nieprzetworzonego kontaktu jako wartośćRAW_CONTACT_ID. Ta wartość nie jest jednak dostępna, gdy tworzysz kolumnę danych ContentProviderOperation, ponieważ nie została jeszcze zastosowana w kolumnie z nieprzetworzonymi danymi o kontaktach ContentProviderOperation. Aby obejść ten problem, klasa ContentProviderOperation.Builder ma metodę withValueBackReference(). Ta metoda pozwala wstawić lub zmodyfikować kolumnę z wynikiem poprzedniej operacji.

Metoda withValueBackReference() ma 2 argumenty:

key
Klucz w parze klucz-wartość. Wartością tego argumentu powinna być nazwa kolumny w tabeli, którą modyfikujesz.
previousResult
Indeks wartości w tablicy obiektów ContentProviderResult z applyBatch(), liczony od 0. Gdy stosowane są operacje wsadowe, wyniki każdej z nich są przechowywane w pośredniej tablicy wyników. Wartość previousResult to indeks jednego z tych wyników, który jest pobierany i przechowywany w wartości key. Dzięki temu możesz wstawić nowy nieprzetworzony rekord kontaktu i odzyskać jego wartość _ID, a następnie utworzyć „odwołania” do tej wartości podczas dodawania wiersza ContactsContract.Data.

Przy pierwszym wywołaniu funkcji applyBatch() tworzona jest cała tablica wyników o rozmiarze równym rozmiarowi ArrayList podanych przez Ciebie obiektów ContentProviderOperation. Jednak wszystkie elementy w tablicy wyników mają wartość null, a jeśli spróbujesz utworzyć odniesienie wsteczne do wyniku dla operacji, która nie została jeszcze zastosowana, withValueBackReference() zwróci Exception.

W tych fragmentach kodu pokazano, jak w partii wstawiać nowy nieprzetworzony kontakt i dane. Zawierają one kod, który określa punkt zwrotu i używa odwołania wstecznego.

Pierwszy fragment kodu pobiera dane kontaktów z interfejsu. W tym momencie użytkownik wybrał już konto, na którym ma zostać dodany nowy kontakt w postaci danych nieprzetworzonych.

Kotlin

// Creates a contact entry from the current UI values, using the currently-selected account.
private fun createContactEntry() {
    /*
     * Gets values from the UI
     */
    val name = contactNameEditText.text.toString()
    val phone = contactPhoneEditText.text.toString()
    val email = contactEmailEditText.text.toString()

    val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition]

    val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]

Java

// Creates a contact entry from the current UI values, using the currently-selected account.
protected void createContactEntry() {
    /*
     * Gets values from the UI
     */
    String name = contactNameEditText.getText().toString();
    String phone = contactPhoneEditText.getText().toString();
    String email = contactEmailEditText.getText().toString();

    int phoneType = contactPhoneTypes.get(
            contactPhoneTypeSpinner.getSelectedItemPosition());

    int emailType = contactEmailTypes.get(
            contactEmailTypeSpinner.getSelectedItemPosition());

Następny fragment kodu tworzy operację, która wstawia wiersz nieprzetworzonego kontaktu do tabeli ContactsContract.RawContacts:

Kotlin

    /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

    // Creates a new array of ContentProviderOperation objects.
    val ops = arrayListOf<ContentProviderOperation>()

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    var op: ContentProviderOperation.Builder =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

     // Creates a new array of ContentProviderOperation objects.
    ArrayList<ContentProviderOperation> ops =
            new ArrayList<ContentProviderOperation>();

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    ContentProviderOperation.Builder op =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType())
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

Następnie kod tworzy wiersze danych dla nazwy wyświetlanej, numeru telefonu i adresu e-mail.

Każdy obiekt kreatora operacji używa funkcji withValueBackReference() do uzyskiwania wartości RAW_CONTACT_ID. Odwołanie wskazuje obiekt ContentProviderResult z pierwszej operacji, co powoduje dodanie wiersza nieprzetworzonego kontaktu i zwraca nową wartość _ID. W efekcie każdy wiersz danych jest automatycznie połączony przez jego RAW_CONTACT_ID z nowym wierszem ContactsContract.RawContacts, do którego należy.

Obiekt ContentProviderOperation.Builder, który dodaje wiersz e-maila, jest oznaczony flagą withYieldAllowed(), która określa punkt zwrotu:

Kotlin

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified phone number and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified email and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType)

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified phone number and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified email and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

Ostatni fragment kodu pokazuje wywołanie funkcji applyBatch(), które wstawia nowe nieprzetworzone wiersze kontaktów i danych.

Kotlin

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})")
    Log.d(TAG, "Creating contact: $name")

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {
        contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
    } catch (e: Exception) {
        // Display a warning
        val txt: String = getString(R.string.contactCreationFailure)
        Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show()

        // Log exception
        Log.e(TAG, "Exception encountered while inserting contact: $e")
    }
}

Java

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" +
            selectedAccount.getType() + ")");
    Log.d(TAG,"Creating contact: " + name);

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {

            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    } catch (Exception e) {

            // Display a warning
            Context ctx = getApplicationContext();

            CharSequence txt = getString(R.string.contactCreationFailure);
            int duration = Toast.LENGTH_SHORT;
            Toast toast = Toast.makeText(ctx, txt, duration);
            toast.show();

            // Log exception
            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    }
}

Operacje zbiorcze umożliwiają też stosowanie optymizmu kontroli współbieżności, czyli metody stosowania transakcji modyfikacji bez konieczności blokowania repozytorium źródłowego. Aby użyć tej metody, zastosuj transakcję, a potem sprawdź, czy w tym samym czasie nie zostały wprowadzone inne modyfikacje. Jeśli zauważysz niespójną modyfikację, wycofaj transakcję i spróbuj ponownie.

Optymistyczne kontrolowanie równoległości jest przydatne na urządzeniu mobilnym, na którym jest tylko jeden użytkownik naraz i rzadko dochodzi do jednoczesnego dostępu do repozytorium danych. Ponieważ blokowanie nie jest używane, nie traci się czasu na ustawianie blokad ani na oczekiwanie na ich odblokowanie przez inne transakcje.

Aby użyć optymistycznej kontroli równoczesności podczas aktualizowania pojedynczego wiersza ContactsContract.RawContacts, wykonaj te czynności:

  1. Pobierz kolumnę VERSIONkontaktu w postaci nieprzetworzonej wraz z innymi pobieranymi danymi.
  2. Utwórz za pomocą metody newAssertQuery(Uri) obiekt ContentProviderOperation.Builder odpowiedni do egzekwowania ograniczenia. W przypadku identyfikatora URI treści użyj wartości RawContacts.CONTENT_URI z dołączonym do niego nieprzetworzonym identyfikatorem _ID kontaktu.
  3. W przypadku obiektu ContentProviderOperation.Builder wywołaj funkcję withValue(), aby porównać kolumnę VERSION z numerem wersji, który został właśnie pobrany.
  4. W przypadku tego samego elementu ContentProviderOperation.Builder wywołaj funkcję withExpectedCount(), aby mieć pewność, że to uogólnienie testuje tylko jeden wiersz.
  5. Wywołaj funkcję build(), aby utworzyć obiekt ContentProviderOperation, a potem dodaj go jako pierwszy obiekt w strukturze ArrayList, którą przekazujesz do funkcji applyBatch().
  6. Zastosuj transakcję zbiorczą.

Jeśli między odczytaniem wiersza a próbą jego zmodyfikowania zostanie on zaktualizowany przez inną operację, instrukcja „assert” ContentProviderOperationnie powiedzie się, a cała partia operacji zostanie wycofana. Następnie możesz ponowić próbę wykonania wsadu lub wykonać inne działanie.

Poniższy fragment kodu pokazuje, jak utworzyć „assert”ContentProviderOperation po wysłaniu zapytania o pojedynczy nieprzetworzony kontakt za pomocą CursorLoader:

Kotlin

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID))
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION))
}

...

// Sets up a Uri for the assert operation
val rawContactUri: Uri = ContentUris.withAppendedId(
        ContactsContract.RawContacts.CONTENT_URI,
        rawContactID
)

// Creates a builder for the assert operation
val assertOp: ContentProviderOperation.Builder =
        ContentProviderOperation.newAssertQuery(rawContactUri).apply {
            // Adds the assertions to the assert operation: checks the version
            withValue(SyncColumns.VERSION, mVersion)

            // and count of rows tested
            withExpectedCount(1)
        }

// Creates an ArrayList to hold the ContentProviderOperation objects
val ops = arrayListOf<ContentProviderOperation>()

ops.add(assertOp.build())

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try {
    val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops)
} catch (e: OperationApplicationException) {
    // Actions you want to take if the assert operation fails go here
}

Java

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}

...

// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactID);

// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(rawContactUri);

// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);

// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList<ContentProviderOperation>;

ops.add(assertOp.build());

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try
    {
        ContentProviderResult[] results =
                getContentResolver().applyBatch(AUTHORITY, ops);

    } catch (OperationApplicationException e) {

        // Actions you want to take if the assert operation fails go here
    }

Pobieranie i modyfikowanie za pomocą intencji

Wysyłanie intencji do aplikacji kontaktów na urządzeniu umożliwia pośredni dostęp do dostawcy kontaktów. Intencje uruchamiają interfejs aplikacji Kontakty na urządzeniu, w której użytkownicy mogą wykonywać czynności związane z kontaktami. Dzięki temu rodzajowi dostępu użytkownicy mogą:

  • Wybierz kontakt z listy i zwróć go do aplikacji, aby kontynuować pracę.
  • Edytowanie danych istniejącego kontaktu.
  • Wstaw nowy kontakt w postaci tekstu zwykłego na dowolnym koncie.
  • usunąć dane kontaktu lub kontaktów;

Jeśli użytkownik wstawia lub aktualizuje dane, możesz najpierw zebrać te dane i wysłać je jako część intencji.

Jeśli używasz intencji do uzyskiwania dostępu do dostawcy kontaktów za pomocą aplikacji kontaktów na urządzeniu, nie musisz pisać własnego interfejsu ani kodu do uzyskiwania dostępu do dostawcy. Nie musisz też prosić o dostęp do odczytu ani zapisu w przypadku dostawcy. Aplikacja kontaktów na urządzeniu może przekazać Ci uprawnienia do odczytu kontaktu. Ponieważ wprowadzasz zmiany w usługodawcy za pomocą innej aplikacji, nie musisz mieć uprawnień do zapisu.

Ogólny proces wysyłania intencji dostępu do dostawcy został szczegółowo opisany w przewodniku Podstawy dotyczące dostawcy treści w sekcji „Dostęp do danych za pomocą intencji”. Działania, typ MIME i wartości danych używane w ramach dostępnych zadań są opisane w tabeli 4, a wartości dodatkowe, których możesz używać z usługą putExtra(), znajdziesz w dokumentacji ContactsContract.Intents.Insert:

Tabela 4. Intencje dostawcy kontaktów.

Zadanie Działanie Dane Typ MIME Uwagi
Wybieranie kontaktu z listy ACTION_PICK Jedna z tych opcji: Bez lampy Wyświetla listę nieprzetworzonych kontaktów lub listę danych z nieprzetworzonego kontaktu w zależności od podanego typu identyfikatora URI treści.

Wywołanie startActivityForResult(), które zwraca identyfikator URI treści wybranego wiersza. Format identyfikatora URI to identyfikator URI treści tabeli z dołączonym LOOKUP_ID wiersza. Aplikacja Kontakty na urządzeniu deleguje uprawnienia do odczytu i zapisu do tego identyfikatora URI treści na czas trwania aktywności. Więcej informacji znajdziesz w  podstawach dotyczących dostawcy treści.

Wstawianie nowego niesformatowanego kontaktu Insert.ACTION Nie dotyczy RawContacts.CONTENT_TYPE, typ MIME dla zbioru nieprzetworzonych kontaktów. Wyświetla ekran Dodaj kontakt w aplikacji Kontakty na urządzeniu. Wyświetlają się wartości dodatkowych informacji dodane do intencji. Jeśli zostanie wysłany za pomocą startActivityForResult(), identyfikator URI treści nowo dodanego nieprzetworzonego kontaktu będzie przekazywany z powrotem do metody wywołania zwrotnego onActivityResult() aktywności w argumencie Intent w polu „dane”. Aby uzyskać wartość, wywołaj funkcję getData().
Edytowanie kontaktu ACTION_EDIT CONTENT_LOOKUP_URI dla kontaktu. Aktywność edytora pozwoli użytkownikowi edytować dowolne dane powiązane z tym kontaktem. Contacts.CONTENT_ITEM_TYPE, pojedynczy kontakt. Wyświetla ekran Edytuj kontakt w aplikacji Kontakty. Wyświetlane są wartości dodatkowe dodane do intencji. Gdy użytkownik kliknie Gotowe, aby zapisać zmiany, Twoja aktywność powróci na pierwszy plan.
Wyświetl selektor, który może też dodawać dane. ACTION_INSERT_OR_EDIT Nie dotyczy CONTENT_ITEM_TYPE Ta intencja zawsze wyświetla ekran wyboru aplikacji kontaktów. Użytkownik może wybrać kontakt do edycji lub dodać nowy kontakt. W zależności od wyboru użytkownika pojawi się edycja lub ekran dodawania wraz z danymi dodatków przekazanymi w intencji. Jeśli Twoja aplikacja wyświetla dane kontaktowe, takie jak adres e-mail lub numer telefonu, użyj tej opcji, aby umożliwić użytkownikowi dodanie tych danych do istniejącego kontaktu. kontakt,

Uwaga: w dodatkowych informacjach o tej intencji nie trzeba wysyłać wartości nazwy, ponieważ użytkownik zawsze wybiera dotychczasową nazwę lub dodaje nową. Jeśli dodatkowo wyślesz nazwę, a użytkownik zdecyduje się ją zmienić, aplikacja Kontakty wyświetli wysłaną nazwę, zastępując poprzednią wartość. Jeśli użytkownik tego nie zauważy i zapisze zmiany, stara wartość zostanie utracona.

Aplikacja Kontakty na urządzeniu nie pozwala na usunięcie nieprzetworzonego kontaktu ani żadnych jego danych za pomocą intencji. Aby usunąć kontakt w postaci danych nieprzetworzonych, użyj polecenia ContentResolver.delete() lub ContentProviderOperation.newDelete().

Z tego fragmentu kodu dowiesz się, jak tworzyć i wysyłać intencje, które wstawiają nowe dane i nieprzetworzone kontakty:

Kotlin

// Gets values from the UI
val name = contactNameEditText.text.toString()
val phone = contactPhoneEditText.text.toString()
val email = contactEmailEditText.text.toString()

val company = companyName.text.toString()
val jobtitle = jobTitle.text.toString()

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
val contactData = arrayListOf<ContentValues>()

/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
val rawContactRow = ContentValues().apply {
    // Adds the account type and name to the row
    put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type)
    put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name)
}

// Adds the row to the array
contactData.add(rawContactRow)

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
val phoneRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

    // Adds the phone number and its type to the row
    put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
}

// Adds the row to the array
contactData.add(phoneRow)

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
val emailRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

    // Adds the email address and its type to the row
    put(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
}

// Adds the row to the array
contactData.add(emailRow)

// Creates a new intent for sending to the device's contacts application
val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply {
    // Sets the MIME type to the one expected by the insertion activity
    type = ContactsContract.RawContacts.CONTENT_TYPE

    // Sets the new contact name
    putExtra(ContactsContract.Intents.Insert.NAME, name)

    // Sets the new company and job title
    putExtra(ContactsContract.Intents.Insert.COMPANY, company)
    putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle)

    /*
    * Adds the array to the intent's extras. It must be a parcelable object in order to
    * travel between processes. The device's contacts app expects its key to be
    * Intents.Insert.DATA
    */
    putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData)
}

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent)

Java

// Gets values from the UI
String name = contactNameEditText.getText().toString();
String phone = contactPhoneEditText.getText().toString();
String email = contactEmailEditText.getText().toString();

String company = companyName.getText().toString();
String jobtitle = jobTitle.getText().toString();

// Creates a new intent for sending to the device's contacts application
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);

// Sets the MIME type to the one expected by the insertion activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);

// Sets the new contact name
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);

// Sets the new company and job title
insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();


/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
ContentValues rawContactRow = new ContentValues();

// Adds the account type and name to the row
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

// Adds the row to the array
contactData.add(rawContactRow);

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
ContentValues phoneRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
phoneRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
);

// Adds the phone number and its type to the row
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);

// Adds the row to the array
contactData.add(phoneRow);

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
ContentValues emailRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
emailRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
);

// Adds the email address and its type to the row
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);

// Adds the row to the array
contactData.add(emailRow);

/*
 * Adds the array to the intent's extras. It must be a parcelable object in order to
 * travel between processes. The device's contacts app expects its key to be
 * Intents.Insert.DATA
 */
insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent);

integralność danych,

Repozytorium kontaktów zawiera ważne i wrażliwe dane, które użytkownicy oczekują, że będą poprawne i aktualne. Dlatego dostawca kontaktów ma dobrze zdefiniowane zasady dotyczące integralności danych. Ponosisz odpowiedzialność za przestrzeganie tych zasad podczas modyfikowania danych kontaktów. Oto najważniejsze zasady:

Dodaj wiersz ContactsContract.CommonDataKinds.StructuredName do każdego dodanego wiersza ContactsContract.RawContacts.
Wiersz ContactsContract.RawContacts bez wiersza ContactsContract.CommonDataKinds.StructuredName w tabeli ContactsContract.Data może powodować problemy podczas agregacji.
Zawsze łącz nowe wiersze ContactsContract.Data z nadrzędnym wierszem ContactsContract.RawContacts.
Wiersz ContactsContract.Data, który nie jest powiązany z wierszem ContactsContract.RawContacts, nie będzie widoczny w aplikacji Kontakty na urządzeniu i może powodować problemy z adapterami synchronizacji.
Zmieniaj dane tylko w przypadku tych nieprzetworzonych kontaktów, które należą do Ciebie.
Pamiętaj, że dostawca kontaktów zwykle zarządza danymi z kilku różnych typów kont lub usług online. Musisz się upewnić, że aplikacja zmienia lub usuwa tylko dane z wierszy, które należą do Ciebie, i że wstawia dane tylko z użyciem określonego przez Ciebie typu i nazwy konta.
Zawsze używaj stałych zdefiniowanych w ContactsContract i jego podklasach w przypadku źródeł, identyfikatorów URI treści, ścieżek URI, nazw kolumn, typów MIME i wartości TYPE.
Użycie tych stałych pozwala uniknąć błędów. Jeśli któraś z konstant jest wycofana, otrzymasz też ostrzeżenia kompilatora.

Wiersze danych niestandardowych

Po utworzeniu i użyciu własnych typów MIME możesz wstawiać, edytować, usuwać i pobierać własne wiersze danych w tabeli ContactsContract.Data. W wierszach możesz używać tylko kolumn zdefiniowanych w ContactsContract.DataColumns, ale możesz też zmapować własne nazwy kolumn domyślnych. W aplikacji Kontakty na urządzeniu wyświetlane są dane Twoich wierszy, ale nie można ich edytować ani usuwać, a użytkownicy nie mogą dodawać dodatkowych danych. Aby umożliwić użytkownikom modyfikowanie wierszy danych niestandardowych, musisz udostępnić w swojej aplikacji aktywność edytora.

Aby wyświetlić dane niestandardowe, prześlij plik contacts.xml zawierający element <ContactsAccountType> i co najmniej 1 element podrzędny <ContactsDataKind>. Więcej informacji znajdziesz w sekcji <ContactsDataKind> element.

Więcej informacji o niestandardowych typach MIME znajdziesz w przewodniku Tworzenie dostawcy treści.

Adaptery synchronizacji dostawcy kontaktów

Dostawca kontaktów został zaprojektowany specjalnie do obsługi synchronizacji danych kontaktów między urządzeniem a usługą online. Dzięki temu użytkownicy mogą pobrać istniejące dane na nowe urządzenie i przesłać je na nowe konto. Synchronizacja zapewnia też, że użytkownicy mają zawsze pod ręką najnowsze dane, niezależnie od źródła dodatków i zmian. Inną zaletą synchronizacji jest to, że dane kontaktów są dostępne nawet wtedy, gdy urządzenie nie jest połączone z siecią.

Synchronizację można wdrażać na różne sposoby, ale system Android udostępnia framework synchronizacji wtyczek, który automatyzuje te zadania:

  • Sprawdzam dostępność sieci.
  • planowanie i wykonywanie synchronizacji na podstawie preferencji użytkownika.
  • ponowne uruchamianie przerwanych synchronizacji;

Aby korzystać z tej platformy, musisz dostarczyć wtyczkę synchronizowanego adaptera. Każdy adapter synchronizacji jest unikalny dla danego dostawcy usług i treści, ale może obsłużyć wiele nazw kont tej samej usługi. Framework umożliwia też stosowanie wielu adapterów synchronizacji w przypadku tej samej usługi i dostawcy.

Klasy i pliki adaptera synchronizacji

Wdrażasz adapter synchronizacji jako podklasę AbstractThreadedSyncAdapter i instalujesz go jako część aplikacji na Androida. System dowiaduje się o adapterze synchronizacji z elementów w manifeście aplikacji oraz z pliku XML, na który wskazuje manifest. Plik XML definiuje typ konta usługi online i autorytet dostawcy treści, które razem jednoznacznie identyfikują adapter. Adapter synchronizacji nie stanie się aktywny, dopóki użytkownik nie doda konta typu adaptera synchronizacji i nie włączy synchronizacji dla dostawcy treści, z którym synchronizuje się adapter synchronizacji. W tym momencie system rozpoczyna zarządzanie adapterem, wywołując go w razie potrzeby na potrzeby synchronizacji między dostawcą treści a serwerem.

Uwaga: użycie typu konta w ramach identyfikacji adaptera synchronizacji pozwala systemowi wykrywać i grupować adaptery synchronizacji, które uzyskują dostęp do różnych usług w ramach tej samej organizacji. Na przykład adaptery synchronizacji dla usług internetowych Google mają ten sam typ konta com.google. Gdy użytkownicy dodają konto Google na swoich urządzeniach, wszystkie zainstalowane adaptery synchronizacji dla usług Google są wyświetlane razem. Każdy adapter synchronizacji jest wyświetlany z innym dostawcą treści na urządzeniu.

Większość usług wymaga od użytkowników potwierdzenia tożsamości przed dostępem do danych, dlatego system Android oferuje mechanizm uwierzytelniania podobny do mechanizmu synchronizacji i często używany w połączeniu z tym mechanizmem. Platforma uwierzytelniania używa wtyczek uwierzytelniających, które są podklasami klasy AbstractAccountAuthenticator. Authenticator weryfikuje tożsamość użytkownika w następujący sposób:

  1. Zbiera imię i nazwisko użytkownika, hasło lub podobne informacje (dane logowania).
  2. Wysyła dane logowania do usługi.
  3. Sprawdza odpowiedź usługi.

Jeśli usługa akceptuje dane logowania, uwierzytelniacz może je przechowywać na później. Dzięki frameworkowi wtyczki uwierzytelniającej AccountManager może ona udostępniać dostęp do dowolnych tokenów autoryzacji obsługiwanych przez aplikację uwierzytelniającą i wybieranych przez nią do udostępnienia, takich jak tokeny autoryzacji OAuth 2.

Chociaż uwierzytelnianie nie jest wymagane, używa go większość usług do obsługi kontaktów. Jednak do uwierzytelniania nie musisz używać platformy uwierzytelniania Androida.

Implementacja adaptera synchronizacji

Aby wdrożyć adapter synchronizacji dla dostawcy kontaktów, zacznij od utworzenia aplikacji na Androida zawierającej te elementy:

Komponent Service, który odpowiada na żądania systemu dotyczące wiązania z adapterem synchronizacji.
Gdy system chce uruchomić synchronizację, wywołuje metodę onBind() usługi, aby uzyskać obiekt IBinder dla adaptera synchronizacji. Dzięki temu system może wykonywać wywołania metod adaptera w ramach różnych procesów.
Rzeczywisty adapter synchronizacji, zaimplementowany jako konkretny podklas AbstractThreadedSyncAdapter.
Ta klasa pobiera dane z serwera, przesyła dane z urządzenia i rozwiązuje konflikty. Główne zadanie adaptera jest wykonywane w metodzie onPerformSync(). Ta klasa musi być tworzona jako pojedynczy obiekt.
Podklasa klasy Application.
Ta klasa działa jako fabryka dla pojedynczego obiektu synchronizowanego adaptera. Użyj metody onCreate(), aby utworzyć instancję adaptera synchronizacji, i przekaż stałą metodę „gettera”, aby zwrócić obiekt singleton do metody onBind() usługi adaptera synchronizacji.
Opcjonalnie: komponent Service, który odpowiada na żądania z systemu w celu uwierzytelnienia użytkownika.
AccountManager uruchamia tę usługę, aby rozpocząć proces uwierzytelniania. Metoda onCreate() usługi tworzy instancję obiektu authenticator. Gdy system chce uwierzytelnić konto użytkownika dla adaptera synchronizacji aplikacji, wywołuje metodę onBind() usługi, aby uzyskać IBinder dla uwierzytelniania. Dzięki temu system może wykonywać wywołania metod uwierzytelniania w ramach różnych procesów.
Opcjonalne: konkretna podklasa AbstractAccountAuthenticator, która obsługuje żądania uwierzytelniania.
Ta klasa udostępnia metody, które AccountManager wywołuje, aby uwierzytelnić dane logowania użytkownika na serwerze. Szczegóły procesu uwierzytelniania różnią się w zależności od używanej technologii serwera. Więcej informacji o uwierzytelnianiu znajdziesz w dokumentacji serwera.
Pliki XML, które definiują w systemie adapter synchronizacji i uwierzytelniający.
Opisane wcześniej komponenty adaptera synchronizacji i usługi uwierzytelniającej są zdefiniowane w elementach <service> w pliku manifestu aplikacji. Te elementy zawierają elementy podrzędne <meta-data>, które dostarczają systemowi określonych danych:
  • Element <meta-data> usługi synchronizatora adaptera wskazuje plik XML res/xml/syncadapter.xml. Ten plik z kolei określa identyfikator URI usługi internetowej, która będzie synchronizowana z usługą dostawcy kontaktów, oraz typ konta dla tej usługi.
  • Opcjonalnie: element <meta-data> w pliku manifestu uwierzytelniania wskazuje na plik XML res/xml/authenticator.xml. Ten plik z kolei określa typ konta, który jest obsługiwany przez ten Authenticator, a także zasoby interfejsu użytkownika, które pojawiają się podczas procesu uwierzytelniania. Typ konta określony w tym elemencie musi być taki sam jak typ konta określony dla adaptera synchronizacji.

Dane ze strumienia społecznościowego

Tabele android.provider.ContactsContract.StreamItems i android.provider.ContactsContract.StreamItemPhotos zarządzają przychodzącymi danymi z sieci społecznościowych. Możesz napisać adapter synchronizacji, który będzie dodawać do tych tabel dane ze strumienia z Twojej sieci, odczytać z tych tabel dane strumienia i wyświetlić je w swojej aplikacji lub w obu tych miejscach. Dzięki tym funkcjom usługi i aplikacje społecznościowe można zintegrować z obsługą sieci społecznościowych Androida.

Tekst strumienia społecznościowego

Elementy strumienia są zawsze powiązane z surowym kontaktem. Element android.provider.ContactsContract.StreamItemsColumn#RAW_CONTACT_ID stanowi link do wartości _ID nieprzetworzonego kontaktu. Rodzaj i nazwa konta nieprzetworzonego kontaktu są również przechowywane w wierszu elementu strumienia.

Dane ze strumienia możesz zapisać w tych kolumnach:

android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
Wymagany. Rodzaj konta użytkownika nieprzetworzonego kontaktu powiązanego z tym elementem strumienia. Pamiętaj, aby ustawić tę wartość podczas wstawiania elementu strumienia.
android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
Wymagany. Nazwa konta użytkownika dla nieprzetworzonego kontaktu powiązanego z tym elementem strumienia. Pamiętaj, aby ustawić tę wartość podczas wstawiania elementu strumienia.
Kolumny identyfikatorów
Wymagany. Podczas wstawiania elementu strumienia musisz wstawić te kolumny identyfikatorów:
  • android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: wartość android.provider.BaseColumns#_ID kontaktu, z którym jest powiązany ten element strumienia.
  • android.provider.ContactsContract.StreamItemsKolumny#CONTACT_LOOKUP_KEY: wartość android.provider.ContactsContract.ContactsColumn#LOOKUP_KEY, z którą jest powiązany ten element strumienia.
  • android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: wartość właściwości android.provider.BaseColumns#_ID nieprzetworzonego kontaktu, z którym jest powiązany ten element strumienia.
android.provider.ContactsContract.StreamItemsColumns#COMMENTS
Opcjonalnie. Zapisuje podsumowanie informacji, które możesz wyświetlić na początku elementu strumienia.
android.provider.ContactsContract.StreamItemsColumns#TEXT
Tekst elementu strumienia, czyli treści opublikowane przez źródło elementu lub opis działania, które wygenerowało element strumienia. Ta kolumna może zawierać dowolne obrazy formatowania i osadzone zasoby, które mogą być renderowane przez fromHtml(). Dostawca może skrócić lub skrócić długie treści, ale będzie starać się unikać uszkodzenia tagów.
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
Ciąg tekstowy zawierający informacje o czasie wstawienia lub aktualizacji elementu strumienia. Może mieć postać milisekund od początku epoki. Aplikacje, które wstawiają lub aktualizują elementy strumienia, są odpowiedzialne za aktualizowanie tej kolumny. Nie jest ona automatycznie aktualizowana przez dostawcę kontaktów.

Aby wyświetlać informacje identyfikacyjne elementów strumienia, użyj kolumn android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL i android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE, aby połączyć się z zasobami w aplikacji.

Tabela android.provider.ContactsContract.StreamItems zawiera też kolumny android.provider.ContactsContract.StreamItemsColumns#SYNC1–android.provider.ContactsContract.StreamItemsColumns#SYNC4, które są przeznaczone wyłącznie do użytku przez adaptery synchronizacji.

zdjęcia ze strumienia społecznościowego,

Tabela android.provider.ContactsContract.StreamItemPhotos zawiera zdjęcia powiązane z elementem strumienia. Kolumna _ID w tabeli android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID zawiera linki do wartości w kolumnie _ID w tabeli android.provider.ContactsContract.StreamItems. Referencje zdjęć są przechowywane w tabeli w tych kolumnach:

Kolumna android.provider.ContactsContract.StreamItemPhotos#PHOTO (BLOB).
Binarne odwzorowanie zdjęcia, którego rozmiar został zmieniony przez dostawcę w celu przechowywania i wyświetlania. Ta kolumna jest dostępna na potrzeby zgodności wstecznej z wcześniejszymi wersjami dostawcy kontaktów, w którym używano jej do przechowywania zdjęć. W bieżącej wersji nie należy jednak używać tej kolumny do przechowywania zdjęć. Zamiast tego użyj kolumny android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID lub kolumny android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (obie opisane w następnych punktach) do przechowywania zdjęć w pliku. Ta kolumna zawiera teraz miniaturę zdjęcia, którą można odczytać.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
Identyfikator numeryczny zdjęcia nieprzetworzonego kontaktu. Dodaj tę wartość do stałej DisplayPhoto.CONTENT_URI, aby uzyskać identyfikator URI treści wskazujący na pojedynczy plik zdjęcia, a potem wywołaj funkcję openAssetFileDescriptor(), aby uzyskać uchwyt do pliku zdjęcia.
android.provider.ContactsContract.StreamItemPhotosColumn#PHOTO_URI
Identyfikator URI treści wskazujący bezpośrednio plik zdjęcia reprezentowanego w tym wierszu. Aby uzyskać uchwyt pliku zdjęcia, wywołaj funkcję openAssetFileDescriptor() z użyciem tego identyfikatora URI.

Korzystanie z tabel strumienia społecznościowego

Te tabele działają tak samo jak inne główne tabele w dostawcy danych Kontakty, z tą różnicą, że:

  • Te tabele wymagają dodatkowych uprawnień dostępu. Aby odczytać te dane, aplikacja musi mieć uprawnienie android.Manifest.permission#READ_SOCIAL_STREAM. Aby je modyfikować, aplikacja musi mieć uprawnienie android.Manifest.permission#WRITE_SOCIAL_STREAM.
  • W przypadku tabeli android.provider.ContactsContract.StreamItems liczba wierszy przechowywanych dla każdego kontaktu w postaci surowych danych jest ograniczona. Gdy zostanie osiągnięty, usługa Contacts Provider tworzy miejsce na nowe wiersze elementów strumienia, automatycznie usuwając wiersze z najstarszymi wartościami android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP. Aby uzyskać limit, prześlij zapytanie do identyfikatora URI treści android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. Możesz pozostawić wszystkie argumenty oprócz URI treści ustawione na null. Zapytanie zwraca kursor zawierający pojedynczy wiersz z jedną kolumną android.provider.ContactsContract.StreamItems#MAX_ITEMS.

Klasa android.provider.ContactsContract.StreamItems.StreamItemPhotos definiuje podtabelę android.provider.ContactsContract.StreamItemPhotos zawierającą wiersze zdjęć dla pojedynczego elementu strumienia.

Interakcje w strumieniach społecznościowych

Dane strumienia społecznościowego zarządzane przez dostawcę kontaktów w połączeniu z aplikacją kontaktów na urządzeniu stanowią skuteczny sposób na połączenie systemu sieci społecznościowej z dotychczasowymi kontaktami. Dostępne są te funkcje:

  • Synchronizując usługę sieci społecznościowych z dostawcą kontaktów przy użyciu adaptera synchronizacji, możesz pobierać ostatnią aktywność dotyczącą kontaktów użytkownika i zapisywać ją w tabelach android.provider.ContactsContract.StreamItems oraz android.provider.ContactsContract.StreamItemPhotos w celu późniejszego użycia.
  • Oprócz regularnej synchronizacji możesz uruchomić adapter synchronizacji, aby pobrać dodatkowe dane, gdy użytkownik wybierze kontakt do wyświetlenia. Dzięki temu adapter synchronizacji może pobierać zdjęcia w wysokiej rozdzielczości i najnowsze elementy strumienia dla danego kontaktu.
  • Dzięki zarejestrowaniu powiadomienia w aplikacji Kontakty na urządzeniu i u dostawcy kontaktów możesz otrzymać zamiar, gdy kontakt jest wyświetlany, i w tym momencie zaktualizować stan kontaktu z usługi. To rozwiązanie może działać szybciej i zużywać mniejszą przepustowość niż w przypadku pełnej synchronizacji przy użyciu adaptera synchronizacji.
  • Użytkownicy mogą dodać kontakt do usługi sieci społecznościowej, gdy będą patrzeć na niego w aplikacji Kontakty na urządzeniu. Umożliwia to funkcja „zapraszanie kontaktów”, którą włącza się za pomocą kombinacji aktywności dodawania istniejącego kontaktu do sieci oraz pliku XML, który zawiera szczegóły aplikacji kontaktów urządzenia i dostawcy kontaktów.

Regularna synchronizacja elementów strumienia z usługą Contacts Provider przebiega tak samo jak inne synchronizacje. Więcej informacji o synchronizacji znajdziesz w sekcji Adaptery synchronizacji dostawcy kontaktów. Rejestrowanie powiadomień i zapraszanie kontaktów opisano w kolejnych 2 sekcjach.

Rejestrowanie się w celu obsługi wyświetleń w sieciach społecznościowych

Aby zarejestrować adapter synchronizacji i otrzymywać powiadomienia, gdy użytkownik wyświetli kontakt zarządzany przez adapter synchronizacji:

  1. Utwórz plik o nazwie contacts.xml w katalogu res/xml/ projektu. Jeśli masz już ten plik, możesz pominąć ten krok.
  2. W tym pliku dodaj element <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. Jeśli ten element już istnieje, możesz pominąć ten krok.
  3. Aby zarejestrować usługę, która jest powiadamiana, gdy użytkownik otworzy stronę szczegółów kontaktu w aplikacji kontaktów na urządzeniu, dodaj do elementu atrybut viewContactNotifyService="serviceclass", gdzie serviceclass to w pełni kwalifikowana nazwa klasy usługi, która powinna otrzymać intencję z aplikacji kontaktów na urządzeniu. W przypadku usługi powiadamiającej użyj klasy, która rozszerza klasę IntentService, aby umożliwić tej usłudze odbieranie intencji. Dane w przychodzącym intencie zawierają identyfikator URI treści kontaktu nieprzetworzonego, na który użytkownik kliknął. W usłudze powiadomienia możesz powiązać i wywołać adapter synchronizacji, aby zaktualizować dane nieprzetworzonego kontaktu.

Aby zarejestrować aktywność, która ma być wywoływana, gdy użytkownik kliknie element strumienia lub zdjęcie albo oba te elementy:

  1. Utwórz plik o nazwie contacts.xml w katalogu res/xml/ projektu. Jeśli masz już ten plik, możesz pominąć ten krok.
  2. W tym pliku dodaj element <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. Jeśli ten element już istnieje, możesz pominąć ten krok.
  3. Aby zarejestrować jedną ze swoich aktywności w celu obsługi kliknięcia przez użytkownika elementu strumienia w aplikacji kontaktów na urządzeniu, dodaj do elementu atrybut viewStreamItemActivity="activityclass", gdzie activityclass to w pełni kwalifikowana nazwa klasy aktywności, która powinna otrzymać intencję z aplikacji kontaktów na urządzeniu.
  4. Aby zarejestrować jedną ze swoich aktywności w celu obsługi kliknięcia przez użytkownika zdjęcia strumienia w aplikacji kontaktów na urządzeniu, dodaj do elementu atrybut viewStreamItemPhotoActivity="activityclass", gdzie activityclass to w pełni kwalifikowana nazwa klasy aktywności, która powinna otrzymywać intencję z aplikacji kontaktów na urządzeniu.

Element <ContactsAccountType> został szczegółowo opisany w sekcji <ContactsAccountType>.

Przychodzący zamiar zawiera identyfikator URI treści elementu lub zdjęcia, które użytkownik kliknął. Aby mieć osobne działania dla elementów tekstowych i zdjęć, użyj obu atrybutów w tym samym pliku.

Interakcja z siecią społecznościową

Aby zaprosić kontakt do korzystania z Twojej sieci społecznościowej, użytkownicy nie muszą opuszczać aplikacji kontaktów na urządzeniu. Zamiast tego możesz użyć aplikacji Kontakty na urządzeniu, aby wysłała intencję zaproszenia kontaktu do jednej z Twoich aktywności. Aby to skonfigurować:

  1. Utwórz plik o nazwie contacts.xml w katalogu res/xml/ projektu. Jeśli masz już ten plik, możesz pominąć ten krok.
  2. W tym pliku dodaj element <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. Jeśli ten element już istnieje, możesz pominąć ten krok.
  3. Dodaj te atrybuty:
    • inviteContactActivity="activityclass"
    • inviteContactActionLabel="@string/invite_action_label"
    Wartość activityclass to pełna nazwa klasy działania, które ma odbierać intencję. Wartość invite_action_label jest ciągiem tekstowym, który jest wyświetlany w menu Dodaj połączenie w aplikacji kontaktów na urządzeniu.

Uwaga: ContactsSource to wycofana nazwa tagu ContactsAccountType.

Plik contacts.xml

Plik contacts.xml zawiera elementy XML, które kontrolują interakcję adaptera i aplikacji do synchronizacji z aplikacją do obsługi kontaktów i dostawcą kontaktów. Te elementy są opisane w następnych sekcjach.

Element <ContactsAccountType>

Element <ContactsAccountType> kontroluje interakcję aplikacji z aplikacją Kontakty. Ma ona taką składnię:

<ContactsAccountType
        xmlns:android="http://schemas.android.com/apk/res/android"
        inviteContactActivity="activity_name"
        inviteContactActionLabel="invite_command_text"
        viewContactNotifyService="view_notify_service"
        viewGroupActivity="group_view_activity"
        viewGroupActionLabel="group_action_text"
        viewStreamItemActivity="viewstream_activity_name"
        viewStreamItemPhotoActivity="viewphotostream_activity_name">

Zawiera:

res/xml/contacts.xml

może zawierać:

<ContactsDataKind>

Opis:

Deklaruje komponenty i etykiety interfejsu Androida, które umożliwiają użytkownikom zapraszanie kontaktów do sieci społecznościowej, powiadamianie użytkowników o aktualizacjach strumieni sieci społecznościowych itp.

Zwróć uwagę, że w przypadku atrybutów <ContactsAccountType> nie jest wymagany prefiks atrybutu android:.

Atrybuty:

inviteContactActivity
Pełna i jednoznaczna nazwa klasy aktywności w aplikacji, którą chcesz aktywować, gdy użytkownik wybierze Dodaj połączenie w aplikacji kontaktów na urządzeniu.
inviteContactActionLabel
Ciąg tekstowy wyświetlany w menu Dodaj połączenie dla aktywności określonej w inviteContactActivity. Możesz na przykład użyć ciągu znaków „Obserwuj w mojej sieci”. Do tej etykiety możesz użyć identyfikatora zasobu ciągu znaków.
viewContactNotifyService
W pełni kwalifikowana nazwa klasy usługi w aplikacji, która powinna otrzymywać powiadomienia, gdy użytkownik wyświetla kontakt. To powiadomienie jest wysyłane przez aplikację Kontakty na urządzeniu. Umożliwia ono aplikacji odłożenie operacji wymagających dużej ilości danych do momentu, gdy będą potrzebne. Aplikacja może na przykład odczytać i wyświetlić zdjęcie kontaktu w wysokiej rozdzielczości oraz najnowsze elementy strumienia społecznościowego. Więcej informacji o tej funkcji znajdziesz w sekcji Interakcje w strumieniach społecznościowych.
viewGroupActivity
Pełna nazwa klasy aktywności w aplikacji, która może wyświetlać informacje o grupie. Gdy użytkownik kliknie etykietę grupy w aplikacji Kontakty na urządzeniu, wyświetli się interfejs tej czynności.
viewGroupActionLabel
Etykieta wyświetlana przez aplikację Kontakty dla elementu interfejsu użytkownika, który umożliwia użytkownikowi przeglądanie grup w aplikacji.

W przypadku tego atrybutu dozwolony jest identyfikator zasobu ciągu znaków.

viewStreamItemActivity
Pełna i jednoznaczna nazwa klasy działania w aplikacji, które aplikacja kontaktów na urządzeniu uruchamia, gdy użytkownik kliknie nieprzetworzony kontakt w strumieniu.
viewStreamItemPhotoActivity
Pełna nazwa klasy działania w aplikacji, którą uruchamia aplikacja kontaktów na urządzeniu, gdy użytkownik kliknie zdjęcie w elemencie strumienia dla nieprzetworzonego kontaktu.

Element <ContactsDataKind>

Element <ContactsDataKind> kontroluje wyświetlanie wierszy danych niestandardowych aplikacji w interfejsie aplikacji Kontakty. Ma ona taką składnię:

<ContactsDataKind
        android:mimeType="MIMEtype"
        android:icon="icon_resources"
        android:summaryColumn="column_name"
        android:detailColumn="column_name">

Znajduje się w:

<ContactsAccountType>

Opis:

Użyj tego elementu, aby aplikacja Kontakty wyświetlała zawartość niestandardowego wiersza danych jako część szczegółów nieprzetworzonego kontaktu. Każdy element podrzędny <ContactsDataKind> w elementach <ContactsAccountType> odpowiada rodzajowi niestandardowego wiersza danych, który adapter synchronizacji dodaje do tabeli ContactsContract.Data. Dodaj po jednym elemencie <ContactsDataKind> dla każdego używanego niestandardowego typu MIME. Nie musisz dodawać tego elementu, jeśli masz niestandardowy wiersz danych, którego nie chcesz wyświetlać.

Atrybuty:

android:mimeType
Niestandardowy typ MIME zdefiniowany dla jednego z typów niestandardowych wierszy danych w tabeli ContactsContract.Data. Na przykład wartość vnd.android.cursor.item/vnd.example.locationstatus może być niestandardowym typem MIME w wierszu danych, który rejestruje ostatnią znaną lokalizację kontaktu.
android:icon
Plik zasobów graficznych na Androida, który aplikacja Kontakty wyświetla obok Twoich danych. Użyj tego, aby wskazać użytkownikowi, że dane pochodzą z Twojej usługi.
android:summaryColumn
Nazwa kolumny z pierwszą z 2 wartości wyodrębnionych z wiersza danych. Wartość ta jest wyświetlana jako pierwszy wiersz wpisu w przypadku tego wiersza danych. Pierwszy wiersz służy do podsumowania danych, ale nie jest wymagany. Zobacz też android:detailColumn.
android:detailColumn
Nazwa kolumny z drugą z 2 wartości wyodrębnionych z wiersza danych. Wartość jest wyświetlana jako drugi wiersz wpisu w przypadku tego wiersza danych. Zobacz też android:summaryColumn.

Dodatkowe funkcje dostawcy kontaktów

Oprócz głównych funkcji opisanych w poprzednich sekcjach dostawca kontaktów oferuje te przydatne funkcje dotyczące danych kontaktów:

  • Grupy kontaktów
  • Funkcje związane ze zdjęciami

Grupy kontaktów

Dostawca kontaktów może opcjonalnie oznaczać zbiory powiązanych kontaktów za pomocą danych grupy. Jeśli serwer powiązany z kontem użytkownika chce obsługiwać grupy, adapter synchronizacji dla konta typu konta powinien przesyłać dane grup między dostawcą kontaktów a serwerem. Gdy użytkownicy dodają nowego kontaktu na serwerze, a następnie umieszczają go w nowej grupie, adapter synchronizacji musi dodać nową grupę do tabeli ContactsContract.Groups. Grupy, do których należy dany kontakt, są przechowywane w tabeli ContactsContract.Data przy użyciu typu MIME ContactsContract.CommonDataKinds.GroupMembership.

Jeśli projektujesz adapter synchronizacji, który dodaje nieprzetworzone dane kontaktów z serwera do dostawcy kontaktów i nie używasz grup, musisz poprosić dostawcę o udostępnienie danych. W kodzie, który jest wykonywany, gdy użytkownik dodaje konto do urządzenia, zaktualizuj wiersz ContactsContract.Settings, który dostawca kontaktów dodaje do konta. W tym wierszu ustaw wartość kolumny Settings.UNGROUPED_VISIBLE na 1. W takim przypadku dostawca kontaktów będzie zawsze udostępniać dane kontaktów, nawet jeśli nie używasz grup.

Zdjęcia kontaktów

Tabela ContactsContract.Data przechowuje zdjęcia jako wiersze z typem MIME Photo.CONTENT_ITEM_TYPE. Kolumna CONTACT_ID wiersza jest połączona z kolumną _ID nieprzetworzonego kontaktu, do którego należy. Klasa ContactsContract.Contacts.Photo definiuje podtabelę tabeli ContactsContract.Contacts zawierającą informacje o głównym zdjęciu kontaktu, czyli głównym nieprzetworzonym kontakcie. Podobnie klasa ContactsContract.RawContacts.DisplayPhoto definiuje podtabelę ContactsContract.RawContacts, która zawiera informacje o głównym zdjęciu nieprzetworzonego kontaktu.

Dokumentacja referencyjna funkcji ContactsContract.Contacts.Photo i ContactsContract.RawContacts.DisplayPhoto zawiera przykłady pobierania informacji o zdjęciach. Nie ma klasy ułatwiającej pobieranie miniatury głównej kontaktu niesformatowanego, ale możesz wysłać zapytanie do tabeli ContactsContract.Data, wybierając kolumny _ID, Photo.CONTENT_ITEM_TYPE i IS_PRIMARY, aby znaleźć wiersz głównego zdjęcia kontaktu niesformatowanego.

Dane z strumenienia społecznościowego dotyczące danej osoby mogą też zawierać zdjęcia. Są one przechowywane w tabeli android.provider.ContactsContract.StreamItemPhotos, która została szczegółowo opisana w sekcji Zdjęcia w strumieniach społecznościowych.