Il fornitore di contatti è un componente Android potente e flessibile che gestisce il repository centrale del dispositivo con i dati sulle persone. Il fornitore di contatti è l'origine dei dati visualizzati nell'applicazione dei contatti del dispositivo; inoltre, puoi accedere ai relativi dati nella tua applicazione e trasferirli tra il dispositivo e i servizi online. Il provider accetta una vasta 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 provider include un ampio set di classi e interfacce di contratti che facilitano il recupero e la modifica dei dati.
La presente guida descrive quanto segue:
- La struttura di base del provider.
- Come recuperare i dati dal provider.
- Come modificare i dati nel provider.
- Come scrivere un adattatore di sincronizzazione per sincronizzare i dati dal tuo server al provider di contatti.
Questa guida presuppone che tu conosca le nozioni di base dei fornitori di contenuti Android. Per scoprire di più sui provider di contenuti Android, leggi la guida Nozioni di base sui fornitori di contenuti.
Organizzazione del provider di contatti
Il fornitore di contatti è un componente del fornitore di contenuti Android. Mantiene tre tipi di dati su una persona, ciascuno dei quali corrisponde a una tabella offerta dal provider, come illustrato nella Figura 1:
![](https://developer.android.com/static/images/providers/contacts_structure.png?authuser=9&hl=it)
Figura 1. Struttura della tabella del fornitore di contatti.
Le tre tabelle sono comunemente indicate con i nomi delle rispettive classi di contratto. Le classi definiscono le costanti per gli URI dei contenuti, i nomi delle colonne e i valori delle colonne utilizzati dalle tabelle:
-
ContactsContract.Contacts
tabella - Righe che rappresentano persone diverse, in base ad aggregazioni di righe di contatto non elaborate.
-
ContactsContract.RawContacts
tabella - Righe contenenti un riepilogo dei dati di una persona, specifici per un account utente e un tipo di account.
-
ContactsContract.Data
tabella - Righe contenenti i dettagli del contatto non elaborato, come indirizzi email o numeri di telefono.
Le altre tabelle rappresentate da classi di contratto in ContactsContract
sono tabelle ausiliarie che il provider di contatti utilizza per gestire le proprie operazioni o supportare funzioni specifiche nei contatti o nelle applicazioni di telefonia del dispositivo.
Contatti non elaborati
Un contatto non elaborato rappresenta i dati di una persona provenienti da un singolo tipo di account e nome account. Poiché il fornitore di contatti consente più di un servizio online come origine dei dati per una persona, il fornitore di contatti consente più contatti non elaborati per la stessa persona. L'utilizzo di più contatti non elaborati consente inoltre a un utente di combinare i dati di una persona provenienti da più account dello stesso tipo.
La maggior parte dei dati per un contatto non elaborato non è archiviata nella tabella ContactsContract.RawContacts
. Viene invece archiviato 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 non elaborati
Le colonne importanti nella tabella ContactsContract.RawContacts
sono elencate nella tabella 1. Leggi le note che seguono dopo la tabella:
Tabella 1. Colonne importanti dei contatti non elaborati.
Nome colonna | Usa | Notes |
---|---|---|
ACCOUNT_NAME
|
Il nome dell'account per il tipo di account che è l'origine di questo contatto non elaborato.
Ad esempio, il nome di un Account Google è uno degli indirizzi Gmail
del proprietario del dispositivo. Per saperne di più, consulta la voce successiva relativa a
ACCOUNT_TYPE .
|
Il formato di questo nome è specifico per il suo tipo di account. Non si tratta necessariamente di un indirizzo email. |
ACCOUNT_TYPE
|
Il tipo di account da cui proviene il contatto non elaborato. 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 che controlli. In questo modo il tuo
tipo di account sarà univoco.
|
Un tipo di account che offre dati dei contatti in genere ha un adattatore di sincronizzazione associato che si sincronizza con il fornitore di contatti. |
DELETED
|
Il flag "deleted" di un contatto non elaborato. | Questo flag consente al provider di contatti di mantenere la riga internamente fino a quando gli adattatori di sincronizzazione non sono in grado di eliminarla dai server per poi eliminarla dal repository. |
Notes
Di seguito sono riportate note importanti sulla tabella ContactsContract.RawContacts
:
-
Il nome di un contatto non elaborato non è archiviato nella relativa riga in
ContactsContract.RawContacts
. Viene invece archiviato nella tabellaContactsContract.Data
, in una rigaContactsContract.CommonDataKinds.StructuredName
. Un contatto non elaborato ha una sola riga di questo tipo nella tabellaContactsContract.Data
. -
Attenzione: per utilizzare i dati del tuo account in una riga di contatto non elaborata, devi prima registrarli con
AccountManager
. Per farlo, chiedi agli utenti di aggiungere il tipo di account e il nome del loro account all'elenco degli account. Se non lo fai, il fornitore di contatti eliminerà automaticamente la riga di contatto non elaborata.Ad esempio, se vuoi che la tua app conservi i dati dei contatti per il servizio basato sul web con il dominio
com.example.dataservice
e l'account dell'utente per il tuo servizio èbecky.sharp@dataservice.example.com
, l'utente deve aggiungere il "tipo" (com.example.dataservice
) e il "nome" dell'account (becky.smart@dataservice.example.com
) prima che l'app possa aggiungere righe di contatti non elaborate. Puoi spiegare questo requisito all'utente nella documentazione o chiedere all'utente di aggiungere il tipo e il nome, o entrambi. I tipi e i nomi degli account sono descritti più dettagliatamente nella prossima sezione.
Origini dei dati non elaborati dei contatti
Per capire come funzionano i contatti non elaborati, considera l'utente "Emily Dickinson", il cui dispositivo ha i seguenti tre account utente:
emily.dickinson@gmail.com
emilyd@gmail.com
- Account Twitter "belle_of_amherst"
Questo utente ha abilitato la sincronizzazione dei contatti per tutti e tre questi 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". Più tardi accede a Gmail come emilyd@gmail.com
e invia un'email a "Thomas Higginson", che lo aggiunge automaticamente come contatto. Segue anche "colonel_tom" (ID Twitter di Thomas Higginson) su Twitter.
Il fornitore di contatti crea tre contatti non elaborati a seguito di 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 non elaborato anche se il nome è identico al nome precedente, perché la persona è stata aggiunta per un account utente diverso. - Un terzo contatto non elaborato per "Thomas Higginson" associato a "belle_of_amherst". Il tipo di account utente è Twitter.
Dati
Come indicato in precedenza, i dati di un contatto non elaborato vengono archiviati in una riga ContactsContract.Data
collegata al valore _ID
del contatto non elaborato. In questo modo, un singolo contatto non elaborato può avere più istanze dello stesso tipo di dati, come indirizzi email o numeri di telefono. Ad esempio, se "Thomas Higginson" per emilyd@gmail.com
(la riga di contatto non elaborata per Thomas Higginson associata all'Account Google emilyd@gmail.com
) ha un indirizzo email di casa thigg@gmail.com
e un indirizzo email di lavoro thomas.higginson@gmail.com
, il provider di contatti memorizza le due righe di indirizzi email e le collega entrambe al contatto non elaborato.
Tieni presente che in questa singola tabella sono archiviati diversi tipi di dati. Le righe Nome visualizzato,
numero di telefono, email, indirizzo postale, foto e dettagli del sito web si trovano tutte nella
tabella ContactsContract.Data
. Per facilitare questa operazione, 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 descrittivi delle colonne
Ecco alcuni esempi di nomi di colonna descrittivi:
-
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 compatibile con il provider di contatti. -
IS_PRIMARY
-
Se questo tipo di riga di dati può verificarsi più di una volta per un contatto non elaborato, la colonna
IS_PRIMARY
segnala la riga di dati che contiene i dati principali per il tipo. Ad esempio, se l'utente esercita una pressione prolungata su un numero di telefono per un contatto e seleziona Imposta predefinito, la rigaContactsContract.Data
contenente quel numero avrà la colonnaIS_PRIMARY
impostata su un valore diverso da zero.
Nomi di colonna generici
Sono disponibili 15 colonne generiche, denominate da DATA1
a DATA15
, in disponibilità generale e altre quattro colonne generiche da SYNC1
a SYNC4
che devono essere utilizzate solo dagli adattatori di sincronizzazione. Le costanti generiche dei nomi di colonna funzionano sempre, indipendentemente dal tipo di dati contenuti nella riga.
La colonna DATA1
è indicizzata. Il fornitore 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 email, questa colonna contiene l'indirizzo email effettivo.
Per convenzione, la colonna DATA15
è riservata all'archiviazione di dati BLOB (Binary Large Object) come le miniature delle foto.
Nomi di colonna specifici per il tipo
Per semplificare l'utilizzo delle colonne per un determinato tipo di riga, il provider di contatti
fornisce anche costanti dei nomi delle colonne specifiche del tipo, definite nelle sottoclassi ContactsContract.CommonDataKinds
. Le costanti assegnano semplicemente un
nome diverso alla costante allo stesso nome della colonna, il che ti aiuta ad accedere ai dati di una riga di
un determinato tipo.
Ad esempio, la classe ContactsContract.CommonDataKinds.Email
definisce le costanti dei nomi di colonna specifiche del tipo per una riga ContactsContract.Data
con il tipo MIME Email.CONTENT_ITEM_TYPE
. Il corso contiene la costante ADDRESS
per la colonna dell'indirizzo email. Il valore effettivo di ADDRESS
è "data1", che corrisponde al nome generico della colonna.
Attenzione: non aggiungere dati personalizzati alla tabella ContactsContract.Data
utilizzando una riga che abbia uno dei tipi MIME predefiniti del provider. Se lo fai, 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 nomi di colonna specifici per il tipo e utilizzare le colonne come preferisci.
La figura 2 mostra in che modo le colonne descrittive e le colonne di dati vengono visualizzate in una riga ContactsContract.Data
e in che modo i nomi di colonna specifici per tipo "sovrappongono" i nomi generici delle colonne
![Modalità di mappatura dei nomi di colonna specifici del tipo a nomi di colonna generici](https://developer.android.com/static/images/providers/data_columns.png?authuser=9&hl=it)
Figura 2. Digitare nomi di colonna specifici e generici.
Classi di nomi di colonne specifiche per tipo
La tabella 2 elenca le classi dei nomi di colonna specifiche per il tipo più utilizzate:
Tabella 2. Classi di nomi di colonne specifiche per tipo
Classe di mappatura | Tipo di dati | Notes |
---|---|---|
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 non elaborato associato a questa riga di dati. | Un contatto non elaborato può avere più indirizzi email. |
ContactsContract.CommonDataKinds.StructuredPostal |
Un indirizzo postale per il contatto non elaborato associato a questa riga di dati. | Un contatto non elaborato può avere più indirizzi postali. |
ContactsContract.CommonDataKinds.GroupMembership |
Un identificatore che collega il contatto non elaborato a uno dei gruppi nel fornitore di contatti. | I gruppi sono una funzionalità facoltativa di un tipo di account e del nome dell'account. Sono descritti in maggiore dettaglio nella sezione Gruppi di contatti. |
Contatti
Il fornitore di contatti combina le righe di contatto non elaborate di 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 fornitore di contatti gestisce la creazione di nuove righe dei contatti e l'aggregazione dei contatti non elaborati con una riga dei contatti esistente. Né le applicazioni né gli adattatori di sincronizzazione sono autorizzati ad aggiungere contatti e alcune colonne in una riga dei contatti sono di sola lettura.
Nota: se tenti di aggiungere un contatto al fornitore di contatti con un
insert()
, riceverai
un'eccezione UnsupportedOperationException
. Se provi ad aggiornare una colonna
indicata come di sola lettura, l'aggiornamento viene ignorato.
Il fornitore di contatti crea un nuovo contatto in risposta all'aggiunta di un nuovo contatto non elaborato che non corrisponde ad alcun contatto esistente. Il provider esegue questa operazione anche se i dati di un contatto non elaborato esistente cambiano in modo tale da non corrispondere più al contatto a cui era stato precedentemente associato. Se un'applicazione o un adattatore di sincronizzazione crea un nuovo contatto non elaborato che corrisponde a un contatto esistente, il nuovo contatto non elaborato viene aggregato al contatto esistente.
Il fornitore di contatti collega una riga di contatto alle sue righe di contatto non elaborate con la colonna _ID
della riga di contatto nella tabella Contacts
. La colonna CONTACT_ID
della tabella dei contatti non elaborati
ContactsContract.RawContacts
contiene valori _ID
per
la riga di contatti associata a ogni riga di contatti non elaborati.
La tabella ContactsContract.Contacts
contiene anche la colonna
LOOKUP_KEY
che rappresenta un
link "permanente" alla riga del contatto. Poiché il fornitore di contatti gestisce i contatti
automaticamente, potrebbe modificare il valore _ID
di una riga di contatto
in risposta a un'aggregazione o una sincronizzazione. Anche in questo caso, l'URI dei contenuti CONTENT_LOOKUP_URI
combinato con il LOOKUP_KEY
del contatto rimanderà comunque 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 a quello della colonna _ID
.
La Figura 3 mostra la correlazione tra le tre tabelle principali.
![Tabelle principali del fornitore di contatti](https://developer.android.com/static/images/providers/contacts_tables.png?authuser=9&hl=it)
Figura 3. Relazioni della tabella Contatti, Contatti non elaborati e Dettagli.
Attenzione: se pubblichi l'app sul Google Play Store o se l'app si trova su un dispositivo con Android 10 (livello API 29) o versioni successive, tieni presente che un insieme limitato di metodi e campi di dati dei contatti è obsoleto.
Alle 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 riportati sono obsolete:
Inoltre, i seguenti campi non restituiscono più i contatti frequenti. Tieni presente che alcuni di questi campi influenzano i 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
(interessa solo i tipi di dati Email, Telefono, Chiamabile e Contattabili) -
ENTERPRISE_CONTENT_FILTER_URI
(interessa solo i tipi di dati Email, Telefono e Chiamabili)
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 tuoi sistemi di backend.
Per verificare che la funzionalità dell'app non sia interessata da questa modifica, puoi cancellare manualmente questi campi di dati. Per farlo, esegui il comando ADB seguente 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 passano anche al provider di contatti dai servizi web tramite adattatore di sincronizzazione, che automatizza 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 Fonti dei dati non elaborati dei contatti. Le seguenti definizioni offrono maggiori dettagli e descrivono la relazione tra tipo e nome di account e adattatori e servizi di sincronizzazione.
- Tipo di conto
-
Identifica un servizio in cui l'utente ha archiviato 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
. - Il nome dell'account
- Identifica un determinato account o login per un tipo di account. Gli account Contatti Google 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 Contatti Google e scaricare i propri dati nel fornitore di contatti; questo può accadere se l'utente ha un insieme di contatti personali associati al nome di un account personale e un altro impostato per il lavoro. In genere, i nomi degli account sono univoci. Insieme, identificano un flusso di dati specifico tra il provider di contatti e un servizio esterno.
Per trasferire i dati del servizio al provider di contatti, devi scrivere un tuo adattatore di sincronizzazione. Questa procedura è descritta in maggiore dettaglio nella sezione Adattatori di sincronizzazione del fornitore di contatti.
La Figura 4 mostra in che modo il fornitore di contatti si inserisce nel flusso di dati sulle persone. Nella casella "adattatore di sincronizzazione", ogni adattatore è etichettato in base al tipo di account.
![Flusso di dati sulle persone](https://developer.android.com/static/images/providers/ContactsDataFlow.png?authuser=9&hl=it)
Figura 4. Il flusso di dati del fornitore di contatti.
Autorizzazioni richieste
Le applicazioni che desiderano 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 autorizzazioni richieste sono discussi nella sezione seguente, Il profilo utente.
Ricorda che i dati dei contatti dell'utente sono personali e sensibili. Gli utenti sono preoccupati per la loro privacy, pertanto non vogliono che le applicazioni raccolgano dati su di loro o sui loro contatti. Se il motivo per cui hai bisogno dell'autorizzazione per accedere ai dati dei suoi contatti non è chiaro, l'app potrebbe dare valutazioni basse o semplicemente rifiutarsi di installarla.
Il profilo utente
La tabella ContactsContract.Contacts
ha una singola 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 dei contatti non elaborata per ogni sistema che utilizza un profilo.
Ogni riga di contatto non elaborata del profilo può avere più righe di dati. Le costanti per l'accesso al profilo utente sono disponibili nel corso ContactsContract.Profile
.
L'accesso al profilo utente richiede autorizzazioni speciali. Oltre alle autorizzazioni READ_CONTACTS
e WRITE_CONTACTS
necessarie per la lettura e la scrittura, l'accesso al profilo utente richiede rispettivamente le autorizzazioni android.Manifest.permission#READ_PROFILE e android.Manifest.permission#WRITE_PROFILE per l'accesso in lettura e scrittura.
Ricorda che il profilo di un utente deve essere considerato sensibile. L'autorizzazione android.Manifest.permission#READ_PROFILE ti consente di accedere ai dati che consentono l'identificazione personale dell'utente del dispositivo. Assicurati di spiegare 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 del contenuto su
CONTENT_URI
e non fornire alcun
criterio di selezione. Puoi anche utilizzare questo URI di contenuti come URI di base per recuperare contatti o 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 dei contatti e vuoi determinare se una di queste è il profilo utente, verifica la colonna IS_USER_PROFILE
della riga. Questa colonna è impostata su "1" se il contatto è il profilo utente.
Metadati del fornitore 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 Contatti, Dati e Contatti non elaborati,
la tabella ContactsContract.Settings
e la
tabella ContactsContract.SyncState
. La seguente tabella mostra l'effetto di ciascuno di questi metadati:
Tabella 3. Metadati nel provider di contatti
Tabella | Colonna | Valori | Significato |
---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
"0" - non modificato dall'ultima sincronizzazione. |
Vengono contrassegnati i contatti non elaborati che sono stati modificati sul dispositivo e che devono essere sincronizzati di nuovo con il server. Il valore viene impostato automaticamente dal provider di contatti quando le app
per Android aggiornano una riga.
Gli adattatori di sincronizzazione che modificano i contatti o le tabelle di dati non elaborati devono sempre aggiungere la stringa |
"1" - modificato dall'ultima sincronizzazione, deve essere sincronizzato nuovamente con il server. | |||
ContactsContract.RawContacts |
VERSION |
Il numero di versione di questa riga. | Il fornitore di contatti incrementa automaticamente questo valore ogni volta che la riga o i dati correlati vengono modificati. |
ContactsContract.Data |
DATA_VERSION |
Il numero di versione di questa riga. | Il fornitore 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 non elaborato nell'account in cui è stato creato. |
Quando un adattatore di sincronizzazione crea un nuovo contatto non elaborato, questa colonna deve essere impostata sull'ID univoco del server per il contatto non elaborato. Quando un'applicazione Android crea un nuovo contatto non elaborato, la colonna dell'applicazione dovrebbe lasciare vuota. Questo indica all'adattatore di sincronizzazione che deve creare un nuovo contatto non elaborato sul server e ottenere un valore per SOURCE_ID .
In particolare, l'ID origine deve essere univoco per ogni tipo di account e deve essere stabile nelle varie sincronizzazioni:
|
ContactsContract.Groups |
GROUP_VISIBLE |
"0" - I contatti in questo gruppo non dovrebbero essere visibili nelle UI delle applicazioni Android. | Questa colonna consente di verificare la compatibilità con i server che consentono a un utente di nascondere i contatti in determinati gruppi. |
"1" - I contatti in questo gruppo possono essere visibili nelle UI dell'applicazione. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
"0" - Per questo account e tipo di account, i contatti che non appartengono a un gruppo non sono visibili alle UI delle applicazioni Android. |
Per impostazione predefinita, i contatti sono invisibili se nessuno dei loro contatti non elaborati appartiene a un gruppo (l'appartenenza al gruppo per un contatto non elaborato è 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 visualizzazione dei contatti senza gruppi.
Questo flag viene utilizzato per mostrare i contatti dai server che non utilizzano gruppi.
|
"1" - Per questo account e tipo di account, i contatti che non appartengono a un gruppo sono visibili nelle UI dell'applicazione. | |||
ContactsContract.SyncState |
(tutti) | Utilizza questa tabella per archiviare i metadati per l'adattatore di sincronizzazione. | In questa tabella puoi archiviare lo stato della sincronizzazione e altri dati relativi alla sincronizzazione in modo permanente sul dispositivo. |
Accesso del fornitore di contatti
Questa sezione descrive le linee guida per accedere ai dati dal fornitore di contatti, con particolare attenzione ai seguenti aspetti:
- Query sulle entità.
- Modifica in batch.
- Recupero e modifica con intenti.
- Integrità dei dati.
Anche le modifiche da un adattatore di sincronizzazione vengono trattate in maggiore dettaglio nella sezione Adattatori di sincronizzazione del fornitore di contatti.
Esecuzione di query su entità
Poiché le tabelle del fornitore di contatti sono organizzate in una gerarchia, spesso è utile recuperare una riga e tutte le righe "figlio" ad essa collegate. Ad esempio, per visualizzare
tutte le informazioni relative a una persona, ti consigliamo di recuperare tutte le
ContactsContract.RawContacts
righe per una singola
riga ContactsContract.Contacts
o tutte le
ContactsContract.CommonDataKinds.Email
righe per una singola
riga ContactsContract.RawContacts
. Per facilitare questa operazione, il provider di contatti offre costrutti entity, che fungono da join di database tra tabelle.
Un'entità è come una tabella composta da colonne selezionate da una tabella principale e dalla relativa tabella figlio.
Quando esegui una query su un'entità, fornisci criteri di proiezione e ricerca basati sulle colonne disponibili dall'entità. Il risultato è un Cursor
che contiene una riga per ogni riga della tabella figlio recuperata. Ad esempio, se esegui una query su ContactsContract.Contacts.Entity
per un nome di contatto e su tutte le righe ContactsContract.CommonDataKinds.Email
per tutti i contatti non elaborati per quel nome, verrà restituito un Cursor
contenente una riga per ogni riga ContactsContract.CommonDataKinds.Email
.
Le entità semplificano le query. Utilizzando un'entità, puoi recuperare contemporaneamente tutti i dati dei contatti per un contatto o un contatto non elaborato, anziché dover prima eseguire una query sulla tabella padre per ottenere un ID, dopodiché eseguire una query sulla tabella figlio con quell'ID. Inoltre, il fornitore di contatti elabora una query su un'entità in un'unica transazione, assicurando che i dati recuperati siano coerenti internamente.
Nota:un'entità di solito non contiene tutte le colonne delle tabelle padre e di quelle figlio. Se tenti di lavorare con un nome di colonna che non è nell'elenco delle costanti dei nomi di colonna per l'entità, otterrai un Exception
.
Il seguente snippet mostra come recuperare tutte le righe di contatto non elaborate per un contatto. Lo snippet fa parte di un'applicazione più grande che ha due attività, "main" e "detail". L'attività principale mostra un elenco di righe di contatti; quando l'utente ne seleziona una, l'attività invia il relativo ID all'attività dei dettagli. L'attività di dettaglio utilizza l'ContactsContract.Contacts.Entity
per visualizzare tutte le righe di dati di tutti i contatti non elaborati associati al contatto
selezionato.
Questo snippet proviene dall'attività "detail":
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 per questo metodo è un
Cursor
con i risultati della query. Nella tua app, puoi recuperare i dati di questo Cursor
per visualizzarli o continuare a utilizzarli.
Modifica batch
Quando possibile, devi inserire, aggiornare ed eliminare i dati nel provider di contatti in
"modalità batch", creando un ArrayList
di
ContentProviderOperation
oggetti e chiamando
applyBatch()
. Poiché
il provider di contatti esegue tutte le operazioni in un
applyBatch()
in una singola
transazione, le tue modifiche non lasceranno mai il repository dei contatti in uno stato
incoerente. Una modifica batch facilita inoltre l'inserimento di un contatto non elaborato e dei relativi dati dettagliati contemporaneamente.
Nota: per modificare un singolo contatto non elaborato, valuta la possibilità di inviare un intent all'applicazione dei contatti del dispositivo anziché gestire la modifica nell'app. Questa operazione è descritta più dettagliatamente nella sezione Recupero e modifica con intent.
Punti di rendimento
Una modifica batch contenente un numero elevato di operazioni può bloccare altri processi,
generando un'esperienza utente complessiva negativa. Per organizzare tutte le modifiche che vuoi eseguire nel minor numero possibile di elenchi separati e allo stesso tempo evitare 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 rileva un punto di rendimento, mette in pausa il lavoro per
consentire l'esecuzione di altri processi e chiude la transazione in corso. Quando il provider viene riavviato, continua con l'operazione successiva in ArrayList
e avvia una nuova transazione.
Tuttavia, i punti di rendimento comportano più di una transazione per chiamata a
applyBatch()
. Per questo motivo, devi impostare un punto di rendimento per l'ultima operazione relativa a un insieme di righe correlate.
Ad esempio, devi impostare un punto di rendimento per l'ultima operazione in un set che aggiunge righe di contatto non elaborate e relative righe di dati associate o l'ultima operazione per un insieme di righe relative a un singolo contatto.
I punti di snervamento sono anche un'unità dell'operazione atomica. Tutti gli accessi tra due punti di rendimento avranno esito positivo o negativo come singola unità. Se non imposti alcun punto di rendimento, l'operazione atomica più piccola è l'intero batch di operazioni. Se utilizzi i punti di rendimento, impedisci alle operazioni di ridurre le prestazioni del sistema, garantendo al contempo che un sottoinsieme di operazioni sia atomico.
Riferimenti a ritroso della modifica
Quando inserisci una nuova riga di contatto non elaborata e le relative righe di dati associate come insieme di oggetti ContentProviderOperation
, devi collegare le righe di dati alla riga di contatto non elaborata inserendo il valore _ID
del contatto non elaborato 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 del contatto non elaborata. Per risolvere questo problema,
la classe ContentProviderOperation.Builder
ha il metodo
withValueBackReference()
.
Questo metodo 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 in base 0 di un valore nell'array di
oggetti
ContentProviderResult
diapplyBatch()
. Quando vengono applicate le operazioni batch, il risultato di ogni operazione viene archiviato in un array intermedio di risultati. Il valorepreviousResult
è l'indice di uno di questi risultati, che viene recuperato e archiviato con il valorekey
. In questo modo puoi inserire un nuovo record di contatto non elaborato e recuperare il relativo valore_ID
, quindi creare un "backriferimento" al valore quando aggiungi una rigaContactsContract.Data
.L'intero array risultato viene creato quando chiami per la prima volta
applyBatch()
, con una dimensione uguale a quella dell'oggettoArrayList
degliContentProviderOperation
oggetti che fornisci. Tuttavia, tutti gli elementi nell'array di risultati sono impostati sunull
e, se provi a eseguire un back-riferimento a un risultato per un'operazione non ancora applicata,withValueBackReference()
genera unException
.
I seguenti snippet mostrano come inserire un nuovo contatto non elaborato e dei dati in batch. Includono il codice che stabilisce un punto di rendimento e utilizza un backreference.
Il primo snippet recupera i dati dei contatti dall'interfaccia utente. A questo punto, l'utente ha già selezionato l'account per il quale deve essere aggiunto il nuovo contatto non elaborato.
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());
Lo snippet successivo crea un'operazione per inserire la riga di contatto non elaborata 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 le righe del nome visualizzato, del numero di telefono e dell'email.
Ogni oggetto del builder di operazioni utilizza withValueBackReference()
per ottenere RAW_CONTACT_ID
. Il riferimento torna all'oggetto ContentProviderResult
dalla prima operazione, che aggiunge la riga di contatto non elaborata e restituisce il nuovo valore _ID
. Di conseguenza, ogni riga di dati viene collegata automaticamente dalla relativa
RAW_CONTACT_ID
alla nuova riga ContactsContract.RawContacts
a cui appartiene.
L'oggetto ContentProviderOperation.Builder
che aggiunge la riga 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 le nuove righe di dati e contatti non elaborati.
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 consentono inoltre di implementare il controllo ottimistico della contemporaneità, un metodo per applicare transazioni di modifica senza dover bloccare il repository sottostante. Per utilizzare questo metodo, applica la transazione e controlla se sono state apportate altre modifiche contemporaneamente. Se noti una modifica incoerente, esegui il rollback della transazione e riprova.
Il controllo ottimistico della contemporaneità è utile per i dispositivi mobili che prevedono la presenza di un solo utente alla volta e gli accessi simultanei a un repository di dati sono rari. Poiché non viene usato il blocco, non perdete tempo per impostare i blocchi o attendere che altre transazioni rilascino i blocchi.
Per utilizzare il controllo ottimistico della contemporaneità 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
idoneo per l'applicazione di un vincolo, utilizzando il metodonewAssertQuery(Uri)
. Per l'URI del contenuto, utilizzaRawContacts.CONTENT_URI
con l'aggiunta del valore_ID
del contatto non elaborato. -
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 solo una riga venga testata da questa asserzione. -
Richiama
build()
per creare l'oggettoContentProviderOperation
, quindi aggiungi questo oggetto come primo oggetto nelArrayList
che passi aapplyBatch()
. - Applica la transazione batch.
Se la riga di contatto non elaborata viene aggiornata da un'altra operazione tra il momento in cui leggi la riga e il momento in cui tenti di modificarla, l'istruzione ContentProviderOperation
non andrà a buon fine e verrà eseguito il backup dell'intero batch di operazioni. Puoi quindi scegliere di riprovare il batch o eseguire un'altra azione.
Lo snippet seguente mostra come creare un'"assert"
ContentProviderOperation
dopo aver eseguito una query per un singolo contatto non elaborato 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 l'intento
L'invio di un intent all'applicazione dei contatti del dispositivo consente di accedere indirettamente al provider di contatti. L'intent avvia l'interfaccia utente dell'applicazione per i contatti del dispositivo, in cui gli utenti possono svolgere le operazioni relative ai contatti. Con questo tipo di accesso, gli utenti possono:
- Scegli un contatto da un elenco e fai in modo che venga restituito alla tua app per ulteriore lavoro.
- Modificare i dati di un contatto esistente.
- Inserisci un nuovo contatto non elaborato per uno dei loro account.
- Eliminare i dati di un contatto o dei contatti.
Se l'utente inserisce o aggiorna dati, puoi prima raccogliere i dati e inviarli come parte dell'intent.
Quando utilizzi gli intent per accedere al provider di contatti tramite l'applicazione per i contatti del dispositivo, non devi scrivere una UI o un codice personalizzati per accedere al provider. Inoltre, non è necessario richiedere l'autorizzazione di lettura o scrittura al provider. L'applicazione Contatti del dispositivo può delegare a te l'autorizzazione di lettura di un contatto e, poiché stai apportando modifiche al provider tramite un'altra applicazione, non devi disporre delle autorizzazioni di scrittura.
Il processo generale di invio di un intent per accedere a un provider è descritto in dettaglio nella guida
Nozioni di base sui fornitori 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 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 | Notes |
---|---|---|---|---|
Scegliere un contatto da un elenco | ACTION_PICK |
Uno dei seguenti:
|
Not used |
Visualizza un elenco di contatti non elaborati o un elenco di dati di un contatto non elaborato, a seconda del tipo di URI del contenuto fornito.
Richiama
|
Inserire un nuovo contatto non elaborato | Insert.ACTION |
N/A |
RawContacts.CONTENT_TYPE , tipo MIME per un insieme di contatti non elaborati.
|
Mostra la schermata Aggiungi contatto dell'applicazione Contatti del dispositivo. Vengono visualizzati i valori 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() dell'attività nell'argomento Intent , nel campo "dati". Per ottenere il valore, chiama getData() .
|
Modificare un contatto | ACTION_EDIT |
CONTENT_LOOKUP_URI per il contatto. L'attività di editor consentirà all'utente di modificare tutti i dati associati a questo contatto.
|
Contacts.CONTENT_ITEM_TYPE , un unico contatto. |
Visualizza la schermata Modifica contatto nell'applicazione dei contatti. I valori extra che aggiungi all'intent vengono visualizzati. Quando l'utente fa clic su Fine per salvare le modifiche, l'attività torna in primo piano. |
Mostra un selettore che possa anche aggiungere dati. | ACTION_INSERT_OR_EDIT |
N/A |
CONTENT_ITEM_TYPE
|
Questo intent mostra sempre la schermata di selezione 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 aggiuntivi trasmessi nell'intent. Se la tua app mostra dati di contatto come un indirizzo 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 del nome 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 lo rileva e salva la modifica, il valore precedente andrà perso. |
L'app Contatti del dispositivo non ti consente di eliminare un contatto non elaborato o i suoi dati con un intent. Per eliminare un contatto non elaborato, utilizza
ContentResolver.delete()
o ContentProviderOperation.newDelete()
.
Il seguente snippet 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 di essere corretti e aggiornati, il provider di contatti ha regole ben definite per l'integrità dei dati. È tua responsabilità conformarti a 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
potrebbe causare problemi durante l'aggregazione. -
Collega sempre le nuove righe
ContactsContract.Data
alla rigaContactsContract.RawContacts
principale. -
Una riga
ContactsContract.Data
non collegata aContactsContract.RawContacts
non sarà visibile nell'applicazione Contatti del dispositivo e potrebbe causare problemi con gli adattatori di sincronizzazione. - Modificare solo i dati dei contatti non elaborati di tua proprietà.
- Ricorda che il fornitore di contatti di solito gestisce i dati di diversi tipi di account/servizi online. Devi assicurarti che l'applicazione modifichi o elimini solo i dati delle righe che ti appartengono e che inserisca solo i dati con un tipo di account e un nome controllati da te.
-
Utilizza sempre le costanti definite in
ContactsContract
e nelle relative sottoclassi per autorità, URI dei contenuti, percorsi URI, nomi colonna, tipi MIME e valoriTYPE
. - L'uso di queste costanti ti consente di evitare errori. Inoltre, se una delle costanti è deprecata, riceverai una notifica con avvisi del compilatore.
Righe di dati personalizzate
Creando e utilizzando 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 nomi di colonna
specifici del tipo a quelli predefiniti. Nell'applicazione dei contatti del dispositivo,
i dati delle righe vengono visualizzati, ma non possono essere modificati o eliminati e gli utenti non possono aggiungere
ulteriori dati. Per consentire agli utenti di modificare le righe di dati personalizzate, devi fornire un'attività di editor nella tua applicazione.
Per visualizzare i dati personalizzati, fornisci un file contacts.xml
contenente un elemento <ContactsAccountType>
e uno o più dei relativi elementi secondari <ContactsDataKind>
. Ciò è descritto più dettagliatamente nella
sezione <ContactsDataKind> element
.
Per saperne di più sui tipi MIME personalizzati, leggi la guida Creare un fornitore di contenuti.
Adattatori di sincronizzazione del provider di contatti
Il fornitore 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 caricare quelli esistenti in un nuovo account. La sincronizzazione garantisce inoltre che gli utenti abbiano a portata di mano i dati più recenti, indipendentemente dall'origine delle aggiunte e delle modifiche. Un altro vantaggio della sincronizzazione è che rende disponibili i dati dei contatti anche quando il dispositivo non è connesso alla rete.
Sebbene sia possibile implementare la sincronizzazione in diversi modi, il sistema Android fornisce un framework di sincronizzazione dei plug-in che automatizza le seguenti attività:
- Controllo della disponibilità della rete in corso.
- Pianificazione ed esecuzione della sincronizzazione in base alle preferenze dell'utente.
- Riavvio delle sincronizzazioni interrotte.
Per utilizzare questo framework, devi fornire un plug-in per l'adattatore di sincronizzazione. Ogni adattatore di sincronizzazione è univoco per un fornitore di servizi e contenuti, ma può gestire più nomi di account per lo stesso servizio. Il framework consente inoltre più adattatori di sincronizzazione per lo stesso servizio e provider.
File e classi di adattatori di sincronizzazione
Puoi implementare un adattatore di sincronizzazione come sottoclasse di AbstractThreadedSyncAdapter
e installarlo come parte di un'applicazione Android. Il sistema apprende l'adattatore di sincronizzazione dagli elementi del file manifest dell'applicazione e da uno speciale file XML a cui rimanda il manifest. Il file XML definisce il tipo di account per il servizio online e l'autorità del 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 fornitore di contenuti
con cui esegue la sincronizzazione. A quel punto, il sistema inizia a gestire l'adattatore, chiamandolo secondo necessità per la sincronizzazione tra il fornitore di contenuti 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 dalla stessa organizzazione. Ad esempio, gli adattatori di sincronizzazione per i servizi online di Google hanno tutti lo stesso tipo di account com.google
. Quando gli utenti aggiungono un Account Google ai propri dispositivi, vengono elencati tutti gli adattatori di sincronizzazione installati per i servizi Google. Ogni adattatore di sincronizzazione elencato viene sincronizzato con un fornitore di contenuti diverso 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, e spesso utilizzato insieme, al framework dell'adattatore di sincronizzazione. Il framework di autenticazione utilizza autenticatori plug-in che sono sottoclassi di AbstractAccountAuthenticator
. Un autenticatore verifica l'identità dell'utente nei seguenti passaggi:
- Raccoglie nome, 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 utilizzarle in un secondo momento. Grazie al framework di autenticazione del plug-in, AccountManager
può fornire l'accesso a qualsiasi token di autenticazione supportato da un autenticatore e che sceglie di esporre, ad esempio i token di autenticazione OAuth2.
Sebbene l'autenticazione non sia richiesta, la maggior parte dei servizi di contatti la utilizza. Tuttavia, non è necessario utilizzare il framework di autenticazione di Android per eseguire l'autenticazione.
Implementazione dell'adattatore di sincronizzazione
Per implementare un adattatore di sincronizzazione per il fornitore di contatti, devi innanzitutto creare 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 cross-process ai metodi dell'adattatore. -
L'adattatore di sincronizzazione effettivo, implementato come una sottoclasse concreta di
AbstractThreadedSyncAdapter
. -
Questa classe si occupa di scaricare i dati dal server, caricarli dal dispositivo e risolvere i conflitti. Il lavoro principale dell'adattatore viene svolto nel metodo
onPerformSync()
. Questa classe deve essere creata un'istanza come singleton. -
Una sottoclasse di
Application
. -
Questa classe funge da fabbrica 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 del sistema per l'autenticazione degli utenti. -
AccountManager
avvia questo servizio per iniziare il processo di autenticazione. Il metodoonCreate()
del servizio crea un'istanza di un oggetto di autenticazione. 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 cross-process ai metodi dell'autenticatore. -
Facoltativo: una sottoclasse concreta di
AbstractAccountAuthenticator
che gestisce le richieste di autenticazione. -
Questa classe fornisce i metodi utilizzati da
AccountManager
per autenticare le credenziali dell'utente con il server. I dettagli del processo di autenticazione variano notevolmente in base alla tecnologia server in uso. Per scoprire 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 dell'adattatore di sincronizzazione e dell'autenticatore 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>
del servizio dell'adattatore di sincronizzazione rimanda 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 rimanda al file XMLres/xml/authenticator.xml
. A sua volta, questo file specifica il tipo di account supportato da questo autenticatore, oltre alle risorse UI visualizzate durante il processo di autenticazione. Il tipo di account specificato in questo elemento deve essere uguale a quello specificato per l'adattatore di sincronizzazione.
-
L'elemento
Dati 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 del flusso della tua rete a queste tabelle oppure puoi leggere i dati dei flussi da queste tabelle e visualizzarli nella tua applicazione, oppure in entrambe. Grazie a queste funzionalità, le tue applicazioni e i tuoi servizi di social networking possono essere integrati nell'esperienza di Android sui social network.
Testo stream social
Gli elementi dello stream sono sempre associati a un contatto non elaborato. Il parametro
android.provider.ContactsContract.StreamItemsColonne#RAW_CONTACT_ID rimanda al valore _ID
del contatto non elaborato. Nella riga dell'elemento dello stream vengono archiviati anche il tipo e il nome dell'account del contatto non elaborato.
Archivia i dati del tuo stream nelle seguenti colonne:
- android.provider.ContactsContract.StreamItemsColonne#ACCOUNT_TYPE
- Obbligatorio. Il tipo di account dell'utente per il contatto non elaborato associato a questo elemento di stream. Ricordati di impostare questo valore quando inserisci un elemento di streaming.
- android.provider.ContactsContract.StreamItemsColonne#ACCOUNT_NAME
- Obbligatorio. Il nome dell'account utente per il contatto non elaborato associato a questo elemento di stream. Ricordati di impostare questo valore quando inserisci un elemento di streaming.
- Colonne dell'identificatore
-
Obbligatorio. Quando inserisci un elemento dello stream, devi inserire le seguenti colonne degli identificatori:
- android.provider.ContactsContract.StreamItemsar#CONTACT_ID: il valore android.provider.BaseColonne#_ID del contatto a cui è associato l'elemento del flusso.
- android.provider.ContactsContract.StreamItemsCon#CONTACT_LOOKUP_KEY: il valore android.provider.ContactsContract.ContactsColumn#LOOKUP_KEY del contatto a cui è associato questo elemento del flusso.
- android.provider.ContactsContract.StreamItemsCon#RAW_CONTACT_ID: il valore android.provider.BaseColonne#_ID del contatto non elaborato a cui è associato questo elemento del flusso.
- android.provider.ContactsContract.StreamItemsColumn#COMMENTS
- Facoltativo. Memorizza le informazioni di riepilogo che puoi visualizzare all'inizio di un elemento dello stream.
- android.provider.ContactsContract.StreamItemsColumn#TEXT
-
Il testo dell'elemento dello streaming, i contenuti pubblicati dall'origine dell'elemento o la descrizione di un'azione che ha generato l'elemento dello stream. Questa colonna può contenere qualsiasi formattazione e immagini delle risorse incorporate di cui
fromHtml()
può eseguire il rendering. Il provider potrebbe troncare o aumentare l'ellittica dei contenuti lunghi, ma cercherà di evitare di danneggiare i tag. - android.provider.ContactsContract.StreamItemsColonne#TIMESTAMP
- Una stringa di testo contenente l'ora in cui l'elemento stream è stato inserito o aggiornato, sotto forma di millisecondi dall'epoca. Le applicazioni che inseriscono o aggiornano elementi del flusso sono responsabili della manutenzione di questa colonna, che non viene gestita automaticamente dal provider di contatti.
Per visualizzare le informazioni per l'identificazione degli elementi del flusso, utilizza android.provider.ContactsContract.StreamItemsar
La tabella android.provider.ContactsContract.StreamItems contiene anche le colonne android.provider.ContactsContract.StreamItemsColumn#SYNC1 tramite android.provider.ContactsContract.StreamItemsColumn#SYNC4 per l'utilizzo esclusivo degli adattatori di sincronizzazione.
Foto stream social
La tabella android.provider.ContactsContract.StreamItemPhotos archivia le foto associate a un elemento dello stream. La colonna android.provider.ContactsContract.StreamItemPhotosColumn#STREAM_ITEM_ID della tabella rimanda ai valori nella 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 provider per l'archiviazione e la visualizzazione. Questa colonna è disponibile per la compatibilità con le versioni precedenti del provider di contatti che la utilizzava per archiviare le foto. Tuttavia, nella versione attuale non dovresti utilizzare questa colonna per archiviare le foto. Utilizza invece android.provider.ContactsContract.StreamItemPhotosColumn#PHOTO_FILE_ID o android.provider.ContactsContract.StreamItemPhotosColumn#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.StreamItemFotoColonne#PHOTO_FILE_ID
-
Un identificatore numerico di una foto per un contatto non elaborato. Aggiungi questo valore alla costante
DisplayPhoto.CONTENT_URI
per ottenere un URI di contenuti che rimandi a un singolo file di foto, quindi chiamaopenAssetFileDescriptor()
per ottenere un handle per il file di foto. - android.provider.ContactsContract.StreamItemFotoColonne#PHOTO_URI
-
Un URI di contenuti che rimanda direttamente al file della foto per la foto rappresentata da questa riga.
Chiama
openAssetFileDescriptor()
con questo URI per ottenere un handle per il file di foto.
Utilizzo delle tabelle degli stream social
Queste tabelle funzionano come le altre tabelle principali nel provider di contatti, ad eccezione di quanto segue:
- Queste tabelle richiedono autorizzazioni di accesso aggiuntive. Per poter leggere questi messaggi, la tua applicazione deve avere l'autorizzazione android.Manifest.permission#READ_SOCIAL_STREAM. Per modificarli, la tua applicazione deve avere l'autorizzazione android.Manifest.permission#WRITE_SOCIAL_STREAM.
-
Per la tabella android.provider.ContactsContract.StreamItems, il numero di righe archiviate per ogni contatto non elaborato è limitato. Una volta raggiunto questo limite,
il fornitore di contatti libera spazio per nuove righe di elementi dello stream eliminando automaticamente
le righe che contengono il valore
android.provider.ContactsContract.StreamItemsColumn#TIMESTAMP meno recente. Per ottenere il
limite, invia una query all'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 colonna singola android.provider.ContactsContract.StreamItems#MAX_ITEMS.
La classe android.provider.ContactsContract.StreamItems.StreamItemPhotos definisce una sottotabella di android.provider.ContactsContract.StreamItemPhotos contenente le righe di foto per un singolo elemento dello stream.
Interazioni con gli stream social
I dati dello stream social gestiti dal provider di contatti, insieme all'applicazione dei contatti del dispositivo, offrono un metodo efficace per connettere il tuo sistema di social network ai contatti esistenti. Sono disponibili le seguenti funzionalità:
- Sincronizzando il tuo servizio di social networking con il provider di contatti mediante un adattatore di sincronizzazione, puoi recuperare l'attività recente dei contatti di un utente e memorizzarla nelle tabelle android.provider.ContactsContract.StreamItems e android.provider.ContactsContract.StreamItemPhotos per un utilizzo futuro.
- Oltre alla sincronizzazione standard, puoi attivare l'adattatore di sincronizzazione in modo che recuperi dati aggiuntivi quando l'utente seleziona un contatto da visualizzare. Ciò consente all'adattatore di sincronizzazione di 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 un contatto viene visualizzato e, a quel punto, aggiornare lo stato del contatto dal 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 servizio di social network mentre lo guarda nell'applicazione Contatti del dispositivo. Puoi attivare questa funzionalità con la funzionalità "Invita contatto", che puoi attivare con una combinazione di un'attività che aggiunge un contatto esistente alla tua rete, e di un file XML che fornisce i dettagli dell'applicazione all'applicazione per i contatti del dispositivo e al provider di contatti.
La sincronizzazione regolare degli elementi dello stream con il provider di contatti è uguale a quella delle 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 a contatti verranno trattati nelle due sezioni successive.
Registrazione per gestire le visualizzazioni sui social network
Per registrare l'adattatore di sincronizzazione in modo che riceva notifiche quando l'utente visualizza un contatto gestito tramite questo adattatore:
-
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 dei contatti del dispositivo, aggiungi l'attributo
viewContactNotifyService="serviceclass"
all'elemento, doveserviceclass
è il nome di classe completo del servizio che dovrebbe ricevere l'intent dall'applicazione dei contatti del dispositivo. Per il servizio di notifica, utilizza una classe che estendaIntentService
, in modo da consentire al servizio di ricevere gli intent. I dati nell'intent in entrata contengono l'URI dei contenuti del contatto non elaborato su cui l'utente ha fatto clic. Dal servizio di notifica, puoi associare e poi chiamare l'adattatore di sincronizzazione per aggiornare i dati del contatto non elaborato.
Per registrare un'attività da richiamare 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 l'utente che fa clic su un elemento dello stream nell'applicazione dei contatti del dispositivo, aggiungi l'attributo
viewStreamItemActivity="activityclass"
all'elemento, doveactivityclass
è il nome di classe completo dell'attività che dovrebbe ricevere l'intent dall'applicazione contatti del dispositivo. -
Per registrare una delle tue attività per gestire l'utente che fa clic su una foto in streaming nell'applicazione dei contatti del dispositivo, aggiungi l'attributo
viewStreamItemPhotoActivity="activityclass"
all'elemento, doveactivityclass
è il nome di classe completo dell'attività che dovrebbe ricevere l'intent dall'applicazione contatti del dispositivo.
L'elemento <ContactsAccountType>
è descritto in maggiore dettaglio nella
sezione Elemento <ContactsAccountType>.
L'intent in entrata contiene l'URI dei contenuti dell'elemento o della foto su cui l'utente ha fatto clic. Per creare attività distinte per gli elementi di testo e per le foto, utilizza entrambi gli attributi nello stesso file.
Interazione con il servizio di social network
Gli utenti non devono uscire dall'applicazione dei contatti del dispositivo per invitare un contatto sul tuo sito di social network. Puoi invece fare in modo che l'app dei contatti sul dispositivo invii 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 obsoleto per ContactsAccountType
.
Riferimento contacts.xml
Il file contacts.xml
contiene elementi XML che controllano l'interazione dell'adattatore e dell'applicazione di sincronizzazione con l'applicazione per i 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 dei contatti. La sintassi è la seguente:
<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">
contenuti in:
res/xml/contacts.xml
può contenere:
<ContactsDataKind>
Descrizione:
Dichiara i componenti Android e le etichette UI che consentono agli utenti di invitare uno dei loro contatti a un social network, avvisare gli utenti quando uno degli stream dei loro 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 completo della classe dell'attività nell'applicazione che vuoi attivare quando l'utente seleziona Aggiungi connessione dall'applicazione dei 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 risorse di tipo stringa per questa etichetta. viewContactNotifyService
- Il nome completo della classe di un servizio nell'applicazione che deve ricevere notifiche quando l'utente visualizza un contatto. Questa notifica viene inviata dall'applicazione dei contatti del dispositivo e consente all'applicazione di posticipare le operazioni con uso intensivo dei dati fino a quando non sono necessarie. Ad esempio, l'applicazione può rispondere a questa notifica leggendo e visualizzando la foto ad alta risoluzione del contatto e gli elementi dello stream social più recenti. Questa funzionalità è descritta in maggiore dettaglio nella sezione Interazioni stream social.
viewGroupActivity
- Il nome completo della classe di un'attività nell'applicazione che può visualizzare 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 visualizzata dall'applicazione Contatti per un controllo dell'interfaccia utente che consente all'utente di visualizzare i gruppi nella tua applicazione.
Per questo attributo è consentito un identificatore di risorsa stringa.
viewStreamItemActivity
- Il nome completo della classe di un'attività nell'applicazione che viene avviata dall'applicazione di contatto del dispositivo quando l'utente fa clic su un elemento dello stream per un contatto non elaborato.
viewStreamItemPhotoActivity
- Il nome completo della classe di un'attività nell'applicazione che viene avviata dall'applicazione di contatto del dispositivo quando l'utente fa clic su una foto nell'elemento dello stream per un contatto non elaborato.
Elemento <ContactsDataKind>
L'elemento <ContactsDataKind>
controlla la visualizzazione delle righe di dati personalizzate
dell'applicazione nell'interfaccia utente dell'applicazione di contatti. La sintassi è la seguente:
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
contenuti in:
<ContactsAccountType>
Descrizione:
Utilizza questo elemento per fare in modo che l'applicazione per i 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 è necessario
aggiungere l'elemento se hai una riga di dati personalizzata per la quale non vuoi visualizzare dati.
Attributi
android:mimeType
-
Il tipo MIME personalizzato che hai definito per uno dei tipi di righe di dati personalizzate 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 visualizzata dall'applicazione Contatti accanto ai tuoi dati. Utilizzalo 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 è destinata a 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 fornitore di contatti
Oltre alle funzionalità principali descritte nelle sezioni precedenti, il fornitore di contatti offre queste utili funzionalità per l'utilizzo dei dati dei contatti:
- Gruppi di contatti
- Funzionalità fotografiche
Gruppi di contatti
Il fornitore di contatti può facoltativamente etichettare raccolte di contatti correlati con dati del
gruppo. Se il server associato a un account utente
desidera gestire i gruppi, l'adattatore di sincronizzazione per il tipo di account dell'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 vengono 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, ma non utilizzi i gruppi, dovrai chiedere al provider di rendere visibili i tuoi dati. Nel codice che viene eseguito quando un utente aggiunge un account al dispositivo, aggiorna la riga ContactsContract.Settings
aggiunta dal fornitore di contatti per l'account. In questa riga, imposta il valore della colonna Settings.UNGROUPED_VISIBLE
su 1. Quando esegui questa operazione, il fornitore di contatti renderà sempre visibili i dati dei tuoi contatti, anche se non utilizzi i gruppi.
Foto dei contatti
La tabella ContactsContract.Data
archivia 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 le informazioni sulla foto della foto principale
di un contatto, che è la foto principale del contatto non elaborato principale del contatto. Allo stesso modo, la classe ContactsContract.RawContacts.DisplayPhoto
definisce una sottotabella di ContactsContract.RawContacts
contenente le informazioni relative alla foto per la foto principale di un contatto non elaborato.
La documentazione di riferimento per ContactsContract.Contacts.Photo
e ContactsContract.RawContacts.DisplayPhoto
contiene esempi del recupero di informazioni sulle foto. Non esiste una classe di convenienza per il recupero della miniatura principale di un contatto non elaborato, ma puoi inviare una query alla tabella ContactsContract.Data
, selezionando la colonna _ID
del contatto non elaborato, Photo.CONTENT_ITEM_TYPE
e IS_PRIMARY
per trovare la riga della foto principale del contatto non elaborato.
I dati degli stream social di una persona possono includere anche foto. Questi elementi sono archiviati nella tabella android.provider.ContactsContract.StreamItemPhotos, descritta in maggiore dettaglio nella sezione Foto di stream social.