Il provider di contatti è un componente Android potente e flessibile che gestisce il repository centrale di dati sulle persone del dispositivo. Il provider di contatti è l'origine dei dati che vedi nell'applicazione Contatti del dispositivo. Puoi anche accedere ai suoi dati nella tua applicazione e trasferire dati tra il dispositivo e i servizi online. Il fornitore ospita un'ampia gamma di origini dati e cerca di gestire il maggior numero possibile di dati per ogni persona, con il risultato che la sua organizzazione è complessa. Per questo motivo, l'API del fornitore include un ampio insieme di classi e interfacce di contratto che facilitano sia il recupero che la modifica dei dati.
Questa guida descrive quanto segue:
- La struttura di base del fornitore.
- Come recuperare i dati dal fornitore.
- Come modificare i dati nel fornitore.
- Come scrivere un adattatore di sincronizzazione per sincronizzare i dati dal server al provider di contatti.
Questa guida presuppone che tu conosca le nozioni di base sui content provider Android. Per saperne di più sui content provider Android, leggi la guida Nozioni di base sui content provider.
Organizzazione del provider di contatti
Il provider di contatti è un componente del fornitore di contenuti Android. Mantiene tre tipi di dati su una persona, ognuno dei quali corrisponde a una tabella offerta dal fornitore, come illustrato nella figura 1:

