Dostawca kontaktów to zaawansowany i elastyczny komponent Androida, który służy do zarządzania centralnym repozytorium danych na urządzeniu. Dostawca kontaktów jest źródłem danych, które widzisz w aplikacji do obsługi kontaktów na urządzeniu. Możesz też uzyskiwać dostęp do danych z tego konta w swojej własnej aplikacji oraz przenosić dane między urządzeniem a usługami online. Dostawca obsługuje szeroką gamę źródeł danych i stara się zarządzać jak największą ilością danych o każdej osobie, co sprawia, że organizacja jest złożona. Z tego powodu interfejs API dostawcy ma obszerny zestaw klas umów i interfejsów, które ułatwiają zarówno pobieranie, jak i modyfikowanie danych.
Tematy w tym przewodniku:
- Podstawowa struktura dostawców.
- Jak pobrać dane od dostawcy.
- Jak zmodyfikować dane u dostawcy.
- Jak utworzyć adapter synchronizacji w celu synchronizowania danych między Twoim serwerem a dostawcą kontaktów.
W tym przewodniku zakładamy, że znasz podstawy dostawców treści na Androida. Aby dowiedzieć się więcej o dostawcach treści na Androida, przeczytaj przewodnik – podstawy dostawców treści.
Organizacja dostawcy kontaktów
Dostawca kontaktów to komponent dostawcy treści na Androida. Zawiera 3 rodzaje danych o osobie. Każdy z nich odpowiada tabeli oferowanej przez dostawcę, jak widać na ilustracji 1:
Te trzy tabele są zwykle nazywane nazwami klas umów. Klasy definiują stałe identyfikatory URI treści, nazwy kolumn i wartości kolumn używane przez tabele:
-
ContactsContract.Contacts
tabela - Wiersze reprezentujące różne osoby na podstawie zagregowanych wierszy nieprzetworzonych kontaktów.
-
ContactsContract.RawContacts
tabela - Wiersze zawierające podsumowanie danych użytkownika powiązane z kontem i typem.
-
ContactsContract.Data
tabela - Wiersze zawierające szczegóły nieprzetworzonego kontaktu, takie jak adresy e-mail lub numery telefonów.
Inne tabele reprezentowane przez klasy umów w ContactsContract
są tabelami pomocniczymi, których dostawca kontaktów używa do zarządzania operacjami lub do obsługi określonych funkcji kontaktów lub aplikacji telefonicznych na urządzeniu.
Nieprzetworzone kontakty
Nieprzetworzony kontakt reprezentuje dane osoby pochodzące z jednego typu konta z tą samą nazwą. Dostawca kontaktów dopuszcza więcej niż jedną usługę online jako źródło danych o danej osobie, dlatego dostawca kontaktów zezwala na wiele nieprzetworzonych kontaktów dla tej samej osoby. Wiele nieprzetworzonych kontaktów umożliwia też łączenie danych użytkownika z więcej niż 1 konta tego samego typu.
Większość danych nieprzetworzonego kontaktu nie jest przechowywana w tabeli ContactsContract.RawContacts
. Jest przechowywany w co najmniej 1 wierszu w tabeli ContactsContract.Data
. Każdy wiersz danych ma kolumnę Data.RAW_CONTACT_ID
, w której znajduje się wartość RawContacts._ID
w nadrzędnym wierszu ContactsContract.RawContacts
.
Ważne kolumny nieprzetworzonych kontaktów
Ważne kolumny w tabeli ContactsContract.RawContacts
są wymienione w tabeli 1. Przeczytaj poniższe uwagi:
Nazwa kolumny | Używanie | Uwagi |
---|---|---|
ACCOUNT_NAME
|
Nazwa konta typu konta, które jest źródłem tego nieprzetworzonego kontaktu.
Na przykład nazwa konta Google jest jednym z adresów Gmail należących do właściciela urządzenia. Aby dowiedzieć się więcej, zapoznaj się z następnym wpisem dotyczącym ACCOUNT_TYPE .
|
Format nazwy zależy od rodzaju konta. Nie musi to być adres e-mail. |
ACCOUNT_TYPE
|
Rodzaj konta, który jest źródłem tego nieprzetworzonego kontaktu. Na przykład typ konta Google to com.google . Zawsze kwalifikuj rodzaj swojego konta za pomocą identyfikatora domeny, która należy do Ciebie lub którą kontrolujesz. Dzięki temu rodzaj konta będzie niepowtarzalny.
|
Konto typu, w którym dostępne są dane kontaktów, ma zwykle powiązany adapter synchronizacji, który synchronizuje się z dostawcą kontaktów. |
DELETED
|
Flaga „deleted” dla nieprzetworzonego kontaktu. | Ta flaga pozwala dostawcy kontaktów wewnętrznie przechowywać wiersz, dopóki adaptery synchronizacji nie będą w stanie usunąć go ze swoich serwerów, a na koniec usunąć go z repozytorium. |
Uwagi
Oto ważne uwagi o tabeli ContactsContract.RawContacts
:
-
Nieprzetworzona nazwa kontaktu nie jest przechowywana w odpowiednim wierszu w funkcji
ContactsContract.RawContacts
. Zamiast tego jest przechowywany w tabeliContactsContract.Data
w wierszuContactsContract.CommonDataKinds.StructuredName
. Nieprzetworzony kontakt ma tylko 1 wiersz tego typu w tabeliContactsContract.Data
. -
Uwaga: aby używać danych z własnego konta w wierszu nieprzetworzonych danych kontaktowych, musisz je najpierw zarejestrować w
AccountManager
. Aby to zrobić, poproś użytkowników o dodanie rodzaju i nazwy konta do listy kont. Jeśli tego nie zrobisz, dostawca kontaktów automatycznie usunie nieprzetworzony wiersz kontaktów.Jeśli na przykład chcesz, aby aplikacja przechowujeła dane kontaktów Twojej usługi internetowej w domenie
com.example.dataservice
, a konto użytkownika usługi tobecky.sharp@dataservice.example.com
, użytkownik musi najpierw dodać konta „typ” (com.example.dataservice
) i „nazwa” (becky.smart@dataservice.example.com
), zanim aplikacja będzie mogła dodać nieprzetworzone wiersze kontaktów. Możesz wyjaśnić użytkownikowi ten wymóg w dokumentacji lub poprosić użytkownika o dodanie typu i nazwy lub oba te wymagania. Typy i nazwy kont zostały szczegółowo opisane w następnej sekcji.
Źródła nieprzetworzonych danych kontaktów
Aby zrozumieć, jak działają nieprzetworzone kontakty, weźmy pod uwagę użytkownika „Emily Dickinson”, który ma na urządzeniu zdefiniowane 3 konta użytkownika:
emily.dickinson@gmail.com
emilyd@gmail.com
- Konto na Twitterze „belle_of_amherst”
Ten użytkownik włączył Synchronizuj kontakty dla wszystkich 3 tych kont 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 „Thomasa Higginsona”. Później loguje się w Gmailu jako emilyd@gmail.com
i wysyła e-maila do „Thomasa Higginsona”, który automatycznie dodaje go jako kontakt. Obserwuje też konto „colonel_tom” (identyfikator Thomasa Higginsona na Twitterze) na Twitterze.
W wyniku tej pracy dostawca kontaktów tworzy trzy nieprzetworzone kontakty:
-
Nieprzetworzony kontakt użytkownika „Thomas Higginson” powiązany z kontem
emily.dickinson@gmail.com
. Rodzaj konta użytkownika to Google. -
Drugi nieprzetworzony kontakt „Thomas Higginson” powiązany z użytkownikiem
emilyd@gmail.com
. Typ konta użytkownika to także Google. Istnieje drugi nieprzetworzony kontakt, mimo że jego nazwa jest taka sama jak poprzednia, ponieważ osoba ta została dodana do innego konta użytkownika. - Trzeci nieprzetworzony kontakt „Thomas Higginson” powiązany z adresem „belle_of_amherst”. Rodzaj konta użytkownika to Twitter.
Dane
Jak już wspomnieliśmy, dane nieprzetworzonego kontaktu są przechowywane w wierszu ContactsContract.Data
połączonym z wartością _ID
nieprzetworzonego kontaktu. Dzięki temu jeden nieprzetworzony kontakt może mieć wiele wystąpień danych tego samego typu, takich jak adresy e-mail lub numery telefonów. Jeśli na przykład „Thomas Higginson” dla emilyd@gmail.com
(wiersz nieprzetworzonych kontaktów Tomasza Higginsona powiązany z kontem Google emilyd@gmail.com
) ma domowy adres e-mail thigg@gmail.com
i służbowy adres e-mail thomas.higginson@gmail.com
, dostawca kontaktów przechowuje oba wiersze adresów e-mail i łączy je z nieprzetworzonym kontaktem.
Zwróć uwagę, że w tej pojedynczej tabeli przechowywane są różne typy danych. Wyświetlana nazwa, numer telefonu, adres e-mail, adres pocztowy, zdjęcie i szczegóły witryny znajdują się w tabeli ContactsContract.Data
. Aby ułatwić zarządzanie tą funkcją, tabela ContactsContract.Data
zawiera niektóre kolumny z nazwami opisowymi, a inne z nazwami ogólnymi. Zawartość kolumny z nazwą opisową ma to samo znaczenie niezależnie od typu danych w wierszu, a zawartość kolumny z nazwą opisową ma różne znaczenie w zależności od typu danych.
Opisowe nazwy kolumn
Oto kilka przykładów opisowych nazw kolumn:
-
RAW_CONTACT_ID
-
Wartość kolumny
_ID
nieprzetworzonego kontaktu 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ą dostępne na licencji open source i mogą być używane przez dowolną aplikację lub adapter synchronizacji współpracujący z dostawcą kontaktów. -
IS_PRIMARY
- Jeśli ten typ wiersza danych może wystąpić więcej niż raz w przypadku nieprzetworzonego kontaktu, w kolumnie
IS_PRIMARY
będzie wskazywany wiersz danych zawierający dane podstawowe tego typu. Jeśli np. użytkownik przytrzyma i przytrzyma numer telefonu kontaktu i wybierze Ustaw jako domyślny, w wierszuContactsContract.Data
w kolumnieIS_PRIMARY
wartość będzie inna niż zero.
Ogólne nazwy kolumn
Dostępnych jest 15 kolumn ogólnych o nazwach od DATA1
do DATA15
, które są ogólnie dostępne, oraz 4 dodatkowe kolumny ogólne od SYNC1
do SYNC4
, które powinny być używane tylko przez adaptery synchronizacji. Stałe nazwy kolumny ogólnej zawsze działają, niezależnie od typu danych w wierszu.
Kolumna DATA1
została zindeksowana. Dostawca kontaktów zawsze używa tej kolumny w przypadku danych, które według niego będą najczęstszym celem zapytania. Na przykład w wierszu adresu e-mail ta kolumna zawiera rzeczywisty adres e-mail.
Zgodnie z konwencją kolumna DATA15
jest zarezerwowana do przechowywania danych BLOB, takich jak miniatury zdjęć.
Nazwy kolumn zależne od typu
Aby ułatwić pracę z kolumnami określonego typu wiersza, dostawca kontaktów udostępnia też stałe nazwy kolumn dla danego typu, zdefiniowane w podklasach ContactsContract.CommonDataKinds
. Stałe nadają tej samej nazwie kolumny inną nazwę, co ułatwia dostęp do danych w wierszu określonego typu.
Na przykład klasa ContactsContract.CommonDataKinds.Email
definiuje stałe nazwy kolumny dla konkretnego typu w wierszu ContactsContract.Data
z typem MIME Email.CONTENT_ITEM_TYPE
. Klasa zawiera stałą ADDRESS
w kolumnie adresu e-mail. Rzeczywista wartość kolumny ADDRESS
to „dane1”, która jest taka sama jak ogólna nazwa kolumny.
Uwaga: nie dodawaj własnych danych niestandardowych do tabeli ContactsContract.Data
za pomocą wiersza, który zawiera jeden ze wstępnie zdefiniowanych typów MIME dostawcy. Jeśli to zrobisz, możesz utracić dane lub spowodować nieprawidłowe działanie dostawcy. Na przykład nie dodawaj wiersza z typem MIME Email.CONTENT_ITEM_TYPE
, który zawiera nazwę użytkownika zamiast adresu e-mail w kolumnie DATA1
. Jeśli używasz w wierszu własnego niestandardowego typu MIME, możesz definiować własne nazwy kolumn z konkretnymi typami i używać ich w dowolny sposób.
Rysunek 2 pokazuje, jak opisowe kolumny i kolumny danych są wyświetlane w wierszu ContactsContract.Data
oraz jak nazwy kolumn „nakładki” o określonym typie nakładają się na ogólne nazwy kolumn.
Klasy nazw kolumn dla danego typu
W tabeli 2 znajdziesz najczęściej używane klasy nazw kolumn z określonymi typami:
Klasa mapowania | Typ danych | Uwagi |
---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
Dane dotyczące imienia i nazwiska nieprzetworzonego kontaktu powiązanego z tym wierszem danych. | Nieprzetworzony kontakt ma tylko jeden 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. | Kontakt nieprzetworzony może mieć kilka adresów e-mail. |
ContactsContract.CommonDataKinds.StructuredPostal |
Adres pocztowy nieprzetworzonego kontaktu powiązanego z tym wierszem danych. | Kontakt nieprzetworzony może mieć kilka adresów pocztowych. |
ContactsContract.CommonDataKinds.GroupMembership |
Identyfikator, który łączy nieprzetworzony kontakt z jedną z grup u dostawcy kontaktów. | Grupy są opcjonalną funkcją typu i nazwy konta. Szczegółowo opisujemy je w sekcji Grupy kontaktów. |
Kontakty
Dostawca kontaktów łączy nieprzetworzone wiersze kontaktów ze wszystkich typów kont i nazw kont, aby utworzyć kontakt. Ułatwia to wyświetlanie i modyfikowanie wszystkich danych zebranych przez użytkownika. Dostawca kontaktów zarządza tworzeniem nowych wierszy kontaktów i agregowaniem nieprzetworzonych kontaktów z istniejącym wierszem kontaktów. 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 insert()
, otrzymasz wyjątek UnsupportedOperationException
. Jeśli spróbujesz zaktualizować kolumnę wymienioną 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 żadnego istniejącego kontaktu. Dostawca robi to również wtedy, gdy dane istniejącego nieprzetworzonego kontaktu zmienią się w taki sposób, że nie będzie on już zgodny z kontaktem, do którego został wcześniej dołączony. Jeśli aplikacja lub adapter synchronizacji utworzy nowy nieprzetworzony kontakt pasujący do istniejącego kontaktu, ten nieprzetworzony kontakt zostanie zagregowany z istniejącym kontaktem.
Dostawca kontaktów łączy wiersz kontaktu ze swoimi nieprzetworzonymi wierszami kontaktów z kolumną _ID
wiersza kontaktu w tabeli Contacts
. Kolumna CONTACT_ID
w tabeli nieprzetworzonych kontaktów ContactsContract.RawContacts
zawiera wartości _ID
w wierszu kontaktów powiązanym z każdym nieprzetworzonym wierszem kontaktów.
Tabela ContactsContract.Contacts
zawiera też kolumnę LOOKUP_KEY
, która jest stałym linkiem do wiersza kontaktu. Dostawca kontaktów przechowuje kontakty automatycznie, dlatego może zmienić wartość _ID
wiersza kontaktu w odpowiedzi na agregację lub synchronizację. Nawet jeśli tak się stanie, identyfikator URI treści CONTENT_LOOKUP_URI
w połączeniu z numerem LOOKUP_KEY
kontaktu nadal będzie wskazywać na wiersz kontaktu, więc możesz używać LOOKUP_KEY
do utrzymywania linków do kontaktów „ulubionych” itp. Ta kolumna ma własny format, który nie jest związany z formatem kolumny _ID
.
Rysunek 3 przedstawia, jak są ze sobą powiązane 3 główne tabele.
Uwaga: jeśli publikujesz aplikację w Sklepie Google Play lub działa ona na urządzeniu z Androidem 10 (poziom interfejsu API 29) lub nowszym, pamiętaj, że ograniczony zestaw pól i metod danych kontaktów jest nieaktualny.
Zgodnie z wymienionymi warunkami system okresowo czyści wszystkie wartości zapisane w tych polach danych:
-
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED
-
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED
-
ContactsContract.DataUsageStatColumns.LAST_TIME_USED
-
ContactsContract.DataUsageStatColumns.TIMES_USED
Interfejsy API używane do ustawiania powyższych pól danych również są nieaktualne:
Poniższe pola nie zwracają już częstych kontaktów. Pamiętaj, że niektóre z tych pól wpływają na ranking kontaktów tylko wtedy, gdy kontakty należą do określonego rodzaju danych.
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI
-
CONTENT_FILTER_URI
(dotyczy tylko rodzajów danych E-mail, Telefon, Dostępne połączenia i Elementy kontaktowe) -
ENTERPRISE_CONTENT_FILTER_URI
(dotyczy tylko danych typu E-mail, Telefon i Dostępne rodzaje danych)
Jeśli Twoje aplikacje uzyskują dostęp do tych pól lub interfejsów API albo je aktualizują, użyj alternatywnych metod. Możesz na przykład w określonych przypadkach użycia korzystać z dostawców treści prywatnych lub innych danych przechowywanych w aplikacji bądź systemach backendu.
Aby sprawdzić, czy zmiana nie wpłynie na funkcje Twojej aplikacji, możesz ręcznie wyczyścić te pola danych. Aby to zrobić, uruchom poniższe 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 są one też przesyłane do dostawcy kontaktów z usług internetowych przez adaptery 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.
Na Androidzie usługa internetowa, z którą działa adapter synchronizacji, jest określana przez typ konta. Każdy adapter synchronizacji działa z jednym typem konta, ale może obsługiwać wiele nazw kont dla tego typu. Typy kont i ich nazwy są pokrótce opisane w sekcji Źródła nieprzetworzonych danych kontaktów. Poniższe definicje zawierają więcej szczegółów i opisują, w jaki sposób typ i nazwa konta są powiązane z adapterami synchronizacji i usługami.
- Rodzaj rachunku
- Identyfikuje usługę, w której użytkownik przechowuje dane. W większości przypadków użytkownik musi się uwierzytelnić w usłudze. Na przykład Kontakty Google to rodzaj konta oznaczony kodem
google.com
. Ta wartość odpowiada rodzajowi konta używanego przez usługęAccountManager
. - nazwę konta.
- Identyfikuje konkretne konto lub login danego typu konta. Konta Kontaktów Google nie różnią się od kont Google, które mają adres e-mail jako nazwę konta. Inne usługi mogą używać jednowyrazowej nazwy użytkownika lub identyfikatora liczbowego.
Rodzaje kont nie muszą być unikalne. Użytkownik może skonfigurować wiele kont Kontaktów Google i pobrać swoje dane do dostawcy kontaktów. Może się tak zdarzyć, jeśli użytkownik ma jeden zestaw kontaktów osobistych na potrzeby nazwy konta osobistego, a drugi zestaw do pracy. Nazwy kont są zwykle niepowtarzalne. Razem określają określony 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ółowo opisaliśmy to w sekcji Adaptery synchronizacji dostawcy kontaktów.
Rysunek 4 pokazuje, w jaki sposób dostawca kontaktów wchodzi w przepływ danych o użytkownikach. W polu „Synchronizuj adaptery” każdy adapter jest oznaczony etykietą według typu konta.
Wymagane uprawnienia
Aplikacje, które chcą uzyskać dostęp do dostawcy kontaktów, muszą prosić o te uprawnienia:
- Uprawnienia do odczytu co najmniej 1 tabeli
-
READ_CONTACTS
, określone wAndroidManifest.xml
z elementem<uses-permission>
jako<uses-permission android:name="android.permission.READ_CONTACTS">
. - Uprawnienia do zapisu w co najmniej 1 tabeli
-
WRITE_CONTACTS
, określone wAndroidManifest.xml
z elementem<uses-permission>
jako<uses-permission android:name="android.permission.WRITE_CONTACTS">
.
Te uprawnienia nie obejmują danych profilowych użytkownika. Profil użytkownika i jego wymagane uprawnienia zostały omówione w następnej sekcji: Profil użytkownika.
Pamiętaj, że dane kontaktowe użytkownika są osobiste i poufne. Użytkownicy martwią się o swoją prywatność, więc nie chcą, aby aplikacje gromadziły dane o nich i ich kontaktach. Jeśli nie jest jasne, dlaczego potrzebujesz uprawnień dostępu do danych kontaktów, aplikacja może przyznać Twojej aplikacji niskie oceny lub po prostu odmówić jej zainstalowania.
Profil użytkownika
Tabela ContactsContract.Contacts
zawiera jeden wiersz z danymi profilu użytkownika urządzenia. Te dane opisują user
na urządzeniu, a nie jeden z kontaktów użytkownika. Wiersz kontaktów w profilu jest połączony z nieprzetworzonym wierszem kontaktów dla każdego systemu, który używa profilu.
Każdy wiersz nieprzetworzonego kontaktu w profilu może zawierać wiele wierszy danych. Stałe umożliwiające dostęp do profilu użytkownika są dostępne w klasie ContactsContract.Profile
.
Dostęp do profilu użytkownika wymaga specjalnych uprawnień. Oprócz uprawnień READ_CONTACTS
i WRITE_CONTACTS
wymaganych do odczytu i zapisu, dostęp do profilu użytkownika wymaga odpowiednio uprawnień android.Manifest.permission#READ_PROFILE i android.Manifest.permission#WRITE_PROFILE 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 umożliwiających identyfikację użytkownika urządzenia. Pamiętaj, aby w opisie aplikacji wyjaśnić użytkownikowi, do czego potrzebujesz uprawnień dostępu do jego profilu.
Aby pobrać wiersz kontaktu zawierający profil użytkownika, wywołaj 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 dla profilu. Na przykład ten fragment kodu pobiera dane związane z profilem:
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 pobierzesz wiele wierszy kontaktów i chcesz ustalić, czy jeden z nich to profil 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 dotyczące repozytorium są przechowywane w różnych miejscach, w tym w wierszach tabel nieprzetworzonych kontaktów, danych i kontaktów, a także w tabelach ContactsContract.Settings
i ContactsContract.SyncState
. Poniższa tabela przedstawia wpływ poszczególnych elementów metadanych:
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ć ponownie zsynchronizowane z serwerem. Wartość jest ustawiana automatycznie przez dostawcę kontaktów, gdy aplikacje na Androida aktualizują wiersz.
Adaptery synchronizacji, które modyfikują tabele nieprzetworzonych danych kontaktowych lub tabeli danych, powinny zawsze dołączać ciąg |
„1” – zmiana od ostatniej synchronizacji wymaga synchronizacji z serwerem. | |||
ContactsContract.RawContacts |
VERSION |
Numer wersji tego 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ść po każdej zmianie wiersza 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 serwera dla nieprzetworzonego kontaktu. Gdy aplikacja na Androida tworzy nowy nieprzetworzony kontakt, ta kolumna powinna pozostać pusta. Informuje to adapter synchronizacji, że powinien utworzyć nowy nieprzetworzony kontakt na serwerze i uzyska wartość dla parametru SOURCE_ID .
W szczególności identyfikator źródła musi być unikalny dla każdego typu konta i powinien być niezmienny podczas synchronizacji:
|
ContactsContract.Groups |
GROUP_VISIBLE |
„0” – kontakty w tej grupie nie powinny być widoczne w interfejsach aplikacji na Androida. | Ta kolumna odnosi się do zgodności z serwerami, które umożliwiają użytkownikowi ukrywanie kontaktów w określonych grupach. |
„1” – kontakty z tej grupy mogą być widoczne w interfejsach aplikacji. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
„0” – przy tym koncie i typie konta kontakty nienależące do grupy są niewidoczne w interfejsach aplikacji na Androida. |
Domyślnie kontakty są niewidoczne, jeśli żaden z ich nieprzetworzonych kontaktów nie należy do grupy (przynależność do grupy nieprzetworzonego kontaktu jest oznaczona co najmniej jednym wierszem ContactsContract.CommonDataKinds.GroupMembership w tabeli ContactsContract.Data ).
Ustawiając tę flagę w wierszu tabeli ContactsContract.Settings dla typu konta i konta, możesz wymusić wyświetlanie kontaktów bez grup.
Jednym z zastosowań tej flagi jest wyświetlanie kontaktów z serwerów, które nie używają grup.
|
„1” – dla tego typu konta i konta kontakty nienależące do grupy są widoczne w interfejsach aplikacji. | |||
ContactsContract.SyncState |
(wszystkie) | Ta tabela służy do przechowywania metadanych adaptera synchronizacji. | Dzięki tej tabeli możesz trwale przechowywać na urządzeniu stan synchronizacji i inne dane związane z synchronizacją. |
Dostęp dostawcy kontaktów
Ta sekcja zawiera wskazówki dotyczące uzyskiwania dostępu do danych dostawcy kontaktów. Obejmują one:
- Zapytania dotyczące encji.
- Zbiorcze modyfikowanie.
- Pobieranie i modyfikowanie z intencjami.
- Integralność danych.
Wprowadzanie zmian za pomocą adaptera synchronizacji zostało również szczegółowo omówione w sekcji Adaptery synchronizacji dostawców kontaktów.
Wysyłanie zapytań dotyczących encji
Tabele dostawców kontaktów mają hierarchię, dlatego często przydaje się możliwość pobrania wiersza i wszystkich połączonych z nim wierszy „podrzędnych”. Aby na przykład wyświetlić wszystkie informacje o osobie, możesz pobrać wszystkie wiersze ContactsContract.RawContacts
z jednego wiersza ContactsContract.Contacts
lub wszystkie wiersze ContactsContract.CommonDataKinds.Email
z jednego wiersza ContactsContract.RawContacts
. Aby to ułatwić, dostawca kontaktów udostępnia konstrukcje entity, które działają jak złączenia bazy danych między tabelami.
Encja jest podobna do tabeli składającej się z kolumn wybranych z tabeli nadrzędnej i jej tabeli podrzędnej.
Wysyłając zapytanie do elementu, podajesz prognozę i kryteria wyszukiwania na podstawie kolumn dostępnych dla tego elementu. Wynik to Cursor
, który zawiera po 1 wierszu na każdy pobrany wiersz tabeli podrzędnej. Jeśli na przykład wyślesz zapytanie ContactsContract.Contacts.Entity
o nazwę kontaktu, a wszystkie wiersze ContactsContract.CommonDataKinds.Email
zawierają wszystkie nieprzetworzone kontakty z tym imieniem i nazwiskiem, otrzymasz Cursor
zawierający po 1 wierszu dla każdego wiersza ContactsContract.CommonDataKinds.Email
.
Elementy upraszczają zapytania. Za pomocą encji możesz jednocześnie pobrać wszystkie dane dotyczące kontaktu lub nieprzetworzonego kontaktu bez konieczności wysyłania zapytania do tabeli nadrzędnej w celu uzyskania identyfikatora, a potem wysyłania z użyciem tego identyfikatora zapytania do tabeli podrzędnej. Ponadto dostawca kontaktów przetwarza zapytanie do elementu w ramach jednej transakcji, co zapewnia spójność pobranych danych.
Uwaga: element zwykle nie zawiera wszystkich kolumn tabeli nadrzędnej i podrzędnej. Jeśli spróbujesz pracować z nazwą kolumny, której nie ma na liście stałych nazw kolumny encji, 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 zawierającej 2 działania: „główne” i „szczegóły”. Główna aktywność zawiera listę wierszy kontaktów. Gdy użytkownik wybierze jeden z nich, aktywność wysyła swój identyfikator do działania szczegółów. W działaniu szczegółów działania używany jest ContactsContract.Contacts.Entity
, aby wyświetlić wszystkie wiersze danych ze wszystkich nieprzetworzonych kontaktów powiązanych z wybranym kontaktem.
Ten fragment 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 wywołanie zwrotne do onLoadFinished()
. Jednym z argumentów przychodzących tej metody jest Cursor
z wynikami zapytania. Dane z tego urządzenia Cursor
możesz pobrać w swojej aplikacji, aby je wyświetlić lub nad nimi pracować.
Modyfikowanie zbiorcze
Jeśli to możliwe, wstawiaj, aktualizuj i usuwaj dane dostawcy kontaktów w „trybie grupowym” przez utworzenie ArrayList
z ContentProviderOperation
obiektów i wywołanie applyBatch()
. Dostawca kontaktów wykonuje wszystkie operacje na obiekcie applyBatch()
w ramach jednej transakcji, dlatego Twoje modyfikacje nigdy nie pozostawiają repozytorium kontaktów w niespójnym stanie. Modyfikacja zbiorcza umożliwia również wstawienie nieprzetworzonego kontaktu i jego szczegółowych danych w tym samym czasie.
Uwaga: jeśli chcesz zmodyfikować pojedynczy nieprzetworzony kontakt, zamiast wprowadzać zmiany w aplikacji, wyślij intencję do aplikacji do obsługi kontaktów na urządzeniu. Szczegółowo opisujemy to w sekcji Pobieranie i modyfikowanie zgodnie z intencjami.
Punkty zysku
Modyfikacja zbiorcza obejmująca dużą liczbę operacji może zablokować inne procesy, co źle wpływa na ogólne wrażenia użytkownika. Aby uporządkować wszystkie modyfikacje, które chcesz wprowadzić, na jak najmniejszej liczbie oddzielnych list i jednocześnie zapobiec blokowaniu przez nie systemu, ustaw punkty zysku dla co najmniej 1 operacji.
Punkt zysku to obiekt ContentProviderOperation
, którego wartość isYieldAllowed()
jest ustawiona na true
. Gdy dostawca kontaktów napotyka punkt zysku, wstrzymuje działanie innych procesów i zamyka bieżącą transakcję. Gdy dostawca uruchomi się ponownie, kontynuuje następną operację w ArrayList
i rozpoczyna nową transakcję.
Punkty zysku prowadzą do więcej niż 1 transakcji na wywołanie applyBatch()
. Z tego powodu należy ustawić punkt zysku dla ostatniej operacji na zbiorze powiązanych wierszy.
Na przykład ustaw punkt zysku dla ostatniej operacji w zbiorze, który dodaje wiersze nieprzetworzone kontaktów i powiązane z nimi wiersze danych, lub ostatnią operację dla zbioru wierszy powiązanych z jednym kontaktem.
Punkty zysku są również jednostką działania niepodzielnego. Wszystkie połączenia między 2 punktami zysku będą skuteczne lub nieskuteczne jako pojedyncza jednostka. Jeśli nie ustawisz żadnych punktów zysku, najmniejsza niepodzielna operacja to cała grupa operacji. Jeśli użyjesz punktów zysku, zapobiegniesz obniżeniu wydajności systemu przez operacje, a jednocześnie zapewnisz, że podzbiór operacji jest niepodzielny.
Wsteczne odniesienia do modyfikacji
Gdy wstawiasz nowy wiersz nieprzetworzonego kontaktu i powiązane z nim wiersze danych jako zbiór obiektów ContentProviderOperation
, musisz połączyć wiersze danych z wierszem nieprzetworzonego kontaktu, wstawiając wartość _ID
nieprzetworzonego kontaktu jako wartość RAW_CONTACT_ID
. Ta wartość jest jednak niedostępna podczas tworzenia wiersza danych (ContentProviderOperation
), ponieważ w wierszu nieprzetworzonych danych kontaktowych nie został jeszcze zastosowany parametr ContentProviderOperation
. Aby obejść ten problem, klasa ContentProviderOperation.Builder
ma metodę withValueBackReference()
.
Ta metoda umożliwia wstawienie lub zmodyfikowanie kolumny z wynikiem poprzedniej operacji.
Metoda withValueBackReference()
ma 2 argumenty:
-
key
- Klucz w parze klucz-wartość. Wartość tego argumentu powinna być nazwą kolumny w tabeli, którą modyfikujesz.
-
previousResult
- Oparty na 0 indeks wartości w tablicy obiektów
ContentProviderResult
zapplyBatch()
. W trakcie wykonywania operacji wsadowych wynik każdej z nich jest przechowywany w tablicy pośredniej wyników. WartośćpreviousResult
to indeks jednego z tych wyników, który jest pobierany i zapisywany z wartościąkey
. Dzięki temu możesz wstawić nowy nieprzetworzony rekord kontaktu i odzyskać jego wartość_ID
, a następnie utworzyć odwołanie do tej wartości podczas dodawania wierszaContactsContract.Data
.Przy pierwszym wywołaniu
applyBatch()
tworzona jest cała tablica wyników. Jej rozmiar jest równy rozmiarowiArrayList
z podanych przez Ciebie obiektówContentProviderOperation
. Wszystkie elementy tablicy wyników są jednak ustawione nanull
, a jeśli spróbujesz utworzyć odwołanie do wyniku operacji, która nie została jeszcze zastosowana,withValueBackReference()
zwracaException
.
Poniższe fragmenty kodu pokazują, jak wstawić nieprzetworzony kontakt i nowe dane zbiorczo. Zawierają one kod, który określa punkt zysku i korzysta z odwołania wstecznego.
Pierwszy fragment pobiera dane kontaktów z interfejsu użytkownika. W tym momencie użytkownik już wybrał konto, do którego ma zostać dodany nowy nieprzetworzony kontakt.
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 nieprzetworzony kontakt 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 z wierszami wyświetlanej nazwy, numeru telefonu i adresu e-mail.
Każdy obiekt konstruktora operacji używa withValueBackReference()
do pobrania RAW_CONTACT_ID
. Odniesienie wskazuje z powrotem do obiektu ContentProviderResult
z pierwszej operacji, który dodaje nieprzetworzony kontakt i zwraca jego nową wartość _ID
. W związku z tym każdy wiersz danych jest automatycznie połączony przez RAW_CONTACT_ID
z nowym wierszem danych ContactsContract.RawContacts
, do którego należy.
Obiekt ContentProviderOperation.Builder
, który dodaje wiersz e-maila, jest oznaczony flagą withYieldAllowed()
, co ustawia punkt zysku:
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 zawiera wywołanie applyBatch()
, które wstawia nowe nieprzetworzone wiersze kontaktu 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 wsadowe umożliwiają również wdrożenie optymalnej kontroli równoczesności, czyli metody stosowania transakcji modyfikacji bez konieczności blokowania repozytorium bazowego. Aby użyć tej metody, zastosuj transakcję, a potem sprawdź, czy w tym samym czasie nie wprowadzono żadnych innych zmian. Jeśli zauważysz, że nastąpiła niespójna modyfikacja, wycofuj transakcję i spróbuj ją jeszcze raz.
Optymalna kontrola równoczesności jest przydatna w przypadku urządzeń mobilnych, na których w danym momencie jest tylko 1 użytkownik, a jednoczesne korzystanie z repozytorium danych jest rzadkie. Blokady nie są używane, więc nie marnujesz czasu na ustawianie blokad ani czekanie, aż inne transakcje je odblokują.
Aby używać optymistycznej kontroli równoczesności podczas aktualizowania pojedynczego wiersza ContactsContract.RawContacts
, wykonaj te czynności:
-
Pobierz kolumnę
VERSION
nieprzetworzonego kontaktu wraz z innymi pobranymi danymi. -
Utwórz obiekt
ContentProviderOperation.Builder
odpowiedni do egzekwowania ograniczenia, używając metodynewAssertQuery(Uri)
. W przypadku identyfikatora URI treści użyj elementuRawContacts.CONTENT_URI
z dołączonym do niego atrybutem_ID
nieprzetworzonego kontaktu. -
W przypadku obiektu
ContentProviderOperation.Builder
wywołajwithValue()
, aby porównać kolumnęVERSION
z pobranym numerem wersji. -
W przypadku tego samego
ContentProviderOperation.Builder
wywołajwithExpectedCount()
, aby mieć pewność, że tylko 1 wiersz zostanie przetestowany przez to potwierdzenie. -
Wywołaj
build()
, aby utworzyć obiektContentProviderOperation
, a następnie dodaj go jako pierwszy obiekt wArrayList
, który przekazujesz doapplyBatch()
. - Zastosuj transakcję zbiorczą.
Jeśli wiersz z nieprzetworzonym kontaktem zostanie zaktualizowany przez inną operację w okresie od odczytania wiersza do momentu, gdy spróbujesz go zmodyfikować, próba potwierdzenia adresu ContentProviderOperation
zakończy się niepowodzeniem, a cała grupa operacji zostanie cofnięta. Możesz wtedy ponowić próbę przesłania wsadu lub wykonać inne działanie.
Ten fragment kodu pokazuje, jak utworzyć ContentProviderOperation
„assert” po wysłaniu zapytania dotyczącego pojedynczego nieprzetworzonego kontaktu za pomocą metody 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 z intencjami
Wysłanie intencji do aplikacji do obsługi kontaktów na urządzeniu umożliwia pośredni dostęp do dostawcy kontaktów. Intencja uruchamia interfejs aplikacji do obsługi kontaktów na urządzeniu, w którym użytkownicy mogą wykonywać czynności związane z kontaktami. Ten typ dostępu umożliwia użytkownikom:
- Wybierz kontakt z listy i poproś o jego zwrot do aplikacji w celu dalszej pracy.
- Edycja danych istniejącego kontaktu.
- Wstaw nowy nieprzetworzony kontakt dla dowolnego z kont.
- Usuń dane kontaktów lub kontaktów.
Jeśli użytkownik wstawia lub aktualizuje dane, możesz je najpierw zebrać i wysłać w ramach intencji.
Jeśli używasz intencji, aby uzyskać dostęp do dostawcy kontaktów za pomocą aplikacji do obsługi kontaktów na urządzeniu, nie musisz pisać własnego interfejsu ani kodu umożliwiającego dostęp do dostawcy. Nie musisz też prosić dostawcy o uprawnienia do odczytu ani zapisu. Aplikacja do obsługi kontaktów na urządzeniu może przekazać Ci uprawnienia do odczytu kontaktu, a ponieważ wprowadzasz zmiany w dostawcy za pomocą innej aplikacji, nie musisz mieć uprawnień do zapisu.
Ogólny proces wysyłania zamiaru uzyskania dostępu do dostawcy został szczegółowo opisany w przewodniku
Podstawowe informacje o dostawcy treści w sekcji „Dostęp do danych przez intencje”. Podsumowanie czynności, typu MIME i wartości danych używanych w przypadku dostępnych zadań znajdziesz w tabeli 4. Wartości dodatkowe, których możesz używać z funkcją putExtra()
, znajdziesz w dokumentacji referencyjnej ContactsContract.Intents.Insert
:
Zadanie | Działanie | Dane | Typ MIME | Uwagi |
---|---|---|---|---|
Wybieranie kontaktu z listy | ACTION_PICK |
Jedna z tych możliwości:
|
Not used |
Wyświetla listę nieprzetworzonych kontaktów lub listę danych nieprzetworzonych kontaktów w zależności od podanego typu identyfikatora URI treści.
Wywołanie |
Wstawianie nowego nieprzetworzonego 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. Zostaną wyświetlone wartości dodatków, które dodasz do intencji. Jeśli zostanie wysłany za pomocą funkcji startActivityForResult() , identyfikator URI treści nowo dodanego nieprzetworzonego kontaktu jest przekazywany z powrotem do metody wywołania zwrotnego onActivityResult() aktywności w argumencie Intent , w polu „data”. Aby uzyskać wartość, wywołaj getData() .
|
Edytowanie kontaktu | ACTION_EDIT |
CONTENT_LOOKUP_URI dla kontaktu. Działanie edytora umożliwi użytkownikowi edytowanie wszystkich danych powiązanych z tym kontaktem.
|
Contacts.CONTENT_ITEM_TYPE , jeden kontakt. |
Wyświetla ekran Edytuj kontakt w aplikacji do obsługi kontaktów. Wyświetlane są wartości dodatków, które dodasz do intencji. Gdy użytkownik kliknie Gotowe, aby zapisać zmiany, aktywność wróci na pierwszy plan. |
Zawierać selektor, w którym można też dodawać dane. | ACTION_INSERT_OR_EDIT |
Nie dotyczy |
CONTENT_ITEM_TYPE
|
Ta intencja zawsze wyświetla ekran wyboru aplikacji do obsługi kontaktów. Użytkownik może wybrać kontakt do edycji lub dodać nowy. W zależności od wyboru użytkownika pojawi się ekran edycji lub ekran dodawania. Wyświetlane są też dane dodatkowe przekazane w intencji. Jeśli aplikacja wyświetla dane kontaktów, takie jak adres e-mail lub numer telefonu, użyj tego intencji, aby użytkownik mógł dodać te dane do istniejącego kontaktu.
kontakt
Uwaga: nie ma potrzeby wysyłania wartości nazwy w dodatkach intencji, ponieważ użytkownik zawsze wybiera istniejącą nazwę lub dodaje nową. Co więcej, jeśli wyślesz nazwę użytkownika, a użytkownik postanowi edytować, aplikacja do obsługi kontaktów wyświetli nazwę, którą wyślesz, zastępując poprzednią wartość. Jeśli użytkownik nie zauważy tej zmiany i zapisze zmianę, stara wartość zostanie utracona. |
Aplikacja do obsługi kontaktów na urządzeniu nie pozwala na celowe usunięcie nieprzetworzonego kontaktu ani żadnych jego danych. Aby usunąć nieprzetworzony kontakt, użyj właściwości ContentResolver.delete()
lub ContentProviderOperation.newDelete()
.
Ten fragment kodu pokazuje, jak utworzyć i wysłać intencję, która wstawia nowy nieprzetworzony kontakt i dane:
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 według użytkowników powinny być poprawne i aktualne, dlatego dostawca kontaktów ma dobrze zdefiniowane reguły integralności danych. Podczas modyfikowania danych kontaktowych odpowiadasz za przestrzeganie tych reguł. Ważne reguły są wymienione tutaj:
-
Zawsze dodawaj wiersz
ContactsContract.CommonDataKinds.StructuredName
do każdego dodawanego wierszaContactsContract.RawContacts
. - Wiersz
ContactsContract.RawContacts
bez wierszaContactsContract.CommonDataKinds.StructuredName
w tabeliContactsContract.Data
może powodować problemy podczas agregacji. -
Zawsze łącz nowe wiersze
ContactsContract.Data
z nadrzędnym wierszemContactsContract.RawContacts
. - Wiersz
ContactsContract.Data
, który nie jest połączony zContactsContract.RawContacts
, nie będzie widoczny w aplikacji Kontakty na urządzeniu i może to powodować problemy z adapterami synchronizacji. - Zmieniaj dane tylko tych nieprzetworzonych kontaktów, które należą do Ciebie.
- Pamiętaj, że dostawca kontaktów zarządza zwykle danymi z kilku różnych typów kont/usług online. Musisz się upewnić, że Twoja aplikacja modyfikuje lub usuwa dane tylko z wierszy należących do Ciebie i wstawia tylko dane o typie konta i nazwie, które kontrolujesz.
-
Zawsze używaj stałych zdefiniowanych w zadaniu
ContactsContract
i jego podklas na potrzeby urzędów, identyfikatorów URI treści, ścieżek URI, nazw kolumn, typów MIME i wartościTYPE
. - Użycie tych stałych pomaga uniknąć błędów. Jeśli któraś ze stałych zostanie wycofana, otrzymasz też powiadomienie z ostrzeżeniami kompilatora.
Niestandardowe wiersze danych
Tworząc i używając własnych niestandardowych typów MIME, możesz wstawiać, edytować, usuwać i pobierać własne wiersze danych w tabeli ContactsContract.Data
. Wiersze mogą korzystać z kolumny zdefiniowanej w zadaniu ContactsContract.DataColumns
, ale możesz zmapować własne nazwy kolumn specyficzne dla danego typu na domyślne. W aplikacji do obsługi kontaktów na urządzeniu dane wierszy są wyświetlane, ale nie można ich edytować ani usuwać, a użytkownicy nie mogą dodawać kolejnych danych. Aby umożliwić użytkownikom modyfikowanie niestandardowych wierszy danych, musisz udostępnić w swojej aplikacji działanie edytora.
Aby wyświetlić dane niestandardowe, udostępnij plik contacts.xml
zawierający element <ContactsAccountType>
i co najmniej 1 z jego elementów podrzędnych: <ContactsDataKind>
. Szczegółowo opisaliśmy to 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 do obsługi synchronizacji danych kontaktów między urządzeniem a usługą online. Dzięki temu użytkownicy będą mogli pobrać istniejące dane na nowe urządzenie i przesłać je na nowe konto. Synchronizacja zapewnia też, że użytkownicy mają dostęp do najnowszych danych 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ą.
Chociaż synchronizację możesz wdrożyć na różne sposoby, system Android udostępnia platformę synchronizacji wtyczek, która automatyzuje te zadania:
- Sprawdzam dostępność sieci.
- Planowanie i wykonywanie synchronizacji na podstawie preferencji użytkownika.
- ponowne uruchamianie zatrzymanych synchronizacji;
Aby korzystać z tej platformy, musisz dostarczyć wtyczkę adaptera synchronizacji. Każdy adapter synchronizacji jest unikalny dla dostawcy usługi i treści, ale może obsługiwać wiele nazw kont w tej samej usłudze. Platforma umożliwia też użycie wielu adapterów synchronizacji dla tej samej usługi i dostawcy.
Synchronizuj klasy i pliki adaptera
Adaptera synchronizacji wdrażasz jako podklasę klasy AbstractThreadedSyncAdapter
i instalujesz go jako część aplikacji na Androida. System uczy się o adapterze synchronizacji na podstawie elementów w pliku manifestu aplikacji oraz ze specjalnego pliku XML, na który wskazuje ten plik. Plik XML określa typ konta usługi online i uprawnienia dostawcy treści, które razem w unikalny sposób identyfikują adapter. Adapter synchronizacji stanie się aktywny dopiero po dodaniu przez użytkownika konta dla typu konta adaptera synchronizacji i włączenie synchronizacji dla dostawcy treści, z którym synchronizuje się adapter synchronizacji. W tym momencie system zacznie zarządzać adapterem, wywołując go zgodnie z potrzebą do przeprowadzenia synchronizacji między dostawcą treści a serwerem.
Uwaga: użycie typu konta do identyfikacji adaptera synchronizacji umożliwia systemowi wykrywanie i grupowanie adapterów synchronizacji, które uzyskują dostęp do różnych usług w tej samej organizacji. Na przykład wszystkie adaptery synchronizacji usług online 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ą wymienione razem. Każdy wymieniony adapter synchronizacji synchronizuje się z innym dostawcą treści na urządzeniu.
Większość usług wymaga od użytkowników zweryfikowania tożsamości przed uzyskaniem dostępu do danych, dlatego system Android oferuje platformę uwierzytelniania podobną do platformy adaptera synchronizacji i często z nią wykorzystywaną. Platforma uwierzytelniania korzysta z mechanizmów uwierzytelniania wtyczek, które są podklasami AbstractAccountAuthenticator
. Tożsamość użytkownika jest weryfikowana w ten sposób:
- Zbiera nazwę użytkownika, hasło lub podobne informacje (jego dane logowania).
- Wysyła dane logowania do usługi
- Analizuje odpowiedź usługi.
Jeśli usługa zaakceptuje dane logowania, mechanizm uwierzytelniania może je zapisać do późniejszego użycia. Ze względu na platformę uwierzytelniania wtyczek usługa AccountManager
może zapewniać dostęp do wszystkich uwierzytelniania obsługiwanych przez ten mechanizm i decyduje się na ich udostępnienie (np. uwierzytelnianie OAuth2).
Chociaż uwierzytelnianie nie jest wymagane, większość usług do obsługi kontaktów korzysta z niego. Nie musisz jednak korzystać z platformy uwierzytelniania Androida.
Implementacja adaptera synchronizacji
Aby wdrożyć adapter synchronizacji dla dostawcy kontaktów, zacznij od utworzenia aplikacji na Androida, która będzie zawierać:
-
Komponent
Service
, który odpowiada na żądania systemu, aby utworzyć powiązanie z adapterem synchronizacji. - Gdy system chce przeprowadzić synchronizację, wywołuje metodę
onBind()
usługi, aby uzyskaćIBinder
dla adaptera synchronizacji. Dzięki temu system będzie mógł wykonywać wywołania metod adaptera między procesami. -
Rzeczywisty adapter synchronizacji wdrożony jako konkretną podklasę klasy
AbstractThreadedSyncAdapter
. - Ta klasa zajmuje się pobieraniem danych z serwera, przesyłaniem danych z urządzenia i rozwiązywaniem konfliktów. Główną pracę adaptera wykonuje się w metodzie
onPerformSync()
. Wystąpienie tej klasy musi być utworzone jako single. -
Podklasa klasy
Application
. - Ta klasa działa jako fabryka singletonu adaptera synchronizacji. Utwórz instancję adaptera synchronizacji za pomocą metody
onCreate()
i podaj statyczną metodę „getter”, która zwróci singleton do metodyonBind()
usługi adaptera synchronizacji. -
Opcjonalnie: komponent
Service
, który odpowiada na żądania systemu w celu uwierzytelnienia użytkowników. -
AccountManager
uruchamia tę usługę, aby rozpocząć proces uwierzytelniania. MetodaonCreate()
usługi tworzy instancję obiektu uwierzytelniającego. Gdy system chce uwierzytelnić konto użytkownika na potrzeby adaptera synchronizacji aplikacji, wywołuje metodęonBind()
usługi, aby uzyskaćIBinder
dla usługi uwierzytelniającej. Dzięki temu system może wykonywać wywołania między procesami uwierzytelniania. -
Opcjonalnie: konkretna podklasa typu
AbstractAccountAuthenticator
, która obsługuje żądania uwierzytelniania. - Ta klasa udostępnia metody wywoływane przez interfejs
AccountManager
w celu uwierzytelnienia danych 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 oprogramowania serwera. - Pliki XML, które definiują adapter synchronizacji i uwierzytelnianie w systemie.
- Opisane wcześniej komponenty adaptera synchronizacji i usługi uwierzytelniającej są zdefiniowane w elementach
<service>
w pliku manifestu aplikacji. Elementy te zawierają<meta-data>
elementy podrzędne, które przekazują do systemu określone dane:-
Element
<meta-data>
usługi adaptera synchronizacji wskazuje plik XMLres/xml/syncadapter.xml
. Zawiera on z kolei identyfikator URI usługi internetowej, który będzie synchronizowany z dostawcą kontaktów, oraz typ konta dla usługi internetowej. -
Opcjonalne: element
<meta-data>
elementu uwierzytelniającego wskazuje plik XMLres/xml/authenticator.xml
. Z kolei ten plik określa typ konta obsługiwany przez ten mechanizm uwierzytelniania, 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.
-
Element
Dane strumienia danych społecznościowych
Tabele android.provider.ContactsContract.StreamItems i android.provider.ContactsContract.StreamItemPhotos zarządzają danymi przychodzącymi z sieci społecznościowych. Możesz utworzyć adapter synchronizacji, który doda do tych tabel dane strumienia z Twojej sieci, albo odczytać dane strumienia z tych tabel i wyświetlić je w swojej aplikacji lub w obu tych miejscach. Dzięki tym funkcjom Twoje usługi i aplikacje w sieciach społecznościowych można zintegrować z systemami Android.
Tekst strumienia społecznościowego
Elementy strumienia są zawsze powiązane z nieprzetworzonym kontaktem. Strona android.provider.ContactsContract.StreamItemsColumn#RAW_CONTACT_ID prowadzi do wartości _ID
nieprzetworzonego kontaktu. Rodzaj konta i nazwa konta nieprzetworzonego kontaktu również są przechowywane w wierszu elementu strumienia.
Przechowuj dane ze strumienia w tych kolumnach:
- android.provider.ContactsContract.StreamItemsKolumny#ACCOUNT_TYPE
- Wymagane. Rodzaj konta użytkownika dla nieprzetworzonego kontaktu powiązanego z tym elementem strumienia. Pamiętaj, aby ustawić tę wartość przy wstawianiu elementu strumienia.
- android.provider.ContactsContract.StreamItemsKolumny#ACCOUNT_NAME
- Wymagane. Nazwa konta użytkownika dla nieprzetworzonego kontaktu powiązanego z tym elementem strumienia. Pamiętaj, aby ustawić tę wartość przy wstawianiu elementu strumienia.
- Kolumny identyfikatorów
-
Wymagane. Podczas wstawiania elementu strumienia musisz wstawić te kolumny z identyfikatorami:
- android.provider.ContactsContract.StreamItemsColumn#CONTACT_ID: wartość android.provider.BaseColumn#_ID kontaktu, z którym jest powiązany ten element strumienia.
- android.provider.ContactsContract.StreamItemsColumn#CONTACT_LOOKUP_KEY: Wartość android.provider.ContactsContract.ContactsColumn#LOOKUP_KEY kontaktu, z którym jest powiązany ten element strumienia.
- android.provider.ContactsContract.StreamItemsColumn#RAW_CONTACT_ID: wartość android.provider.BaseColumn#_ID nieprzetworzonego kontaktu, z którym jest powiązany ten element strumienia.
- android.provider.ContactsContract.StreamItemsKolumny#COMMENTS
- Opcjonalne. Przechowuje podsumowanie informacji, które możesz wyświetlić na początku elementu strumienia.
- android.provider.ContactsContract.StreamItemsKolumny#TEXT
- Tekst elementu strumienia, treść opublikowana przez źródło elementu lub opis działania, które spowodowało wygenerowanie elementu strumienia. Ta kolumna może zawierać dowolne formatowanie i osadzone obrazy zasobów, które mogą być renderowane przez funkcję
fromHtml()
. Dostawca może skracać lub obcinać długie treści, ale stara się unikać naruszania tagów. - android.provider.ContactsContract.StreamItemsKolumny#TIMESTAMP
- Ciąg tekstowy zawierający czas wstawienia lub aktualizacji elementu strumienia podany w postaci milisekundy od początku epoki. Za obsługę tej kolumny odpowiadają aplikacje, które wstawiają lub aktualizują elementy strumienia. Nie jest ona automatycznie utrzymywana przez dostawcę kontaktów.
Aby wyświetlić dane identyfikacyjne elementów strumienia, użyj funkcji android.provider.ContactsContract.StreamItemsColumn#RES_ICON, android.provider.ContactsContract.StreamItemsColumn#RES_LABEL i android.provider.ContactsContract.StreamItemsColumn#RES_PACKAGE, aby dodać link do zasobów w aplikacji.
Tabela android.provider.ContactsContract.StreamItems zawiera też kolumny od android.provider.ContactsContract.StreamItemsColumn#SYNC1 do android.provider.ContactsContract.StreamItemsColumn#SYNC4, aby umożliwić wyłączne korzystanie z adapterów synchronizacji.
Zdjęcia w strumieniu danych społecznościowych
Tabela android.provider.ContactsContract.StreamItemPhotos zawiera zdjęcia powiązane z elementem strumienia. Kolumna android.provider.ContactsContract.StreamItemPhotosColumn#STREAM_ITEM_ID zawiera linki do wartości w kolumnie _ID
tabeli android.provider.ContactsContract.StreamItems. Odniesienia do zdjęć są przechowywane w tabeli w tych kolumnach:
- Kolumna android.provider.ContactsContract.StreamItemPhotos#PHOTO (obiekt BLOB).
- Plik binarny zdjęcia zmieniony przez dostawcę w celu przechowywania i wyświetlania. Ta kolumna jest dostępna na potrzeby zgodności wstecznej z poprzednimi wersjami dostawcy kontaktów, który używał jej do przechowywania zdjęć. Jednak w bieżącej wersji nie należy używać tej kolumny do przechowywania zdjęć. Zamiast tego do przechowywania zdjęć w pliku używaj funkcji android.provider.ContactsContract.StreamItemPhotosColumn#PHOTO_FILE_ID lub android.provider.ContactsContract.StreamItemPhotosColumn#PHOTO_URI (oba zostały opisane w poniższych punktach). Ta kolumna zawiera teraz miniaturę zdjęcia, którą można przeczytać.
- android.provider.ContactsContract.StreamItemPhotosKolumny#PHOTO_FILE_ID
-
Liczbowy identyfikator zdjęcia nieprzetworzonego kontaktu. Dołącz tę wartość do stałej
DisplayPhoto.CONTENT_URI
, aby uzyskać identyfikator URI treści wskazujący na pojedynczy plik zdjęcia, a następnie wywołajopenAssetFileDescriptor()
, aby uzyskać uchwyt do pliku zdjęcia. - android.provider.ContactsContract.StreamItemPhotosKolumny#PHOTO_uri
- Identyfikator URI treści wskazujący bezpośrednio plik zdjęcia dla zdjęcia reprezentowanego w tym wierszu.
Wywołaj funkcję
openAssetFileDescriptor()
, używając tego identyfikatora URI, aby uzyskać uchwyt dla pliku zdjęcia.
Korzystanie z tabel strumieni społecznościowych
Te tabele działają tak samo jak inne tabele główne dostawcy kontaktów, z wyjątkiem tych:
- Te tabele wymagają dodatkowych uprawnień dostępu. Aby odczytywać z nich dane, aplikacja musi mieć uprawnienie android.Manifest.permission#READ_SOCIAL_STREAM. Aby je modyfikować, Twoja aplikacja musi mieć uprawnienie android.Manifest.permission#WRITE_SOCIAL_STREAM.
-
W tabeli android.provider.ContactsContract.StreamItems liczba wierszy przechowywanych dla każdego nieprzetworzonego kontaktu jest ograniczona. Gdy ten limit zostanie osiągnięty, dostawca kontaktów zwolni miejsce na nowe wiersze elementów strumienia, automatycznie usuwając wiersze o najstarszej kolumnie android.provider.ContactsContract.StreamItemsColumn#TIMESTAMP. Aby uzyskać limit, wyślij zapytanie do identyfikatora URI treści android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_uri. Wszystkie argumenty inne niż identyfikator URI treści możesz pozostawić na
null
. Zapytanie zwraca Kursor zawierający pojedynczy wiersz z pojedynczą 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 ze strumieniem społecznościowym
Dane strumieni społecznościowych zarządzane przez dostawcę kontaktów w połączeniu z aplikacją do obsługi kontaktów na urządzeniu oferują zaawansowane możliwości połączenia systemu sieci społecznościowych z istniejącymi kontaktami. Dostępne są te funkcje:
- Synchronizując usługę sieci społecznościowych z dostawcą kontaktów za pomocą adaptera synchronizacji, możesz pobierać informacje o ostatniej aktywności związanej z kontaktami użytkownika i zapisywać je w tabelach android.provider.ContactsContract.StreamItems i android.provider.ContactsContract.StreamItemPhotos.
- Oprócz regularnej synchronizacji możesz też aktywować adapter synchronizacji w celu pobrania dodatkowych danych, gdy użytkownik wybierze do wyświetlenia kontakt. Dzięki temu adapter synchronizacji będzie mógł pobierać zdjęcia w wysokiej rozdzielczości i najnowsze elementy strumienia danych z danego kontaktu.
- Gdy zarejestrujesz powiadomienie w aplikacji do obsługi kontaktów na urządzeniu i w dostawcy kontaktów, możesz otrzymać intencję, gdy kontakt zostanie wyświetlony, i w tym momencie zaktualizować jego stan z poziomu Twojej usługi. To podejście może być szybsze i wykorzystywać mniejszą przepustowość niż przeprowadzanie pełnej synchronizacji przy użyciu adaptera synchronizacji.
- Użytkownicy mogą dodać kontakt do usługi społecznościowej, przeglądając go w aplikacji Kontakty na urządzeniu. Aby ją włączyć, użyj funkcji „Zaproś kontakt”, którą włącza się przez połączenie działania, które dodaje istniejący kontakt do sieci, oraz pliku XML, który zawiera szczegóły aplikacji dla aplikacji do obsługi kontaktów na urządzeniu i dostawcy kontaktów.
Regularna synchronizacja elementów strumienia z dostawcą kontaktów 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 następnych 2 sekcjach.
Rejestracja w celu obsługi wyświetleń w sieciach społecznościowych
Aby zarejestrować adapter synchronizacji w celu otrzymywania powiadomień, gdy użytkownik wyświetli kontakt zarządzany przez Twój adapter synchronizacji:
-
Utwórz plik o nazwie
contacts.xml
w katalogures/xml/
projektu. Jeśli masz już ten plik, możesz pominąć ten krok. -
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. -
Aby zarejestrować usługę, która jest powiadamiana, gdy użytkownik otworzy stronę z informacjami o kontakcie w aplikacji do obsługi kontaktów na urządzeniu, dodaj do elementu atrybut
viewContactNotifyService="serviceclass"
, gdzieserviceclass
to w pełni kwalifikowana nazwa klasy usługi, która powinna otrzymywać intencję z aplikacji do obsługi kontaktów na urządzeniu. W przypadku usługi powiadomień użyj klasy, która rozszerzaIntentService
, aby umożliwić jej odbieranie intencji. Dane w intencji przychodzącej zawierają identyfikator URI treści nieprzetworzonego kontaktu klikniętego przez użytkownika. W usłudze powiadomień możesz powiązać, a następnie wywołać adapter synchronizacji, aby zaktualizować dane nieprzetworzonego kontaktu.
Aby zarejestrować działanie, które ma być wywoływane, gdy użytkownik kliknie element w strumieniu, zdjęcie lub oba te elementy:
-
Utwórz plik o nazwie
contacts.xml
w katalogures/xml/
projektu. Jeśli masz już ten plik, możesz pominąć ten krok. -
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. -
Aby zarejestrować jedno z działań, które ma obsługiwać kliknięcie elementu strumienia w aplikacji do obsługi kontaktów na urządzeniu, dodaj do elementu atrybut
viewStreamItemActivity="activityclass"
, gdzieactivityclass
jest pełną i jednoznaczną nazwą klasy działania, która powinna otrzymać intencję z aplikacji do obsługi kontaktów na urządzeniu. -
Aby zarejestrować jedną z aktywności, która obsługuje użytkownika klikającego zdjęcie ze strumienia w aplikacji do obsługi kontaktów na urządzeniu, dodaj do elementu atrybut
viewStreamItemPhotoActivity="activityclass"
, gdzieactivityclass
jest pełną i jednoznaczną nazwą klasy działania, która powinna otrzymać intencję z aplikacji do obsługi kontaktów na urządzeniu.
Element <ContactsAccountType>
został szczegółowo opisany w sekcji Element<ContactsAccountType>.
Intencja przychodzący zawiera identyfikator URI treści elementu lub zdjęcia klikniętego przez użytkownika. Aby utworzyć oddzielne działania dla elementów tekstowych i zdjęć, użyj obu atrybutów w tym samym pliku.
Interakcja z serwisem społecznościowym
Użytkownicy nie muszą zamykać aplikacji do obsługi kontaktów na urządzeniu, aby zaprosić kontakt do witryny społecznościowej. Możesz użyć aplikacji do obsługi kontaktów na urządzeniu, która wyśle Ci intencję zaproszenia kontaktu do jednego z Twoich działań. Aby to skonfigurować:
-
Utwórz plik o nazwie
contacts.xml
w katalogures/xml/
projektu. Jeśli masz już ten plik, możesz pominąć ten krok. -
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. -
Dodaj te atrybuty:
inviteContactActivity="activityclass"
-
inviteContactActionLabel="@string/invite_action_label"
activityclass
to w pełni kwalifikowana nazwa klasy działania, które powinno otrzymać intencję. Wartośćinvite_action_label
jest ciągiem tekstowym wyświetlanym w menu Dodaj połączenie w aplikacji kontaktów na urządzeniu.
Uwaga: ContactsSource
to wycofana nazwa tagu ContactsAccountType
.
Dokumentacja contacts.xml
Plik contacts.xml
zawiera elementy XML, które kontrolują interakcję adaptera synchronizacji i aplikacji z aplikacjami do obsługi kontaktów i dostawcą kontaktów. Elementy te zostały opisane w kolejnych sekcjach.
Element <ContactsAccountType>
Element <ContactsAccountType>
kontroluje interakcję aplikacji z aplikacją do obsługi kontaktów. Ma 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">
są zawarte w:
res/xml/contacts.xml
może zawierać:
<ContactsDataKind>
Opis:
Deklaruje komponenty Androida i etykiety interfejsu, które umożliwiają użytkownikom zapraszanie jednego z ich kontaktów do sieci społecznościowej, powiadamianie użytkowników o zaktualizowaniu jednego z ich strumieni w sieciach społecznościowych itd.
Zwróć uwagę, że w przypadku atrybutów <ContactsAccountType>
prefiks atrybutu android:
nie jest wymagany.
Atrybuty:
inviteContactActivity
- Pełna nazwa klasy aktywności w aplikacji, którą chcesz aktywować, gdy użytkownik wybierze Dodaj połączenie w aplikacji do obsługi kontaktów na urządzeniu.
inviteContactActionLabel
- Ciąg tekstowy wyświetlany w przypadku aktywności określonej w polu
inviteContactActivity
w menu Dodaj połączenie. Możesz np. użyć tekstu „Obserwuj w mojej sieci”. Dla tej etykiety możesz użyć identyfikatora zasobu w postaci ciągu znaków. viewContactNotifyService
- Pełna i jednoznaczna nazwa klasy usługi w aplikacji, która powinna otrzymywać powiadomienia, gdy użytkownik wyświetli kontakt. To powiadomienie jest wysyłane przez aplikację do obsługi kontaktów na urządzeniu. Pozwala jej opóźnić operacje wymagające dużej ilości danych do momentu, gdy będą potrzebne. Na przykład aplikacja może zareagować na to powiadomienie, odczytując i wyświetlając zdjęcie kontaktu w wysokiej rozdzielczości oraz najnowsze elementy w strumieniach społecznościowych. Szczegółowo opisujemy tę funkcję w sekcji Interakcje ze strumieniem społecznościowym.
viewGroupActivity
- Pełna nazwa klasy działania w aplikacji, która może wyświetlać informacje o grupach. Gdy użytkownik kliknie etykietę grupy w aplikacji kontaktów na urządzeniu, wyświetli się interfejs tej aktywności.
viewGroupActionLabel
- Etykieta wyświetlana przez aplikację kontaktów na potrzeby elementu sterującego interfejsu, który umożliwia użytkownikowi przeglądanie grup w aplikacji.
Ten atrybut może zawierać identyfikator zasobu w formie ciągu znaków.
viewStreamItemActivity
- Pełna nazwa klasy działania w aplikacji, która jest uruchamiana przez aplikację do obsługi kontaktów na urządzeniu, gdy użytkownik kliknie element strumienia w przypadku nieprzetworzonego kontaktu.
viewStreamItemPhotoActivity
- Pełna nazwa klasy działania w aplikacji, która jest uruchamiana przez aplikację do obsługi kontaktów na urządzeniu, gdy użytkownik kliknie zdjęcie nieprzetworzonego kontaktu w elemencie strumienia.
Element <ContactsDataKind>
Element <ContactsDataKind>
steruje wyświetlaniem w interfejsie aplikacji kontaktów wierszy niestandardowych danych. Ma taką składnię:
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
są zawarte w:
<ContactsAccountType>
Opis:
Użyj tego elementu, aby aplikacja do obsługi kontaktów wyświetlała zawartość niestandardowego wiersza danych jako część szczegółów nieprzetworzonego kontaktu. Każdy element podrzędny <ContactsDataKind>
elementu <ContactsAccountType>
reprezentuje typ 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ć elementu, jeśli masz niestandardowy wiersz danych, w przypadku którego nie chcesz wyświetlać danych.
Atrybuty:
android:mimeType
- Niestandardowy typ MIME zdefiniowany dla jednego z niestandardowych typów wierszy danych w tabeli
ContactsContract.Data
. Na przykład wartośćvnd.android.cursor.item/vnd.example.locationstatus
może być niestandardowym typem MIME wiersza danych, który rejestruje ostatnią znaną lokalizację kontaktu. android:icon
- Zasób rysowalny Androida, który aplikacja kontaktów wyświetla obok danych. Użyj tego atrybutu, aby wskazać użytkownikowi, że dane pochodzą z Twojej usługi.
android:summaryColumn
- Nazwa kolumny dla pierwszej z 2 wartości pobranych z wiersza danych. Wartość jest wyświetlana jako pierwszy wiersz wpisu dla tego wiersza danych. Pierwszy wiersz powinien być używany jako podsumowanie danych, ale jest on opcjonalny. Zobacz też android:detailColumn.
android:detailColumn
- Nazwa kolumny dla drugiej z 2 wartości pobranych z wiersza danych. Wartość jest wyświetlana jako drugi wiersz wpisu dla 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 związane z pracą z danymi kontaktów:
- Grupy kontaktów
- Funkcje zdjęć
Grupy kontaktów
Dostawca kontaktów może opcjonalnie oznaczać kolekcje powiązanych kontaktów etykietami z danymi grupy. Jeśli serwer powiązany z kontem użytkownika chce obsługiwać grupy, adapter synchronizacji dla danego typu konta powinien przenieść dane grup między dostawcą kontaktów a serwerem. Gdy użytkownicy dodają nowy kontakt do serwera, a następnie umieszczają go w nowej grupie, adapter synchronizacji musi dodać tę grupę do tabeli ContactsContract.Groups
. Grupa lub grupy, do których należy nieprzetworzony kontakt, są przechowywane w tabeli ContactsContract.Data
z użyciem typu MIME ContactsContract.CommonDataKinds.GroupMembership
.
Jeśli projektujesz adapter synchronizacji, który doda nieprzetworzone dane kontaktów z serwera do dostawcy kontaktów, a nie używasz grup, musisz poprosić dostawcę o udostępnienie danych. W kodzie, który jest uruchamiany, gdy użytkownik dodaje konto na urządzeniu, zaktualizuj wiersz ContactsContract.Settings
, który dostawca kontaktów dodaje do konta. W tym wierszu ustaw wartość kolumny Settings.UNGROUPED_VISIBLE
na 1. Gdy to zrobisz, dostawca kontaktów zawsze udostępni dane Twoich kontaktów, nawet jeśli nie używasz grup.
Zdjęcia kontaktów
Tabela ContactsContract.Data
zawiera zdjęcia jako wiersze o typie MIME Photo.CONTENT_ITEM_TYPE
. Kolumna CONTACT_ID
w wierszu 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 zdjęciu głównym zdjęcia kontaktu, które jest zdjęciem podstawowego nieprzetworzonego kontaktu kontaktu. Podobnie klasa ContactsContract.RawContacts.DisplayPhoto
określa podtabelę ContactsContract.RawContacts
zawierającą informacje o zdjęciu głównym zdjęcia głównego kontaktu.
Dokumentacja referencyjna ContactsContract.Contacts.Photo
i ContactsContract.RawContacts.DisplayPhoto
zawiera przykłady pobierania informacji o zdjęciach. Nie ma klasy wygodnej do pobierania miniatury głównej dla nieprzetworzonego kontaktu, ale można wysłać zapytanie do tabeli ContactsContract.Data
, wybierając kolumny _ID
nieprzetworzonego kontaktu, Photo.CONTENT_ITEM_TYPE
i IS_PRIMARY
, aby znaleźć podstawowy wiersz zdjęcia nieprzetworzonego kontaktu.
Dane ze strumienia społecznościowego dotyczące użytkownika 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 ze strumienia społecznościowego.