Figura 1. Struttura della tabella del fornitore di contatti.
Le tre tabelle sono comunemente denominate con i nomi delle relative classi di contratto. Le classi definiscono costanti per URI dei contenuti, nomi delle colonne e valori delle colonne utilizzati dalle tabelle:
-
ContactsContract.Contacts
tabella - Righe che rappresentano persone diverse, in base alle aggregazioni delle righe di contatti non elaborati.
-
ContactsContract.RawContacts
tabella - Righe contenenti un riepilogo dei dati di una persona, specifico per un tipo e un account utente.
-
ContactsContract.Data
tabella - Righe contenenti i dettagli del contatto non elaborato, ad esempio indirizzi email o numeri di telefono.
Le altre tabelle rappresentate dalle classi di contratti in ContactsContract
sono tabelle ausiliarie che il provider di contatti utilizza per gestire le proprie operazioni o supportare
funzioni specifiche nelle applicazioni di contatti o telefonia del dispositivo.
Contatti non elaborati
Un contatto grezzo rappresenta i dati di una persona provenienti da un singolo tipo di account e nome account. Poiché il provider di contatti consente di utilizzare più di un servizio online come origine dei dati di una persona, consente di utilizzare più contatti grezzi per la stessa persona. Più contatti grezzi consentono inoltre a un utente di combinare i dati di una persona provenienti da più account dello stesso tipo.
La maggior parte dei dati di un contatto grezzo non viene archiviata nella
tabella ContactsContract.RawContacts
. ma in una o più righe
della tabella ContactsContract.Data
. Ogni riga di dati ha una colonna
Data.RAW_CONTACT_ID
che
contiene il valore RawContacts._ID
della
riga ContactsContract.RawContacts
padre.
Colonne importanti dei contatti grezzi
Le colonne importanti della tabella ContactsContract.RawContacts
sono
elencate nella tabella 1. Leggi le note riportate dopo la tabella:
Tabella 1. Colonne importanti dei contatti grezzi.
Nome colonna | Usa | Note |
---|---|---|
ACCOUNT_NAME
|
Il nome dell'account per il tipo di account che è l'origine di questo contatto grezzo.
Ad esempio, il nome dell'account di un Account Google è uno degli indirizzi Gmail del proprietario del dispositivo. Consulta la voce successiva per
ACCOUNT_TYPE per ulteriori
informazioni.
|
Il formato di questo nome è specifico per il tipo di account. Non è necessariamente un indirizzo email. |
ACCOUNT_TYPE
|
Il tipo di account che è l'origine di questo contatto grezzo. Ad esempio, il tipo di account
di un Account Google è com.google . Qualifica sempre il tipo di account
con un identificatore di dominio per un dominio di tua proprietà o sotto il tuo controllo. In questo modo, il tuo
tipo di account sarà univoco.
|
Un tipo di account che offre dati dei contatti di solito ha un adattatore di sincronizzazione associato che si sincronizza con il provider di contatti. |
DELETED
|
Il flag "eliminato" per un contatto grezzo. | Questo flag consente al provider di contatti di mantenere internamente la riga fino a quando gli adattatori di sincronizzazione non sono in grado di eliminarla dai propri server e quindi eliminarla definitivamente dal repository. |
Note
Di seguito sono riportate alcune note importanti sulla tabella
ContactsContract.RawContacts
:
-
Il nome di un contatto grezzo non è memorizzato nella relativa riga in
ContactsContract.RawContacts
. ma viene archiviata nella tabellaContactsContract.Data
, in una rigaContactsContract.CommonDataKinds.StructuredName
. Un contatto grezzo ha una sola riga di questo tipo nella tabellaContactsContract.Data
. -
Attenzione:per utilizzare i dati del tuo account in una riga di contatto grezzo, devi
prima registrarli con
AccountManager
. Per farlo, chiedi agli utenti di aggiungere il tipo di account e il nome dell'account all'elenco degli account. Se non lo fai, il provider di contatti eliminerà automaticamente la riga del contatto non elaborato.Ad esempio, se vuoi che la tua app mantenga i dati dei contatti per il tuo servizio web con il dominio
com.example.dataservice
e l'account utente per il tuo servizio èbecky.sharp@dataservice.example.com
, l'utente deve prima aggiungere il "tipo" di account (com.example.dataservice
) e il "nome" dell'account (becky.smart@dataservice.example.com
) prima che la tua app possa aggiungere righe di contatti non elaborati. Puoi spiegare questo requisito all'utente nella documentazione oppure puoi chiedere all'utente di aggiungere il tipo e il nome o entrambi. I tipi di account e i nomi degli account sono descritti in modo più dettagliato nella sezione successiva.
Origini dei dati dei contatti non elaborati
Per capire come funzionano i contatti grezzi, considera l'utente "Emily Dickinson" che ha i seguenti tre account utente definiti sul suo dispositivo:
emily.dickinson@gmail.com
emilyd@gmail.com
- Account Twitter "belle_of_amherst"
Questo utente ha attivato l'opzione Sincronizza contatti per tutti e tre gli account nelle impostazioni Account.
Supponiamo che Emily Dickinson apra una finestra del browser, acceda a Gmail come
emily.dickinson@gmail.com
, apra
Contatti e aggiunga "Thomas Higginson". In un secondo momento, accede a Gmail come
emilyd@gmail.com
e invia un'email a "Thomas Higginson", che lo aggiunge automaticamente
come contatto. Seguiva anche "colonel_tom" (l'ID Twitter di Thomas Higginson) su
Twitter.
Il provider di contatti crea tre contatti non elaborati in seguito a questo lavoro:
-
Un contatto non elaborato per "Thomas Higginson" associato a
emily.dickinson@gmail.com
. Il tipo di account utente è Google. -
Un secondo contatto non elaborato per "Thomas Higginson" associato a
emilyd@gmail.com
. Anche il tipo di account utente è Google. Esiste un secondo contatto grezzo anche se il nome è identico a un nome precedente, perché la persona è stata aggiunta per un account utente diverso. - Un terzo contatto grezzo per "Thomas Higginson" associato a "belle_of_amherst". Il tipo di account utente è Twitter.
Dati
Come indicato in precedenza, i dati di un contatto grezzo vengono archiviati in una riga ContactsContract.Data
collegata al valore _ID
del contatto grezzo. Ciò consente a un singolo contatto non elaborato di avere più istanze dello stesso
tipo di dati, ad esempio indirizzi email o numeri di telefono. Ad esempio, se
"Thomas Higginson" per emilyd@gmail.com
(la riga del contatto non elaborato per Thomas Higginson
associato all'Account Google emilyd@gmail.com
) ha un indirizzo email personale
thigg@gmail.com
e un indirizzo email di lavoro
thomas.higginson@gmail.com
, il provider di contatti memorizza le due righe dell'indirizzo email
e le collega entrambe al contatto non elaborato.
Tieni presente che in questa singola tabella sono archiviati diversi tipi di dati. Le righe dei dettagli di nome visualizzato,
numero di telefono, email, indirizzo postale, foto e sito web si trovano tutte nella
tabella ContactsContract.Data
. Per facilitare la gestione, la tabella
ContactsContract.Data
ha alcune colonne con nomi descrittivi
e altre con nomi generici. I contenuti di una colonna con nome descrittivo hanno lo stesso significato
indipendentemente dal tipo di dati nella riga, mentre i contenuti di una colonna con nome generico hanno
significati diversi a seconda del tipo di dati.
Nomi delle colonne descrittivi
Alcuni esempi di nomi di colonne descrittivi sono:
-
RAW_CONTACT_ID
-
Il valore della colonna
_ID
del contatto non elaborato per questi dati. -
MIMETYPE
-
Il tipo di dati archiviati in questa riga, espresso come tipo MIME personalizzato. Il provider di contatti
utilizza i tipi MIME definiti nelle sottoclassi di
ContactsContract.CommonDataKinds
. Questi tipi MIME sono open source e possono essere utilizzati da qualsiasi applicazione o adattatore di sincronizzazione che funziona con il provider di contatti. -
IS_PRIMARY
-
Se questo tipo di riga di dati può verificarsi più di una volta per un contatto grezzo, la
colonna
IS_PRIMARY
contrassegna la riga di dati che contiene i dati principali per il tipo. Ad esempio, se l'utente preme a lungo un numero di telefono per un contatto e seleziona Imposta predefinito, la rigaContactsContract.Data
contenente quel numero ha la colonnaIS_PRIMARY
impostata su un valore diverso da zero.
Nomi di colonne generici
Sono disponibili 15 colonne generiche denominate DATA1
-DATA15
e altre quattro colonne generiche SYNC1
-SYNC4
che devono essere utilizzate solo dagli adattatori di sincronizzazione. Le costanti del nome della colonna generica funzionano sempre, indipendentemente dal tipo di
dati contenuti nella riga.
La colonna DATA1
è indicizzata. Il provider di contatti utilizza sempre questa colonna per
i dati che il provider prevede saranno il target più frequente di una query. Ad esempio,
in una riga di email, questa colonna contiene l'indirizzo email effettivo.
Per convenzione, la colonna DATA15
è riservata all'archiviazione di dati di oggetti binari di grandi dimensioni
(BLOB) come le miniature delle foto.
Nomi delle colonne specifici per tipo
Per facilitare l'utilizzo delle colonne per un particolare tipo di riga, il provider di contatti
fornisce anche costanti di nomi di colonne specifiche per il tipo, definite nelle sottoclassi di
ContactsContract.CommonDataKinds
. Le costanti assegnano semplicemente un
nome diverso alla stessa colonna, il che ti aiuta ad accedere ai dati in una riga di un
tipo particolare.
Ad esempio, la classe ContactsContract.CommonDataKinds.Email
definisce
costanti di nomi di colonne specifiche per il tipo per una riga ContactsContract.Data
con il tipo MIME
Email.CONTENT_ITEM_TYPE
. La classe contiene la costante
ADDRESS
per la colonna
dell'indirizzo email. Il valore effettivo di
ADDRESS
è "data1", che è
lo stesso del nome generico della colonna.
Attenzione:non aggiungere i tuoi dati personalizzati alla
tabella ContactsContract.Data
utilizzando una riga con uno dei
tipi MIME predefiniti del fornitore. In caso contrario, potresti perdere i dati o causare il malfunzionamento del provider. Ad esempio, non devi aggiungere una riga con il tipo MIME
Email.CONTENT_ITEM_TYPE
che contiene un nome utente anziché un indirizzo email nella
colonna DATA1
. Se utilizzi un tipo MIME personalizzato per la riga, puoi
definire i nomi delle colonne specifici per il tipo e utilizzare le colonne come preferisci.
La figura 2 mostra come appaiono le colonne descrittive e le colonne di dati in una riga ContactsContract.Data
e come i nomi delle colonne specifici per tipo "sovrappongono" i nomi delle colonne generici.

Figura 2. Nomi delle colonne specifici per il tipo e nomi delle colonne generici.
Classi di nomi di colonne specifiche per tipo
La Tabella 2 elenca le classi di nomi di colonne specifiche per tipo più comunemente utilizzate:
Tabella 2. Classi di nomi di colonne specifiche per tipo
Classe di mappatura | Tipo di dati | Note |
---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
I dati del nome del contatto non elaborato associato a questa riga di dati. | Un contatto non elaborato ha solo una di queste righe. |
ContactsContract.CommonDataKinds.Photo |
La foto principale del contatto non elaborato associato a questa riga di dati. | Un contatto non elaborato ha solo una di queste righe. |
ContactsContract.CommonDataKinds.Email |
Un indirizzo email per il contatto grezzo associato a questa riga di dati. | Un contatto grezzo può avere più indirizzi email. |
ContactsContract.CommonDataKinds.StructuredPostal |
Un indirizzo postale per il contatto grezzo associato a questa riga di dati. | Un contatto grezzo può avere più indirizzi postali. |
ContactsContract.CommonDataKinds.GroupMembership |
Un identificatore che collega il contatto grezzo a uno dei gruppi nel provider di contatti. | I gruppi sono una funzionalità facoltativa di un tipo di account e di un nome account. Sono descritti in maggiori dettagli nella sezione Gruppi di contatti. |
Contatti
Il provider di contatti combina le righe di contatti non elaborati in tutti i tipi di account e nomi di account per formare un contatto. Ciò facilita la visualizzazione e la modifica di tutti i dati che un utente ha raccolto per una persona. Il provider di contatti gestisce la creazione di nuove righe di contatti e l'aggregazione dei contatti non elaborati con una riga di contatti esistente. Né le applicazioni né gli adattatori di sincronizzazione possono aggiungere contatti e alcune colonne di una riga di contatto sono di sola lettura.
Nota:se provi ad aggiungere un contatto al provider di contatti con un
insert()
, riceverai
un'eccezione UnsupportedOperationException
. Se provi ad aggiornare una colonna
elencata come "di sola lettura ", l'aggiornamento viene ignorato.
Il provider di contatti crea un nuovo contatto in risposta all'aggiunta di un nuovo contatto grezzo che non corrisponde a nessun contatto esistente. Il provider esegue questa operazione anche se i dati di un contatto grezzo esistente cambiano in modo tale da non corrispondere più al contatto a cui era collegato in precedenza. Se un'applicazione o un adattatore di sincronizzazione crea un nuovo contatto grezzo che corrisponde a un contatto esistente, il nuovo contatto grezzo viene aggregato a quello esistente.
Il provider di contatti collega una riga di contatto alle relative righe di contatto non elaborate con la colonna
_ID
della tabella Contacts
. La colonna CONTACT_ID
della tabella dei contatti grezzi
ContactsContract.RawContacts
contiene valori _ID
per
la riga dei contatti associata a ogni riga dei contatti grezzi.
La tabella ContactsContract.Contacts
contiene anche la colonna
LOOKUP_KEY
, che è un
link "permanente" alla riga del contatto. Poiché il provider di contatti gestisce i contatti
automaticamente, potrebbe modificare il valore _ID
di una riga di contatto
in risposta a un'aggregazione o a una sincronizzazione. Anche in questo caso, l'URI dei contenuti
CONTENT_LOOKUP_URI
combinato con
LOOKUP_KEY
del contatto continuerà
a puntare alla riga del contatto, quindi puoi utilizzare
LOOKUP_KEY
per mantenere i link ai contatti "preferiti" e così via. Questa colonna ha un proprio formato
non correlato al formato della colonna _ID
.
La Figura 3 mostra la relazione tra le tre tabelle principali.

Figura 3. Relazioni tra le tabelle Contatti, Contatti non elaborati e Dettagli.
Attenzione : se pubblichi la tua app sul Google Play Store o se la tua app si trova su un dispositivo con Android 10 (livello API 29) o versioni successive, tieni presente che un insieme limitato di campi e metodi di dati dei contatti sono obsoleti.
Nelle condizioni indicate, il sistema cancella periodicamente tutti i valori scritti in questi campi di dati:
-
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED
-
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED
-
ContactsContract.DataUsageStatColumns.LAST_TIME_USED
-
ContactsContract.DataUsageStatColumns.TIMES_USED
Anche le API utilizzate per impostare i campi di dati sopra indicati sono obsolete:
Inoltre, i seguenti campi non restituiscono più i contatti frequenti. Tieni presente che alcuni di questi campi influiscono sul ranking dei contatti solo quando i contatti fanno parte di un tipo di dati specifico.
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI
-
CONTENT_FILTER_URI
(riguarda solo i tipi di dati Email, Telefono, Chiamabile e Contattabili) -
ENTERPRISE_CONTENT_FILTER_URI
(riguarda solo i tipi di dati Email, Telefono, e Chiamabile)
Se le tue app accedono a questi campi o API o li aggiornano, utilizza metodi alternativi. Ad esempio, puoi soddisfare determinati casi d'uso utilizzando fornitori di contenuti privati o altri dati archiviati all'interno della tua app o dei sistemi di backend.
Per verificare che la funzionalità della tua app non sia interessata da questa modifica, puoi cancellare manualmente questi campi di dati. Per farlo, esegui il seguente comando ADB su un dispositivo con Android 4.1 (livello API 16) o versioni successive:
adb shell content delete \ --uri content://com.android.contacts/contacts/delete_usage
Dati dagli adattatori di sincronizzazione
Gli utenti inseriscono i dati dei contatti direttamente nel dispositivo, ma i dati vengono trasferiti anche nel provider di contatti
dai servizi web tramite gli adattatori di sincronizzazione, che automatizzano
il trasferimento dei dati tra il dispositivo e i servizi. Gli adattatori di sincronizzazione vengono eseguiti in background
sotto il controllo del sistema e chiamano i metodi ContentResolver
per gestire i dati.
In Android, il servizio web con cui funziona un adattatore di sincronizzazione è identificato da un tipo di account. Ogni adattatore di sincronizzazione funziona con un tipo di account, ma può supportare più nomi di account per quel tipo. I tipi di account e i nomi degli account sono descritti brevemente nella sezione Origini dei dati dei contatti non elaborati. Le seguenti definizioni offrono maggiori dettagli e descrivono la relazione tra tipo e nome dell'account e servizi e adattatori di sincronizzazione.
- Tipo di account
-
Identifica un servizio in cui l'utente ha memorizzato i dati. La maggior parte delle volte, l'utente deve
autenticarsi con il servizio. Ad esempio, Contatti Google è un tipo di account identificato
dal codice
google.com
. Questo valore corrisponde al tipo di account utilizzato daAccountManager
. - Nome account
- Identifica un account o un accesso specifico per un tipo di account. Gli account Google Contatti sono uguali agli Account Google, che hanno un indirizzo email come nome account. Altri servizi potrebbero utilizzare un nome utente di una sola parola o un ID numerico.
I tipi di account non devono essere univoci. Un utente può configurare più account Google Contatti e scaricare i propri dati nel provider di contatti. Ciò può accadere se l'utente ha un insieme di contatti personali per un nome account personale e un altro insieme per il lavoro. I nomi degli account sono generalmente univoci. Insieme, identificano un flusso di dati specifico tra il provider di contatti e un servizio esterno.
Se vuoi trasferire i dati del tuo servizio al provider di contatti, devi scrivere il tuo adattatore di sincronizzazione. Questo argomento è descritto in modo più dettagliato nella sezione Adattatori di sincronizzazione del provider di contatti.
La figura 4 mostra come il provider di contatti si inserisce nel flusso di dati sulle persone. Nella casella contrassegnata con "Adattatori di sincronizzazione", ogni adattatore è etichettato in base al tipo di account.

Figura 4. Il flusso di dati del provider di contatti.
Autorizzazioni richieste
Le applicazioni che vogliono accedere al provider di contatti devono richiedere le seguenti autorizzazioni:
- Accesso in lettura a una o più tabelle
-
READ_CONTACTS
, specificato inAndroidManifest.xml
con l'elemento<uses-permission>
come<uses-permission android:name="android.permission.READ_CONTACTS">
. - Accesso in scrittura a una o più tabelle
-
WRITE_CONTACTS
, specificato inAndroidManifest.xml
con l'elemento<uses-permission>
come<uses-permission android:name="android.permission.WRITE_CONTACTS">
.
Queste autorizzazioni non si estendono ai dati del profilo utente. Il profilo utente e le relative autorizzazioni richieste sono descritti nella sezione successiva, Il profilo utente.
Ricorda che i dati dei contatti dell'utente sono personali e sensibili. Gli utenti sono preoccupati per la loro privacy, quindi non vogliono che le applicazioni raccolgano dati su di loro o sui loro contatti. Se non è ovvio il motivo per cui hai bisogno dell'autorizzazione per accedere ai dati dei contatti, gli utenti potrebbero assegnare valutazioni basse alla tua applicazione o semplicemente rifiutarsi di installarla.
Il profilo utente
La tabella ContactsContract.Contacts
ha una sola riga contenente
i dati del profilo dell'utente del dispositivo. Questi dati descrivono il user
del dispositivo anziché uno dei contatti dell'utente. La riga dei contatti del profilo è collegata a una riga
di contatti non elaborati per ogni sistema che utilizza un profilo.
Ogni riga di contatto grezzo del profilo può avere più righe di dati. Le costanti per accedere al profilo utente sono disponibili nella classe ContactsContract.Profile
.
L'accesso al profilo utente richiede autorizzazioni speciali. Oltre alle autorizzazioni
READ_CONTACTS
e
WRITE_CONTACTS
necessarie per leggere e scrivere, l'accesso
al profilo utente richiede le autorizzazioni android.Manifest.permission#READ_PROFILE e
android.Manifest.permission#WRITE_PROFILE per l'accesso in lettura e scrittura,
rispettivamente.
Ricorda che devi considerare il profilo di un utente come sensibile. L'autorizzazione android.Manifest.permission#READ_PROFILE consente di accedere ai dati di identificazione personale dell'utente del dispositivo. Assicurati di comunicare all'utente il motivo per cui hai bisogno delle autorizzazioni di accesso al profilo utente nella descrizione dell'applicazione.
Per recuperare la riga del contatto che contiene il profilo dell'utente,
chiama ContentResolver.query()
. Imposta l'URI dei contenuti su
CONTENT_URI
e non fornire
criteri di selezione. Puoi anche utilizzare questo URI dei contenuti come URI di base per recuperare i contatti o i dati non elaborati per il profilo. Ad esempio, questo snippet recupera i dati per il profilo:
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);
Nota:se recuperi più righe di contatti e vuoi determinare se una di queste
è il profilo utente, testa la colonna
IS_USER_PROFILE
. Questa colonna
è impostata su "1" se il contatto è il profilo utente.
Metadati del provider di contatti
Il provider di contatti gestisce i dati che tengono traccia dello stato dei dati dei contatti nel
repository. Questi metadati sul repository vengono archiviati in varie posizioni, tra cui le righe delle tabelle Raw Contacts, Data e Contacts, la tabella ContactsContract.Settings
e la tabella ContactsContract.SyncState
. La tabella seguente mostra l'effetto di ciascuno di questi metadati:
Tabella 3. Metadati nel provider di contatti
Tabella | Colonna | Valori | Significato |
---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
"0" - non è stato modificato dall'ultima sincronizzazione. |
Contrassegna i contatti non elaborati modificati sul dispositivo e che devono essere sincronizzati di nuovo con il
server. Il valore viene impostato automaticamente dal provider di contatti quando le applicazioni Android aggiornano una riga.
Gli adattatori di sincronizzazione che modificano le tabelle di dati o di contatti non elaborati devono sempre aggiungere la
stringa |
"1" - modificato dall'ultima sincronizzazione, deve essere sincronizzato di nuovo con il server. | |||
ContactsContract.RawContacts |
VERSION |
Il numero di versione di questa riga. | Il provider di contatti incrementa automaticamente questo valore ogni volta che la riga o i relativi dati cambiano. |
ContactsContract.Data |
DATA_VERSION |
Il numero di versione di questa riga. | Il provider di contatti incrementa automaticamente questo valore ogni volta che la riga di dati viene modificata. |
ContactsContract.RawContacts |
SOURCE_ID |
Un valore stringa che identifica in modo univoco questo contatto grezzo nell'account in cui è stato creato. |
Quando un adattatore di sincronizzazione crea un nuovo contatto grezzo, questa colonna deve essere impostata sull'ID univoco del server per il contatto grezzo. Quando un'applicazione Android crea un nuovo
contatto grezzo, deve lasciare vuota questa colonna. In questo modo l'adattatore di sincronizzazione
crea un nuovo contatto grezzo sul server e ottiene un
valore per SOURCE_ID .
In particolare, l'ID origine deve essere univoco per ogni tipo di account e deve rimanere invariato durante le sincronizzazioni:
|
ContactsContract.Groups |
GROUP_VISIBLE |
"0" - I contatti di questo gruppo non devono essere visibili nelle UI delle applicazioni Android. | Questa colonna è per la compatibilità con i server che consentono a un utente di nascondere i contatti in determinati gruppi. |
"1" - I contatti di questo gruppo possono essere visibili nelle interfacce utente dell'applicazione. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
"0" - Per questo account e tipo di account, i contatti che non appartengono a un gruppo sono invisibili alle UI delle applicazioni Android. |
Per impostazione predefinita, i contatti sono invisibili se nessuno dei loro contatti grezzi appartiene a un gruppo
(l'appartenenza a un gruppo per un contatto grezzo è indicata da una o più
righe ContactsContract.CommonDataKinds.GroupMembership
nella tabella ContactsContract.Data ).
Se imposti questo flag nella riga della tabella ContactsContract.Settings
per un tipo di account e un account, puoi forzare la visibilità dei contatti senza gruppi.
Uno degli utilizzi di questo flag è mostrare i contatti provenienti da server che non utilizzano i gruppi.
|
"1" - Per questo account e tipo di account, i contatti che non appartengono a un gruppo sono visibili alle UI delle applicazioni. | |||
ContactsContract.SyncState |
(tutte) | Utilizza questa tabella per archiviare i metadati dell'adattatore di sincronizzazione. | Con questa tabella puoi archiviare in modo permanente sullo smartphone lo stato della sincronizzazione e altri dati correlati. |
Accesso al provider di contatti
Questa sezione descrive le linee guida per l'accesso ai dati del provider di contatti, concentrandosi su quanto segue:
- Query di entità.
- Modifica batch.
- Recupero e modifica con gli intent.
- Integrità dei dati.
Le modifiche apportate da un adattatore di sincronizzazione sono trattate in modo più dettagliato nella sezione Adattatori di sincronizzazione del provider di contatti.
Esecuzione di query sulle entità
Poiché le tabelle del provider di contatti sono organizzate in una gerarchia, spesso è utile
recuperare una riga e tutte le righe "figlio" collegate. Ad esempio, per visualizzare
tutte le informazioni di una persona, potresti voler recuperare tutte le
righe ContactsContract.RawContacts
per una singola
riga ContactsContract.Contacts
o tutte le
righe ContactsContract.CommonDataKinds.Email
per una singola
riga ContactsContract.RawContacts
. A questo scopo, il provider di contatti offre costrutti di entità, che fungono da join di database tra le tabelle.
Un'entità è come una tabella composta da colonne selezionate di una tabella principale e della relativa tabella secondaria.
Quando esegui una query su un'entità, fornisci una proiezione e criteri di ricerca basati sulle colonne
disponibili dall'entità. Il risultato è un Cursor
che contiene
una riga per ogni riga della tabella secondaria recuperata. Ad esempio, se esegui una query
ContactsContract.Contacts.Entity
per un nome di contatto
e tutte le righe ContactsContract.CommonDataKinds.Email
per tutti i
contatti grezzi per quel nome, ricevi un Cursor
contenente una riga
per ogni riga ContactsContract.CommonDataKinds.Email
.
Le entità semplificano le query. Utilizzando un'entità, puoi recuperare tutti i dati dei contatti per un contatto o un contatto grezzo contemporaneamente, anziché dover eseguire prima una query sulla tabella principale per ottenere un ID e poi dover eseguire una query sulla tabella secondaria con quell'ID. Inoltre, il provider di contatti elabora una query su un'entità in una singola transazione, il che garantisce che i dati recuperati siano internamente coerenti.
Nota:in genere, un'entità non contiene tutte le colonne della tabella padre e
figlio. Se tenti di utilizzare un nome di colonna che non è presente nell'elenco delle costanti
per l'entità, riceverai un Exception
.
Il seguente snippet mostra come recuperare tutte le righe di contatti grezzi per un contatto. Lo snippet
fa parte di un'applicazione più grande con due attività, "principale" e "dettagli". L'attività principale
mostra un elenco di righe di contatto; quando l'utente ne seleziona una, l'attività invia il relativo ID all'attività
di dettaglio. L'attività dettagliata utilizza ContactsContract.Contacts.Entity
per visualizzare tutte le righe di dati di tutti i contatti grezzi associati al contatto selezionato.
Questo snippet è tratto dall'attività "Dettaglio":
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. }
Al termine del caricamento, LoaderManager
richiama un callback a
onLoadFinished()
. Uno degli argomenti in entrata di questo metodo è un
Cursor
con i risultati della query. Nella tua app, puoi ottenere i
dati da questo Cursor
per visualizzarli o utilizzarli ulteriormente.
Modifica batch
Quando possibile, devi inserire, aggiornare ed eliminare i dati nel provider di contatti in "modalità batch", creando un ArrayList
di oggetti ContentProviderOperation
e chiamando applyBatch()
. Poiché
il provider di contatti esegue tutte le operazioni in un
applyBatch()
in una singola
transazione, le modifiche non lasceranno mai il repository dei contatti in uno stato incoerente. Una modifica batch facilita anche l'inserimento contemporaneo di un contatto non elaborato e dei relativi dati di dettaglio.
Nota:per modificare un contatto grezzo singolo, valuta la possibilità di inviare un intent all'applicazione Contatti del dispositivo anziché gestire la modifica nella tua app. Questa procedura è descritta in dettaglio nella sezione Recupero e modifica con gli intent.
Punti di rendimento
Una modifica batch contenente un numero elevato di operazioni può bloccare altri processi,
con conseguente esperienza utente complessiva negativa. Per organizzare tutte le modifiche che vuoi
eseguire in un numero di elenchi separati il più ridotto possibile e allo stesso tempo impedire che
blocchino il sistema, devi impostare punti di rendimento per una o più operazioni.
Un punto di rendimento è un oggetto ContentProviderOperation
il cui
valore isYieldAllowed()
è impostato su
true
. Quando il provider di contatti incontra un punto di rendimento, mette in pausa il suo lavoro per
consentire l'esecuzione di altri processi e chiude la transazione corrente. Quando il fornitore riavvia, continua con l'operazione successiva in ArrayList
e avvia una nuova transazione.
I punti di rendimento generano più di una transazione per chiamata a
applyBatch()
. Per questo motivo, devi impostare un punto di rendimento per l'ultima operazione per un insieme di righe correlate.
Ad esempio, devi impostare un punto di rendimento per l'ultima operazione di un insieme che aggiunge
righe di contatti non elaborati e le righe di dati associate oppure l'ultima operazione per un insieme di righe correlate
a un singolo contatto.
I punti di rendimento sono anche un'unità di operazione atomica. Tutti gli accessi tra due punti di rendimento riusciranno o non riusciranno come singola unità. Se non imposti punti di rendimento, l'operazione atomica più piccola è l'intero batch di operazioni. Se utilizzi i punti di rendimento, impedisci alle operazioni di peggiorare le prestazioni del sistema, garantendo al contempo che un sottoinsieme di operazioni sia atomico.
Riferimenti inversi di modifica
Quando inserisci una nuova riga di contatto grezzo e le relative righe di dati come insieme di
oggetti ContentProviderOperation
, devi collegare le righe di dati alla
riga di contatto grezzo inserendo il valore _ID
del contatto grezzo come valore
RAW_CONTACT_ID
. Tuttavia, questo
valore non è disponibile quando crei ContentProviderOperation
per la riga di dati, perché non hai ancora applicato
ContentProviderOperation
per la riga di contatto non elaborato. Per ovviare a questo problema,
la classe ContentProviderOperation.Builder
ha il metodo
withValueBackReference()
.
Questo metodo ti consente di inserire o modificare una colonna con il
risultato di un'operazione precedente.
Il metodo withValueBackReference()
ha due argomenti:
-
key
- La chiave di una coppia chiave-valore. Il valore di questo argomento deve essere il nome di una colonna nella tabella che stai modificando.
-
previousResult
-
L'indice a base 0 di un valore nell'array di
oggetti
ContentProviderResult
daapplyBatch()
. Man mano che vengono applicate le operazioni batch, il risultato di ogni operazione viene memorizzato in un array intermedio di risultati. Il valorepreviousResult
è l'indice di uno di questi risultati, che viene recuperato e memorizzato con il valorekey
. In questo modo puoi inserire un nuovo record di contatto non elaborato e recuperare il relativo valore_ID
, quindi creare un "riferimento inverso" al valore quando aggiungi una rigaContactsContract.Data
.L'intero array di risultati viene creato quando chiami per la prima volta
applyBatch()
, con una dimensione uguale a quella diArrayList
di oggettiContentProviderOperation
che fornisci. Tuttavia, tutti gli elementi dell'array dei risultati sono impostati sunull
e se provi a fare un riferimento inverso a un risultato per un'operazione che non è ancora stata applicata,withValueBackReference()
genera unException
.
I seguenti snippet mostrano come inserire in batch un nuovo contatto grezzo e i relativi dati. Sono incluso codice che stabilisce un punto di rendimento e utilizza un riferimento inverso.
Il primo snippet recupera i dati di contatto dalla UI. A questo punto, l'utente ha già selezionato l'account a cui aggiungere il nuovo contatto grezzo.
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());
Il seguente snippet crea un'operazione per inserire la riga del contatto grezzo nella
tabella 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());
Successivamente, il codice crea righe di dati per il nome visualizzato, il telefono e l'email.
Ogni oggetto di creazione dell'operazione utilizza
withValueBackReference()
per ottenere
RAW_CONTACT_ID
. I punti di riferimento
tornano all'oggetto ContentProviderResult
della prima operazione,
che aggiunge la riga del contatto non elaborato e restituisce il nuovo valore _ID
. Di conseguenza, ogni riga di dati viene collegata automaticamente tramite il relativo
RAW_CONTACT_ID
alla nuova riga ContactsContract.RawContacts
a cui appartiene.
L'oggetto ContentProviderOperation.Builder
che aggiunge la riga dell'email è
contrassegnato con withYieldAllowed()
, che imposta un punto di rendimento:
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());
L'ultimo snippet mostra la chiamata a
applyBatch()
che
inserisce il nuovo contatto non elaborato e le righe di dati.
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); } }
Le operazioni batch ti consentono anche di implementare il controllo della concorrenza ottimistico, un metodo per applicare le transazioni di modifica senza dover bloccare il repository sottostante. Per utilizzare questo metodo, applichi la transazione e poi controlli altre modifiche che potrebbero essere state apportate contemporaneamente. Se rilevi una modifica incoerente, puoi annullare la transazione e riprovare.
Il controllo della concorrenza ottimistico è utile per un dispositivo mobile, in cui è presente un solo utente alla volta e gli accessi simultanei a un repository di dati sono rari. Poiché il blocco non viene utilizzato, non si perde tempo a impostare i blocchi o ad attendere che altre transazioni rilascino i blocchi.
Per utilizzare il controllo della concorrenza ottimistico durante l'aggiornamento di una singola riga ContactsContract.RawContacts
, segui questi passaggi:
-
Recupera la colonna
VERSION
del contatto non elaborato insieme agli altri dati recuperati. -
Crea un oggetto
ContentProviderOperation.Builder
adatto per l'applicazione di un vincolo utilizzando il metodonewAssertQuery(Uri)
. Per l'URI dei contenuti, utilizzaRawContacts.CONTENT_URI
con_ID
del contatto non elaborato aggiunto. -
Per l'oggetto
ContentProviderOperation.Builder
, chiamawithValue()
per confrontare la colonnaVERSION
con il numero di versione appena recuperato. -
Per lo stesso
ContentProviderOperation.Builder
, chiamawithExpectedCount()
per assicurarti che venga testata una sola riga da questa asserzione. -
Chiama
build()
per creare l'oggettoContentProviderOperation
, quindi aggiungi questo oggetto come primo oggetto nell'ArrayList
che passi aapplyBatch()
. - Applica la transazione batch.
Se la riga del contatto non elaborato viene aggiornata da un'altra operazione tra il momento in cui leggi la riga e
il momento in cui tenti di modificarla, l'"asserzione" ContentProviderOperation
non andrà a buon fine e l'intero batch di operazioni verrà annullato. Puoi quindi scegliere di riprovare
il batch o intraprendere un'altra azione.
Lo snippet seguente mostra come creare un'asserzione
ContentProviderOperation
dopo aver eseguito una query per un singolo contatto grezzo utilizzando
un 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 }
Recupero e modifica con gli intent
L'invio di un intent all'applicazione Contatti del dispositivo consente di accedere indirettamente al provider di contatti. L'intent avvia l'interfaccia utente dell'applicazione Contatti del dispositivo, in cui gli utenti possono svolgere attività correlate ai contatti. Con questo tipo di accesso, gli utenti possono:
- Scegli un contatto da un elenco e restituiscilo alla tua app per ulteriori operazioni.
- Modifica i dati di un contatto esistente.
- Inserisci un nuovo contatto grezzo per uno dei suoi account.
- Eliminare un contatto o i dati dei contatti.
Se l'utente sta inserendo o aggiornando i dati, puoi raccoglierli prima e inviarli come parte dell'intent.
Quando utilizzi gli intent per accedere al provider di contatti tramite l'applicazione Contatti del dispositivo, non devi scrivere la tua UI o il tuo codice per accedere al provider. Inoltre, non devi richiedere l'autorizzazione per leggere o scrivere nel fornitore. L'applicazione Contatti del dispositivo può delegarti l'autorizzazione di lettura per un contatto e, poiché stai apportando modifiche al provider tramite un'altra applicazione, non devi disporre delle autorizzazioni di scrittura.
La procedura generale per inviare un intent di accesso a un fornitore è descritta in dettaglio nella guida
Nozioni di base sul fornitore di contenuti nella sezione "Accesso ai dati tramite intent". L'azione,
il tipo MIME e i valori dei dati che utilizzi per le attività disponibili sono riepilogati nella Tabella 4, mentre i
valori degli extra che puoi utilizzare con
putExtra()
sono elencati nella
documentazione di riferimento per ContactsContract.Intents.Insert
:
Tabella 4. Intent del provider di contatti.
Attività | Azione | Dati | Tipo MIME | Note |
---|---|---|---|---|
Scegliere un contatto da un elenco | ACTION_PICK |
Uno dei seguenti:
|
Non utilizzato |
Visualizza un elenco di contatti grezzi o un elenco di dati di un contatto grezzo, a seconda del
tipo di URI dei contenuti fornito.
Chiama
|
Inserire un nuovo contatto grezzo | Insert.ACTION |
N/D |
RawContacts.CONTENT_TYPE , tipo MIME per un insieme di contatti grezzi.
|
Visualizza la schermata Aggiungi contatto dell'applicazione Contatti del dispositivo. Vengono visualizzati i valori
degli extra che aggiungi all'intent. Se inviato con
startActivityForResult() ,
l'URI dei contenuti del contatto non elaborato appena aggiunto viene restituito al metodo di callback
onActivityResult()
della tua attività nell'argomento Intent , nel campo
"data". Per ottenere il valore, chiama getData() .
|
Modifica un contatto | ACTION_EDIT |
CONTENT_LOOKUP_URI per
il contatto. L'attività di modifica consentirà all'utente di modificare tutti i dati associati
a questo contatto.
|
Contacts.CONTENT_ITEM_TYPE , un singolo contatto. |
Visualizza la schermata Modifica contatto nell'applicazione Contatti. Vengono visualizzati i valori degli extra che aggiungi all'intent. Quando l'utente fa clic su Fine per salvare le modifiche, la tua attività torna in primo piano. |
Visualizzare un selettore che può anche aggiungere dati. | ACTION_INSERT_OR_EDIT |
N/D |
CONTENT_ITEM_TYPE
|
Questo intent mostra sempre la schermata del selettore dell'app Contatti. L'utente può
scegliere un contatto da modificare o aggiungerne uno nuovo. Viene visualizzata la schermata di modifica o di aggiunta, a seconda della scelta dell'utente, e vengono visualizzati i dati degli extra che trasmetti nell'intent. Se la tua app mostra dati di contatto come un'email o un numero di telefono, utilizza
questo intent per consentire all'utente di aggiungere i dati a un contatto esistente.
contatto,
Nota:non è necessario inviare un valore name negli extra di questo intent, perché l'utente sceglie sempre un nome esistente o ne aggiunge uno nuovo. Inoltre, se invii un nome e l'utente sceglie di apportare una modifica, l'app Contatti mostrerà il nome che invii, sovrascrivendo il valore precedente. Se l'utente non se ne accorge e salva la modifica, il vecchio valore viene perso. |
L'app Contatti del dispositivo non consente di eliminare un contatto non elaborato o i relativi dati con un intent. Per eliminare un contatto grezzo, utilizza
ContentResolver.delete()
o ContentProviderOperation.newDelete()
.
Lo snippet seguente mostra come creare e inviare un intent che inserisce un nuovo contatto e dati non elaborati:
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);
Integrità dei dati
Poiché il repository dei contatti contiene dati importanti e sensibili che gli utenti si aspettano siano corretti e aggiornati, il provider di contatti ha regole ben definite per l'integrità dei dati. È tua responsabilità rispettare queste regole quando modifichi i dati dei contatti. Le regole importanti sono elencate qui:
-
Aggiungi sempre una riga
ContactsContract.CommonDataKinds.StructuredName
per ogni rigaContactsContract.RawContacts
che aggiungi. -
Una riga
ContactsContract.RawContacts
senza una rigaContactsContract.CommonDataKinds.StructuredName
nella tabellaContactsContract.Data
può causare problemi durante l'aggregazione. -
Collega sempre le nuove righe
ContactsContract.Data
alla rigaContactsContract.RawContacts
principale. -
Una riga
ContactsContract.Data
non collegata a unContactsContract.RawContacts
non sarà visibile nell'applicazione Contatti del dispositivo e potrebbe causare problemi con gli adattatori di sincronizzazione. - Modifica i dati solo per i contatti grezzi di tua proprietà.
- Tieni presente che il provider di contatti di solito gestisce i dati di diversi tipi di account/servizi online. Devi assicurarti che la tua applicazione modifichi o elimini solo i dati delle righe che ti appartengono e che inserisca solo dati con un tipo e un nome di account che controlli.
-
Utilizza sempre le costanti definite in
ContactsContract
e nelle relative sottoclassi per autorità, URI dei contenuti, percorsi URI, nomi delle colonne, tipi MIME e valoriTYPE
. - L'utilizzo di queste costanti ti aiuta a evitare errori. Riceverai anche notifiche con avvisi del compilatore se una delle costanti è deprecata.
Righe di dati personalizzate
Creando e utilizzando i tuoi tipi MIME personalizzati, puoi inserire, modificare, eliminare e recuperare
le tue righe di dati nella tabella ContactsContract.Data
. Le righe
possono utilizzare solo la colonna definita in
ContactsContract.DataColumns
, anche se puoi mappare i tuoi
nomi delle colonne specifici per tipo ai nomi delle colonne predefiniti. Nell'applicazione Contatti del dispositivo,
i dati delle righe vengono visualizzati, ma non possono essere modificati o eliminati e gli utenti non possono aggiungere
dati aggiuntivi. Per consentire agli utenti di modificare le righe di dati personalizzati, devi fornire un'attività di modifica nella tua applicazione.
Per visualizzare i tuoi dati personalizzati, fornisci un file contacts.xml
contenente un elemento <ContactsAccountType>
e uno o più dei relativi elementi secondari <ContactsDataKind>
. Questo aspetto è descritto in modo più dettagliato nella sezione
<ContactsDataKind> element
.
Per scoprire di più sui tipi MIME personalizzati, leggi la guida Creare un content provider.
Adattatori di sincronizzazione del provider di contatti
Il provider di contatti è progettato specificamente per la gestione della sincronizzazione dei dati dei contatti tra un dispositivo e un servizio online. In questo modo, gli utenti possono scaricare i dati esistenti su un nuovo dispositivo e caricarli su un nuovo account. La sincronizzazione garantisce inoltre che gli utenti abbiano a portata di mano i dati più recenti, indipendentemente dalla fonte di aggiunte e modifiche. Un altro vantaggio della sincronizzazione è che rende i dati dei contatti disponibili anche quando il dispositivo non è connesso alla rete.
Sebbene sia possibile implementare la sincronizzazione in vari modi, il sistema Android fornisce un framework di sincronizzazione dei plug-in che automatizza le seguenti attività:
- Controllo della disponibilità della rete.
- Pianificazione ed esecuzione della sincronizzazione in base alle preferenze dell'utente.
- Riavvio delle sincronizzazioni interrotte.
Per utilizzare questo framework, fornisci un plug-in dell'adattatore di sincronizzazione. Ogni adattatore di sincronizzazione è univoco per un servizio e un fornitore di contenuti, ma può gestire più nomi account per lo stesso servizio. Il framework consente anche più adattatori di sincronizzazione per lo stesso servizio e provider.
Classi e file dell'adattatore di sincronizzazione
Implementa un adattatore di sincronizzazione come sottoclasse di
AbstractThreadedSyncAdapter
e lo installa come parte di un'applicazione
Android. Il sistema acquisisce informazioni sull'adattatore di sincronizzazione dagli elementi del file manifest dell'applicazione e da un file XML speciale a cui fa riferimento il manifest. Il file XML definisce il tipo di account per il servizio online e l'autorità per il fornitore di contenuti, che insieme identificano in modo univoco l'adattatore. L'adattatore di sincronizzazione non diventa attivo finché l'utente non aggiunge un
account per il tipo di account dell'adattatore di sincronizzazione e non attiva la sincronizzazione per il content provider con cui l'adattatore di sincronizzazione si sincronizza. A questo punto, il sistema inizia a gestire l'adattatore,
richiamandolo in base alle necessità per la sincronizzazione tra il content provider e il server.
Nota:l'utilizzo di un tipo di account come parte dell'identificazione dell'adattatore di sincronizzazione consente
al sistema di rilevare e raggruppare gli adattatori di sincronizzazione che accedono a servizi diversi della
stessa organizzazione. Ad esempio, tutti gli adattatori di sincronizzazione per i servizi online di Google hanno lo stesso
tipo di account com.google
. Quando gli utenti aggiungono un Account Google ai propri dispositivi, tutti
gli adattatori di sincronizzazione installati per i servizi Google vengono elencati insieme; ogni adattatore di sincronizzazione
elencato si sincronizza con un diverso fornitore di contenuti sul dispositivo.
Poiché la maggior parte dei servizi richiede agli utenti di verificare la propria identità prima di accedere
ai dati, il sistema Android offre un framework di autenticazione simile a quello dell'adattatore di sincronizzazione e spesso
utilizzato in combinazione con quest'ultimo. Il framework di autenticazione utilizza
autenticatori plug-in che sono sottoclassi di
AbstractAccountAuthenticator
. Un autenticatore verifica
l'identità dell'utente nei seguenti passaggi:
- Raccoglie il nome, la password o informazioni simili dell'utente (le credenziali dell'utente).
- Invia le credenziali al servizio
- Esamina la risposta del servizio.
Se il servizio accetta le credenziali, l'autenticatore può
memorizzarle per un utilizzo successivo. Grazie al framework di autenticazione dei plug-in, AccountManager
può fornire l'accesso a qualsiasi token di autenticazione supportato e scelto da un autenticatore, ad esempio i token di autenticazione OAuth2.
Sebbene l'autenticazione non sia obbligatoria, la maggior parte dei servizi di contatti la utilizza. Tuttavia, non è necessario utilizzare il framework di autenticazione Android per eseguire l'autenticazione.
Implementazione dell'adattatore di sincronizzazione
Per implementare un adattatore di sincronizzazione per il provider di contatti, inizia creando un'applicazione Android che contenga quanto segue:
-
Un componente
Service
che risponde alle richieste del sistema di associazione all'adattatore di sincronizzazione. -
Quando il sistema vuole eseguire una sincronizzazione, chiama il metodo
onBind()
del servizio per ottenere unIBinder
per l'adattatore di sincronizzazione. Ciò consente al sistema di effettuare chiamate tra processi ai metodi dell'adattatore. -
L'adattatore di sincronizzazione effettivo, implementato come sottoclasse concreta di
AbstractThreadedSyncAdapter
. -
Questa classe si occupa di scaricare i dati dal server, caricare i dati dal
dispositivo e risolvere i conflitti. Il lavoro principale dell'adattatore
viene svolto nel metodo
onPerformSync()
. Questa classe deve essere istanziata come singleton. -
Una sottoclasse di
Application
. -
Questa classe funge da factory per il singleton dell'adattatore di sincronizzazione. Utilizza il
metodo
onCreate()
per creare un'istanza dell'adattatore di sincronizzazione e fornisci un metodo "getter" statico per restituire il singleton al metodoonBind()
del servizio dell'adattatore di sincronizzazione. -
(Facoltativo) Un componente
Service
che risponde alle richieste di autenticazione degli utenti da parte del sistema. -
AccountManager
avvia questo servizio per iniziare la procedura di autenticazione. Il metodoonCreate()
del servizio crea un oggetto autenticatore. Quando il sistema vuole autenticare un account utente per l'adattatore di sincronizzazione dell'applicazione, chiama il metodoonBind()
del servizio per ottenere unIBinder
per l'autenticatore. Ciò consente al sistema di effettuare chiamate tra processi ai metodi dell'autenticatore. -
(Facoltativo) Una sottoclasse concreta di
AbstractAccountAuthenticator
che gestisce le richieste di autenticazione. -
Questa classe fornisce metodi che
AccountManager
richiama per autenticare le credenziali dell'utente con il server. I dettagli della procedura di autenticazione variano notevolmente in base alla tecnologia del server in uso. Per saperne di più sull'autenticazione, consulta la documentazione del software del server. - File XML che definiscono l'adattatore di sincronizzazione e l'autenticatore per il sistema.
-
I componenti del servizio di autenticazione e dell'adattatore di sincronizzazione descritti in precedenza sono
definiti negli elementi
<service>
del manifest dell'applicazione. Questi elementi contengono<meta-data>
elementi secondari che forniscono dati specifici al sistema:-
L'elemento
<meta-data>
per il servizio dell'adattatore di sincronizzazione punta al file XMLres/xml/syncadapter.xml
. A sua volta, questo file specifica un URI per il servizio web che verrà sincronizzato con il provider di contatti e un tipo di account per il servizio web. -
(Facoltativo) L'elemento
<meta-data>
per l'autenticatore punta al file XMLres/xml/authenticator.xml
. A sua volta, questo file specifica il tipo di account supportato da questo autenticatore, nonché le risorse dell'interfaccia utente che vengono visualizzate durante il processo di autenticazione. Il tipo di account specificato in questo elemento deve essere lo stesso specificato per l'adattatore di sincronizzazione.
-
L'elemento
Dati dello stream social
Le tabelle android.provider.ContactsContract.StreamItems e android.provider.ContactsContract.StreamItemPhotos gestiscono i dati in entrata dai social network. Puoi scrivere un adattatore di sincronizzazione che aggiunga dati di stream dalla tua rete a queste tabelle oppure puoi leggere i dati di stream da queste tabelle e visualizzarli nella tua applicazione o entrambe le cose. Con queste funzionalità, i tuoi servizi e le tue applicazioni di social networking possono essere integrati nell'esperienza di social networking di Android.
Testo dello stream social
Gli elementi dello stream sono sempre associati a un contatto grezzo. android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID rimanda al valore _ID
per il contatto grezzo. Anche il tipo di account e il nome dell'account del contatto
non elaborato vengono memorizzati nella riga dell'elemento dello stream.
Archivia i dati del flusso nelle seguenti colonne:
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
- Obbligatorio. Il tipo di account dell'utente per il contatto grezzo associato a questo elemento dello stream. Ricorda di impostare questo valore quando inserisci un elemento dello stream.
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
- Obbligatorio. Il nome dell'account dell'utente per il contatto grezzo associato a questo elemento dello stream. Ricorda di impostare questo valore quando inserisci un elemento dello stream.
- Colonne identificatore
-
Obbligatorio. Devi inserire le seguenti colonne di identificatori quando
inserisci un elemento dello stream:
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: il valore android.provider.BaseColumns#_ID del contatto a cui è associato questo elemento dello stream.
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: il valore android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY del contatto a cui è associato questo elemento del flusso.
- android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: il valore android.provider.BaseColumns#_ID del contatto grezzo a cui è associato questo elemento dello stream.
- android.provider.ContactsContract.StreamItemsColumns#COMMENTS
- (Facoltativo) Memorizza le informazioni di riepilogo che puoi visualizzare all'inizio di un elemento dello stream.
- android.provider.ContactsContract.StreamItemsColumns#TEXT
-
Il testo dell'elemento dello stream, ovvero i contenuti pubblicati dalla fonte dell'elemento o una descrizione di un'azione che ha generato l'elemento dello stream. Questa colonna può contenere
qualsiasi formattazione e immagini di risorse incorporate che possono essere visualizzate da
fromHtml()
. Il provider potrebbe troncare o inserire i puntini di sospensione per i contenuti lunghi, ma cercherà di evitare di interrompere i tag. - android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
- Una stringa di testo contenente l'ora in cui l'elemento dello stream è stato inserito o aggiornato, sotto forma di millisecondi dall'epoca. Le applicazioni che inseriscono o aggiornano gli elementi del flusso sono responsabili della gestione di questa colonna, che non viene gestita automaticamente dal provider di contatti.
Per visualizzare le informazioni identificative degli elementi dello stream, utilizza android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL e android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE per collegarti alle risorse nella tua applicazione.
La tabella android.provider.ContactsContract.StreamItems contiene anche le colonne android.provider.ContactsContract.StreamItemsColumns#SYNC1 fino a android.provider.ContactsContract.StreamItemsColumns#SYNC4 per l'uso esclusivo degli adattatori di sincronizzazione.
Foto dello stream social
La tabella android.provider.ContactsContract.StreamItemPhotos memorizza le foto associate
a un elemento dello stream. La colonna
android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID della tabella
rimanda ai valori della colonna _ID
della tabella
android.provider.ContactsContract.StreamItems. I riferimenti alle foto sono archiviati nella
tabella in queste colonne:
- Colonna android.provider.ContactsContract.StreamItemPhotos#PHOTO (un BLOB).
- Una rappresentazione binaria della foto, ridimensionata dal fornitore per l'archiviazione e la visualizzazione. Questa colonna è disponibile per la compatibilità con le versioni precedenti del provider di contatti che la utilizzavano per archiviare le foto. Tuttavia, nella versione attuale non devi utilizzare questa colonna per archiviare le foto. Utilizza invece android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID o android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (entrambi descritti nei punti seguenti) per archiviare le foto in un file. Questa colonna ora contiene una miniatura della foto, disponibile per la lettura.
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
Un identificatore numerico di una foto per un contatto grezzo. Aggiungi questo valore alla costante
DisplayPhoto.CONTENT_URI
per ottenere un URI dei contenuti che punta a un singolo file di foto, quindi chiamaopenAssetFileDescriptor()
per ottenere un handle per il file di foto. - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
-
Un URI dei contenuti che punta direttamente al file della foto rappresentata da questa riga.
Chiama
openAssetFileDescriptor()
con questo URI per ottenere un handle per il file della foto.
Utilizzo delle tabelle del flusso social
Queste tabelle funzionano come le altre tabelle principali nel provider di contatti, tranne che:
- Queste tabelle richiedono autorizzazioni di accesso aggiuntive. Per leggerli, la tua applicazione deve disporre dell'autorizzazione android.Manifest.permission#READ_SOCIAL_STREAM. Per modificarli, la tua applicazione deve disporre dell'autorizzazione android.Manifest.permission#WRITE_SOCIAL_STREAM.
-
Per la tabella android.provider.ContactsContract.StreamItems, il numero di righe
memorizzate per ogni contatto grezzo è limitato. Una volta raggiunto questo limite,
il provider di contatti libera spazio per le nuove righe di elementi del flusso eliminando automaticamente
le righe con il valore android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP meno recente. Per ottenere il limite, esegui una query sull'URI dei contenuti android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. Puoi lasciare
tutti gli argomenti diversi dall'URI dei contenuti impostati su
null
. La query restituisce un cursore contenente una singola riga, con la singola colonna android.provider.ContactsContract.StreamItems#MAX_ITEMS.
La classe android.provider.ContactsContract.StreamItems.StreamItemPhotos definisce una sottotabella di android.provider.ContactsContract.StreamItemPhotos contenente le righe delle foto per un singolo elemento dello stream.
Interazioni con lo stream social
I dati del flusso social gestiti dal provider di contatti, insieme all'applicazione contatti del dispositivo, offrono un modo efficace per connettere il tuo sistema di social networking con i contatti esistenti. Sono disponibili le seguenti funzionalità:
- Se sincronizzi il tuo servizio di social networking con il provider di contatti tramite un adattatore di sincronizzazione, puoi recuperare l'attività recente per i contatti di un utente e memorizzarla nelle tabelle android.provider.ContactsContract.StreamItems e android.provider.ContactsContract.StreamItemPhotos per un utilizzo successivo.
- Oltre alla sincronizzazione regolare, puoi attivare l'adattatore di sincronizzazione per recuperare dati aggiuntivi quando l'utente seleziona un contatto da visualizzare. In questo modo, l'adattatore di sincronizzazione può recuperare le foto ad alta risoluzione e gli elementi dello stream più recenti per il contatto.
- Registrando una notifica con l'applicazione Contatti del dispositivo e il provider di contatti, puoi ricevere un intent quando viene visualizzato un contatto e, a quel punto, aggiornare lo stato del contatto dal tuo servizio. Questo approccio potrebbe essere più veloce e utilizzare meno larghezza di banda rispetto a una sincronizzazione completa con un adattatore di sincronizzazione.
- Gli utenti possono aggiungere un contatto al tuo servizio di social networking mentre lo visualizzano nell'applicazione Contatti del dispositivo. Puoi attivare questa funzionalità con l'opzione "Invita contatto", che si attiva con una combinazione di un'attività che aggiunge un contatto esistente alla tua rete e un file XML che fornisce l'applicazione Contatti del dispositivo e il provider di contatti con i dettagli della tua applicazione.
La sincronizzazione regolare degli elementi del flusso con il provider di contatti è uguale alle altre sincronizzazioni. Per scoprire di più sulla sincronizzazione, consulta la sezione Adattatori di sincronizzazione del provider di contatti. La registrazione delle notifiche e l'invito dei contatti sono trattati nelle due sezioni successive.
Registrazione per gestire le visualizzazioni dei social network
Per registrare l'adattatore di sincronizzazione in modo da ricevere notifiche quando l'utente visualizza un contatto gestito dall'adattatore di sincronizzazione:
-
Crea un file denominato
contacts.xml
nella directoryres/xml/
del progetto. Se hai già questo file, puoi saltare questo passaggio. -
In questo file, aggiungi l'elemento
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Se questo elemento esiste già, puoi saltare questo passaggio. -
Per registrare un servizio che riceve una notifica quando l'utente apre la pagina dei dettagli di un contatto nell'applicazione Contatti del dispositivo, aggiungi l'attributo
viewContactNotifyService="serviceclass"
all'elemento, doveserviceclass
è il nome della classe completo del servizio che deve ricevere l'intent dall'applicazione Contatti del dispositivo. Per il servizio di notifica, utilizza una classe che estendeIntentService
, per consentire al servizio di ricevere intent. I dati nell'intent in entrata contengono l'URI dei contenuti del contatto grezzo su cui l'utente ha fatto clic. Dal servizio di notifica, puoi eseguire il binding e poi chiamare l'adattatore di sincronizzazione per aggiornare i dati del contatto grezzo.
Per registrare un'attività da chiamare quando l'utente fa clic su un elemento dello stream, su una foto o su entrambi:
-
Crea un file denominato
contacts.xml
nella directoryres/xml/
del progetto. Se hai già questo file, puoi saltare questo passaggio. -
In questo file, aggiungi l'elemento
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Se questo elemento esiste già, puoi saltare questo passaggio. -
Per registrare una delle tue attività per gestire il clic dell'utente su un elemento dello stream nell'applicazione Contatti del dispositivo, aggiungi l'attributo
viewStreamItemActivity="activityclass"
all'elemento, doveactivityclass
è il nome della classe completo dell'attività che deve ricevere l'intent dall'applicazione Contatti del dispositivo. -
Per registrare una delle tue attività per gestire il clic dell'utente su una foto del flusso nell'applicazione Contatti del dispositivo, aggiungi l'attributo
viewStreamItemPhotoActivity="activityclass"
all'elemento, doveactivityclass
è il nome della classe completo dell'attività che deve ricevere l'intent dall'applicazione Contatti del dispositivo.
L'elemento <ContactsAccountType>
è descritto in modo più dettagliato nella
sezione Elemento <ContactsAccountType>.
L'intent in entrata contiene l'URI dei contenuti dell'elemento o della foto su cui ha fatto clic l'utente. Per avere attività separate per gli elementi di testo e per le foto, utilizza entrambi gli attributi nello stesso file.
Interagire con il tuo servizio di social networking
Gli utenti non devono uscire dall'applicazione Contatti del dispositivo per invitare un contatto sul tuo sito di social networking. In alternativa, puoi chiedere all'app Contatti del dispositivo di inviare un intent per invitare il contatto a una delle tue attività. Per configurare questa impostazione:
-
Crea un file denominato
contacts.xml
nella directoryres/xml/
del progetto. Se hai già questo file, puoi saltare questo passaggio. -
In questo file, aggiungi l'elemento
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Se questo elemento esiste già, puoi saltare questo passaggio. -
Aggiungi i seguenti attributi:
inviteContactActivity="activityclass"
-
inviteContactActionLabel="@string/invite_action_label"
activityclass
è il nome della classe completo dell'attività che deve ricevere l'intent. Il valoreinvite_action_label
è una stringa di testo visualizzata nel menu Aggiungi connessione nell'applicazione Contatti del dispositivo.
Nota:ContactsSource
è un nome tag deprecato per
ContactsAccountType
.
Riferimento contacts.xml
Il file contacts.xml
contiene elementi XML che controllano l'interazione dell'adattatore di sincronizzazione e dell'applicazione con l'applicazione Contatti e il provider di contatti. Questi
elementi sono descritti nelle sezioni seguenti.
Elemento <ContactsAccountType>
L'elemento <ContactsAccountType>
controlla l'interazione della tua
applicazione con l'applicazione Contatti. Ha la seguente sintassi:
<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">
contenuto in:
res/xml/contacts.xml
può contenere:
<ContactsDataKind>
Descrizione:
Dichiara i componenti Android e le etichette dell'interfaccia utente che consentono agli utenti di invitare uno dei loro contatti a un social network, di notificare agli utenti quando uno dei loro stream di social network viene aggiornato e così via.
Tieni presente che il prefisso dell'attributo android:
non è necessario per gli attributi di <ContactsAccountType>
.
Attributi:
inviteContactActivity
- Il nome della classe completo dell'attività nell'applicazione che vuoi attivare quando l'utente seleziona Aggiungi connessione dall'applicazione Contatti del dispositivo.
inviteContactActionLabel
-
Una stringa di testo visualizzata per l'attività specificata in
inviteContactActivity
, nel menu Aggiungi connessione. Ad esempio, puoi utilizzare la stringa "Segui nella mia rete". Puoi utilizzare un identificatore di risorsa stringa per questa etichetta. viewContactNotifyService
- Il nome della classe completo di un servizio nella tua applicazione che deve ricevere le notifiche quando l'utente visualizza un contatto. Questa notifica viene inviata dall'applicazione Contatti del dispositivo e consente alla tua applicazione di posticipare le operazioni che richiedono molti dati fino a quando non sono necessarie. Ad esempio, la tua applicazione può rispondere a questa notifica leggendo e visualizzando la foto ad alta risoluzione del contatto e gli elementi più recenti del flusso social. Questa funzionalità è descritta in modo più dettagliato nella sezione Interazioni con lo stream social.
viewGroupActivity
- Il nome completo della classe di un'attività nella tua applicazione che può visualizzare le informazioni sul gruppo. Quando l'utente fa clic sull'etichetta del gruppo nell'applicazione Contatti del dispositivo, viene visualizzata l'interfaccia utente per questa attività.
viewGroupActionLabel
-
L'etichetta che l'applicazione Contatti mostra per un controllo UI che consente
all'utente di esaminare i gruppi nella tua applicazione.
Per questo attributo è consentito un identificatore di risorsa stringa.
viewStreamItemActivity
- Il nome della classe completo di un'attività nella tua applicazione che l'applicazione Contatti del dispositivo avvia quando l'utente fa clic su un elemento dello stream per un contatto grezzo.
viewStreamItemPhotoActivity
- Il nome della classe completo di un'attività nella tua applicazione che l'applicazione contatti del dispositivo avvia quando l'utente fa clic su una foto nell'elemento dello stream per un contatto grezzo.
Elemento <ContactsDataKind>
L'elemento <ContactsDataKind>
controlla la visualizzazione delle righe di dati personalizzati dell'applicazione nell'interfaccia utente dell'applicazione Contatti. Ha la seguente sintassi:
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
contenuto in:
<ContactsAccountType>
Descrizione:
Utilizza questo elemento per fare in modo che l'applicazione Contatti mostri i contenuti di una riga di dati personalizzata come
parte dei dettagli di un contatto non elaborato. Ogni elemento secondario <ContactsDataKind>
di <ContactsAccountType>
rappresenta un tipo di riga di dati personalizzata che l'adattatore di sincronizzazione aggiunge alla tabella ContactsContract.Data
. Aggiungi un elemento
<ContactsDataKind>
per ogni tipo MIME personalizzato che utilizzi. Non devi
aggiungere l'elemento se hai una riga di dati personalizzata per cui non vuoi visualizzare i dati.
Attributi:
android:mimeType
-
Il tipo MIME personalizzato che hai definito per uno dei tipi di riga di dati personalizzati nella
tabella
ContactsContract.Data
. Ad esempio, il valorevnd.android.cursor.item/vnd.example.locationstatus
potrebbe essere un tipo MIME personalizzato per una riga di dati che registra l'ultima posizione nota di un contatto. android:icon
- Una risorsa disegnabile di Android che l'applicazione Contatti mostra accanto ai tuoi dati. Utilizza questo campo per indicare all'utente che i dati provengono dal tuo servizio.
android:summaryColumn
- Il nome della colonna per il primo dei due valori recuperati dalla riga di dati. Il valore viene visualizzato come prima riga della voce per questa riga di dati. La prima riga è pensata per essere utilizzata come riepilogo dei dati, ma è facoltativa. Vedi anche android:detailColumn.
android:detailColumn
-
Il nome della colonna per il secondo dei due valori recuperati dalla riga di dati. Il valore viene
visualizzato come seconda riga della voce per questa riga di dati. Vedi anche
android:summaryColumn
.
Funzionalità aggiuntive del provider di contatti
Oltre alle funzionalità principali descritte nelle sezioni precedenti, il provider di contatti offre queste utili funzionalità per lavorare con i dati dei contatti:
- Gruppi di contatti
- Funzionalità per le foto
Gruppi di contatti
Il provider di contatti può facoltativamente etichettare le raccolte di contatti correlati con
dati di gruppo. Se il server associato a un account utente
vuole gestire i gruppi, l'adattatore di sincronizzazione per il tipo di account deve trasferire
i dati dei gruppi tra il provider di contatti e il server. Quando gli utenti aggiungono un nuovo contatto al server e lo inseriscono in un nuovo gruppo, l'adattatore di sincronizzazione deve aggiungere il nuovo gruppo alla tabella ContactsContract.Groups
. Il gruppo o i gruppi a cui appartiene un contatto
non elaborato sono archiviati nella tabella ContactsContract.Data
, utilizzando
il tipo MIME ContactsContract.CommonDataKinds.GroupMembership
.
Se stai progettando un adattatore di sincronizzazione che aggiungerà dati di contatto non elaborati dal server al provider di contatti e non utilizzi i gruppi, devi indicare al provider di rendere visibili i tuoi dati. Nel codice eseguito quando un utente aggiunge un account
al dispositivo, aggiorna la riga ContactsContract.Settings
aggiunta dal provider di contatti per l'account. In questa riga, imposta il valore della colonna
Settings.UNGROUPED_VISIBLE
su 1. In questo modo, il provider di contatti renderà sempre
visibili i dati dei tuoi contatti, anche se non utilizzi i gruppi.
Foto contatti
La tabella ContactsContract.Data
memorizza le foto come righe con tipo MIME
Photo.CONTENT_ITEM_TYPE
. La colonna
CONTACT_ID
della riga è collegata alla
colonna _ID
del contatto non elaborato a cui appartiene.
La classe ContactsContract.Contacts.Photo
definisce una sottotabella di
ContactsContract.Contacts
contenente informazioni sulle foto per la foto principale di un contatto, ovvero la foto principale del contatto grezzo principale. Analogamente,
la classe ContactsContract.RawContacts.DisplayPhoto
definisce una sottotabella
di ContactsContract.RawContacts
contenente le informazioni sulle foto per una
foto principale di un contatto non elaborato.
La documentazione di riferimento per ContactsContract.Contacts.Photo
e
ContactsContract.RawContacts.DisplayPhoto
contiene esempi di
recupero delle informazioni sulle foto. Non esiste una classe di convenienza per recuperare la miniatura principale di un contatto grezzo, ma puoi inviare una query alla tabella ContactsContract.Data
, selezionando _ID
, Photo.CONTENT_ITEM_TYPE
e la colonna IS_PRIMARY
del contatto grezzo per trovare la riga della foto principale del contatto grezzo.
I dati dello stream sociale di una persona possono includere anche foto. Questi vengono archiviati nella tabella android.provider.ContactsContract.StreamItemPhotos, descritta in modo più dettagliato nella sezione Foto del flusso social.